docs: preserve row 219 full gate on the post-295 merge

This commit is contained in:
Rocko committed 2026-10-23 05:32:20 -05:00
1 parent 054da642f9
commit f0a6b3cf46
131 files changed
+52426 -19

No files matched your search

+53
View File
@@ -340,3 +340,56 @@ push only after authorized. Estimate3-5min. No tests/mutants/Node/push until
Sage CLEAR. Dewey delta review follows; later full no-carry restart includes
all10 changed files, bus/cli/webui, all10 shells, all24 mutants and reader
control/audit. Further old-test red stops and goes to Mos through Sage.
# Full restart r6, sage-3345 / mos-1439
Merge 054da642f9b0680bd7743583e4c67266d7e14e2a, parents 2efd127ce617a9bff8dc3bee3a3b657b0c175c07 and b899095e4.
Tree e86a84a852c9739f157033e3772243b293742983 matches Sage before any run.
No conflicts or manual product/test edits. Same16 paths, new manifest
4a2271ddbd4212324f39e608c3a35a4ae834c045cb0d13a619715017ded0ffef.
Fresh worktree /mnt/storage/scratch/rocko-r219-g3345, node_modules linked immediately.
Runner uses mktemp -d /mnt/storage/scratch/tmp/rXXXXXX and validates
[A-Za-z0-9/.-] for each stage; no previous results carry.
10 whole singles; bus,cli,webui,harness suites; all10 shell scripts
including test-task with Docker;24 mutants;reader audit.
Timeout -k10: singles300s,suites1800s,shells7200s,mutants180s;
7200s exceeds3x the prior conservative25min shell baseline.
Every child has a process group; any red stops, no retry/edit.
Finally remove owned networks including rocko-r219-g3345_default,
sweep, preserve packet evidence, remove node_modules link and this worktree.
## Post-295 gate result
## Completed gate, sage-3345
Full run 2026-10-23 10:12:57Z-10:29:22Z on merge054da642f9.
Ten singles183/0/0; bus889/0/1; cli927/0/0; webui373/0/1;
harness151/0/0. Node total2523pass/0fail/2skip,0cancelled.
All10 shell files green,449pass/0fail.24 assertion kills,0timeout kills,
0survivors,0invalid. T15 finished in66.9s with ERR_ASSERTION on the
missing 'no longer available' status; T24 got decision-required instead
of host-creative-role (same exact later-fence mismatch as r4).
Both Bean pins and the sandbox-dependent native-history test ran, no skip.
Skips: X-G3 scratch Gitea1.27.1 unavailable (Node stage uses deadDocker);
WebUI rebuild needs esbuild0.28.2 but host has0.28.1; queue verify/render
requires canonical checkout; test-task live provider cases intentionally
skipped twice (row244, no provider call). GATE-RESULTS.json quotes all5
skip lines, including the duplicated live-provider line.
Reader audit: all16 changed paths plus unchanged public/bus.js,237 reader
hits and22 64-hex lines. None of those pins freezes a changed file. Positive
control channel-proof.test.mjs webui bus.mjs pin matches actual bytes;
public/bus.js stays unchanged. Full audit in reader-audit.json.
16 candidate hashes restored. Driver cwd node/chromium sweep empty.
Owned rocko-r219-gate_default removed; worktree-basename network
rocko-r219-g3345_default already absent (rm attempted as instructed).
No retries, unexpected reds or guard interventions in this run. Results
from prior environmental failures were not carried.
Historical environmental failures remain in run-0780-mos1429/ (harness
underscore TMPDIR) and run-0780-mos1432/ (Docker address pool exhaustion).
Mos1432 required alphanumeric paths; Mos1437/Mos1439 and sage3339/3345
resolved the network issue outside this candidate. No product/test fix
for either. Tests and source remain byte-identical to the checked merge.
+1 -1
View File
@@ -1 +1 @@
80793d824e05baaef4820f4be7790aba55971b6f
2efd127ce617a9bff8dc3bee3a3b657b0c175c07
+10 -10
View File
@@ -1,5 +1,5 @@
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
index d76085739..11061c778 100644
index 7befba0c0..20da8b84c 100644
--- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs
@@ -4,7 +4,7 @@ import { initializeControl, processingStatus } from '../../processing-control/sr
@@ -20,7 +20,7 @@ index d76085739..11061c778 100644
// user in threads. Its runs claim a thread, not the role, and every message to or from it names its
// thread (REQ-MSG-1).
// Row 146 (#1640): the sender of the bus's notice to launch.by. A role instance name matches the
@@ -598,7 +598,7 @@ export class Broker {
@@ -610,7 +610,7 @@ export class Broker {
);
}
#threaded(business, role) {
@@ -29,7 +29,7 @@ index d76085739..11061c778 100644
}
// Row 264a (#1769): a claim holds one session of a thread, and each session has its own holder.
#sessionHolder(session) {
@@ -6068,24 +6068,27 @@ export class Broker {
@@ -6171,24 +6171,27 @@ export class Broker {
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
}
@@ -65,7 +65,7 @@ index d76085739..11061c778 100644
if (this.#store.get('SELECT 1 FROM threads WHERE project=? AND name=?', a.project, a.name)) fail('thread-exists');
const tid = randomUUID();
this.#store.run(
@@ -6094,13 +6097,13 @@ export class Broker {
@@ -6197,13 +6200,13 @@ export class Broker {
this.#now(),
s.business,
a.project,
@@ -157,7 +157,7 @@ index a6086e56c..7275ad902 100644
assert.doesNotMatch(source, /\bimport\s*\(/);
assert.doesNotMatch(readFileSync(new URL('../src/landing.mjs', import.meta.url), 'utf8'), /\bimport\b/);
diff --git a/packages/bus/tests/threads.test.mjs b/packages/bus/tests/threads.test.mjs
index 8b31da9f9..280e03184 100644
index 27193a745..ae44fb66a 100644
--- a/packages/bus/tests/threads.test.mjs
+++ b/packages/bus/tests/threads.test.mjs
@@ -601,3 +601,56 @@ test('project.create keeps unrelated names with and without a repository', t =>
@@ -261,7 +261,7 @@ index 4dcd1ebeb..8471ddf85 100644
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'old instance', instance: 'assistant' }), 'access-refused');
+});
diff --git a/packages/cli/src/launcher.mjs b/packages/cli/src/launcher.mjs
index bfe55b61e..d8248284c 100644
index f1ee4043e..cb9631ddf 100644
--- a/packages/cli/src/launcher.mjs
+++ b/packages/cli/src/launcher.mjs
@@ -53,7 +53,7 @@
@@ -273,7 +273,7 @@ index bfe55b61e..d8248284c 100644
// a launch from a session or --pm refuses thread-required. Every tick
// (threadTickMs) the host asks the broker for the business's threads
// (launch op threadsState) and:
@@ -139,6 +139,7 @@ import { createServer } from "node:net";
@@ -142,6 +142,7 @@ import { createServer } from "node:net";
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync } from "node:fs";
import { dirname, join, relative } from "node:path";
import { configDir, loadBusiness, resolveInstance, systemVars } from "../../business/src/index.mjs";
@@ -281,7 +281,7 @@ index bfe55b61e..d8248284c 100644
import { limitHit } from "../../business/src/launch.mjs";
import { BundleError, buildBundle, hasFallback, readSkills, sessionModel } from "../../harness/src/bundle.mjs";
import { createLoader } from "../../plugins/src/loader.mjs";
@@ -176,8 +177,6 @@ const STOP_TIMEOUT_MS = 30000;
@@ -179,8 +180,6 @@ const STOP_TIMEOUT_MS = 30000;
const LINE_MAX = 4096;
// How often the host asks for the threads' state; a poll period, not a limit.
const THREAD_TICK_MS = 2000;
@@ -290,7 +290,7 @@ index bfe55b61e..d8248284c 100644
// Row 194: the broker's LIFECYCLE_REQUEST_MS. A regenerate's old run must be gone by then.
const LIFECYCLE_REQUEST_MS = 60000;
@@ -331,7 +330,7 @@ export function createLauncher({
@@ -334,7 +333,7 @@ export function createLauncher({
try { b=loadBusiness(host.business,{dir:configDir(env),rolesDir:rolesDir(env)}); } catch(e){throw new Refusal("organization-refused",e.message);}
if (!b.launch) throw new Refusal("launch-not-configured", `business ${b.id} has no launch block`);
if (!(pm ? instance === b.launch.by : b.launch.instances.includes(instance) || (human && Object.hasOwn(b.roles, instance)))) throw new Refusal("instance-not-listed");
@@ -299,7 +299,7 @@ index bfe55b61e..d8248284c 100644
if (threaded && !thread) throw new Refusal("thread-required", `${instance} runs only for a thread`);
if (!threaded && thread) throw new Refusal("thread-not-allowed", `${instance} isn't a thread instance`);
if (threaded && !session) throw new Refusal("session-required", `${instance} runs for one session of a thread`);
@@ -997,7 +996,7 @@ export function createLauncher({
@@ -1014,7 +1013,7 @@ export function createLauncher({
function ending(r) {
const runs = r.runs ?? [];
const at = Date.parse(r.at);
@@ -1,10 +1,10 @@
142a210882535108159e045c61be288bc8703910a8ee0169137bc1eb80baff64 packages/bus/src/broker.mjs
09569596eba8d129d0aa78c52dbf92d2a26a8f2b1960d54898eb90a810398465 packages/bus/src/broker.mjs
b0c66c8999b567896923f6c876911dba936c6eca1ccc7aaeada51369fd613594 packages/bus/tests/creative.test.mjs
3efd0238da60e0bfcd3b25e5256b9c213085119a86a7b852b13e7a60340d3a24 packages/bus/tests/grant-executor.test.mjs
b7ea857637947c7a9ca45c56cea47e37e06f0fd8446ea91316990d9fa7299a7a packages/bus/tests/project-runtime.test.mjs
264d82944c53b638bf6a14cc0706cf9ba184d668211c2b9c1031cdbe88e84bb9 packages/bus/tests/threads.test.mjs
760c4a5dc2881a47be2a174f92c4fcb1b6e46abb299cb045a2e3dcbc485fb334 packages/bus/tests/threads.test.mjs
8f7d6745fffb2a0898ac80c012084ac48964748da6439a8c56facc6529ffb19b packages/bus/tests/user-principal.test.mjs
b74275ce43249bafc97edc87a6a3aa0431c62fe1a28cd9fa265246e6d6da219b packages/cli/src/launcher.mjs
20423a8614ada8f2d1156ab2b041e93582986a1277e86520b0909e66892e4aec packages/cli/src/launcher.mjs
2c3f120dfdc0b52bf8ab38a88e86251be75d4b0d218e23df489dcaf815d6bc15 packages/cli/tests/launcher.test.mjs
7f308fdf601e839194f026beb3fea279eba1e356ce2756aa4a42fa4873b79b30 packages/cli/tests/thread-launch.test.mjs
39c6d207e026debb7db8aade5534503847d0f5c84d560ba28f3cd65490378363 packages/webui/src/chat-http.mjs
+29
View File
@@ -0,0 +1,29 @@
## Full restart r5: sage-3313 / post-302 merge
Merged b899095e4 onto 07802dc57ad36337351da70af15c447bea38ab8b, clean, merge 06d7cc2f0ffc544abfb2755f99dce76ca164212c.
Both row302 and row219 changes in broker.mjs and threads.test.mjs retained
by the automatic merge; no manual product/test edits. New 16-path manifest
420befed0aa8291736d34187ac7e745b7660efed15348ece045a7dd8957c9124.
No previous runs carried. All 10 whole touched files, bus/cli/webui/harness
suites, all shell files, 24 mutants, reader audit. Fresh short TMPDIR per
stage, process groups, timeout -k 10; previous time limits remain.
Packet runner now writes a fresh run-0780-mos1429 directory and includes
harness explicitly. It classifies GNU timeout exit 124/137 or signal -9 as
timeout too, so a timed-out mutant can never be called assertion-killed.
No mutation/test/product changes for that runner classification correction.
Every old-test red (including compactions-launch:229) stops for Mos.
## Runner-only restart under mos-1432 / sage-3323
Previous full run stopped at harness claude-session.test.mjs:298. Saved
TMPDIR /mnt/storage/scratch/tmp/rh85v_4j3 contains underscore; the fixture's
slug replaces only / and . while Claude normalizes underscore too. Sage's
clean-base diagnostic reproduces 10/0 with alphanumeric TMPDIR and 9/1
with underscore. Latent test bug is a separate row; no code/test fix here.
Runner now creates each stage TMPDIR with mktemp -d
/mnt/storage/scratch/tmp/rXXXXXX and rejects any full path outside
[A-Za-z0-9/.-]. New output run-0780-mos1432 preserves the prior red logs.
Same merge 06d7cc2f0 and 16-path manifest 420befed; no results carried.
Full restart waits for Sage CLEAR after 295. No tests or Node in preparation.
+1
View File
@@ -0,0 +1 @@
07802dc57ad36337351da70af15c447bea38ab8b
+832
View File
@@ -0,0 +1,832 @@
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
index b05922479..4c59c3a4a 100644
--- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs
@@ -4,7 +4,7 @@ import { initializeControl, processingStatus } from '../../processing-control/sr
import { NAME as HARNESS_NAME } from '../../plugins/src/kinds.mjs';
import { loadRequirements } from '../../tasks/src/requirements.mjs';
import { MERGE, LandingRefusal, readLanding } from './landing.mjs';
-import { ID_PATTERN, THREAD_DEFINITION, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';
+import { ID_PATTERN, THREAD_DEFINITIONS, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';
// Kept closed while S1 lands; its vocabulary is the integration authority.
export const ACTIONS = Object.freeze([
@@ -314,7 +314,7 @@ const enrollBinding = (enroll) => ({
});
// r6 E3: the installer line's comment, then the tag's 32 hex digits.
const INSTALLER_COMMENT = 'mosaic-installer-';
-// Row 121 (#1615): an instance of THREAD_DEFINITION (vocabulary.mjs since row 155) talks with the
+// Row 121 (#1615): an instance of THREAD_DEFINITIONS (vocabulary.mjs since row 213) talks with the
// user in threads. Its runs claim a thread, not the role, and every message to or from it names its
// thread (REQ-MSG-1).
// Row 146 (#1640): the sender of the bus's notice to launch.by. A role instance name matches the
@@ -602,7 +602,7 @@ export class Broker {
);
}
#threaded(business, role) {
- return this.#business(business).roles[role]?.definition === THREAD_DEFINITION;
+ return THREAD_DEFINITIONS.includes(this.#business(business).roles[role]?.definition);
}
// Row 264a (#1769): a claim holds one session of a thread, and each session has its own holder.
#sessionHolder(session) {
@@ -6079,24 +6079,27 @@ export class Broker {
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
}
- // A thread belongs to the user who made it and to the business's one thread instance.
+ // A thread belongs to the user who made it and to one declared thread instance.
// Row 177 (#1675): a tester's thread is owned by `tester`, so neither reaches the other's.
case 'thread.create': {
if (!s.tester && !s.user) this.#human(s);
- keys(a, ['project', 'name'], ['project', 'name']);
+ keys(a, ['project', 'name', 'instance'], ['project', 'name']);
if (!name(a.name)) fail('invalid-request');
+ const instances = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
+ const instance = a.instance === undefined
+ ? (instances.length === 1 ? instances[0] : null)
+ : identifier(a.instance);
// FED R3 (design 3.5): a user's grant must cover the project and the thread instance, checked
// before the project is looked up, so an ungranted id reads the same whether it exists or not.
if (s.user) {
- const threaded = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
- if (threaded.length !== 1 || !this.#granted(s, 'thread.create', identifier(a.project), threaded[0])) fail('access-refused');
+ if (!instance || !instances.includes(instance) || !this.#granted(s, 'thread.create', identifier(a.project), instance))
+ fail('access-refused');
}
if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', identifier(a.project), s.business))
fail('project-not-found');
- // The bus never picks between instances; the business file declares exactly one.
- const instances = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
- if (!instances.length) fail('thread-instance-missing');
- if (instances.length > 1) fail('thread-instance-ambiguous');
+ // The human selects an instance; omission is compatible only with one candidate.
+ if (!instances.length || (a.instance !== undefined && !instances.includes(instance))) fail('thread-instance-missing');
+ if (a.instance === undefined && instances.length > 1) fail('thread-instance-ambiguous');
if (this.#store.get('SELECT 1 FROM threads WHERE project=? AND name=?', a.project, a.name)) fail('thread-exists');
const tid = randomUUID();
this.#store.run(
@@ -6105,13 +6108,13 @@ export class Broker {
this.#now(),
s.business,
a.project,
- instances[0],
+ instance,
this.#owner(s),
a.name,
);
// Row 264a (#1769, D3): every thread has a default session from the start.
- this.#newSession(s, { id: tid, business: s.business, instance: instances[0], owner: this.#owner(s) }, 'default', 'default', true);
- return { id: tid, instance: instances[0] };
+ this.#newSession(s, { id: tid, business: s.business, instance, owner: this.#owner(s) }, 'default', 'default', true);
+ return { id: tid, instance };
}
// Row 177 (#1675): each lists its own threads. The human's list leaves out the tester's,
// and the tester's lists only its own (row 175 listed it the business human's).
diff --git a/packages/bus/tests/creative.test.mjs b/packages/bus/tests/creative.test.mjs
index 6cfbf1cd6..6d421ba19 100644
--- a/packages/bus/tests/creative.test.mjs
+++ b/packages/bus/tests/creative.test.mjs
@@ -44,15 +44,18 @@ function setup(t) {
const caps = {};
caps.human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
const project = b.request(caps.human, { verb: 'project.create', args: { name: 'demo' } }).id;
- const thread = b.request(caps.human, { verb: 'thread.create', args: { project, name: 'main' } }).id;
+ const thread = b.request(caps.human, { verb: 'thread.create', args: { project, name: 'main', instance: 'assistant' } }).id;
+ const threads = { assistant: thread };
for (const role of Object.keys(roles)) {
- caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi', ...(role === 'assistant' ? { thread } : {}) });
+ if (['bean', 'quill'].includes(role))
+ threads[role] = b.request(caps.human, { verb: 'thread.create', args: { project, name: role, instance: role } }).id;
+ caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi', ...(threads[role] ? { thread: threads[role] } : {}) });
b.request(caps[role], { verb: 'role.claim' });
}
caps.tester = b.bindTester({ business: 'demo' });
const call = (who, verb, args = {}) => b.request(caps[who], { verb, args });
const count = (sql) => store.get(sql).n;
- return { call, count, store };
+ return { call, count, store, threads };
}
const raise = { action: 'policy.change', question: 'Change?', options: [{ key: 'yes', text: 'Yes' }, { key: 'no', text: 'No' }], recommendation: 'yes', blocking: false };
@@ -72,7 +75,7 @@ test('M6: a creative raise refuses decision-not-allowed, cited or not, and no de
});
test('a creative instance sends only to the user and hears only from the user', (t) => {
- const { call, count } = setup(t);
+ const { call, count, threads } = setup(t);
for (const who of ['bean', 'quill']) {
call(who, 'message.send', { to: 'human', body: 'hello' });
for (const to of ['chief', 'assistant', 'pm', 'bean', 'quill'].filter((n) => n !== who)) {
@@ -81,7 +84,7 @@ test('a creative instance sends only to the user and hears only from the user',
for (const from of ['chief', 'pm']) {
assert.throws(() => call(from, 'message.send', { to: who, body: 'hello' }), { code: 'message-sender-refused' }, `${from} to ${who}`);
}
- call('human', 'message.send', { to: who, body: 'hello' });
+ call('human', 'thread.send', { thread: threads[who], body: 'hello' });
}
assert.equal(count("SELECT count(*) n FROM messages WHERE from_role IN ('bean','quill')"), 2);
assert.equal(count("SELECT count(*) n FROM messages WHERE to_role IN ('bean','quill')"), 2);
diff --git a/packages/bus/tests/grant-executor.test.mjs b/packages/bus/tests/grant-executor.test.mjs
index e449b7d77..46235d16b 100644
--- a/packages/bus/tests/grant-executor.test.mjs
+++ b/packages/bus/tests/grant-executor.test.mjs
@@ -1047,7 +1047,10 @@ test('sage-1908: once the agent of an applied grant is a creative role, host.run
const f = await setup(t);
assert.equal((await f.approve()).end.action, 'grant.applied');
const r = await restart(t, f, creativeOps(f.business));
- const cap = r.broker.bindLaunch({ business: 'demo', role: 'ops', run: 'ops-run', harness: 'pi', address: 'ops' });
+ const human = r.broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
+ const project = r.broker.request(human, { verb: 'project.create', args: { name: 'host grant check' } }).id;
+ const thread = r.broker.request(human, { verb: 'thread.create', args: { project, name: 'ops', instance: 'ops' } }).id;
+ const cap = r.broker.bindLaunch({ business: 'demo', role: 'ops', run: 'ops-run', harness: 'pi', address: 'ops', thread });
r.broker.request(cap, { verb: 'role.claim' });
const executor = hostExecutor({ credentials: r.credentials, root: join(f.dir, 'host-run') });
const before = f.requests().length;
diff --git a/packages/bus/tests/project-runtime.test.mjs b/packages/bus/tests/project-runtime.test.mjs
index a6086e56c..7275ad902 100644
--- a/packages/bus/tests/project-runtime.test.mjs
+++ b/packages/bus/tests/project-runtime.test.mjs
@@ -32,7 +32,8 @@ test('Broker keeps the sandbox client import closure: only the listed imports, n
// (#1692, D2.4) reads unhireableAction, the hire veto's action name.
// Row 236 (#1742, #436b) reads giteaName and parseGiteaUrl for the Gitea grant path.
// FED R3 r5 (row 274, #414) reads covers, a user's grant check, from it.
- "import { ID_PATTERN, THREAD_DEFINITION, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';",
+ // Row 219 reads THREAD_DEFINITIONS from the same vocabulary for creative thread admission.
+ "import { ID_PATTERN, THREAD_DEFINITIONS, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';",
]);
assert.doesNotMatch(source, /\bimport\s*\(/);
assert.doesNotMatch(readFileSync(new URL('../src/landing.mjs', import.meta.url), 'utf8'), /\bimport\b/);
diff --git a/packages/bus/tests/threads.test.mjs b/packages/bus/tests/threads.test.mjs
index 27193a745..ae44fb66a 100644
--- a/packages/bus/tests/threads.test.mjs
+++ b/packages/bus/tests/threads.test.mjs
@@ -601,3 +601,56 @@ test('project.create keeps unrelated names with and without a repository', t =>
{ id: linked.id, name: 'work', repo: 'stack' },
]);
});
+
+test('C2-09a: explicit creative instances bind independent threads, default sessions and holders', t => {
+ const creative = ['companion', 'ideas', 'research', 'proofreader', 'fact-checker', 'continuity'];
+ const extra = Object.fromEntries(creative.map((definition, i) => [`writer${i}`, { definition, authority: { withinRole: ['message.send'], crossRole: [] } }]));
+ const ctx = setup(t, demo({ roles: { ...roles, ...extra }, definitions: {
+ ...definitions, ...Object.fromEntries(creative.map(definition => [definition, { messaging: shipped(definition).messaging }])),
+ } }));
+ const project = ctx.call('human', 'project.create', { name: 'Writing' }).id;
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: 'ambiguous' }), { code: 'thread-instance-ambiguous' });
+ for (const [instance, role] of Object.entries(extra)) {
+ const receipt = ctx.call('human', 'thread.create', { project, name: instance, instance });
+ assert.equal(receipt.instance, instance);
+ assert.equal(ctx.store.get('SELECT instance FROM threads WHERE id=?', receipt.id).instance, instance);
+ assert.equal(ctx.store.get('SELECT instance FROM sessions WHERE thread=?', receipt.id).instance, instance);
+ ctx.launch(instance, instance, { thread: receipt.id });
+ assert.equal(ctx.claim(instance).thread, receipt.id);
+ const sent = ctx.call('human', 'thread.send', { thread: receipt.id, body: `For ${instance}` });
+ const read = ctx.call(instance, 'message.receive');
+ assert.deepEqual(read.map(m => m.id), [sent.id], role.definition);
+ assert.equal(ctx.threadOf(sent.id), receipt.id);
+ assert.equal(ctx.store.get('SELECT count(*) n FROM role_claims WHERE role=?', instance).n, 0);
+ assert.throws(() => ctx.call('human', 'message.send', { to: instance, body: 'No implicit thread' }),
+ { code: 'thread-required-use-thread-send' });
+ }
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), creative.length);
+});
+
+test('C2-09a: explicit missing, non-thread and malformed selectors create no thread or session', t => {
+ const ctx = setup(t);
+ const project = ctx.call('human', 'project.create', { name: 'Writing' }).id;
+ for (const instance of ['not-declared', 'pm', 'coder'])
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: instance, instance }), { code: 'thread-instance-missing' });
+ for (const instance of [null, 7, {}, [], '', 'bad name'])
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: 'bad', instance }), { code: 'invalid-request' });
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), 0);
+ assert.equal(ctx.count('SELECT count(*) n FROM sessions'), 0);
+ const legacy = ctx.call('human', 'thread.create', { project, name: 'Default' });
+ assert.equal(legacy.instance, 'assistant');
+});
+
+
+test('C2-09a: creative bindLaunch without a thread refuses thread-required', t => {
+ const creative = ['companion', 'ideas', 'research', 'proofreader', 'fact-checker', 'continuity'];
+ const extra = Object.fromEntries(creative.map((definition, i) => [`creative${i}`, { definition, authority: { withinRole: ['message.send'], crossRole: [] } }]));
+ const ctx = setup(t, demo({ roles: { ...roles, ...extra }, definitions: {
+ ...definitions, ...Object.fromEntries(creative.map(definition => [definition, {}])),
+ } }));
+ for (const instance of Object.keys(extra)) {
+ assert.throws(() => ctx.launch(`unthreaded-${instance}`, instance), { code: 'thread-required' });
+ }
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), 0);
+ assert.equal(ctx.count('SELECT count(*) n FROM role_claims'), 0);
+});
diff --git a/packages/bus/tests/user-principal.test.mjs b/packages/bus/tests/user-principal.test.mjs
index 4dcd1ebeb..8471ddf85 100644
--- a/packages/bus/tests/user-principal.test.mjs
+++ b/packages/bus/tests/user-principal.test.mjs
@@ -23,7 +23,7 @@ const grant = (over = {}) => ({
});
// `processing` stands in for the halt switch, as lifecycle-verbs.test.mjs's T15 does.
-function setup(t, { grants = [grant()], processing = null } = {}) {
+function setup(t, { grants = [grant()], processing = null, threadRoles = {} } = {}) {
const root = mkdtempSync(join(tmpdir(), 'bus-user-principal-'));
const store = new Store(root);
t.after(() => {
@@ -40,6 +40,7 @@ function setup(t, { grants = [grant()], processing = null } = {}) {
roles: {
chief: { definition: 'chief-of-staff', authority: { withinRole: ['message.send'], crossRole: [] } },
assistant: { definition: 'assistant', authority: { withinRole: ['message.send'], crossRole: [] } },
+ ...threadRoles,
},
definitions: { 'chief-of-staff': {}, assistant: { messaging: shipped('assistant').messaging } },
},
@@ -394,3 +395,21 @@ test('R3 r4: the chief\'s hire refuses an instance named user with the code it g
test('R3 r4: a non-function access throws before the store is read', () => {
assert.throws(() => new Broker({ store: null, businesses: { demo: {} }, access: 'access.json' }), /access must be a function/);
});
+
+test('C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project', t => {
+ const ctx = setup(t, { threadRoles: { bean: { definition: 'companion', authority: { withinRole: ['message.send'], crossRole: [] } } } });
+ const project = ctx.call('human', 'project.create', { name: 'shared' }).id;
+ const privateProject = ctx.call('human', 'project.create', { name: 'private' }).id;
+ ctx.access.doc.grants = [grant({ projects: [project] })];
+ ctx.user('alice');
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'blocked bean', instance: 'bean' }), 'access-refused');
+ for (const id of [privateProject, '00000000-0000-4000-8000-000000000000'])
+ assert.equal(ctx.code('alice', 'thread.create', { project: id, name: 'blocked project', instance: 'assistant' }), 'access-refused');
+ assert.equal(ctx.store.get('SELECT count(*) n FROM threads').n, 0);
+ const allowed = ctx.call('alice', 'thread.create', { project, name: 'assistant work', instance: 'assistant' });
+ assert.equal(allowed.instance, 'assistant');
+ ctx.access.doc.grants = [grant({ projects: [project], instances: ['bean'] })];
+ const bean = ctx.call('alice', 'thread.create', { project, name: 'bean work', instance: 'bean' });
+ assert.equal(bean.instance, 'bean');
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'old instance', instance: 'assistant' }), 'access-refused');
+});
diff --git a/packages/cli/src/launcher.mjs b/packages/cli/src/launcher.mjs
index bfe55b61e..d8248284c 100644
--- a/packages/cli/src/launcher.mjs
+++ b/packages/cli/src/launcher.mjs
@@ -53,7 +53,7 @@
// session.json names it as gitSocket.
//
// Thread sessions (row 121, #1615). A thread instance (an instance whose
-// role definition is the assistant) runs once per thread, never on its own:
+// role definition is in THREAD_DEFINITIONS) runs once per thread, never on its own:
// a launch from a session or --pm refuses thread-required. Every tick
// (threadTickMs) the host asks the broker for the business's threads
// (launch op threadsState) and:
@@ -139,6 +139,7 @@ import { createServer } from "node:net";
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync } from "node:fs";
import { dirname, join, relative } from "node:path";
import { configDir, loadBusiness, resolveInstance, systemVars } from "../../business/src/index.mjs";
+import { THREAD_DEFINITIONS } from "../../business/src/vocabulary.mjs";
import { limitHit } from "../../business/src/launch.mjs";
import { BundleError, buildBundle, hasFallback, readSkills, sessionModel } from "../../harness/src/bundle.mjs";
import { createLoader } from "../../plugins/src/loader.mjs";
@@ -176,8 +177,6 @@ const STOP_TIMEOUT_MS = 30000;
const LINE_MAX = 4096;
// How often the host asks for the threads' state; a poll period, not a limit.
const THREAD_TICK_MS = 2000;
-// The broker's thread instance definition (packages/bus/src/broker.mjs).
-const THREAD_DEFINITION = "assistant";
// Row 194: the broker's LIFECYCLE_REQUEST_MS. A regenerate's old run must be gone by then.
const LIFECYCLE_REQUEST_MS = 60000;
@@ -331,7 +330,7 @@ export function createLauncher({
try { b=loadBusiness(host.business,{dir:configDir(env),rolesDir:rolesDir(env)}); } catch(e){throw new Refusal("organization-refused",e.message);}
if (!b.launch) throw new Refusal("launch-not-configured", `business ${b.id} has no launch block`);
if (!(pm ? instance === b.launch.by : b.launch.instances.includes(instance) || (human && Object.hasOwn(b.roles, instance)))) throw new Refusal("instance-not-listed");
- const threaded = b.roles[instance]?.definition === THREAD_DEFINITION;
+ const threaded = THREAD_DEFINITIONS.includes(b.roles[instance]?.definition);
if (threaded && !thread) throw new Refusal("thread-required", `${instance} runs only for a thread`);
if (!threaded && thread) throw new Refusal("thread-not-allowed", `${instance} isn't a thread instance`);
if (threaded && !session) throw new Refusal("session-required", `${instance} runs for one session of a thread`);
@@ -997,7 +996,7 @@ export function createLauncher({
function ending(r) {
const runs = r.runs ?? [];
const at = Date.parse(r.at);
- const threaded = b.roles[r.instance]?.definition === THREAD_DEFINITION;
+ const threaded = THREAD_DEFINITIONS.includes(b.roles[r.instance]?.definition);
return registry.sessions.filter((s) => s.business === b.id && (runs.includes(s.run) ||
(!threaded && s.instance === r.instance && (spawnedAt.get(s.run) ?? Date.parse(s.startedAt)) <= at)));
}
diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs
index 2202c7753..baabd843e 100644
--- a/packages/cli/tests/launcher.test.mjs
+++ b/packages/cli/tests/launcher.test.mjs
@@ -773,21 +773,34 @@ test("row 213 a creative instance's persona is read from <configDir>/personas: o
const dir = join(given.root, "config", "personas", "acme");
mkdirSync(dir, { recursive: true, mode: 0o700 });
writeFileSync(join(dir, "bean.md"), "Not the pinned text.\n", { mode: 0o600 });
- const { f, host, launcher } = await setup(t, undefined, given);
- // The chief of staff holds role.launch; a test chief session bound to this process asks.
- const chief = await host.bindLaunch({ business: "acme", role: "chief", run: "chief-test", harness: "pi", pid: process.pid, startTime: startTimeOf(process.pid) });
- await new Client({ path: host.path, cap: chief.cap }).call("role.claim");
- // The bundle's refusal comes back as launch-failed, as webPlan's does, with its reason in the log.
- assert.deepEqual(await launcher.fromSession(chief.cap, "bean"), { ok: false, error: "launch-failed" });
- const [e] = log(f);
- assert.deepEqual([e.event, e.code, log(f).length], ["refused", "launch-failed", 1]);
+ // Row 219: creative instances launch from their thread, not from a chief's standalone request.
+ // The mismatch occurs before bindLaunch, so only the host's thread-state snapshot is a fixture.
+ const thread = 'persona-thread';
+ const SESSION = 'persona-session';
+ const { f, launcher } = await setup(t, undefined, given, { wrap: host => ({
+ ...host,
+ op: message => message.op === 'threadsState'
+ ? Promise.resolve([{ thread, session: SESSION, instance: 'bean', holder: null, pending: 1 }])
+ : host.op(message),
+ }) });
+ // The thread path records row 270's run-start count before the bundle's persona refusal.
+ await launcher.tick();
+ const runs = readdirSync(join(f.dataRoot, "launches", "acme"));
+ const entries = log(f);
+ assert.equal(entries.length, 2);
+ const { at, ...compactions } = entries[0];
+ assert.deepEqual(compactions, { event: 'compactions', instance: 'bean', thread, session: SESSION, run: runs[0], count: 0, known: true, reason: null });
+ const e = entries[1];
+ assert.deepEqual([e.event, e.code], ["refused", "launch-failed"]);
+ assert.equal(e.thread, thread);
assert.equal(e.why, `instance bean: persona file ${join(dir, "bean.md")} isn't the file persona.sha256 names`);
assert.ok(!JSON.stringify(log(f)).includes("PERSONA-MARKER-d2"));
assert.deepEqual(readSessions(f.dataRoot), []);
// The run directory exists by then (BUILD.md residual); the persona is read before any bundle file.
- const runs = readdirSync(join(f.dataRoot, "launches", "acme"));
assert.equal(runs.length, 1);
- assert.deepEqual(readdirSync(join(f.dataRoot, "launches", "acme", runs[0])), ["session"]);
+ assert.deepEqual(readdirSync(join(f.dataRoot, "launches", "acme", runs[0])), []);
+ assert.equal(existsSync(join(f.dataRoot, "threads", "acme", thread, "sessions", SESSION, "session")), true);
+ assert.equal(existsSync(join(f.dataRoot, "threads", "acme", thread, "session")), false);
});
test("row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts", { timeout: 30000 }, async (t) => {
diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs
index 0d207a0a0..fa3adb866 100644
--- a/packages/cli/tests/thread-launch.test.mjs
+++ b/packages/cli/tests/thread-launch.test.mjs
@@ -396,6 +396,22 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t
assert.deepEqual(launchLog(f).map((e) => [e.event, e.instance, e.code]), [["refused", "assistant", "thread-required"]]);
});
+// Admission refuses before bundle construction or a sandbox is needed.
+test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => {
+ const prepared = prepare(t, { edit: doc => {
+ doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' };
+ doc.launch.instances.push('bean');
+ return doc;
+ } });
+ const { f } = prepared;
+ const host = await start(t, prepared);
+ const chief = await host.host.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-bean-refusal', harness: 'pi', pid: process.pid, startTime: startTimeOf(process.pid) });
+ await new Client({ path: host.host.path, cap: chief.cap }).call('role.claim');
+ assert.deepEqual(await host.launcher.fromSession(chief.cap, 'bean'), { ok: false, error: 'thread-required' });
+ assert.deepEqual(readSessions(f.dataRoot), []);
+ assert.deepEqual(launchLog(f).map(e => [e.event, e.instance, e.code]), [['refused', 'bean', 'thread-required']]);
+});
+
// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the token-limit check switched on, with the
// assistant's limit at 0, so it is at its limit before any turn. The real check runs through the
// bus's plug-in loader.
@@ -443,3 +459,73 @@ test("REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn:
assert.deepEqual(threadRuns(f, A), []);
assert.equal(host.runtime.threadsState("acme").find((x) => x.thread === A).holder, null);
});
+
+test('C2-09a: creative thread messages start distinct runs and keep each instance history separate', { skip, timeout: 90000 }, async t => {
+ const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => {
+ const vars = { ...doc.roles.assistant.vars };
+ delete doc.roles.assistant;
+ doc.roles.bean = { definition: 'companion', vars: { ...vars } };
+ doc.roles.chet = { definition: 'continuity', vars: { ...vars } };
+ doc.launch.instances = ['coder', 'bean', 'chet'];
+ return doc;
+ } });
+ const host = await start(t, prepared), { f } = prepared;
+ const project = host.call('project.create', { name: 'Writing' }).id;
+ const bean = host.call('thread.create', { project, name: 'Bean', instance: 'bean' }).id;
+ const chet = host.call('thread.create', { project, name: 'Chet', instance: 'chet' }).id;
+ const send = async (thread, instance, body) => {
+ const message = host.call('thread.send', { thread, body });
+ const outcome = await until(() => host.call('thread.messages', { thread }).find(m => m.in_reply_to === message.id) ||
+ launchLog(f).find(row => row.thread === thread && row.event === 'refused'));
+ assert.notEqual(outcome.event, 'refused', `creative thread launch refused: ${outcome.code}`);
+ const reply = outcome;
+ assert.equal(reply.from_role, instance);
+ assert.equal(reply.to_role, 'human');
+ return JSON.parse(reply.body.match(/THREAD (\{.*\})/)[1]);
+ };
+ assert.deepEqual((await send(bean, 'bean', 'Bean input')).before, []);
+ assert.deepEqual((await send(chet, 'chet', 'Chet input')).before, []);
+ assert.deepEqual((await send(bean, 'bean', 'Bean again')).before, ['Bean input']);
+ assert.equal(readFileSync(join(f.dataRoot, 'threads', 'acme', bean, 'sessions', host.runtime.threadsState('acme').find(x => x.thread === bean).session, 'session', 'history'), 'utf8'), 'Bean input\nBean again\n');
+ assert.equal(readFileSync(join(f.dataRoot, 'threads', 'acme', chet, 'sessions', host.runtime.threadsState('acme').find(x => x.thread === chet).session, 'session', 'history'), 'utf8'), 'Chet input\n');
+ assert.equal(existsSync(join(f.dataRoot, 'threads', 'acme', bean, 'session')), false);
+ assert.equal(existsSync(join(f.dataRoot, 'threads', 'acme', chet, 'session')), false);
+ const launches = launchLog(f).filter(row => row.event === 'launch');
+ assert.ok(launches.some(row => row.instance === 'bean' && row.thread === bean));
+ assert.ok(launches.some(row => row.instance === 'chet' && row.thread === chet));
+ const chief = await host.host.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-creative-test', harness: 'pi', pid: process.pid, startTime: startTimeOf(process.pid) });
+ await new Client({ path: host.host.path, cap: chief.cap }).call('role.claim');
+ assert.deepEqual(await host.launcher.fromSession(chief.cap, 'bean'), { ok: false, error: 'thread-required' });
+});
+
+test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => {
+ const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => {
+ doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' };
+ delete doc.roles.assistant;
+ doc.launch.instances = ['coder', 'bean'];
+ return doc;
+ } });
+ let requests = [];
+ const host = await start(t, prepared, { ops: () => ({
+ lifecycleRequests: () => requests,
+ lifecycleTake: () => ({ ...requests[0], go: true }),
+ lifecycleDone: () => { requests = []; return {}; },
+ }) });
+ const { f } = prepared;
+ const project = host.call('project.create', { name: 'Writing' }).id;
+ const threads = ['one', 'two'].map(name => host.call('thread.create', { project, name, instance: 'bean' }).id);
+ for (const thread of threads) {
+ const message = host.call('thread.send', { thread, body: 'Stay in this thread' });
+ const outcome = await until(() => host.call('thread.messages', { thread }).find(row => row.in_reply_to === message.id) ||
+ launchLog(f).find(row => row.thread === thread && row.event === 'refused'));
+ assert.notEqual(outcome.event, 'refused', `creative thread launch refused: ${outcome.code}`);
+ }
+ const runs = threads.map(thread => launchLog(f).find(row => row.thread === thread && row.event === 'launch').run);
+ requests = [{ request: 'one-thread-end', state: 'open', business: 'acme', action: 'lifecycle.end',
+ instance: 'bean', runs: [runs[0]], by: f.doc.human, at: new Date(Date.now() + 1000).toISOString() }];
+ await host.launcher.tick();
+ await until(() => launchLog(f).some(row => row.event === 'lifecycle-end' && row.run === runs[0]));
+ assert.deepEqual(launchLog(f).filter(row => row.event === 'lifecycle-end').map(row => row.run), [runs[0]]);
+ await until(() => !readSessions(f.dataRoot).some(row => row.run === runs[0]));
+ assert.ok(readSessions(f.dataRoot).some(row => row.run === runs[1]));
+});
diff --git a/packages/webui/src/chat-http.mjs b/packages/webui/src/chat-http.mjs
index 7980c1442..63d3ca974 100644
--- a/packages/webui/src/chat-http.mjs
+++ b/packages/webui/src/chat-http.mjs
@@ -80,7 +80,8 @@ export async function chatHTTP(req, res, { path, search, chat, named, send, prev
(Object.hasOwn(v, 'repo') && (typeof v.repo !== 'string' || !KEY.test(v.repo)))) throw Error();
action = 'createProject'; args = v;
} else if (path === '/api/chat/threads') {
- if (!exact(v, ['project', 'name']) || !id(v.project) || !name(v.name)) throw Error();
+ if (!exact(v, ['project', 'name', 'instance'], ['project', 'name']) || !id(v.project) || !name(v.name) ||
+ (Object.hasOwn(v, 'instance') && (typeof v.instance !== 'string' || !KEY.test(v.instance)))) throw Error();
action = 'createThread'; args = v;
} else if (match?.[2] === 'messages') {
if (!exact(v, ['body']) || typeof v.body !== 'string' || !v.body.trim() || v.body.length > 32768) throw Error();
diff --git a/packages/webui/src/chat.mjs b/packages/webui/src/chat.mjs
index 1b0a41dce..5a1ed8bf1 100644
--- a/packages/webui/src/chat.mjs
+++ b/packages/webui/src/chat.mjs
@@ -25,7 +25,7 @@ export function createChat({ call, owner = null }) {
projects: () => channel('project.list', {}),
createProject: ({ name, repo }) => channel('project.create', { name, ...(repo === undefined ? {} : { repo }) }),
threads: ({ project } = {}) => channel('thread.list', project === undefined ? {} : { project }),
- createThread: ({ project, name }) => channel('thread.create', { project, name }),
+ createThread: ({ project, name, instance }) => channel('thread.create', { project, name, ...(instance === undefined ? {} : { instance }) }),
messages: ({ thread, after, limit }) => channel('thread.messages', { thread, ...(after ? { after } : {}), limit }),
send: ({ thread, body }) => channel('thread.send', { thread, body, class: 'REQUEST' }),
open: ({ thread }) => channel('thread.open', { thread }),
diff --git a/packages/webui/src/public/chat.js b/packages/webui/src/public/chat.js
index a6b805c1b..15066bdc6 100644
--- a/packages/webui/src/public/chat.js
+++ b/packages/webui/src/public/chat.js
@@ -14,7 +14,7 @@
return states.get(id);
};
const form = key => {
- if (!forms.has(key)) forms.set(key, { name: '', repo: '', receipt: '', pending: false });
+ if (!forms.has(key)) forms.set(key, { name: '', repo: '', instance: '', instanceChosen: false, receipt: '', pending: false });
return forms.get(key);
};
class ChatError extends Error {
@@ -58,7 +58,11 @@
function creation(kind, key) {
const f = form(key), project = kind === 'project';
const repo = project && current.repos.length ? `<label>Repository (optional)<select id="chat-repo" data-chat-field="repo"><option value="">No repository link</option>${current.repos.map(r => `<option value="${esc(r)}"${r === f.repo ? ' selected' : ''}>${txt(r)}</option>`).join('')}</select></label>` : '';
- return `<form id="chat-${kind}-form" class="chat-create" data-key="${esc(key)}"><h2>New ${kind}</h2><label>Name<input id="chat-${kind}-name" data-chat-field="name" value="${esc(f.name)}" maxlength="200" required${disabled(f.pending)}></label>${repo}<button class="btn" type="submit"${disabled(f.pending)}>${f.pending ? 'Creating…' : `Create ${kind}`}</button><p role="status">${txt(f.receipt)}</p></form>`;
+ const choices = current.instances ?? [];
+ const picker = project ? '' : `<label>Thread with<select id="chat-instance" data-chat-field="instance" required${disabled(f.pending || !!current.instanceError || !choices.length)}><option value="">Choose who this thread is with</option>${choices.map(row => `<option value="${esc(row.instance)}"${row.instance === f.instance ? ' selected' : ''}>${txt(row.name === row.instance ? row.instance : `${row.name} (${row.instance})`)}</option>`).join('')}</select></label>`;
+ const unavailable = !project && (!!current.instanceError || !choices.some(row => row.instance === f.instance));
+ const status = !project && current.instanceError ? current.instanceError : f.receipt || (!project && !choices.length ? 'No thread instances are configured.' : '');
+ return `<form id="chat-${kind}-form" class="chat-create" data-key="${esc(key)}"><h2>New ${kind}</h2><label>Name<input id="chat-${kind}-name" data-chat-field="name" value="${esc(f.name)}" maxlength="200" required${disabled(f.pending)}></label>${repo}${picker}<button class="btn" type="submit"${disabled(f.pending || unavailable)}>${f.pending ? 'Creating…' : `Create ${kind}`}</button><p role="status">${txt(status)}</p></form>`;
}
function threads(rows) {
return rows.length ? `<ul class="chat-thread-list">${rows.map(t => `<li>${link('threads', t.id, t.name)} <span class="muted">${txt(t.instance)}</span></li>`).join('')}</ul>` : '<p>No threads yet.</p>';
@@ -106,7 +110,7 @@
const f = document.getElementById(keep.field.id);
if (f && !f.disabled) {
f.focus({ preventScroll: true });
- if (keep.field.start !== null) f.setSelectionRange(keep.field.start, keep.field.end);
+ if (keep.field.start != null && typeof f.setSelectionRange === 'function') f.setSelectionRange(keep.field.start, keep.field.end);
}
} else if (navigated) c.container.querySelector('h1')?.focus({ preventScroll: true });
}
@@ -126,7 +130,7 @@
paint(); return;
}
const old = current;
- current = { container, kind, id, canSend, projects: old?.projects ?? [], threads: old?.threads ?? [], repos: old?.repos ?? [], error: '', refused: false };
+ current = { container, kind, id, canSend, projects: old?.projects ?? [], threads: old?.threads ?? [], repos: old?.repos ?? [], instances: old?.instances ?? [], instanceError: kind === 'project' ? 'Reading thread instances…' : '', error: '', refused: false };
if (navigated || !old) container.innerHTML = '<p class="chat-loading" role="status">Reading chat…</p>';
try {
const s = kind === 'thread' ? state(id) : null;
@@ -147,6 +151,25 @@
s.receipt = 'Message shown in this conversation.'; s.receiptId = null;
}
}
+ if (kind === 'project') {
+ try {
+ const listed = await request('instances', undefined, signal);
+ if (g !== generation || out()) return;
+ if (!listed.rows.every(row => typeof row?.instance === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,159}$/.test(row.instance) && typeof row.name === 'string'))
+ throw new ChatError('invalid-response');
+ current.instances = listed.rows; current.instanceError = '';
+ const f = form(id);
+ if (f.instance && !listed.rows.some(row => row.instance === f.instance)) {
+ f.instance = ''; f.instanceChosen = true;
+ f.receipt = 'The selected instance is no longer available. Choose who this thread is with.';
+ }
+ if (!f.instanceChosen && !f.instance && listed.rows.length === 1) f.instance = listed.rows[0].instance;
+ } catch (err) {
+ if (err.name === 'AbortError' || out() || g !== generation) return;
+ current.instances = [];
+ current.instanceError = "Couldn't read the thread instances; reload to try again";
+ }
+ }
// Repository choices are optional metadata; an unavailable read must not block unlinked chat.
if (kind === 'list') {
try {
@@ -179,7 +202,11 @@
if (!current || out()) return;
if (event.target.id === 'chat-body') state(current.id).draft = event.target.value;
const f = event.target.closest('.chat-create');
- if (f && event.target.dataset.chatField) form(f.dataset.key)[event.target.dataset.chatField] = event.target.value;
+ if (f && event.target.dataset.chatField) {
+ const draft = form(f.dataset.key), field = event.target.dataset.chatField;
+ draft[field] = event.target.value;
+ if (field === 'instance') { draft.instanceChosen = true; draft.receipt = ''; paint(); }
+ }
});
document.addEventListener('scroll', event => {
if (event.target.matches?.('.chat-log') && current?.kind === 'thread') {
@@ -232,7 +259,8 @@
void write('send', id, { body: state(id).draft }, `threads/${id}/messages`);
} else if (event.target.classList.contains('chat-create')) {
const key = event.target.dataset.key, f = form(key), project = key === 'projects';
- const value = project ? { name: f.name, ...(f.repo ? { repo: f.repo } : {}) } : { project: key, name: f.name };
+ if (!project && (current.instanceError || !(current.instances ?? []).some(row => row.instance === f.instance))) return;
+ const value = project ? { name: f.name, ...(f.repo ? { repo: f.repo } : {}) } : { project: key, name: f.name, instance: f.instance };
void write(project ? 'createProject' : 'createThread', key, value, project ? 'projects' : 'threads');
}
});
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
index cd51a0b6e..56fcdd4e1 100644
--- a/packages/webui/src/serve.mjs
+++ b/packages/webui/src/serve.mjs
@@ -8,6 +8,7 @@ import { BusReadError, busStatus as coreBusStatus, subjectOk, sendOutcome } from
// Row 176, sage-1111: HTTP-only tester refusals; the row-164 transport pin stays untouched.
const busStatus = code => ['tester-off', 'tester-required'].includes(code) ? 403 : coreBusStatus(code);
import { ReadError, readStatus, fieldTable } from './reads.mjs';
+import { THREAD_DEFINITIONS } from '../../business/src/vocabulary.mjs';
import { chatHTTP } from './chat-http.mjs';
import { processingHTTP } from './processing-http.mjs';
@@ -740,6 +741,25 @@ export async function startServer({ host = '127.0.0.1', port = 7330, board = DEF
if (req.method !== 'POST') return json(res, 405, { error: 'method not allowed' });
return busSend(req, res, { send, preview: signer, log });
}
+ // Row 219: labels only, from this listener's own human or tester bus, after auth above.
+ if (path === '/api/chat/instances') {
+ if (req.method !== 'GET') return json(res, 405, { error: 'method-not-allowed' });
+ if (search) return json(res, 400, { error: 'invalid-request' });
+ if (!bus?.instances) return json(res, 503, { error: 'not-configured' });
+ try {
+ const value = await bus.instances();
+ if (!value || !value.instances || typeof value.instances !== 'object' || Array.isArray(value.instances))
+ throw new BusReadError('invalid-response');
+ const rows = Object.entries(value.instances)
+ .filter(([, entry]) => THREAD_DEFINITIONS.includes(entry?.definition))
+ .map(([instance, entry]) => ({ instance, name: entry.vars?.['persona.name'] || instance }));
+ if (rows.some(row => typeof row.name !== 'string')) throw new BusReadError('invalid-response');
+ return json(res, 200, { rows, at: new Date().toISOString() });
+ } catch (err) {
+ const code = err instanceof BusReadError ? err.code : 'invalid-response';
+ return json(res, busStatus(code), { error: code });
+ }
+ }
if (path === '/api/chat' || path.startsWith('/api/chat/')) {
return chatHTTP(req, res, { path, search, chat, named, send, preview: writer, log });
}
diff --git a/packages/webui/tests/chat-browser.test.mjs b/packages/webui/tests/chat-browser.test.mjs
index f3eeeca96..cc3ec093d 100644
--- a/packages/webui/tests/chat-browser.test.mjs
+++ b/packages/webui/tests/chat-browser.test.mjs
@@ -12,6 +12,7 @@ const HOSTILE = '<img src=x onerror="window.injected=1">\u001b[31m\u202eevil';
const stub = `(() => {
const real = fetch.bind(window);
window.__writes=[];window.__reads=[];window.__answers=[];window.__pages=[];
+ window.__instances=[{instance:'assistant',name:'assistant'}];
window.__projects=[{id:'${P}', name:${JSON.stringify(HOSTILE)}, repo:null},{id:'\"><img src=x>',name:'Invalid record'}];
window.__threads=[{id:'${A}',project:'${P}',name:'alpha',instance:'assistant'}, {id:'${B}',project:'${P}',name:'beta',instance:'assistant'}];
window.fetch=async (url,init={})=>{
@@ -33,6 +34,7 @@ const stub = `(() => {
window.__reads.push(path);
if(window.__holdRead && path.includes(window.__holdRead)){window.__heldSignal=init.signal;await new Promise(r=>window.__releaseRead=r);}
if(window.__readFailure)return new Response(JSON.stringify({error:window.__readFailure}),{status:window.__readFailure==='thread-not-owned'?403:503});
+ if(path==='/api/chat/instances')return window.__instanceFailure ? new Response(JSON.stringify({error:'not-configured'}),{status:503}) : data({rows:window.__instances});
if(path==='/api/chat/projects')return data({rows:window.__projects});
if(path.startsWith('/api/chat/threads')&&!path.includes('/messages'))return data({rows:window.__threads});
if(window.__pages.length)return data(window.__pages.shift());
@@ -82,7 +84,7 @@ test('chat browser: list/create named projects and threads, hostile labels inert
await b.evaluate(`window.__answers.push({body:{id:'${A}',instance:'assistant'}}),true`);
await click('#chat-thread-form button[type=submit]');
await wait('document.querySelector("#chat-send-form")');
- assert.deepEqual(await b.evaluate('window.__writes[1].body'), { project: P, name: 'named thread' });
+ assert.deepEqual(await b.evaluate('window.__writes[1].body'), { project: P, name: 'named thread', instance: 'assistant' });
assert.equal(await b.evaluate('window.__writes[1].path'), '/api/chat/threads');
assert.equal(await b.evaluate('window.__writes.length'), 2);
});
@@ -264,3 +266,65 @@ test('chat browser: a late project creation receipt cannot navigate away from an
assert.equal(await b.evaluate('location.hash'), '#/chat/threads/' + A);
assert.equal(await b.evaluate('document.querySelector("#chat-body").value'), 'stay in alpha');
});
+
+test('C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST', async t => {
+ const { b, wait, set, click, refresh } = await setup(t);
+ await b.evaluate(`window.__instances=[{instance:'bean',name:${JSON.stringify(HOSTILE)}},{instance:'chet',name:${JSON.stringify(HOSTILE)}}];location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelectorAll("#chat-instance option").length===3');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").required'), true);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ const labels = await b.evaluate('[...document.querySelectorAll("#chat-instance option")].map(o=>o.textContent)');
+ assert.match(labels[1], /\(bean\)/);
+ assert.match(labels[2], /\(chet\)/);
+ assert.doesNotMatch(labels.join(''), /[\u202a-\u202e\u2066-\u2069]/u);
+ assert.equal(await b.evaluate('!!document.querySelector("#chat-thread-form img")'), false);
+ assert.equal(await b.evaluate('window.injected'), undefined);
+ await set('#chat-thread-name', 'Bean discussion');
+ await click('#chat-thread-form button');
+ assert.equal(await b.evaluate('window.__writes.length'), 0);
+ await set('#chat-instance', 'bean');
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), 'bean');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-name").value'), 'Bean discussion');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), false);
+ await b.evaluate(`window.__answers.push({body:{id:'${A}',instance:'bean'}});true`);
+ await click('#chat-thread-form button');
+ await wait('window.__writes.length===1');
+ assert.deepEqual(await b.evaluate('window.__writes[0]'), { path: '/api/chat/threads', body: { project: P, name: 'Bean discussion', instance: 'bean' }, redirect: 'manual' });
+});
+
+test('C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching', async t => {
+ const { b, wait, set, refresh } = await setup(t);
+ await b.evaluate(`window.__instanceFailure=true;location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelector("#chat-thread-form") && !document.querySelector(".chat-loading")');
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /read the thread instances/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await b.evaluate('window.__instanceFailure=false;window.__instances=[];true');
+ await refresh();
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /No thread instances/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await b.evaluate("window.__instances=[{instance:'bean',name:'Bean'},{instance:'chet',name:'Chet'}];true");
+ await refresh();
+ await set('#chat-instance', 'bean');
+ await b.evaluate("window.__instances=[{instance:'chet',name:'Chet'}];true");
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '');
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /no longer available/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '', 'another refresh must not silently select the one remaining instance');
+ await set('#chat-instance', 'chet');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), false);
+});
+
+test('C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread', async t => {
+ const { b, wait } = await setup(t, '?readonly');
+ await b.evaluate(`location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelectorAll("#chat-instance option").length===2');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), 'assistant');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").disabled'), true);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ assert.match(await b.evaluate('document.querySelector(".chat-area").textContent'), /only reads/);
+ assert.equal(await b.evaluate('window.__writes.length'), 0);
+});
diff --git a/packages/webui/tests/chat-instances.test.mjs b/packages/webui/tests/chat-instances.test.mjs
new file mode 100644
index 000000000..893c38f80
--- /dev/null
+++ b/packages/webui/tests/chat-instances.test.mjs
@@ -0,0 +1,87 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { request } from 'node:http';
+import { mkdtempSync, rmSync } from 'node:fs';
+import { join } from 'node:path';
+import { tmpdir } from 'node:os';
+import { startServer } from '../src/serve.mjs';
+import { startTesterSocket, TESTER_ORIGIN } from '../src/tester-socket.mjs';
+import { close } from './fixture.mjs';
+
+const path = '/api/chat/instances';
+const preview = { host: 'chat.example', header: 'x-user', user: 'jason' };
+const snapshot = {
+ revision: 'a'.repeat(64), plugins: { private: 'do-not-project' },
+ instances: {
+ assistant: { definition: 'assistant', vars: { model: 'private-model' } },
+ bean: { definition: 'companion', vars: { 'persona.name': 'Bean', 'persona.sha256': 'private-hash' } },
+ chet: { definition: 'continuity', vars: { 'persona.name': 'Bean', file: '/private/path' } },
+ pm: { definition: 'pm', vars: { 'persona.name': 'Hidden PM' } },
+ },
+};
+function get(server, { route = path, method = 'GET', headers = {} } = {}) {
+ const address = server.address(), unix = typeof address === 'string';
+ return new Promise((resolve, reject) => {
+ const req = request({ ...(unix ? { socketPath: address } : { host: '127.0.0.1', port: address.port }),
+ path: route, method, headers: { host: unix ? new URL(TESTER_ORIGIN).host : preview.host, ...headers } }, res => {
+ let text = '';
+ res.on('data', chunk => text += chunk);
+ res.on('end', () => resolve({ status: res.statusCode, text, value: JSON.parse(text) }));
+ });
+ req.on('error', reject); req.end();
+ });
+}
+test('C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads', async t => {
+ let calls = 0;
+ const server = await startServer({ port: 0, preview, bus: { instances: async () => { calls++; return snapshot; } } });
+ t.after(() => close(server));
+ const refused = await get(server);
+ assert.equal(refused.status, 401);
+ assert.equal(calls, 0);
+ const answer = await get(server, { headers: { 'x-user': 'jason' } });
+ assert.equal(answer.status, 200);
+ assert.deepEqual(answer.value.rows, [{ instance: 'assistant', name: 'assistant' }, { instance: 'bean', name: 'Bean' }, { instance: 'chet', name: 'Bean' }]);
+ for (const row of answer.value.rows) assert.deepEqual(Object.keys(row).sort(), ['instance', 'name']);
+ assert.doesNotMatch(answer.text, /private|Hidden PM/);
+ assert.equal(calls, 1);
+ for (const options of [{ route: path + '?extra=1' }, { method: 'POST' }]) {
+ const result = await get(server, { ...options, headers: { 'x-user': 'jason' } });
+ assert.ok([400, 405].includes(result.status));
+ }
+ assert.equal(calls, 1, 'invalid metadata requests never call a bus');
+});
+test('C2-09a metadata uses the private tester listener own bus and no human transport', async t => {
+ const root = mkdtempSync(join(tmpdir(), 'chat219-')); t.after(() => rmSync(root, { recursive: true, force: true }));
+ let reads = 0;
+ const server = await startTesterSocket({ dataRoot: root, board: 'http://127.0.0.1:9',
+ bus: { instances: async () => { reads++; return { instances: { quill: { definition: 'proofreader', vars: { 'persona.name': 'Quill' } } } }; } } });
+ t.after(() => close(server));
+ const answer = await get(server);
+ assert.equal(answer.status, 200);
+ assert.deepEqual(answer.value.rows, [{ instance: 'quill', name: 'Quill' }]);
+ assert.equal(reads, 1);
+});
+test('C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields', async t => {
+ for (const bus of [null, { instances: async () => ({ instances: [] }) }, { instances: async () => { throw new Error('private-path'); } }]) {
+ const server = await startServer({ port: 0, preview, bus }); t.after(() => close(server));
+ const answer = await get(server, { headers: { 'x-user': 'jason' } });
+ assert.ok(answer.status >= 400);
+ assert.doesNotMatch(answer.text, /private-path/);
+ }
+});
+
+test('C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity', async t => {
+ let session = null, reads = 0;
+ const touches = [];
+ const sso = { publicHost: 'sso-chat.example', publicOrigin: 'https://sso-chat.example', formTargets: () => [],
+ session: (_req, { touch }) => { touches.push(touch); return session; } };
+ const server = await startServer({ port: 0, sso, bus: { instances: async () => { reads++; return snapshot; } } });
+ t.after(() => close(server));
+ const headers = { host: sso.publicHost };
+ assert.equal((await get(server, { headers })).status, 401);
+ assert.equal(reads, 0);
+ session = { name: 'jason' };
+ assert.equal((await get(server, { headers })).status, 200);
+ assert.equal(reads, 1);
+ assert.deepEqual(touches, [false, false]);
+});
diff --git a/packages/webui/tests/chat.test.mjs b/packages/webui/tests/chat.test.mjs
index 8c11611cf..b47ecf1a7 100644
--- a/packages/webui/tests/chat.test.mjs
+++ b/packages/webui/tests/chat.test.mjs
@@ -335,3 +335,21 @@ test('the chat seam names the transport it is given: the human one and the teste
assert.throws(() => createChat({}), { name: 'TypeError', message: 'chat needs a bus transport function' });
assert.equal(typeof createChat({ call: async () => ({}) }).projects, 'function');
});
+
+test('C2-09a createThread forwards an explicit instance through the existing channel', async t => {
+ const c = await setup(t);
+ c.state.answer = { id: T, instance: 'bean' };
+ const answer = await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'Bean work', instance: 'bean' } });
+ assert.equal(answer.status, 200);
+ assert.deepEqual(c.calls, [['thread.create', { project: P, name: 'Bean work', instance: 'bean' }]]);
+ assert.equal(answer.data.instance, 'bean');
+});
+test('C2-09a malformed instance and extra thread fields refuse before the channel write', async t => {
+ const c = await setup(t);
+ for (const value of [null, 7, {}, [], '', 'bad name', 'bean/../../other']) {
+ const answer = await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'work', instance: value } });
+ assert.equal(answer.status, 400, JSON.stringify(value));
+ }
+ assert.equal((await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'work', instance: 'bean', extra: true } })).status, 400);
+ assert.deepEqual(c.calls, []);
+});
@@ -0,0 +1,16 @@
f21daf94d057341f8f9f55ec65fcc16a7d6774311c1ca5b1ab336fc2a9e086e2 packages/bus/src/broker.mjs
b0c66c8999b567896923f6c876911dba936c6eca1ccc7aaeada51369fd613594 packages/bus/tests/creative.test.mjs
3efd0238da60e0bfcd3b25e5256b9c213085119a86a7b852b13e7a60340d3a24 packages/bus/tests/grant-executor.test.mjs
b7ea857637947c7a9ca45c56cea47e37e06f0fd8446ea91316990d9fa7299a7a packages/bus/tests/project-runtime.test.mjs
760c4a5dc2881a47be2a174f92c4fcb1b6e46abb299cb045a2e3dcbc485fb334 packages/bus/tests/threads.test.mjs
8f7d6745fffb2a0898ac80c012084ac48964748da6439a8c56facc6529ffb19b packages/bus/tests/user-principal.test.mjs
b74275ce43249bafc97edc87a6a3aa0431c62fe1a28cd9fa265246e6d6da219b packages/cli/src/launcher.mjs
2c3f120dfdc0b52bf8ab38a88e86251be75d4b0d218e23df489dcaf815d6bc15 packages/cli/tests/launcher.test.mjs
7f308fdf601e839194f026beb3fea279eba1e356ce2756aa4a42fa4873b79b30 packages/cli/tests/thread-launch.test.mjs
39c6d207e026debb7db8aade5534503847d0f5c84d560ba28f3cd65490378363 packages/webui/src/chat-http.mjs
77878dd33fd9e0381deb996969389dde21c109eaddf46a8a5c91907ec0dfe24e packages/webui/src/chat.mjs
8e89df5fe8a9da35de41b5968afc6efcd92503e92ef28c6f557bb5371d10d1a4 packages/webui/src/public/chat.js
443c83debe21559de2e4a6bcc07514bce9bebe209df58b8011827fcc1cabd905 packages/webui/src/serve.mjs
d918e5b061ced9db28337a9b02c7042f9ddafaf09f32affce9f51190c0cba93f packages/webui/tests/chat-browser.test.mjs
2bad14535372488de326b82b791b79a61e1a4ab839458535132958ef85b746eb packages/webui/tests/chat-instances.test.mjs
7779d33d5b1dd3fe183722d0f91ff1f71e741e113cf2d5ee407f3d26428fc162 packages/webui/tests/chat.test.mjs
@@ -0,0 +1,16 @@
packages/bus/src/broker.mjs
packages/bus/tests/creative.test.mjs
packages/bus/tests/grant-executor.test.mjs
packages/bus/tests/project-runtime.test.mjs
packages/bus/tests/threads.test.mjs
packages/bus/tests/user-principal.test.mjs
packages/cli/src/launcher.mjs
packages/cli/tests/launcher.test.mjs
packages/cli/tests/thread-launch.test.mjs
packages/webui/src/chat-http.mjs
packages/webui/src/chat.mjs
packages/webui/src/public/chat.js
packages/webui/src/serve.mjs
packages/webui/tests/chat-browser.test.mjs
packages/webui/tests/chat-instances.test.mjs
packages/webui/tests/chat.test.mjs
@@ -0,0 +1,56 @@
START single-bus-creative.test 2026-10-23T09:21:11.124448+00:00
END {"label": "single-bus-creative.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/creative.test.mjs"], "start": "2026-10-23T09:21:11.124448+00:00", "end": "2026-10-23T09:21:11.489626+00:00", "seconds": 0.365, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-creative.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rE8UFj4"}
START single-bus-grant-executor.test 2026-10-23T09:21:11.492089+00:00
END {"label": "single-bus-grant-executor.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T09:21:11.492089+00:00", "end": "2026-10-23T09:21:33.052545+00:00", "seconds": 21.56, "exit": 0, "timeout": false, "counts": {"tests": 42, "pass": 42, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-grant-executor.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rgY53eZ"}
START single-bus-project-runtime.test 2026-10-23T09:21:33.054938+00:00
END {"label": "single-bus-project-runtime.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/project-runtime.test.mjs"], "start": "2026-10-23T09:21:33.054938+00:00", "end": "2026-10-23T09:21:33.469616+00:00", "seconds": 0.415, "exit": 0, "timeout": false, "counts": {"tests": 3, "pass": 3, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-project-runtime.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rlFp4K6"}
START single-bus-threads.test 2026-10-23T09:21:33.471762+00:00
END {"label": "single-bus-threads.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T09:21:33.471762+00:00", "end": "2026-10-23T09:21:34.838718+00:00", "seconds": 1.367, "exit": 0, "timeout": false, "counts": {"tests": 20, "pass": 20, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-threads.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rshM4G7"}
START single-bus-user-principal.test 2026-10-23T09:21:34.841038+00:00
END {"label": "single-bus-user-principal.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T09:21:34.841038+00:00", "end": "2026-10-23T09:21:35.456433+00:00", "seconds": 0.615, "exit": 0, "timeout": false, "counts": {"tests": 23, "pass": 23, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-user-principal.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYf1Xvx"}
START single-cli-launcher.test 2026-10-23T09:21:35.458772+00:00
END {"label": "single-cli-launcher.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/launcher.test.mjs"], "start": "2026-10-23T09:21:35.458772+00:00", "end": "2026-10-23T09:22:02.030225+00:00", "seconds": 26.571, "exit": 0, "timeout": false, "counts": {"tests": 34, "pass": 34, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-cli-launcher.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/raf1e60"}
START single-cli-thread-launch.test 2026-10-23T09:22:02.032690+00:00
END {"label": "single-cli-thread-launch.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T09:22:02.032690+00:00", "end": "2026-10-23T09:22:20.094607+00:00", "seconds": 18.062, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-cli-thread-launch.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r1Bsuqs"}
START single-webui-chat-browser.test 2026-10-23T09:22:20.097013+00:00
END {"label": "single-webui-chat-browser.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T09:22:20.097013+00:00", "end": "2026-10-23T09:22:38.808507+00:00", "seconds": 18.711, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat-browser.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYKVUnz"}
START single-webui-chat-instances.test 2026-10-23T09:22:38.810775+00:00
END {"label": "single-webui-chat-instances.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T09:22:38.810775+00:00", "end": "2026-10-23T09:22:38.975118+00:00", "seconds": 0.164, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat-instances.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rRn9NQr"}
START single-webui-chat.test 2026-10-23T09:22:38.977256+00:00
END {"label": "single-webui-chat.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T09:22:38.977256+00:00", "end": "2026-10-23T09:22:39.442318+00:00", "seconds": 0.465, "exit": 0, "timeout": false, "counts": {"tests": 25, "pass": 25, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rZRH3FV"}
START suite-bus 2026-10-23T09:22:39.445008+00:00
END {"label": "suite-bus", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/account-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/approval-binding.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/broker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/business.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/channel-proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/chief-of-staff.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-cursors.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-claim.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-store.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/csuite-messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/daily-quota-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-close.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decisions.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/end-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/fire-revokes-grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant-docker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-enroll.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-executor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/hard-waits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-run.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/human.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/instances-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/interpreter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/land.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launch-notice.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launcher-released.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library-sync.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-stop.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-citation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-filter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/name-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peer-transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peercred.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/plugins-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/principal.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/process.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-timeout.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-web.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-await.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-files.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-runtime.test.mjs", "/Line truncated
START suite-cli 2026-10-23T09:23:03.024737+00:00
END {"label": "suite-cli", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/app.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/boot-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-accept.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-hostcheck.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-persona-import.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-unit.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-recovery.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/compactions-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/config.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/follow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/format.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-auth.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-fixtures.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-image.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-state.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/git-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/hosts.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/idle-stop-race.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-account.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launcher.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/lifecycle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits-tokens.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/mark-read.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/notifier.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/other-tree.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/processing-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/publish-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-clear.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release-external.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-upgrade.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-http.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-threads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/trackers-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/verbs.test.mjs"], "start": "2026-10-23T09:23:03.024737+00:00", "end": "2026-10-23T09:33:27.205598+00:00", "seconds": 624.181, "exit": 0, "timeout": false, "counts": {"tests": 921, "pass": 921, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/suite-cli.tap", "tmpdir": "/mnt/storage/scratch/tmp/rl2zH3q"}
START suite-webui 2026-10-23T09:33:27.214222+00:00
END {"label": "suite-webui", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/assistant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/board-held.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser-edge.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/channel-chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/conversation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/discord.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/docs-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-provenance.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/granted-user.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/history-return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/markdown-preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/pinned-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-ui.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-file.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/relaunch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/serve.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/service.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-form.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-write.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/shell.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-listener.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/views.test.mjs"], "start": "2026-10-23T09:33:27.214222+00:00", "end": "2026-10-23T09:34:24.087380+00:00", "seconds": 56.873, "exit": 0, "timeout": false, "counts": {"tests": 374, "pass": 373, "fail": 0, "cancelled": 0, "skipped": 1}, "skips": ["ok 107 - rebuilding from the source gives the same bytes (Line truncated
START suite-harness 2026-10-23T09:34:24.092087+00:00
END {"label": "suite-harness", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/bundle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/git-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/mcp-server.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/pi-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/raise-domain.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/runner.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/skills.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/spec-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/tools.test.mjs"], "start": "2026-10-23T09:34:24.092087+00:00", "end": "2026-10-23T09:34:36.185801+00:00", "seconds": 12.094, "exit": 0, "timeout": false, "counts": {"tests": 151, "pass": 151, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/suite-harness.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYOp9Yo"}
START test-auth 2026-10-23T09:34:36.189152+00:00
END {"label": "test-auth", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-auth.sh"], "start": "2026-10-23T09:34:36.189152+00:00", "end": "2026-10-23T09:34:37.005046+00:00", "seconds": 0.816, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-auth.tap", "tmpdir": "/mnt/storage/scratch/tmp/r7XakMj"}
START test-conductor 2026-10-23T09:34:37.007148+00:00
END {"label": "test-conductor", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-conductor.sh"], "start": "2026-10-23T09:34:37.007148+00:00", "end": "2026-10-23T09:34:43.835703+00:00", "seconds": 6.829, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-conductor.tap", "tmpdir": "/mnt/storage/scratch/tmp/rH7Fj9q"}
START test-config 2026-10-23T09:34:43.837837+00:00
END {"label": "test-config", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-config.sh"], "start": "2026-10-23T09:34:43.837837+00:00", "end": "2026-10-23T09:34:46.357228+00:00", "seconds": 2.519, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-config.tap", "tmpdir": "/mnt/storage/scratch/tmp/rXiHyiw"}
START test-discord 2026-10-23T09:34:46.359375+00:00
END {"label": "test-discord", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-discord.sh"], "start": "2026-10-23T09:34:46.359375+00:00", "end": "2026-10-23T09:34:53.590250+00:00", "seconds": 7.231, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-discord.tap", "tmpdir": "/mnt/storage/scratch/tmp/rcHYakY"}
START test-extension-package 2026-10-23T09:34:53.592317+00:00
END {"label": "test-extension-package", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-extension-package.sh"], "start": "2026-10-23T09:34:53.592317+00:00", "end": "2026-10-23T09:34:54.908860+00:00", "seconds": 1.317, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-extension-package.tap", "tmpdir": "/mnt/storage/scratch/tmp/rxrTT3R"}
START test-foundation 2026-10-23T09:34:54.910888+00:00
END {"label": "test-foundation", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-foundation.sh"], "start": "2026-10-23T09:34:54.910888+00:00", "end": "2026-10-23T09:35:33.605612+00:00", "seconds": 38.695, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-foundation.tap", "tmpdir": "/mnt/storage/scratch/tmp/rs2BwtK"}
START test-land-work 2026-10-23T09:35:33.607688+00:00
END {"label": "test-land-work", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-land-work.sh"], "start": "2026-10-23T09:35:33.607688+00:00", "end": "2026-10-23T09:35:45.646160+00:00", "seconds": 12.038, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-land-work.tap", "tmpdir": "/mnt/storage/scratch/tmp/rZFhfL5"}
START test-queue 2026-10-23T09:35:45.648522+00:00
END {"label": "test-queue", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-queue.sh"], "start": "2026-10-23T09:35:45.648522+00:00", "end": "2026-10-23T09:36:06.604342+00:00", "seconds": 20.956, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": ["skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-queue.tap", "tmpdir": "/mnt/storage/scratch/tmp/rAKp0eR"}
START test-release 2026-10-23T09:36:06.606593+00:00
END {"label": "test-release", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-release.sh"], "start": "2026-10-23T09:36:06.606593+00:00", "end": "2026-10-23T09:36:07.873175+00:00", "seconds": 1.267, "exit": 1, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-release.tap", "tmpdir": "/mnt/storage/scratch/tmp/rbunO3T"}
Traceback (most recent call last):
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 96, in <module>
try:stages()
^^^^^^^^
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 65, in stages
execute(path.stem,['timeout','-k','10','7200','bash',str(path)],deadline=7200,docker=True)
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 55, in execute
if rc or timeout or re.search(r'^\s*not ok\b',text,re.M):raise RuntimeError('RED '+label+' '+str(log))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: RED test-release /mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-0780-mos1432/test-release.tap
+38
View File
@@ -0,0 +1,38 @@
START single-bus-creative.test 2026-10-23T08:55:46.514934+00:00
END {"label": "single-bus-creative.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/creative.test.mjs"], "start": "2026-10-23T08:55:46.514934+00:00", "end": "2026-10-23T08:55:46.880147+00:00", "seconds": 0.365, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-creative.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rlmkrn532"}
START single-bus-grant-executor.test 2026-10-23T08:55:46.881647+00:00
END {"label": "single-bus-grant-executor.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T08:55:46.881647+00:00", "end": "2026-10-23T08:56:08.493753+00:00", "seconds": 21.612, "exit": 0, "timeout": false, "counts": {"tests": 42, "pass": 42, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-grant-executor.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rjpu1ic5m"}
START single-bus-project-runtime.test 2026-10-23T08:56:08.495370+00:00
END {"label": "single-bus-project-runtime.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/project-runtime.test.mjs"], "start": "2026-10-23T08:56:08.495370+00:00", "end": "2026-10-23T08:56:08.910312+00:00", "seconds": 0.415, "exit": 0, "timeout": false, "counts": {"tests": 3, "pass": 3, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-project-runtime.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r6j9zk1mz"}
START single-bus-threads.test 2026-10-23T08:56:08.911614+00:00
END {"label": "single-bus-threads.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T08:56:08.911614+00:00", "end": "2026-10-23T08:56:10.278831+00:00", "seconds": 1.367, "exit": 0, "timeout": false, "counts": {"tests": 20, "pass": 20, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-threads.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/ridmpf8e9"}
START single-bus-user-principal.test 2026-10-23T08:56:10.280242+00:00
END {"label": "single-bus-user-principal.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T08:56:10.280242+00:00", "end": "2026-10-23T08:56:10.845658+00:00", "seconds": 0.565, "exit": 0, "timeout": false, "counts": {"tests": 23, "pass": 23, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-user-principal.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rxak25z_4"}
START single-cli-launcher.test 2026-10-23T08:56:10.847112+00:00
END {"label": "single-cli-launcher.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/launcher.test.mjs"], "start": "2026-10-23T08:56:10.847112+00:00", "end": "2026-10-23T08:56:37.072632+00:00", "seconds": 26.226, "exit": 0, "timeout": false, "counts": {"tests": 34, "pass": 34, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-cli-launcher.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r6tlrj8kb"}
START single-cli-thread-launch.test 2026-10-23T08:56:37.074143+00:00
END {"label": "single-cli-thread-launch.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T08:56:37.074143+00:00", "end": "2026-10-23T08:56:55.131362+00:00", "seconds": 18.057, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-cli-thread-launch.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r9k_bmlhv"}
START single-webui-chat-browser.test 2026-10-23T08:56:55.132860+00:00
END {"label": "single-webui-chat-browser.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T08:56:55.132860+00:00", "end": "2026-10-23T08:57:13.336278+00:00", "seconds": 18.203, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat-browser.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r3mqd7dfy"}
START single-webui-chat-instances.test 2026-10-23T08:57:13.337687+00:00
END {"label": "single-webui-chat-instances.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T08:57:13.337687+00:00", "end": "2026-10-23T08:57:13.502100+00:00", "seconds": 0.164, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat-instances.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/ri0d5cw9g"}
START single-webui-chat.test 2026-10-23T08:57:13.503425+00:00
END {"label": "single-webui-chat.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T08:57:13.503425+00:00", "end": "2026-10-23T08:57:14.018577+00:00", "seconds": 0.515, "exit": 0, "timeout": false, "counts": {"tests": 25, "pass": 25, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rf6pbt6z6"}
START suite-bus 2026-10-23T08:57:14.020317+00:00
END {"label": "suite-bus", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/account-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/approval-binding.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/broker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/business.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/channel-proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/chief-of-staff.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-cursors.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-claim.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-store.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/csuite-messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/daily-quota-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-close.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decisions.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/end-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/fire-revokes-grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant-docker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-enroll.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-executor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/hard-waits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-run.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/human.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/instances-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/interpreter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/land.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launch-notice.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launcher-released.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library-sync.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-stop.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-citation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-filter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/name-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peer-transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peercred.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/plugins-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/principal.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/process.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-timeout.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-web.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-await.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-files.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-runtime.test.mjs", "/Line truncated
START suite-cli 2026-10-23T08:57:37.800853+00:00
END {"label": "suite-cli", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/app.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/boot-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-accept.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-hostcheck.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-persona-import.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-unit.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-recovery.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/compactions-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/config.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/follow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/format.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-auth.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-fixtures.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-image.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-state.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/git-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/hosts.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/idle-stop-race.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-account.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launcher.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/lifecycle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits-tokens.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/mark-read.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/notifier.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/other-tree.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/processing-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/publish-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-clear.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release-external.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-upgrade.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-http.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-threads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/trackers-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/verbs.test.mjs"], "start": "2026-10-23T08:57:37.800853+00:00", "end": "2026-10-23T09:08:05.517184+00:00", "seconds": 627.716, "exit": 0, "timeout": false, "counts": {"tests": 921, "pass": 921, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/suite-cli.tap", "tmpdir": "/mnt/storage/scratch/tmp/r29043nvq"}
START suite-webui 2026-10-23T09:08:05.524672+00:00
END {"label": "suite-webui", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/assistant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/board-held.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser-edge.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/channel-chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/conversation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/discord.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/docs-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-provenance.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/granted-user.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/history-return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/markdown-preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/pinned-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-ui.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-file.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/relaunch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/serve.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/service.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-form.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-write.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/shell.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-listener.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/views.test.mjs"], "start": "2026-10-23T09:08:05.524672+00:00", "end": "2026-10-23T09:09:02.641939+00:00", "seconds": 57.117, "exit": 0, "timeout": false, "counts": {"tests": 374, "pass": 373, "fail": 0, "cancelled": 0, "skipped": 1}, "skips": ["ok 107 - rebuilding from the source gives the same bytes (Line truncated
START suite-harness 2026-10-23T09:09:02.645756+00:00
END {"label": "suite-harness", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/bundle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/git-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/mcp-server.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/pi-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/raise-domain.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/runner.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/skills.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/spec-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/tools.test.mjs"], "start": "2026-10-23T09:09:02.645756+00:00", "end": "2026-10-23T09:09:14.638654+00:00", "seconds": 11.993, "exit": 1, "timeout": false, "counts": {"tests": 151, "pass": 150, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/suite-harness.tap", "tmpdir": "/mnt/storage/scratch/tmp/rh85v_4j3"}
Traceback (most recent call last):
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 95, in <module>
try:stages()
^^^^^^^^
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 61, in stages
execute('suite-'+package,['node','--test',*files],cwd=ROOT/'packages'/package,deadline=1800)
File "/mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-219.py", line 54, in execute
if rc or timeout or re.search(r'^\s*not ok\b',text,re.M):raise RuntimeError('RED '+label+' '+str(log))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: RED suite-harness /mnt/storage/scratch/rocko-r219/agents/rocko/work/queue-219/run-0780-mos1429/suite-harness.tap
+5
View File
@@ -0,0 +1,5 @@
06d7cc2f0ffc544abfb2755f99dce76ca164212c
07802dc57ad36337351da70af15c447bea38ab8b b899095e482cb2b68919efff1e332f272203e017
817eda2289955e683213bab4659e537426e7d021
merge: combine row 219 with landed row 302
@@ -0,0 +1,24 @@
from pathlib import Path
import subprocess,json,re,hashlib
ROOT=Path(__file__).resolve().parents[5];P=ROOT/'agents/rocko/work/queue-219';OUT=P/'r5'
files=sorted(ROOT.glob('packages/*/tests/**/*'))+sorted(ROOT.glob('scripts/test-*.sh'))
files=[f for f in files if f.is_file()]
texts={f:f.read_text(errors='replace') for f in files}
rows=[]
for path in (P/'changed-paths.txt').read_text().splitlines()+['packages/webui/src/public/bus.js']:
base=Path(path).name;hits=[]
for f,text in texts.items():
for n,line in enumerate(text.splitlines(),1):
if path in line or base in line:hits.append(dict(file=str(f.relative_to(ROOT)),line=n,text=line))
rows.append(dict(path=path,hits=hits))
pins=[]
for f,text in texts.items():
for n,line in enumerate(text.splitlines(),1):
if re.search(r'(?<![0-9a-f])[0-9a-f]{64}(?![0-9a-f])',line):pins.append(dict(file=str(f.relative_to(ROOT)),line=n,text=line))
control=ROOT/'packages/bus/tests/channel-proof.test.mjs';t=control.read_text()
m=re.search(r'"packages/webui/src/bus.mjs": "([0-9a-f]{64})"',t);assert m
assert hashlib.sha256((ROOT/'packages/webui/src/bus.mjs').read_bytes()).hexdigest()==m[1]
assert subprocess.check_output(['git','diff','07802dc57','HEAD','--','packages/webui/src/public/bus.js'],cwd=ROOT)==b''
report=dict(head=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip(),paths=rows,hexPins=pins,positiveControl=dict(file=str(control.relative_to(ROOT)),pin='packages/webui/src/bus.mjs',sha256=m[1],matches=True),busJsUnchanged=True)
(OUT/'reader-audit.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps(dict(paths=len(rows)-1,additionalBusJs=True,readerHits=sum(len(x['hits']) for x in rows),hexPinLines=len(pins),positiveControl=True,busJsUnchanged=True)))
+50
View File
@@ -0,0 +1,50 @@
# Full restart r6, sage-3345 / mos-1439
Merge 054da642f9b0680bd7743583e4c67266d7e14e2a, parents 2efd127ce617a9bff8dc3bee3a3b657b0c175c07 and b899095e4.
Tree e86a84a852c9739f157033e3772243b293742983 matches Sage before any run.
No conflicts or manual product/test edits. Same16 paths, new manifest
4a2271ddbd4212324f39e608c3a35a4ae834c045cb0d13a619715017ded0ffef.
Fresh worktree /mnt/storage/scratch/rocko-r219-g3345, node_modules linked immediately.
Runner uses mktemp -d /mnt/storage/scratch/tmp/rXXXXXX and validates
[A-Za-z0-9/.-] for each stage; no previous results carry.
10 whole singles; bus,cli,webui,harness suites; all10 shell scripts
including test-task with Docker;24 mutants;reader audit.
Timeout -k10: singles300s,suites1800s,shells7200s,mutants180s;
7200s exceeds3x the prior conservative25min shell baseline.
Every child has a process group; any red stops, no retry/edit.
Finally remove owned networks including rocko-r219-g3345_default,
sweep, preserve packet evidence, remove node_modules link and this worktree.
## Completed gate, sage-3345
Full run 2026-10-23 10:12:57Z-10:29:22Z on merge054da642f9.
Ten singles183/0/0; bus889/0/1; cli927/0/0; webui373/0/1;
harness151/0/0. Node total2523pass/0fail/2skip,0cancelled.
All10 shell files green,449pass/0fail.24 assertion kills,0timeout kills,
0survivors,0invalid. T15 finished in66.9s with ERR_ASSERTION on the
missing 'no longer available' status; T24 got decision-required instead
of host-creative-role (same exact later-fence mismatch as r4).
Both Bean pins and the sandbox-dependent native-history test ran, no skip.
Skips: X-G3 scratch Gitea1.27.1 unavailable (Node stage uses deadDocker);
WebUI rebuild needs esbuild0.28.2 but host has0.28.1; queue verify/render
requires canonical checkout; test-task live provider cases intentionally
skipped twice (row244, no provider call). GATE-RESULTS.json quotes all5
skip lines, including the duplicated live-provider line.
Reader audit: all16 changed paths plus unchanged public/bus.js,237 reader
hits and22 64-hex lines. None of those pins freezes a changed file. Positive
control channel-proof.test.mjs webui bus.mjs pin matches actual bytes;
public/bus.js stays unchanged. Full audit in reader-audit.json.
16 candidate hashes restored. Driver cwd node/chromium sweep empty.
Owned rocko-r219-gate_default removed; worktree-basename network
rocko-r219-g3345_default already absent (rm attempted as instructed).
No retries, unexpected reds or guard interventions in this run. Results
from prior environmental failures were not carried.
Historical environmental failures remain in run-0780-mos1429/ (harness
underscore TMPDIR) and run-0780-mos1432/ (Docker address pool exhaustion).
Mos1432 required alphanumeric paths; Mos1437/Mos1439 and sage3339/3345
resolved the network issue outside this candidate. No product/test fix
for either. Tests and source remain byte-identical to the checked merge.
@@ -0,0 +1,262 @@
{
"head": "054da642f9b0680bd7743583e4c67266d7e14e2a",
"manifest": "4a2271ddbd4212324f39e608c3a35a4ae834c045cb0d13a619715017ded0ffef",
"start": "2026-10-23T10:12:57.651391+00:00",
"end": "2026-10-23T10:29:22.193573+00:00",
"node": {
"tests": 2525,
"pass": 2523,
"fail": 0,
"cancelled": 0,
"skipped": 2
},
"nodeStages": [
{
"label": "single-bus-creative.test",
"counts": {
"tests": 4,
"pass": 4,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-bus-grant-executor.test",
"counts": {
"tests": 42,
"pass": 42,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-bus-project-runtime.test",
"counts": {
"tests": 3,
"pass": 3,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-bus-threads.test",
"counts": {
"tests": 20,
"pass": 20,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-bus-user-principal.test",
"counts": {
"tests": 23,
"pass": 23,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-cli-launcher.test",
"counts": {
"tests": 34,
"pass": 34,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-cli-thread-launch.test",
"counts": {
"tests": 14,
"pass": 14,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-webui-chat-browser.test",
"counts": {
"tests": 14,
"pass": 14,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-webui-chat-instances.test",
"counts": {
"tests": 4,
"pass": 4,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "single-webui-chat.test",
"counts": {
"tests": 25,
"pass": 25,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "suite-bus",
"counts": {
"tests": 890,
"pass": 889,
"fail": 0,
"cancelled": 0,
"skipped": 1
}
},
{
"label": "suite-cli",
"counts": {
"tests": 927,
"pass": 927,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
},
{
"label": "suite-webui",
"counts": {
"tests": 374,
"pass": 373,
"fail": 0,
"cancelled": 0,
"skipped": 1
}
},
{
"label": "suite-harness",
"counts": {
"tests": 151,
"pass": 151,
"fail": 0,
"cancelled": 0,
"skipped": 0
}
}
],
"shells": [
{
"label": "test-auth",
"passCount": 15,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-conductor",
"passCount": 17,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-config",
"passCount": 55,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-discord",
"passCount": 81,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-extension-package",
"passCount": 18,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-foundation",
"passCount": 44,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-land-work",
"passCount": 86,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-queue",
"passCount": 28,
"failCount": 0,
"exit": 0,
"skips": [
"skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219-g3345) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"
]
},
{
"label": "test-release",
"passCount": 14,
"failCount": 0,
"exit": 0,
"skips": []
},
{
"label": "test-task",
"passCount": 91,
"failCount": 0,
"exit": 0,
"skips": [
"skip live provider cases (row 244: no provider call in the gate)",
"skip live provider cases (row 244: no provider call in the gate)"
]
}
],
"shellPass": 449,
"shellFail": 0,
"mutants": {
"assertionKills": 24,
"timeoutKills": 0,
"survived": 0,
"invalid": 0
},
"skips": [
{
"stage": "suite-bus",
"text": "ok 203 - X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token # SKIP no Docker or no local gitea/gitea:1.27.1; this test never pulls"
},
{
"stage": "suite-webui",
"text": "ok 107 - rebuilding from the source gives the same bytes (needs the pinned esbuild; MOSAIC_REQUIRE_EDITOR_BUILD=1 makes its absence a failure) # SKIP esbuild 0.28.1 is installed at /mnt/storage/src/mosaic-stack/node_modules/esbuild; the editor rebuild needs exactly 0.28.2 (npm ci --ignore-scripts in packages/webui) (set MOSAIC_REQUIRE_EDITOR_BUILD=1 to fail instead of skip)"
},
{
"stage": "test-queue",
"text": "skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219-g3345) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"
},
{
"stage": "test-task",
"text": "skip live provider cases (row 244: no provider call in the gate)"
},
{
"stage": "test-task",
"text": "skip live provider cases (row 244: no provider call in the gate)"
}
]
}
+1
View File
@@ -0,0 +1 @@
2efd127ce617a9bff8dc3bee3a3b657b0c175c07
+832
View File
@@ -0,0 +1,832 @@
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
index 7befba0c0..20da8b84c 100644
--- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs
@@ -4,7 +4,7 @@ import { initializeControl, processingStatus } from '../../processing-control/sr
import { NAME as HARNESS_NAME } from '../../plugins/src/kinds.mjs';
import { loadRequirements } from '../../tasks/src/requirements.mjs';
import { MERGE, LandingRefusal, readLanding } from './landing.mjs';
-import { ID_PATTERN, THREAD_DEFINITION, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';
+import { ID_PATTERN, THREAD_DEFINITIONS, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';
// Kept closed while S1 lands; its vocabulary is the integration authority.
export const ACTIONS = Object.freeze([
@@ -314,7 +314,7 @@ const enrollBinding = (enroll) => ({
});
// r6 E3: the installer line's comment, then the tag's 32 hex digits.
const INSTALLER_COMMENT = 'mosaic-installer-';
-// Row 121 (#1615): an instance of THREAD_DEFINITION (vocabulary.mjs since row 155) talks with the
+// Row 121 (#1615): an instance of THREAD_DEFINITIONS (vocabulary.mjs since row 213) talks with the
// user in threads. Its runs claim a thread, not the role, and every message to or from it names its
// thread (REQ-MSG-1).
// Row 146 (#1640): the sender of the bus's notice to launch.by. A role instance name matches the
@@ -610,7 +610,7 @@ export class Broker {
);
}
#threaded(business, role) {
- return this.#business(business).roles[role]?.definition === THREAD_DEFINITION;
+ return THREAD_DEFINITIONS.includes(this.#business(business).roles[role]?.definition);
}
// Row 264a (#1769): a claim holds one session of a thread, and each session has its own holder.
#sessionHolder(session) {
@@ -6171,24 +6171,27 @@ export class Broker {
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
}
- // A thread belongs to the user who made it and to the business's one thread instance.
+ // A thread belongs to the user who made it and to one declared thread instance.
// Row 177 (#1675): a tester's thread is owned by `tester`, so neither reaches the other's.
case 'thread.create': {
if (!s.tester && !s.user) this.#human(s);
- keys(a, ['project', 'name'], ['project', 'name']);
+ keys(a, ['project', 'name', 'instance'], ['project', 'name']);
if (!name(a.name)) fail('invalid-request');
+ const instances = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
+ const instance = a.instance === undefined
+ ? (instances.length === 1 ? instances[0] : null)
+ : identifier(a.instance);
// FED R3 (design 3.5): a user's grant must cover the project and the thread instance, checked
// before the project is looked up, so an ungranted id reads the same whether it exists or not.
if (s.user) {
- const threaded = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
- if (threaded.length !== 1 || !this.#granted(s, 'thread.create', identifier(a.project), threaded[0])) fail('access-refused');
+ if (!instance || !instances.includes(instance) || !this.#granted(s, 'thread.create', identifier(a.project), instance))
+ fail('access-refused');
}
if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', identifier(a.project), s.business))
fail('project-not-found');
- // The bus never picks between instances; the business file declares exactly one.
- const instances = Object.keys(b.roles).filter((r) => this.#threaded(s.business, r));
- if (!instances.length) fail('thread-instance-missing');
- if (instances.length > 1) fail('thread-instance-ambiguous');
+ // The human selects an instance; omission is compatible only with one candidate.
+ if (!instances.length || (a.instance !== undefined && !instances.includes(instance))) fail('thread-instance-missing');
+ if (a.instance === undefined && instances.length > 1) fail('thread-instance-ambiguous');
if (this.#store.get('SELECT 1 FROM threads WHERE project=? AND name=?', a.project, a.name)) fail('thread-exists');
const tid = randomUUID();
this.#store.run(
@@ -6197,13 +6200,13 @@ export class Broker {
this.#now(),
s.business,
a.project,
- instances[0],
+ instance,
this.#owner(s),
a.name,
);
// Row 264a (#1769, D3): every thread has a default session from the start.
- this.#newSession(s, { id: tid, business: s.business, instance: instances[0], owner: this.#owner(s) }, 'default', 'default', true);
- return { id: tid, instance: instances[0] };
+ this.#newSession(s, { id: tid, business: s.business, instance, owner: this.#owner(s) }, 'default', 'default', true);
+ return { id: tid, instance };
}
// Row 177 (#1675): each lists its own threads. The human's list leaves out the tester's,
// and the tester's lists only its own (row 175 listed it the business human's).
diff --git a/packages/bus/tests/creative.test.mjs b/packages/bus/tests/creative.test.mjs
index 6cfbf1cd6..6d421ba19 100644
--- a/packages/bus/tests/creative.test.mjs
+++ b/packages/bus/tests/creative.test.mjs
@@ -44,15 +44,18 @@ function setup(t) {
const caps = {};
caps.human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
const project = b.request(caps.human, { verb: 'project.create', args: { name: 'demo' } }).id;
- const thread = b.request(caps.human, { verb: 'thread.create', args: { project, name: 'main' } }).id;
+ const thread = b.request(caps.human, { verb: 'thread.create', args: { project, name: 'main', instance: 'assistant' } }).id;
+ const threads = { assistant: thread };
for (const role of Object.keys(roles)) {
- caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi', ...(role === 'assistant' ? { thread } : {}) });
+ if (['bean', 'quill'].includes(role))
+ threads[role] = b.request(caps.human, { verb: 'thread.create', args: { project, name: role, instance: role } }).id;
+ caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi', ...(threads[role] ? { thread: threads[role] } : {}) });
b.request(caps[role], { verb: 'role.claim' });
}
caps.tester = b.bindTester({ business: 'demo' });
const call = (who, verb, args = {}) => b.request(caps[who], { verb, args });
const count = (sql) => store.get(sql).n;
- return { call, count, store };
+ return { call, count, store, threads };
}
const raise = { action: 'policy.change', question: 'Change?', options: [{ key: 'yes', text: 'Yes' }, { key: 'no', text: 'No' }], recommendation: 'yes', blocking: false };
@@ -72,7 +75,7 @@ test('M6: a creative raise refuses decision-not-allowed, cited or not, and no de
});
test('a creative instance sends only to the user and hears only from the user', (t) => {
- const { call, count } = setup(t);
+ const { call, count, threads } = setup(t);
for (const who of ['bean', 'quill']) {
call(who, 'message.send', { to: 'human', body: 'hello' });
for (const to of ['chief', 'assistant', 'pm', 'bean', 'quill'].filter((n) => n !== who)) {
@@ -81,7 +84,7 @@ test('a creative instance sends only to the user and hears only from the user',
for (const from of ['chief', 'pm']) {
assert.throws(() => call(from, 'message.send', { to: who, body: 'hello' }), { code: 'message-sender-refused' }, `${from} to ${who}`);
}
- call('human', 'message.send', { to: who, body: 'hello' });
+ call('human', 'thread.send', { thread: threads[who], body: 'hello' });
}
assert.equal(count("SELECT count(*) n FROM messages WHERE from_role IN ('bean','quill')"), 2);
assert.equal(count("SELECT count(*) n FROM messages WHERE to_role IN ('bean','quill')"), 2);
diff --git a/packages/bus/tests/grant-executor.test.mjs b/packages/bus/tests/grant-executor.test.mjs
index e449b7d77..46235d16b 100644
--- a/packages/bus/tests/grant-executor.test.mjs
+++ b/packages/bus/tests/grant-executor.test.mjs
@@ -1047,7 +1047,10 @@ test('sage-1908: once the agent of an applied grant is a creative role, host.run
const f = await setup(t);
assert.equal((await f.approve()).end.action, 'grant.applied');
const r = await restart(t, f, creativeOps(f.business));
- const cap = r.broker.bindLaunch({ business: 'demo', role: 'ops', run: 'ops-run', harness: 'pi', address: 'ops' });
+ const human = r.broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
+ const project = r.broker.request(human, { verb: 'project.create', args: { name: 'host grant check' } }).id;
+ const thread = r.broker.request(human, { verb: 'thread.create', args: { project, name: 'ops', instance: 'ops' } }).id;
+ const cap = r.broker.bindLaunch({ business: 'demo', role: 'ops', run: 'ops-run', harness: 'pi', address: 'ops', thread });
r.broker.request(cap, { verb: 'role.claim' });
const executor = hostExecutor({ credentials: r.credentials, root: join(f.dir, 'host-run') });
const before = f.requests().length;
diff --git a/packages/bus/tests/project-runtime.test.mjs b/packages/bus/tests/project-runtime.test.mjs
index a6086e56c..7275ad902 100644
--- a/packages/bus/tests/project-runtime.test.mjs
+++ b/packages/bus/tests/project-runtime.test.mjs
@@ -32,7 +32,8 @@ test('Broker keeps the sandbox client import closure: only the listed imports, n
// (#1692, D2.4) reads unhireableAction, the hire veto's action name.
// Row 236 (#1742, #436b) reads giteaName and parseGiteaUrl for the Gitea grant path.
// FED R3 r5 (row 274, #414) reads covers, a user's grant check, from it.
- "import { ID_PATTERN, THREAD_DEFINITION, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';",
+ // Row 219 reads THREAD_DEFINITIONS from the same vocabulary for creative thread admission.
+ "import { ID_PATTERN, THREAD_DEFINITIONS, END_PROTECTED, forkProblem, forkUrl, credentialKind, validArgv, commandAllowed, HOST_ID, CREATIVE_DEFINITIONS, unhireableAction, giteaName, parseGiteaUrl, covers } from '../../business/src/vocabulary.mjs';",
]);
assert.doesNotMatch(source, /\bimport\s*\(/);
assert.doesNotMatch(readFileSync(new URL('../src/landing.mjs', import.meta.url), 'utf8'), /\bimport\b/);
diff --git a/packages/bus/tests/threads.test.mjs b/packages/bus/tests/threads.test.mjs
index 27193a745..ae44fb66a 100644
--- a/packages/bus/tests/threads.test.mjs
+++ b/packages/bus/tests/threads.test.mjs
@@ -601,3 +601,56 @@ test('project.create keeps unrelated names with and without a repository', t =>
{ id: linked.id, name: 'work', repo: 'stack' },
]);
});
+
+test('C2-09a: explicit creative instances bind independent threads, default sessions and holders', t => {
+ const creative = ['companion', 'ideas', 'research', 'proofreader', 'fact-checker', 'continuity'];
+ const extra = Object.fromEntries(creative.map((definition, i) => [`writer${i}`, { definition, authority: { withinRole: ['message.send'], crossRole: [] } }]));
+ const ctx = setup(t, demo({ roles: { ...roles, ...extra }, definitions: {
+ ...definitions, ...Object.fromEntries(creative.map(definition => [definition, { messaging: shipped(definition).messaging }])),
+ } }));
+ const project = ctx.call('human', 'project.create', { name: 'Writing' }).id;
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: 'ambiguous' }), { code: 'thread-instance-ambiguous' });
+ for (const [instance, role] of Object.entries(extra)) {
+ const receipt = ctx.call('human', 'thread.create', { project, name: instance, instance });
+ assert.equal(receipt.instance, instance);
+ assert.equal(ctx.store.get('SELECT instance FROM threads WHERE id=?', receipt.id).instance, instance);
+ assert.equal(ctx.store.get('SELECT instance FROM sessions WHERE thread=?', receipt.id).instance, instance);
+ ctx.launch(instance, instance, { thread: receipt.id });
+ assert.equal(ctx.claim(instance).thread, receipt.id);
+ const sent = ctx.call('human', 'thread.send', { thread: receipt.id, body: `For ${instance}` });
+ const read = ctx.call(instance, 'message.receive');
+ assert.deepEqual(read.map(m => m.id), [sent.id], role.definition);
+ assert.equal(ctx.threadOf(sent.id), receipt.id);
+ assert.equal(ctx.store.get('SELECT count(*) n FROM role_claims WHERE role=?', instance).n, 0);
+ assert.throws(() => ctx.call('human', 'message.send', { to: instance, body: 'No implicit thread' }),
+ { code: 'thread-required-use-thread-send' });
+ }
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), creative.length);
+});
+
+test('C2-09a: explicit missing, non-thread and malformed selectors create no thread or session', t => {
+ const ctx = setup(t);
+ const project = ctx.call('human', 'project.create', { name: 'Writing' }).id;
+ for (const instance of ['not-declared', 'pm', 'coder'])
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: instance, instance }), { code: 'thread-instance-missing' });
+ for (const instance of [null, 7, {}, [], '', 'bad name'])
+ assert.throws(() => ctx.call('human', 'thread.create', { project, name: 'bad', instance }), { code: 'invalid-request' });
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), 0);
+ assert.equal(ctx.count('SELECT count(*) n FROM sessions'), 0);
+ const legacy = ctx.call('human', 'thread.create', { project, name: 'Default' });
+ assert.equal(legacy.instance, 'assistant');
+});
+
+
+test('C2-09a: creative bindLaunch without a thread refuses thread-required', t => {
+ const creative = ['companion', 'ideas', 'research', 'proofreader', 'fact-checker', 'continuity'];
+ const extra = Object.fromEntries(creative.map((definition, i) => [`creative${i}`, { definition, authority: { withinRole: ['message.send'], crossRole: [] } }]));
+ const ctx = setup(t, demo({ roles: { ...roles, ...extra }, definitions: {
+ ...definitions, ...Object.fromEntries(creative.map(definition => [definition, {}])),
+ } }));
+ for (const instance of Object.keys(extra)) {
+ assert.throws(() => ctx.launch(`unthreaded-${instance}`, instance), { code: 'thread-required' });
+ }
+ assert.equal(ctx.count('SELECT count(*) n FROM threads'), 0);
+ assert.equal(ctx.count('SELECT count(*) n FROM role_claims'), 0);
+});
diff --git a/packages/bus/tests/user-principal.test.mjs b/packages/bus/tests/user-principal.test.mjs
index 4dcd1ebeb..8471ddf85 100644
--- a/packages/bus/tests/user-principal.test.mjs
+++ b/packages/bus/tests/user-principal.test.mjs
@@ -23,7 +23,7 @@ const grant = (over = {}) => ({
});
// `processing` stands in for the halt switch, as lifecycle-verbs.test.mjs's T15 does.
-function setup(t, { grants = [grant()], processing = null } = {}) {
+function setup(t, { grants = [grant()], processing = null, threadRoles = {} } = {}) {
const root = mkdtempSync(join(tmpdir(), 'bus-user-principal-'));
const store = new Store(root);
t.after(() => {
@@ -40,6 +40,7 @@ function setup(t, { grants = [grant()], processing = null } = {}) {
roles: {
chief: { definition: 'chief-of-staff', authority: { withinRole: ['message.send'], crossRole: [] } },
assistant: { definition: 'assistant', authority: { withinRole: ['message.send'], crossRole: [] } },
+ ...threadRoles,
},
definitions: { 'chief-of-staff': {}, assistant: { messaging: shipped('assistant').messaging } },
},
@@ -394,3 +395,21 @@ test('R3 r4: the chief\'s hire refuses an instance named user with the code it g
test('R3 r4: a non-function access throws before the store is read', () => {
assert.throws(() => new Broker({ store: null, businesses: { demo: {} }, access: 'access.json' }), /access must be a function/);
});
+
+test('C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project', t => {
+ const ctx = setup(t, { threadRoles: { bean: { definition: 'companion', authority: { withinRole: ['message.send'], crossRole: [] } } } });
+ const project = ctx.call('human', 'project.create', { name: 'shared' }).id;
+ const privateProject = ctx.call('human', 'project.create', { name: 'private' }).id;
+ ctx.access.doc.grants = [grant({ projects: [project] })];
+ ctx.user('alice');
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'blocked bean', instance: 'bean' }), 'access-refused');
+ for (const id of [privateProject, '00000000-0000-4000-8000-000000000000'])
+ assert.equal(ctx.code('alice', 'thread.create', { project: id, name: 'blocked project', instance: 'assistant' }), 'access-refused');
+ assert.equal(ctx.store.get('SELECT count(*) n FROM threads').n, 0);
+ const allowed = ctx.call('alice', 'thread.create', { project, name: 'assistant work', instance: 'assistant' });
+ assert.equal(allowed.instance, 'assistant');
+ ctx.access.doc.grants = [grant({ projects: [project], instances: ['bean'] })];
+ const bean = ctx.call('alice', 'thread.create', { project, name: 'bean work', instance: 'bean' });
+ assert.equal(bean.instance, 'bean');
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'old instance', instance: 'assistant' }), 'access-refused');
+});
diff --git a/packages/cli/src/launcher.mjs b/packages/cli/src/launcher.mjs
index f1ee4043e..cb9631ddf 100644
--- a/packages/cli/src/launcher.mjs
+++ b/packages/cli/src/launcher.mjs
@@ -53,7 +53,7 @@
// session.json names it as gitSocket.
//
// Thread sessions (row 121, #1615). A thread instance (an instance whose
-// role definition is the assistant) runs once per thread, never on its own:
+// role definition is in THREAD_DEFINITIONS) runs once per thread, never on its own:
// a launch from a session or --pm refuses thread-required. Every tick
// (threadTickMs) the host asks the broker for the business's threads
// (launch op threadsState) and:
@@ -142,6 +142,7 @@ import { createServer } from "node:net";
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync } from "node:fs";
import { dirname, join, relative } from "node:path";
import { configDir, loadBusiness, resolveInstance, systemVars } from "../../business/src/index.mjs";
+import { THREAD_DEFINITIONS } from "../../business/src/vocabulary.mjs";
import { limitHit } from "../../business/src/launch.mjs";
import { BundleError, buildBundle, hasFallback, readSkills, sessionModel } from "../../harness/src/bundle.mjs";
import { createLoader } from "../../plugins/src/loader.mjs";
@@ -179,8 +180,6 @@ const STOP_TIMEOUT_MS = 30000;
const LINE_MAX = 4096;
// How often the host asks for the threads' state; a poll period, not a limit.
const THREAD_TICK_MS = 2000;
-// The broker's thread instance definition (packages/bus/src/broker.mjs).
-const THREAD_DEFINITION = "assistant";
// Row 194: the broker's LIFECYCLE_REQUEST_MS. A regenerate's old run must be gone by then.
const LIFECYCLE_REQUEST_MS = 60000;
@@ -334,7 +333,7 @@ export function createLauncher({
try { b=loadBusiness(host.business,{dir:configDir(env),rolesDir:rolesDir(env)}); } catch(e){throw new Refusal("organization-refused",e.message);}
if (!b.launch) throw new Refusal("launch-not-configured", `business ${b.id} has no launch block`);
if (!(pm ? instance === b.launch.by : b.launch.instances.includes(instance) || (human && Object.hasOwn(b.roles, instance)))) throw new Refusal("instance-not-listed");
- const threaded = b.roles[instance]?.definition === THREAD_DEFINITION;
+ const threaded = THREAD_DEFINITIONS.includes(b.roles[instance]?.definition);
if (threaded && !thread) throw new Refusal("thread-required", `${instance} runs only for a thread`);
if (!threaded && thread) throw new Refusal("thread-not-allowed", `${instance} isn't a thread instance`);
if (threaded && !session) throw new Refusal("session-required", `${instance} runs for one session of a thread`);
@@ -1014,7 +1013,7 @@ export function createLauncher({
function ending(r) {
const runs = r.runs ?? [];
const at = Date.parse(r.at);
- const threaded = b.roles[r.instance]?.definition === THREAD_DEFINITION;
+ const threaded = THREAD_DEFINITIONS.includes(b.roles[r.instance]?.definition);
return registry.sessions.filter((s) => s.business === b.id && (runs.includes(s.run) ||
(!threaded && s.instance === r.instance && (spawnedAt.get(s.run) ?? Date.parse(s.startedAt)) <= at)));
}
diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs
index 2202c7753..baabd843e 100644
--- a/packages/cli/tests/launcher.test.mjs
+++ b/packages/cli/tests/launcher.test.mjs
@@ -773,21 +773,34 @@ test("row 213 a creative instance's persona is read from <configDir>/personas: o
const dir = join(given.root, "config", "personas", "acme");
mkdirSync(dir, { recursive: true, mode: 0o700 });
writeFileSync(join(dir, "bean.md"), "Not the pinned text.\n", { mode: 0o600 });
- const { f, host, launcher } = await setup(t, undefined, given);
- // The chief of staff holds role.launch; a test chief session bound to this process asks.
- const chief = await host.bindLaunch({ business: "acme", role: "chief", run: "chief-test", harness: "pi", pid: process.pid, startTime: startTimeOf(process.pid) });
- await new Client({ path: host.path, cap: chief.cap }).call("role.claim");
- // The bundle's refusal comes back as launch-failed, as webPlan's does, with its reason in the log.
- assert.deepEqual(await launcher.fromSession(chief.cap, "bean"), { ok: false, error: "launch-failed" });
- const [e] = log(f);
- assert.deepEqual([e.event, e.code, log(f).length], ["refused", "launch-failed", 1]);
+ // Row 219: creative instances launch from their thread, not from a chief's standalone request.
+ // The mismatch occurs before bindLaunch, so only the host's thread-state snapshot is a fixture.
+ const thread = 'persona-thread';
+ const SESSION = 'persona-session';
+ const { f, launcher } = await setup(t, undefined, given, { wrap: host => ({
+ ...host,
+ op: message => message.op === 'threadsState'
+ ? Promise.resolve([{ thread, session: SESSION, instance: 'bean', holder: null, pending: 1 }])
+ : host.op(message),
+ }) });
+ // The thread path records row 270's run-start count before the bundle's persona refusal.
+ await launcher.tick();
+ const runs = readdirSync(join(f.dataRoot, "launches", "acme"));
+ const entries = log(f);
+ assert.equal(entries.length, 2);
+ const { at, ...compactions } = entries[0];
+ assert.deepEqual(compactions, { event: 'compactions', instance: 'bean', thread, session: SESSION, run: runs[0], count: 0, known: true, reason: null });
+ const e = entries[1];
+ assert.deepEqual([e.event, e.code], ["refused", "launch-failed"]);
+ assert.equal(e.thread, thread);
assert.equal(e.why, `instance bean: persona file ${join(dir, "bean.md")} isn't the file persona.sha256 names`);
assert.ok(!JSON.stringify(log(f)).includes("PERSONA-MARKER-d2"));
assert.deepEqual(readSessions(f.dataRoot), []);
// The run directory exists by then (BUILD.md residual); the persona is read before any bundle file.
- const runs = readdirSync(join(f.dataRoot, "launches", "acme"));
assert.equal(runs.length, 1);
- assert.deepEqual(readdirSync(join(f.dataRoot, "launches", "acme", runs[0])), ["session"]);
+ assert.deepEqual(readdirSync(join(f.dataRoot, "launches", "acme", runs[0])), []);
+ assert.equal(existsSync(join(f.dataRoot, "threads", "acme", thread, "sessions", SESSION, "session")), true);
+ assert.equal(existsSync(join(f.dataRoot, "threads", "acme", thread, "session")), false);
});
test("row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts", { timeout: 30000 }, async (t) => {
diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs
index 0d207a0a0..fa3adb866 100644
--- a/packages/cli/tests/thread-launch.test.mjs
+++ b/packages/cli/tests/thread-launch.test.mjs
@@ -396,6 +396,22 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t
assert.deepEqual(launchLog(f).map((e) => [e.event, e.instance, e.code]), [["refused", "assistant", "thread-required"]]);
});
+// Admission refuses before bundle construction or a sandbox is needed.
+test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => {
+ const prepared = prepare(t, { edit: doc => {
+ doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' };
+ doc.launch.instances.push('bean');
+ return doc;
+ } });
+ const { f } = prepared;
+ const host = await start(t, prepared);
+ const chief = await host.host.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-bean-refusal', harness: 'pi', pid: process.pid, startTime: startTimeOf(process.pid) });
+ await new Client({ path: host.host.path, cap: chief.cap }).call('role.claim');
+ assert.deepEqual(await host.launcher.fromSession(chief.cap, 'bean'), { ok: false, error: 'thread-required' });
+ assert.deepEqual(readSessions(f.dataRoot), []);
+ assert.deepEqual(launchLog(f).map(e => [e.event, e.instance, e.code]), [['refused', 'bean', 'thread-required']]);
+});
+
// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the token-limit check switched on, with the
// assistant's limit at 0, so it is at its limit before any turn. The real check runs through the
// bus's plug-in loader.
@@ -443,3 +459,73 @@ test("REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn:
assert.deepEqual(threadRuns(f, A), []);
assert.equal(host.runtime.threadsState("acme").find((x) => x.thread === A).holder, null);
});
+
+test('C2-09a: creative thread messages start distinct runs and keep each instance history separate', { skip, timeout: 90000 }, async t => {
+ const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => {
+ const vars = { ...doc.roles.assistant.vars };
+ delete doc.roles.assistant;
+ doc.roles.bean = { definition: 'companion', vars: { ...vars } };
+ doc.roles.chet = { definition: 'continuity', vars: { ...vars } };
+ doc.launch.instances = ['coder', 'bean', 'chet'];
+ return doc;
+ } });
+ const host = await start(t, prepared), { f } = prepared;
+ const project = host.call('project.create', { name: 'Writing' }).id;
+ const bean = host.call('thread.create', { project, name: 'Bean', instance: 'bean' }).id;
+ const chet = host.call('thread.create', { project, name: 'Chet', instance: 'chet' }).id;
+ const send = async (thread, instance, body) => {
+ const message = host.call('thread.send', { thread, body });
+ const outcome = await until(() => host.call('thread.messages', { thread }).find(m => m.in_reply_to === message.id) ||
+ launchLog(f).find(row => row.thread === thread && row.event === 'refused'));
+ assert.notEqual(outcome.event, 'refused', `creative thread launch refused: ${outcome.code}`);
+ const reply = outcome;
+ assert.equal(reply.from_role, instance);
+ assert.equal(reply.to_role, 'human');
+ return JSON.parse(reply.body.match(/THREAD (\{.*\})/)[1]);
+ };
+ assert.deepEqual((await send(bean, 'bean', 'Bean input')).before, []);
+ assert.deepEqual((await send(chet, 'chet', 'Chet input')).before, []);
+ assert.deepEqual((await send(bean, 'bean', 'Bean again')).before, ['Bean input']);
+ assert.equal(readFileSync(join(f.dataRoot, 'threads', 'acme', bean, 'sessions', host.runtime.threadsState('acme').find(x => x.thread === bean).session, 'session', 'history'), 'utf8'), 'Bean input\nBean again\n');
+ assert.equal(readFileSync(join(f.dataRoot, 'threads', 'acme', chet, 'sessions', host.runtime.threadsState('acme').find(x => x.thread === chet).session, 'session', 'history'), 'utf8'), 'Chet input\n');
+ assert.equal(existsSync(join(f.dataRoot, 'threads', 'acme', bean, 'session')), false);
+ assert.equal(existsSync(join(f.dataRoot, 'threads', 'acme', chet, 'session')), false);
+ const launches = launchLog(f).filter(row => row.event === 'launch');
+ assert.ok(launches.some(row => row.instance === 'bean' && row.thread === bean));
+ assert.ok(launches.some(row => row.instance === 'chet' && row.thread === chet));
+ const chief = await host.host.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-creative-test', harness: 'pi', pid: process.pid, startTime: startTimeOf(process.pid) });
+ await new Client({ path: host.host.path, cap: chief.cap }).call('role.claim');
+ assert.deepEqual(await host.launcher.fromSession(chief.cap, 'bean'), { ok: false, error: 'thread-required' });
+});
+
+test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => {
+ const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => {
+ doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' };
+ delete doc.roles.assistant;
+ doc.launch.instances = ['coder', 'bean'];
+ return doc;
+ } });
+ let requests = [];
+ const host = await start(t, prepared, { ops: () => ({
+ lifecycleRequests: () => requests,
+ lifecycleTake: () => ({ ...requests[0], go: true }),
+ lifecycleDone: () => { requests = []; return {}; },
+ }) });
+ const { f } = prepared;
+ const project = host.call('project.create', { name: 'Writing' }).id;
+ const threads = ['one', 'two'].map(name => host.call('thread.create', { project, name, instance: 'bean' }).id);
+ for (const thread of threads) {
+ const message = host.call('thread.send', { thread, body: 'Stay in this thread' });
+ const outcome = await until(() => host.call('thread.messages', { thread }).find(row => row.in_reply_to === message.id) ||
+ launchLog(f).find(row => row.thread === thread && row.event === 'refused'));
+ assert.notEqual(outcome.event, 'refused', `creative thread launch refused: ${outcome.code}`);
+ }
+ const runs = threads.map(thread => launchLog(f).find(row => row.thread === thread && row.event === 'launch').run);
+ requests = [{ request: 'one-thread-end', state: 'open', business: 'acme', action: 'lifecycle.end',
+ instance: 'bean', runs: [runs[0]], by: f.doc.human, at: new Date(Date.now() + 1000).toISOString() }];
+ await host.launcher.tick();
+ await until(() => launchLog(f).some(row => row.event === 'lifecycle-end' && row.run === runs[0]));
+ assert.deepEqual(launchLog(f).filter(row => row.event === 'lifecycle-end').map(row => row.run), [runs[0]]);
+ await until(() => !readSessions(f.dataRoot).some(row => row.run === runs[0]));
+ assert.ok(readSessions(f.dataRoot).some(row => row.run === runs[1]));
+});
diff --git a/packages/webui/src/chat-http.mjs b/packages/webui/src/chat-http.mjs
index 7980c1442..63d3ca974 100644
--- a/packages/webui/src/chat-http.mjs
+++ b/packages/webui/src/chat-http.mjs
@@ -80,7 +80,8 @@ export async function chatHTTP(req, res, { path, search, chat, named, send, prev
(Object.hasOwn(v, 'repo') && (typeof v.repo !== 'string' || !KEY.test(v.repo)))) throw Error();
action = 'createProject'; args = v;
} else if (path === '/api/chat/threads') {
- if (!exact(v, ['project', 'name']) || !id(v.project) || !name(v.name)) throw Error();
+ if (!exact(v, ['project', 'name', 'instance'], ['project', 'name']) || !id(v.project) || !name(v.name) ||
+ (Object.hasOwn(v, 'instance') && (typeof v.instance !== 'string' || !KEY.test(v.instance)))) throw Error();
action = 'createThread'; args = v;
} else if (match?.[2] === 'messages') {
if (!exact(v, ['body']) || typeof v.body !== 'string' || !v.body.trim() || v.body.length > 32768) throw Error();
diff --git a/packages/webui/src/chat.mjs b/packages/webui/src/chat.mjs
index 1b0a41dce..5a1ed8bf1 100644
--- a/packages/webui/src/chat.mjs
+++ b/packages/webui/src/chat.mjs
@@ -25,7 +25,7 @@ export function createChat({ call, owner = null }) {
projects: () => channel('project.list', {}),
createProject: ({ name, repo }) => channel('project.create', { name, ...(repo === undefined ? {} : { repo }) }),
threads: ({ project } = {}) => channel('thread.list', project === undefined ? {} : { project }),
- createThread: ({ project, name }) => channel('thread.create', { project, name }),
+ createThread: ({ project, name, instance }) => channel('thread.create', { project, name, ...(instance === undefined ? {} : { instance }) }),
messages: ({ thread, after, limit }) => channel('thread.messages', { thread, ...(after ? { after } : {}), limit }),
send: ({ thread, body }) => channel('thread.send', { thread, body, class: 'REQUEST' }),
open: ({ thread }) => channel('thread.open', { thread }),
diff --git a/packages/webui/src/public/chat.js b/packages/webui/src/public/chat.js
index a6b805c1b..15066bdc6 100644
--- a/packages/webui/src/public/chat.js
+++ b/packages/webui/src/public/chat.js
@@ -14,7 +14,7 @@
return states.get(id);
};
const form = key => {
- if (!forms.has(key)) forms.set(key, { name: '', repo: '', receipt: '', pending: false });
+ if (!forms.has(key)) forms.set(key, { name: '', repo: '', instance: '', instanceChosen: false, receipt: '', pending: false });
return forms.get(key);
};
class ChatError extends Error {
@@ -58,7 +58,11 @@
function creation(kind, key) {
const f = form(key), project = kind === 'project';
const repo = project && current.repos.length ? `<label>Repository (optional)<select id="chat-repo" data-chat-field="repo"><option value="">No repository link</option>${current.repos.map(r => `<option value="${esc(r)}"${r === f.repo ? ' selected' : ''}>${txt(r)}</option>`).join('')}</select></label>` : '';
- return `<form id="chat-${kind}-form" class="chat-create" data-key="${esc(key)}"><h2>New ${kind}</h2><label>Name<input id="chat-${kind}-name" data-chat-field="name" value="${esc(f.name)}" maxlength="200" required${disabled(f.pending)}></label>${repo}<button class="btn" type="submit"${disabled(f.pending)}>${f.pending ? 'Creating…' : `Create ${kind}`}</button><p role="status">${txt(f.receipt)}</p></form>`;
+ const choices = current.instances ?? [];
+ const picker = project ? '' : `<label>Thread with<select id="chat-instance" data-chat-field="instance" required${disabled(f.pending || !!current.instanceError || !choices.length)}><option value="">Choose who this thread is with</option>${choices.map(row => `<option value="${esc(row.instance)}"${row.instance === f.instance ? ' selected' : ''}>${txt(row.name === row.instance ? row.instance : `${row.name} (${row.instance})`)}</option>`).join('')}</select></label>`;
+ const unavailable = !project && (!!current.instanceError || !choices.some(row => row.instance === f.instance));
+ const status = !project && current.instanceError ? current.instanceError : f.receipt || (!project && !choices.length ? 'No thread instances are configured.' : '');
+ return `<form id="chat-${kind}-form" class="chat-create" data-key="${esc(key)}"><h2>New ${kind}</h2><label>Name<input id="chat-${kind}-name" data-chat-field="name" value="${esc(f.name)}" maxlength="200" required${disabled(f.pending)}></label>${repo}${picker}<button class="btn" type="submit"${disabled(f.pending || unavailable)}>${f.pending ? 'Creating…' : `Create ${kind}`}</button><p role="status">${txt(status)}</p></form>`;
}
function threads(rows) {
return rows.length ? `<ul class="chat-thread-list">${rows.map(t => `<li>${link('threads', t.id, t.name)} <span class="muted">${txt(t.instance)}</span></li>`).join('')}</ul>` : '<p>No threads yet.</p>';
@@ -106,7 +110,7 @@
const f = document.getElementById(keep.field.id);
if (f && !f.disabled) {
f.focus({ preventScroll: true });
- if (keep.field.start !== null) f.setSelectionRange(keep.field.start, keep.field.end);
+ if (keep.field.start != null && typeof f.setSelectionRange === 'function') f.setSelectionRange(keep.field.start, keep.field.end);
}
} else if (navigated) c.container.querySelector('h1')?.focus({ preventScroll: true });
}
@@ -126,7 +130,7 @@
paint(); return;
}
const old = current;
- current = { container, kind, id, canSend, projects: old?.projects ?? [], threads: old?.threads ?? [], repos: old?.repos ?? [], error: '', refused: false };
+ current = { container, kind, id, canSend, projects: old?.projects ?? [], threads: old?.threads ?? [], repos: old?.repos ?? [], instances: old?.instances ?? [], instanceError: kind === 'project' ? 'Reading thread instances…' : '', error: '', refused: false };
if (navigated || !old) container.innerHTML = '<p class="chat-loading" role="status">Reading chat…</p>';
try {
const s = kind === 'thread' ? state(id) : null;
@@ -147,6 +151,25 @@
s.receipt = 'Message shown in this conversation.'; s.receiptId = null;
}
}
+ if (kind === 'project') {
+ try {
+ const listed = await request('instances', undefined, signal);
+ if (g !== generation || out()) return;
+ if (!listed.rows.every(row => typeof row?.instance === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,159}$/.test(row.instance) && typeof row.name === 'string'))
+ throw new ChatError('invalid-response');
+ current.instances = listed.rows; current.instanceError = '';
+ const f = form(id);
+ if (f.instance && !listed.rows.some(row => row.instance === f.instance)) {
+ f.instance = ''; f.instanceChosen = true;
+ f.receipt = 'The selected instance is no longer available. Choose who this thread is with.';
+ }
+ if (!f.instanceChosen && !f.instance && listed.rows.length === 1) f.instance = listed.rows[0].instance;
+ } catch (err) {
+ if (err.name === 'AbortError' || out() || g !== generation) return;
+ current.instances = [];
+ current.instanceError = "Couldn't read the thread instances; reload to try again";
+ }
+ }
// Repository choices are optional metadata; an unavailable read must not block unlinked chat.
if (kind === 'list') {
try {
@@ -179,7 +202,11 @@
if (!current || out()) return;
if (event.target.id === 'chat-body') state(current.id).draft = event.target.value;
const f = event.target.closest('.chat-create');
- if (f && event.target.dataset.chatField) form(f.dataset.key)[event.target.dataset.chatField] = event.target.value;
+ if (f && event.target.dataset.chatField) {
+ const draft = form(f.dataset.key), field = event.target.dataset.chatField;
+ draft[field] = event.target.value;
+ if (field === 'instance') { draft.instanceChosen = true; draft.receipt = ''; paint(); }
+ }
});
document.addEventListener('scroll', event => {
if (event.target.matches?.('.chat-log') && current?.kind === 'thread') {
@@ -232,7 +259,8 @@
void write('send', id, { body: state(id).draft }, `threads/${id}/messages`);
} else if (event.target.classList.contains('chat-create')) {
const key = event.target.dataset.key, f = form(key), project = key === 'projects';
- const value = project ? { name: f.name, ...(f.repo ? { repo: f.repo } : {}) } : { project: key, name: f.name };
+ if (!project && (current.instanceError || !(current.instances ?? []).some(row => row.instance === f.instance))) return;
+ const value = project ? { name: f.name, ...(f.repo ? { repo: f.repo } : {}) } : { project: key, name: f.name, instance: f.instance };
void write(project ? 'createProject' : 'createThread', key, value, project ? 'projects' : 'threads');
}
});
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
index cd51a0b6e..56fcdd4e1 100644
--- a/packages/webui/src/serve.mjs
+++ b/packages/webui/src/serve.mjs
@@ -8,6 +8,7 @@ import { BusReadError, busStatus as coreBusStatus, subjectOk, sendOutcome } from
// Row 176, sage-1111: HTTP-only tester refusals; the row-164 transport pin stays untouched.
const busStatus = code => ['tester-off', 'tester-required'].includes(code) ? 403 : coreBusStatus(code);
import { ReadError, readStatus, fieldTable } from './reads.mjs';
+import { THREAD_DEFINITIONS } from '../../business/src/vocabulary.mjs';
import { chatHTTP } from './chat-http.mjs';
import { processingHTTP } from './processing-http.mjs';
@@ -740,6 +741,25 @@ export async function startServer({ host = '127.0.0.1', port = 7330, board = DEF
if (req.method !== 'POST') return json(res, 405, { error: 'method not allowed' });
return busSend(req, res, { send, preview: signer, log });
}
+ // Row 219: labels only, from this listener's own human or tester bus, after auth above.
+ if (path === '/api/chat/instances') {
+ if (req.method !== 'GET') return json(res, 405, { error: 'method-not-allowed' });
+ if (search) return json(res, 400, { error: 'invalid-request' });
+ if (!bus?.instances) return json(res, 503, { error: 'not-configured' });
+ try {
+ const value = await bus.instances();
+ if (!value || !value.instances || typeof value.instances !== 'object' || Array.isArray(value.instances))
+ throw new BusReadError('invalid-response');
+ const rows = Object.entries(value.instances)
+ .filter(([, entry]) => THREAD_DEFINITIONS.includes(entry?.definition))
+ .map(([instance, entry]) => ({ instance, name: entry.vars?.['persona.name'] || instance }));
+ if (rows.some(row => typeof row.name !== 'string')) throw new BusReadError('invalid-response');
+ return json(res, 200, { rows, at: new Date().toISOString() });
+ } catch (err) {
+ const code = err instanceof BusReadError ? err.code : 'invalid-response';
+ return json(res, busStatus(code), { error: code });
+ }
+ }
if (path === '/api/chat' || path.startsWith('/api/chat/')) {
return chatHTTP(req, res, { path, search, chat, named, send, preview: writer, log });
}
diff --git a/packages/webui/tests/chat-browser.test.mjs b/packages/webui/tests/chat-browser.test.mjs
index f3eeeca96..cc3ec093d 100644
--- a/packages/webui/tests/chat-browser.test.mjs
+++ b/packages/webui/tests/chat-browser.test.mjs
@@ -12,6 +12,7 @@ const HOSTILE = '<img src=x onerror="window.injected=1">\u001b[31m\u202eevil';
const stub = `(() => {
const real = fetch.bind(window);
window.__writes=[];window.__reads=[];window.__answers=[];window.__pages=[];
+ window.__instances=[{instance:'assistant',name:'assistant'}];
window.__projects=[{id:'${P}', name:${JSON.stringify(HOSTILE)}, repo:null},{id:'\"><img src=x>',name:'Invalid record'}];
window.__threads=[{id:'${A}',project:'${P}',name:'alpha',instance:'assistant'}, {id:'${B}',project:'${P}',name:'beta',instance:'assistant'}];
window.fetch=async (url,init={})=>{
@@ -33,6 +34,7 @@ const stub = `(() => {
window.__reads.push(path);
if(window.__holdRead && path.includes(window.__holdRead)){window.__heldSignal=init.signal;await new Promise(r=>window.__releaseRead=r);}
if(window.__readFailure)return new Response(JSON.stringify({error:window.__readFailure}),{status:window.__readFailure==='thread-not-owned'?403:503});
+ if(path==='/api/chat/instances')return window.__instanceFailure ? new Response(JSON.stringify({error:'not-configured'}),{status:503}) : data({rows:window.__instances});
if(path==='/api/chat/projects')return data({rows:window.__projects});
if(path.startsWith('/api/chat/threads')&&!path.includes('/messages'))return data({rows:window.__threads});
if(window.__pages.length)return data(window.__pages.shift());
@@ -82,7 +84,7 @@ test('chat browser: list/create named projects and threads, hostile labels inert
await b.evaluate(`window.__answers.push({body:{id:'${A}',instance:'assistant'}}),true`);
await click('#chat-thread-form button[type=submit]');
await wait('document.querySelector("#chat-send-form")');
- assert.deepEqual(await b.evaluate('window.__writes[1].body'), { project: P, name: 'named thread' });
+ assert.deepEqual(await b.evaluate('window.__writes[1].body'), { project: P, name: 'named thread', instance: 'assistant' });
assert.equal(await b.evaluate('window.__writes[1].path'), '/api/chat/threads');
assert.equal(await b.evaluate('window.__writes.length'), 2);
});
@@ -264,3 +266,65 @@ test('chat browser: a late project creation receipt cannot navigate away from an
assert.equal(await b.evaluate('location.hash'), '#/chat/threads/' + A);
assert.equal(await b.evaluate('document.querySelector("#chat-body").value'), 'stay in alpha');
});
+
+test('C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST', async t => {
+ const { b, wait, set, click, refresh } = await setup(t);
+ await b.evaluate(`window.__instances=[{instance:'bean',name:${JSON.stringify(HOSTILE)}},{instance:'chet',name:${JSON.stringify(HOSTILE)}}];location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelectorAll("#chat-instance option").length===3');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").required'), true);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ const labels = await b.evaluate('[...document.querySelectorAll("#chat-instance option")].map(o=>o.textContent)');
+ assert.match(labels[1], /\(bean\)/);
+ assert.match(labels[2], /\(chet\)/);
+ assert.doesNotMatch(labels.join(''), /[\u202a-\u202e\u2066-\u2069]/u);
+ assert.equal(await b.evaluate('!!document.querySelector("#chat-thread-form img")'), false);
+ assert.equal(await b.evaluate('window.injected'), undefined);
+ await set('#chat-thread-name', 'Bean discussion');
+ await click('#chat-thread-form button');
+ assert.equal(await b.evaluate('window.__writes.length'), 0);
+ await set('#chat-instance', 'bean');
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), 'bean');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-name").value'), 'Bean discussion');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), false);
+ await b.evaluate(`window.__answers.push({body:{id:'${A}',instance:'bean'}});true`);
+ await click('#chat-thread-form button');
+ await wait('window.__writes.length===1');
+ assert.deepEqual(await b.evaluate('window.__writes[0]'), { path: '/api/chat/threads', body: { project: P, name: 'Bean discussion', instance: 'bean' }, redirect: 'manual' });
+});
+
+test('C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching', async t => {
+ const { b, wait, set, refresh } = await setup(t);
+ await b.evaluate(`window.__instanceFailure=true;location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelector("#chat-thread-form") && !document.querySelector(".chat-loading")');
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /read the thread instances/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await b.evaluate('window.__instanceFailure=false;window.__instances=[];true');
+ await refresh();
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /No thread instances/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await b.evaluate("window.__instances=[{instance:'bean',name:'Bean'},{instance:'chet',name:'Chet'}];true");
+ await refresh();
+ await set('#chat-instance', 'bean');
+ await b.evaluate("window.__instances=[{instance:'chet',name:'Chet'}];true");
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '');
+ assert.match(await b.evaluate('document.querySelector("#chat-thread-form [role=status]").textContent'), /no longer available/);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ await refresh();
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), '', 'another refresh must not silently select the one remaining instance');
+ await set('#chat-instance', 'chet');
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), false);
+});
+
+test('C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread', async t => {
+ const { b, wait } = await setup(t, '?readonly');
+ await b.evaluate(`location.hash='#/chat/projects/${P}';true`);
+ await wait('document.querySelectorAll("#chat-instance option").length===2');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").value'), 'assistant');
+ assert.equal(await b.evaluate('document.querySelector("#chat-instance").disabled'), true);
+ assert.equal(await b.evaluate('document.querySelector("#chat-thread-form button").disabled'), true);
+ assert.match(await b.evaluate('document.querySelector(".chat-area").textContent'), /only reads/);
+ assert.equal(await b.evaluate('window.__writes.length'), 0);
+});
diff --git a/packages/webui/tests/chat-instances.test.mjs b/packages/webui/tests/chat-instances.test.mjs
new file mode 100644
index 000000000..893c38f80
--- /dev/null
+++ b/packages/webui/tests/chat-instances.test.mjs
@@ -0,0 +1,87 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { request } from 'node:http';
+import { mkdtempSync, rmSync } from 'node:fs';
+import { join } from 'node:path';
+import { tmpdir } from 'node:os';
+import { startServer } from '../src/serve.mjs';
+import { startTesterSocket, TESTER_ORIGIN } from '../src/tester-socket.mjs';
+import { close } from './fixture.mjs';
+
+const path = '/api/chat/instances';
+const preview = { host: 'chat.example', header: 'x-user', user: 'jason' };
+const snapshot = {
+ revision: 'a'.repeat(64), plugins: { private: 'do-not-project' },
+ instances: {
+ assistant: { definition: 'assistant', vars: { model: 'private-model' } },
+ bean: { definition: 'companion', vars: { 'persona.name': 'Bean', 'persona.sha256': 'private-hash' } },
+ chet: { definition: 'continuity', vars: { 'persona.name': 'Bean', file: '/private/path' } },
+ pm: { definition: 'pm', vars: { 'persona.name': 'Hidden PM' } },
+ },
+};
+function get(server, { route = path, method = 'GET', headers = {} } = {}) {
+ const address = server.address(), unix = typeof address === 'string';
+ return new Promise((resolve, reject) => {
+ const req = request({ ...(unix ? { socketPath: address } : { host: '127.0.0.1', port: address.port }),
+ path: route, method, headers: { host: unix ? new URL(TESTER_ORIGIN).host : preview.host, ...headers } }, res => {
+ let text = '';
+ res.on('data', chunk => text += chunk);
+ res.on('end', () => resolve({ status: res.statusCode, text, value: JSON.parse(text) }));
+ });
+ req.on('error', reject); req.end();
+ });
+}
+test('C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads', async t => {
+ let calls = 0;
+ const server = await startServer({ port: 0, preview, bus: { instances: async () => { calls++; return snapshot; } } });
+ t.after(() => close(server));
+ const refused = await get(server);
+ assert.equal(refused.status, 401);
+ assert.equal(calls, 0);
+ const answer = await get(server, { headers: { 'x-user': 'jason' } });
+ assert.equal(answer.status, 200);
+ assert.deepEqual(answer.value.rows, [{ instance: 'assistant', name: 'assistant' }, { instance: 'bean', name: 'Bean' }, { instance: 'chet', name: 'Bean' }]);
+ for (const row of answer.value.rows) assert.deepEqual(Object.keys(row).sort(), ['instance', 'name']);
+ assert.doesNotMatch(answer.text, /private|Hidden PM/);
+ assert.equal(calls, 1);
+ for (const options of [{ route: path + '?extra=1' }, { method: 'POST' }]) {
+ const result = await get(server, { ...options, headers: { 'x-user': 'jason' } });
+ assert.ok([400, 405].includes(result.status));
+ }
+ assert.equal(calls, 1, 'invalid metadata requests never call a bus');
+});
+test('C2-09a metadata uses the private tester listener own bus and no human transport', async t => {
+ const root = mkdtempSync(join(tmpdir(), 'chat219-')); t.after(() => rmSync(root, { recursive: true, force: true }));
+ let reads = 0;
+ const server = await startTesterSocket({ dataRoot: root, board: 'http://127.0.0.1:9',
+ bus: { instances: async () => { reads++; return { instances: { quill: { definition: 'proofreader', vars: { 'persona.name': 'Quill' } } } }; } } });
+ t.after(() => close(server));
+ const answer = await get(server);
+ assert.equal(answer.status, 200);
+ assert.deepEqual(answer.value.rows, [{ instance: 'quill', name: 'Quill' }]);
+ assert.equal(reads, 1);
+});
+test('C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields', async t => {
+ for (const bus of [null, { instances: async () => ({ instances: [] }) }, { instances: async () => { throw new Error('private-path'); } }]) {
+ const server = await startServer({ port: 0, preview, bus }); t.after(() => close(server));
+ const answer = await get(server, { headers: { 'x-user': 'jason' } });
+ assert.ok(answer.status >= 400);
+ assert.doesNotMatch(answer.text, /private-path/);
+ }
+});
+
+test('C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity', async t => {
+ let session = null, reads = 0;
+ const touches = [];
+ const sso = { publicHost: 'sso-chat.example', publicOrigin: 'https://sso-chat.example', formTargets: () => [],
+ session: (_req, { touch }) => { touches.push(touch); return session; } };
+ const server = await startServer({ port: 0, sso, bus: { instances: async () => { reads++; return snapshot; } } });
+ t.after(() => close(server));
+ const headers = { host: sso.publicHost };
+ assert.equal((await get(server, { headers })).status, 401);
+ assert.equal(reads, 0);
+ session = { name: 'jason' };
+ assert.equal((await get(server, { headers })).status, 200);
+ assert.equal(reads, 1);
+ assert.deepEqual(touches, [false, false]);
+});
diff --git a/packages/webui/tests/chat.test.mjs b/packages/webui/tests/chat.test.mjs
index 8c11611cf..b47ecf1a7 100644
--- a/packages/webui/tests/chat.test.mjs
+++ b/packages/webui/tests/chat.test.mjs
@@ -335,3 +335,21 @@ test('the chat seam names the transport it is given: the human one and the teste
assert.throws(() => createChat({}), { name: 'TypeError', message: 'chat needs a bus transport function' });
assert.equal(typeof createChat({ call: async () => ({}) }).projects, 'function');
});
+
+test('C2-09a createThread forwards an explicit instance through the existing channel', async t => {
+ const c = await setup(t);
+ c.state.answer = { id: T, instance: 'bean' };
+ const answer = await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'Bean work', instance: 'bean' } });
+ assert.equal(answer.status, 200);
+ assert.deepEqual(c.calls, [['thread.create', { project: P, name: 'Bean work', instance: 'bean' }]]);
+ assert.equal(answer.data.instance, 'bean');
+});
+test('C2-09a malformed instance and extra thread fields refuse before the channel write', async t => {
+ const c = await setup(t);
+ for (const value of [null, 7, {}, [], '', 'bad name', 'bean/../../other']) {
+ const answer = await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'work', instance: value } });
+ assert.equal(answer.status, 400, JSON.stringify(value));
+ }
+ assert.equal((await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'work', instance: 'bean', extra: true } })).status, 400);
+ assert.deepEqual(c.calls, []);
+});
@@ -0,0 +1,16 @@
09569596eba8d129d0aa78c52dbf92d2a26a8f2b1960d54898eb90a810398465 packages/bus/src/broker.mjs
b0c66c8999b567896923f6c876911dba936c6eca1ccc7aaeada51369fd613594 packages/bus/tests/creative.test.mjs
3efd0238da60e0bfcd3b25e5256b9c213085119a86a7b852b13e7a60340d3a24 packages/bus/tests/grant-executor.test.mjs
b7ea857637947c7a9ca45c56cea47e37e06f0fd8446ea91316990d9fa7299a7a packages/bus/tests/project-runtime.test.mjs
760c4a5dc2881a47be2a174f92c4fcb1b6e46abb299cb045a2e3dcbc485fb334 packages/bus/tests/threads.test.mjs
8f7d6745fffb2a0898ac80c012084ac48964748da6439a8c56facc6529ffb19b packages/bus/tests/user-principal.test.mjs
20423a8614ada8f2d1156ab2b041e93582986a1277e86520b0909e66892e4aec packages/cli/src/launcher.mjs
2c3f120dfdc0b52bf8ab38a88e86251be75d4b0d218e23df489dcaf815d6bc15 packages/cli/tests/launcher.test.mjs
7f308fdf601e839194f026beb3fea279eba1e356ce2756aa4a42fa4873b79b30 packages/cli/tests/thread-launch.test.mjs
39c6d207e026debb7db8aade5534503847d0f5c84d560ba28f3cd65490378363 packages/webui/src/chat-http.mjs
77878dd33fd9e0381deb996969389dde21c109eaddf46a8a5c91907ec0dfe24e packages/webui/src/chat.mjs
8e89df5fe8a9da35de41b5968afc6efcd92503e92ef28c6f557bb5371d10d1a4 packages/webui/src/public/chat.js
443c83debe21559de2e4a6bcc07514bce9bebe209df58b8011827fcc1cabd905 packages/webui/src/serve.mjs
d918e5b061ced9db28337a9b02c7042f9ddafaf09f32affce9f51190c0cba93f packages/webui/tests/chat-browser.test.mjs
2bad14535372488de326b82b791b79a61e1a4ab839458535132958ef85b746eb packages/webui/tests/chat-instances.test.mjs
7779d33d5b1dd3fe183722d0f91ff1f71e741e113cf2d5ee407f3d26428fc162 packages/webui/tests/chat.test.mjs
@@ -0,0 +1,16 @@
packages/bus/src/broker.mjs
packages/bus/tests/creative.test.mjs
packages/bus/tests/grant-executor.test.mjs
packages/bus/tests/project-runtime.test.mjs
packages/bus/tests/threads.test.mjs
packages/bus/tests/user-principal.test.mjs
packages/cli/src/launcher.mjs
packages/cli/tests/launcher.test.mjs
packages/cli/tests/thread-launch.test.mjs
packages/webui/src/chat-http.mjs
packages/webui/src/chat.mjs
packages/webui/src/public/chat.js
packages/webui/src/serve.mjs
packages/webui/tests/chat-browser.test.mjs
packages/webui/tests/chat-instances.test.mjs
packages/webui/tests/chat.test.mjs
+96
View File
@@ -0,0 +1,96 @@
START single-bus-creative.test 2026-10-23T10:12:57.651391+00:00
END {"label": "single-bus-creative.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/creative.test.mjs"], "start": "2026-10-23T10:12:57.651391+00:00", "end": "2026-10-23T10:12:58.016551+00:00", "seconds": 0.365, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-bus-creative.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rAhIikO"}
START single-bus-grant-executor.test 2026-10-23T10:12:58.018990+00:00
END {"label": "single-bus-grant-executor.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T10:12:58.018990+00:00", "end": "2026-10-23T10:13:19.628786+00:00", "seconds": 21.61, "exit": 0, "timeout": false, "counts": {"tests": 42, "pass": 42, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-bus-grant-executor.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rB5qFSr"}
START single-bus-project-runtime.test 2026-10-23T10:13:19.631298+00:00
END {"label": "single-bus-project-runtime.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/project-runtime.test.mjs"], "start": "2026-10-23T10:13:19.631298+00:00", "end": "2026-10-23T10:13:20.046077+00:00", "seconds": 0.415, "exit": 0, "timeout": false, "counts": {"tests": 3, "pass": 3, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-bus-project-runtime.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rFHRKBT"}
START single-bus-threads.test 2026-10-23T10:13:20.048159+00:00
END {"label": "single-bus-threads.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T10:13:20.048159+00:00", "end": "2026-10-23T10:13:21.364784+00:00", "seconds": 1.317, "exit": 0, "timeout": false, "counts": {"tests": 20, "pass": 20, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-bus-threads.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r2Ck4U7"}
START single-bus-user-principal.test 2026-10-23T10:13:21.367024+00:00
END {"label": "single-bus-user-principal.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T10:13:21.367024+00:00", "end": "2026-10-23T10:13:21.982332+00:00", "seconds": 0.615, "exit": 0, "timeout": false, "counts": {"tests": 23, "pass": 23, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-bus-user-principal.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rC2Lkzs"}
START single-cli-launcher.test 2026-10-23T10:13:21.984639+00:00
END {"label": "single-cli-launcher.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/launcher.test.mjs"], "start": "2026-10-23T10:13:21.984639+00:00", "end": "2026-10-23T10:13:48.153430+00:00", "seconds": 26.169, "exit": 0, "timeout": false, "counts": {"tests": 34, "pass": 34, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-cli-launcher.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rJvKWvQ"}
START single-cli-thread-launch.test 2026-10-23T10:13:48.155987+00:00
END {"label": "single-cli-thread-launch.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T10:13:48.155987+00:00", "end": "2026-10-23T10:14:06.157369+00:00", "seconds": 18.001, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-cli-thread-launch.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r7AlG4T"}
START single-webui-chat-browser.test 2026-10-23T10:14:06.159688+00:00
END {"label": "single-webui-chat-browser.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:14:06.159688+00:00", "end": "2026-10-23T10:14:24.163642+00:00", "seconds": 18.004, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-webui-chat-browser.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rWPdp6l"}
START single-webui-chat-instances.test 2026-10-23T10:14:24.165883+00:00
END {"label": "single-webui-chat-instances.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:14:24.165883+00:00", "end": "2026-10-23T10:14:24.330166+00:00", "seconds": 0.164, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-webui-chat-instances.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rb7PUSc"}
START single-webui-chat.test 2026-10-23T10:14:24.332301+00:00
END {"label": "single-webui-chat.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T10:14:24.332301+00:00", "end": "2026-10-23T10:14:24.797185+00:00", "seconds": 0.465, "exit": 0, "timeout": false, "counts": {"tests": 25, "pass": 25, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/single-webui-chat.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rlIKT2J"}
START suite-bus 2026-10-23T10:14:24.799798+00:00
END {"label": "suite-bus", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/access-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/account-migration.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/approval-binding.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/broker.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/business.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/channel-proof.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/chief-of-staff.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/comment-cursors.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/comment-migration.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/credential-claim.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/credential-status.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/credential-store.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/credential-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/csuite-messaging.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/daily-quota-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/decision-close.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/decision-route.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/decisions.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/end-launch.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/fire-revokes-grants.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/gitea-grant-docker.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/gitea-grant.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/gitea-migration.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/grant-enroll.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/grant-executor.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/grant-host.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/grant-migration.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/hard-waits.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/host-run.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/host-tools.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/human.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/instances-verb.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/interpreter.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/land.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/launch-notice.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/launcher-released.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/library-sync.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/library.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/lifecycle-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/lifecycle-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/limit-stop.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/limits-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/message-citation.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/message-filter.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/messaging.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/name-migration.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/peer-transport.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/peercred.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/plugins-verb.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/principal.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/process.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/processing-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345Line truncated
START suite-cli 2026-10-23T10:14:48.226535+00:00
END {"label": "suite-cli", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/access-boot.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/access.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/app.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/boot-status.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/case1-accept.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/case1-hostcheck.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/case2-persona-import.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/case2-unit.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/channel-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/channel-recovery.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/compactions-launch.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/config.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/follow.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/format.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/gate-lane-auth.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/gate-lane-fixtures.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/gate-lane-image.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/gate-lane-state.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/gate-lane.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/git-socket.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/grants.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/host.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/hosts.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/idle-stop-race.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/launch-account.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/launch-limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/launch-limits.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/launch-routes.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/launcher.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/lifecycle.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/limits-tokens.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/mark-read.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/notifier.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/other-tree.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/processing-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/project.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/publish-socket.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/session-clear.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/session-launch.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/stack-copy.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/stack-instances.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/stack-release-external.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/stack-release.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/stack-upgrade.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/tester-http.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/tester-reads.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/tester-threads.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/tester.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-launch.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/trackers-boot.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/transport.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/usage.test.mjs", "/mnt/storage/scratch/Line truncated
START suite-webui 2026-10-23T10:25:17.264441+00:00
END {"label": "suite-webui", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/agents-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/assistant.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/board-held.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/browser-edge.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/bus-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/bus.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/channel-chat.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/conversation.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/discord.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/docs-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/editor-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/editor-provenance.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/editor.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/granted-user.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/history-return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/instances-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/library-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/library-status.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/limits-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/limits-route.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/markdown-preview.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/pinned-copy.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/preview.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/processing-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/processing-ui.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/project-file.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/proof-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/proof.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/reads.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/relaunch.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/routes-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/routes.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/send-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/send.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/serve.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/service.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/settings-form.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/settings-write.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/shell.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/sso-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/sso.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/tester-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/tester-cli.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/tester-listener.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/tester-socket.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/thread-claims-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/thread-claims.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/usage-browser.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/usage.test.mjs", "/mLine truncated
START suite-harness 2026-10-23T10:26:05.118318+00:00
END {"label": "suite-harness", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/bundle.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/claude-gate.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/claude-session.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/gate.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/git-tools.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/mcp-server.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/pi-session.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/raise-domain.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/runner.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/skills.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/spec-tools.test.mjs", "/mnt/storage/scratch/rocko-r219-g3345/packages/harness/tests/tools.test.mjs"], "start": "2026-10-23T10:26:05.118318+00:00", "end": "2026-10-23T10:26:17.260234+00:00", "seconds": 12.142, "exit": 0, "timeout": false, "counts": {"tests": 151, "pass": 151, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/suite-harness.tap", "tmpdir": "/mnt/storage/scratch/tmp/rdlbuJf"}
START test-auth 2026-10-23T10:26:17.263654+00:00
END {"label": "test-auth", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-auth.sh"], "start": "2026-10-23T10:26:17.263654+00:00", "end": "2026-10-23T10:26:18.079311+00:00", "seconds": 0.816, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-auth.tap", "tmpdir": "/mnt/storage/scratch/tmp/r1JtWXh"}
START test-conductor 2026-10-23T10:26:18.081520+00:00
END {"label": "test-conductor", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-conductor.sh"], "start": "2026-10-23T10:26:18.081520+00:00", "end": "2026-10-23T10:26:24.911181+00:00", "seconds": 6.83, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-conductor.tap", "tmpdir": "/mnt/storage/scratch/tmp/rTbT7Gz"}
START test-config 2026-10-23T10:26:24.913377+00:00
END {"label": "test-config", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-config.sh"], "start": "2026-10-23T10:26:24.913377+00:00", "end": "2026-10-23T10:26:27.432721+00:00", "seconds": 2.519, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-config.tap", "tmpdir": "/mnt/storage/scratch/tmp/rXeHjrT"}
START test-discord 2026-10-23T10:26:27.434929+00:00
END {"label": "test-discord", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-discord.sh"], "start": "2026-10-23T10:26:27.434929+00:00", "end": "2026-10-23T10:26:34.864671+00:00", "seconds": 7.43, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-discord.tap", "tmpdir": "/mnt/storage/scratch/tmp/rB9JAZm"}
START test-extension-package 2026-10-23T10:26:34.866931+00:00
END {"label": "test-extension-package", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-extension-package.sh"], "start": "2026-10-23T10:26:34.866931+00:00", "end": "2026-10-23T10:26:36.233555+00:00", "seconds": 1.367, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-extension-package.tap", "tmpdir": "/mnt/storage/scratch/tmp/rnDXVhr"}
START test-foundation 2026-10-23T10:26:36.235644+00:00
END {"label": "test-foundation", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-foundation.sh"], "start": "2026-10-23T10:26:36.235644+00:00", "end": "2026-10-23T10:27:13.879988+00:00", "seconds": 37.644, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-foundation.tap", "tmpdir": "/mnt/storage/scratch/tmp/ryCvPGQ"}
START test-land-work 2026-10-23T10:27:13.882219+00:00
END {"label": "test-land-work", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-land-work.sh"], "start": "2026-10-23T10:27:13.882219+00:00", "end": "2026-10-23T10:27:25.920760+00:00", "seconds": 12.039, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-land-work.tap", "tmpdir": "/mnt/storage/scratch/tmp/rr7Aj6K"}
START test-queue 2026-10-23T10:27:25.922966+00:00
END {"label": "test-queue", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-queue.sh"], "start": "2026-10-23T10:27:25.922966+00:00", "end": "2026-10-23T10:27:47.680652+00:00", "seconds": 21.758, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": ["skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219-g3345) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-queue.tap", "tmpdir": "/mnt/storage/scratch/tmp/rf7eQ8K"}
START test-release 2026-10-23T10:27:47.682835+00:00
END {"label": "test-release", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-release.sh"], "start": "2026-10-23T10:27:47.682835+00:00", "end": "2026-10-23T10:27:50.502936+00:00", "seconds": 2.82, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-release.tap", "tmpdir": "/mnt/storage/scratch/tmp/rZg7ntV"}
START test-task 2026-10-23T10:27:50.505059+00:00
END {"label": "test-task", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219-g3345/scripts/test-task.sh"], "start": "2026-10-23T10:27:50.505059+00:00", "end": "2026-10-23T10:28:05.901435+00:00", "seconds": 15.396, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": ["skip live provider cases (row 244: no provider call in the gate)", "skip live provider cases (row 244: no provider call in the gate)"], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/test-task.tap", "tmpdir": "/mnt/storage/scratch/tmp/rdZhOdC"}
START mutant-T01 2026-10-23T10:28:05.904390+00:00
END {"label": "mutant-T01", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: creative bindLaunch without a thread", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T10:28:05.904390+00:00", "end": "2026-10-23T10:28:06.068776+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T01.tap", "tmpdir": "/mnt/storage/scratch/tmp/ra0u5Cv"}
START mutant-T02 2026-10-23T10:28:06.073278+00:00
END {"label": "mutant-T02", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: explicit creative", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T10:28:06.073278+00:00", "end": "2026-10-23T10:28:06.237700+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T02.tap", "tmpdir": "/mnt/storage/scratch/tmp/raxjDbw"}
START mutant-T03 2026-10-23T10:28:06.241790+00:00
END {"label": "mutant-T03", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: explicit missing", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T10:28:06.241790+00:00", "end": "2026-10-23T10:28:06.406036+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T03.tap", "tmpdir": "/mnt/storage/scratch/tmp/r8U6xIi"}
START mutant-T04 2026-10-23T10:28:06.410053+00:00
END {"label": "mutant-T04", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: explicit creative", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T10:28:06.410053+00:00", "end": "2026-10-23T10:28:06.574472+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T04.tap", "tmpdir": "/mnt/storage/scratch/tmp/r40J2sE"}
START mutant-T05 2026-10-23T10:28:06.578716+00:00
END {"label": "mutant-T05", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: naming an instance", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T10:28:06.578716+00:00", "end": "2026-10-23T10:28:06.743075+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T05.tap", "tmpdir": "/mnt/storage/scratch/tmp/rzYiKuH"}
START mutant-T06 2026-10-23T10:28:06.746526+00:00
END {"label": "mutant-T06", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: creative thread messages", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T10:28:06.746526+00:00", "end": "2026-10-23T10:28:07.111231+00:00", "seconds": 0.365, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T06.tap", "tmpdir": "/mnt/storage/scratch/tmp/rDcILeY"}
START mutant-T07 2026-10-23T10:28:07.114611+00:00
END {"label": "mutant-T07", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a metadata uses the human", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:28:07.114611+00:00", "end": "2026-10-23T10:28:07.278880+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T07.tap", "tmpdir": "/mnt/storage/scratch/tmp/rSC2VpK"}
START mutant-T08 2026-10-23T10:28:07.282442+00:00
END {"label": "mutant-T08", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a metadata uses the human", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:28:07.282442+00:00", "end": "2026-10-23T10:28:07.446897+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T08.tap", "tmpdir": "/mnt/storage/scratch/tmp/rtkz48N"}
START mutant-T09 2026-10-23T10:28:07.450307+00:00
END {"label": "mutant-T09", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a malformed instance", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T10:28:07.450307+00:00", "end": "2026-10-23T10:28:07.614703+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T09.tap", "tmpdir": "/mnt/storage/scratch/tmp/rgoWDjy"}
START mutant-T10 2026-10-23T10:28:07.618001+00:00
END {"label": "mutant-T10", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a createThread forwards", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T10:28:07.618001+00:00", "end": "2026-10-23T10:28:07.782438+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T10.tap", "tmpdir": "/mnt/storage/scratch/tmp/reW9fHz"}
START mutant-T11 2026-10-23T10:28:07.785854+00:00
END {"label": "mutant-T11", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: no silent choice", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:28:07.785854+00:00", "end": "2026-10-23T10:28:08.501474+00:00", "seconds": 0.716, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T11.tap", "tmpdir": "/mnt/storage/scratch/tmp/rUbDgih"}
START mutant-T12 2026-10-23T10:28:08.504841+00:00
END {"label": "mutant-T12", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: no silent choice", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:28:08.504841+00:00", "end": "2026-10-23T10:28:09.270344+00:00", "seconds": 0.766, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T12.tap", "tmpdir": "/mnt/storage/scratch/tmp/rHk3Ste"}
START mutant-T13 2026-10-23T10:28:09.273653+00:00
END {"label": "mutant-T13", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: metadata failure", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:28:09.273653+00:00", "end": "2026-10-23T10:28:09.989159+00:00", "seconds": 0.716, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T13.tap", "tmpdir": "/mnt/storage/scratch/tmp/rsuzhDJ"}
START mutant-T14 2026-10-23T10:28:09.992670+00:00
END {"label": "mutant-T14", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: no silent choice", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:28:09.992670+00:00", "end": "2026-10-23T10:28:10.708154+00:00", "seconds": 0.715, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T14.tap", "tmpdir": "/mnt/storage/scratch/tmp/rp1LF4L"}
START mutant-T15 2026-10-23T10:28:10.711476+00:00
END {"label": "mutant-T15", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: metadata failure", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:28:10.711476+00:00", "end": "2026-10-23T10:29:17.627180+00:00", "seconds": 66.916, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T15.tap", "tmpdir": "/mnt/storage/scratch/tmp/rexq2Om"}
START mutant-T16 2026-10-23T10:29:17.630507+00:00
END {"label": "mutant-T16", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: metadata failure", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:29:17.630507+00:00", "end": "2026-10-23T10:29:18.396002+00:00", "seconds": 0.765, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T16.tap", "tmpdir": "/mnt/storage/scratch/tmp/rR8WkiV"}
START mutant-T17 2026-10-23T10:29:18.399281+00:00
END {"label": "mutant-T17", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: no silent choice", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:29:18.399281+00:00", "end": "2026-10-23T10:29:19.164854+00:00", "seconds": 0.766, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T17.tap", "tmpdir": "/mnt/storage/scratch/tmp/rKsTwMQ"}
START mutant-T18 2026-10-23T10:29:19.168373+00:00
END {"label": "mutant-T18", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a metadata uses the human", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:29:19.168373+00:00", "end": "2026-10-23T10:29:19.332774+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T18.tap", "tmpdir": "/mnt/storage/scratch/tmp/r0SU7Da"}
START mutant-T19 2026-10-23T10:29:19.336197+00:00
END {"label": "mutant-T19", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a metadata uses the human", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:29:19.336197+00:00", "end": "2026-10-23T10:29:19.500560+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T19.tap", "tmpdir": "/mnt/storage/scratch/tmp/rIuZFB1"}
START mutant-T20 2026-10-23T10:29:19.503894+00:00
END {"label": "mutant-T20", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a picker: metadata failure", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T10:29:19.503894+00:00", "end": "2026-10-23T10:29:20.169220+00:00", "seconds": 0.665, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T20.tap", "tmpdir": "/mnt/storage/scratch/tmp/rhbRFSs"}
START mutant-T21 2026-10-23T10:29:20.172733+00:00
END {"label": "mutant-T21", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a metadata uses the human", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T10:29:20.172733+00:00", "end": "2026-10-23T10:29:20.337140+00:00", "seconds": 0.164, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T21.tap", "tmpdir": "/mnt/storage/scratch/tmp/rakV3xD"}
START mutant-T22 2026-10-23T10:29:20.340611+00:00
END {"label": "mutant-T22", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: terminating one creative thread", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T10:29:20.340611+00:00", "end": "2026-10-23T10:29:21.156329+00:00", "seconds": 0.816, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T22.tap", "tmpdir": "/mnt/storage/scratch/tmp/riZhpGU"}
START mutant-T23 2026-10-23T10:29:21.159751+00:00
END {"label": "mutant-T23", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "C2-09a: fromSession refuses a creative instance outside a thread without a sandbox", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T10:29:21.159751+00:00", "end": "2026-10-23T10:29:21.524453+00:00", "seconds": 0.365, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T23.tap", "tmpdir": "/mnt/storage/scratch/tmp/rbmcPB7"}
START mutant-T24 2026-10-23T10:29:21.528207+00:00
END {"label": "mutant-T24", "args": ["timeout", "-k", "10", "180", "node", "--test-reporter=tap", "--test", "--test-name-pattern", "sage-1908: once the agent of an applied grant is a creative role", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T10:29:21.528207+00:00", "end": "2026-10-23T10:29:22.193573+00:00", "seconds": 0.665, "exit": 1, "timeout": false, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-2efd-mos1439/mutant-T24.tap", "tmpdir": "/mnt/storage/scratch/tmp/reo5pMQ"}
+5
View File
@@ -0,0 +1,5 @@
054da642f9b0680bd7743583e4c67266d7e14e2a
2efd127ce617a9bff8dc3bee3a3b657b0c175c07 b899095e482cb2b68919efff1e332f272203e017
e86a84a852c9739f157033e3772243b293742983
merge: combine row 219 with landed row 295
@@ -0,0 +1,26 @@
| Mutant | Result | Killing test pattern |
|---|---|---|
| T01 | assertion-killed | C2-09a: creative bindLaunch without a thread |
| T02 | assertion-killed | C2-09a: explicit creative |
| T03 | assertion-killed | C2-09a: explicit missing |
| T04 | assertion-killed | C2-09a: explicit creative |
| T05 | assertion-killed | C2-09a: naming an instance |
| T06 | assertion-killed | C2-09a: creative thread messages |
| T07 | assertion-killed | C2-09a metadata uses the human |
| T08 | assertion-killed | C2-09a metadata uses the human |
| T09 | assertion-killed | C2-09a malformed instance |
| T10 | assertion-killed | C2-09a createThread forwards |
| T11 | assertion-killed | C2-09a picker: no silent choice |
| T12 | assertion-killed | C2-09a picker: no silent choice |
| T13 | assertion-killed | C2-09a picker: metadata failure |
| T14 | assertion-killed | C2-09a picker: no silent choice |
| T15 | assertion-killed | C2-09a picker: metadata failure |
| T16 | assertion-killed | C2-09a picker: metadata failure |
| T17 | assertion-killed | C2-09a picker: no silent choice |
| T18 | assertion-killed | C2-09a metadata uses the human |
| T19 | assertion-killed | C2-09a metadata uses the human |
| T20 | assertion-killed | C2-09a picker: metadata failure |
| T21 | assertion-killed | C2-09a metadata uses the human |
| T22 | assertion-killed | C2-09a: terminating one creative thread |
| T23 | assertion-killed | C2-09a: fromSession refuses a creative instance outside a thread without a sandbox |
| T24 | assertion-killed | sage-1908: once the agent of an applied grant is a creative role |
@@ -0,0 +1,13 @@
8de616a220a1330c7f05d7688040f008c821ec772a4116886cb077d9dfbd6247 BUILD.md
128003fca0dc7d99c3368f26a89833bc0182473e9fb8b7abe2b61023936b9c55 GATE-RESULTS.json
b889f3836dd736bf61f61eabd9abfdeb0375e982cdf4ffa7d0c19b99b90df4d5 base.txt
5d29d2bd6f52fa45aab75521f9cd8c79b679d47f817ef448b63ec33186c3da7e build.patch
4a2271ddbd4212324f39e608c3a35a4ae834c045cb0d13a619715017ded0ffef candidate-manifest.sha256
5222c9f6d7c1da26f5b9447626ba8e005a7ab36006183ed36df40190b9133fb4 changed-paths.txt
8231c2b54a60426f4ac469811aba53ba35cca9701294796f8826a59c88a0ec55 driver.log
42e1acb53d2cf213b62012950ab2bfd66a45f51372302657eee1f669c902fe82 merge.txt
c868213b522a241659d588d8beda2e0529abc4d20eb5210b554d80141351d3ca mutant-kill-table.md
acf6860b5195c8a740070a400f256d0a827f6a3db84bf3b617f27213a222cdf8 reader-audit.json
8f1b6e867ed645834a56d82fe156cabb13083e22ba59bf600bb16c50204bd8a8 reader-audit.py
f60af3ef6e8f458ccc53975ef8c117bc30c7715842d08ff2eed61228cc0f84d9 run-inventory.sha256
1d0547a54c9225f341ecf22f29da5302e9b8fb37fcacd11685c4e682f1c12975 runner.py
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,24 @@
from pathlib import Path
import subprocess,json,re,hashlib
ROOT=Path(__file__).resolve().parents[5];P=ROOT/'agents/rocko/work/queue-219';OUT=P/'r6'
files=sorted(ROOT.glob('packages/*/tests/**/*'))+sorted(ROOT.glob('scripts/test-*.sh'))
files=[f for f in files if f.is_file()]
texts={f:f.read_text(errors='replace') for f in files}
rows=[]
for path in (P/'changed-paths.txt').read_text().splitlines()+['packages/webui/src/public/bus.js']:
base=Path(path).name;hits=[]
for f,text in texts.items():
for n,line in enumerate(text.splitlines(),1):
if path in line or base in line:hits.append(dict(file=str(f.relative_to(ROOT)),line=n,text=line))
rows.append(dict(path=path,hits=hits))
pins=[]
for f,text in texts.items():
for n,line in enumerate(text.splitlines(),1):
if re.search(r'(?<![0-9a-f])[0-9a-f]{64}(?![0-9a-f])',line):pins.append(dict(file=str(f.relative_to(ROOT)),line=n,text=line))
control=ROOT/'packages/bus/tests/channel-proof.test.mjs';t=control.read_text()
m=re.search(r'"packages/webui/src/bus.mjs": "([0-9a-f]{64})"',t);assert m
assert hashlib.sha256((ROOT/'packages/webui/src/bus.mjs').read_bytes()).hexdigest()==m[1]
assert subprocess.check_output(['git','diff','2efd127ce','HEAD','--','packages/webui/src/public/bus.js'],cwd=ROOT)==b''
report=dict(head=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip(),paths=rows,hexPins=pins,positiveControl=dict(file=str(control.relative_to(ROOT)),pin='packages/webui/src/bus.mjs',sha256=m[1],matches=True),busJsUnchanged=True)
(OUT/'reader-audit.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps(dict(paths=len(rows)-1,additionalBusJs=True,readerHits=sum(len(x['hits']) for x in rows),hexPinLines=len(pins),positiveControl=True,busJsUnchanged=True)))
@@ -0,0 +1,54 @@
4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945 run-2efd-mos1439/cwd-sweep-final.json
4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945 run-2efd-mos1439/cwd-sweep.json
a244ba4bcf1802bfec56b8b7722c269842c9d8bdf4e7171cb20ec70351380b8c run-2efd-mos1439/head.json
9bbd1a6c433db7010b6c24b8eb80214475d561ab13f6d769daf50c497209cf90 run-2efd-mos1439/mutant-T01.tap
f519161bed3de19178f5b1d1c9555d281b269802b621f759e49025bd85ed1bf8 run-2efd-mos1439/mutant-T02.tap
ecd31fcf6652f3f2751538d750edd5eb4076a4072db17a07df78e0e9a9cf377a run-2efd-mos1439/mutant-T03.tap
955fd31df9aaa5c50dbf5c38c4a51c31a2a215675689ced750f825bb8843eeed run-2efd-mos1439/mutant-T04.tap
acc11e6db75f510e1c54c77fc787620bfd5ff78b6e86a650a9c5d7e04a3dccea run-2efd-mos1439/mutant-T05.tap
02a790c3c05466963c3eba3d3c1624e7a87af9a4e19b2499db69502442d2cf57 run-2efd-mos1439/mutant-T06.tap
46daf5a77b5fe1e89dbce6b271be2982a9a4e1fa6580fcfbc55251706e1ff358 run-2efd-mos1439/mutant-T07.tap
17ab0e2e583ae2f054966a283a782ac259fc88d20c8a61d4bc1e3d9bdf9946ec run-2efd-mos1439/mutant-T08.tap
f4afe3d142c965365c97330772e84c49a4a0de5b82eb234455de16772de40288 run-2efd-mos1439/mutant-T09.tap
6c90bddb31a61964d6fb1d934aadcf9d113dd61b5c74f1212c82eac402ee32e6 run-2efd-mos1439/mutant-T10.tap
c45649004ee5a91ff668c747a5cd4b01df77796836873e17e81b44dfb1630617 run-2efd-mos1439/mutant-T11.tap
11cc0d3df8413711e2ad7ad81ec55c931c3ea39e063b17af5ec2a0d54b9840cd run-2efd-mos1439/mutant-T12.tap
c9cea400fcff6d75f1dc8bb837f901477702eaf9a52ac4d33e9a34290033cf87 run-2efd-mos1439/mutant-T13.tap
e79b0b05bbe075d686cabfb344c0ec4b986284d574422c8a9817083d485af0d8 run-2efd-mos1439/mutant-T14.tap
af325623360037967309834bf5e9c57f099ffa26c15c60a82da7f6c0a8bfd781 run-2efd-mos1439/mutant-T15.tap
4ffbe980197969e73e4dfc30fcb38edfdccd2b685f6600363ae9d611521c47df run-2efd-mos1439/mutant-T16.tap
c3c6d33bb30fa3bdf60b1cac616bb6e5128efcc3a6d0a312a0a9f9032ef28b6b run-2efd-mos1439/mutant-T17.tap
b93aa79e8b22c3bf297cee696aa03ee73f2eda0e0b3bcedd74fb6398506ba99f run-2efd-mos1439/mutant-T18.tap
20a798dfa91be1fd19fc43f3ca2c4f8b4fff74efd8fb9719230aa6455cadfff8 run-2efd-mos1439/mutant-T19.tap
0fd4bcb6d08fb00a22612bc456a2146a0679089e524f8b8553f47a1a45f23c4c run-2efd-mos1439/mutant-T20.tap
47900e7314dd7cbebb70d593c36bdb61033e21af0bba9e021aa29a534002e821 run-2efd-mos1439/mutant-T21.tap
e5eed94633e5328b7f1025385bfe398ce0953f4e024c6b6dd48f1812b52a721e run-2efd-mos1439/mutant-T22.tap
d8e7b7566be3d988f073a5278bdb63e6228477b94c52a8a2dcee74ba8b96cb57 run-2efd-mos1439/mutant-T23.tap
95c96a08e2236484ac7e9888525b0e17b65d3d939e5e6a442ad3eedf51fcc743 run-2efd-mos1439/mutant-T24.tap
134fe70aef2b14b480167dc0d3e30e4ac50cad2152e6e944b0c209f3dead318f run-2efd-mos1439/mutants.jsonl
48cac5f443a55beb7b9ad9b12617bc02e67c2dc995db54c596e6a0ff9722c794 run-2efd-mos1439/network-cleanup.log
ff56abff5c017baac632cd41989b2cd1da7f70ef89814183780022f1a812a663 run-2efd-mos1439/results.jsonl
090b4e9a4058ee5cbc6cf69d9e39976186bb3e711390f09ce4be7dc95e653dfb run-2efd-mos1439/single-bus-creative.test.tap
df93c6986ee970e44c2e36f1ba20cb402219fc8d580e6b971074d961b3863b41 run-2efd-mos1439/single-bus-grant-executor.test.tap
1fbe54cd9f1ce2d06db5499d970ef7052eaf852b33973102baaea5342fccd7bc run-2efd-mos1439/single-bus-project-runtime.test.tap
1e6832c9e81e1b60700e1668b287276ca1009e41378d84c4808d35e248e97058 run-2efd-mos1439/single-bus-threads.test.tap
23da9b118299bcbb8deaa5ea2b525355dbf08aaee69f16c94741342c1c299762 run-2efd-mos1439/single-bus-user-principal.test.tap
076095678c2e075f15df795a3d9731dffb07df334bd2488105872da96f2d89eb run-2efd-mos1439/single-cli-launcher.test.tap
0966a01d8a4d459d42be3cf7c36f82decfa8c8e62dac25e94b5cab1a6427d365 run-2efd-mos1439/single-cli-thread-launch.test.tap
ea6efe8ceccf27ded164a9c8c14dc39f1d62e12fcc4eb3a8d31806e9de85bf03 run-2efd-mos1439/single-webui-chat-browser.test.tap
384004d2ec9c0a1e30a683630331927dbd6a01c6f1c7d82a0e1ff14fda84b2ec run-2efd-mos1439/single-webui-chat-instances.test.tap
fcf510a59af4e6b86a00200c7f3e39d83d7020e5bdb096e0c04de5e5a392f341 run-2efd-mos1439/single-webui-chat.test.tap
32c9f244675a5a6cb93555008e11646c849b4c64330743faeef70c380969bf6d run-2efd-mos1439/suite-bus.tap
ebd80f7b179db829eb319f741d8893ffafbabdbc92fe4a2a5f9fe9034b02cd8e run-2efd-mos1439/suite-cli.tap
809534666dc439d35408f289986391ca66e2b65c13195b7bf8a6cea11ee11827 run-2efd-mos1439/suite-harness.tap
0d59c1ced0045746db28792f9229f5ff11da8f1628208d9b95a122fa05d9e346 run-2efd-mos1439/suite-webui.tap
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 run-2efd-mos1439/test-auth.tap
dc02b870340f0078121ca4abe87bcbcccc2c751498a4a4b95c77957cc351e114 run-2efd-mos1439/test-conductor.tap
92ea8ce5e66d892fefacc76b87d140bcfb8c5a51dc77bdf4a47a6213ac5d2898 run-2efd-mos1439/test-config.tap
c558883538ad963b264d429c58ea9310bb32930bd1d6dfc828920ecb14f0e7ea run-2efd-mos1439/test-discord.tap
2f068af0a46414328be11034faad14211bef616a8e7ba64a68c52d6a29f7c13e run-2efd-mos1439/test-extension-package.tap
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f run-2efd-mos1439/test-foundation.tap
3e2f8024027592e77790db2bbe039803060915ebf37c7b67f04071a7e83d20dc run-2efd-mos1439/test-land-work.tap
f1d0aef960a36787e61fbe8f75c52c73fbbd32be7a3928d70ed6833af6f5afc9 run-2efd-mos1439/test-queue.tap
fa832794ee35025fb4559d58d01a270f6bff2eb65e863e9075b797f47fa68545 run-2efd-mos1439/test-release.tap
8cd9a6c8131b17964e135f3e26bce56aaf053d42d6749f3c30813dd454102cef run-2efd-mos1439/test-task.tap
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Row219 serial gate. Requires Sage CLEAR; all children use a process group."""
import os,sys,time,json,signal,subprocess,tempfile,hashlib,re,glob
from pathlib import Path
from datetime import datetime,timezone
ROOT=Path(__file__).resolve().parents[4]
PACKET=Path(__file__).resolve().parent
OUT=PACKET/'run-2efd-mos1439';OUT.mkdir(exist_ok=True)
def stamp():return datetime.now(timezone.utc).isoformat()
def verify():
for line in (PACKET/'candidate-manifest.sha256').read_text().splitlines():
sha,path=line.split(' ',1)
if hashlib.sha256((ROOT/path).read_bytes()).hexdigest()!=sha:raise RuntimeError('candidate drift: '+path)
def sweep():
found=[]
for proc in Path('/proc').glob('[0-9]*'):
try:
cwd=(proc/'cwd').resolve();name=(proc/'comm').read_text().strip()
if (str(cwd).startswith(str(ROOT)) or str(cwd).startswith('/mnt/storage/scratch/tmp/')) and ('node' in name or 'chrom' in name):found.append(dict(pid=int(proc.name),name=name,cwd=str(cwd)))
except (FileNotFoundError,PermissionError,ProcessLookupError):pass
return found
def execute(label,args,cwd=ROOT,deadline=300,docker=False):
if args[0]=='node' and '--test' in args and '--test-reporter=tap' not in args:args=[args[0],'--test-reporter=tap',*args[1:]]
if args[0]!='timeout':args=['timeout','-k','10',str(deadline),*args]
verify();short=subprocess.check_output(['mktemp','-d','/mnt/storage/scratch/tmp/rXXXXXX'],text=True).strip()
if not re.fullmatch(r'[A-Za-z0-9/.-]+',short):raise RuntimeError('unsafe TMPDIR charset')
env=os.environ.copy();env.update(TMPDIR=short,NO_COLOR='1',COMPOSE_PROJECT_NAME='rocko-r219-gate')
env.pop('MOSAIC_GITEA_CREDENTIAL_FILE',None);env.pop('MOSAIC_LIVE_PROVIDER',None)
if docker:env.pop('DOCKER_HOST',None)
else:env['DOCKER_HOST']='unix:///mnt/storage/scratch/rocko-r219-dead.sock'
log=OUT/(label+'.tap');begin=stamp();t=time.monotonic();timeout=False
print('START',label,begin,flush=True)
with log.open('wb') as f:
child=subprocess.Popen(args,cwd=cwd,env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True)
try:rc=child.wait(timeout=deadline+15)
except subprocess.TimeoutExpired:
timeout=True
try:os.killpg(child.pid,signal.SIGTERM)
except ProcessLookupError:pass
try:child.wait(timeout=10)
except subprocess.TimeoutExpired:
try:os.killpg(child.pid,signal.SIGKILL)
except ProcessLookupError:pass
child.wait()
rc=child.returncode
finally:
try:os.killpg(child.pid,signal.SIGTERM)
except ProcessLookupError:pass
timeout = timeout or rc in (124,137,-9)
text=log.read_text(errors='replace')
counts={k:int(m[-1]) if (m:=re.findall(r'^# '+k+r' (\d+)\s*$',text,re.M)) else None for k in ['tests','pass','fail','cancelled','skipped']}
row=dict(label=label,args=args,start=begin,end=stamp(),seconds=round(time.monotonic()-t,3),exit=rc,timeout=timeout,counts=counts,skips=[l for l in text.splitlines() if re.search(r'^skip\b|# SKIP\b',l,re.I)],log=str(log.relative_to(ROOT)),tmpdir=short)
with (OUT/'results.jsonl').open('a') as f:f.write(json.dumps(row)+'\n')
print('END',json.dumps(row),flush=True)
if rc or timeout or re.search(r'^\s*not ok\b',text,re.M):raise RuntimeError('RED '+label+' '+str(log))
return row
singles=[line for line in (PACKET/'changed-paths.txt').read_text().splitlines() if '/tests/' in line]
def stages():
for path in singles:execute('single-'+Path(path).parent.parent.name+'-'+Path(path).stem,['node','--test',path],deadline=300)
for package in ['bus','cli','webui','harness']:
files=sorted(glob.glob(str(ROOT/'packages'/package/'tests'/'*.test.mjs')))
execute('suite-'+package,['node','--test',*files],cwd=ROOT/'packages'/package,deadline=1800)
for path in sorted(ROOT.glob('scripts/test-*.sh')):
# timeout itself implements TERM then KILL, inside the dedicated child group.
execute(path.stem,['timeout','-k','10','7200','bash',str(path)],deadline=7200,docker=True)
# Every mutant starts from verified candidate bytes and restores in finally.
for spec in json.loads((PACKET/'mutants.json').read_text()):
verify();edits=[spec]+spec.get('additional_edits',[]);saved={}
try:
for e in edits:
path=ROOT/e['file'];saved.setdefault(path,path.read_bytes());text=path.read_text()
if text.count(e['old'])!=1:raise RuntimeError('anchor '+spec['id'])
path.write_text(text.replace(e['old'],e['new'],1))
# Mutation intentionally changes candidate bytes; execute has its own verify.
oldverify=globals()['verify'];globals()['verify']=lambda:None
try:
execute('mutant-'+spec['id'],['node','--test','--test-name-pattern',spec['pattern'],spec['test']],deadline=180)
status='survived'
except RuntimeError:
record=json.loads((OUT/'results.jsonl').read_text().splitlines()[-1]);text=(ROOT/record['log']).read_text(errors='replace')
failing=re.findall(r'^\s*not ok \d+ - (.*)$',text,re.M)
status='killed' if record['exit'] and not record['timeout'] and any(re.search(spec['pattern'],name) for name in failing) and 'SyntaxError:' not in text else 'invalid-failure'
finally:globals()['verify']=oldverify
finally:
for path,data in saved.items():path.write_bytes(data)
verify()
with (OUT/'mutants.jsonl').open('a') as f:f.write(json.dumps(dict(id=spec['id'],status=status,test=spec['test'],pattern=spec['pattern']))+'\n')
if status!='killed':raise RuntimeError(spec['id']+' '+status)
if __name__=='__main__':
if Path.cwd()!=ROOT:raise SystemExit('wrong cwd')
head=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()
expected=(PACKET/'run-head.txt').read_text().strip()
if head!=expected:raise SystemExit('wrong HEAD')
(OUT/'head.json').write_text(json.dumps(dict(head=head,manifest=hashlib.sha256((PACKET/'candidate-manifest.sha256').read_bytes()).hexdigest(),start=stamp())))
verify()
try:stages()
finally:
verify();(OUT/'cwd-sweep.json').write_text(json.dumps(sweep(),indent=2))
# Remove only the owned fixture compose network, if any; never daemon/volumes.
with (OUT/'network-cleanup.log').open('w') as f:
for network in ['rocko-r219-gate_default', ROOT.name+'_default']:
subprocess.run(['docker','network','rm',network],stdout=f,stderr=subprocess.STDOUT)
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
{"head": "06d7cc2f0ffc544abfb2755f99dce76ca164212c", "manifest": "420befed0aa8291736d34187ac7e745b7660efed15348ece045a7dd8957c9124", "start": "2026-10-23T08:55:46.512736+00:00"}
@@ -0,0 +1,2 @@
Error response from daemon: network rocko-r219-gate_default not found
exit status 1
@@ -0,0 +1,14 @@
{"label": "single-bus-creative.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/creative.test.mjs"], "start": "2026-10-23T08:55:46.514934+00:00", "end": "2026-10-23T08:55:46.880147+00:00", "seconds": 0.365, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-creative.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rlmkrn532"}
{"label": "single-bus-grant-executor.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T08:55:46.881647+00:00", "end": "2026-10-23T08:56:08.493753+00:00", "seconds": 21.612, "exit": 0, "timeout": false, "counts": {"tests": 42, "pass": 42, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-grant-executor.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rjpu1ic5m"}
{"label": "single-bus-project-runtime.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/project-runtime.test.mjs"], "start": "2026-10-23T08:56:08.495370+00:00", "end": "2026-10-23T08:56:08.910312+00:00", "seconds": 0.415, "exit": 0, "timeout": false, "counts": {"tests": 3, "pass": 3, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-project-runtime.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r6j9zk1mz"}
{"label": "single-bus-threads.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T08:56:08.911614+00:00", "end": "2026-10-23T08:56:10.278831+00:00", "seconds": 1.367, "exit": 0, "timeout": false, "counts": {"tests": 20, "pass": 20, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-threads.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/ridmpf8e9"}
{"label": "single-bus-user-principal.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T08:56:10.280242+00:00", "end": "2026-10-23T08:56:10.845658+00:00", "seconds": 0.565, "exit": 0, "timeout": false, "counts": {"tests": 23, "pass": 23, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-bus-user-principal.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rxak25z_4"}
{"label": "single-cli-launcher.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/launcher.test.mjs"], "start": "2026-10-23T08:56:10.847112+00:00", "end": "2026-10-23T08:56:37.072632+00:00", "seconds": 26.226, "exit": 0, "timeout": false, "counts": {"tests": 34, "pass": 34, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-cli-launcher.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r6tlrj8kb"}
{"label": "single-cli-thread-launch.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T08:56:37.074143+00:00", "end": "2026-10-23T08:56:55.131362+00:00", "seconds": 18.057, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-cli-thread-launch.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r9k_bmlhv"}
{"label": "single-webui-chat-browser.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T08:56:55.132860+00:00", "end": "2026-10-23T08:57:13.336278+00:00", "seconds": 18.203, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat-browser.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r3mqd7dfy"}
{"label": "single-webui-chat-instances.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T08:57:13.337687+00:00", "end": "2026-10-23T08:57:13.502100+00:00", "seconds": 0.164, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat-instances.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/ri0d5cw9g"}
{"label": "single-webui-chat.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T08:57:13.503425+00:00", "end": "2026-10-23T08:57:14.018577+00:00", "seconds": 0.515, "exit": 0, "timeout": false, "counts": {"tests": 25, "pass": 25, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/single-webui-chat.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rf6pbt6z6"}
{"label": "suite-bus", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/account-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/approval-binding.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/broker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/business.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/channel-proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/chief-of-staff.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-cursors.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-claim.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-store.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/csuite-messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/daily-quota-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-close.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decisions.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/end-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/fire-revokes-grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant-docker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-enroll.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-executor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/hard-waits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-run.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/human.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/instances-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/interpreter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/land.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launch-notice.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launcher-released.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library-sync.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-stop.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-citation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-filter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/name-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peer-transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peercred.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/plugins-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/principal.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/process.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-timeout.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-web.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-await.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-files.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-runtime.test.mjs", "/mnt/Line truncated
{"label": "suite-cli", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/app.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/boot-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-accept.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-hostcheck.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-persona-import.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-unit.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-recovery.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/compactions-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/config.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/follow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/format.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-auth.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-fixtures.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-image.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-state.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/git-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/hosts.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/idle-stop-race.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-account.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launcher.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/lifecycle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits-tokens.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/mark-read.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/notifier.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/other-tree.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/processing-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/publish-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-clear.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release-external.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-upgrade.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-http.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-threads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/trackers-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/verbs.test.mjs"], "start": "2026-10-23T08:57:37.800853+00:00", "end": "2026-10-23T09:08:05.517184+00:00", "seconds": 627.716, "exit": 0, "timeout": false, "counts": {"tests": 921, "pass": 921, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/suite-cli.tap", "tmpdir": "/mnt/storage/scratch/tmp/r29043nvq"}
{"label": "suite-webui", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/assistant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/board-held.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser-edge.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/channel-chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/conversation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/discord.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/docs-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-provenance.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/granted-user.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/history-return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/markdown-preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/pinned-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-ui.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-file.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/relaunch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/serve.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/service.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-form.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-write.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/shell.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-listener.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/views.test.mjs"], "start": "2026-10-23T09:08:05.524672+00:00", "end": "2026-10-23T09:09:02.641939+00:00", "seconds": 57.117, "exit": 0, "timeout": false, "counts": {"tests": 374, "pass": 373, "fail": 0, "cancelled": 0, "skipped": 1}, "skips": ["ok 107 - rebuilding from the source gives the same bytes (needLine truncated
{"label": "suite-harness", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/bundle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/git-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/mcp-server.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/pi-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/raise-domain.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/runner.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/skills.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/spec-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/tools.test.mjs"], "start": "2026-10-23T09:09:02.645756+00:00", "end": "2026-10-23T09:09:14.638654+00:00", "seconds": 11.993, "exit": 1, "timeout": false, "counts": {"tests": 151, "pass": 150, "fail": 1, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1429/suite-harness.tap", "tmpdir": "/mnt/storage/scratch/tmp/rh85v_4j3"}
@@ -0,0 +1,36 @@
TAP version 13
# organization: {"event":"reload-needs-restart","business":"demo"}
# organization: {"event":"reload-needs-restart","business":"demo"}
# Subtest: M6: a creative raise refuses decision-not-allowed, cited or not, and no decision row appears; the assistant still raises
ok 1 - M6: a creative raise refuses decision-not-allowed, cited or not, and no decision row appears; the assistant still raises
---
duration_ms: 37.351559
type: 'test'
...
# Subtest: a creative instance sends only to the user and hears only from the user
ok 2 - a creative instance sends only to the user and hears only from the user
---
duration_ms: 44.084602
type: 'test'
...
# Subtest: M10 through the broker: the chief and the tester are refused on bean, the human's limits.set of its models key works
ok 3 - M10 through the broker: the chief and the tester are refused on bean, the human's limits.set of its models key works
---
duration_ms: 78.575531
type: 'test'
...
# Subtest: row 256 T6 the human deletes a creative instance through the broker; the chief and the tester are refused at the verb
ok 4 - row 256 T6 the human deletes a creative instance through the broker; the chief and the tester are refused at the verb
---
duration_ms: 65.006343
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 335.111631
@@ -0,0 +1,262 @@
TAP version 13
# Subtest: row 280: an occupied TCP port is attempted, refused, and retried with our sshd
ok 1 - row 280: an occupied TCP port is attempted, refused, and retried with our sshd
---
duration_ms: 85.854841
type: 'test'
...
# Subtest: row 280: every injected picker sample is unprivileged and outside the ephemeral range
ok 2 - row 280: every injected picker sample is unprivileged and outside the ephemeral range
---
duration_ms: 0.458132
type: 'test'
...
# Subtest: G7: an Approve installs the tagged line, logs in with the new key, and writes the key file, record and grant.applied
ok 3 - G7: an Approve installs the tagged line, logs in with the new key, and writes the key file, record and grant.applied
---
duration_ms: 682.514738
type: 'test'
...
# Subtest: G7: a commands change on a live grant updates the record in one transaction and keeps the key and line
ok 4 - G7: a commands change on a live grant updates the record in one transaction and keeps the key and line
---
duration_ms: 516.713046
type: 'test'
...
# Subtest: G8: a failure at install or verify removes the line and leaves no record, key file or reference
ok 5 - G8: a failure at install or verify removes the line and leaves no record, key file or reference
---
duration_ms: 1517.563204
type: 'test'
...
# Subtest: G8: a failed remove adds hostLineLeft and tells the human to sweep
ok 6 - G8: a failed remove adds hostLineLeft and tells the human to sweep
---
duration_ms: 647.910884
type: 'test'
...
# Subtest: G8: a failure in step 5 before commit unlinks the key file, drops the reference and removes the line
ok 7 - G8: a failure in step 5 before commit unlinks the key file, drops the reference and removes the line
---
duration_ms: 630.485061
type: 'test'
...
# Subtest: G8: a store fault in the commit transaction ends host-failed with no record, key file, reference or line
ok 8 - G8: a store fault in the commit transaction ends host-failed with no record, key file, reference or line
---
duration_ms: 630.630309
type: 'test'
...
# Subtest: G8: a failed step 1 check spends the decision and runs nothing remote
ok 9 - G8: a failed step 1 check spends the decision and runs nothing remote
---
duration_ms: 81.046373
type: 'test'
...
# Subtest: G9: a registry, definition or commands change between raise and Approve ends decision-mismatch before any remote step
ok 10 - G9: a registry, definition or commands change between raise and Approve ends decision-mismatch before any remote step
---
duration_ms: 521.712377
type: 'test'
...
# Subtest: G9: an agent no longer a role or no longer declaring host ends with its code, spent
ok 11 - G9: an agent no longer a role or no longer declaring host ends with its code, spent
---
duration_ms: 70.08876
type: 'test'
...
# Subtest: G10: a second start refuses decision-consumed, and an unresolved or agent-resolved decision not-authorized, writing nothing
ok 12 - G10: a second start refuses decision-consumed, and an unresolved or agent-resolved decision not-authorized, writing nothing
---
duration_ms: 514.836044
type: 'test'
...
# Subtest: G15: no event, message or decision holds the private key
ok 13 - G15: no event, message or decision holds the private key
---
duration_ms: 508.872296
type: 'test'
...
# Subtest: G23: a revoke during install ends grant.failed revoked with no key file, no grant and no line
ok 14 - G23: a revoke during install ends grant.failed revoked with no key file, no grant and no line
---
duration_ms: 529.060942
type: 'test'
...
# Subtest: G23 (row 249): an add that writes after an abort's remove leaves hostLineLeft until the sweep
ok 15 - G23 (row 249): an add that writes after an abort's remove leaves hostLineLeft until the sweep
---
duration_ms: 862.980682
type: 'test'
...
# Subtest: G23 (row 249): an add that writes after a timed-out install's remove leaves hostLineLeft until the sweep
ok 16 - G23 (row 249): an add that writes after a timed-out install's remove leaves hostLineLeft until the sweep
---
duration_ms: 858.582377
type: 'test'
...
# Subtest: G23: a revoke recorded after the raise and before the start ends step 1 revoked, and a held slot ends host-busy
ok 17 - G23: a revoke recorded after the raise and before the start ends step 1 revoked, and a held slot ends host-busy
---
duration_ms: 519.682325
type: 'test'
...
# Subtest: G24: names at 64 characters with . and _ give distinct tags of the fixed length
ok 18 - G24: names at 64 characters with . and _ give distinct tags of the fixed length
---
duration_ms: 0.396492
type: 'test'
...
# Subtest: G11: a restart after install removes the line, then ends interrupted with the tag, holding the slot until then
ok 19 - G11: a restart after install removes the line, then ends interrupted with the tag, holding the slot until then
---
duration_ms: 217.46658
type: 'test'
...
# Subtest: G11: a restart after a new key's commit, with the key file written, ends with no key file, record, reference or line
ok 20 - G11: a restart after a new key's commit, with the key file written, ends with no key file, record, reference or line
---
duration_ms: 222.032907
type: 'test'
...
# Subtest: G11: a restart after a commands change's commit leaves the record as it was and asks nothing of the host
ok 21 - G11: a restart after a commands change's commit leaves the record as it was and asks nothing of the host
---
duration_ms: 518.824945
type: 'test'
...
# Subtest: G11 (B2): a restart before step 1, spent or not yet spent, ends interrupted and nothing runs later
ok 22 - G11 (B2): a restart before step 1, spent or not yet spent, ends interrupted and nothing runs later
---
duration_ms: 69.832285
type: 'test'
...
# Subtest: G11: a remote undo that fails ends interrupted with hostLineLeft and the sweep hint
ok 23 - G11: a remote undo that fails ends interrupted with hostLineLeft and the sweep hint
---
duration_ms: 280.805045
type: 'test'
...
# Subtest: G11: a restart leaves a finished grant alone, and the reconcile adds nothing for it
ok 24 - G11: a restart leaves a finished grant alone, and the reconcile adds nothing for it
---
duration_ms: 510.059757
type: 'test'
...
# Subtest: N1 (G27): the start sweep unlinks host.<id> files with no record in loaded businesses only, and refuses a store dir that is not private
ok 25 - N1 (G27): the start sweep unlinks host.<id> files with no record in loaded businesses only, and refuses a store dir that is not private
---
duration_ms: 6.320299
type: 'test'
...
# Subtest: G12: the human's hosts.revoke deletes the record, the key file, the reference and the line, with a closed marker
ok 26 - G12: the human's hosts.revoke deletes the record, the key file, the reference and the line, with a closed marker
---
duration_ms: 654.552501
type: 'test'
...
# Subtest: G12 (N4): a revoke with no record still removes the computed tag's line, with granted false; bad args refuse
ok 27 - G12 (N4): a revoke with no record still removes the computed tag's line, with granted false; bad args refuse
---
duration_ms: 217.275867
type: 'test'
...
# Subtest: G13: a revoke during install aborts the executor, which ends revoked, and the line goes
ok 28 - G13: a revoke during install aborts the executor, which ends revoked, and the line goes
---
duration_ms: 538.809236
type: 'test'
...
# Subtest: G26: the human, the assistant and the tester revoke; the chief and a pm refuse; sweep is the human's or the tester's
ok 29 - G26: the human, the assistant and the tester revoke; the chief and a pm refuse; sweep is the human's or the tester's
---
duration_ms: 500.126327
type: 'test'
...
# Subtest: row 281 T8: a tester hosts.revoke refuses tester-dev-only once bus.tester is off, and a human revoke writes no job records
ok 30 - row 281 T8: a tester hosts.revoke refuses tester-dev-only once bus.tester is off, and a human revoke writes no job records
---
duration_ms: 660.584288
type: 'test'
...
# Subtest: G27: a restart after step 1 sweeps the key file and runs step 3 once the bus is up; status shows the revoke until then
ok 31 - G27: a restart after step 1 sweeps the key file and runs step 3 once the bus is up; status shows the revoke until then
---
duration_ms: 666.103534
type: 'test'
...
# Subtest: G27: after step 2 with the host down, the restart closes the marker with hostLineLeft; a marker without a revoke id is not reopened
ok 32 - G27: after step 2 with the host down, the restart closes the marker with hostLineLeft; a marker without a revoke id is not reopened
---
duration_ms: 942.572259
type: 'test'
...
# Subtest: G27: a key file a revoke could not unlink is reported left and goes at the next start
ok 33 - G27: a key file a revoke could not unlink is reported left and goes at the next start
---
duration_ms: 655.234738
type: 'test'
...
# Subtest: G28: a commands change, then a revoke, then a restart leave no record, key, reference or line
ok 34 - G28: a commands change, then a revoke, then a restart leave no record, key, reference or line
---
duration_ms: 681.058457
type: 'test'
...
# Subtest: G24 (B4): hosts.sweep removes only this business's recorded tags no live grant holds, and records what it left
ok 35 - G24 (B4): hosts.sweep removes only this business's recorded tags no live grant holds, and records what it left
---
duration_ms: 989.811098
type: 'test'
...
# Subtest: G12: the local steps finish before the host step, so a host that hangs leaves the agent no access
ok 36 - G12: the local steps finish before the host step, so a host that hangs leaves the agent no access
---
duration_ms: 711.940669
type: 'test'
...
# Subtest: G23 (X5): a revoke right after the spend reaches the executor's claim, so the grant ends revoked
ok 37 - G23 (X5): a revoke right after the spend reaches the executor's claim, so the grant ends revoked
---
duration_ms: 209.337803
type: 'test'
...
# Subtest: G23 (X5): a revoke that lands as verify returns ends revoked with no record, key file or line
ok 38 - G23 (X5): a revoke that lands as verify returns ends revoked with no record, key file or line
---
duration_ms: 650.317732
type: 'test'
...
# Subtest: G13: a revoke whose host remove fails still drops the record and key, and reports the line left for a sweep
ok 39 - G13: a revoke whose host remove fails still drops the record and key, and reports the line left for a sweep
---
duration_ms: 649.752917
type: 'test'
...
# Subtest: G22: enroll, check, grant, host.run, a commands change the next host.run uses, and a revoke that kills a run in flight
ok 40 - G22: enroll, check, grant, host.run, a commands change the next host.run uses, and a revoke that kills a run in flight
---
duration_ms: 1161.391549
type: 'test'
...
# Subtest: mos-1163: a grant whose agent became a creative role after the raise ends host-creative-role at step 1, spent, with nothing remote
ok 41 - mos-1163: a grant whose agent became a creative role after the raise ends host-creative-role at step 1, spent, with nothing remote
---
duration_ms: 64.590353
type: 'test'
...
# Subtest: sage-1908: once the agent of an applied grant is a creative role, host.run refuses host-creative-role, runs nothing remote and keeps the record
ok 42 - sage-1908: once the agent of an applied grant is a creative role, host.run refuses host-creative-role, runs nothing remote and keeps the record
---
duration_ms: 494.590559
type: 'test'
...
1..42
# tests 42
# suites 0
# pass 42
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 21536.413692
@@ -0,0 +1,28 @@
TAP version 13
# Subtest: Broker keeps the sandbox client import closure: only the listed imports, no lazy project import
ok 1 - Broker keeps the sandbox client import closure: only the listed imports, no lazy project import
---
duration_ms: 1.878382
type: 'test'
...
# Subtest: without a trusted project adapter all three async paths refuse project-not-configured
ok 2 - without a trusted project adapter all three async paths refuse project-not-configured
---
duration_ms: 39.792991
type: 'test'
...
# Subtest: real startBroker injects the project adapter for socket reads and bound-author record writes
ok 3 - real startBroker injects the project adapter for socket reads and bound-author record writes
---
duration_ms: 197.452786
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 346.195541
@@ -0,0 +1,130 @@
TAP version 13
# Subtest: REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger
ok 1 - REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger
---
duration_ms: 39.935415
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker
ok 2 - REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker
---
duration_ms: 68.167379
type: 'test'
...
# Subtest: REQ-MSG-1: a message to or from the assistant names its thread, or is refused
ok 3 - REQ-MSG-1: a message to or from the assistant names its thread, or is refused
---
duration_ms: 33.042545
type: 'test'
...
# Subtest: REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work
ok 4 - REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work
---
duration_ms: 589.697235
type: 'test'
...
# Subtest: REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance
ok 5 - REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance
---
duration_ms: 25.439145
type: 'test'
...
# Subtest: REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder
ok 6 - REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder
---
duration_ms: 32.810336
type: 'test'
...
# Subtest: REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it
ok 7 - REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it
---
duration_ms: 30.930747
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder
ok 8 - REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder
---
duration_ms: 33.583742
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder
ok 9 - REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder
---
duration_ms: 29.506668
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-THREAD-1 Q169: a role.revoke the human approved ends every thread run of the assistant
ok 10 - REQ-ROLE-2 REQ-THREAD-1 Q169: a role.revoke the human approved ends every thread run of the assistant
---
duration_ms: 27.727681
type: 'test'
...
# Subtest: Q169 REQ-USER-2: assistant.stop ends the human's own assistant thread runs, and only the human's
ok 11 - Q169 REQ-USER-2: assistant.stop ends the human's own assistant thread runs, and only the human's
---
duration_ms: 41.315113
type: 'test'
...
# Subtest: REQ-USER-2: the human verbs refuse another owner's thread
ok 12 - REQ-USER-2: the human verbs refuse another owner's thread
---
duration_ms: 28.275757
type: 'test'
...
# Subtest: REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one
ok 13 - REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one
---
duration_ms: 23.795562
type: 'test'
...
# Subtest: REQ-ORG-2: projects and threads are named once, and the repo names a business project
ok 14 - REQ-ORG-2: projects and threads are named once, and the repo names a business project
---
duration_ms: 24.710376
type: 'test'
...
# Subtest: REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing
ok 15 - REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing
---
duration_ms: 85.400186
type: 'test'
...
# Subtest: project.create refuses a repository name without its matching repo before any write
ok 16 - project.create refuses a repository name without its matching repo before any write
---
duration_ms: 15.391876
type: 'test'
...
# Subtest: project.create keeps unrelated names with and without a repository
ok 17 - project.create keeps unrelated names with and without a repository
---
duration_ms: 10.08154
type: 'test'
...
# Subtest: C2-09a: explicit creative instances bind independent threads, default sessions and holders
ok 18 - C2-09a: explicit creative instances bind independent threads, default sessions and holders
---
duration_ms: 46.325924
type: 'test'
...
# Subtest: C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
ok 19 - C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
---
duration_ms: 20.515175
type: 'test'
...
# Subtest: C2-09a: creative bindLaunch without a thread refuses thread-required
ok 20 - C2-09a: creative bindLaunch without a thread refuses thread-required
---
duration_ms: 9.391139
type: 'test'
...
1..20
# tests 20
# suites 0
# pass 20
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1297.818732
@@ -0,0 +1,148 @@
TAP version 13
# Subtest: R3: bindUser binds a granted user and refuses one with no live grant
ok 1 - R3: bindUser binds a granted user and refuses one with no live grant
---
duration_ms: 13.944093
type: 'test'
...
# Subtest: R3: a user named like the business human, the tester or a principal word is refused, whatever the grants say
ok 2 - R3: a user named like the business human, the tester or a principal word is refused, whatever the grants say
---
duration_ms: 9.742292
type: 'test'
...
# Subtest: R3: each collaborator verb is allowed to a granted user
ok 3 - R3: each collaborator verb is allowed to a granted user
---
duration_ms: 18.679921
type: 'test'
...
# Subtest: R3: every verb off the collaborator list refuses a user access-refused before its own checks
ok 4 - R3: every verb off the collaborator list refuses a user access-refused before its own checks
---
duration_ms: 99.486855
type: 'test'
...
# Subtest: R3: task, web and host verbs refuse a user access-refused
ok 5 - R3: task, web and host verbs refuse a user access-refused
---
duration_ms: 24.025805
type: 'test'
...
# Subtest: R3 leak: project.list shows granted projects only
ok 6 - R3 leak: project.list shows granted projects only
---
duration_ms: 13.165462
type: 'test'
...
# Subtest: R3 leak: thread.create needs the project and the thread instance granted, and an ungranted id reads as one that does not exist
ok 7 - R3 leak: thread.create needs the project and the thread instance granted, and an ungranted id reads as one that does not exist
---
duration_ms: 15.661272
type: 'test'
...
# Subtest: R3 leak: thread.list shows the user its own threads that are still granted, never others
ok 8 - R3 leak: thread.list shows the user its own threads that are still granted, never others
---
duration_ms: 32.621553
type: 'test'
...
# Subtest: R3 leak: names shows granted instances only
ok 9 - R3 leak: names shows granted instances only
---
duration_ms: 14.594611
type: 'test'
...
# Subtest: R3: thread.send is from user:<id> and writes no human.input
ok 10 - R3: thread.send is from user:<id> and writes no human.input
---
duration_ms: 12.108779
type: 'test'
...
# Subtest: R3: a reply-chain message.send into another user thread is refused
ok 11 - R3: a reply-chain message.send into another user thread is refused
---
duration_ms: 15.050058
type: 'test'
...
# Subtest: R3: the run reply to a user message reaches that user thread.messages, with no waiting and no notice
ok 12 - R3: the run reply to a user message reaches that user thread.messages, with no waiting and no notice
---
duration_ms: 19.068793
type: 'test'
...
# Subtest: R3: a reply to: user without a matching in_reply_to is refused user-reply-required
ok 13 - R3: a reply to: user without a matching in_reply_to is refused user-reply-required
---
duration_ms: 26.350152
type: 'test'
...
# Subtest: R3: a non-thread run, the human and the tester cannot send to: user
ok 14 - R3: a non-thread run, the human and the tester cannot send to: user
---
duration_ms: 17.161103
type: 'test'
...
# Subtest: R3: a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9
ok 15 - R3: a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9
---
duration_ms: 19.180463
type: 'test'
...
# Subtest: R3: a removed or expired grant refuses the next request access-revoked, and the file is read per request
ok 16 - R3: a removed or expired grant refuses the next request access-revoked, and the file is read per request
---
duration_ms: 16.523551
type: 'test'
...
# Subtest: R3: a user refusal is recorded as action.refused by user:<id>
ok 17 - R3: a user refusal is recorded as action.refused by user:<id>
---
duration_ms: 13.680695
type: 'test'
...
# Subtest: R3: a business with a role keyed user refuses invalid-business
ok 18 - R3: a business with a role keyed user refuses invalid-business
---
duration_ms: 10.04229
type: 'test'
...
# Subtest: R3: name.set refuses the name user
ok 19 - R3: name.set refuses the name user
---
duration_ms: 10.604386
type: 'test'
...
# Subtest: R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still answers
ok 20 - R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still answers
---
duration_ms: 14.542729
type: 'test'
...
# Subtest: R3 r4: the chief's hire refuses an instance named user with the code it gives human and tester
ok 21 - R3 r4: the chief's hire refuses an instance named user with the code it gives human and tester
---
duration_ms: 15.860218
type: 'test'
...
# Subtest: R3 r4: a non-function access throws before the store is read
ok 22 - R3 r4: a non-function access throws before the store is read
---
duration_ms: 0.137097
type: 'test'
...
# Subtest: C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
ok 23 - C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
---
duration_ms: 16.969684
type: 'test'
...
1..23
# tests 23
# suites 0
# pass 23
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 526.916212
@@ -0,0 +1,229 @@
TAP version 13
# Subtest: C2-04: launcher workspace clones code but never the library
ok 1 - C2-04: launcher workspace clones code but never the library
---
duration_ms: 1386.185829
type: 'test'
...
# Subtest: bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest
ok 2 - bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest
---
duration_ms: 1313.619586
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r199f875ccb7f","instance":"chief","turn":"37b28923-c276-4eeb-82e7-2f04edfc63b0","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"raee3d01ce9da","instance":"coder","turn":"e790f5a0-c0ac-4401-9351-30233ea05022","source":"@none","code":"baseline-unknown"}
# Subtest: the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code
ok 3 - the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code
---
duration_ms: 733.864246
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rd8d7c7627ba8","instance":"chief","turn":"a2a42660-2726-4c63-99cb-bd35899ba5fa","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"ra8bf65ecb11d","instance":"pm","turn":"b2e16af9-d95b-4b57-801d-1560ad84966f","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rd8d7c7627ba8","instance":"chief","turn":"288e9f5b-eb1f-4a66-a216-0e2e6e8a21c8","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"ra8bf65ecb11d","instance":"pm","turn":"47125f10-7905-43f3-9d19-50b94f2c2025","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"ra8bf65ecb11d","instance":"pm","turn":"96a194ac-2588-4135-ab20-7a4d85208a2c","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rd8d7c7627ba8","instance":"chief","turn":"b836f76e-02f0-44e2-902f-b820e5601ec1","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"ra8bf65ecb11d","instance":"pm","turn":"469bfa42-84ac-439a-8f22-a27ff0cd2b0f","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rd8d7c7627ba8","instance":"chief","turn":"79a1518c-ca9f-494e-87aa-df1ea958fd71","source":"@none","code":"baseline-unknown"}
# Subtest: a launched pm's request_staff reaches the chief of staff; no other session has the tool and the launch limits still hold
ok 4 - a launched pm's request_staff reaches the chief of staff; no other session has the tool and the launch limits still hold
---
duration_ms: 1196.320237
type: 'test'
...
# Subtest: each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged
ok 5 - each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged
---
duration_ms: 4059.673988
type: 'test'
...
# Subtest: a role the broker says holds a merge decision gets land_work, its prompt line, its landing and the git socket; without it, none
ok 6 - a role the broker says holds a merge decision gets land_work, its prompt line, its landing and the git socket; without it, none
---
duration_ms: 610.995934
type: 'test'
...
# Subtest: REQ-LAUNCH-1 the tick starts a listed merge decision holder once, with land_work, the Landing line and its landing turn; an unlisted holder starts nothing
ok 7 - REQ-LAUNCH-1 the tick starts a listed merge decision holder once, with land_work, the Landing line and its landing turn; an unlisted holder starts nothing
---
duration_ms: 364.057414
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r6bea6ac4c470","instance":"coder","turn":"d55a4936-6ea7-4bd6-986c-6e8cca2f0052","source":"@none","code":"baseline-unknown"}
# Subtest: a holder running since before the resolution is stopped once the broker releases it, land-relaunch, and the next tick starts it: landing turn first, then the message that waited
ok 8 - a holder running since before the resolution is stopped once the broker releases it, land-relaunch, and the next tick starts it: landing turn first, then the message that waited
---
duration_ms: 686.195959
type: 'test'
...
# Subtest: a landing run that ended isn't started again for its decision, a spent decision starts nothing, and a new decision starts the holder
ok 9 - a landing run that ended isn't started again for its decision, a spent decision starts nothing, and a new decision starts the holder
---
duration_ms: 633.426446
type: 'test'
...
# Subtest: after a restart, the new host's first tick starts a holder whose decision is still held
ok 10 - after a restart, the new host's first tick starts a holder whose decision is still held
---
duration_ms: 839.982216
type: 'test'
...
# Subtest: a refused landing start is logged once per code with its decision, isn't a start, and the next tick with room starts the holder
ok 11 - a refused landing start is logged once per code with its decision, isn't a start, and the next tick with room starts the holder
---
duration_ms: 622.725531
type: 'test'
...
# Subtest: a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left
ok 12 - a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left
---
duration_ms: 257.600871
type: 'test'
...
# Subtest: a runner that stops at once ends its launch with the runner's reason
ok 13 - a runner that stops at once ends its launch with the runner's reason
---
duration_ms: 336.235131
type: 'test'
...
# Subtest: a Claude Code launch with no session credential refuses before it starts
ok 14 - a Claude Code launch with no session credential refuses before it starts
---
duration_ms: 185.109906
type: 'test'
...
# Subtest: a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables
ok 15 - a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables
---
duration_ms: 376.058384
type: 'test'
...
# usage: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/mnt/storage/scratch/rocko-r219/packages/plugins/harness/down is missing","kept":[]}
# plugins: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/mnt/storage/scratch/rocko-r219/packages/plugins/harness/down is missing","kept":[]}
# Subtest: row 65 a failed primary preflight launches the fallback on a fixture harness module, recording the route and the reason
ok 16 - row 65 a failed primary preflight launches the fallback on a fixture harness module, recording the route and the reason
---
duration_ms: 375.264214
type: 'test'
...
# Subtest: a runner killed from the host ends its launch as killed, not failed
ok 17 - a runner killed from the host ends its launch as killed, not failed
---
duration_ms: 378.898804
type: 'test'
...
# Subtest: a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback
ok 18 - a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback
---
duration_ms: 189.672473
type: 'test'
...
# Subtest: row 64 a role skill that isn't enabled refuses skill-not-enabled, and one that isn't a plain directory skill-refused, before anything starts
ok 19 - row 64 a role skill that isn't enabled refuses skill-not-enabled, and one that isn't a plain directory skill-refused, before anything starts
---
duration_ms: 198.478931
type: 'test'
...
# Subtest: row 213 a creative instance's persona is read from <configDir>/personas: off its digest the launch fails with the mismatch as its reason
ok 20 - row 213 a creative instance's persona is read from <configDir>/personas: off its digest the launch fails with the mismatch as its reason
---
duration_ms: 273.044614
type: 'test'
...
# Subtest: row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts
ok 21 - row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts
---
duration_ms: 197.74503
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r8a3b586b8f2f","instance":"chief","turn":"4b701d58-8cbe-4466-a7de-18c2246211c5","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r04010eaba86b","instance":"coder","turn":"5bddbfc5-6a5a-4eab-b5ca-bf4fa2040769","source":"@none","code":"baseline-unknown"}
# Subtest: a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold
ok 22 - a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold
---
duration_ms: 1472.6753
type: 'test'
...
# Subtest: a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again
ok 23 - a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again
---
duration_ms: 1050.128747
type: 'test'
...
# Subtest: a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again
ok 24 - a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again
---
duration_ms: 3972.696162
type: 'test'
...
# Subtest: bus start --no-launch leaves an earlier host's live session alone: no signal, no end, sessions.json as it was
ok 25 - bus start --no-launch leaves an earlier host's live session alone: no signal, no end, sessions.json as it was
---
duration_ms: 232.233441
type: 'test'
...
# Subtest: bus start --no-launch refuses every launch: the PM launch and a session's request, each logged, nothing spawned
ok 26 - bus start --no-launch refuses every launch: the PM launch and a session's request, each logged, nothing spawned
---
duration_ms: 192.380723
type: 'test'
...
# Subtest: bus start --no-launch starts no thread tick; the same host with launches runs it
ok 27 - bus start --no-launch starts no thread tick; the same host with launches runs it
---
duration_ms: 1168.043143
type: 'test'
...
# Subtest: a malformed sessions.json refuses the host start with exit 3 and stays as it was
ok 28 - a malformed sessions.json refuses the host start with exit 3 and stays as it was
---
duration_ms: 173.460107
type: 'test'
...
# Subtest: an over-long launch request is refused at once, not at the 10 s idle timeout
ok 29 - an over-long launch request is refused at once, not at the 10 s idle timeout
---
duration_ms: 181.721277
type: 'test'
...
# Subtest: a launch client that never closes its side doesn't hold the host's close
ok 30 - a launch client that never closes its side doesn't hold the host's close
---
duration_ms: 184.217213
type: 'test'
...
# Subtest: a runner that ignores SIGTERM is killed when the host closes
ok 31 - a runner that ignores SIGTERM is killed when the host closes
---
duration_ms: 1355.394246
type: 'test'
...
# Subtest: kill switch refuses chief and hire before launch work and a clear permits a later request
ok 32 - kill switch refuses chief and hire before launch work and a clear permits a later request
---
duration_ms: 319.95812
type: 'test'
...
# Subtest: kill switch stops an actual managed namespace and records the halt reason without rewriting its bundle
ok 33 - kill switch stops an actual managed namespace and records the halt reason without rewriting its bundle
---
duration_ms: 327.164689
type: 'test'
...
# Subtest: a launch waiting across halt and resume cannot use its stale admission
ok 34 - a launch waiting across halt and resume cannot use its stale admission
---
duration_ms: 194.717407
type: 'test'
...
1..34
# tests 34
# suites 0
# pass 34
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 26156.328784
@@ -0,0 +1,113 @@
TAP version 13
# usage: {"event":"usage-unknown","business":"acme","run":"rc568ee5fade9","instance":"assistant","turn":"8a8b601e-f850-41ae-8370-4a4948e785e0","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r051f145959ab","instance":"assistant","turn":"19e244d5-1e6e-4797-a655-41f6380c26dd","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rec6f0012e42e","instance":"assistant","turn":"b77db7a5-f03b-480a-8c9e-258b2baed6a2","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rcbca03b4294d","instance":"assistant","turn":"0ea60c25-f171-47f8-b71f-2d3cad84b496","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it
ok 1 - REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it
---
duration_ms: 5892.762685
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r0404b31e175a","instance":"assistant","turn":"d0ac6a94-d898-4f7d-82be-4fcf63c992ac","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rc88d83c6460c","instance":"assistant","turn":"ead59d62-6b55-45cd-955d-caf53ee8e25f","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't
ok 2 - REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't
---
duration_ms: 1692.527776
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"ra1708b66d4b7","instance":"assistant","turn":"e27e2188-87f0-4971-967d-93a96eeedff4","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it
ok 3 - REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it
---
duration_ms: 4388.820082
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rfbd6a0a0afa4","instance":"assistant","turn":"ddec8f31-aec7-4eb4-90d8-bbd4e7ee0394","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rfbd6a0a0afa4","instance":"assistant","turn":"2f664ba9-6f69-4eab-86a2-62da25b64273","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it
ok 4 - REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it
---
duration_ms: 2886.685515
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session
ok 5 - REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session
---
duration_ms: 82.134614
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r6f1704540344","instance":"assistant","turn":"5132642f-f52a-436c-8648-6b11d6a0d812","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-LAUNCH-1 a thread run's session file has no landing, even when landHolders names its instance
ok 6 - REQ-LAUNCH-1 a thread run's session file has no landing, even when landHolders names its instance
---
duration_ms: 371.847425
type: 'test'
...
# Subtest: REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message
ok 7 - REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message
---
duration_ms: 223.179675
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing
ok 8 - REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing
---
duration_ms: 222.380867
type: 'test'
...
# Subtest: REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread
ok 9 - REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread
---
duration_ms: 80.396078
type: 'test'
...
# Subtest: C2-09a: fromSession refuses a creative instance outside a thread without a sandbox
ok 10 - C2-09a: fromSession refuses a creative instance outside a thread without a sandbox
---
duration_ms: 72.675249
type: 'test'
...
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0}
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0}
# Subtest: REQ-LIMIT-3 REQ-LIMIT-4 a thread start whose instance is at its token limit is refused, logged once with the limit and the count
ok 11 - REQ-LIMIT-3 REQ-LIMIT-4 a thread start whose instance is at its token limit is refused, logged once with the limit and the count
---
duration_ms: 194.687144
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r45d9f1feb664","instance":"assistant","turn":"94f62246-a46b-4bff-9b85-43cad6f2f9da","source":"@none","code":"baseline-unknown"}
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mustStop","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":1}
# Subtest: REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn: the reply lands, and the end record names the limit
ok 12 - REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn: the reply lands, and the end record names the limit
---
duration_ms: 363.771015
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r25fd94375762","instance":"bean","turn":"8a8a214f-68b7-4940-9b10-7764e16b4d0d","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r6d58cdce8c9f","instance":"chet","turn":"a12dee45-9efc-4542-9b7b-982f60843525","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r25fd94375762","instance":"bean","turn":"5b798386-3f51-4de8-82db-a66ac44276c6","source":"@none","code":"baseline-unknown"}
# Subtest: C2-09a: creative thread messages start distinct runs and keep each instance history separate
ok 13 - C2-09a: creative thread messages start distinct runs and keep each instance history separate
---
duration_ms: 749.150495
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r6b2fca8831e0","instance":"bean","turn":"936ceedd-82ae-41ae-81f6-06b862035858","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r579a6302e6d2","instance":"bean","turn":"73cb49bf-b4ea-4781-8a7f-4821a1e59286","source":"@none","code":"baseline-unknown"}
# Subtest: C2-09a: terminating one creative thread does not signal an unselected peer of the same instance
ok 14 - C2-09a: terminating one creative thread does not signal an unselected peer of the same instance
---
duration_ms: 639.42083
type: 'test'
...
1..14
# tests 14
# suites 0
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 17983.289749
@@ -0,0 +1,94 @@
TAP version 13
# Subtest: chat browser: list/create named projects and threads, hostile labels inert and no implicit writes
ok 1 - chat browser: list/create named projects and threads, hostile labels inert and no implicit writes
---
duration_ms: 691.67169
type: 'test'
...
# Subtest: chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes
ok 2 - chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes
---
duration_ms: 480.248405
type: 'test'
...
# Subtest: chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send
ok 3 - chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send
---
duration_ms: 597.238297
type: 'test'
...
# Subtest: chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads
ok 4 - chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads
---
duration_ms: 818.266171
type: 'test'
...
# Subtest: chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate
ok 5 - chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate
---
duration_ms: 502.038978
type: 'test'
...
# Subtest: chat browser: scrolled history stays put, New messages and an own send return to the tail
ok 6 - chat browser: scrolled history stays put, New messages and an own send return to the tail
---
duration_ms: 546.644083
type: 'test'
...
# Subtest: chat browser: automatic polling follows a reply without a POST or a lost draft
ok 7 - chat browser: automatic polling follows a reply without a POST or a lost draft
---
duration_ms: 10578.074278
type: 'test'
...
# Subtest: chat browser: display stays bounded while every forward page remains reachable
ok 8 - chat browser: display stays bounded while every forward page remains reachable
---
duration_ms: 581.659608
type: 'test'
...
# Subtest: chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown
ok 9 - chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown
---
duration_ms: 444.584502
type: 'test'
...
# Subtest: chat browser: a refusal elsewhere forgets private chat drafts
ok 10 - chat browser: a refusal elsewhere forgets private chat drafts
---
duration_ms: 583.995207
type: 'test'
...
# Subtest: chat browser: a late project creation receipt cannot navigate away from another thread
ok 11 - chat browser: a late project creation receipt cannot navigate away from another thread
---
duration_ms: 611.03461
type: 'test'
...
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
ok 12 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 560.328725
type: 'test'
...
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
ok 13 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 648.448462
type: 'test'
...
# Subtest: C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread
ok 14 - C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread
---
duration_ms: 425.089318
type: 'test'
...
1..14
# tests 14
# suites 0
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 18170.580111
@@ -0,0 +1,34 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 16.444565
type: 'test'
...
# Subtest: C2-09a metadata uses the private tester listener own bus and no human transport
ok 2 - C2-09a metadata uses the private tester listener own bus and no human transport
---
duration_ms: 2.808985
type: 'test'
...
# Subtest: C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields
ok 3 - C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields
---
duration_ms: 3.286588
type: 'test'
...
# Subtest: C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity
ok 4 - C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity
---
duration_ms: 2.026814
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 123.938342
@@ -0,0 +1,160 @@
TAP version 13
# Subtest: chat reads map only fixed verbs, strict forward paging and pure GET
ok 1 - chat reads map only fixed verbs, strict forward paging and pure GET
---
duration_ms: 34.208753
type: 'test'
...
# Subtest: chat refuses malformed and repeated query/id values before transport
ok 2 - chat refuses malformed and repeated query/id values before transport
---
duration_ms: 4.797632
type: 'test'
...
# Subtest: chat project.create: one verified write, fixed identity/class and private log
ok 3 - chat project.create: one verified write, fixed identity/class and private log
---
duration_ms: 2.811902
type: 'test'
...
# Subtest: chat project.create: each existing Console write fence makes zero bus calls
ok 4 - chat project.create: each existing Console write fence makes zero bus calls
---
duration_ms: 6.841947
type: 'test'
...
# Subtest: chat project.create: refusal or lost/malformed receipt never retries or invents success
ok 5 - chat project.create: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 3.588766
type: 'test'
...
# Subtest: chat thread.create: one verified write, fixed identity/class and private log
ok 6 - chat thread.create: one verified write, fixed identity/class and private log
---
duration_ms: 1.139425
type: 'test'
...
# Subtest: chat thread.create: each existing Console write fence makes zero bus calls
ok 7 - chat thread.create: each existing Console write fence makes zero bus calls
---
duration_ms: 5.609374
type: 'test'
...
# Subtest: chat thread.create: refusal or lost/malformed receipt never retries or invents success
ok 8 - chat thread.create: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 3.766206
type: 'test'
...
# Subtest: chat thread.send: one verified write, fixed identity/class and private log
ok 9 - chat thread.send: one verified write, fixed identity/class and private log
---
duration_ms: 1.047356
type: 'test'
...
# Subtest: chat thread.send: each existing Console write fence makes zero bus calls
ok 10 - chat thread.send: each existing Console write fence makes zero bus calls
---
duration_ms: 4.912497
type: 'test'
...
# Subtest: chat thread.send: refusal or lost/malformed receipt never retries or invents success
ok 11 - chat thread.send: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 2.781203
type: 'test'
...
# Subtest: chat thread.open: one verified write, fixed identity/class and private log
ok 12 - chat thread.open: one verified write, fixed identity/class and private log
---
duration_ms: 0.929315
type: 'test'
...
# Subtest: chat thread.open: each existing Console write fence makes zero bus calls
ok 13 - chat thread.open: each existing Console write fence makes zero bus calls
---
duration_ms: 5.993363
type: 'test'
...
# Subtest: chat thread.open: refusal or lost/malformed receipt never retries or invents success
ok 14 - chat thread.open: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 4.414109
type: 'test'
...
# Subtest: chat createThread receipt requires an instance even when its id is valid
ok 15 - chat createThread receipt requires an instance even when its id is valid
---
duration_ms: 1.984854
type: 'test'
...
# Subtest: chat write receipts expose only the fields belonging to their operation
ok 16 - chat write receipts expose only the fields belonging to their operation
---
duration_ms: 3.123412
type: 'test'
...
# Subtest: chat validates names, body, project and strict empty open payload
ok 17 - chat validates names, body, project and strict empty open payload
---
duration_ms: 3.374476
type: 'test'
...
# Subtest: chat read failures redact ownership and refuse invalid transport arrays
ok 18 - chat read failures redact ownership and refuse invalid transport arrays
---
duration_ms: 3.309239
type: 'test'
...
# Subtest: REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations
ok 19 - REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations
---
duration_ms: 82.677307
type: 'test'
...
# Subtest: chat repeats every POST boundary even when called behind an admitted request
ok 20 - chat repeats every POST boundary even when called behind an admitted request
---
duration_ms: 0.912069
type: 'test'
...
# Subtest: configured Console process injects chat through its existing human transport
ok 21 - configured Console process injects chat through its existing human transport
---
duration_ms: 135.46624
type: 'test'
...
# Subtest: chat assets, GET purity, method and unknown-route refusals are explicit
ok 22 - chat assets, GET purity, method and unknown-route refusals are explicit
---
duration_ms: 11.555701
type: 'test'
...
# Subtest: the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError
ok 23 - the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError
---
duration_ms: 0.45427
type: 'test'
...
# Subtest: C2-09a createThread forwards an explicit instance through the existing channel
ok 24 - C2-09a createThread forwards an explicit instance through the existing channel
---
duration_ms: 3.503543
type: 'test'
...
# Subtest: C2-09a malformed instance and extra thread fields refuse before the channel write
ok 25 - C2-09a malformed instance and extra thread fields refuse before the channel write
---
duration_ms: 3.129652
type: 'test'
...
1..25
# tests 25
# suites 0
# pass 25
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 442.329912
@@ -0,0 +1,19 @@
{
"counts": {
"tests": 2509,
"pass": 2506,
"fail": 1,
"cancelled": 0,
"skipped": 2
},
"skips": [
"ok 193 - X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token # SKIP no Docker or no local gitea/gitea:1.27.1; this test never pulls",
"ok 107 - rebuilding from the source gives the same bytes (needs the pinned esbuild; MOSAIC_REQUIRE_EDITOR_BUILD=1 makes its absence a failure) # SKIP esbuild 0.28.1 is installed at /mnt/storage/src/mosaic-stack/node_modules/esbuild; the editor rebuild needs exactly 0.28.2 (npm ci --ignore-scripts in packages/webui) (set MOSAIC_REQUIRE_EDITOR_BUILD=1 to fail instead of skip)"
],
"stop": "suite-harness",
"unrun": [
"shells",
"24 mutants",
"final reader audit"
]
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,945 @@
TAP version 13
# Subtest: sessionModel: the route is the instance's own vars, and a missing one refuses no-route by name
ok 1 - sessionModel: the route is the instance's own vars, and a missing one refuses no-route by name
---
duration_ms: 34.856426
type: 'test'
...
# Subtest: a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory
ok 2 - a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory
---
duration_ms: 9.851494
type: 'test'
...
# Subtest: a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools
ok 3 - a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools
---
duration_ms: 5.120431
type: 'test'
...
# Subtest: a chief-of-staff bundle lists end_run, change_organization and set_name as typed and nothing untyped, for either harness
ok 4 - a chief-of-staff bundle lists end_run, change_organization and set_name as typed and nothing untyped, for either harness
---
duration_ms: 8.232092
type: 'test'
...
# Subtest: the stop line names end_run for a role.end holder, and every other role keeps today's text
ok 5 - the stop line names end_run for a role.end holder, and every other role keeps today's text
---
duration_ms: 13.301817
type: 'test'
...
# Subtest: a claude-code bundle adds the wrapped gate hook and the MCP config
ok 6 - a claude-code bundle adds the wrapped gate hook and the MCP config
---
duration_ms: 8.191276
type: 'test'
...
# Subtest: the prompt's built-in tools line is the harness's own names, as the manifest lists them
ok 7 - the prompt's built-in tools line is the harness's own names, as the manifest lists them
---
duration_ms: 20.825509
type: 'test'
...
# Subtest: the prompt names the project clones in the workspace, and none when there are none
ok 8 - the prompt names the project clones in the workspace, and none when there are none
---
duration_ms: 12.281607
type: 'test'
...
# Subtest: the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review
ok 9 - the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review
---
duration_ms: 14.129552
type: 'test'
...
# Subtest: the prompt explains landing to a role with land_work, and to no other
ok 10 - the prompt explains landing to a role with land_work, and to no other
---
duration_ms: 7.467651
type: 'test'
...
# Subtest: the prompt tells the pm to ask the chief of staff for sessions, and no other role
ok 11 - the prompt tells the pm to ask the chief of staff for sessions, and no other role
---
duration_ms: 14.371613
type: 'test'
...
# Subtest: a bundle is written once: an existing file refuses
ok 12 - a bundle is written once: an existing file refuses
---
duration_ms: 3.698221
type: 'test'
...
# Subtest: a path with a single quote can't go into the hook command
ok 13 - a path with a single quote can't go into the hook command
---
duration_ms: 3.75786
type: 'test'
...
# Subtest: web grant on pi: the bundle has web_search and web_fetch, and no built-in web tool
ok 14 - web grant on pi: the bundle has web_search and web_fetch, and no built-in web tool
---
duration_ms: 5.149102
type: 'test'
...
# Subtest: no web grant on pi: no web tool in the bundle, and the gate blocks a web call
ok 15 - no web grant on pi: no web tool in the bundle, and the gate blocks a web call
---
duration_ms: 6.16053
type: 'test'
...
# Subtest: web grant on claude-code: the bundle has web_search and web_fetch, and no built-in web tool
ok 16 - web grant on claude-code: the bundle has web_search and web_fetch, and no built-in web tool
---
duration_ms: 4.111996
type: 'test'
...
# Subtest: no web grant on claude-code: no web tool in the bundle, and the gate blocks a web call
ok 17 - no web grant on claude-code: no web tool in the bundle, and the gate blocks a web call
---
duration_ms: 4.051441
type: 'test'
...
# Subtest: a granted session's gate lets the typed web tools through and still blocks the built-ins
ok 18 - a granted session's gate lets the typed web tools through and still blocks the built-ins
---
duration_ms: 3.69592
type: 'test'
...
# Subtest: search-source searxng-url: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
ok 19 - search-source searxng-url: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
---
duration_ms: 10.440022
type: 'test'
...
# Subtest: search-source searxng-bundled: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
ok 20 - search-source searxng-bundled: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
---
duration_ms: 11.39935
type: 'test'
...
# Subtest: search-source harness-builtin: claude-code gets WebSearch and WebFetch and no typed web tool, only when granted
ok 21 - search-source harness-builtin: claude-code gets WebSearch and WebFetch and no typed web tool, only when granted
---
duration_ms: 12.01268
type: 'test'
...
# Subtest: search-source harness-builtin is refused for a granted pi role, which has no built-in web tool
ok 22 - search-source harness-builtin is refused for a granted pi role, which has no built-in web tool
---
duration_ms: 3.963537
type: 'test'
...
# Subtest: allow exits 0, a deny exits 2 with the reason on stderr
ok 23 - allow exits 0, a deny exits 2 with the reason on stderr
---
duration_ms: 198.492155
type: 'test'
...
# Subtest: a missing or wrong policy, or a bad event, exits 2
ok 24 - a missing or wrong policy, or a bad event, exits 2
---
duration_ms: 112.79382
type: 'test'
...
# Subtest: the bundle's wrapped command: a missing gate or node still blocks
ok 25 - the bundle's wrapped command: a missing gate or node still blocks
---
duration_ms: 1094.954866
type: 'test'
...
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
ok 26 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
---
duration_ms: 963.051269
type: 'test'
...
# Subtest: claude: the hook alone blocks a path outside the workspace
ok 27 - claude: the hook alone blocks a path outside the workspace
---
duration_ms: 494.23923
type: 'test'
...
# Subtest: claude: a second turn resumes the first turn's session
ok 28 - claude: a second turn resumes the first turn's session
---
duration_ms: 903.815433
type: 'test'
...
# Subtest: claude adapter: --restricted is always passed
ok 29 - claude adapter: --restricted is always passed
---
duration_ms: 4.775681
type: 'test'
...
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
ok 30 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
---
duration_ms: 17.582625
type: 'test'
...
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
ok 31 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
---
duration_ms: 6.164679
type: 'test'
...
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
not ok 32 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
---
duration_ms: 872.829879
type: 'test'
location: '/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs:270:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
[
'MARKER-USER',
'MARKER-PARENT',
'MARKER-WS',
- 'MARKER-MEMORY'
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'MARKER-USER'
1: 'MARKER-PARENT'
2: 'MARKER-WS'
3: 'MARKER-MEMORY'
actual:
0: 'MARKER-USER'
1: 'MARKER-PARENT'
2: 'MARKER-WS'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs:298:10)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: claude: a missing hook or MCP file refuses before claude starts
ok 33 - claude: a missing hook or MCP file refuses before claude starts
---
duration_ms: 8.368667
type: 'test'
...
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
ok 34 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
---
duration_ms: 1785.018464
type: 'test'
...
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
ok 35 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
---
duration_ms: 853.709418
type: 'test'
...
# Subtest: M2: the gate refuses write, edit and bash to every creative role on pi and claude-code
ok 36 - M2: the gate refuses write, edit and bash to every creative role on pi and claude-code
---
duration_ms: 69.878437
type: 'test'
...
# Subtest: M6: no creative session gets raise_decision; the assistant and the chief of staff keep it
ok 37 - M6: no creative session gets raise_decision; the assistant and the chief of staff keep it
---
duration_ms: 24.848412
type: 'test'
...
# Subtest: web: research and companion get the broker's typed web tools, and a harness-builtin source refuses web-source-not-allowed
ok 38 - web: research and companion get the broker's typed web tools, and a harness-builtin source refuses web-source-not-allowed
---
duration_ms: 30.528945
type: 'test'
...
# Subtest: persona: the block sits between the contract and \#\# This session, fenced, with the name and digest
ok 39 - persona: the block sits between the contract and \#\# This session, fenced, with the name and digest
---
duration_ms: 9.664778
type: 'test'
...
# Subtest: persona: each unsafe or unpinned file refuses before the bundle directory is made
ok 40 - persona: each unsafe or unpinned file refuses before the bundle directory is made
---
duration_ms: 28.523424
type: 'test'
...
# Subtest: adapters pass the prompt file's absolute path; the persona and the prompt text never sit in argv
ok 41 - adapters pass the prompt file's absolute path; the persona and the prompt text never sit in argv
---
duration_ms: 38.381105
type: 'test'
...
# Subtest: the pi adapter refuses a missing, unreadable or directory prompt file before pi starts
ok 42 - the pi adapter refuses a missing, unreadable or directory prompt file before pi starts
---
duration_ms: 12.120999
type: 'test'
...
# Subtest: pi: policy tools and typed tools pass, anything else is blocked
ok 43 - pi: policy tools and typed tools pass, anything else is blocked
---
duration_ms: 4.788064
type: 'test'
...
# Subtest: claude: builtins map from pi names, typed tools need the mcp prefix
ok 44 - claude: builtins map from pi names, typed tools need the mcp prefix
---
duration_ms: 1.088307
type: 'test'
...
# Subtest: file tool paths must resolve inside the workspace
ok 45 - file tool paths must resolve inside the workspace
---
duration_ms: 2.117046
type: 'test'
...
# Subtest: pi's own path normalisation can't be used to step out
ok 46 - pi's own path normalisation can't be used to step out
---
duration_ms: 3.792384
type: 'test'
...
# Subtest: a symlink inside the workspace that points out is outside
ok 47 - a symlink inside the workspace that points out is outside
---
duration_ms: 2.415204
type: 'test'
...
# Subtest: a dangling symlink is refused at any depth, in both harnesses
ok 48 - a dangling symlink is refused at any depth, in both harnesses
---
duration_ms: 5.167029
type: 'test'
...
# Subtest: read is checked under every spelling pi's read would open, in both harnesses
ok 49 - read is checked under every spelling pi's read would open, in both harnesses
---
duration_ms: 30.76287
type: 'test'
...
# Subtest: other spellings cover directories, dangling links and pi's cwd
ok 50 - other spellings cover directories, dangling links and pi's cwd
---
duration_ms: 2.671956
type: 'test'
...
# Subtest: a relative path climbs from the workspace's real path, in both harnesses
ok 51 - a relative path climbs from the workspace's real path, in both harnesses
---
duration_ms: 10.167886
type: 'test'
...
# Subtest: claude path fields per tool
ok 52 - claude path fields per tool
---
duration_ms: 1.56407
type: 'test'
...
# Subtest: glob patterns stay inside the workspace
ok 53 - glob patterns stay inside the workspace
---
duration_ms: 1.269903
type: 'test'
...
# Subtest: a path that can't be checked is blocked
ok 54 - a path that can't be checked is blocked
---
duration_ms: 0.552002
type: 'test'
...
# Subtest: fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX
ok 55 - fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX
---
duration_ms: 350.146065
type: 'test'
...
# Subtest: fetch_review's have orders the working refs newest first within each clone, one clone after another
ok 56 - fetch_review's have orders the working refs newest first within each clone, one clone after another
---
duration_ms: 151.918127
type: 'test'
...
# Subtest: a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period
ok 57 - a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period
---
duration_ms: 1808.893824
type: 'test'
...
# Subtest: once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too
ok 58 - once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too
---
duration_ms: 302.665475
type: 'test'
...
# Subtest: land_work sends one land line and resolves the host's started answer
ok 59 - land_work sends one land line and resolves the host's started answer
---
duration_ms: 3.310262
type: 'test'
...
# Subtest: initialize, ping and tools/list
ok 60 - initialize, ping and tools/list
---
duration_ms: 71.924925
type: 'test'
...
# Subtest: tools/call goes through the tool socket; a refusal is an isError result
ok 61 - tools/call goes through the tool socket; a refusal is an isError result
---
duration_ms: 53.929883
type: 'test'
...
# Subtest: replies are compact JSON; over the cap a tool error names the size and the cap
ok 62 - replies are compact JSON; over the cap a tool error names the size and the cap
---
duration_ms: 62.845386
type: 'test'
...
# Subtest: unknown tools and methods are JSON-RPC errors and never reach the socket
ok 63 - unknown tools and methods are JSON-RPC errors and never reach the socket
---
duration_ms: 47.664888
type: 'test'
...
# Subtest: a missing argument is a usage error
ok 64 - a missing argument is a usage error
---
duration_ms: 33.983265
type: 'test'
...
# Subtest: pi: typed tools reach the socket, the gate blocks, agent_end writes the marker
ok 65 - pi: typed tools reach the socket, the gate blocks, agent_end writes the marker
---
duration_ms: 623.733255
type: 'test'
...
# Subtest: pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks
ok 66 - pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks
---
duration_ms: 437.262054
type: 'test'
...
# Subtest: pi: a write through a dangling symlink is blocked, and nothing appears outside
ok 67 - pi: a write through a dangling symlink is blocked, and nothing appears outside
---
duration_ms: 441.96058
type: 'test'
...
# Subtest: pi: a read is refused when pi would open another spelling outside
ok 68 - pi: a read is refused when pi would open another spelling outside
---
duration_ms: 442.313486
type: 'test'
...
# Subtest: pi: a relative path climbs from the real path of a workspace behind a symlink
ok 69 - pi: a relative path climbs from the real path of a workspace behind a symlink
---
duration_ms: 467.731209
type: 'test'
...
# Subtest: pi: a relative path climbs from the real path of a dataRoot behind a symlink
ok 70 - pi: a relative path climbs from the real path of a dataRoot behind a symlink
---
duration_ms: 434.995228
type: 'test'
...
# Subtest: pi: a missing extension refuses before any model call
ok 71 - pi: a missing extension refuses before any model call
---
duration_ms: 6.502861
type: 'test'
...
# Subtest: pi: an extension without its configuration fails pi's start
ok 72 - pi: an extension without its configuration fails pi's start
---
duration_ms: 380.396774
type: 'test'
...
# Subtest: pi: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
ok 73 - pi: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
---
duration_ms: 1358.948547
type: 'test'
...
# Subtest: pi: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
ok 74 - pi: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
---
duration_ms: 707.366425
type: 'test'
...
# Subtest: raise_decision offers domain as technical or delivery only, optional, and says where a raise goes
ok 75 - raise_decision offers domain as technical or delivery only, optional, and says where a raise goes
---
duration_ms: 27.051506
type: 'test'
...
# Subtest: raise_decision forwards domain to decision.raise, and a call without it sends none
ok 76 - raise_decision forwards domain to decision.raise, and a call without it sends none
---
duration_ms: 90.915335
type: 'test'
...
# Subtest: founderCheck: founder variables, then a needed service without a usable token
ok 77 - founderCheck: founder variables, then a needed service without a usable token
---
duration_ms: 2.609338
type: 'test'
...
# Subtest: turnRequest names the sender, class, reply and decision
ok 78 - turnRequest names the sender, class, reply and decision
---
duration_ms: 0.351146
type: 'test'
...
# Subtest: turnRequest says a tester's message is a test and never Jason's instruction
ok 79 - turnRequest says a tester's message is a test and never Jason's instruction
---
duration_ms: 0.451421
type: 'test'
...
# Subtest: turnRequest says a user's message is a granted user's and never the owner's instruction
ok 80 - turnRequest says a user's message is a granted user's and never the owner's instruction
---
duration_ms: 0.338738
type: 'test'
...
# Subtest: turnRequest shows the message's own sent time, a day old as well as new
ok 81 - turnRequest shows the message's own sent time, a day old as well as new
---
duration_ms: 0.838097
type: 'test'
...
# Subtest: stateBlock names the launch state with its time and event, and the open tasks with the cap
ok 82 - stateBlock names the launch state with its time and event, and the open tasks with the cap
---
duration_ms: 0.438412
type: 'test'
...
# Subtest: a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0
ok 83 - a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0
---
duration_ms: 274.936534
type: 'test'
...
# Subtest: launches revoked and then restored: the turn's block reads allowed and gives the restore time
ok 84 - launches revoked and then restored: the turn's block reads allowed and gives the restore time
---
duration_ms: 231.401784
type: 'test'
...
# Subtest: a task closed on the tracker is missing from the next turn's open list
ok 85 - a task closed on the tracker is missing from the next turn's open list
---
duration_ms: 225.343214
type: 'test'
...
# Subtest: a message read a day after it was sent shows the time it was sent
ok 86 - a message read a day after it was sent shows the time it was sent
---
duration_ms: 114.024862
type: 'test'
...
# Subtest: a decision the human resolves becomes a turn citing the decision and the answer
ok 87 - a decision the human resolves becomes a turn citing the decision and the answer
---
duration_ms: 165.1602
type: 'test'
...
# Subtest: a failed state read fails the turn: no adapter runs, the sender hears why, the message is read
ok 88 - a failed state read fails the turn: no adapter runs, the sender hears why, the message is read
---
duration_ms: 87.646023
type: 'test'
...
# Subtest: a SIGTERM before the claim stops the runner with exit 0 and no claim
ok 89 - a SIGTERM before the claim stops the runner with exit 0 and no claim
---
duration_ms: 73.002788
type: 'test'
...
# Subtest: typed tools carry the runner's capability; launch goes to the host's launch socket
ok 90 - typed tools carry the runner's capability; launch goes to the host's launch socket
---
duration_ms: 329.934911
type: 'test'
...
# Subtest: end_run, organization_revision and change_organization reach the broker with the tool's arguments, and refusals keep their codes
ok 91 - end_run, organization_revision and change_organization reach the broker with the tool's arguments, and refusals keep their codes
---
duration_ms: 394.890047
type: 'test'
...
# Subtest: set_name reaches the broker's name.set with the tool's arguments, and refusals keep their codes
ok 92 - set_name reaches the broker's name.set with the tool's arguments, and refusals keep their codes
---
duration_ms: 172.910326
type: 'test'
...
# Subtest: a RESULT gets no automatic reply; failed turns reply with the reason
ok 93 - a RESULT gets no automatic reply; failed turns reply with the reason
---
duration_ms: 1262.379947
type: 'test'
...
# Subtest: the bus's launch notice is one turn with no reply, and is not taken again
ok 94 - the bus's launch notice is one turn with no reply, and is not taken again
---
duration_ms: 272.213669
type: 'test'
...
# Subtest: SIGTERM during a turn kills the turn's process group and still exits 0
ok 95 - SIGTERM during a turn kills the turn's process group and still exits 0
---
duration_ms: 119.660067
type: 'test'
...
# Subtest: founder credentials stop before the claim (20)
ok 96 - founder credentials stop before the claim (20)
---
duration_ms: 151.103872
type: 'test'
...
# Subtest: a refused claim exits 21; an ended run's capability exits 22
ok 97 - a refused claim exits 21; an ended run's capability exits 22
---
duration_ms: 161.135577
type: 'test'
...
# Subtest: the launch ending under a running session exits 22
ok 98 - the launch ending under a running session exits 22
---
duration_ms: 133.354952
type: 'test'
...
# Subtest: a broker that stays unreachable exits 23 after brokerRetries polls
ok 99 - a broker that stays unreachable exits 23 after brokerRetries polls
---
duration_ms: 228.779063
type: 'test'
...
# Subtest: a broker that is down at the claim exits 23, not 21
ok 100 - a broker that is down at the claim exits 23, not 21
---
duration_ms: 90.478293
type: 'test'
...
# Subtest: no capability, or a malformed one, on stdin exits 2
ok 101 - no capability, or a malformed one, on stdin exits 2
---
duration_ms: 198.639044
type: 'test'
...
# Subtest: a missing or malformed policy exits 2 before the claim
ok 102 - a missing or malformed policy exits 2 before the claim
---
duration_ms: 117.131672
type: 'test'
...
# Subtest: REQ-LIMIT-4 runner brackets its actual harness turn before the reply and read receipt
ok 103 - REQ-LIMIT-4 runner brackets its actual harness turn before the reply and read receipt
---
duration_ms: 166.804096
type: 'test'
...
# Subtest: REQ-LIMIT-4 a runner at its limit stops at the end of its turn: RESULT and read first, then exit 22 and a limit-check revoke
ok 104 - REQ-LIMIT-4 a runner at its limit stops at the end of its turn: RESULT and read first, then exit 22 and a limit-check revoke
---
duration_ms: 174.919803
type: 'test'
...
# Subtest: landingRequest is the fixed template, with the decision and task ref and nothing else
ok 105 - landingRequest is the fixed template, with the decision and task ref and nothing else
---
duration_ms: 0.164816
type: 'test'
...
# Subtest: each landing entry is one turn before any message, with the state block and the fixed request; it sends nothing and reads nothing
ok 106 - each landing entry is one turn before any message, with the state block and the fixed request; it sends nothing and reads nothing
---
duration_ms: 194.388382
type: 'test'
...
# Subtest: an empty landing takes no landing turn: the message is turn 1
ok 107 - an empty landing takes no landing turn: the message is turn 1
---
duration_ms: 169.087161
type: 'test'
...
# Subtest: a run in a landing turn isn't released for a second held decision until its landing turns end and it has read its messages
ok 108 - a run in a landing turn isn't released for a second held decision until its landing turns end and it has read its messages
---
duration_ms: 593.697326
type: 'test'
...
# Subtest: a malformed landing exits 2 before the claim, with no turn
ok 109 - a malformed landing exits 2 before the claim, with no turn
---
duration_ms: 588.465293
type: 'test'
...
# Subtest: row 65 a session.json without a turnEnd of marker or exit, or a harnessEnv of strings, exits 2 before the claim
ok 110 - row 65 a session.json without a turnEnd of marker or exit, or a harnessEnv of strings, exits 2 before the claim
---
duration_ms: 269.490704
type: 'test'
...
# Subtest: row 65 harnessEnv reaches the adapter, and an exit harness needs no turn marker
ok 111 - row 65 harnessEnv reaches the adapter, and an exit harness needs no turn marker
---
duration_ms: 140.939478
type: 'test'
...
# Subtest: row 66 the adapter gets the run's unslop log, and a flagged reply's note rides that reply's RESULT, once
ok 112 - row 66 the adapter gets the run's unslop log, and a flagged reply's note rides that reply's RESULT, once
---
duration_ms: 299.513041
type: 'test'
...
# Subtest: row 66 a turn with no reply holds its note for the next reply; a broken check is told once
ok 113 - row 66 a turn with no reply holds its note for the next reply; a broken check is told once
---
duration_ms: 344.053772
type: 'test'
...
# Subtest: row 66 unslopNotes keeps a note until sent(), reads only whole lines, and unslopCounts reads a log after the session
ok 114 - row 66 unslopNotes keeps a note until sent(), reads only whole lines, and unslopCounts reads a log after the session
---
duration_ms: 1.625977
type: 'test'
...
# Subtest: row 66 a clipped answer with both note parts stays under the bus's body limit, and the note under 600
ok 115 - row 66 a clipped answer with both note parts stays under the bus's body limit, and the note under 600
---
duration_ms: 164.004169
type: 'test'
...
# Subtest: row 66 a refused send keeps the note: it rides the next RESULT the session sends
ok 116 - row 66 a refused send keeps the note: it rides the next RESULT the session sends
---
duration_ms: 265.387583
type: 'test'
...
# Subtest: each repo role's prompt carries exactly its listed skills, each file as the bundle copy and the source
ok 117 - each repo role's prompt carries exactly its listed skills, each file as the bundle copy and the source
---
duration_ms: 67.932587
type: 'test'
...
# Subtest: a skill's files go into the prompt in sorted path order, sub-directories included
ok 118 - a skill's files go into the prompt in sorted path order, sub-directories included
---
duration_ms: 8.841973
type: 'test'
...
# Subtest: a role with no skills gets no Skills section, no skills directory and an empty manifest list
ok 119 - a role with no skills gets no Skills section, no skills directory and an empty manifest list
---
duration_ms: 9.451587
type: 'test'
...
# Subtest: an enabled skill the role doesn't list stays out of the prompt
ok 120 - an enabled skill the role doesn't list stays out of the prompt
---
duration_ms: 8.012055
type: 'test'
...
# Subtest: the bundle copy outlives the source: deactivating a skill after the build leaves the run's copy
ok 121 - the bundle copy outlives the source: deactivating a skill after the build leaves the run's copy
---
duration_ms: 4.002388
type: 'test'
...
# Subtest: a listed skill that isn't enabled refuses skill-not-enabled, naming the activate command
ok 122 - a listed skill that isn't enabled refuses skill-not-enabled, naming the activate command
---
duration_ms: 8.866982
type: 'test'
...
# Subtest: a skill directory that isn't plain refuses: a symlinked skill, SKILL.md or sub-file, or a non-file entry
ok 123 - a skill directory that isn't plain refuses: a symlinked skill, SKILL.md or sub-file, or a non-file entry
---
duration_ms: 2.434392
type: 'test'
...
# Subtest: a skill file that can't go into the prompt refuses: a bad name, non-UTF-8 bytes, or the end marker
ok 124 - a skill file that can't go into the prompt refuses: a bad name, non-UTF-8 bytes, or the end marker
---
duration_ms: 2.963756
type: 'test'
...
# Subtest: a role's skills over SKILL_BYTES together refuse, naming the skill that crossed it
ok 125 - a role's skills over SKILL_BYTES together refuse, naming the skill that crossed it
---
duration_ms: 10.218264
type: 'test'
...
# Subtest: each adapter passes the skills only in the prompt file, with skill discovery off
ok 126 - each adapter passes the skills only in the prompt file, with skill discovery off
---
duration_ms: 51.481115
type: 'test'
...
# Subtest: every role sees read_spec, record writer follows its grant, citation schemas stay strict
ok 127 - every role sees read_spec, record writer follows its grant, citation schemas stay strict
---
duration_ms: 26.090685
type: 'test'
...
# Subtest: typed socket forwards read content/history and agent record writes, and cannot write a spec
ok 128 - typed socket forwards read content/history and agent record writes, and cannot write a spec
---
duration_ms: 160.029157
type: 'test'
...
# Subtest: the PM gets its task verbs and the reads, and no launch
ok 129 - the PM gets its task verbs and the reads, and no launch
---
duration_ms: 16.993015
type: 'test'
...
# Subtest: send_message offers the BLOCK class, beside the classes it already had
ok 130 - send_message offers the BLOCK class, beside the classes it already had
---
duration_ms: 6.326843
type: 'test'
...
# Subtest: the chief of staff gets launch, send_message, end_run, hire, fire, change_organization and set_name, and no task verbs
ok 131 - the chief of staff gets launch, send_message, end_run, hire, fire, change_organization and set_name, and no task verbs
---
duration_ms: 6.133336
type: 'test'
...
# Subtest: hire and fire send instance.hire and instance.fire under role.hire and role.fire, only for a holder of the grant
ok 132 - hire and fire send instance.hire and instance.fire under role.hire and role.fire, only for a holder of the grant
---
duration_ms: 32.305882
type: 'test'
...
# Subtest: request_staff is the pm's alone: staff.request over message.send, roles and counts bounded
ok 133 - request_staff is the pm's alone: staff.request over message.send, roles and counts bounded
---
duration_ms: 11.979392
type: 'test'
...
# Subtest: end_run sends role and reason, no decision; change_organization sends revision and the organization keys
ok 134 - end_run sends role and reason, no decision; change_organization sends revision and the organization keys
---
duration_ms: 3.333633
type: 'test'
...
# Subtest: set_name sends instance and name to name.set under instance.name, only for a holder of that grant
ok 135 - set_name sends instance and name to name.set under instance.name, only for a holder of that grant
---
duration_ms: 11.144938
type: 'test'
...
# Subtest: the organization keys change_organization takes are the broker's ORGANIZATION_KEYS and the settings' SETTINGS_KEYS
ok 136 - the organization keys change_organization takes are the broker's ORGANIZATION_KEYS and the settings' SETTINGS_KEYS
---
duration_ms: 0.154911
type: 'test'
...
# Subtest: a role without role.end or organization.change gets none of the three tools, and each follows its own grant
ok 137 - a role without role.end or organization.change gets none of the three tools, and each follows its own grant
---
duration_ms: 11.053899
type: 'test'
...
# Subtest: no role's tools send instances or instances.set, and change_organization takes no instance write keys
ok 138 - no role's tools send instances or instances.set, and change_organization takes no instance write keys
---
duration_ms: 16.543469
type: 'test'
...
# Subtest: the assistant gets send_message, the reads and the web tools (it holds the web grant), no launch
ok 139 - the assistant gets send_message, the reads and the web tools (it holds the web grant), no launch
---
duration_ms: 2.023044
type: 'test'
...
# Subtest: list_requirements reads the requirement ids, for a task.create holder with a tracker
ok 140 - list_requirements reads the requirement ids, for a task.create holder with a tracker
---
duration_ms: 3.61918
type: 'test'
...
# Subtest: a coder gets no launch, no resolve_decision, and no task tools without a tracker
ok 141 - a coder gets no launch, no resolve_decision, and no task tools without a tracker
---
duration_ms: 1.306923
type: 'test'
...
# Subtest: a coder with a tracker gets its own task tools and no task_reassign
ok 142 - a coder with a tracker gets its own task tools and no task_reassign
---
duration_ms: 1.839146
type: 'test'
...
# Subtest: launch only when the business's launch block names the instance as launcher
ok 143 - launch only when the business's launch block names the instance as launcher
---
duration_ms: 3.69801
type: 'test'
...
# Subtest: an action outside the instance's authority has no tool
ok 144 - an action outside the instance's authority has no tool
---
duration_ms: 2.748294
type: 'test'
...
# Subtest: the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM
ok 145 - the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM
---
duration_ms: 7.876885
type: 'test'
...
# Subtest: land_work is there only with land, after push_working, with the task and the decision
ok 146 - land_work is there only with land, after push_working, with the task and the decision
---
duration_ms: 3.4848
type: 'test'
...
# Subtest: callTool: one JSON line out, the result back, a refusal rejects
ok 147 - callTool: one JSON line out, the result back, a refusal rejects
---
duration_ms: 19.887473
type: 'test'
...
# Subtest: list_tasks takes the broker's filters and pages; get_task takes a task_ref
ok 148 - list_tasks takes the broker's filters and pages; get_task takes a task_ref
---
duration_ms: 2.905426
type: 'test'
...
# Subtest: callTool sends list_tasks done false unless the session names done; other tools as given
ok 149 - callTool sends list_tasks done false unless the session names done; other tools as given
---
duration_ms: 3.254658
type: 'test'
...
# Subtest: replyText: compact JSON up to the cap, an error naming the size and the cap over it
ok 150 - replyText: compact JSON up to the cap, an error naming the size and the cap over it
---
duration_ms: 0.93267
type: 'test'
...
# Subtest: web_search's query maxLength is the web layer's SEARCH_MAX_QUERY_CHARS
ok 151 - web_search's query maxLength is the web layer's SEARCH_MAX_QUERY_CHARS
---
duration_ms: 2.974653
type: 'test'
...
1..151
# tests 151
# suites 0
# pass 150
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 11932.623925
File diff suppressed because it is too large. Load diff
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
{"head": "06d7cc2f0ffc544abfb2755f99dce76ca164212c", "manifest": "420befed0aa8291736d34187ac7e745b7660efed15348ece045a7dd8957c9124", "start": "2026-10-23T09:21:11.121484+00:00"}
@@ -0,0 +1,2 @@
Error response from daemon: network rocko-r219-gate_default not found
exit status 1
@@ -0,0 +1,23 @@
{"label": "single-bus-creative.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/creative.test.mjs"], "start": "2026-10-23T09:21:11.124448+00:00", "end": "2026-10-23T09:21:11.489626+00:00", "seconds": 0.365, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-creative.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rE8UFj4"}
{"label": "single-bus-grant-executor.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/grant-executor.test.mjs"], "start": "2026-10-23T09:21:11.492089+00:00", "end": "2026-10-23T09:21:33.052545+00:00", "seconds": 21.56, "exit": 0, "timeout": false, "counts": {"tests": 42, "pass": 42, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-grant-executor.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rgY53eZ"}
{"label": "single-bus-project-runtime.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/project-runtime.test.mjs"], "start": "2026-10-23T09:21:33.054938+00:00", "end": "2026-10-23T09:21:33.469616+00:00", "seconds": 0.415, "exit": 0, "timeout": false, "counts": {"tests": 3, "pass": 3, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-project-runtime.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rlFp4K6"}
{"label": "single-bus-threads.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/threads.test.mjs"], "start": "2026-10-23T09:21:33.471762+00:00", "end": "2026-10-23T09:21:34.838718+00:00", "seconds": 1.367, "exit": 0, "timeout": false, "counts": {"tests": 20, "pass": 20, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-threads.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rshM4G7"}
{"label": "single-bus-user-principal.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/bus/tests/user-principal.test.mjs"], "start": "2026-10-23T09:21:34.841038+00:00", "end": "2026-10-23T09:21:35.456433+00:00", "seconds": 0.615, "exit": 0, "timeout": false, "counts": {"tests": 23, "pass": 23, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-bus-user-principal.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYf1Xvx"}
{"label": "single-cli-launcher.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/launcher.test.mjs"], "start": "2026-10-23T09:21:35.458772+00:00", "end": "2026-10-23T09:22:02.030225+00:00", "seconds": 26.571, "exit": 0, "timeout": false, "counts": {"tests": 34, "pass": 34, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-cli-launcher.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/raf1e60"}
{"label": "single-cli-thread-launch.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/cli/tests/thread-launch.test.mjs"], "start": "2026-10-23T09:22:02.032690+00:00", "end": "2026-10-23T09:22:20.094607+00:00", "seconds": 18.062, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-cli-thread-launch.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/r1Bsuqs"}
{"label": "single-webui-chat-browser.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-browser.test.mjs"], "start": "2026-10-23T09:22:20.097013+00:00", "end": "2026-10-23T09:22:38.808507+00:00", "seconds": 18.711, "exit": 0, "timeout": false, "counts": {"tests": 14, "pass": 14, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat-browser.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYKVUnz"}
{"label": "single-webui-chat-instances.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat-instances.test.mjs"], "start": "2026-10-23T09:22:38.810775+00:00", "end": "2026-10-23T09:22:38.975118+00:00", "seconds": 0.164, "exit": 0, "timeout": false, "counts": {"tests": 4, "pass": 4, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat-instances.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rRn9NQr"}
{"label": "single-webui-chat.test", "args": ["timeout", "-k", "10", "300", "node", "--test-reporter=tap", "--test", "packages/webui/tests/chat.test.mjs"], "start": "2026-10-23T09:22:38.977256+00:00", "end": "2026-10-23T09:22:39.442318+00:00", "seconds": 0.465, "exit": 0, "timeout": false, "counts": {"tests": 25, "pass": 25, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/single-webui-chat.test.tap", "tmpdir": "/mnt/storage/scratch/tmp/rZRH3FV"}
{"label": "suite-bus", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/account-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/approval-binding.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/broker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/business.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/channel-proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/chief-of-staff.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-cursors.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/comment-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-claim.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-store.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credential-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/csuite-messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/daily-quota-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-close.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decision-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/decisions.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/end-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/fire-revokes-grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant-docker.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-grant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/gitea-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-enroll.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-executor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/grant-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/hard-waits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-run.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/host-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/human.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/instances-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/interpreter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/land.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launch-notice.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/launcher-released.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library-sync.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/library.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-revoke.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/lifecycle-verbs.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limit-stop.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-citation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/message-filter.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/messaging.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/name-migration.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peer-transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/peercred.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/plugins-verb.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/principal.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/process.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-runtime.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-timeout.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing-web.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-await.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-files.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/bus/tests/project-runtime.test.mjs", "/mnt/Line truncated
{"label": "suite-cli", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/access.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/app.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/boot-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-accept.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case1-hostcheck.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-persona-import.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/case2-unit.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/channel-recovery.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/compactions-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/config.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/credentials.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/follow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/format.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-auth.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-fixtures.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-image.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane-state.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/gate-lane.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/git-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/grants.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/host.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/hosts.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/idle-stop-race.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-account.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limit-checks.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launch-routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/launcher.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/lifecycle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits-tokens.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/limits.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/mark-read.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/names.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/notifier.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/other-tree.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/processing-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/project.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/publish-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-clear.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/session-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release-external.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-release.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/stack-upgrade.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-http.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester-threads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/tester.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/thread-launch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/trackers-boot.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/transport.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/cli/tests/verbs.test.mjs"], "start": "2026-10-23T09:23:03.024737+00:00", "end": "2026-10-23T09:33:27.205598+00:00", "seconds": 624.181, "exit": 0, "timeout": false, "counts": {"tests": 921, "pass": 921, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/suite-cli.tap", "tmpdir": "/mnt/storage/scratch/tmp/rl2zH3q"}
{"label": "suite-webui", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/agents.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/assistant.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/board-held.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser-edge.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/bus.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/channel-chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat-instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/chat.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/conversation.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/discord.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/docs-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor-provenance.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/editor.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/granted-user.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/history-return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/instances.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/library-status.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/limits-route.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/markdown-preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/messages.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/pinned-copy.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/preview.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing-ui.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/processing.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-file.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/project-name-repo.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/proof.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/reads.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/relaunch.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/return-flow.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/routes.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/send.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/serve.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/service.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-form.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/settings-write.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/shell.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/sso.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-cli.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-listener.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/tester-socket.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/thread-claims.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage-browser.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/usage.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/webui/tests/views.test.mjs"], "start": "2026-10-23T09:33:27.214222+00:00", "end": "2026-10-23T09:34:24.087380+00:00", "seconds": 56.873, "exit": 0, "timeout": false, "counts": {"tests": 374, "pass": 373, "fail": 0, "cancelled": 0, "skipped": 1}, "skips": ["ok 107 - rebuilding from the source gives the same bytes (needLine truncated
{"label": "suite-harness", "args": ["timeout", "-k", "10", "1800", "node", "--test-reporter=tap", "--test", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/bundle.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/claude-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/creative.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/gate.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/git-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/mcp-server.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/pi-session.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/raise-domain.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/runner.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/skills.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/spec-tools.test.mjs", "/mnt/storage/scratch/rocko-r219/packages/harness/tests/tools.test.mjs"], "start": "2026-10-23T09:34:24.092087+00:00", "end": "2026-10-23T09:34:36.185801+00:00", "seconds": 12.094, "exit": 0, "timeout": false, "counts": {"tests": 151, "pass": 151, "fail": 0, "cancelled": 0, "skipped": 0}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/suite-harness.tap", "tmpdir": "/mnt/storage/scratch/tmp/rYOp9Yo"}
{"label": "test-auth", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-auth.sh"], "start": "2026-10-23T09:34:36.189152+00:00", "end": "2026-10-23T09:34:37.005046+00:00", "seconds": 0.816, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-auth.tap", "tmpdir": "/mnt/storage/scratch/tmp/r7XakMj"}
{"label": "test-conductor", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-conductor.sh"], "start": "2026-10-23T09:34:37.007148+00:00", "end": "2026-10-23T09:34:43.835703+00:00", "seconds": 6.829, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-conductor.tap", "tmpdir": "/mnt/storage/scratch/tmp/rH7Fj9q"}
{"label": "test-config", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-config.sh"], "start": "2026-10-23T09:34:43.837837+00:00", "end": "2026-10-23T09:34:46.357228+00:00", "seconds": 2.519, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-config.tap", "tmpdir": "/mnt/storage/scratch/tmp/rXiHyiw"}
{"label": "test-discord", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-discord.sh"], "start": "2026-10-23T09:34:46.359375+00:00", "end": "2026-10-23T09:34:53.590250+00:00", "seconds": 7.231, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-discord.tap", "tmpdir": "/mnt/storage/scratch/tmp/rcHYakY"}
{"label": "test-extension-package", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-extension-package.sh"], "start": "2026-10-23T09:34:53.592317+00:00", "end": "2026-10-23T09:34:54.908860+00:00", "seconds": 1.317, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-extension-package.tap", "tmpdir": "/mnt/storage/scratch/tmp/rxrTT3R"}
{"label": "test-foundation", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-foundation.sh"], "start": "2026-10-23T09:34:54.910888+00:00", "end": "2026-10-23T09:35:33.605612+00:00", "seconds": 38.695, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-foundation.tap", "tmpdir": "/mnt/storage/scratch/tmp/rs2BwtK"}
{"label": "test-land-work", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-land-work.sh"], "start": "2026-10-23T09:35:33.607688+00:00", "end": "2026-10-23T09:35:45.646160+00:00", "seconds": 12.038, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-land-work.tap", "tmpdir": "/mnt/storage/scratch/tmp/rZFhfL5"}
{"label": "test-queue", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-queue.sh"], "start": "2026-10-23T09:35:45.648522+00:00", "end": "2026-10-23T09:36:06.604342+00:00", "seconds": 20.956, "exit": 0, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": ["skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-queue.tap", "tmpdir": "/mnt/storage/scratch/tmp/rAKp0eR"}
{"label": "test-release", "args": ["timeout", "-k", "10", "7200", "bash", "/mnt/storage/scratch/rocko-r219/scripts/test-release.sh"], "start": "2026-10-23T09:36:06.606593+00:00", "end": "2026-10-23T09:36:07.873175+00:00", "seconds": 1.267, "exit": 1, "timeout": false, "counts": {"tests": null, "pass": null, "fail": null, "cancelled": null, "skipped": null}, "skips": [], "log": "agents/rocko/work/queue-219/run-0780-mos1432/test-release.tap", "tmpdir": "/mnt/storage/scratch/tmp/rbunO3T"}
@@ -0,0 +1,36 @@
TAP version 13
# organization: {"event":"reload-needs-restart","business":"demo"}
# organization: {"event":"reload-needs-restart","business":"demo"}
# Subtest: M6: a creative raise refuses decision-not-allowed, cited or not, and no decision row appears; the assistant still raises
ok 1 - M6: a creative raise refuses decision-not-allowed, cited or not, and no decision row appears; the assistant still raises
---
duration_ms: 36.85368
type: 'test'
...
# Subtest: a creative instance sends only to the user and hears only from the user
ok 2 - a creative instance sends only to the user and hears only from the user
---
duration_ms: 43.623512
type: 'test'
...
# Subtest: M10 through the broker: the chief and the tester are refused on bean, the human's limits.set of its models key works
ok 3 - M10 through the broker: the chief and the tester are refused on bean, the human's limits.set of its models key works
---
duration_ms: 75.244973
type: 'test'
...
# Subtest: row 256 T6 the human deletes a creative instance through the broker; the chief and the tester are refused at the verb
ok 4 - row 256 T6 the human deletes a creative instance through the broker; the chief and the tester are refused at the verb
---
duration_ms: 59.840986
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 329.154565
@@ -0,0 +1,262 @@
TAP version 13
# Subtest: row 280: an occupied TCP port is attempted, refused, and retried with our sshd
ok 1 - row 280: an occupied TCP port is attempted, refused, and retried with our sshd
---
duration_ms: 84.792579
type: 'test'
...
# Subtest: row 280: every injected picker sample is unprivileged and outside the ephemeral range
ok 2 - row 280: every injected picker sample is unprivileged and outside the ephemeral range
---
duration_ms: 0.326649
type: 'test'
...
# Subtest: G7: an Approve installs the tagged line, logs in with the new key, and writes the key file, record and grant.applied
ok 3 - G7: an Approve installs the tagged line, logs in with the new key, and writes the key file, record and grant.applied
---
duration_ms: 664.962935
type: 'test'
...
# Subtest: G7: a commands change on a live grant updates the record in one transaction and keeps the key and line
ok 4 - G7: a commands change on a live grant updates the record in one transaction and keeps the key and line
---
duration_ms: 530.919797
type: 'test'
...
# Subtest: G8: a failure at install or verify removes the line and leaves no record, key file or reference
ok 5 - G8: a failure at install or verify removes the line and leaves no record, key file or reference
---
duration_ms: 1515.247467
type: 'test'
...
# Subtest: G8: a failed remove adds hostLineLeft and tells the human to sweep
ok 6 - G8: a failed remove adds hostLineLeft and tells the human to sweep
---
duration_ms: 652.389071
type: 'test'
...
# Subtest: G8: a failure in step 5 before commit unlinks the key file, drops the reference and removes the line
ok 7 - G8: a failure in step 5 before commit unlinks the key file, drops the reference and removes the line
---
duration_ms: 682.957188
type: 'test'
...
# Subtest: G8: a store fault in the commit transaction ends host-failed with no record, key file, reference or line
ok 8 - G8: a store fault in the commit transaction ends host-failed with no record, key file, reference or line
---
duration_ms: 652.039785
type: 'test'
...
# Subtest: G8: a failed step 1 check spends the decision and runs nothing remote
ok 9 - G8: a failed step 1 check spends the decision and runs nothing remote
---
duration_ms: 83.702893
type: 'test'
...
# Subtest: G9: a registry, definition or commands change between raise and Approve ends decision-mismatch before any remote step
ok 10 - G9: a registry, definition or commands change between raise and Approve ends decision-mismatch before any remote step
---
duration_ms: 501.685122
type: 'test'
...
# Subtest: G9: an agent no longer a role or no longer declaring host ends with its code, spent
ok 11 - G9: an agent no longer a role or no longer declaring host ends with its code, spent
---
duration_ms: 70.200852
type: 'test'
...
# Subtest: G10: a second start refuses decision-consumed, and an unresolved or agent-resolved decision not-authorized, writing nothing
ok 12 - G10: a second start refuses decision-consumed, and an unresolved or agent-resolved decision not-authorized, writing nothing
---
duration_ms: 495.905464
type: 'test'
...
# Subtest: G15: no event, message or decision holds the private key
ok 13 - G15: no event, message or decision holds the private key
---
duration_ms: 507.045691
type: 'test'
...
# Subtest: G23: a revoke during install ends grant.failed revoked with no key file, no grant and no line
ok 14 - G23: a revoke during install ends grant.failed revoked with no key file, no grant and no line
---
duration_ms: 487.107602
type: 'test'
...
# Subtest: G23 (row 249): an add that writes after an abort's remove leaves hostLineLeft until the sweep
ok 15 - G23 (row 249): an add that writes after an abort's remove leaves hostLineLeft until the sweep
---
duration_ms: 868.96012
type: 'test'
...
# Subtest: G23 (row 249): an add that writes after a timed-out install's remove leaves hostLineLeft until the sweep
ok 16 - G23 (row 249): an add that writes after a timed-out install's remove leaves hostLineLeft until the sweep
---
duration_ms: 854.053656
type: 'test'
...
# Subtest: G23: a revoke recorded after the raise and before the start ends step 1 revoked, and a held slot ends host-busy
ok 17 - G23: a revoke recorded after the raise and before the start ends step 1 revoked, and a held slot ends host-busy
---
duration_ms: 518.869102
type: 'test'
...
# Subtest: G24: names at 64 characters with . and _ give distinct tags of the fixed length
ok 18 - G24: names at 64 characters with . and _ give distinct tags of the fixed length
---
duration_ms: 0.35727
type: 'test'
...
# Subtest: G11: a restart after install removes the line, then ends interrupted with the tag, holding the slot until then
ok 19 - G11: a restart after install removes the line, then ends interrupted with the tag, holding the slot until then
---
duration_ms: 209.447782
type: 'test'
...
# Subtest: G11: a restart after a new key's commit, with the key file written, ends with no key file, record, reference or line
ok 20 - G11: a restart after a new key's commit, with the key file written, ends with no key file, record, reference or line
---
duration_ms: 212.583298
type: 'test'
...
# Subtest: G11: a restart after a commands change's commit leaves the record as it was and asks nothing of the host
ok 21 - G11: a restart after a commands change's commit leaves the record as it was and asks nothing of the host
---
duration_ms: 513.973581
type: 'test'
...
# Subtest: G11 (B2): a restart before step 1, spent or not yet spent, ends interrupted and nothing runs later
ok 22 - G11 (B2): a restart before step 1, spent or not yet spent, ends interrupted and nothing runs later
---
duration_ms: 69.77008
type: 'test'
...
# Subtest: G11: a remote undo that fails ends interrupted with hostLineLeft and the sweep hint
ok 23 - G11: a remote undo that fails ends interrupted with hostLineLeft and the sweep hint
---
duration_ms: 273.086714
type: 'test'
...
# Subtest: G11: a restart leaves a finished grant alone, and the reconcile adds nothing for it
ok 24 - G11: a restart leaves a finished grant alone, and the reconcile adds nothing for it
---
duration_ms: 509.129523
type: 'test'
...
# Subtest: N1 (G27): the start sweep unlinks host.<id> files with no record in loaded businesses only, and refuses a store dir that is not private
ok 25 - N1 (G27): the start sweep unlinks host.<id> files with no record in loaded businesses only, and refuses a store dir that is not private
---
duration_ms: 6.448287
type: 'test'
...
# Subtest: G12: the human's hosts.revoke deletes the record, the key file, the reference and the line, with a closed marker
ok 26 - G12: the human's hosts.revoke deletes the record, the key file, the reference and the line, with a closed marker
---
duration_ms: 656.766603
type: 'test'
...
# Subtest: G12 (N4): a revoke with no record still removes the computed tag's line, with granted false; bad args refuse
ok 27 - G12 (N4): a revoke with no record still removes the computed tag's line, with granted false; bad args refuse
---
duration_ms: 212.916285
type: 'test'
...
# Subtest: G13: a revoke during install aborts the executor, which ends revoked, and the line goes
ok 28 - G13: a revoke during install aborts the executor, which ends revoked, and the line goes
---
duration_ms: 556.515063
type: 'test'
...
# Subtest: G26: the human, the assistant and the tester revoke; the chief and a pm refuse; sweep is the human's or the tester's
ok 29 - G26: the human, the assistant and the tester revoke; the chief and a pm refuse; sweep is the human's or the tester's
---
duration_ms: 506.975086
type: 'test'
...
# Subtest: row 281 T8: a tester hosts.revoke refuses tester-dev-only once bus.tester is off, and a human revoke writes no job records
ok 30 - row 281 T8: a tester hosts.revoke refuses tester-dev-only once bus.tester is off, and a human revoke writes no job records
---
duration_ms: 656.756338
type: 'test'
...
# Subtest: G27: a restart after step 1 sweeps the key file and runs step 3 once the bus is up; status shows the revoke until then
ok 31 - G27: a restart after step 1 sweeps the key file and runs step 3 once the bus is up; status shows the revoke until then
---
duration_ms: 656.196081
type: 'test'
...
# Subtest: G27: after step 2 with the host down, the restart closes the marker with hostLineLeft; a marker without a revoke id is not reopened
ok 32 - G27: after step 2 with the host down, the restart closes the marker with hostLineLeft; a marker without a revoke id is not reopened
---
duration_ms: 948.521525
type: 'test'
...
# Subtest: G27: a key file a revoke could not unlink is reported left and goes at the next start
ok 33 - G27: a key file a revoke could not unlink is reported left and goes at the next start
---
duration_ms: 656.796578
type: 'test'
...
# Subtest: G28: a commands change, then a revoke, then a restart leave no record, key, reference or line
ok 34 - G28: a commands change, then a revoke, then a restart leave no record, key, reference or line
---
duration_ms: 681.732565
type: 'test'
...
# Subtest: G24 (B4): hosts.sweep removes only this business's recorded tags no live grant holds, and records what it left
ok 35 - G24 (B4): hosts.sweep removes only this business's recorded tags no live grant holds, and records what it left
---
duration_ms: 950.42161
type: 'test'
...
# Subtest: G12: the local steps finish before the host step, so a host that hangs leaves the agent no access
ok 36 - G12: the local steps finish before the host step, so a host that hangs leaves the agent no access
---
duration_ms: 706.747634
type: 'test'
...
# Subtest: G23 (X5): a revoke right after the spend reaches the executor's claim, so the grant ends revoked
ok 37 - G23 (X5): a revoke right after the spend reaches the executor's claim, so the grant ends revoked
---
duration_ms: 208.910785
type: 'test'
...
# Subtest: G23 (X5): a revoke that lands as verify returns ends revoked with no record, key file or line
ok 38 - G23 (X5): a revoke that lands as verify returns ends revoked with no record, key file or line
---
duration_ms: 653.947432
type: 'test'
...
# Subtest: G13: a revoke whose host remove fails still drops the record and key, and reports the line left for a sweep
ok 39 - G13: a revoke whose host remove fails still drops the record and key, and reports the line left for a sweep
---
duration_ms: 655.105432
type: 'test'
...
# Subtest: G22: enroll, check, grant, host.run, a commands change the next host.run uses, and a revoke that kills a run in flight
ok 40 - G22: enroll, check, grant, host.run, a commands change the next host.run uses, and a revoke that kills a run in flight
---
duration_ms: 1175.585222
type: 'test'
...
# Subtest: mos-1163: a grant whose agent became a creative role after the raise ends host-creative-role at step 1, spent, with nothing remote
ok 41 - mos-1163: a grant whose agent became a creative role after the raise ends host-creative-role at step 1, spent, with nothing remote
---
duration_ms: 64.065129
type: 'test'
...
# Subtest: sage-1908: once the agent of an applied grant is a creative role, host.run refuses host-creative-role, runs nothing remote and keeps the record
ok 42 - sage-1908: once the agent of an applied grant is a creative role, host.run refuses host-creative-role, runs nothing remote and keeps the record
---
duration_ms: 515.14952
type: 'test'
...
1..42
# tests 42
# suites 0
# pass 42
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 21511.94314
@@ -0,0 +1,28 @@
TAP version 13
# Subtest: Broker keeps the sandbox client import closure: only the listed imports, no lazy project import
ok 1 - Broker keeps the sandbox client import closure: only the listed imports, no lazy project import
---
duration_ms: 2.221263
type: 'test'
...
# Subtest: without a trusted project adapter all three async paths refuse project-not-configured
ok 2 - without a trusted project adapter all three async paths refuse project-not-configured
---
duration_ms: 46.879844
type: 'test'
...
# Subtest: real startBroker injects the project adapter for socket reads and bound-author record writes
ok 3 - real startBroker injects the project adapter for socket reads and bound-author record writes
---
duration_ms: 198.251134
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 358.792646
@@ -0,0 +1,130 @@
TAP version 13
# Subtest: REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger
ok 1 - REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger
---
duration_ms: 46.276479
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker
ok 2 - REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker
---
duration_ms: 69.469229
type: 'test'
...
# Subtest: REQ-MSG-1: a message to or from the assistant names its thread, or is refused
ok 3 - REQ-MSG-1: a message to or from the assistant names its thread, or is refused
---
duration_ms: 34.991913
type: 'test'
...
# Subtest: REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work
ok 4 - REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work
---
duration_ms: 594.420694
type: 'test'
...
# Subtest: REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance
ok 5 - REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance
---
duration_ms: 23.495652
type: 'test'
...
# Subtest: REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder
ok 6 - REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder
---
duration_ms: 27.65556
type: 'test'
...
# Subtest: REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it
ok 7 - REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it
---
duration_ms: 30.593037
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder
ok 8 - REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder
---
duration_ms: 32.534079
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder
ok 9 - REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder
---
duration_ms: 28.389825
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-THREAD-1 Q169: a role.revoke the human approved ends every thread run of the assistant
ok 10 - REQ-ROLE-2 REQ-THREAD-1 Q169: a role.revoke the human approved ends every thread run of the assistant
---
duration_ms: 25.464009
type: 'test'
...
# Subtest: Q169 REQ-USER-2: assistant.stop ends the human's own assistant thread runs, and only the human's
ok 11 - Q169 REQ-USER-2: assistant.stop ends the human's own assistant thread runs, and only the human's
---
duration_ms: 39.491556
type: 'test'
...
# Subtest: REQ-USER-2: the human verbs refuse another owner's thread
ok 12 - REQ-USER-2: the human verbs refuse another owner's thread
---
duration_ms: 28.048633
type: 'test'
...
# Subtest: REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one
ok 13 - REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one
---
duration_ms: 24.088048
type: 'test'
...
# Subtest: REQ-ORG-2: projects and threads are named once, and the repo names a business project
ok 14 - REQ-ORG-2: projects and threads are named once, and the repo names a business project
---
duration_ms: 26.256197
type: 'test'
...
# Subtest: REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing
ok 15 - REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing
---
duration_ms: 87.797965
type: 'test'
...
# Subtest: project.create refuses a repository name without its matching repo before any write
ok 16 - project.create refuses a repository name without its matching repo before any write
---
duration_ms: 16.297996
type: 'test'
...
# Subtest: project.create keeps unrelated names with and without a repository
ok 17 - project.create keeps unrelated names with and without a repository
---
duration_ms: 11.447173
type: 'test'
...
# Subtest: C2-09a: explicit creative instances bind independent threads, default sessions and holders
ok 18 - C2-09a: explicit creative instances bind independent threads, default sessions and holders
---
duration_ms: 46.283738
type: 'test'
...
# Subtest: C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
ok 19 - C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
---
duration_ms: 19.79704
type: 'test'
...
# Subtest: C2-09a: creative bindLaunch without a thread refuses thread-required
ok 20 - C2-09a: creative bindLaunch without a thread refuses thread-required
---
duration_ms: 10.088475
type: 'test'
...
1..20
# tests 20
# suites 0
# pass 20
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1306.906064
@@ -0,0 +1,148 @@
TAP version 13
# Subtest: R3: bindUser binds a granted user and refuses one with no live grant
ok 1 - R3: bindUser binds a granted user and refuses one with no live grant
---
duration_ms: 24.024956
type: 'test'
...
# Subtest: R3: a user named like the business human, the tester or a principal word is refused, whatever the grants say
ok 2 - R3: a user named like the business human, the tester or a principal word is refused, whatever the grants say
---
duration_ms: 9.836512
type: 'test'
...
# Subtest: R3: each collaborator verb is allowed to a granted user
ok 3 - R3: each collaborator verb is allowed to a granted user
---
duration_ms: 18.664347
type: 'test'
...
# Subtest: R3: every verb off the collaborator list refuses a user access-refused before its own checks
ok 4 - R3: every verb off the collaborator list refuses a user access-refused before its own checks
---
duration_ms: 103.87669
type: 'test'
...
# Subtest: R3: task, web and host verbs refuse a user access-refused
ok 5 - R3: task, web and host verbs refuse a user access-refused
---
duration_ms: 24.446629
type: 'test'
...
# Subtest: R3 leak: project.list shows granted projects only
ok 6 - R3 leak: project.list shows granted projects only
---
duration_ms: 13.549355
type: 'test'
...
# Subtest: R3 leak: thread.create needs the project and the thread instance granted, and an ungranted id reads as one that does not exist
ok 7 - R3 leak: thread.create needs the project and the thread instance granted, and an ungranted id reads as one that does not exist
---
duration_ms: 15.528212
type: 'test'
...
# Subtest: R3 leak: thread.list shows the user its own threads that are still granted, never others
ok 8 - R3 leak: thread.list shows the user its own threads that are still granted, never others
---
duration_ms: 32.908592
type: 'test'
...
# Subtest: R3 leak: names shows granted instances only
ok 9 - R3 leak: names shows granted instances only
---
duration_ms: 14.244576
type: 'test'
...
# Subtest: R3: thread.send is from user:<id> and writes no human.input
ok 10 - R3: thread.send is from user:<id> and writes no human.input
---
duration_ms: 12.891074
type: 'test'
...
# Subtest: R3: a reply-chain message.send into another user thread is refused
ok 11 - R3: a reply-chain message.send into another user thread is refused
---
duration_ms: 14.699119
type: 'test'
...
# Subtest: R3: the run reply to a user message reaches that user thread.messages, with no waiting and no notice
ok 12 - R3: the run reply to a user message reaches that user thread.messages, with no waiting and no notice
---
duration_ms: 18.27228
type: 'test'
...
# Subtest: R3: a reply to: user without a matching in_reply_to is refused user-reply-required
ok 13 - R3: a reply to: user without a matching in_reply_to is refused user-reply-required
---
duration_ms: 27.890498
type: 'test'
...
# Subtest: R3: a non-thread run, the human and the tester cannot send to: user
ok 14 - R3: a non-thread run, the human and the tester cannot send to: user
---
duration_ms: 18.357501
type: 'test'
...
# Subtest: R3: a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9
ok 15 - R3: a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9
---
duration_ms: 20.248033
type: 'test'
...
# Subtest: R3: a removed or expired grant refuses the next request access-revoked, and the file is read per request
ok 16 - R3: a removed or expired grant refuses the next request access-revoked, and the file is read per request
---
duration_ms: 18.534911
type: 'test'
...
# Subtest: R3: a user refusal is recorded as action.refused by user:<id>
ok 17 - R3: a user refusal is recorded as action.refused by user:<id>
---
duration_ms: 13.664712
type: 'test'
...
# Subtest: R3: a business with a role keyed user refuses invalid-business
ok 18 - R3: a business with a role keyed user refuses invalid-business
---
duration_ms: 9.426995
type: 'test'
...
# Subtest: R3: name.set refuses the name user
ok 19 - R3: name.set refuses the name user
---
duration_ms: 10.045778
type: 'test'
...
# Subtest: R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still answers
ok 20 - R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still answers
---
duration_ms: 13.715167
type: 'test'
...
# Subtest: R3 r4: the chief's hire refuses an instance named user with the code it gives human and tester
ok 21 - R3 r4: the chief's hire refuses an instance named user with the code it gives human and tester
---
duration_ms: 14.879652
type: 'test'
...
# Subtest: R3 r4: a non-function access throws before the store is read
ok 22 - R3 r4: a non-function access throws before the store is read
---
duration_ms: 0.118911
type: 'test'
...
# Subtest: C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
ok 23 - C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
---
duration_ms: 16.011863
type: 'test'
...
1..23
# tests 23
# suites 0
# pass 23
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 546.253182
@@ -0,0 +1,231 @@
TAP version 13
# Subtest: C2-04: launcher workspace clones code but never the library
ok 1 - C2-04: launcher workspace clones code but never the library
---
duration_ms: 1406.523375
type: 'test'
...
# Subtest: bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest
ok 2 - bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest
---
duration_ms: 1320.670671
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r3012fd8ab4ab","instance":"chief","turn":"6ed30efc-4fea-4331-ba08-46f0a1dc9a41","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r00eaf5e634a6","instance":"coder","turn":"10861190-6c32-41eb-93b4-29b0a5dd8c00","source":"@none","code":"baseline-unknown"}
# Subtest: the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code
ok 3 - the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code
---
duration_ms: 730.802466
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r1fd25c512b27","instance":"chief","turn":"cec8f880-bf75-4659-8da8-ed89c1dbf6e9","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r21039fe49a81","instance":"pm","turn":"eaa555de-47cd-41f1-8f03-56b33e8a74bd","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r1fd25c512b27","instance":"chief","turn":"4229d602-cd6b-4750-bf5b-b323b8a6f858","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r1fd25c512b27","instance":"chief","turn":"954dca21-de1b-4976-93c6-84f21c2dbd09","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r21039fe49a81","instance":"pm","turn":"ab644a01-0890-425c-92ad-b29a72ad01ed","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r21039fe49a81","instance":"pm","turn":"f63766ea-00f5-46aa-9c1c-4efd8d9e707b","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r1fd25c512b27","instance":"chief","turn":"c621466f-f3c9-4e9b-b41a-f4c8dc8fb5f8","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r21039fe49a81","instance":"pm","turn":"bedfe37d-d6ed-4461-82a3-f5dda0a0373a","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r1fd25c512b27","instance":"chief","turn":"03c5fd16-fd6d-47ea-84c1-b6845607ce0e","source":"@none","code":"baseline-unknown"}
# Subtest: a launched pm's request_staff reaches the chief of staff; no other session has the tool and the launch limits still hold
ok 4 - a launched pm's request_staff reaches the chief of staff; no other session has the tool and the launch limits still hold
---
duration_ms: 1051.54464
type: 'test'
...
# Subtest: each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged
ok 5 - each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged
---
duration_ms: 4090.531559
type: 'test'
...
# Subtest: a role the broker says holds a merge decision gets land_work, its prompt line, its landing and the git socket; without it, none
ok 6 - a role the broker says holds a merge decision gets land_work, its prompt line, its landing and the git socket; without it, none
---
duration_ms: 618.874624
type: 'test'
...
# Subtest: REQ-LAUNCH-1 the tick starts a listed merge decision holder once, with land_work, the Landing line and its landing turn; an unlisted holder starts nothing
ok 7 - REQ-LAUNCH-1 the tick starts a listed merge decision holder once, with land_work, the Landing line and its landing turn; an unlisted holder starts nothing
---
duration_ms: 412.969873
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rea675e77cc9a","instance":"coder","turn":"454996a2-5763-466b-ac34-1a35df60907a","source":"@none","code":"baseline-unknown"}
# Subtest: a holder running since before the resolution is stopped once the broker releases it, land-relaunch, and the next tick starts it: landing turn first, then the message that waited
ok 8 - a holder running since before the resolution is stopped once the broker releases it, land-relaunch, and the next tick starts it: landing turn first, then the message that waited
---
duration_ms: 701.16635
type: 'test'
...
# Subtest: a landing run that ended isn't started again for its decision, a spent decision starts nothing, and a new decision starts the holder
ok 9 - a landing run that ended isn't started again for its decision, a spent decision starts nothing, and a new decision starts the holder
---
duration_ms: 629.761324
type: 'test'
...
# Subtest: after a restart, the new host's first tick starts a holder whose decision is still held
ok 10 - after a restart, the new host's first tick starts a holder whose decision is still held
---
duration_ms: 943.001139
type: 'test'
...
# Subtest: a refused landing start is logged once per code with its decision, isn't a start, and the next tick with room starts the holder
ok 11 - a refused landing start is logged once per code with its decision, isn't a start, and the next tick with room starts the holder
---
duration_ms: 631.166574
type: 'test'
...
# Subtest: a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left
ok 12 - a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left
---
duration_ms: 270.930737
type: 'test'
...
# Subtest: a runner that stops at once ends its launch with the runner's reason
ok 13 - a runner that stops at once ends its launch with the runner's reason
---
duration_ms: 339.61276
type: 'test'
...
# Subtest: a Claude Code launch with no session credential refuses before it starts
ok 14 - a Claude Code launch with no session credential refuses before it starts
---
duration_ms: 192.912776
type: 'test'
...
# Subtest: a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables
ok 15 - a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables
---
duration_ms: 326.23852
type: 'test'
...
# usage: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/mnt/storage/scratch/rocko-r219/packages/plugins/harness/down is missing","kept":[]}
# plugins: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/mnt/storage/scratch/rocko-r219/packages/plugins/harness/down is missing","kept":[]}
# Subtest: row 65 a failed primary preflight launches the fallback on a fixture harness module, recording the route and the reason
ok 16 - row 65 a failed primary preflight launches the fallback on a fixture harness module, recording the route and the reason
---
duration_ms: 367.29797
type: 'test'
...
# Subtest: a runner killed from the host ends its launch as killed, not failed
ok 17 - a runner killed from the host ends its launch as killed, not failed
---
duration_ms: 385.709039
type: 'test'
...
# Subtest: a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback
ok 18 - a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback
---
duration_ms: 221.250023
type: 'test'
...
# Subtest: row 64 a role skill that isn't enabled refuses skill-not-enabled, and one that isn't a plain directory skill-refused, before anything starts
ok 19 - row 64 a role skill that isn't enabled refuses skill-not-enabled, and one that isn't a plain directory skill-refused, before anything starts
---
duration_ms: 196.084738
type: 'test'
...
# Subtest: row 213 a creative instance's persona is read from <configDir>/personas: off its digest the launch fails with the mismatch as its reason
ok 20 - row 213 a creative instance's persona is read from <configDir>/personas: off its digest the launch fails with the mismatch as its reason
---
duration_ms: 262.645812
type: 'test'
...
# Subtest: row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts
ok 21 - row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts
---
duration_ms: 192.972319
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r2d40ff6b8e4c","instance":"chief","turn":"fb22ae27-5633-49ee-8f68-c28e9216b756","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r2d40ff6b8e4c","instance":"chief","turn":"c1b15a96-c3e4-4937-ac09-5c7e34d31403","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r02b5676e3507","instance":"coder","turn":"0638932a-c3d1-405f-b6c9-f507dd774f5f","source":"@none","code":"baseline-unknown"}
# Subtest: a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold
ok 22 - a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold
---
duration_ms: 1637.361775
type: 'test'
...
# Subtest: a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again
ok 23 - a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again
---
duration_ms: 1055.418634
type: 'test'
...
# Subtest: a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again
ok 24 - a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again
---
duration_ms: 4026.232693
type: 'test'
...
# Subtest: bus start --no-launch leaves an earlier host's live session alone: no signal, no end, sessions.json as it was
ok 25 - bus start --no-launch leaves an earlier host's live session alone: no signal, no end, sessions.json as it was
---
duration_ms: 281.474074
type: 'test'
...
# Subtest: bus start --no-launch refuses every launch: the PM launch and a session's request, each logged, nothing spawned
ok 26 - bus start --no-launch refuses every launch: the PM launch and a session's request, each logged, nothing spawned
---
duration_ms: 205.160468
type: 'test'
...
# Subtest: bus start --no-launch starts no thread tick; the same host with launches runs it
ok 27 - bus start --no-launch starts no thread tick; the same host with launches runs it
---
duration_ms: 1177.367351
type: 'test'
...
# Subtest: a malformed sessions.json refuses the host start with exit 3 and stays as it was
ok 28 - a malformed sessions.json refuses the host start with exit 3 and stays as it was
---
duration_ms: 173.025482
type: 'test'
...
# Subtest: an over-long launch request is refused at once, not at the 10 s idle timeout
ok 29 - an over-long launch request is refused at once, not at the 10 s idle timeout
---
duration_ms: 179.463604
type: 'test'
...
# Subtest: a launch client that never closes its side doesn't hold the host's close
ok 30 - a launch client that never closes its side doesn't hold the host's close
---
duration_ms: 183.930813
type: 'test'
...
# Subtest: a runner that ignores SIGTERM is killed when the host closes
ok 31 - a runner that ignores SIGTERM is killed when the host closes
---
duration_ms: 1356.296508
type: 'test'
...
# Subtest: kill switch refuses chief and hire before launch work and a clear permits a later request
ok 32 - kill switch refuses chief and hire before launch work and a clear permits a later request
---
duration_ms: 321.746898
type: 'test'
...
# Subtest: kill switch stops an actual managed namespace and records the halt reason without rewriting its bundle
ok 33 - kill switch stops an actual managed namespace and records the halt reason without rewriting its bundle
---
duration_ms: 329.710571
type: 'test'
...
# Subtest: a launch waiting across halt and resume cannot use its stale admission
ok 34 - a launch waiting across halt and resume cannot use its stale admission
---
duration_ms: 190.011569
type: 'test'
...
1..34
# tests 34
# suites 0
# pass 34
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 26525.391292
@@ -0,0 +1,113 @@
TAP version 13
# usage: {"event":"usage-unknown","business":"acme","run":"rd9fe94c6a93c","instance":"assistant","turn":"acdd17ee-d9c0-41c9-9b18-03f0e4d7ccf1","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r488a73e7e21c","instance":"assistant","turn":"f40d47d4-d3e4-4ed8-9155-e3d28385a4f0","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r62e991ccbe40","instance":"assistant","turn":"5a93737f-88ea-4a36-a8df-d5af7c5c727f","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rab07b97c6b59","instance":"assistant","turn":"c366ee9b-47bc-41eb-89a5-6cdac996498e","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it
ok 1 - REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it
---
duration_ms: 5863.212229
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r8f921b27b736","instance":"assistant","turn":"960a3503-f2d2-4683-9b36-24dbfa43d9a8","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r2c17b223cf58","instance":"assistant","turn":"a2dec2ed-2ce7-4278-b023-d8a1eac619f0","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't
ok 2 - REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't
---
duration_ms: 1699.456613
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"ra7fc0a5e5087","instance":"assistant","turn":"e201b1b5-2e0c-4893-869f-e3c822d486e9","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it
ok 3 - REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it
---
duration_ms: 4443.468494
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"r25b13eb6e839","instance":"assistant","turn":"4b84a129-4452-48a3-9c82-8e847b026b42","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r25b13eb6e839","instance":"assistant","turn":"ca697e50-5aa6-4ecf-ab5f-0180f39671d8","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it
ok 4 - REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it
---
duration_ms: 2906.555821
type: 'test'
...
# Subtest: REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session
ok 5 - REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session
---
duration_ms: 77.448744
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rc92eb2ad48fb","instance":"assistant","turn":"86c5dc34-0018-468b-945d-cdde7c07cc1f","source":"@none","code":"baseline-unknown"}
# Subtest: REQ-LAUNCH-1 a thread run's session file has no landing, even when landHolders names its instance
ok 6 - REQ-LAUNCH-1 a thread run's session file has no landing, even when landHolders names its instance
---
duration_ms: 373.463679
type: 'test'
...
# Subtest: REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message
ok 7 - REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message
---
duration_ms: 220.418034
type: 'test'
...
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing
ok 8 - REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing
---
duration_ms: 218.661261
type: 'test'
...
# Subtest: REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread
ok 9 - REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread
---
duration_ms: 77.033197
type: 'test'
...
# Subtest: C2-09a: fromSession refuses a creative instance outside a thread without a sandbox
ok 10 - C2-09a: fromSession refuses a creative instance outside a thread without a sandbox
---
duration_ms: 74.007967
type: 'test'
...
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0}
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0}
# Subtest: REQ-LIMIT-3 REQ-LIMIT-4 a thread start whose instance is at its token limit is refused, logged once with the limit and the count
ok 11 - REQ-LIMIT-3 REQ-LIMIT-4 a thread start whose instance is at its token limit is refused, logged once with the limit and the count
---
duration_ms: 189.559346
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rb55b5fb42f65","instance":"assistant","turn":"1327e9e8-dbce-4d13-8d91-12224c10cc06","source":"@none","code":"baseline-unknown"}
# limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mustStop","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":1}
# Subtest: REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn: the reply lands, and the end record names the limit
ok 12 - REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn: the reply lands, and the end record names the limit
---
duration_ms: 340.483797
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rd4cf5ce21427","instance":"bean","turn":"27ddff31-caaa-4a9c-bda9-f36185b56dc9","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r890816a98bc2","instance":"chet","turn":"70137265-3ce6-434e-bd90-cef6fd12d283","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"rd4cf5ce21427","instance":"bean","turn":"1d6ad2b2-d083-438a-b96f-e65ed3230da1","source":"@none","code":"baseline-unknown"}
# Subtest: C2-09a: creative thread messages start distinct runs and keep each instance history separate
ok 13 - C2-09a: creative thread messages start distinct runs and keep each instance history separate
---
duration_ms: 743.366431
type: 'test'
...
# usage: {"event":"usage-unknown","business":"acme","run":"rb4c5e063b74b","instance":"bean","turn":"66cf82d6-45bc-49ff-9916-e9a2092d9e64","source":"@none","code":"baseline-unknown"}
# usage: {"event":"usage-unknown","business":"acme","run":"r6eef47e198ff","instance":"bean","turn":"357e8a93-e184-471e-803d-505682754bbc","source":"@none","code":"baseline-unknown"}
# Subtest: C2-09a: terminating one creative thread does not signal an unselected peer of the same instance
ok 14 - C2-09a: terminating one creative thread does not signal an unselected peer of the same instance
---
duration_ms: 646.331526
type: 'test'
...
1..14
# tests 14
# suites 0
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 17998.465895
@@ -0,0 +1,94 @@
TAP version 13
# Subtest: chat browser: list/create named projects and threads, hostile labels inert and no implicit writes
ok 1 - chat browser: list/create named projects and threads, hostile labels inert and no implicit writes
---
duration_ms: 706.684304
type: 'test'
...
# Subtest: chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes
ok 2 - chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes
---
duration_ms: 699.638509
type: 'test'
...
# Subtest: chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send
ok 3 - chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send
---
duration_ms: 667.019939
type: 'test'
...
# Subtest: chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads
ok 4 - chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads
---
duration_ms: 821.739159
type: 'test'
...
# Subtest: chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate
ok 5 - chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate
---
duration_ms: 561.063862
type: 'test'
...
# Subtest: chat browser: scrolled history stays put, New messages and an own send return to the tail
ok 6 - chat browser: scrolled history stays put, New messages and an own send return to the tail
---
duration_ms: 449.326852
type: 'test'
...
# Subtest: chat browser: automatic polling follows a reply without a POST or a lost draft
ok 7 - chat browser: automatic polling follows a reply without a POST or a lost draft
---
duration_ms: 10598.338648
type: 'test'
...
# Subtest: chat browser: display stays bounded while every forward page remains reachable
ok 8 - chat browser: display stays bounded while every forward page remains reachable
---
duration_ms: 688.844208
type: 'test'
...
# Subtest: chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown
ok 9 - chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown
---
duration_ms: 569.840106
type: 'test'
...
# Subtest: chat browser: a refusal elsewhere forgets private chat drafts
ok 10 - chat browser: a refusal elsewhere forgets private chat drafts
---
duration_ms: 578.081863
type: 'test'
...
# Subtest: chat browser: a late project creation receipt cannot navigate away from another thread
ok 11 - chat browser: a late project creation receipt cannot navigate away from another thread
---
duration_ms: 541.022347
type: 'test'
...
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
ok 12 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 518.744602
type: 'test'
...
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
ok 13 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 630.375708
type: 'test'
...
# Subtest: C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread
ok 14 - C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread
---
duration_ms: 532.387548
type: 'test'
...
1..14
# tests 14
# suites 0
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 18670.357703
@@ -0,0 +1,34 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 16.846446
type: 'test'
...
# Subtest: C2-09a metadata uses the private tester listener own bus and no human transport
ok 2 - C2-09a metadata uses the private tester listener own bus and no human transport
---
duration_ms: 2.896367
type: 'test'
...
# Subtest: C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields
ok 3 - C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields
---
duration_ms: 3.330037
type: 'test'
...
# Subtest: C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity
ok 4 - C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity
---
duration_ms: 2.085754
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 128.601956
@@ -0,0 +1,160 @@
TAP version 13
# Subtest: chat reads map only fixed verbs, strict forward paging and pure GET
ok 1 - chat reads map only fixed verbs, strict forward paging and pure GET
---
duration_ms: 22.1221
type: 'test'
...
# Subtest: chat refuses malformed and repeated query/id values before transport
ok 2 - chat refuses malformed and repeated query/id values before transport
---
duration_ms: 4.606275
type: 'test'
...
# Subtest: chat project.create: one verified write, fixed identity/class and private log
ok 3 - chat project.create: one verified write, fixed identity/class and private log
---
duration_ms: 2.784213
type: 'test'
...
# Subtest: chat project.create: each existing Console write fence makes zero bus calls
ok 4 - chat project.create: each existing Console write fence makes zero bus calls
---
duration_ms: 6.747634
type: 'test'
...
# Subtest: chat project.create: refusal or lost/malformed receipt never retries or invents success
ok 5 - chat project.create: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 3.56051
type: 'test'
...
# Subtest: chat thread.create: one verified write, fixed identity/class and private log
ok 6 - chat thread.create: one verified write, fixed identity/class and private log
---
duration_ms: 1.163659
type: 'test'
...
# Subtest: chat thread.create: each existing Console write fence makes zero bus calls
ok 7 - chat thread.create: each existing Console write fence makes zero bus calls
---
duration_ms: 5.496457
type: 'test'
...
# Subtest: chat thread.create: refusal or lost/malformed receipt never retries or invents success
ok 8 - chat thread.create: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 3.893248
type: 'test'
...
# Subtest: chat thread.send: one verified write, fixed identity/class and private log
ok 9 - chat thread.send: one verified write, fixed identity/class and private log
---
duration_ms: 1.062409
type: 'test'
...
# Subtest: chat thread.send: each existing Console write fence makes zero bus calls
ok 10 - chat thread.send: each existing Console write fence makes zero bus calls
---
duration_ms: 4.794857
type: 'test'
...
# Subtest: chat thread.send: refusal or lost/malformed receipt never retries or invents success
ok 11 - chat thread.send: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 2.739527
type: 'test'
...
# Subtest: chat thread.open: one verified write, fixed identity/class and private log
ok 12 - chat thread.open: one verified write, fixed identity/class and private log
---
duration_ms: 0.96705
type: 'test'
...
# Subtest: chat thread.open: each existing Console write fence makes zero bus calls
ok 13 - chat thread.open: each existing Console write fence makes zero bus calls
---
duration_ms: 6.409182
type: 'test'
...
# Subtest: chat thread.open: refusal or lost/malformed receipt never retries or invents success
ok 14 - chat thread.open: refusal or lost/malformed receipt never retries or invents success
---
duration_ms: 4.54952
type: 'test'
...
# Subtest: chat createThread receipt requires an instance even when its id is valid
ok 15 - chat createThread receipt requires an instance even when its id is valid
---
duration_ms: 1.948475
type: 'test'
...
# Subtest: chat write receipts expose only the fields belonging to their operation
ok 16 - chat write receipts expose only the fields belonging to their operation
---
duration_ms: 3.039253
type: 'test'
...
# Subtest: chat validates names, body, project and strict empty open payload
ok 17 - chat validates names, body, project and strict empty open payload
---
duration_ms: 3.332596
type: 'test'
...
# Subtest: chat read failures redact ownership and refuse invalid transport arrays
ok 18 - chat read failures redact ownership and refuse invalid transport arrays
---
duration_ms: 3.252877
type: 'test'
...
# Subtest: REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations
ok 19 - REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations
---
duration_ms: 84.521895
type: 'test'
...
# Subtest: chat repeats every POST boundary even when called behind an admitted request
ok 20 - chat repeats every POST boundary even when called behind an admitted request
---
duration_ms: 0.894693
type: 'test'
...
# Subtest: configured Console process injects chat through its existing human transport
ok 21 - configured Console process injects chat through its existing human transport
---
duration_ms: 130.200727
type: 'test'
...
# Subtest: chat assets, GET purity, method and unknown-route refusals are explicit
ok 22 - chat assets, GET purity, method and unknown-route refusals are explicit
---
duration_ms: 11.59045
type: 'test'
...
# Subtest: the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError
ok 23 - the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError
---
duration_ms: 0.423449
type: 'test'
...
# Subtest: C2-09a createThread forwards an explicit instance through the existing channel
ok 24 - C2-09a createThread forwards an explicit instance through the existing channel
---
duration_ms: 3.442281
type: 'test'
...
# Subtest: C2-09a malformed instance and extra thread fields refuse before the channel write
ok 25 - C2-09a malformed instance and extra thread fields refuse before the channel write
---
duration_ms: 2.896034
type: 'test'
...
1..25
# tests 25
# suites 0
# pass 25
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 425.227286
@@ -0,0 +1,83 @@
{
"node": {
"tests": 2509,
"pass": 2507,
"fail": 0,
"cancelled": 0,
"skipped": 2
},
"shells": [
{
"label": "test-auth",
"passed": 15,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-conductor",
"passed": 17,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-config",
"passed": 55,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-discord",
"passed": 81,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-extension-package",
"passed": 18,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-foundation",
"passed": 44,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-land-work",
"passed": 86,
"failed": 0,
"exit": 0,
"skips": []
},
{
"label": "test-queue",
"passed": 28,
"failed": 0,
"exit": 0,
"skips": [
"skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)"
]
},
{
"label": "test-release",
"passed": 11,
"failed": 3,
"exit": 1,
"skips": []
}
],
"shellPassed": 355,
"shellFailed": 3,
"unrun": [
"test-task",
"24 mutants",
"final reader audit"
]
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,916 @@
TAP version 13
# Subtest: sessionModel: the route is the instance's own vars, and a missing one refuses no-route by name
ok 1 - sessionModel: the route is the instance's own vars, and a missing one refuses no-route by name
---
duration_ms: 33.874129
type: 'test'
...
# Subtest: a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory
ok 2 - a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory
---
duration_ms: 8.467453
type: 'test'
...
# Subtest: a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools
ok 3 - a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools
---
duration_ms: 3.96755
type: 'test'
...
# Subtest: a chief-of-staff bundle lists end_run, change_organization and set_name as typed and nothing untyped, for either harness
ok 4 - a chief-of-staff bundle lists end_run, change_organization and set_name as typed and nothing untyped, for either harness
---
duration_ms: 5.695693
type: 'test'
...
# Subtest: the stop line names end_run for a role.end holder, and every other role keeps today's text
ok 5 - the stop line names end_run for a role.end holder, and every other role keeps today's text
---
duration_ms: 14.125465
type: 'test'
...
# Subtest: a claude-code bundle adds the wrapped gate hook and the MCP config
ok 6 - a claude-code bundle adds the wrapped gate hook and the MCP config
---
duration_ms: 4.987674
type: 'test'
...
# Subtest: the prompt's built-in tools line is the harness's own names, as the manifest lists them
ok 7 - the prompt's built-in tools line is the harness's own names, as the manifest lists them
---
duration_ms: 18.692534
type: 'test'
...
# Subtest: the prompt names the project clones in the workspace, and none when there are none
ok 8 - the prompt names the project clones in the workspace, and none when there are none
---
duration_ms: 14.927989
type: 'test'
...
# Subtest: the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review
ok 9 - the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review
---
duration_ms: 15.928932
type: 'test'
...
# Subtest: the prompt explains landing to a role with land_work, and to no other
ok 10 - the prompt explains landing to a role with land_work, and to no other
---
duration_ms: 8.166314
type: 'test'
...
# Subtest: the prompt tells the pm to ask the chief of staff for sessions, and no other role
ok 11 - the prompt tells the pm to ask the chief of staff for sessions, and no other role
---
duration_ms: 16.315049
type: 'test'
...
# Subtest: a bundle is written once: an existing file refuses
ok 12 - a bundle is written once: an existing file refuses
---
duration_ms: 3.472529
type: 'test'
...
# Subtest: a path with a single quote can't go into the hook command
ok 13 - a path with a single quote can't go into the hook command
---
duration_ms: 3.232429
type: 'test'
...
# Subtest: web grant on pi: the bundle has web_search and web_fetch, and no built-in web tool
ok 14 - web grant on pi: the bundle has web_search and web_fetch, and no built-in web tool
---
duration_ms: 4.782236
type: 'test'
...
# Subtest: no web grant on pi: no web tool in the bundle, and the gate blocks a web call
ok 15 - no web grant on pi: no web tool in the bundle, and the gate blocks a web call
---
duration_ms: 6.580906
type: 'test'
...
# Subtest: web grant on claude-code: the bundle has web_search and web_fetch, and no built-in web tool
ok 16 - web grant on claude-code: the bundle has web_search and web_fetch, and no built-in web tool
---
duration_ms: 3.769295
type: 'test'
...
# Subtest: no web grant on claude-code: no web tool in the bundle, and the gate blocks a web call
ok 17 - no web grant on claude-code: no web tool in the bundle, and the gate blocks a web call
---
duration_ms: 3.619003
type: 'test'
...
# Subtest: a granted session's gate lets the typed web tools through and still blocks the built-ins
ok 18 - a granted session's gate lets the typed web tools through and still blocks the built-ins
---
duration_ms: 4.175596
type: 'test'
...
# Subtest: search-source searxng-url: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
ok 19 - search-source searxng-url: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
---
duration_ms: 11.248706
type: 'test'
...
# Subtest: search-source searxng-bundled: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
ok 20 - search-source searxng-bundled: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing
---
duration_ms: 10.139734
type: 'test'
...
# Subtest: search-source harness-builtin: claude-code gets WebSearch and WebFetch and no typed web tool, only when granted
ok 21 - search-source harness-builtin: claude-code gets WebSearch and WebFetch and no typed web tool, only when granted
---
duration_ms: 9.532167
type: 'test'
...
# Subtest: search-source harness-builtin is refused for a granted pi role, which has no built-in web tool
ok 22 - search-source harness-builtin is refused for a granted pi role, which has no built-in web tool
---
duration_ms: 3.6275
type: 'test'
...
# Subtest: allow exits 0, a deny exits 2 with the reason on stderr
ok 23 - allow exits 0, a deny exits 2 with the reason on stderr
---
duration_ms: 188.002461
type: 'test'
...
# Subtest: a missing or wrong policy, or a bad event, exits 2
ok 24 - a missing or wrong policy, or a bad event, exits 2
---
duration_ms: 103.629156
type: 'test'
...
# Subtest: the bundle's wrapped command: a missing gate or node still blocks
ok 25 - the bundle's wrapped command: a missing gate or node still blocks
---
duration_ms: 1105.742163
type: 'test'
...
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
ok 26 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
---
duration_ms: 951.625174
type: 'test'
...
# Subtest: claude: the hook alone blocks a path outside the workspace
ok 27 - claude: the hook alone blocks a path outside the workspace
---
duration_ms: 542.468659
type: 'test'
...
# Subtest: claude: a second turn resumes the first turn's session
ok 28 - claude: a second turn resumes the first turn's session
---
duration_ms: 937.521882
type: 'test'
...
# Subtest: claude adapter: --restricted is always passed
ok 29 - claude adapter: --restricted is always passed
---
duration_ms: 4.926388
type: 'test'
...
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
ok 30 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
---
duration_ms: 17.263685
type: 'test'
...
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
ok 31 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
---
duration_ms: 5.895812
type: 'test'
...
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
ok 32 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
---
duration_ms: 880.142974
type: 'test'
...
# Subtest: claude: a missing hook or MCP file refuses before claude starts
ok 33 - claude: a missing hook or MCP file refuses before claude starts
---
duration_ms: 8.959808
type: 'test'
...
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
ok 34 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
---
duration_ms: 1769.308084
type: 'test'
...
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
ok 35 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
---
duration_ms: 877.553016
type: 'test'
...
# Subtest: M2: the gate refuses write, edit and bash to every creative role on pi and claude-code
ok 36 - M2: the gate refuses write, edit and bash to every creative role on pi and claude-code
---
duration_ms: 58.288959
type: 'test'
...
# Subtest: M6: no creative session gets raise_decision; the assistant and the chief of staff keep it
ok 37 - M6: no creative session gets raise_decision; the assistant and the chief of staff keep it
---
duration_ms: 25.28371
type: 'test'
...
# Subtest: web: research and companion get the broker's typed web tools, and a harness-builtin source refuses web-source-not-allowed
ok 38 - web: research and companion get the broker's typed web tools, and a harness-builtin source refuses web-source-not-allowed
---
duration_ms: 32.627529
type: 'test'
...
# Subtest: persona: the block sits between the contract and \#\# This session, fenced, with the name and digest
ok 39 - persona: the block sits between the contract and \#\# This session, fenced, with the name and digest
---
duration_ms: 8.81196
type: 'test'
...
# Subtest: persona: each unsafe or unpinned file refuses before the bundle directory is made
ok 40 - persona: each unsafe or unpinned file refuses before the bundle directory is made
---
duration_ms: 29.064281
type: 'test'
...
# Subtest: adapters pass the prompt file's absolute path; the persona and the prompt text never sit in argv
ok 41 - adapters pass the prompt file's absolute path; the persona and the prompt text never sit in argv
---
duration_ms: 37.501659
type: 'test'
...
# Subtest: the pi adapter refuses a missing, unreadable or directory prompt file before pi starts
ok 42 - the pi adapter refuses a missing, unreadable or directory prompt file before pi starts
---
duration_ms: 11.744855
type: 'test'
...
# Subtest: pi: policy tools and typed tools pass, anything else is blocked
ok 43 - pi: policy tools and typed tools pass, anything else is blocked
---
duration_ms: 4.339988
type: 'test'
...
# Subtest: claude: builtins map from pi names, typed tools need the mcp prefix
ok 44 - claude: builtins map from pi names, typed tools need the mcp prefix
---
duration_ms: 1.245674
type: 'test'
...
# Subtest: file tool paths must resolve inside the workspace
ok 45 - file tool paths must resolve inside the workspace
---
duration_ms: 1.977493
type: 'test'
...
# Subtest: pi's own path normalisation can't be used to step out
ok 46 - pi's own path normalisation can't be used to step out
---
duration_ms: 2.797769
type: 'test'
...
# Subtest: a symlink inside the workspace that points out is outside
ok 47 - a symlink inside the workspace that points out is outside
---
duration_ms: 1.172457
type: 'test'
...
# Subtest: a dangling symlink is refused at any depth, in both harnesses
ok 48 - a dangling symlink is refused at any depth, in both harnesses
---
duration_ms: 4.665439
type: 'test'
...
# Subtest: read is checked under every spelling pi's read would open, in both harnesses
ok 49 - read is checked under every spelling pi's read would open, in both harnesses
---
duration_ms: 25.330584
type: 'test'
...
# Subtest: other spellings cover directories, dangling links and pi's cwd
ok 50 - other spellings cover directories, dangling links and pi's cwd
---
duration_ms: 2.633565
type: 'test'
...
# Subtest: a relative path climbs from the workspace's real path, in both harnesses
ok 51 - a relative path climbs from the workspace's real path, in both harnesses
---
duration_ms: 8.552688
type: 'test'
...
# Subtest: claude path fields per tool
ok 52 - claude path fields per tool
---
duration_ms: 1.078208
type: 'test'
...
# Subtest: glob patterns stay inside the workspace
ok 53 - glob patterns stay inside the workspace
---
duration_ms: 1.015431
type: 'test'
...
# Subtest: a path that can't be checked is blocked
ok 54 - a path that can't be checked is blocked
---
duration_ms: 0.497114
type: 'test'
...
# Subtest: fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX
ok 55 - fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX
---
duration_ms: 315.138858
type: 'test'
...
# Subtest: fetch_review's have orders the working refs newest first within each clone, one clone after another
ok 56 - fetch_review's have orders the working refs newest first within each clone, one clone after another
---
duration_ms: 149.700401
type: 'test'
...
# Subtest: a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period
ok 57 - a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period
---
duration_ms: 1809.591642
type: 'test'
...
# Subtest: once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too
ok 58 - once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too
---
duration_ms: 302.932534
type: 'test'
...
# Subtest: land_work sends one land line and resolves the host's started answer
ok 59 - land_work sends one land line and resolves the host's started answer
---
duration_ms: 4.001511
type: 'test'
...
# Subtest: initialize, ping and tools/list
ok 60 - initialize, ping and tools/list
---
duration_ms: 74.132597
type: 'test'
...
# Subtest: tools/call goes through the tool socket; a refusal is an isError result
ok 61 - tools/call goes through the tool socket; a refusal is an isError result
---
duration_ms: 62.808126
type: 'test'
...
# Subtest: replies are compact JSON; over the cap a tool error names the size and the cap
ok 62 - replies are compact JSON; over the cap a tool error names the size and the cap
---
duration_ms: 58.11207
type: 'test'
...
# Subtest: unknown tools and methods are JSON-RPC errors and never reach the socket
ok 63 - unknown tools and methods are JSON-RPC errors and never reach the socket
---
duration_ms: 39.258122
type: 'test'
...
# Subtest: a missing argument is a usage error
ok 64 - a missing argument is a usage error
---
duration_ms: 34.820111
type: 'test'
...
# Subtest: pi: typed tools reach the socket, the gate blocks, agent_end writes the marker
ok 65 - pi: typed tools reach the socket, the gate blocks, agent_end writes the marker
---
duration_ms: 606.423339
type: 'test'
...
# Subtest: pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks
ok 66 - pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks
---
duration_ms: 480.797888
type: 'test'
...
# Subtest: pi: a write through a dangling symlink is blocked, and nothing appears outside
ok 67 - pi: a write through a dangling symlink is blocked, and nothing appears outside
---
duration_ms: 482.616253
type: 'test'
...
# Subtest: pi: a read is refused when pi would open another spelling outside
ok 68 - pi: a read is refused when pi would open another spelling outside
---
duration_ms: 487.073693
type: 'test'
...
# Subtest: pi: a relative path climbs from the real path of a workspace behind a symlink
ok 69 - pi: a relative path climbs from the real path of a workspace behind a symlink
---
duration_ms: 470.344115
type: 'test'
...
# Subtest: pi: a relative path climbs from the real path of a dataRoot behind a symlink
ok 70 - pi: a relative path climbs from the real path of a dataRoot behind a symlink
---
duration_ms: 433.687877
type: 'test'
...
# Subtest: pi: a missing extension refuses before any model call
ok 71 - pi: a missing extension refuses before any model call
---
duration_ms: 6.469015
type: 'test'
...
# Subtest: pi: an extension without its configuration fails pi's start
ok 72 - pi: an extension without its configuration fails pi's start
---
duration_ms: 388.371394
type: 'test'
...
# Subtest: pi: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
ok 73 - pi: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
---
duration_ms: 1324.20464
type: 'test'
...
# Subtest: pi: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
ok 74 - pi: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
---
duration_ms: 739.601215
type: 'test'
...
# Subtest: raise_decision offers domain as technical or delivery only, optional, and says where a raise goes
ok 75 - raise_decision offers domain as technical or delivery only, optional, and says where a raise goes
---
duration_ms: 22.046969
type: 'test'
...
# Subtest: raise_decision forwards domain to decision.raise, and a call without it sends none
ok 76 - raise_decision forwards domain to decision.raise, and a call without it sends none
---
duration_ms: 91.610922
type: 'test'
...
# Subtest: founderCheck: founder variables, then a needed service without a usable token
ok 77 - founderCheck: founder variables, then a needed service without a usable token
---
duration_ms: 2.121163
type: 'test'
...
# Subtest: turnRequest names the sender, class, reply and decision
ok 78 - turnRequest names the sender, class, reply and decision
---
duration_ms: 0.320665
type: 'test'
...
# Subtest: turnRequest says a tester's message is a test and never Jason's instruction
ok 79 - turnRequest says a tester's message is a test and never Jason's instruction
---
duration_ms: 0.414139
type: 'test'
...
# Subtest: turnRequest says a user's message is a granted user's and never the owner's instruction
ok 80 - turnRequest says a user's message is a granted user's and never the owner's instruction
---
duration_ms: 0.292892
type: 'test'
...
# Subtest: turnRequest shows the message's own sent time, a day old as well as new
ok 81 - turnRequest shows the message's own sent time, a day old as well as new
---
duration_ms: 0.26369
type: 'test'
...
# Subtest: stateBlock names the launch state with its time and event, and the open tasks with the cap
ok 82 - stateBlock names the launch state with its time and event, and the open tasks with the cap
---
duration_ms: 0.458904
type: 'test'
...
# Subtest: a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0
ok 83 - a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0
---
duration_ms: 276.929759
type: 'test'
...
# Subtest: launches revoked and then restored: the turn's block reads allowed and gives the restore time
ok 84 - launches revoked and then restored: the turn's block reads allowed and gives the restore time
---
duration_ms: 235.804005
type: 'test'
...
# Subtest: a task closed on the tracker is missing from the next turn's open list
ok 85 - a task closed on the tracker is missing from the next turn's open list
---
duration_ms: 228.852949
type: 'test'
...
# Subtest: a message read a day after it was sent shows the time it was sent
ok 86 - a message read a day after it was sent shows the time it was sent
---
duration_ms: 143.314714
type: 'test'
...
# Subtest: a decision the human resolves becomes a turn citing the decision and the answer
ok 87 - a decision the human resolves becomes a turn citing the decision and the answer
---
duration_ms: 193.160152
type: 'test'
...
# Subtest: a failed state read fails the turn: no adapter runs, the sender hears why, the message is read
ok 88 - a failed state read fails the turn: no adapter runs, the sender hears why, the message is read
---
duration_ms: 92.267731
type: 'test'
...
# Subtest: a SIGTERM before the claim stops the runner with exit 0 and no claim
ok 89 - a SIGTERM before the claim stops the runner with exit 0 and no claim
---
duration_ms: 90.850268
type: 'test'
...
# Subtest: typed tools carry the runner's capability; launch goes to the host's launch socket
ok 90 - typed tools carry the runner's capability; launch goes to the host's launch socket
---
duration_ms: 363.843789
type: 'test'
...
# Subtest: end_run, organization_revision and change_organization reach the broker with the tool's arguments, and refusals keep their codes
ok 91 - end_run, organization_revision and change_organization reach the broker with the tool's arguments, and refusals keep their codes
---
duration_ms: 429.453211
type: 'test'
...
# Subtest: set_name reaches the broker's name.set with the tool's arguments, and refusals keep their codes
ok 92 - set_name reaches the broker's name.set with the tool's arguments, and refusals keep their codes
---
duration_ms: 173.960254
type: 'test'
...
# Subtest: a RESULT gets no automatic reply; failed turns reply with the reason
ok 93 - a RESULT gets no automatic reply; failed turns reply with the reason
---
duration_ms: 1237.450404
type: 'test'
...
# Subtest: the bus's launch notice is one turn with no reply, and is not taken again
ok 94 - the bus's launch notice is one turn with no reply, and is not taken again
---
duration_ms: 271.363823
type: 'test'
...
# Subtest: SIGTERM during a turn kills the turn's process group and still exits 0
ok 95 - SIGTERM during a turn kills the turn's process group and still exits 0
---
duration_ms: 119.278583
type: 'test'
...
# Subtest: founder credentials stop before the claim (20)
ok 96 - founder credentials stop before the claim (20)
---
duration_ms: 154.532734
type: 'test'
...
# Subtest: a refused claim exits 21; an ended run's capability exits 22
ok 97 - a refused claim exits 21; an ended run's capability exits 22
---
duration_ms: 156.994628
type: 'test'
...
# Subtest: the launch ending under a running session exits 22
ok 98 - the launch ending under a running session exits 22
---
duration_ms: 129.961066
type: 'test'
...
# Subtest: a broker that stays unreachable exits 23 after brokerRetries polls
ok 99 - a broker that stays unreachable exits 23 after brokerRetries polls
---
duration_ms: 229.871224
type: 'test'
...
# Subtest: a broker that is down at the claim exits 23, not 21
ok 100 - a broker that is down at the claim exits 23, not 21
---
duration_ms: 71.761313
type: 'test'
...
# Subtest: no capability, or a malformed one, on stdin exits 2
ok 101 - no capability, or a malformed one, on stdin exits 2
---
duration_ms: 189.814923
type: 'test'
...
# Subtest: a missing or malformed policy exits 2 before the claim
ok 102 - a missing or malformed policy exits 2 before the claim
---
duration_ms: 115.720449
type: 'test'
...
# Subtest: REQ-LIMIT-4 runner brackets its actual harness turn before the reply and read receipt
ok 103 - REQ-LIMIT-4 runner brackets its actual harness turn before the reply and read receipt
---
duration_ms: 166.881927
type: 'test'
...
# Subtest: REQ-LIMIT-4 a runner at its limit stops at the end of its turn: RESULT and read first, then exit 22 and a limit-check revoke
ok 104 - REQ-LIMIT-4 a runner at its limit stops at the end of its turn: RESULT and read first, then exit 22 and a limit-check revoke
---
duration_ms: 182.191191
type: 'test'
...
# Subtest: landingRequest is the fixed template, with the decision and task ref and nothing else
ok 105 - landingRequest is the fixed template, with the decision and task ref and nothing else
---
duration_ms: 0.167667
type: 'test'
...
# Subtest: each landing entry is one turn before any message, with the state block and the fixed request; it sends nothing and reads nothing
ok 106 - each landing entry is one turn before any message, with the state block and the fixed request; it sends nothing and reads nothing
---
duration_ms: 201.176324
type: 'test'
...
# Subtest: an empty landing takes no landing turn: the message is turn 1
ok 107 - an empty landing takes no landing turn: the message is turn 1
---
duration_ms: 218.477916
type: 'test'
...
# Subtest: a run in a landing turn isn't released for a second held decision until its landing turns end and it has read its messages
ok 108 - a run in a landing turn isn't released for a second held decision until its landing turns end and it has read its messages
---
duration_ms: 584.911338
type: 'test'
...
# Subtest: a malformed landing exits 2 before the claim, with no turn
ok 109 - a malformed landing exits 2 before the claim, with no turn
---
duration_ms: 557.606738
type: 'test'
...
# Subtest: row 65 a session.json without a turnEnd of marker or exit, or a harnessEnv of strings, exits 2 before the claim
ok 110 - row 65 a session.json without a turnEnd of marker or exit, or a harnessEnv of strings, exits 2 before the claim
---
duration_ms: 256.156715
type: 'test'
...
# Subtest: row 65 harnessEnv reaches the adapter, and an exit harness needs no turn marker
ok 111 - row 65 harnessEnv reaches the adapter, and an exit harness needs no turn marker
---
duration_ms: 164.97969
type: 'test'
...
# Subtest: row 66 the adapter gets the run's unslop log, and a flagged reply's note rides that reply's RESULT, once
ok 112 - row 66 the adapter gets the run's unslop log, and a flagged reply's note rides that reply's RESULT, once
---
duration_ms: 296.421186
type: 'test'
...
# Subtest: row 66 a turn with no reply holds its note for the next reply; a broken check is told once
ok 113 - row 66 a turn with no reply holds its note for the next reply; a broken check is told once
---
duration_ms: 293.54634
type: 'test'
...
# Subtest: row 66 unslopNotes keeps a note until sent(), reads only whole lines, and unslopCounts reads a log after the session
ok 114 - row 66 unslopNotes keeps a note until sent(), reads only whole lines, and unslopCounts reads a log after the session
---
duration_ms: 1.554984
type: 'test'
...
# Subtest: row 66 a clipped answer with both note parts stays under the bus's body limit, and the note under 600
ok 115 - row 66 a clipped answer with both note parts stays under the bus's body limit, and the note under 600
---
duration_ms: 163.649095
type: 'test'
...
# Subtest: row 66 a refused send keeps the note: it rides the next RESULT the session sends
ok 116 - row 66 a refused send keeps the note: it rides the next RESULT the session sends
---
duration_ms: 266.580145
type: 'test'
...
# Subtest: each repo role's prompt carries exactly its listed skills, each file as the bundle copy and the source
ok 117 - each repo role's prompt carries exactly its listed skills, each file as the bundle copy and the source
---
duration_ms: 61.768064
type: 'test'
...
# Subtest: a skill's files go into the prompt in sorted path order, sub-directories included
ok 118 - a skill's files go into the prompt in sorted path order, sub-directories included
---
duration_ms: 8.52293
type: 'test'
...
# Subtest: a role with no skills gets no Skills section, no skills directory and an empty manifest list
ok 119 - a role with no skills gets no Skills section, no skills directory and an empty manifest list
---
duration_ms: 9.561171
type: 'test'
...
# Subtest: an enabled skill the role doesn't list stays out of the prompt
ok 120 - an enabled skill the role doesn't list stays out of the prompt
---
duration_ms: 9.546384
type: 'test'
...
# Subtest: the bundle copy outlives the source: deactivating a skill after the build leaves the run's copy
ok 121 - the bundle copy outlives the source: deactivating a skill after the build leaves the run's copy
---
duration_ms: 4.333461
type: 'test'
...
# Subtest: a listed skill that isn't enabled refuses skill-not-enabled, naming the activate command
ok 122 - a listed skill that isn't enabled refuses skill-not-enabled, naming the activate command
---
duration_ms: 8.795891
type: 'test'
...
# Subtest: a skill directory that isn't plain refuses: a symlinked skill, SKILL.md or sub-file, or a non-file entry
ok 123 - a skill directory that isn't plain refuses: a symlinked skill, SKILL.md or sub-file, or a non-file entry
---
duration_ms: 2.555487
type: 'test'
...
# Subtest: a skill file that can't go into the prompt refuses: a bad name, non-UTF-8 bytes, or the end marker
ok 124 - a skill file that can't go into the prompt refuses: a bad name, non-UTF-8 bytes, or the end marker
---
duration_ms: 5.208875
type: 'test'
...
# Subtest: a role's skills over SKILL_BYTES together refuse, naming the skill that crossed it
ok 125 - a role's skills over SKILL_BYTES together refuse, naming the skill that crossed it
---
duration_ms: 9.876759
type: 'test'
...
# Subtest: each adapter passes the skills only in the prompt file, with skill discovery off
ok 126 - each adapter passes the skills only in the prompt file, with skill discovery off
---
duration_ms: 43.788426
type: 'test'
...
# Subtest: every role sees read_spec, record writer follows its grant, citation schemas stay strict
ok 127 - every role sees read_spec, record writer follows its grant, citation schemas stay strict
---
duration_ms: 25.139634
type: 'test'
...
# Subtest: typed socket forwards read content/history and agent record writes, and cannot write a spec
ok 128 - typed socket forwards read content/history and agent record writes, and cannot write a spec
---
duration_ms: 144.347507
type: 'test'
...
# Subtest: the PM gets its task verbs and the reads, and no launch
ok 129 - the PM gets its task verbs and the reads, and no launch
---
duration_ms: 13.582778
type: 'test'
...
# Subtest: send_message offers the BLOCK class, beside the classes it already had
ok 130 - send_message offers the BLOCK class, beside the classes it already had
---
duration_ms: 3.430334
type: 'test'
...
# Subtest: the chief of staff gets launch, send_message, end_run, hire, fire, change_organization and set_name, and no task verbs
ok 131 - the chief of staff gets launch, send_message, end_run, hire, fire, change_organization and set_name, and no task verbs
---
duration_ms: 3.40306
type: 'test'
...
# Subtest: hire and fire send instance.hire and instance.fire under role.hire and role.fire, only for a holder of the grant
ok 132 - hire and fire send instance.hire and instance.fire under role.hire and role.fire, only for a holder of the grant
---
duration_ms: 23.265588
type: 'test'
...
# Subtest: request_staff is the pm's alone: staff.request over message.send, roles and counts bounded
ok 133 - request_staff is the pm's alone: staff.request over message.send, roles and counts bounded
---
duration_ms: 14.641248
type: 'test'
...
# Subtest: end_run sends role and reason, no decision; change_organization sends revision and the organization keys
ok 134 - end_run sends role and reason, no decision; change_organization sends revision and the organization keys
---
duration_ms: 2.502036
type: 'test'
...
# Subtest: set_name sends instance and name to name.set under instance.name, only for a holder of that grant
ok 135 - set_name sends instance and name to name.set under instance.name, only for a holder of that grant
---
duration_ms: 12.229717
type: 'test'
...
# Subtest: the organization keys change_organization takes are the broker's ORGANIZATION_KEYS and the settings' SETTINGS_KEYS
ok 136 - the organization keys change_organization takes are the broker's ORGANIZATION_KEYS and the settings' SETTINGS_KEYS
---
duration_ms: 0.143959
type: 'test'
...
# Subtest: a role without role.end or organization.change gets none of the three tools, and each follows its own grant
ok 137 - a role without role.end or organization.change gets none of the three tools, and each follows its own grant
---
duration_ms: 10.841024
type: 'test'
...
# Subtest: no role's tools send instances or instances.set, and change_organization takes no instance write keys
ok 138 - no role's tools send instances or instances.set, and change_organization takes no instance write keys
---
duration_ms: 10.300174
type: 'test'
...
# Subtest: the assistant gets send_message, the reads and the web tools (it holds the web grant), no launch
ok 139 - the assistant gets send_message, the reads and the web tools (it holds the web grant), no launch
---
duration_ms: 2.183569
type: 'test'
...
# Subtest: list_requirements reads the requirement ids, for a task.create holder with a tracker
ok 140 - list_requirements reads the requirement ids, for a task.create holder with a tracker
---
duration_ms: 5.950375
type: 'test'
...
# Subtest: a coder gets no launch, no resolve_decision, and no task tools without a tracker
ok 141 - a coder gets no launch, no resolve_decision, and no task tools without a tracker
---
duration_ms: 2.026542
type: 'test'
...
# Subtest: a coder with a tracker gets its own task tools and no task_reassign
ok 142 - a coder with a tracker gets its own task tools and no task_reassign
---
duration_ms: 2.142864
type: 'test'
...
# Subtest: launch only when the business's launch block names the instance as launcher
ok 143 - launch only when the business's launch block names the instance as launcher
---
duration_ms: 3.8985
type: 'test'
...
# Subtest: an action outside the instance's authority has no tool
ok 144 - an action outside the instance's authority has no tool
---
duration_ms: 2.256397
type: 'test'
...
# Subtest: the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM
ok 145 - the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM
---
duration_ms: 8.449366
type: 'test'
...
# Subtest: land_work is there only with land, after push_working, with the task and the decision
ok 146 - land_work is there only with land, after push_working, with the task and the decision
---
duration_ms: 4.262077
type: 'test'
...
# Subtest: callTool: one JSON line out, the result back, a refusal rejects
ok 147 - callTool: one JSON line out, the result back, a refusal rejects
---
duration_ms: 16.711113
type: 'test'
...
# Subtest: list_tasks takes the broker's filters and pages; get_task takes a task_ref
ok 148 - list_tasks takes the broker's filters and pages; get_task takes a task_ref
---
duration_ms: 3.972724
type: 'test'
...
# Subtest: callTool sends list_tasks done false unless the session names done; other tools as given
ok 149 - callTool sends list_tasks done false unless the session names done; other tools as given
---
duration_ms: 3.063645
type: 'test'
...
# Subtest: replyText: compact JSON up to the cap, an error naming the size and the cap over it
ok 150 - replyText: compact JSON up to the cap, an error naming the size and the cap over it
---
duration_ms: 0.915587
type: 'test'
...
# Subtest: web_search's query maxLength is the web layer's SEARCH_MAX_QUERY_CHARS
ok 151 - web_search's query maxLength is the web layer's SEARCH_MAX_QUERY_CHARS
---
duration_ms: 3.3941
type: 'test'
...
1..151
# tests 151
# suites 0
# pass 151
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 12043.554653
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,17 @@
OK status with missing harness credential exits 3 and still lists accounts
OK status reports harness credential (read-only) + mosaic accounts
OK api key material never reaches output
OK oauth token material never reaches output
OK unparseable credential file exits 2
OK symlinked credential file exits 4
OK env-side credential names reported
OK env var values never reach output
OK accounts without an accounts dir reports none and creates nothing
OK accounts lists files and marks the active one
OK loose account perms flagged in listing
OK agent --auth with missing account file refuses (exit 4)
OK agent --auth with non-0600 account file refuses
OK agent --auth with invalid account name refuses
OK auth.sh without valid config refuses
selftest: 15 passed, 0 failed
@@ -0,0 +1,23 @@
On branch rocko/row219-on-07802dc57
Your branch is up to date with 'origin/rocko/row219-on-07802dc57'.
nothing to commit, working tree clean
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
OK dry-run committed nothing
OK apply: allowed change exits 0 (exit 0)
OK apply: attribution in commit subject
OK apply: target tree clean after commit
OK disallowed path refused (exit 1)
OK disallowed path: target untouched
OK syntax gate refused broken .mjs (exit 1)
OK syntax gate: target untouched
OK suite failure refused (exit 1)
OK suite failure: target reverted to clean
OK disabled policy refused (exit 2)
OK disabled policy: target untouched
OK failed run refused (exit 1)
OK failed run: target untouched
OK missing run exits 4 (exit 4)
OK invalid policy exits 2 (exit 2)
selftest: 17 passed, 0 failed
@@ -0,0 +1,57 @@
OK absent adapter defaults to pi
OK adapter mock validates (exit 0)
OK unsupported adapter exits 2 (exit 2)
OK env exports adapter
OK bootstrap creates default when absent (exit 0)
OK bootstrap wrote config file
OK bootstrap is idempotent on existing config (exit 0)
OK bootstrap did not rewrite existing config
OK validate missing config exits 3 (exit 3)
OK malformed JSON exits 2 (exit 2)
OK unsupported configVersion exits 2 (exit 2)
OK unknown top-level key exits 2 (exit 2)
OK unknown execution key exits 2 (exit 2)
OK unsupported backend exits 2 (exit 2)
OK unsupported environment exits 2 (exit 2)
OK relative dataRoot exits 2 (exit 2)
OK non-canonical dataRoot exits 2 (exit 2)
OK filesystem root dataRoot exits 2 (exit 2)
OK home directory dataRoot exits 2 (exit 2)
OK dataRoot containing config dir exits 2 (exit 2)
OK control character in provider exits 2 (exit 2)
OK symlinked config file exits 2 (exit 2)
OK env exports resolve correctly
OK valid console.sso loads and is in the output
OK console.sso HS256 alg exits 2 naming the path (exit 2)
OK console.sso alg none exits 2 naming the path (exit 2)
OK console.sso missing slot field exits 2 naming the path (exit 2)
OK console.sso slot name outside the pattern exits 2 naming the path (exit 2)
OK unknown key under console exits 2 naming the path (exit 2)
OK valid federation key loads and is in the output
OK federation unknown sub-key exits 2 naming the key (exit 2)
OK federation peer unknown sub-key exits 2 naming the key (exit 2)
OK federation peer origin over http exits 2 naming the key (exit 2)
OK federation peer origin with a path exits 2 naming the key (exit 2)
OK federation peer origin with userinfo exits 2 naming the key (exit 2)
OK federation peer id equal to self exits 2 naming the key (exit 2)
OK federation peer label empty exits 2 naming the key (exit 2)
OK federation not an object exits 2 naming the key (exit 2)
OK federation self pattern exits 2 naming the key (exit 2)
OK federation peers not an array exits 2 naming the key (exit 2)
OK federation peer not an object exits 2 naming the key (exit 2)
OK federation peer id pattern exits 2 naming the key (exit 2)
OK federation peer label over 80 characters exits 2 naming the key (exit 2)
OK federation duplicate peer id exits 2 naming the key (exit 2)
OK federation peer origin with a trailing slash exits 2 naming the key (exit 2)
OK federation self missing exits 2 naming the key (exit 2)
OK federation peer id missing exits 2 naming the key (exit 2)
OK federation peer label missing exits 2 naming the key (exit 2)
OK config without federation loads and has no federation output
OK config without console loads with unchanged output
OK compactionReadBytes 8388608 loads and is in the output as written
OK compactionReadBytes "x" loads and is in the output as written
OK compactionStreams 2048 loads and is in the output as written
OK compactionStreams "x" loads and is in the output as written
OK failed validation modified nothing
selftest: 55 passed, 0 failed
@@ -0,0 +1,85 @@
toolchain: node v26.8.1
OK syntax: packages/discord/src/approvals.mjs
OK syntax: packages/discord/src/authorize.mjs
OK syntax: packages/discord/src/binding.mjs
OK syntax: packages/discord/src/cli.mjs
OK syntax: packages/discord/src/connector.mjs
OK syntax: packages/discord/src/context.mjs
OK syntax: packages/discord/src/engine-pi.mjs
OK syntax: packages/discord/src/errors.mjs
OK syntax: packages/discord/src/gateway.mjs
OK syntax: packages/discord/src/git.mjs
OK syntax: packages/discord/src/journal.mjs
OK syntax: packages/discord/src/notify.mjs
OK syntax: packages/discord/src/processing.mjs
OK syntax: packages/discord/src/rest.mjs
OK syntax: packages/discord/src/setspark.mjs
OK syntax: packages/discord/src/tools.mjs
OK syntax: packages/discord/src/web.mjs
OK syntax: packages/discord/bin/git-credential.mjs
OK syntax: packages/discord/extension/tools.mjs
OK syntax: packages/discord/tests/approvals.test.mjs
OK syntax: packages/discord/tests/authorize.test.mjs
OK syntax: packages/discord/tests/binding.test.mjs
OK syntax: packages/discord/tests/connector.test.mjs
OK syntax: packages/discord/tests/context.test.mjs
OK syntax: packages/discord/tests/copy-service.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/offline-check.test.mjs
OK syntax: packages/discord/tests/processing-stop.test.mjs
OK syntax: packages/discord/tests/processing.test.mjs
OK syntax: packages/discord/tests/recover.test.mjs
OK syntax: packages/discord/tests/rest.test.mjs
OK syntax: packages/discord/tests/setspark.test.mjs
OK syntax: packages/discord/tests/tools.test.mjs
OK syntax: packages/discord/tests/web.test.mjs
OK syntax: packages/discord/fixtures/claim-worker.mjs
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
OK syntax: scripts/discord.sh
OK syntax: scripts/discord-service.sh
OK packages/discord declares no dependencies
OK no bot-token-shaped string in packages/discord
OK fixture binding uses placeholder ids only
OK fixture binding validates
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
OK real pi with protocol vault adds reserve_id to the git verbs
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
OK real pi refuses a git key on a read-only root (fail closed)
OK real pi with the pilot flags (--no-tools) exposes no tool at all
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test packages/discord/tests/ (ℹ pass 192)
OK scripts/discord.sh --help exits 0
OK scripts/discord.sh check without a binding exits 4
OK scripts/discord.sh recover without a binding exits 4
OK scripts/discord.sh reload without a binding exits 4
OK scripts/discord-service.sh without a command exits 4
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
OK service install writes the rendered unit (0644) and leaves no temp file
OK service install a second time reports unchanged
OK systemd-analyze verify accepts the rendered unit
OK service uninstall removes the unit file
OK service install with an unknown flag exits 4
OK service install with USER unset finishes and names the account for lingering
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
OK pinned install writes the rendered unit in a temporary unit directory
OK pinned install refuses short prerequisite and writes no unit
OK pinned install refuses missing prerequisite and writes no unit
OK pinned install refuses cli prerequisite and writes no unit
OK pinned install refuses pi prerequisite and writes no unit
OK status names the copy from fake systemctl effective cwd
OK checkout install explicitly recommends --copy
OK wrapper check --offline passes local fixture and makes no network call
OK wrapper run --offline refuses usage
discord suite: 81 passed, 0 failed
@@ -0,0 +1,21 @@
OK initial ordinary-file install
OK installed tree matches canonical source
OK installed tree has no symlinks
OK check detects installation drift
OK sync refuses to overwrite installation drift
OK check detects an extra destination file
OK check detects an extra destination directory
OK check rejects a destination symlink
OK sync accepts a canonical source update
OK updated installation matches canonical source
/mnt/storage/scratch/rocko-r219/scripts/test-extension-package.sh: line 14: 3698730 Killed "$@" > /dev/null 2>&1
OK forced interruption kills the replacing process
OK next invocation recovers old consistent installation
OK interrupted replacement rolled back
OK sync succeeds after interruption recovery
OK unlocked stale lock file does not block
OK active lock refuses a concurrent sync
OK source symlink fails closed
OK nested second entrypoint fails closed
extension package selftest: 18 passed, 0 failed
@@ -0,0 +1,53 @@
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
OK syntax: scripts/foundation-inspect.mjs
OK syntax: scripts/foundation/strict-json.mjs
OK syntax: scripts/foundation/canonical.mjs
OK syntax: scripts/foundation/resolve.mjs
OK syntax: scripts/foundation/validate-record.mjs
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
OK syntax: scripts/foundation/canonical.test.mjs
OK syntax: scripts/foundation/cli.test.mjs
OK syntax: scripts/foundation/fixtures.test.mjs
OK syntax: scripts/foundation/resolve.test.mjs
OK syntax: scripts/foundation/strict-json.test.mjs
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
OK fixture generator runs
OK checked-in fixtures/bundles equal a fresh generation
OK checked-in fixtures/raw equal a fresh generation
OK checked-in fixtures/index.json equal a fresh generation
OK checked-in demo bundles equal a fresh generation
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test scripts/foundation/ (ℹ pass 80)
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
OK oracle: zero schema-column disagreements with the pinned checker
OK oracle: strict-only profile refusals are counted and asserted
OK demo: permitted read preview exits 0 (exit 0)
OK demo: permitted file.change preview exits 0 (exit 0)
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
OK demo: revoked registration is refused (exit 3) (exit 3)
OK demo: message is not authority (exit 3) (exit 3)
OK usage: no arguments exits 2 (exit 2)
OK io: missing file exits 4 (exit 4)
OK io: directory exits 4 (exit 4)
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
OK bound: oversize fixture exits 2 (exit 2)
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
OK text output starts with the disclaimer
OK json output is valid JSON with result allowed and exactly the charter §7 fields
OK json golden matches byte-for-byte
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
OK canary never printed (bundle run and credential-file run)
OK a non-bundle JSON file is refused at the shape gate, not read into output
OK no field of the non-bundle file is echoed
selftest: 44 passed, 0 failed
@@ -0,0 +1,88 @@
OK args: exit 2, target-branch
OK args: exit 2, target-branch
OK args: exit 4
OK args: exit 4
OK args: exit 4
OK --base next refuses before the config is read (got 2)
OK a missing system config exits 3 (got 3)
OK nobus: exit 3
OK schema-table: exit 2, bus-schema
OK schema-meta: exit 2, bus-schema
OK openBus opens the database read-only
OK other-business: exit 2, decision-not-found
OK no-raise: exit 2, decision-not-found
OK not-a-merge: exit 2, not-a-merge
OK open: exit 2, not-resolved
OK withdrawn: exit 2, not-resolved
OK by-agent: exit 2, not-human
OK via-webui: exit 2, not-human
OK no-answer: exit 2, not-human
OK answer-role: exit 2, not-human
OK answer-run: exit 2, not-human
OK answer-choice: exit 2, not-human
OK two-answers: exit 2, not-human
OK arbiter-cross: exit 2, not-human
OK arbiter-cited: exit 2, not-human
OK chose-no: exit 2, not-merge-option
OK two-refs: exit 2, work-ref
OK bad-ref: exit 2, work-ref
OK task-ref-disagrees: exit 2, work-ref
OK raise-task-disagrees: exit 2, work-ref
OK target-main: exit 2, target-branch
OK superseded-question: exit 2, superseded
OK superseded-task: exit 2, superseded
OK superseded-by: exit 2, superseded
OK no-verdict: exit 2, no-verdict
OK changes-later: exit 2, not-approved
OK short-commit: exit 2, not-approved
OK wrong-task-body: exit 2, not-approved
OK args: exit 2, decision-not-found
OK earlier-merge: exit 0
OK ref-moved: exit 2, ref-moved
OK root-top: exit 2, root-file
OK root-newdir: exit 2, root-file
OK root-src: exit 2, root-file
OK unrelated: exit 2, base-conflict
OK empty: exit 2, base-conflict
OK an empty reviewed diff says so
OK dirty-changed: exit 2, checkout-dirty
OK dirty-staged: exit 2, checkout-dirty
OK dirty-untracked: exit 2, checkout-dirty
OK conflict: exit 2, base-conflict
OK a refusal after the worktree is added leaves no worktree or scratch
OK cleanup looks for the worktree's Docker network
OK dirty-during: exit 2, checkout-dirty
OK a reviewed path dirtied during the gate moves no ref
OK drift: exit 2, patch-drift
OK patch-drift leaves no worktree or scratch
OK gate-pkg: exit 2, gate-failed
OK gate-failed names the failing step's log, and it exists
OK gate-failed leaves no worktree or scratch
OK gate-script: exit 2, gate-failed
OK a failing scripts/test-*.sh fails the gate
OK a failed gate moves no ref
OK nomods: exit 4
OK a target without node_modules leaves no worktree or scratch
OK moved: exit 2, base-moved
OK base-moved keeps the base where it moved to
OK dry: exit 0
OK --dry-run writes no ref
OK --dry-run prints the commit with --subject and --attribution
OK --dry-run runs the gate
OK --dry-run leaves no worktree or scratch
OK land: exit 0
OK the landing is one commit on the old base
OK its diff is the reviewed diff by patch-id
OK its tree is the dry run's
OK the message has the option's subject, the citations and the attribution last
OK it prints the line for the PM
OK the checked-out base has the reviewed paths and keeps the unrelated edit
OK only refs/heads/refactor exists in the target
OK the work ref is unchanged
OK the bus database is unchanged
OK the gate ran each package suite but queue, and each scripts/test-*.sh
OK the landing leaves no worktree or scratch
OK the target's node_modules is still there, with its contents
OK the worktree's Docker network is removed
---
land-work selftests: 86 passed, 0 failed
@@ -0,0 +1,36 @@
toolchain: node v26.8.1, git version 2.55.0
OK syntax: packages/queue/src/cli.mjs
OK syntax: packages/queue/src/errors.mjs
OK syntax: packages/queue/src/io.mjs
OK syntax: packages/queue/src/lock.mjs
OK syntax: packages/queue/src/queue.mjs
OK syntax: packages/queue/src/review.mjs
OK syntax: packages/queue/src/store.mjs
OK syntax: packages/queue/tests/commit.test.mjs
OK syntax: packages/queue/tests/data.test.mjs
OK syntax: packages/queue/tests/dispatch.test.mjs
OK syntax: packages/queue/tests/gate-owner.test.mjs
OK syntax: packages/queue/tests/helpers.mjs
OK syntax: packages/queue/tests/lock.test.mjs
OK syntax: packages/queue/tests/migration.test.mjs
OK syntax: packages/queue/tests/review.test.mjs
OK syntax: packages/queue/tests/store.test.mjs
OK syntax: packages/queue/tests/write.test.mjs
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
OK syntax: scripts/queue-commit.sh
OK syntax: scripts/git-hooks/pre-commit
OK syntax: scripts/mosaic
OK queue-commit.sh, the guard and scripts/mosaic are executable
OK packages/queue declares no dependencies
ℹ tests 156
ℹ pass 156
ℹ fail 0
OK node --test packages/queue/tests/
OK scripts/mosaic queue help
skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r219) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
queue suite: 28 passed, 0 failed
@@ -0,0 +1,37 @@
OK valid RELEASE resolves (exit 0)
OK invalid RELEASE exits 1 (exit 1)
OK missing RELEASE exits 1 (exit 1)
OK valid RELEASE leaves image tag consistent with version
OK status safe on empty state (exit 0)
OK status created no pointer
OK fault-injected activation refuses (exit 1)
OK refused activation wrote no pointer
OK refusal logged exactly once with valid fields
FAIL healthy activation succeeds (exit 1, expected 0)
node:fs:621
return binding.readFileUtf8(path, stringToFlags(options.flag));
^
Error: ENOENT: no such file or directory, open '/mnt/storage/scratch/tmp/rbunO3T/tmp.HQT7aH1NBB/data/state/active.json'
at Object.readFileSync (node:fs:621:20)
at [eval]:3:25
at runScriptInThisContext (node:internal/vm:219:10)
at node:internal/process/execution:485:12
at [eval]-wrapper:6:24
at runScriptInContext (node:internal/process/execution:483:60)
at evalFunction (node:internal/process/execution:317:30)
at evalTypeScript (node:internal/process/execution:329:3)
at node:internal/main/eval_string:71:3 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/mnt/storage/scratch/tmp/rbunO3T/tmp.HQT7aH1NBB/data/state/active.json'
}
Node.js v26.8.1
FAIL pointer written with valid fields
FAIL repeat activation succeeds (log grows) (exit 1, expected 0)
OK log is append-only across activations
OK rollback without previous refuses (exit 1)
selftest: 11 passed, 3 failed
+10 -5
View File
@@ -5,7 +5,7 @@ from pathlib import Path
from datetime import datetime,timezone
ROOT=Path(__file__).resolve().parents[4]
PACKET=Path(__file__).resolve().parent
OUT=PACKET/'run-4a9c-mos1408';OUT.mkdir(exist_ok=True)
OUT=PACKET/'run-2efd-mos1439';OUT.mkdir(exist_ok=True)
def stamp():return datetime.now(timezone.utc).isoformat()
def verify():
for line in (PACKET/'candidate-manifest.sha256').read_text().splitlines():
@@ -22,7 +22,8 @@ def sweep():
def execute(label,args,cwd=ROOT,deadline=300,docker=False):
if args[0]=='node' and '--test' in args and '--test-reporter=tap' not in args:args=[args[0],'--test-reporter=tap',*args[1:]]
if args[0]!='timeout':args=['timeout','-k','10',str(deadline),*args]
verify();short=tempfile.mkdtemp(prefix='r',dir='/mnt/storage/scratch/tmp')
verify();short=subprocess.check_output(['mktemp','-d','/mnt/storage/scratch/tmp/rXXXXXX'],text=True).strip()
if not re.fullmatch(r'[A-Za-z0-9/.-]+',short):raise RuntimeError('unsafe TMPDIR charset')
env=os.environ.copy();env.update(TMPDIR=short,NO_COLOR='1',COMPOSE_PROJECT_NAME='rocko-r219-gate')
env.pop('MOSAIC_GITEA_CREDENTIAL_FILE',None);env.pop('MOSAIC_LIVE_PROVIDER',None)
if docker:env.pop('DOCKER_HOST',None)
@@ -45,9 +46,10 @@ def execute(label,args,cwd=ROOT,deadline=300,docker=False):
finally:
try:os.killpg(child.pid,signal.SIGTERM)
except ProcessLookupError:pass
timeout = timeout or rc in (124,137,-9)
text=log.read_text(errors='replace')
counts={k:int(m[-1]) if (m:=re.findall(r'^# '+k+r' (\d+)\s*$',text,re.M)) else None for k in ['tests','pass','fail','cancelled','skipped']}
row=dict(label=label,args=args,start=begin,end=stamp(),seconds=round(time.monotonic()-t,3),exit=rc,timeout=timeout,counts=counts,skips=[l for l in text.splitlines() if re.search(r'^skip|# SKIP',l,re.I)],log=str(log.relative_to(ROOT)),tmpdir=short)
row=dict(label=label,args=args,start=begin,end=stamp(),seconds=round(time.monotonic()-t,3),exit=rc,timeout=timeout,counts=counts,skips=[l for l in text.splitlines() if re.search(r'^skip\b|# SKIP\b',l,re.I)],log=str(log.relative_to(ROOT)),tmpdir=short)
with (OUT/'results.jsonl').open('a') as f:f.write(json.dumps(row)+'\n')
print('END',json.dumps(row),flush=True)
if rc or timeout or re.search(r'^\s*not ok\b',text,re.M):raise RuntimeError('RED '+label+' '+str(log))
@@ -55,7 +57,7 @@ def execute(label,args,cwd=ROOT,deadline=300,docker=False):
singles=[line for line in (PACKET/'changed-paths.txt').read_text().splitlines() if '/tests/' in line]
def stages():
for path in singles:execute('single-'+Path(path).parent.parent.name+'-'+Path(path).stem,['node','--test',path],deadline=300)
for package in ['bus','cli','webui']:
for package in ['bus','cli','webui','harness']:
files=sorted(glob.glob(str(ROOT/'packages'/package/'tests'/'*.test.mjs')))
execute('suite-'+package,['node','--test',*files],cwd=ROOT/'packages'/package,deadline=1800)
for path in sorted(ROOT.glob('scripts/test-*.sh')):
@@ -95,4 +97,7 @@ if __name__=='__main__':
finally:
verify();(OUT/'cwd-sweep.json').write_text(json.dumps(sweep(),indent=2))
# Remove only the owned fixture compose network, if any; never daemon/volumes.
subprocess.run(['docker','network','rm','rocko-r219-gate_default'],stdout=(OUT/'network-cleanup.log').open('w'),stderr=subprocess.STDOUT)
with (OUT/'network-cleanup.log').open('w') as f:
for network in ['rocko-r219-gate_default', ROOT.name+'_default']:
subprocess.run(['docker','network','rm',network],stdout=f,stderr=subprocess.STDOUT)
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
[]
@@ -0,0 +1 @@
{"head": "054da642f9b0680bd7743583e4c67266d7e14e2a", "manifest": "4a2271ddbd4212324f39e608c3a35a4ae834c045cb0d13a619715017ded0ffef", "start": "2026-10-23T10:12:57.648452+00:00"}
@@ -0,0 +1,30 @@
TAP version 13
# Subtest: C2-09a: creative bindLaunch without a thread refuses thread-required
not ok 1 - C2-09a: creative bindLaunch without a thread refuses thread-required
---
duration_ms: 15.929586
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:645:1'
failureType: 'testCodeFailure'
error: 'Missing expected exception.'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
code: 'thread-required'
operator: 'throws'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:652:12)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 89.932343
@@ -0,0 +1,36 @@
TAP version 13
# Subtest: C2-09a: explicit creative instances bind independent threads, default sessions and holders
not ok 1 - C2-09a: explicit creative instances bind independent threads, default sessions and holders
---
duration_ms: 19.275187
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:605:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'assistant'
- 'writer0'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'writer0'
actual: 'assistant'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:615:12)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 91.60992
@@ -0,0 +1,30 @@
TAP version 13
# Subtest: C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
not ok 1 - C2-09a: explicit missing, non-thread and malformed selectors create no thread or session
---
duration_ms: 17.510719
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:631:1'
failureType: 'testCodeFailure'
error: 'Missing expected exception.'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
code: 'thread-instance-missing'
operator: 'throws'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:635:12)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 89.201033
@@ -0,0 +1,51 @@
TAP version 13
# Subtest: C2-09a: explicit creative instances bind independent threads, default sessions and holders
not ok 1 - C2-09a: explicit creative instances bind independent threads, default sessions and holders
---
duration_ms: 19.552932
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:605:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
Comparison {
+ code: 'storage-refused'
- code: 'thread-instance-ambiguous'
}
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
code: 'thread-instance-ambiguous'
actual:
error: 'storage-refused'
stack: |-
#refused (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/src/broker.mjs:4510:47)
Broker.request (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/src/broker.mjs:4682:26)
Object.call (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:56:44)
assert.throws.code (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:612:27)
getActual (node:assert:580:5)
strict.throws (node:assert:728:24)
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:612:10)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
operator: 'throws'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/threads.test.mjs:612:10)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 94.246865
@@ -0,0 +1,36 @@
TAP version 13
# Subtest: C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
not ok 1 - C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project
---
duration_ms: 18.98222
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/user-principal.test.mjs:399:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ null
- 'access-refused'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'access-refused'
actual: ~
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/bus/tests/user-principal.test.mjs:405:10)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 89.7564
@@ -0,0 +1,29 @@
TAP version 13
# Subtest: C2-09a: creative thread messages start distinct runs and keep each instance history separate
not ok 1 - C2-09a: creative thread messages start distinct runs and keep each instance history separate
---
duration_ms: 197.010178
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-launch.test.mjs:463:1'
failureType: 'testCodeFailure'
error: 'creative thread launch refused: thread-not-allowed'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'refused'
actual: 'refused'
operator: 'notStrictEqual'
stack: |-
send (file:///mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-launch.test.mjs:480:12)
async TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/cli/tests/thread-launch.test.mjs:486:21)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 321.200539
@@ -0,0 +1,67 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
not ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 15.993582
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:34:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
[
{
instance: 'assistant',
name: 'assistant'
},
...
},
+ {
+ instance: 'pm',
+ name: 'Hidden PM'
+ }
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0:
instance: 'assistant'
name: 'assistant'
1:
instance: 'bean'
name: 'Bean'
2:
instance: 'chet'
name: 'Bean'
actual:
0:
instance: 'assistant'
name: 'assistant'
1:
instance: 'bean'
name: 'Bean'
2:
instance: 'chet'
name: 'Bean'
3:
instance: 'pm'
name: 'Hidden PM'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:43:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 119.791147
@@ -0,0 +1,67 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
not ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 16.209347
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:34:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
[
{
instance: 'assistant',
name: 'assistant'
},
{
instance: 'bean',
+ name: 'bean'
- name: 'Bean'
},
{
instance: 'chet',
+ name: 'chet'
- name: 'Bean'
}
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0:
instance: 'assistant'
name: 'assistant'
1:
instance: 'bean'
name: 'Bean'
2:
instance: 'chet'
name: 'Bean'
actual:
0:
instance: 'assistant'
name: 'assistant'
1:
instance: 'bean'
name: 'bean'
2:
instance: 'chet'
name: 'chet'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:43:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 112.500283
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a malformed instance and extra thread fields refuse before the channel write
not ok 1 - C2-09a malformed instance and extra thread fields refuse before the channel write
---
duration_ms: 17.897837
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat.test.mjs:347:1'
failureType: 'testCodeFailure'
error: |-
null
503 !== 400
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 400
actual: 503
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat.test.mjs:351:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 122.242476
@@ -0,0 +1,54 @@
TAP version 13
# Subtest: C2-09a createThread forwards an explicit instance through the existing channel
not ok 1 - C2-09a createThread forwards an explicit instance through the existing channel
---
duration_ms: 18.423418
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat.test.mjs:339:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
[
[
'thread.create',
{
- instance: 'bean',
name: 'Bean work',
project: '10000000-0000-4000-8000-000000000001'
}
]
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0:
0: 'thread.create'
1:
project: '10000000-0000-4000-8000-000000000001'
name: 'Bean work'
instance: 'bean'
actual:
0:
0: 'thread.create'
1:
project: '10000000-0000-4000-8000-000000000001'
name: 'Bean work'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat.test.mjs:344:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 120.076841
@@ -0,0 +1,52 @@
TAP version 13
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
not ok 1 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 569.715562
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:270:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
{
body: {
- instance: 'bean',
name: 'Bean discussion',
project: '10000000-0000-4000-8000-000000000001'
},
path: '/api/chat/threads',
redirect: 'manual'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
path: '/api/chat/threads'
body:
project: '10000000-0000-4000-8000-000000000001'
name: 'Bean discussion'
instance: 'bean'
redirect: 'manual'
actual:
path: '/api/chat/threads'
body:
project: '10000000-0000-4000-8000-000000000001'
name: 'Bean discussion'
redirect: 'manual'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:294:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 671.053636
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
not ok 1 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 605.200854
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:270:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'bean' !== ''
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: ''
actual: 'bean'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:274:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 710.854507
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
not ok 1 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 576.032383
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:297:1'
failureType: 'testCodeFailure'
error: |-
The input did not match the regular expression /read the thread instances/. Input:
'No thread instances are configured.'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
actual: 'No thread instances are configured.'
operator: 'match'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:301:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 679.633888
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
not ok 1 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 568.713206
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:270:1'
failureType: 'testCodeFailure'
error: |-
The input did not match the regular expression /\(bean\)/. Input:
'<img src=x onerror="window.injected=1">\\u001b[31m\\u202eevil'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
actual: '<img src=x onerror="window.injected=1">\\u001b[31m\\u202eevil'
operator: 'match'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:278:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 683.056698
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
not ok 1 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 66757.108414
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:297:1'
failureType: 'testCodeFailure'
error: |-
The input did not match the regular expression /no longer available/. Input:
''
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
actual: ''
operator: 'match'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:313:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 66863.389451
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
not ok 1 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 607.794033
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:297:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'chet' !== ''
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: ''
actual: 'chet'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:312:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 709.649006
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
not ok 1 - C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST
---
duration_ms: 591.47268
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:270:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
false !== true
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: true
actual: false
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:276:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 697.367041
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
not ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 19.755478
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:34:1'
failureType: 'testCodeFailure'
error: |-
The expression evaluated to a falsy value:
assert.ok([400, 405].includes(result.status))
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: true
actual: false
operator: '=='
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:49:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 123.785013
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
not ok 1 - C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads
---
duration_ms: 19.259813
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:34:1'
failureType: 'testCodeFailure'
error: |-
The expression evaluated to a falsy value:
assert.ok([400, 405].includes(result.status))
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: true
actual: false
operator: '=='
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-instances.test.mjs:49:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 122.394255
@@ -0,0 +1,33 @@
TAP version 13
# Subtest: C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
not ok 1 - C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching
---
duration_ms: 528.731905
type: 'test'
location: '/mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:297:1'
failureType: 'testCodeFailure'
error: |-
The input did not match the regular expression /No thread instances/. Input:
'Reading thread instances…'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
actual: 'Reading thread instances…'
operator: 'match'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r219-g3345/packages/webui/tests/chat-browser.test.mjs:305:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 630.644432
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more