diff --git a/agents/researcher/work/queue-88/r1/BUILD.md b/agents/researcher/work/queue-88/r1/BUILD.md new file mode 100644 index 000000000..90091c456 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/BUILD.md @@ -0,0 +1,212 @@ +# Row 88 (#1582) build, r1 + +Phase B complete on the rebased candidate. Base: 8e0fbca420a3161ce98c5e0f3910f500daabebba +(refactor, #1588 plus row 80 and later). Result: every package suite and every test script +passed with zero failures, and all eight mutants were KILLED. Nothing committed, pushed, +merged or moved in the queue. + +Correction (2026-10-13, after the first phase B report): the first version of this file said +"zero skips" for every suite. That was wrong for three scripts. The phase B run had +DOCKER_HOST=unix:///nonexistent.sock exported, so docker-dependent cases skipped: +test-release (4 passed, state-machine cases skipped), test-task (docker cases skipped), and +test-queue (`queue verify and render --check` skipped, because this worktree is not the queue's +canonical root). The package suites had zero skips. See "Script skips" below. + +Revision (the fix Sage ruled on, same base): line 99 of bus-browser.test.mjs now uses the +`\u202e` escape instead of a literal U+202E. The runtime string is unchanged. The webui suite +was rerun (108/108, 0 skipped), and mutants.py was rerun (all eight KILLED). The manifest was +regenerated; see "Revision" below for the new sha256. + +Owner: researcher. Reviewer: Filbert. Brief: docs/plans/2026-10-12_row61-followup.md +(read only, not edited). Ruling: the packet is at agents/researcher/work/queue-88/r1/, +not agents/darkwing/work/queue-/ as the brief names it. + +## Base and rebase + +BASE was 2b2a9e14eec4ff81cdada5dc42bbf1b8063b29b4 (origin/refactor, #1588). Sage's +slot-clear message moved the candidate to 8e0fbca420a3161ce98c5e0f3910f500daabebba +(decision 127). Since 2b2a9e14, row 97 changed packages/webui/README.md (the Board-test +paragraph, near line 567). Rebase steps, in order: + +1. The five tracked paths were stashed by tag ("row88-r1-rebase-20261013"), not with a bare + stash. The entry's SHA was captured (bf6831953e6e04cbd8b5ef4cd1236a3575aa90e3) before + the detach. Other sessions' stash entries were left in place. +2. `git checkout --detach 8e0fbca420a3161ce98c5e0f3910f500daabebba`. +3. `git stash apply `, not pop. README auto-merged with no conflict. The entry was + then dropped by its SHA. +4. Checked: `git diff --stat` against the new base shows only the five paths (93 insertions, + 18 deletions). The README diff against the new base is my one-phrase hunk, so row 97's + paragraph is intact in HEAD. No conflict markers. + +base.txt holds 8e0fbca420a3161ce98c5e0f3910f500daabebba. build.patch was regenerated with +`git diff --binary 8e0fbca... -- packages` and covers the five paths only. + +node_modules: linked to /mnt/storage/src/mosaic-stack/node_modules for the run, then removed +with a plain rm (the link only). The canonical node_modules is untouched. + +## Files changed + +- packages/webui/src/public/bus.js: one attribute value, the writers table aria-label (line 400). +- packages/webui/README.md: the task fields paragraph (line 522), one phrase. +- packages/webui/tests/bus-browser.test.mjs: a readFileSync import, the hostile fields copy, + the label-excerpt class assertions (lines 94-99), three S5 assertions (lines 236-240), and + one README pin test (line 76). +- packages/tasks/src/fields.mjs: header comment, lines 3-5. No code change. +- packages/tasks/tests/fields.test.mjs: header comment; schema case (line 29); prose test with + the unmapped-name check (line 91); cto walk actor (lines 143 onward). + +Not changed: fields.json, roles/*.json, verbs.mjs, broker.mjs, bus.js beyond the aria-label, +the slice 1 plan, the brief. + +Packet files: base.txt, build.patch, files.txt, candidate-manifest.sha256, BUILD.md, +mutants.py, phase-b.sh (the runner, see below), out/ (logs of this run). + +Runner note: the worktree guard refused a command that ran `env -C ` with a glob computed +at runtime. The phase B sequence therefore lives in r1/phase-b.sh, a file with no glob in the +command text. It runs each suite with set -e, so the first nonzero exit stops the run. It was +run once, from the background, against this worktree. + +## Phase B results + +Run: DOCKER_HOST=unix:///nonexistent.sock, sequential, each step's output in r1/out/. +The counts are from the "ℹ tests / pass / fail / skipped" lines of each log. + +| suite | tests | pass | fail | skipped | log | +|---|---|---|---|---|---| +| packages/bus (glob) | 127 | 127 | 0 | 0 | out/pkg-bus.log | +| packages/business | 67 | 67 | 0 | 0 | out/pkg-business.log | +| packages/cli (glob) | 137 | 137 | 0 | 0 | out/pkg-cli.log | +| packages/control-board | 124 | 124 | 0 | 0 | out/pkg-control-board.log | +| packages/conversation | 182 | 182 | 0 | 0 | out/pkg-conversation.log | +| packages/discord | 178 | 178 | 0 | 0 | out/pkg-discord.log | +| packages/harness (glob) | 69 | 69 | 0 | 0 | out/pkg-harness.log | +| packages/ledger (no package.json; tests/ run directly) | 78 | 78 | 0 | 0 | out/pkg-ledger.log | +| packages/mosaic | 69 | 69 | 0 | 0 | out/pkg-mosaic.log | +| packages/queue | 150 | 150 | 0 | 0 | out/pkg-queue.log | +| packages/runs | 43 | 43 | 0 | 0 | out/pkg-runs.log | +| packages/seat | 105 | 105 | 0 | 0 | out/pkg-seat.log | +| packages/tasks (glob) | 92 | 92 | 0 | 0 | out/pkg-tasks.log | +| packages/webui | 108 | 108 | 0 | 0 | out/pkg-webui.log | + +"(glob)" marks the packages whose package.json script runs tests/*.test.mjs. The others run +tests/ as given, as their scripts do. + +Scripts (each exit 0; the last line of each log is its own selftest or suite summary): + +| script | own summary | log | +|---|---|---| +| scripts/test-auth.sh | selftest: 15 passed, 0 failed | out/script-auth.log | +| scripts/test-conductor.sh | selftest: 17 passed, 0 failed | out/script-conductor.log | +| scripts/test-config.sh | selftest: 24 passed, 0 failed | out/script-config.log | +| scripts/test-discord.sh | discord suite: 66 passed, 0 failed | out/script-discord.log | +| scripts/test-extension-package.sh | extension package selftest: 18 passed, 0 failed | out/script-extension-package.log | +| scripts/test-foundation.sh | selftest: 44 passed, 0 failed | out/script-foundation.log | +| scripts/test-queue.sh | queue suite: 27 passed, 0 failed | out/script-queue.log | +| scripts/test-release.sh | selftest: 4 passed, 0 failed | out/script-release.log | +| scripts/test-task.sh | selftest: 26 passed, 0 failed | out/script-task.log | + +The row 88 tests themselves are in the logs as passing: the README pin (pkg-webui.log), the S5 +browser test with its inertCheck (pkg-webui.log), the inherited-verb test and the prose test +(pkg-tasks.log). + +Pass-count note: packages/tasks/tests/fields.test.mjs is run by the glob; its new tests are +the inherited-verb case, the prose test and the walk. The walk ran with the cto as an actor. + +## Ruling D: inertCheck + +Outcome: passed. The S5 browser test ran inertCheck('task') (bus-browser.test.mjs line 240) +against the page built from the hostile copy. The test did not fail, so inert() strips or +neutralizes the control and bidi characters before txt() escapes them, as assumed. Nothing in +bus.js was changed beyond the aria-label. Note the assumption was checked by this run, not by +reading inert() in phase A. + +## Mutants: run after the suites passed + +Runner: r1/mutants.py, run as `python3 -I .../r1/mutants.py `. Log: +out/mutants.log. Both baselines passed first (packages/webui bus-browser, packages/tasks +fields). Every mutant was restored with a byte check. No RESTORE or ANCHOR failure. Verdict per +mutant: KILLED. + +The runner records only the exit code of each killer run, not which assertion fired. The +killer locations below are the test and the assertion lines that cover each item, read from the +test source. A line-level failure trace would need a re-run that keeps the killer's output. + +| id | mutation (file) | verdict | killer (file:line) | +|---|---|---|---| +| M1 | aria-label suffix removed (packages/webui/src/public/bus.js:400) | KILLED | packages/webui/tests/bus-browser.test.mjs:236 (aria-label assertion, in the S5 test at :81) | +| M2 | Object.hasOwn becomes `in` (packages/tasks/src/fields.mjs:48) | KILLED | packages/tasks/tests/fields.test.mjs:29 (inherited-verb test) | +| M3 | label not escaped (bus.js:399) | KILLED | packages/webui/tests/bus-browser.test.mjs:238 (comments label shown), and :240 (inertCheck('task')) | +| M3b | note not escaped (bus.js:399) | KILLED | packages/webui/tests/bus-browser.test.mjs:239 (note shown), and :240 (inertCheck('task')) | +| M4 | due date row: task.schedule to task.assign (docs/plans/2026-10-04_slice-1.md:77) | KILLED | packages/tasks/tests/fields.test.mjs:91 (prose test), assertion at :115 | +| M5 | verbs.mjs:145 lets cto pass the pm writer check (packages/tasks/src/verbs.mjs:145) | KILLED | packages/tasks/tests/fields.test.mjs:143 (walk), expectation at :201 | +| M6 | README wording reverted (packages/webui/README.md:522) | KILLED | packages/webui/tests/bus-browser.test.mjs:76 (README pin) | +| M9 | broker line 676 routing rule removed (packages/bus/src/broker.mjs:676) | KILLED | packages/tasks/tests/fields.test.mjs:143 (walk), route expectation at :223-229 | + +Items 3 and 4 have no mutant (comment-only; Sage ruling B). + +## Resolved finding: literal bidi character in the test source + +Found after the first run: packages/webui/tests/bus-browser.test.mjs line 99 held a literal +U+202E (bytes E2 80 AE), an invisible right-to-left override in source. Sage ruled to fix it +(Trojan Source pattern). Fixed: line 99 now reads +`assert.ok(comments.label.includes('\u202e'), ...)`, the escape used on the lines around it. +The string's runtime value is unchanged. The fix is a single-character edit that does not move +any line, so the killer locations in the table above still hold. + +Checks after the fix (run with `grep -cP '[\x{200E}\x{200F}\x{202A}-\x{202E}\x{2066}-\x{2069}]'`): +- packages/webui/tests/bus-browser.test.mjs: 0 (it was 1) +- build.patch: 0 + +## Script skips + +Logs under out/ that show skips (none is a failure; all exit 0): +- out/script-release.log: "skip state-machine cases (docker daemon unavailable)". The 4 passing + tests are the non-docker cases. The state-machine cases were skipped only because DOCKER_HOST + pointed at a dead socket; on the normal host they run, so the release count differs there. +- out/script-task.log: adapter seam, workspace/capability, live task, and live recall pair + cases skipped (docker daemon unavailable). +- out/script-queue.log: "skip queue verify and render --check: this checkout is not the queue's + canonical root". This check is not run in a worktree. It must run on the canonical checkout + (or be run by the gate there). +- The package suites (out/pkg-*.log) have "\u2139 skipped 0"; the skip lines matched there are test + names, not skipped cases. + +The scripts were not rerun in this revision; no script references packages/webui or the changed +test (grep), so the phase B script results stand. + +## Revision: fix, reruns, manifest + +- base.txt: 8e0fbca420a3161ce98c5e0f3910f500daabebba (unchanged). +- build.patch: regenerated with `git diff --binary 8e0fbca420a3161ce98c5e0f3910f500daabebba -- packages`; + 237 lines; five paths; 0 bidi control characters. +- candidate-manifest.sha256, file sha256: 59a164c6099b490c3dfbcb7b11dbae63ed94a25c16bf131115914c829e17f5a6. + The changed hash is bus-browser.test.mjs (now a979fe69...). `sha256sum -c` passes on all five. +- webui suite rerun (DOCKER_HOST=unix:///nonexistent.sock, `node --test tests/`, out/pkg-webui.log): + exit 0; tests 108, pass 108, fail 0, skipped 0. +- mutants.py rerun, gated on the webui pass (out/mutants.log, overwritten): both baselines ok; + M1, M2, M3, M3b, M4, M5, M6, M9 all KILLED; exit 0. The runner restores each file with a + byte check; the manifest verified again afterwards. +- node_modules: linked for the run, removed with plain rm. Canonical node_modules intact. +- No process has a cwd in the worktree after the run (the two /proc matches seen were my own + shell and had exited). +- Nothing committed, pushed, or moved in the queue. + +## Deviations from the plan, recorded + +- Item 8 label is HOSTILE.slice(34), not all of HOSTILE, because checkFields caps labels at 80 + (fields.mjs line 42). Accepted by Sage (ruling A). +- The phase B sequence is in r1/phase-b.sh because the guard refused `env -C` with a runtime + glob. The commands are the same ones Sage listed. The ledger package has no package.json, so + its tests/ directory was run directly. +- The row 88 README hash in candidate-manifest.sha256 changed (76cd207... to 209434f...) because + the README on the new base includes row 97's paragraph. The other four hashes are unchanged. + +## Open questions, with Sage's rulings + +A. Ruled: the label excerpt is accepted. Deviation recorded under item 8; the excerpt-class + assertions are at bus-browser.test.mjs lines 94-99. +B. Ruled: items 3 and 4 are comment-only. A review read is enough; no pin. +C. Ruled: the walk pins the current broker routing at broker.mjs line 676. +D. Outcome: inertCheck passed in this run (see above). No stop was needed. +E. Ruled, applied: strip at most one trailing s; the prose test fails, not skips, on an + unmapped prose name (fields.test.mjs lines 103-111). diff --git a/agents/researcher/work/queue-88/r1/base.txt b/agents/researcher/work/queue-88/r1/base.txt new file mode 100644 index 000000000..47c4bdae7 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/base.txt @@ -0,0 +1 @@ +8e0fbca420a3161ce98c5e0f3910f500daabebba diff --git a/agents/researcher/work/queue-88/r1/build.patch b/agents/researcher/work/queue-88/r1/build.patch new file mode 100644 index 000000000..d3a722a58 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/build.patch @@ -0,0 +1,237 @@ +diff --git a/packages/tasks/src/fields.mjs b/packages/tasks/src/fields.mjs +index 15d8e4f47..1c1d61fbc 100644 +--- a/packages/tasks/src/fields.mjs ++++ b/packages/tasks/src/fields.mjs +@@ -1,8 +1,8 @@ + import { readFileSync } from 'node:fs'; + import { TASK_VERBS } from '../../bus/src/broker.mjs'; +-// Row 61 (#1550): the slice 1 field table as data, in ../fields.json. The prose in +-// docs/plans/2026-10-04_slice-1.md stays the reviewed source; tests/fields.test.mjs checks both +-// against what the verbs do. Two layers, as lead decision 113 rules: ++// Row 61 (#1550): the slice 1 field table as data, in ../fields.json. The Field table in ++// docs/plans/2026-10-04_slice-1.md is the reviewed prose it must match, and tests/fields.test.mjs ++// checks that prose and this table against what the verbs do. Two layers, as lead decision 113 rules: + // - `verbs`: the writer verbs.mjs checks before anything else. `pm` is the pm definition + // (field-writer otherwise), `assigned` a role whose bot is an assignee (not-assigned + // otherwise), `any` no writer check. +diff --git a/packages/tasks/tests/fields.test.mjs b/packages/tasks/tests/fields.test.mjs +index 59332d0fe..ef81f3385 100644 +--- a/packages/tasks/tests/fields.test.mjs ++++ b/packages/tasks/tests/fields.test.mjs +@@ -9,11 +9,11 @@ import { FakeVikunja } from '../src/fake.mjs'; + import { loadFields, checkFields, FieldTableError, FIELDS_FILE } from '../src/fields.mjs'; + import { TASK_VERBS } from '../../bus/src/broker.mjs'; + import { loadRole } from '../../business/src/index.mjs'; +-// Row 61 (#1550): fields.json against what the verbs do. The writer layer is read from the table; +-// whether a write needs a decision, and who resolves it, from roles/*.json as the business loader +-// reads them (lead decision 113). A version 1 file, the researcher's, grants nothing. +-// Neither expectation is written out here, so a change to the table, a role file or the verbs +-// that leaves them disagreeing fails this file. ++// Row 61 (#1550): fields.json, and the Field table in docs/plans/2026-10-04_slice-1.md, against what ++// the verbs in src/verbs.mjs do, with the role files as given. The writer layer is read from the table; whether a write needs a decision, and who ++// resolves it, from roles/*.json as the business loader reads them (lead decision 113). A version 1 ++// file, the researcher's, grants nothing. Neither expectation is written out here, so a change to ++// the table, the prose, a role file or the verbs that leaves them disagreeing fails this file. + const table = loadFields(); + const ROLES = fileURLToPath(new URL('../../../roles/', import.meta.url)); + const authority = (r) => loadRole(ROLES, r).authority; +@@ -26,6 +26,14 @@ test('the committed table loads frozen; its verbs are the broker task verbs', () + assert.deepEqual(checkFields(raw()), raw()); + }); + ++test('a write names a verb the table has as its own key, not one it inherits', () => { ++ for (const verb of ['constructor', 'toString']) { ++ const t = raw(); ++ t.fields[0].writes[0].verb = verb; ++ assert.throws(() => checkFields(t), tableError('fields[0].writes[0].verb: not a task verb'), verb); ++ } ++}); ++ + test('the schema refuses any other shape', () => { + const bad = { + 'an unknown top key': (t) => (t.writers = {}), +@@ -80,6 +88,34 @@ test('the schema refuses any other shape', () => { + } + }); + ++test('the Field table in the slice 1 plan names the verbs each field is written by', () => { ++ const doc = readFileSync(new URL('../../../docs/plans/2026-10-04_slice-1.md', import.meta.url), 'utf8'); ++ const start = doc.indexOf('### Field table'); ++ const section = doc.slice(start, doc.indexOf('### Rows and order', start)); ++ // Header and separator aside, a row is Field, Writer, Action, Vikunja call. ++ const rows = section ++ .split('\n') ++ .filter((l) => l.startsWith('| ')) ++ .slice(1) ++ .map((l) => l.split('|').slice(1, -1).map((c) => c.trim())); ++ // Names compare without case, backticks or a plural s: "Assignees" is "assignee" in the prose. ++ const norm = (s) => s.toLowerCase().replace(/`/g, '').replace(/s$/, ''); ++ // Every name the prose gives must be a label in fields.json. A name with no label fails here and ++ // the message names it, so a renamed or invented field cannot pass by matching nothing. ++ const labels = new Set(table.fields.map((f) => norm(f.label))); ++ for (const [field] of rows) { ++ for (const name of field.split(', ')) { ++ assert.ok(labels.has(norm(name)), `the Field table names "${name}", which fields.json does not have`); ++ } ++ } ++ for (const f of table.fields) { ++ const hit = rows.filter(([field]) => field.split(', ').map(norm).includes(norm(f.label))); ++ assert.equal(hit.length, 1, `${f.field}: one row of the Field table`); ++ const named = [...hit[0][2].matchAll(/`(task\.[a-z.]+)`/g)].map((m) => m[1]); ++ assert.deepEqual([...new Set(named)].sort(), [...new Set(f.writes.map((w) => w.verb))].sort(), f.field); ++ } ++}); ++ + // A Vikunja write, under /api/v2, as the table names it, and the fields it writes. A bucket move + // writes `done` when it moves into the done bucket and the bucket otherwise. + const CALLS = [ +@@ -114,12 +150,23 @@ test('each verb refuses, asks for a decision and writes as the table and the rol + }; + const dir = mkdtempSync(join(tmpdir(), 'fields-')); + t.after(() => rmSync(dir, { recursive: true, force: true })); +- // The role files' authority, and a researcher seat. ++ // The role files' authority, a researcher seat, and the cto as a tracker seat: the cto is the ++ // technical arbiter, and its own writes are walked like the others'. + const w = await world(t, { + fake, + fetch, + business: (b, { owner, project }) => { + for (const r of ['pm', 'coder', 'reviewer', 'cto']) b.roles[r].authority = authority(r); ++ const ctoBot = fake.user('bot-demo-cto', { owner }); ++ fake.share(project, ctoBot, 1); ++ const ctoFile = join(dir, 'cto.token'); ++ writeFileSync(ctoFile, fake.token(ctoBot, SCOPES.worker) + '\n'); ++ chmodSync(ctoFile, 0o600); ++ b.roles.cto = { ++ ...b.roles.cto, ++ tracker: { bot: 'bot-demo-cto', botId: ctoBot }, ++ credentials: { vikunja: { file: ctoFile, expires: '2099-01-01' } }, ++ }; + const bot = fake.user('bot-demo-researcher', { owner }); + fake.share(project, bot, 1); + const file = join(dir, 'researcher.token'); +@@ -136,9 +183,8 @@ test('each verb refuses, asks for a decision and writes as the table and the rol + assert.deepEqual(w.businesses.demo.roles.researcher.authority, { withinRole: [], crossRole: [] }); + await w.adapter.start('demo'); + assert.equal(w.adapter.status()[0].state, 'ready'); +- const ROLE_NAMES = ['pm', 'coder', 'reviewer', 'researcher']; ++ const ROLE_NAMES = ['pm', 'coder', 'reviewer', 'researcher', 'cto']; + const caps = Object.fromEntries(ROLE_NAMES.map((r) => [r, w.agent(r)])); +- const cto = w.agent('cto'); + const botId = (r) => w.businesses.demo.roles[r].tracker.botId; + const done = w.installed.buckets.done; + const seen = Object.fromEntries(TASK_VERBS.map((v) => [v, new Set()])); +@@ -175,9 +221,12 @@ test('each verb refuses, asks for a decision and writes as the table and the rol + }, + }); + // Cross-role goes to the technical arbiter; anything the role file doesn't list, to the human. +- const route = a.crossRole.includes(verb) ? w.businesses.demo.arbiters.technical : 'human'; ++ // The arbiter's own cross-role decision goes to the human: the broker never routes a role's ++ // decision back to that role (broker.mjs, decision.raise). ++ const arbiter = w.businesses.demo.arbiters.technical; ++ const route = a.crossRole.includes(verb) && arbiter !== role ? arbiter : 'human'; + assert.equal(w.store.get('SELECT route_to FROM decisions WHERE id=?', d.id).route_to, route, `${role} ${verb} route`); +- w.broker.request(route === 'human' ? w.human : cto, { verb: 'decision.resolve', args: { id: d.id, choice: 'yes' } }); ++ w.broker.request(route === 'human' ? w.human : caps[route], { verb: 'decision.resolve', args: { id: d.id, choice: 'yes' } }); + decision = d.id; + } + const r = await w.call(caps[role], verb, decision ? { ...args, decision } : args); +@@ -207,11 +256,12 @@ test('each verb refuses, asks for a decision and writes as the table and the rol + for (const [i, r] of ROLE_NAMES.entries()) await attempt(r, 'task.scope.change', { task_ref, title: `b${i}`, description: `what ${i}` }); + for (const r of ['pm', 'coder', 'researcher']) await attempt(r, 'task.update.assigned', { task_ref, state: 'in-progress' }); + await attempt('reviewer', 'task.update.assigned', { task_ref, percent_done: 0.5, state: 'in-progress', comment: 'started' }); +- // People add the coder and the researcher in Vikunja; each may then write as the assigned role. ++ // People add the coder, the researcher and the cto in Vikunja; each may then write as the assigned role. + const id = Number(task_ref.split('/')[1]); +- for (const r of ['coder', 'researcher']) assert.equal((await w.ui('POST', `/tasks/${id}/assignees`, { user_id: botId(r) })).status, 201); ++ for (const r of ['coder', 'researcher', 'cto']) assert.equal((await w.ui('POST', `/tasks/${id}/assignees`, { user_id: botId(r) })).status, 201); + await attempt('coder', 'task.update.assigned', { task_ref, percent_done: 0.7 }); + await attempt('researcher', 'task.update.assigned', { task_ref, state: 'in-review' }); ++ await attempt('cto', 'task.update.assigned', { task_ref, percent_done: 0.8 }); + await attempt('pm', 'task.close', { task_ref, verdict: 'row 61 r1' }); + assert.equal(fake.task(id).done, true); + for (const v of TASK_VERBS) assert.deepEqual([...seen[v]].sort(), expected(v), v); +diff --git a/packages/webui/README.md b/packages/webui/README.md +index 07caedf3a..de34660cf 100644 +--- a/packages/webui/README.md ++++ b/packages/webui/README.md +@@ -519,7 +519,7 @@ GET `/api/task-fields` is row 61's read (#1550): the task field table, + request. It is repository data, so it answers without `--business` or a + system config. The answer is the table itself, with no `at`. A table that + fails its check is refused whole with 503 `read-failed` and the loader's +-message, which names the file and never a path. Any other method is 405. ++message, which names the file or the key at fault, never a path. Any other method is 405. + + Console's shared CSS, Console CSS, brand.js and local Manrope fonts were copied + unchanged from `agents/dewey/work/wui/`. Font license and source URLs accompany +diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js +index b9f9b2e21..108df3b98 100644 +--- a/packages/webui/src/public/bus.js ++++ b/packages/webui/src/public/bus.js +@@ -397,7 +397,7 @@ + if (!fields.length) return `

${why(Object.assign(new ReadError('invalid-response', ''), { module: true }))}

`; + const by = f => (Array.isArray(f.writes) ? f.writes : []).map(w => `${txt(WRITER[verbs[w.verb]] ?? 'unknown')} ${txt(w.verb)}`).join('
'); + const rows = fields.map(f => `${txt(f.label)}${f.note ? `${txt(f.note)}` : ''}${by(f)}`).join(''); +- return `
${rows}
FieldWriter and verb
`; ++ return `
${rows}
FieldWriter and verb
`; + } + async function taskView(get, ref) { + const row = (await get('tasks')).find(r => r.task_ref === ref); +diff --git a/packages/webui/tests/bus-browser.test.mjs b/packages/webui/tests/bus-browser.test.mjs +index e9b2a9c8e..106a8dd26 100644 +--- a/packages/webui/tests/bus-browser.test.mjs ++++ b/packages/webui/tests/bus-browser.test.mjs +@@ -4,7 +4,7 @@ + // CLI or reaches a real broker; the seeding writes go straight to the broker. + import { test } from 'node:test'; + import assert from 'node:assert/strict'; +-import { copyFileSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; ++import { copyFileSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; + import { tmpdir } from 'node:os'; + import { join } from 'node:path'; + import { Store } from '../../bus/src/store.mjs'; +@@ -71,12 +71,33 @@ function seed(root) { + return { store, bus, calls, second, fail: code => { fail = code; }, ids: { old: old.id, open: open.id, routed: routed.id, resolved: resolved.id } }; + } + ++// Row 61 (#1550): the README's account of a refused field table, pinned so the wording and the ++// behavior it describes move together. reads.test.mjs checks the messages themselves. ++test('the README says a refused field table names the file or the key at fault, never a path', () => { ++ const readme = readFileSync(new URL('../README.md', import.meta.url), 'utf8'); ++ assert.match(readme, /the loader's\s+message, which names the file or the key at fault, never a path\./); ++}); ++ + test('S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes', { timeout: 180000 }, async () => { + const root = mkdtempSync(join(tmpdir(), 'webui-bus-browser-')); + const s = seed(root), f = await fixture(), b = await browser(); + // Row 61 (#1550): a copy of the field table, so the test can break it and read it again. + const fieldsFile = join(root, 'fields.json'); + copyFileSync(FIELDS_FILE, fieldsFile); ++ // Hostile text in the comments row, as the table's own label and note: the label is at most ++ // 80 characters, so it carries an excerpt of HOSTILE; the note carries all of it. ++ const hostile = JSON.parse(readFileSync(fieldsFile, 'utf8')); ++ const comments = hostile.fields.find(x => x.field === 'comments'); ++ comments.label = `Comments ${HOSTILE.slice(34)}`; ++ comments.note = `Append only. ${HOSTILE}`; ++ // The label excerpt must keep each hostile class, so a later change to the slice cannot drop one ++ // without failing here. These assert on the data copied in, not on the rendered page. ++ assert.ok(comments.label.length <= 80, 'the label fits the 80-character cap'); ++ assert.ok(comments.label.includes(''), 'the label excerpt is shown'); ++ assert.ok(t7.includes(`Append only. ${HOSTILE.slice(0, 34)}`), 'the note is shown'); + await inertCheck('task'); await noOverflow('task'); + // A table that fails its check: the section says why, and the rest of the task page stays. + writeFileSync(fieldsFile, '{"source":'); diff --git a/agents/researcher/work/queue-88/r1/candidate-manifest.sha256 b/agents/researcher/work/queue-88/r1/candidate-manifest.sha256 new file mode 100644 index 000000000..d86b131a5 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/candidate-manifest.sha256 @@ -0,0 +1,5 @@ +96e697437218f1d200e3e11cf3dd181eac3281cc4532480f15a9088dfa6c5e62 packages/webui/src/public/bus.js +209434f617bfa720d14792b5c38261945d636bf3450ab1970d46e86144174a8c packages/webui/README.md +a979fe6983e0acfec63a65caa71f6c3144d60829b948696bf0464af26cdf3360 packages/webui/tests/bus-browser.test.mjs +6da744bbc7b3b159afd7de9d5b8a8ba76df1aac23c8fc90ade2a2e5f375e93a3 packages/tasks/src/fields.mjs +2a4ecb0ccfa51437e06796aa1944983c30e8a7dc55c32636a3aa5bcd4466d651 packages/tasks/tests/fields.test.mjs diff --git a/agents/researcher/work/queue-88/r1/files.txt b/agents/researcher/work/queue-88/r1/files.txt new file mode 100644 index 000000000..248bee850 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/files.txt @@ -0,0 +1,5 @@ +packages/webui/src/public/bus.js +packages/webui/README.md +packages/webui/tests/bus-browser.test.mjs +packages/tasks/src/fields.mjs +packages/tasks/tests/fields.test.mjs diff --git a/agents/researcher/work/queue-88/r1/mutants.py b/agents/researcher/work/queue-88/r1/mutants.py new file mode 100644 index 000000000..40326da15 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/mutants.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Row 88 (#1582) mutants, r1. Written in phase A; NOT RUN in phase A. + +Run only after Sage sends "slot clear" and the suites pass: + python3 -I agents/researcher/work/queue-88/r1/mutants.py +Optional: --only runs one mutant after the baseline. + +Each mutant is one exact text replacement that must match exactly once. The killer +command runs with the mutant in place. The file is restored in a finally block and +its bytes are checked against the original. Each run has its own process group, and +the group is killed on exit and on timeout. A timeout is reported as a gate failure, +never as a kill. Baseline: every killer must pass unmutated, or nothing runs. +""" +import hashlib +import os +import pathlib +import signal +import subprocess +import sys + +WEBUI_BROWSER = ['node', '--test', 'tests/bus-browser.test.mjs'] +TASKS_FIELDS = ['node', '--test', 'tests/fields.test.mjs'] +TIMEOUT = 600 + +MUTANTS = [ + dict(id='M1', file='packages/webui/src/public/bus.js', + old='aria-label="Who may write each field, scroll for all columns"', + new='aria-label="Who may write each field"', + cwd='packages/webui', argv=WEBUI_BROWSER, why='item 1: aria-label suffix'), + dict(id='M2', file='packages/tasks/src/fields.mjs', + old='if (!Object.hasOwn(table.verbs, w.verb))', + new='if (!(w.verb in table.verbs))', + cwd='packages/tasks', argv=TASKS_FIELDS, why='item 7: inherited verb key accepted'), + dict(id='M3', file='packages/webui/src/public/bus.js', + old='${txt(f.label)}${f.note', + new='${f.label}${f.note', + cwd='packages/webui', argv=WEBUI_BROWSER, why='item 8: label not escaped'), + dict(id='M3b', file='packages/webui/src/public/bus.js', + old='${txt(f.note)}', + new='${f.note}', + cwd='packages/webui', argv=WEBUI_BROWSER, why='item 8: note not escaped'), + dict(id='M4', file='docs/plans/2026-10-04_slice-1.md', + old='| due date | pm | `task.create`, `task.schedule` |', + new='| due date | pm | `task.create`, `task.assign` |', + cwd='packages/tasks', argv=TASKS_FIELDS, why='item 5: prose verb edit'), + dict(id='M5', file='packages/tasks/src/verbs.mjs', + old="if (PM_VERBS.has(verb) && mine.definition !== 'pm') fail('field-writer');", + new="if (PM_VERBS.has(verb) && mine.definition !== 'pm' && mine.definition !== 'cto') fail('field-writer');", + cwd='packages/tasks', argv=TASKS_FIELDS, why='item 6: cto passes the pm writer check'), + dict(id='M6', file='packages/webui/README.md', + old='names the file or the key at fault, never a path', + new='names the file and never a path', + cwd='packages/webui', argv=WEBUI_BROWSER, why='item 2: README wording reverted'), + dict(id='M9', file='packages/bus/src/broker.mjs', + old="const route = cls === 'cross-role' && proposedRoute === s.role ? 'human' : proposedRoute;", + new='const route = proposedRoute;', + cwd='packages/tasks', argv=TASKS_FIELDS, why='item 6: arbiter own decision not sent to human'), +] + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def run(argv, cwd, timeout): + proc = subprocess.Popen(argv, cwd=cwd, start_new_session=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + out, _ = proc.communicate(timeout=timeout) + return proc.returncode, out.decode(errors='replace') + except subprocess.TimeoutExpired: + os.killpg(proc.pid, signal.SIGKILL) + out, _ = proc.communicate() + return 'timeout', out.decode(errors='replace') + finally: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + + +def main(): + root = pathlib.Path(sys.argv[1]).resolve() + only = sys.argv[3] if len(sys.argv) > 3 and sys.argv[2] == '--only' else None + seen = set() + for m in MUTANTS: + key = (m['cwd'], tuple(m['argv'])) + if key in seen: + continue + seen.add(key) + rc, _ = run(m['argv'], root / m['cwd'], TIMEOUT) + if rc != 0: + print(f"BASELINE FAILED {m['cwd']} {' '.join(m['argv'])} rc={rc}; no mutant was run") + return 2 + print(f"baseline ok {m['cwd']} {' '.join(m['argv'])}") + results = [] + for m in MUTANTS: + if only and m['id'] != only: + continue + path = root / m['file'] + orig = path.read_bytes() + text = orig.decode() + if text.count(m['old']) != 1: + results.append((m['id'], 'ANCHOR', f"old text matches {text.count(m['old'])} times")) + continue + path.write_bytes(text.replace(m['old'], m['new'], 1).encode()) + try: + rc, _ = run(m['argv'], root / m['cwd'], TIMEOUT) + finally: + path.write_bytes(orig) + if sha(path.read_bytes()) != sha(orig): + print(f"RESTORE FAILED {m['file']}; stop and report") + return 3 + if rc == 'timeout': + verdict = 'TIMEOUT (gate failure, not a kill)' + elif rc == 0: + verdict = 'SURVIVED' + else: + verdict = 'KILLED' + results.append((m['id'], verdict, m['why'])) + print(f"{m['id']}: {verdict} ({m['why']})") + print('---') + for r in results: + print(*r) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/agents/researcher/work/queue-88/r1/out/mutants.log b/agents/researcher/work/queue-88/r1/out/mutants.log new file mode 100644 index 000000000..ae1f1a1f1 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/mutants.log @@ -0,0 +1,19 @@ +baseline ok packages/webui node --test tests/bus-browser.test.mjs +baseline ok packages/tasks node --test tests/fields.test.mjs +M1: KILLED (item 1: aria-label suffix) +M2: KILLED (item 7: inherited verb key accepted) +M3: KILLED (item 8: label not escaped) +M3b: KILLED (item 8: note not escaped) +M4: KILLED (item 5: prose verb edit) +M5: KILLED (item 6: cto passes the pm writer check) +M6: KILLED (item 2: README wording reverted) +M9: KILLED (item 6: arbiter own decision not sent to human) +--- +M1 KILLED item 1: aria-label suffix +M2 KILLED item 7: inherited verb key accepted +M3 KILLED item 8: label not escaped +M3b KILLED item 8: note not escaped +M4 KILLED item 5: prose verb edit +M5 KILLED item 6: cto passes the pm writer check +M6 KILLED item 2: README wording reverted +M9 KILLED item 6: arbiter own decision not sent to human diff --git a/agents/researcher/work/queue-88/r1/out/pkg-bus.log b/agents/researcher/work/queue-88/r1/out/pkg-bus.log new file mode 100644 index 000000000..ee1e889ff --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-bus.log @@ -0,0 +1,135 @@ +✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (32.154704ms) +✔ agents: working counts what the holder received and has not read; routed alone is not working (29.565496ms) +✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (44.011536ms) +✔ agents: working wins over waiting, and Input needed still shows (17.843842ms) +✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (23.818452ms) +✔ agents: state reads only its own business, even for a run name both use (22.344503ms) +✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (23.870846ms) +✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (21.991812ms) +✔ agents: stranded is oldest first and capped at 50 for a role (90.563603ms) +✔ agents: stranded is per role and per business (29.07379ms) +✔ agents: a message routed to a held role and not yet received is not stranded (12.692386ms) +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (30.66113ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (38.169687ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (31.389482ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (18.311773ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (21.193597ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (20.823306ms) +✔ task action subjects and linked decision trail are complete and ordered (27.872877ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (12.621107ms) +✔ business isolation includes inherited object names and cross-business message references (20.672684ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (25.16714ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (16.974116ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (18.945262ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (13.863367ms) +✔ both arbiters require human resolution when their cross-role route is themselves (20.188884ms) +✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (19.807645ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.073831ms) +✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (25.464515ms) +✔ credentials refuses a role session and any argument (24.033953ms) +✔ credentials reads the status on each request, and a broker without one lists none (28.632332ms) +✔ the broker process answers credentials without a token, path or variable in its output, log or store (86.734082ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (6.720288ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.907017ms) +✔ expiry refuses use and env references never become client data (0.892018ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.721256ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.389514ms) +✔ roles lists open decisions routed to a role, and never the human inbox (42.466974ms) +✔ recent lists closed decisions of every route, newest close first, with how each closed (42.548884ms) +✔ recent gives the closing event as closed when the decision was seen first (33.820964ms) +✔ recent stops at RECENT rows (106.846694ms) +✔ every row is what trail already gives the same session for that id (39.338671ms) +✔ a role session can't list decisions, so it can't read another role's open decisions (37.215507ms) +✔ decisions is a read: one business, strict arguments, nothing written (34.054614ms) +✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (25.158477ms) +✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (21.759125ms) +✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (20.924198ms) +✔ endLaunch leaves a claim another run took alone (19.394753ms) +✔ refuse records action.refused against the caller with the code only (15.831795ms) +✔ launches off refuses role.launch with launch-revoked until launches on (19.429979ms) +✔ broker process: launch ops authorize role.launch, record refusals and end runs (101.316813ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (6.739646ms) +✔ process reader gets own kernel identity without exposing environment values (1.066794ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.895765ms) +✔ real pid 1 remains inspectable when its environment is protected (0.373228ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (34.35105ms) +✔ missing and foreign-business citations refuse and roll back message and grant (28.07279ms) +✔ cross-role sends still need a matching resolved decision and consume it once (29.158061ms) +✔ messages pages all human messages newest first without writes; read state is history (362.609383ms) +✔ messages refuses role callers, invalid paging and cursors outside this human feed (21.299171ms) +✔ views messages passes only its paging arguments through (0.404197ms) +✔ messages isolates businesses even with a valid foreign cursor (11.509175ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (93.451331ms) +✔ startup token refusal returns safe code without value or partial listening broker (58.693643ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (80.241704ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (100.361455ms) +✔ trusted host registers later launches; socket clients never have a registration verb (75.201018ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (37.607832ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (49.253746ms) +✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (50.074452ms) +✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (24.018014ms) +✔ a message with no reply gives an empty list (18.990485ms) +✔ replies come oldest first and stop at REPLIES (87.112928ms) +✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (31.622464ms) +✔ strict arguments; a role session gets human-required (35.754003ms) +✔ views passes replies through as one call with the id (0.316859ms) +✔ requirements returns the committed list to a holding role session, and no one else (34.882088ms) +✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (21.051768ms) +✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (18.993314ms) +✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (37.842462ms) +✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (27.133244ms) +✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (39.858723ms) +✔ reviewFetch answers only the addressed reviewer, for the request it cites (28.500981ms) +✔ review.verdict answers one request once, to the role that asked (28.92143ms) +✔ task.close on a pushed task needs an approve verdict for the latest push (41.550732ms) +✔ a refusal the host decided is filed on the task it names, and only a task ref counts (23.290246ms) +✔ v3b prototype refusals, views and append-only mutations (107.113002ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (36.300621ms) +✔ another run cannot consume an approval; a failed check leaves it usable (23.639487ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (18.619231ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (33.531033ms) +✔ class drift gated to cross-role refuses before consumption (26.350237ms) +✔ class drift cross-role to gated refuses before consumption (23.916207ms) +✔ class drift gated to within-role refuses before consumption (22.887385ms) +✔ class drift cross-role to within-role refuses before consumption (24.655437ms) +✔ class drift within-role to gated refuses before consumption (20.523084ms) +✔ class drift within-role to cross-role refuses before consumption (16.610045ms) +✔ message.send consumes approval and prevents a later send or authorize (21.952829ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (27.419189ms) +✔ creates private WAL store and excludes a second writer until explicit close (17.196149ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (18.888319ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (18.753173ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (58.572381ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (12.411598ms) +✔ async transactions refuse before invoking their function (10.57707ms) +✔ the session default fits under the reply cap and pages to the end with next (176.813554ms) +✔ the short form: no description, assignees by role, every listed key (84.44107ms) +✔ each filter, alone and together, and total counts every match (165.974893ms) +✔ task_ref reads one task in full, as the whole board has it (83.194336ms) +✔ no arguments is the read the Console and mosaic make, unchanged (69.664065ms) +✔ an unknown argument or a bad value refuses invalid-request (274.962322ms) +✔ tasks rows carry priority and requirement for every session that reads tasks (41.592792ms) +✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (46.618373ms) +✔ an empty business and one with snapshots but no task.created read cleanly (14.234235ms) +✔ recordTask keeps sync reads and a role write apart (23.007193ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (11.245662ms) +✔ a bad entry refuses the whole record (11.155766ms) +✔ taskView reads the projection for one business (13.272027ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (23.462233ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (275.749209ms) +✔ without an adapter the server refuses every task verb (16.510685ms) +✔ the runtime refuses an invalid adapter and closes a valid one (15.953122ms) +✔ the process loads the S3 adapter from plain-data trackers (93.053763ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (29.90853ms) +✔ two wire claims serialize; a lost reply never automatically retries (22.607657ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (13.431756ms) +✔ client preserves UTF-8 when a response divides a multibyte character (15.380377ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (15.060819ms) +ℹ tests 127 +ℹ suites 0 +ℹ pass 127 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1031.166301 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-business.log b/agents/researcher/work/queue-88/r1/out/pkg-business.log new file mode 100644 index 000000000..6a562022f --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-business.log @@ -0,0 +1,75 @@ +✔ config directory and file path follow MOSAIC_CONFIG (1.178972ms) +✔ the fixture business validates and comes back frozen (3.566625ms) +✔ two instances may share a definition (1.097249ms) +✔ top-level refusals (3.346793ms) +✔ arbiters and projects (4.704064ms) +✔ role instances (2.588988ms) +✔ Vikunja bots (5.800015ms) +✔ a role without Vikunja takes no tracker block (1.902455ms) +✔ credential references match the definition's services (2.374171ms) +✔ launch (6.605361ms) +✔ loadBusiness: file checks (1.55219ms) +✔ loadBusiness: not a regular file (35.273769ms) +✔ loading writes nothing (0.980686ms) +✔ names that are Object.prototype properties don't count as declared (2.547053ms) +✔ the shipped example refuses as written and validates once filled in (0.636411ms) +✔ the role file loads and resolves through loadBusiness (4.457991ms) +✔ the shipped file is exactly decision 104's list (0.59044ms) +✔ the resolved record matches decision 104, so a widened file fails (1.348651ms) +✔ message.send is within role and every other action is gated (0.983455ms) +✔ a business grant of a gated action through limits does not widen the role (1.107627ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.550905ms) +✔ the instance takes no credential references and no tracker (1.902697ms) +✔ usage errors exit 4 (268.447319ms) +✔ validate: a good business exits 0 and prints instance digests (75.537065ms) +✔ validate: project files (308.662639ms) +✔ validate: missing files and a broken system config (222.380555ms) +✔ validate: credential reference problems exit 2 and name each one (63.353745ms) +✔ validate: a token file inside the repository is refused (63.202307ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (182.051753ms) +✔ resolve: prints one instance's record (185.346146ms) +✔ resolve: refusals (366.417079ms) +✔ parse: exactly one of file or env, plus the service's date (1.915661ms) +✔ check: a good file has no problems (0.742114ms) +✔ check never opens the file: a write-only token passes (0.2839ms) +✔ check: file problems (0.689708ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.607833ms) +✔ check: dates and environment references (0.302345ms) +✔ path and load (2.471638ms) +✔ refusals (1.332967ms) +✔ systemVars flattens the validated config (2.215691ms) +✔ precedence: system, business, project, project role, agent (5.239769ms) +✔ limits narrow the definition and never widen it (1.867027ms) +✔ role.launch stays within-role only for the instance the launch block names (3.89242ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.561595ms) +✔ limits.authority narrows cross-role actions too (1.26922ms) +✔ classify (1.358813ms) +✔ the record carries what the broker and launcher need (0.954671ms) +✔ digest: key order doesn't matter, any value change does (5.120489ms) +✔ refusals (1.840067ms) +✔ the four shipped version 2 roles load (2.514889ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (0.904899ms) +✔ shipped authority follows the note's table (0.601641ms) +✔ version 1 files keep loading with no authority (0.933702ms) +✔ the conductor policy isn't a role (0.215552ms) +✔ a missing role file is exit 4, a symbolic link too (0.652901ms) +✔ version 2 refusals (1.215178ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.085515ms) +✔ credentials: Gitea scopes (0.755521ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.03525ms) +✔ credentials: services (0.692816ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.840836ms) +✔ every key names known layers and a merge rule (0.681542ms) +✔ unknown keys and wrong layers refuse (0.555442ms) +✔ types (1.569645ms) +✔ merge: defaults, then the most specific layer wins (0.25246ms) +✔ merge: limits only narrow, and provenance lists each source (0.304161ms) +✔ merge doesn't change its inputs (0.175833ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1801.574643 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-cli.log b/agents/researcher/work/queue-88/r1/out/pkg-cli.log new file mode 100644 index 000000000..a4ee47b2f --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-cli.log @@ -0,0 +1,147 @@ +✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1301.052679ms) +✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (65.830694ms) +✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (172.089424ms) +✔ NO_COLOR: no escape codes, except reverse video on the selected row (232.622251ms) +✔ 1-9 shows the decide flow's own lines and question; only y resolves (46.992105ms) +✔ the app shows the same decide text as mosaic decide (20.846652ms) +✔ any key but y or Y at the question answers No; nothing is sent (177.8983ms) +✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (54.6917ms) +✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (257.84444ms) +✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (32.163724ms) +✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (20.048124ms) +✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (395.297563ms) +✔ quitting at the question answers No and sends nothing, armed or not (33.740078ms) +✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (29.668557ms) +✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (46.897273ms) +✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (288.690609ms) +✔ at every size the decide box shows each line y depends on, or says it is hidden (8424.243973ms) +✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (65.107842ms) +✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (29.834002ms) +✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (16.375557ms) +✔ keys: views, moving, trail of a selection, the command line, help and quit (73.184078ms) +✔ one option: the status line says Press 1, not 1-1 (15.589809ms) +✔ no business: the app says so and :business picks one (8.217069ms) +✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.694525ms) +✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (164.73142ms) +✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.428053ms) +✔ decide with --note still asks; a No sends nothing (9.739411ms) +✔ the app's business comes from a live bus host only (0.998501ms) +✔ runApp leaves the terminal as it found it on q, end of input, SIGTERM, SIGINT, SIGHUP and an error, the first frame's included (7.070168ms) +✔ draw is pure: same state, same frame (0.818098ms) +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (38.434072ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (22.825802ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (14.602677ms) +✔ decide prints a declining choice as declining (11.620871ms) +✔ an unknown outcome is reported once and never resent (13.275521ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (14.574013ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (14.994695ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (14.300435ms) +✔ every human command refuses inside an agent run before it touches the bus (13.065368ms) +✔ usage errors exit 4; no business and no host is a usage error (14.078462ms) +✔ agents and tasks print through the broker (12.723892ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (2.131807ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (57.738886ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (97.247702ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (38.712942ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (42.548726ms) +✔ a business without tracker.baseUrl gets no trackers entry (40.936852ms) +✔ an unknown business and a broken system config refuse with exit 3 (61.289361ms) +✔ follow prints an unsolicited message after plain talk returned (51.801023ms) +✔ talk --follow continues after reply and prints a later unsolicited message (30.280366ms) +✔ inbox lists all unread human messages under decisions, does not deliver or read them (284.532382ms) +✔ follow refuses agent context and bad arguments (8.873839ms) +✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (10.902112ms) +✔ plain talk leaves the process SIGINT handlers unchanged (11.802126ms) +✔ empty views say so (1.118987ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.46219ms) +✔ tasks print the tracker fields the snapshot carries (0.23817ms) +✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (424.037151ms) +✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (296.492721ms) +✔ the size cap refuses a push before any byte (131.078577ms) +✔ a fetch over the cap refuses before any byte reaches the reviewer (290.11865ms) +✔ the socket refuses a request with no capability, an unknown one, and a bad request line (139.223249ms) +✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (246.562707ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (915.97713ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (141.672182ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (75.528221ms) +✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1079.187438ms) +✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (157.332405ms) +✔ a broker reply with no request waiting breaks the channel and the host exits 1 (76.468301ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (123.002412ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (116.357897ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (67.492849ms) +✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (124.63357ms) +✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (71.886631ms) +✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (137.904744ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (21.591351ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.346794ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (252.961906ms) +✔ bus start refuses with exit 3 without a notifier config (105.428049ms) +✔ bus start runs until bus stop; status reports it while it runs (673.071702ms) +✔ bus-service.sh renders the unit and installs it into a given directory (44.759862ms) +✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (56.983082ms) +✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (12.128533ms) +✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (7.951716ms) +✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (1253.026678ms) +✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (396.847397ms) +✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or kept and logged (3475.334116ms) +✔ a project clone that can't be made refuses checkout-failed, from --pm and from a session, with no run and no clone left (105.671484ms) +✔ a runner that stops at once ends its launch with the runner's reason (171.634637ms) +✔ a Claude Code launch with no session credential refuses before it starts (88.870748ms) +✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (158.128038ms) +✔ a runner killed from the host ends its launch as killed, not failed (221.12791ms) +✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (90.37849ms) +✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1286.253637ms) +✔ 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 (571.018633ms) +✔ 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 (3533.035348ms) +✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (80.634697ms) +✔ an over-long launch request is refused at once, not at the 10 s idle timeout (82.794396ms) +✔ a launch client that never closes its side doesn't hold the host's close (85.461584ms) +✔ a runner that ignores SIGTERM is killed when the host closes (1201.443656ms) +✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.528257ms) +✔ zoned uses the IANA zone across DST (18.082972ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (33.663102ms) +✔ two blocking decisions get two DMs with different nonces (30.099184ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.349269ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (22.063746ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (17.615846ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (18.497945ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (15.75673ms) +✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (105.653214ms) +✔ a restart after the second refusal does not send before that refusal's 30 min are up (50.195633ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (14.346587ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (11.339998ms) +✔ an inbox read failure is logged and the next poll retries (0.79376ms) +✔ no Discord id reaches the journal or the log (9.417977ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (2.354267ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (2.806994ms) +✔ the journal: a whole file that is one torn line truncates to empty (1.300428ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.501206ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.700037ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (2.207722ms) +✔ the journal: a symlinked directory refuses and says it is a link (0.38735ms) +✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.534701ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.447468ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.518482ms) +✔ digest content stays within Discord's 2000 characters (2.041345ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.269447ms) +task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200 +task.close on a missing task answered: task-not-found; it made GET /tasks/999 404 +✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (308.381909ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (53.971284ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2204.469413ms) +✔ busExit and refuseInsideAgent (0.497978ms) +✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (49.001467ms) +✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1016.841451ms) +✔ a late reply the Console has listed still reaches the next talk (2011.298573ms) +✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (10.411174ms) +✔ launches off and on go to the broker and change the business's launch state (8.295262ms) +✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (8.241867ms) +ℹ tests 137 +ℹ suites 0 +ℹ pass 137 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 12958.071394 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-control-board.log b/agents/researcher/work/queue-88/r1/out/pkg-control-board.log new file mode 100644 index 000000000..b596f3105 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-control-board.log @@ -0,0 +1,132 @@ +✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (3.480314ms) +✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.212782ms) +✔ completed smoke replies and ordinary questions are idle, not human blockers (0.717446ms) +✔ only an explicit first-line input request makes a finished reply waiting (0.146883ms) +✔ tool activity, user text, errors and unfinished turns override attention text (0.124835ms) +✔ STOP access failure is unknown, not absence, under a non-root identity (29.856024ms) +✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.554719ms) +✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.252605ms) +✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (4.96004ms) +✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.536417ms) +✔ server rescans connector discovery and refuses HTTP reply without transport (29.262814ms) +✔ connector session links and linked directories are not read (0.975036ms) +✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.334951ms) +✔ CLI print uses the relaunch notice instead of old current preview (54.611578ms) +✔ connector owner and fixed task never inherit a native relaunch notice (1.400583ms) +✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (0.97348ms) +✔ loadConfig: missing file throws ConfigError (1.636832ms) +✔ loadConfig: invalid JSON throws ConfigError (0.291053ms) +✔ loadConfig: missing dataRoot throws ConfigError (0.198453ms) +✔ loadConfig: relative dataRoot throws ConfigError (0.214576ms) +✔ loadConfig: valid config returns dataRoot (0.619933ms) +✔ findNewestSession: picks the newest by mtime among two files (0.424907ms) +✔ findNewestSession: finds files in nested subdirectories (0.273835ms) +✔ findNewestSession: returns null for a missing dir (0.109669ms) +✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.581659ms) +✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.142259ms) +✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.428042ms) +✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (0.996895ms) +✔ deriveState: full state table (0.161336ms) +✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.289363ms) +✔ rule: newest entry is a tool result with no assistant text after it is working (0.249592ms) +✔ rule: a finished ordinary turn is idle, even if it says your move (0.238725ms) +✔ task: the first user message of the session, from text blocks (0.233188ms) +✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.24446ms) +✔ task: no user message in the log means null (shown as unknown), never a guess (0.249021ms) +✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.342261ms) +✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.468196ms) +✔ scan: the written record carries task, workspace and activeProject (0.442417ms) +✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.534786ms) +✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.3503ms) +✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (0.908957ms) +✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.385212ms) +✔ loadRegistrations: a missing seatsDir gives empty lists (0.143224ms) +✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.657604ms) +✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (0.304265ms) +✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.004101ms) +✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.24116ms) +✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.418583ms) +✔ scanAgent: ageSeconds is computed from the injected now (0.241475ms) +✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.137361ms) +✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.290914ms) +✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.406517ms) +✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.784674ms) +✔ scan: relative boardDir throws ConfigError (0.071447ms) +✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (51.151059ms) +✔ CLI: missing config exits 2 with a refused: message (45.724004ms) +✔ CLI: unknown command exits 2 (48.901885ms) +✔ CLI: unknown --liveness value exits 2 (50.015738ms) +✔ panesRunPi: true when any trimmed line equals 'pi' (0.204422ms) +✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.071983ms) +✔ tmuxIsAlive: a pane running pi is alive (0.165609ms) +✔ tmuxIsAlive: session exists but pi has exited is not alive (0.068194ms) +✔ tmuxIsAlive: no such tmux session is not alive (0.047639ms) +✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.055222ms) +✔ tmuxIsAlive: passes -L only when a socket is given (0.087273ms) +✔ parsePanes: one pane per line, command and optional tab-separated path (0.073952ms) +✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.071206ms) +✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.096066ms) +✔ loadSeen: missing file returns {} (0.176021ms) +✔ loadSeen: invalid JSON throws ConfigError (0.186521ms) +✔ loadSeen: a JSON array throws ConfigError (0.148905ms) +✔ loadSeen: a non-string value throws ConfigError (0.158042ms) +✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.300912ms) +✔ markSeen: seen false deletes the key (0.256822ms) +✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.192243ms) +✔ markSeen: project containing '/' throws ConfigError (0.115152ms) +✔ markSeen: non-boolean seen throws ConfigError (0.107261ms) +✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.286961ms) +✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.209834ms) +✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.205788ms) +✔ scanAgent: an error-state session with a matching mark is seen (0.22237ms) +✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.534265ms) +✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.176597ms) +✔ taskSetBy: a registered task carries the record's setter; a record without the field (pre-#1511) reads unknown; the value is not copied into registered (0.614369ms) +✔ taskSetBy: null whenever the task shown is not the registered one: no registration, an empty registered task, a stale registration; the field is always present (0.566178ms) +✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (1.32754ms) +✔ isLoopbackHost: recognizes loopback hosts (1.298944ms) +✔ isLoopbackHost: rejects non-loopback hosts (3.951545ms) +✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (2.872169ms) +✔ startServer: serves page, healthz, and a rescanning /api/board (30.066433ms) +✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (5.280605ms) +✔ startServer: /api/board returns 500 JSON with an error field when scan throws (1.969942ms) +✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (51.086203ms) +✔ CLI: serve rejects a non-numeric --port with exit 2 (47.292317ms) +✔ CLI: scan still works after the async cli refactor (58.156918ms) +✔ CLI: live serve prints its URL and answers /healthz (63.162464ms) +✔ page.html: esc() escapes every HTML-significant character (0.590562ms) +✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (7.706752ms) +✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.828537ms) +✔ POST /api/seen with invalid JSON returns 400 (1.448042ms) +✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (1.916696ms) +✔ POST /api/seen with a missing agent returns 400 (1.294565ms) +✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.617214ms) +✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (50.397682ms) +✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.28345ms) +✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.289897ms) +✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.18376ms) +✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.176298ms) +✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.305571ms) +✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (0.55361ms) +✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S :control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (25.098533ms) +✔ POST /api/reply: a registration with a tmux socket adds -L (25.088695ms) +✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (26.508703ms) +✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (14.691643ms) +✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.422267ms) +✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.16122ms) +✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (0.575874ms) +✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.269613ms) +✔ page.html: the task cell and detail show who set a registered task via setByTag()/setByText(), both escaped, only from rec.taskSetBy; the reply gate does not read it (0.290008ms) +✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (7.559985ms) +✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (33.976066ms) +✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (4.567598ms) +✔ every refusal code the reader can raise has an HTTP status (1.992921ms) +✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (45.838859ms) +ℹ tests 124 +ℹ suites 0 +ℹ pass 124 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 610.686242 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-conversation.log b/agents/researcher/work/queue-88/r1/out/pkg-conversation.log new file mode 100644 index 000000000..546f16238 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-conversation.log @@ -0,0 +1,190 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (120.706128ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (6.715054ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (4.166097ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (107.716976ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (176.718963ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (145.19508ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (19.335975ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (15.631966ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.784419ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (5425.016463ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (24261.152343ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (161.091836ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (101.731771ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (245.285302ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (180.040425ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (209.617084ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (30.139248ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (99.948047ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (31.573163ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (89.070056ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (21.450227ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (52.802012ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (48.671063ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.327172ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.091197ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.741771ms) +✔ no shim from this file's tests is left running for the after hook (4.892455ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (300.051105ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (158.204259ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (286.695096ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2314.946389ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (169.934499ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (275.040158ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (182.341245ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4363.524097ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (422.181918ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (357.305749ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (248.319119ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (61.692073ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (30.170474ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (39.654231ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3039.695745ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (5.219563ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (29.558753ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (26.962875ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (94.689941ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (159.321048ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (280.984017ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (682.746215ms) +✔ E1: a natural engine exit with no member left: an engine-exit stop, stopped on a proof naming the engine, and the scope released (180.430185ms) +✔ E2: an engine exit with a tool child alive: the stop ends uncertain and nothing is released; a force stop then ends it (2222.97532ms) +✔ E3: an engine exit and a force stop take one escalation slot, in either order (635.116089ms) +✔ E4: a shim that doesn't answer: the engine-exit stop misses its deadline, ends uncertain and frees the slot (695.167347ms) +✔ E5: an engine exit after a force stop ended uncertain starts no engine-exit stop; the force stop stays current (2459.036193ms) +✔ E6: the engine-exit observation refuses a reaped process that isn't the recorded engine: another PID or start ticks (181.015197ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (375.058156ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (104.334181ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2326.170159ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (226.345728ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (235.037722ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (187.720823ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2290.817687ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (155.332187ms) +✔ no shim from this file's tests is left running for the after hook (4.66288ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (40.874923ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (27.415101ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (17.388778ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (30.715187ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (17.427561ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.441134ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (20.345036ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (124.706971ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (34.471524ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (18.147138ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (31.298085ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (30.897833ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.89938ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (27.77365ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (47.391549ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (38.618371ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (25.345792ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.54635ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.347642ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.435084ms) +✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.610323ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.615573ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.519177ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.839783ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.446851ms) +✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.351693ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.112065ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (263.899833ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (47.88923ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (79.68383ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (130.403191ms) +✔ H4: self-takeover is refused (17.30876ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (86.910176ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (79.453071ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (25.179747ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (78.481732ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (131.658584ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (15.977409ms) +✔ H13: a retry with the same request ID and different text is refused (15.38562ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (135.928303ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (70.661656ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (16.860601ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (60.622629ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (24.441618ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (55.788276ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (12.509877ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (117.174573ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.666667ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (458.671995ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (355.896159ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (335.858276ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2385.826707ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (456.257176ms) +✔ no shim from this file's tests is left running for the after hook (4.433643ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.357503ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.758775ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.406317ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.31349ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.673993ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.029799ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.728188ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.100687ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.223892ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.14035ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.442277ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (7.741837ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.484108ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (10.784051ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.150708ms) +✔ F9: registrations never add or redirect a root (1.665532ms) +✔ F10: a header cwd naming another project is refused (3.61707ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.840072ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.004349ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.908598ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.502095ms) +✔ F13: compaction is a marker in place, then the retained content (0.77505ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (545.809981ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.606913ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.47585ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.184209ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.085554ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.899304ms) +✔ every page and cursor is a valid CHAT-01 record (778.968189ms) +✔ the engine pin holds for the installed package (2.816787ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (275.139293ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (560.859573ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (258.990968ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (68.752978ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (44.356621ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (19.863242ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.176238ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (32.95739ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (22.093549ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (115.979229ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (45.020948ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1530.904675ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (14.21698ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (69.486995ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (13.395936ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (16.378772ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (32.572551ms) +✔ N10: fake conformance (32.333525ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (29.751043ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (20.143483ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (68.745741ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (30.270993ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (26.474876ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (20.399829ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (13.19345ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (26.900113ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (108.716992ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (135.93206ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (85.805811ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (13.79483ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (13.46777ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (14.001945ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (38.13072ms) +✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (27.563529ms) +ℹ tests 182 +ℹ suites 0 +ℹ pass 182 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 33445.652483 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-discord.log b/agents/researcher/work/queue-88/r1/out/pkg-discord.log new file mode 100644 index 000000000..f413b9de7 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-discord.log @@ -0,0 +1,186 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.370182ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.491367ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.904627ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.468744ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (17.252746ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.387009ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.486969ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (3.195538ms) +✔ authorize: open channel, listed user (1.873561ms) +✔ authorize: wrong guild (0.159959ms) +✔ authorize: no guild (DM) (0.173ms) +✔ authorize: unlisted channel (0.15554ms) +✔ authorize: unknown channel, no info (2.506286ms) +✔ authorize: thread of listed parent (0.200432ms) +✔ authorize: thread of unlisted parent (0.15296ms) +✔ authorize: text channel that is not a thread and not listed (0.146592ms) +✔ authorize: unlisted user (1.11616ms) +✔ authorize: no author (0.501301ms) +✔ authorize: bot author (listed id, bot flag) (0.149627ms) +✔ authorize: system author (0.118838ms) +✔ authorize: the bot itself (0.094499ms) +✔ authorize: webhook (0.160811ms) +✔ authorize: mention channel without mention (0.132694ms) +✔ authorize: mention channel with bot mention (0.133605ms) +✔ authorize: mention channel with @everyone only (0.118966ms) +✔ authorize: mention channel mentioning someone else (0.072696ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.090279ms) +✔ authorize: private thread under mention channel, mentioned (0.087938ms) +✔ authorize: private thread under mention channel, not mentioned (0.073559ms) +✔ authorize: thread in another guild per channel info (0.076951ms) +✔ authorize: not an object (0.062177ms) +✔ authorize: no id (0.060482ms) +✔ authorize: oversize content is accepted and flagged (0.06689ms) +✔ authorize: exactly the limit is not oversize (0.06848ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (1.696858ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.150766ms) +✔ binding: a complete binding validates and is frozen (4.750107ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.787379ms) +✔ binding: empty allowlists refuse (0.431811ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.20946ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.38676ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.382839ms) +✔ binding: file must be 0600, regular, not a symlink (1.380174ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.124192ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (71.30569ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.818019ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (263.460548ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (174.958595ms) +✔ cli: run refuses when STOP is present, before any network use (115.037099ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.538256ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.00503ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (19.36225ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (13.818761ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.367955ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.104632ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.196945ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (1.74649ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.771451ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (2.816367ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (0.660139ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.622004ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.154906ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.309667ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (4.120595ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.540451ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.46424ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (3.832654ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.053373ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.373507ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.070139ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.736665ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.413121ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.750153ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.472055ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.438701ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.75634ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.948008ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.186557ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.431407ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.540847ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.791352ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.404881ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.4784ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (66.32444ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (29.744921ms) +✔ engine: one prompt, one turn, text and usage come back (35.582786ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (339.787986ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (230.775556ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (114.492184ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (225.143227ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (124.786902ms) +✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (212.748603ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (613.628391ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.370511ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.519937ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (428.548543ms) +✔ engine: a malformed JSONL line fails the turn, not the process (23.653137ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (45.282598ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.371189ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.630996ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.474572ms) +✔ gateway: op 9 resumable resumes (0.356464ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.801463ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.587189ms) +✔ gateway: close() is final and unparseable frames are ignored (0.41891ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (46.522885ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (34.27423ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (54.47591ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.27674ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (66.558341ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (70.812179ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (72.611766ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (169.34089ms) +✔ git: push pushes the named branch only and reports up to date (62.931167ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (72.663344ms) +✔ git: the credential helper answers get over https from a private file and nothing else (191.75851ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (691.442644ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (4.15342ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (55.576885ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.68498ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.41422ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (34.445686ms) +✔ lock: a live pid whose record carries a malformed or noncanonical start or boot string is unknown, not a mismatch; nothing signals, removes, or claims over it (249.468877ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.361957ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (22.667948ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.887409ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (38.168059ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (126.409424ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (74.305303ms) +✔ notices: a kind is recorded per UTC day and found again (0.399282ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.874922ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.20713ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.660287ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (41.217474ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (32.320164ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (24.116015ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (56.677069ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (588.36784ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.499924ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (7.293586ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (1.112552ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.265101ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (0.874622ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (4.963325ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.565776ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.385371ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.834542ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (18.111338ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (7.269725ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (4.456727ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (2.396188ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.378079ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (1.704491ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1009.800627ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.465342ms) +✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (5.837472ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.164239ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.203665ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.256798ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.126635ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.628159ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (4.649ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.289909ms) +✔ tools: listing and search caps hold (8.818945ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.814744ms) +✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (4.599759ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (0.853424ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (0.916007ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.249805ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.410998ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.615374ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.114392ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (1.952627ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1022.962312ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.251339ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.252882ms) +✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.661391ms) +✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (2.476694ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2612.781867 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-harness.log b/agents/researcher/work/queue-88/r1/out/pkg-harness.log new file mode 100644 index 000000000..fa0df0779 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-harness.log @@ -0,0 +1,77 @@ +✔ sessionModel: agent vars win, then the system's execution settings (13.375931ms) +✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (4.440604ms) +✔ a claude-code bundle adds the wrapped gate hook and the MCP config (3.062857ms) +✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (10.284474ms) +✔ the prompt names the project clones in the workspace, and none when there are none (5.589829ms) +✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (7.479221ms) +✔ a bundle is written once: an existing file refuses (1.929475ms) +✔ a path with a single quote can't go into the hook command (2.078815ms) +✔ allow exits 0, a deny exits 2 with the reason on stderr (113.488384ms) +✔ a missing or wrong policy, or a bad event, exits 2 (84.717616ms) +✔ the bundle's wrapped command: a missing gate or node still blocks (1087.341531ms) +✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (673.321045ms) +✔ claude: the hook alone blocks a path outside the workspace (388.615805ms) +✔ claude: a second turn resumes the first turn's session (686.03657ms) +✔ claude adapter: --restricted is always passed (5.670175ms) +✔ claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable (17.920453ms) +✔ claude adapter: a directory pin exits 2 before it runs; a pin with a space runs (6.40466ms) +✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (744.520264ms) +✔ claude: a missing hook or MCP file refuses before claude starts (6.968544ms) +✔ pi: policy tools and typed tools pass, anything else is blocked (5.389108ms) +✔ claude: builtins map from pi names, typed tools need the mcp prefix (0.932975ms) +✔ file tool paths must resolve inside the workspace (1.195316ms) +✔ pi's own path normalisation can't be used to step out (0.999258ms) +✔ a symlink inside the workspace that points out is outside (0.829339ms) +✔ a dangling symlink is refused at any depth, in both harnesses (3.269635ms) +✔ read is checked under every spelling pi's read would open, in both harnesses (18.127132ms) +✔ other spellings cover directories, dangling links and pi's cwd (1.273265ms) +✔ a relative path climbs from the workspace's real path, in both harnesses (3.760412ms) +✔ claude path fields per tool (0.610974ms) +✔ glob patterns stay inside the workspace (0.552789ms) +✔ a path that can't be checked is blocked (0.325837ms) +✔ initialize, ping and tools/list (43.747222ms) +✔ tools/call goes through the tool socket; a refusal is an isError result (31.476039ms) +✔ replies are compact JSON; over the cap a tool error names the size and the cap (32.852297ms) +✔ unknown tools and methods are JSON-RPC errors and never reach the socket (34.129298ms) +✔ a missing argument is a usage error (27.732293ms) +✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (353.131066ms) +✔ pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks (308.788983ms) +✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (305.382298ms) +✔ pi: a read is refused when pi would open another spelling outside (301.230922ms) +✔ pi: a relative path climbs from the real path of a workspace behind a symlink (314.449881ms) +✔ pi: a relative path climbs from the real path of a dataRoot behind a symlink (346.980265ms) +✔ pi: a missing extension refuses before any model call (6.525094ms) +✔ pi: an extension without its configuration fails pi's start (276.51999ms) +✔ founderCheck: founder variables, then a needed service without a usable token (1.280784ms) +✔ turnRequest names the sender, class, reply and decision (0.244056ms) +✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (185.354606ms) +✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (52.112518ms) +✔ typed tools carry the runner's capability; launch goes to the host's launch socket (272.112041ms) +✔ a RESULT gets no automatic reply; failed turns reply with the reason (1190.019061ms) +✔ SIGTERM during a turn kills the turn's process group and still exits 0 (92.311259ms) +✔ founder credentials stop before the claim (20) (109.351973ms) +✔ a refused claim exits 21; an ended run's capability exits 22 (104.94204ms) +✔ the launch ending under a running session exits 22 (106.726367ms) +✔ a broker that stays unreachable exits 23 after brokerRetries polls (206.345615ms) +✔ a broker that is down at the claim exits 23, not 21 (77.395341ms) +✔ no capability, or a malformed one, on stdin exits 2 (156.686935ms) +✔ a missing or malformed policy exits 2 before the claim (76.456516ms) +✔ the PM gets launch, its task verbs and the reads (6.52669ms) +✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (3.787743ms) +✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (2.263487ms) +✔ a coder with a tracker gets its own task tools and no task_reassign (1.7602ms) +✔ launch only when the business's launch block names the instance as launcher (2.105506ms) +✔ an action outside the instance's authority has no tool (1.777666ms) +✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (6.419698ms) +✔ callTool: one JSON line out, the result back, a refusal rejects (7.660706ms) +✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (2.427098ms) +✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.812914ms) +✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.810969ms) +ℹ tests 69 +ℹ suites 0 +ℹ pass 69 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 9898.075075 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-ledger.log b/agents/researcher/work/queue-88/r1/out/pkg-ledger.log new file mode 100644 index 000000000..85a75ee1c --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-ledger.log @@ -0,0 +1,86 @@ +✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (121.434626ms) +✔ the raw path accepts nothing else, and refuses before curl runs (343.676551ms) +✔ the file checks still apply on the raw path: mode, symlink, missing, directory (261.25732ms) +✔ the raw path base URL has no override (54.649957ms) +✔ the JSON path is unchanged, and JSON never falls through to the raw path (227.971911ms) +✔ a file that changes between the two reads refuses before curl runs, with or without a body (657.477975ms) +✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (657.315769ms) +✔ real helper GET HTTP 200 preserves exit 0 without credentials (13.779522ms) +✔ real helper POST HTTP 201 preserves exit 0 without credentials (10.401007ms) +✔ real helper GET HTTP 403 preserves exit 1 without credentials (9.094238ms) +✔ fixture git subjects only, follow-ups and three session kinds (106.688358ms) +✔ text and JSON carry same numbers, open and truncated title (172.081486ms) +✔ missing credentials exit 2, no-issues never calls API and shows unknown (131.162295ms) +✔ empty range gives no rows and zero totals (89.4767ms) +✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (103.636997ms) +✔ close-only issue included, even median, missing metadata stays unknown (91.801495ms) +✔ unique commits but per-issue links count multiple tags once each (93.428832ms) +✔ page cap refuses rather than silently undercounting (82.144851ms) +✔ bad API payload not JSON refuses (84.718137ms) +✔ bad API payload {} refuses (80.301716ms) +✔ bad API payload [{"number":1}] refuses (83.183602ms) +✔ partial or malformed session log refuses with location, not content (90.143052ms) +✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (93.794418ms) +✔ no sessions is an empty table; symlink source refuses (154.432681ms) +✔ reads only refactor even when another branch is checked out (95.099328ms) +✔ invalid dates, reverse dates and duplicate options refuse (57.892673ms) +✔ T3 agent assignments do not count as human in Table 2 (92.697028ms) +✔ preamble parsing and issue number boundaries (0.476028ms) +✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.147157ms) +✔ no closed issues with human messages means undefined ratio, not invented zero (0.199112ms) +✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (188.165418ms) +✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (400.515258ms) +✔ T3: a HOME with no database exits 1 and names --no-t3 (89.756786ms) +✔ T3: a file that is not a database exits 1 and names --no-t3 (89.652981ms) +✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (111.652302ms) +✔ T3: an unmapped thread addressed as a seat exits 1 (111.024669ms) +✔ T3: a header to another thread id is not cross-checked (111.544532ms) +✔ T3: no project, or two, for this root exits 1 (289.160357ms) +✔ T3: a removed column exits 1 and names it (98.958989ms) +✔ T3: a missing table exits 1 and names it (97.667927ms) +✔ T3: a counted row with an unknown role exits 1 without its text (109.360939ms) +✔ T3: a counted row with non-text content exits 1 without its text (115.54212ms) +✔ T3: a counted row with an unparseable created_at exits 1 without its text (111.22727ms) +✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (208.041246ms) +✔ T3: a human message with no event counts in humanWithoutEvent (112.395721ms) +✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (205.253472ms) +✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (205.025064ms) +✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (58.374533ms) +✔ T3: a symlink at ~/.t3 exits 1 (80.135832ms) +✔ T3: a symlink at ~/.t3/userdata exits 1 (82.148231ms) +✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (84.205055ms) +✔ T3: with --t3-db, a symlinked file or directory exits 1 (164.146385ms) +✔ T3 WAL: the newest message only in -wal, writer attached, is counted (119.580189ms) +✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (115.154544ms) +✔ T3 WAL: -wal without -shm in a writable directory is read (141.585618ms) +✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (132.215651ms) +✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (110.120285ms) +✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5113.58233ms) +✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.856683ms) +✔ an issue several rows close is expected closed only once all of them are done (0.487954ms) +✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.326482ms) +✔ each owner of an in-progress or in-review row gets one liveness class (7.476799ms) +✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.372994ms) +✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.226236ms) +✔ the text section always ends in a count and a result, and never prints a full pass (0.332298ms) +✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (27.494984ms) +✔ issue states: open list first, then the metric page, then at most 10 lookups (260.09966ms) +✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (336.479272ms) +✔ the open list refuses on a failed call or a bad record, and never echoes the helper (159.075764ms) +✔ a helper call past the deadline is killed with its child, and the call reports it (2007.52333ms) +✔ readQueue loads queue.json through the queue validator and refuses anything else (86.394247ms) +✔ protected changes list every in-range entry that changes a required or parked row (233.404826ms) +✔ the CLI prints the queue section above the weekly table and under a queue key in --json (459.386916ms) +✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (181.661717ms) +✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (256.936473ms) +✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (231.359979ms) +✔ --unsupported-runtime repeats once per seat and takes a seat name (298.495188ms) +✔ an unreadable config makes every owner invalid instead of passing them (122.269379ms) +ℹ tests 78 +ℹ suites 0 +ℹ pass 78 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 10616.02426 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-mosaic.log b/agents/researcher/work/queue-88/r1/out/pkg-mosaic.log new file mode 100644 index 000000000..0f415a0bd --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-mosaic.log @@ -0,0 +1,77 @@ +✔ pure resolution selects current default or explicit enrolled account (2.30609ms) +✔ scope is explicit, bounded and never inferred (1.593157ms) +✔ fork pin is preserved against default change, override, missing account and revocation (0.72821ms) +✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.775815ms) +✔ only explicit synthetic credential forms and internal fixture stores admitted (5.362022ms) +✔ two concurrent workspaces of the same agent publish distinct complete private generations (38.460831ms) +✔ same execution ID is exclusively claimed and cannot overwrite a published generation (39.764711ms) +✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (47.928465ms) +✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (57.631307ms) +✔ credential lock contention refuses without duplicate side effects (11.426634ms) +✔ symlinked pre-existing final target is refused and never followed (26.538283ms) +✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.259842ms) +✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (30.624937ms) +✔ expired credentials refresh under transaction and subsequent generation reuses rotation (86.564764ms) +✔ refresh failure retains prior generation and store state (63.442889ms) +✔ refresh timeout retains prior generation and store state (160.971228ms) +✔ refresh malformed retains prior generation and store state (65.547601ms) +✔ concurrent refresh on same account refuses contention while unrelated account proceeds (222.401483ms) +✔ invalid refresh options refuse before burning claim (31.383683ms) +✔ fixed fake process rotates both OAuth fields without mutating caller input (28.371429ms) +✔ concurrent isolated processes preserve separate provider credentials (25.451797ms) +✔ fake failure is refused with fixed diagnostics (21.069855ms) +✔ fake malformed is refused with fixed diagnostics (22.203854ms) +✔ fake timeout is refused with fixed diagnostics (102.984842ms) +✔ fake unchanged is refused with fixed diagnostics (24.548492ms) +✔ caller executable/environment injection is rejected before spawning (0.282536ms) +✔ valid fixture tree validates and lists without secrets (66.434385ms) +✔ unknown-field refuses (0.374047ms) +✔ invalid-id refuses uppercase and traversal shapes (0.278411ms) +✔ plain-http baseUrl requires allowInsecureTransport (0.29086ms) +✔ native provider rejects allowInsecureTransport (0.128715ms) +✔ unsupported credential type and kind refuse (0.601511ms) +✔ account provider-path mismatch refuses (0.110577ms) +✔ profile account refs must be provider/account shaped (0.214494ms) +✔ seat selection accepts fork pin field, validates account refs (0.219564ms) +✔ harness manifest id must equal executable (gate 1) (0.197176ms) +✔ CLI validate: duplicate provider id across files refuses (27.171517ms) +✔ CLI validate: missing referenced provider/account refuse (29.6444ms) +✔ CLI validate: broken JSON refuses without secret echo (24.745772ms) +✔ CLI usage errors exit 2 (50.541376ms) +✔ credential.json sibling presence does not break validation and is never read (62.791547ms) +✔ D1 missing, empty and structurally empty roots refuse, no list projection (163.310179ms) +✔ D1 required directory auth cannot be absent (50.41157ms) +✔ D1 required directory auth/providers cannot be absent (58.785457ms) +✔ D1 required directory auth/accounts cannot be absent (59.36062ms) +✔ D1 required directory auth/settings cannot be absent (64.402751ms) +✔ D1 required directory harnesses cannot be absent (58.240707ms) +✔ D1 root file and unreadable metadata refuse (103.997649ms) +✔ D2 no symlink traversal at auth/providers/openai-codex.json (60.021887ms) +✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (60.826113ms) +✔ D2 no symlink traversal at auth/providers (50.192899ms) +✔ D2 no symlink traversal at auth (53.399355ms) +✔ D2 root and ancestor symlinks and lexical traversal refuse (151.771562ms) +✔ private filesystem modes enforced for root (49.77742ms) +✔ private filesystem modes enforced for auth (51.520908ms) +✔ private filesystem modes enforced for auth/providers/openai-codex.json (57.040562ms) +✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (61.075916ms) +✔ D3 numeric version 1 only across all record kinds (1.198763ms) +✔ D4 nested unknown keys and missing per-kind required fields refuse (60.800257ms) +✔ D5 unenrolled default refuses even when account exists (59.440944ms) +✔ D6 provider/account credential type must match (62.649188ms) +✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.464473ms) +✔ D8 URLs reject embedded credentials and unsupported protocols without echo (160.36776ms) +✔ D9 malformed JSON diagnostics contain no content excerpt (61.8233ms) +✔ D10 missing metadata is missing-path, not invalid-json (59.222225ms) +✔ D10 library returns no partial entries on any invalid record (71.400497ms) +✔ null/scalar/array metadata refuses without stack or echo (235.360766ms) +✔ credential sibling is never opened, even when an unreadable symlink (32.51144ms) +✔ oversized metadata refuses before parsing (51.775806ms) +ℹ tests 69 +ℹ suites 0 +ℹ pass 69 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2073.087958 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-queue.log b/agents/researcher/work/queue-88/r1/out/pkg-queue.log new file mode 100644 index 000000000..19d3386ea --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-queue.log @@ -0,0 +1,160 @@ +✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1051.044718ms) +✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (919.517721ms) +ℹ git commit -e: index.lock free during the editor +✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (838.946503ms) +ℹ git commit -e -- src.txt: index.lock held during the editor +✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (916.118727ms) +✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (878.848487ms) +✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (865.939237ms) +✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1100.675326ms) +✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (787.048038ms) +✔ F1: a shared-index change during the procedure is not committed (884.937774ms) +✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (869.87432ms) +✔ F1: a missing or a different hook refuses (594.399906ms) +✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1025.633438ms) +✔ F1: the canary refuses a hook that git would not run (521.632761ms) +✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (798.918492ms) +✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (737.889547ms) +✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (769.599856ms) +✔ bootstrap: the archived tests and validator run outside any repository (738.769294ms) +✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (743.761577ms) +✔ general: a queue write after the snapshot is not committed (1065.317359ms) +✔ general: a snapshot whose log does not extend the base refuses (811.713629ms) +✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (149.733952ms) +✔ general: environment overrides, a linked worktree and usage (488.088262ms) +✔ general: a queue path staged before the run refuses at step 1 (547.52849ms) +✔ general: HEAD's queue tests failing in the archive refuse (720.817954ms) +✔ genesis document serializes deterministically and replays (4.688707ms) +✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.625426ms) +✔ a tampered result, receipt or viewSha fails replay (2.232126ms) +✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.192122ms) +✔ add: defaults for an ordinary seat, privileged extras, refusals (4.082937ms) +✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (5.658867ms) +✔ matrix: release, review round, changes requested and waiting-on-jason (15.933127ms) +✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (8.689942ms) +✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (16.595729ms) +✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.225026ms) +✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1284.013173ms) +✔ matrix: blocked keeps the claim and returns only to previousState (4.357848ms) +✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.650407ms) +✔ field edits: who may change what (5.185969ms) +✔ set issues keeps a logged narrowing of closes (N10) (3.550958ms) +✔ text the table shows refuses \ and <, everywhere it enters (N8) (2.862355ms) +✔ every accepted text renders to nine cells on every row (N8) (24.043092ms) +✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.515362ms) +✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.543681ms) +✔ replay holds every op id to the caller's rule (N11) (8.080029ms) +✔ note: owner, listed reviewer or privileged; empty clears (1.919379ms) +✔ assign moves the claim with the owner; done clears it (4.251058ms) +✔ render is byte-stable and escapes pipes (0.782922ms) +✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (1.038504ms) +✔ next: resume, then review, then start, then wait, then nothing; lowest id first (30.070547ms) +✔ canonical args make a retry's identity independent of list order (0.406992ms) +✔ manifests, headings and blob ids (3.330964ms) +✔ the migration map: one queue-map block, exact keys (2.348083ms) +✔ every call but `queue` reaches the seat CLI exactly as before A2 (493.664207ms) +✔ no argument reaches the app entry in packages/cli, and only that call (62.044821ms) +✔ `queue` reaches the queue CLI with the rest of the arguments (184.665589ms) +✔ the pre-A2 fixture is the script A2 changed (0.308872ms) +✔ acquire publishes the record by link; release removes only its own lock (5.704763ms) +✔ a kill between the temp write and the link leaves no lock (44.138858ms) +✔ a short or failed temp write refuses and leaves no lock and no temp (1.966046ms) +✔ a link error other than EEXIST refuses (1.34294ms) +✔ an error after the link releases the lock: unreadable gate, failing temp stat (3.021717ms) +✔ a release that fails on a gate path is reported, never a stack trace (P1) (4.058148ms) +✔ a paused holder: another writer waits 10 s, then refuses naming it live (10062.983513ms) +✔ two concurrent unlockers: the second refuses on the gate (21.667018ms) +✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (2.050518ms) +✔ a writer publishing during an unlock, gate first: the writer releases and refuses (1.536489ms) +✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (1.767661ms) +✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (3.177798ms) +✔ the same pid and start on a different boot is mismatch (1.020939ms) +✔ a foreign host is unknown whatever the local pid says; unlock refuses (42.26508ms) +✔ unreadable /proc: classification is unknown and acquire refuses (0.612953ms) +✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.191046ms) +✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (3.292093ms) +✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (2.010496ms) +✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (0.830631ms) +✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (2.98141ms) +✔ the migration map validates and renders the golden genesis table (3.744919ms) +✔ the marked QUEUE.md holds every row and parked item between its markers (1.648865ms) +✔ map-check reports each kind of drift (4.441518ms) +✔ a request posts once as the requester; a retry sends nothing (530.798232ms) +✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (2898.76889ms) +✔ the pre-send checks: GET user must name the requester, under the deadline (919.527171ms) +✔ the lead's request refuses a token for login sage (468.543496ms) +✔ the credential file: the seat's own, 0600, no symlink, never the shared default (392.758934ms) +✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (1283.959247ms) +✔ a same-op retry after a kill sends nothing, even with a stale view (1975.582299ms) +✔ a held lock at the outcome exits 3 and names what the transport said (507.155205ms) +✔ late outcomes: after an abandon, and after a resolve with the same or another id (1298.430598ms) +✔ resolve checks the comment: issue, markers, round, candidate and author (1365.090084ms) +✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (459.16631ms) +✔ validateRow checks a request round's shape, which every replayed entry must keep (300.753457ms) +✔ request, changes, a new candidate, approval: every round pinned; no review files (1214.47722ms) +✔ a row with no reviewers opens a round that posts nothing (736.738306ms) +✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1010.448849ms) +✔ semantics: v1 entries replay as before; review entries need v2 (318.543696ms) +✔ set reviewers refuses the row's owner (2026-09-28) (232.959263ms) +✔ the owner records no verdict, even as a listed reviewer (305.65676ms) +✔ a request comment over the length limit is not sent (329.271944ms) +✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (344.690051ms) +✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (1602.86339ms) +✔ genesis: refusals before anything is written (388.033731ms) +✔ genesis: the map must be committed, well formed, with committed briefs and seats (534.159046ms) +✔ genesis: markers, a stray witness, once only; a retry returns the receipt (449.183742ms) +✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (317.741869ms) +✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (556.580766ms) +✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (463.971847ms) +✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (558.43461ms) +✔ a retried add returns the id it first allocated, after reassignment and after done (551.188786ms) +✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (386.355717ms) +✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (738.667232ms) +✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (528.949631ms) +✔ add, set reviewers and assign refuse the row's owner as a reviewer (333.254504ms) +✔ the working-brief check: a changed working copy refuses the start and flags next (466.584352ms) +✔ next: resume first, then nothing for an idle seat; needs a seat (244.521339ms) +✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (493.648822ms) +✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (895.35057ms) +✔ a hand edit to queue.json refuses every verb, reads included (471.294115ms) +✔ verify and render --check leave bytes and mtimes unchanged (346.511291ms) +✔ render is byte-stable across runs and repositories (203.335736ms) +✔ snapshot and verify --snapshot (607.369304ms) +✔ usage errors exit 4 (469.959101ms) +✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (157.798311ms) +✔ a rename failure: nothing visible, temp removed (121.481381ms) +✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (142.765158ms) +✔ a directory fsync failure, then sync names the op (279.239744ms) +✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (124.183393ms) +✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (119.084471ms) +✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (120.502016ms) +✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (101.892151ms) +✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (108.891074ms) +✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (139.75551ms) +✔ unlock prints a swapped gate's warning on stderr, the result on stdout (136.133294ms) +✔ a view write that fails keeps the op and reports a stale view (100.094259ms) +✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (541.966258ms) +✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (582.31838ms) +✔ SIGKILL after the witness, before the view: the stale refusal names the op (539.342635ms) +✔ SIGKILL after the view, before the receipt: the retry returns the receipt (541.762785ms) +✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (114.655115ms) +✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (726.96557ms) +✔ a deleted witness: refused after the locked recheck; accept-history records it absent (172.333567ms) +✔ a header edit during a write: the op stands, the view write is skipped with a warning (108.447554ms) +✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (107.590224ms) +✔ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (140.473592ms) +✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (112.640739ms) +✔ a writer paused before and after the witness rename: readers see a tail, then a match (115.737285ms) +✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (454.459536ms) +✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (424.232587ms) +✔ the platform check refuses other filesystems (97.692151ms) +✔ tmpfs passes only a test layer that allows it (N5) (120.528324ms) +✔ unlock keeps a multi-line lock record on stdout (P3) (141.032726ms) +ℹ tests 150 +ℹ suites 0 +ℹ pass 150 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 18891.904228 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-runs.log b/agents/researcher/work/queue-88/r1/out/pkg-runs.log new file mode 100644 index 000000000..3e8aa3f0e --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-runs.log @@ -0,0 +1,51 @@ +✔ isRunId accepts the run id shape and nothing else (0.826811ms) +✔ a missing data root or runs directory lists nothing (1.284854ms) +✔ runs as a regular file lists nothing, as before (0.490778ms) +✔ listRunIds keeps r- names only, sorted oldest first (0.633818ms) +✔ listRunRecords returns each result, or null for an incomplete or unreadable one (1.748579ms) +✔ readRunRecord returns documents and artifacts in directory order (0.843954ms) +✔ readRunRecord is null for a missing run, a dangling link or a file (0.576044ms) +✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.574708ms) +✔ readRunDocument reads only the three run documents (0.459296ms) +✔ a link inside the data root is followed (0.79002ms) +✔ a data root that is itself a link is trusted as configured (0.463484ms) +✔ a run directory linked out of the data root is never read (0.530378ms) +✔ a document linked out of the data root reads as null (0.789164ms) +✔ a runs directory linked out of the data root refuses (0.441758ms) +✔ a relative link that climbs out of the data root refuses (0.373507ms) +✔ a sibling whose name starts with the data root's name is outside it (0.440799ms) +✔ a link loop refuses instead of reading as missing (0.519175ms) +✔ an unreadable runs directory refuses instead of listing nothing (0.352333ms) +✔ an unreadable run directory refuses instead of reading as missing (0.408697ms) +✔ a link to the data root's parent is outside it (0.325624ms) +✔ listRunIds sorts whatever order the directory returns (0.633446ms) +✔ listRunRecords lists an r- name that isn't a valid run id, as before (0.40032ms) +✔ the readers write nothing (1.67201ms) +✔ no pointer is null (1.510808ms) +✔ the pointer release.sh writes reads back (1.051444ms) +✔ a pointer that isn't the version 1 shape refuses with exit 2 (1.573956ms) +✔ a pointer that is a directory refuses with exit 4 (0.496659ms) +✔ a state file linked out of the data root refuses (0.77086ms) +✔ a state directory linked out of the data root refuses (0.460162ms) +✔ a state directory linked out to nothing reads as no release and an empty log (0.386912ms) +✔ a missing log is empty (0.320981ms) +✔ the log reads oldest first and counts malformed lines (0.783719ms) +✔ last must be a positive integer (0.396642ms) +✔ the state readers write nothing (0.687449ms) +✔ list prints each run in the established format (67.838532ms) +✔ list prints a damaged result.json as unknown and keeps listing the runs after it (67.519872ms) +✔ list prints each result field that isn't a string as unknown or - (59.356119ms) +✔ show prints the run in the established format (59.536661ms) +✔ show of a missing run and an invalid id exit 4 as before (93.282831ms) +✔ list and show refuse a runs directory linked out of the data root (118.446923ms) +✔ show refuses a run linked out of the data root; list reports it unknown (112.81785ms) +✔ list shows an r- name that isn't a valid run id, as before (62.621282ms) +✔ show refuses an invalid id before reading the config (36.089462ms) +ℹ tests 43 +ℹ suites 0 +ℹ pass 43 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 743.688077 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-seat.log b/agents/researcher/work/queue-88/r1/out/pkg-seat.log new file mode 100644 index 000000000..d54d74777 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-seat.log @@ -0,0 +1,116 @@ +✔ a first launch clones the root at its branch, with the root's path as origin (31.66046ms) +✔ the clone is not at its place until git clone has finished (27.623778ms) +✔ a later launch fast-forwards a clean clone after a commit to the root, then finds it current (75.515234ms) +✔ a clone ahead of the root keeps its commits and counts as current (37.53501ms) +▶ a dirty clone, one on another branch and one diverged are kept as they are + ✔ a modified file (30.900722ms) + ✔ an untracked file (29.725298ms) + ✔ a staged file (29.865396ms) + ✔ another branch (31.68363ms) + ✔ a detached HEAD (28.540923ms) + ✔ a diverged branch (48.802378ms) +✔ a dirty clone, one on another branch and one diverged are kept as they are (200.738422ms) +▶ a clone the session rigged is kept, and git runs nothing and writes nothing outside it + ✔ core.fsmonitor (32.416225ms) + ✔ core.hooksPath (37.260509ms) + ✔ a hook file (32.254131ms) + ✔ include.path (34.441047ms) + ✔ includeIf (32.634406ms) + ✔ a filter driver (33.508333ms) + ✔ origin moved (31.601988ms) + ✔ a second origin url (36.927744ms) + ✔ the origin url twice, both the root (33.842246ms) + ✔ an ext:: origin (33.422391ms) + ✔ a branch tracking another remote (36.466449ms) + ✔ a branch merging a tag (31.655342ms) + ✔ a fetch line onto local branches (30.804489ms) + ✔ no origin (31.169973ms) + ✔ a config file over the size cap (29.147241ms) + ✔ config a symlink to /dev/zero (29.11116ms) + ✔ the clone a symlink to the bait (28.009067ms) + ✔ the clone a file (27.855838ms) + ✔ the clone a plain directory (28.705619ms) + ✔ .git a gitdir: file (28.71463ms) + ✔ .git a symlink (28.805482ms) + ✔ .git/logs a symlink (28.392731ms) + ✔ .git/refs/heads a symlink (28.452701ms) + ✔ .git/objects a symlink (28.367463ms) + ✔ a reflog hard-linked to a file outside (28.986755ms) + ✔ HEAD a fifo (30.101193ms) + ✔ a commondir file (29.113302ms) + ✔ an alternates file (29.81317ms) + ✔ a gitlink staged, no .gitmodules (41.432562ms) + ✔ a gitlink and .gitmodules staged (42.527256ms) + ✔ a gitlink to a repository with its own gitlink (50.520844ms) + ✔ a gitlink and .gitmodules committed (45.376059ms) + ✔ a gitlink and submodule.active in config (42.491865ms) + ✔ a .git/modules with no gitlink (30.94199ms) +✔ a clone the session rigged is kept, and git runs nothing and writes nothing outside it (1129.970675ms) +▶ a root that can't be cloned refuses with the project id and leaves nothing behind + ✔ missing (10.367291ms) + ✔ not a repository (10.002159ms) + ✔ a plain directory inside another repository (14.499364ms) + ✔ unreadable (9.633611ms) + ✔ on no branch (11.480861ms) + ✔ missing an object (14.718537ms) +✔ a root that can't be cloned refuses with the project id and leaves nothing behind (71.547165ms) +✔ a clone whose root is on no branch, or gone, is kept and named (25.941837ms) +✔ no credential or forge remote of the root reaches a clone (49.151692ms) +✔ every project in the business file gets its own clone at its own branch (29.516105ms) +✔ RUNNER_FILES is exactly what the runner and the harness sources it starts import (1.563251ms) +✔ sandboxProblem names a missing bwrap and a bwrap that fails (8.379306ms) +✔ every bind says why; the bundle and session file stay read-only over the run directory (1.383664ms) +✔ the host's git socket is bound only when given, read-only and before the writable binds (0.801071ms) +✔ a confined session's shell reaches its own workspace and run directory and nothing of the founder's or another session's (1726.26608ms) +✔ a bind that exposes a stand-in, or a missing workspace bind, fails the matching check (2524.006123ms) +✔ resolveSeat: by name under --repo resolves the repo layout (1.464935ms) +✔ resolveSeat: by path resolves the fleet layout (0.34873ms) +✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.716539ms) +✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.671341ms) +✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.786777ms) +✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.312809ms) +✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.338399ms) +✔ CLI launch: registers, execs the fake launch script, and passes args through (31.808206ms) +✔ CLI launch: --harness lands in the record (35.606716ms) +✔ CLI launch: the launch script's own exit code passes through (32.510903ms) +✔ CLI launch: relaunching a seat rewrites the one registration record (68.898272ms) +✔ CLI launch: omitting --task records an empty string, not null (27.653772ms) +✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (81.773598ms) +✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (141.064069ms) +✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.479064ms) +✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.675273ms) +✔ validateRegistration/readRegistration: taskSetBy is optional; a record without it (written before #1511) still loads unchanged; an invalid one is refused; the version does not change (0.855051ms) +✔ updateTask: records setBy, preserves startedAt and every unrelated field, upgrades an old record in place only when the task is set, and replaces a previous attribution (0.900465ms) +✔ CLI seat task: --by beats MOSAIC_AGENT_NAME, the environment beats nothing, empty environment is unknown, invalid --by or environment refuses with exit 4 and leaves the record byte for byte (276.895673ms) +✔ family: exactly one launch.max key in the model name, else null (0.666554ms) +✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.663014ms) +✔ sessionExit gives a runner's signal death back as the signal; a session credential is one whole set of nonempty variables (0.243061ms) +✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.535341ms) +✔ session file and launch log: 0600, the session file written once (1.098761ms) +✔ endReason maps the runner's exit codes; a signal is killed (0.11825ms) +✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.285865ms) +✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (1.024315ms) +✔ a session runs in its sandbox as pid 1 of its namespace, claims, answers, and stops on mosaic stop (244.623274ms) +✔ workRef and validBranch take only what a ref name can hold (1.610377ms) +✔ a push lands as the task's work ref, a fast-forward lands after it, and main is the root's (119.305096ms) +✔ a rewritten branch refuses non-fast-forward and the work ref stays (89.36511ms) +✔ a branch with no history in common with main refuses unrelated-history (44.022575ms) +✔ a bundle that needs commits the working repository lacks refuses missing-prerequisite (41.564705ms) +✔ main is fetched from the root again before each push, so a clone of a newer root pushes (101.069946ms) +✔ a root moved back after the clone leaves the push's base unknown: missing-prerequisite (69.450387ms) +✔ a commit with a broken object refuses invalid-bundle, and only because fsck is on (61.973406ms) +✔ only a v2 bundle with the named branch is read (47.48597ms) +✔ a branch named main, or named for another task, moves only this task's work ref (78.160174ms) +✔ a record that throws moves the work ref back, or removes a new one (107.236547ms) +✔ a working repository path that isn't a directory refuses (22.632492ms) +✔ review bundles a pushed commit as refs/mosaic/review, leaving out what the reviewer has (94.559739ms) +✔ review of a project with no working repository refuses unknown-commit (12.690624ms) +✔ the runner's git tools hold the host's limits and branch rule (1.142196ms) +ℹ tests 105 +ℹ suites 0 +ℹ pass 105 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 4334.513995 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-tasks.log b/agents/researcher/work/queue-88/r1/out/pkg-tasks.log new file mode 100644 index 000000000..98f30727c --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-tasks.log @@ -0,0 +1,100 @@ +✔ the boot config is checked before anything starts (17.557961ms) +✔ a business with no tracker entry refuses task verbs (18.558118ms) +✔ credential.expiring and .expired are recorded once per instance (32.730232ms) +✔ a token file that changes on disk records credential.changed (14.539906ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (35.923799ms) +✔ a refusal a restart must clear is not retried by the poll (10.561413ms) +✔ a poll that fires while two are queued is dropped (12.249779ms) +✔ close waits for a running verb and refuses one that has not started (43.97927ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (0.804906ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.263759ms) +✔ the committed table loads frozen; its verbs are the broker task verbs (1.620113ms) +✔ a write names a verb the table has as its own key, not one it inherits (0.559549ms) +✔ the schema refuses any other shape (4.371732ms) +✔ the Field table in the slice 1 plan names the verbs each field is written by (1.009628ms) +✔ each verb refuses, asks for a decision and writes as the table and the role files say (179.749364ms) +✔ the list holds the PRD's requirement ids, in its order, at its version (5.836841ms) +✔ the committed list loads frozen (0.78609ms) +✔ the schema refuses any other shape (2.098109ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (100.263194ms) +✔ the project list holds recorded project objects in the recorded list envelope (10.280286ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.469372ms) +✔ the client works against the fake over real HTTP with the platform fetch (100.070121ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (31.081768ms) +✔ verbs refuse while a business is starting and after startup refused it (16.878713ms) +✔ startup refuses a token that can do more than its role needs (42.459328ms) +✔ startup refuses an unsupported version and flags an untested one (31.98518ms) +✔ startup refuses a board that the runbook did not install (38.505386ms) +✔ startup refuses a project the sync bot cannot read (6.964101ms) +✔ startup refuses a configured label the pm bot cannot see (9.556081ms) +✔ startup refuses an expired credential and a missing sync credential (16.713312ms) +✔ an unreachable tracker refuses with tracker-unavailable (7.574252ms) +✔ an edit in the UI is recorded once, with the fields that changed (83.631765ms) +✔ a move between open buckets is seen on the board, though updated does not change (39.736657ms) +✔ a person's comment is counted and a bot's is not (70.526721ms) +✔ the hourly reconcile catches a comment through comment_count (76.846716ms) +✔ a task closed in the UI leaves the open view with its done bucket (111.761615ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (61.908445ms) +✔ a poll that read before a verb wrote does not overwrite the verb (34.248389ms) +✔ a tracker fault during a tick is reported and the next tick catches up (35.271493ms) +✔ a malformed answer refuses the tick with tracker-shape (11.282688ms) +✔ no token value reaches the database, the log or a refusal (41.534728ms) +✔ the first look at a task counts only comments inside the window (34.395449ms) +✔ with descendants off the adapter reads no project list and no child, as in v0 (48.392361ms) +✔ a three-level tree is read per project, and a project outside it is not (20.613195ms) +✔ a watch-only child keeps the business ready, and a failed child read refuses as transient (25.851529ms) +✔ a root without the installed buckets refuses with descendants on (10.058089ms) +✔ the project list refuses without read_all, and skips archived projects and saved filters (22.725817ms) +✔ descendants must be true or false (10.72415ms) +✔ a move inside the tree is a moved tombstone plus a snapshot under the new ref (60.571151ms) +✔ a child made after startup joins the tree at the next reconcile (52.626866ms) +✔ a child that leaves the tree has its tasks recorded missing, and the ledger keeps their history (58.607537ms) +✔ a child deleted or hidden between reconciles drops out at the next tick, and the root still refuses (80.921416ms) +✔ a reconcile that fails keeps the previous tree (31.869442ms) +✔ a closed task the cursor missed takes its own project's done bucket (32.469038ms) +✔ closed tasks read at startup take their own project's done bucket (8.983374ms) +✔ a closed task the cursor returns takes its own project's done bucket (32.923735ms) +✔ a task moved out of a child that leaves the tree at the same time is moved, not no-access (32.449611ms) +✔ a task in a bucket outside the five keeps its raw bucket id (30.154677ms) +✔ bucket 0 on a watch-only task never becomes a bucket or a state (78.166609ms) +✔ a child whose kanban view is deleted still refuses the tick, and the reconcile reads it watch-only (55.446959ms) +✔ a child dropped at a tick comes back at the next reconcile once the bot reads it again (35.142231ms) +✔ task.create needs a recorded human request and a requirement id (42.861924ms) +✔ task.create takes only an approved requirement id (23.437279ms) +✔ task.create cites the request a received message carries, not the message's id (19.917402ms) +✔ only labels named in the business file can be written (27.640909ms) +✔ task.schedule sets and clears a due date and relations (34.452547ms) +✔ assign and reassign move the role bots and record task.assigned (54.566881ms) +✔ task.update.assigned is for the assignee and records task.state (45.022992ms) +✔ a wrong expected digest records task.conflict and writes nothing (18.762761ms) +✔ a cross-role verb needs a resolved decision, used once (20.744615ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (19.084736ms) +✔ a task with pushed work closes only citing an approve verdict for its latest push, and a refusal writes nothing (28.151446ms) +✔ a lost answer is settled by a re-read and never retried (21.461222ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (35.406612ms) +✔ a task the sync bot cannot read refuses and records nothing (13.548348ms) +✔ verbs and polls for one business run one at a time (83.752363ms) +✔ a due date with milliseconds is written to the second (62.981887ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (37.616807ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (12.553519ms) +✔ a create whose final read fails succeeds and records task.created (32.574806ms) +✔ an edit between the last write and the final read shows as external on the next poll (35.201337ms) +✔ task.created is recorded when a later label write fails (10.930808ms) +✔ every verb acts in an installed child, on the child's own board (42.314108ms) +✔ task.create refuses a project outside the tree, a watch-only one and one that is not an id (23.076421ms) +✔ with descendants off, task.create takes only the root project (11.179489ms) +✔ a verb on a watch-only task, or a relation to one, refuses with project-not-installed (16.1111ms) +✔ a related task is read before authority: in another project than its ref names, task-moved; missing, task-not-found (21.614773ms) +✔ a task moved to another project of the tree refuses its old ref with task-moved and takes its new one (13.722096ms) +✔ a verb and a create in the child record the final read of the child's board (13.088189ms) +✔ a create in the child whose final read fails records the child's todo bucket (32.961111ms) +✔ a lost answer in the child is settled by a re-read of the child's board (20.901166ms) +✔ every related task is read before authority, not only the first (14.508934ms) +ℹ tests 92 +ℹ suites 0 +ℹ pass 92 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1000.744296 diff --git a/agents/researcher/work/queue-88/r1/out/pkg-webui.log b/agents/researcher/work/queue-88/r1/out/pkg-webui.log new file mode 100644 index 000000000..88d15e678 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/pkg-webui.log @@ -0,0 +1,117 @@ +✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (6146.684455ms) +✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (5631.213358ms) +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4087.894896ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4621.768642ms) +✔ the README says a refused field table names the file or the key at fault, never a path (1.330117ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7157.492731ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (113.406774ms) +✔ decisions route serves the roles and recent lists from the reader view (81.874173ms) +✔ credentials route serves the broker credential status from the reader view (40.341184ms) +✔ replies route serves the replies to one message from the reader view (130.92982ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (185.704534ms) +✔ bus refusals map to statuses and never carry a path or a stack (153.83314ms) +✔ humanCall speaks the human transport protocol without blocking the server (2234.016672ms) +✔ busReader takes --business, else the live bus host, else refuses (476.61738ms) +✔ humanCall kills a transport that runs past its timeout (549.570783ms) +✔ serve refuses a --business value that is not a business id (651.180169ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3804.969868ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2442.04986ms) +✔ conversation view: a newer session with no readable history keeps the marker (1548.991318ms) +✔ conversation view: seats without history say so and offer no reply (810.808511ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3554.912897ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54231.765865ms) +✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (154.05454ms) +✔ Console Messages lists late unsolicited messages on polling with sender class time read state and trail; nav count includes beyond the page; reads never deliver (3689.976425ms) +✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (3211.9977ms) +✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (1458.593432ms) +✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (1819.043261ms) +✔ copy writes nothing when an archived path is missing at the commit (483.012352ms) +✔ a copy that loses the rename race leaves no read-only temp directory behind (514.120598ms) +✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (722.398343ms) +✔ a copy that meets a target holding files says exists: and moves nothing into it (567.902152ms) +✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (427.691005ms) +✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (1084.875677ms) +✔ serve refuses a --checkout that is not an absolute directory path (360.296494ms) +✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (56.115602ms) +✔ preview options are all three or none, and each is checked (15.86576ms) +✔ the CLI refuses a partial or invalid preview flag set at start (2174.538329ms) +✔ the CLI with all three flags serves the preview mode (583.957767ms) +✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (56.975478ms) +✔ the header name matches without regard to case, and the value compare is exact (12.222222ms) +✔ the public name with the header reads the bus views, and the Host and Origin are checked first (133.450218ms) +✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (20.769714ms) +✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (8.890851ms) +✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (34.565834ms) +✔ a repeated Host is 400 before any other check, in preview mode and without it (14.194498ms) +✔ without the preview flags the public name is refused and writes still reach the board (11.053335ms) +✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.15017ms) +✔ credential state from the date alone, as the bus states it (0.276397ms) +✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1633.515133ms) +✔ queue: a refused read fails closed with the store's text, redacted (1181.531444ms) +✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1661.360651ms) +✔ business: names, vars on the allowlist, authority, credential metadata only (17.202823ms) +✔ business: missing or invalid file refuses with the module's text, redacted (22.940064ms) +✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (23.042167ms) +✔ settings: release, board, loopback address, notifier binding name only (497.730319ms) +✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (124.031017ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3572.865578ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23170.632208ms) +✔ the send box: held roles, the draft and caret across a refresh, each receipt, one POST per Send, and the trail link; the board Reply on a 4xx and a 5xx (5998.787001ms) +✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (11673.240554ms) +✔ startServer takes send only as a function and only with the preview mode (9.288048ms) +✔ the CLI refuses --send without the preview flags, and without a configured bus (912.642934ms) +✔ the CLI with --send and a configured bus sends through the human transport and logs one line (478.853279ms) +✔ without --send the route is read-only like every other write, and config says no send (14.474791ms) +✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (11.622496ms) +✔ config says send only on the public name with --send on (3.501794ms) +✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (17.577656ms) +✔ each request check refuses before anything is sent, and logs the refusal without the body (40.59855ms) +✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (12.828038ms) +✔ a crafted role name cannot forge a log line (5.421284ms) +✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (18.601737ms) +✔ through the human transport the request is exactly the business, message.send and the keys (279.537324ms) +✔ against a real broker: the message is written once as a human send, and its refusals map (92.187084ms) +✔ loopback host and board origin fail closed (12.760139ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (149.682941ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (104.555962ms) +✔ unreachable board reports URL; CLI rejects unsupported options (1017.946829ms) +✔ render prints the unit without the preview flags (33.217362ms) +✔ render carries every serve flag, the preview set included (16.887935ms) +✔ a partial preview set is refused (51.309174ms) +✔ a value the unit cannot carry is refused, for every flag (1410.158981ms) +✔ the rendered unit names none of the human proof agent markers and sets no agent variable (87.594001ms) +✔ install into --dir with --no-reload writes the file whole, then reports unchanged (181.506333ms) +✔ install without --no-reload runs daemon-reload and nothing that starts the unit (82.318303ms) +✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (342.334883ms) +✔ status prints the unit state and GET /healthz on the installed port (308.697793ms) +✔ usage: an unknown verb or flag, and start or enable, exit 4 (151.554687ms) +✔ without --send, render is the template with its placeholders filled, byte for byte (165.02068ms) +✔ render --send with the preview set ends ExecStart with --send and changes nothing else (187.551817ms) +✔ --send without the whole preview set is refused and writes nothing (513.834595ms) +✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (241.365825ms) +✔ uninstall and status take no --send (29.356891ms) +✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (207.604615ms) +✔ when the state read fails, install and uninstall refuse with one line and change nothing (54.155687ms) +✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (55.505508ms) +✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (341.055337ms) +✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (52.927524ms) +✔ the uid header takes only an Authentik uid; the username header still takes a username (336.659794ms) +✔ a preview value cannot start with - or . (28.382655ms) +✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (353.698966ms) +✔ tokens.css and icons.svg are byte copies of docs/design (1.722391ms) +✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.006026ms) +✔ shipped strings are unchanged, and the web keeps its write boundary (4.798026ms) +✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6806.075236ms) +✔ five states on Board, Inbox, Tasks, Agents and Trail (2246.892529ms) +✔ Queue, Business and Settings in their five states, with no move button (4109.162795ms) +✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1129.662258ms) +✔ the server log names no config path when the system config refuses (212.829772ms) +ℹ tests 108 +ℹ suites 0 +ℹ pass 108 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 54680.200501 diff --git a/agents/researcher/work/queue-88/r1/out/script-auth.log b/agents/researcher/work/queue-88/r1/out/script-auth.log new file mode 100644 index 000000000..ac2287606 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-auth.log @@ -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 diff --git a/agents/researcher/work/queue-88/r1/out/script-conductor.log b/agents/researcher/work/queue-88/r1/out/script-conductor.log new file mode 100644 index 000000000..62afc513c --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-conductor.log @@ -0,0 +1,23 @@ +On branch refactor +Your branch is up to date with 'origin/refactor'. + +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 diff --git a/agents/researcher/work/queue-88/r1/out/script-config.log b/agents/researcher/work/queue-88/r1/out/script-config.log new file mode 100644 index 000000000..76c05ef74 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-config.log @@ -0,0 +1,26 @@ +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 failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/researcher/work/queue-88/r1/out/script-discord.log b/agents/researcher/work/queue-88/r1/out/script-discord.log new file mode 100644 index 000000000..b013f9ffa --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-discord.log @@ -0,0 +1,70 @@ +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/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/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/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 178) +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 + +discord suite: 66 passed, 0 failed diff --git a/agents/researcher/work/queue-88/r1/out/script-extension-package.log b/agents/researcher/work/queue-88/r1/out/script-extension-package.log new file mode 100644 index 000000000..c1eab2ed2 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-extension-package.log @@ -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 +scripts/test-extension-package.sh: line 14: 2452658 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 diff --git a/agents/researcher/work/queue-88/r1/out/script-foundation.log b/agents/researcher/work/queue-88/r1/out/script-foundation.log new file mode 100644 index 000000000..6338e1e6d --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-foundation.log @@ -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 diff --git a/agents/researcher/work/queue-88/r1/out/script-queue.log b/agents/researcher/work/queue-88/r1/out/script-queue.log new file mode 100644 index 000000000..1bf5cdf07 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-queue.log @@ -0,0 +1,35 @@ +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/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 150 +ℹ pass 150 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/mnt/storage/src/mosaic-stack/.claude/worktrees/agent-aae703ffcabbad4b7) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) + +queue suite: 27 passed, 0 failed diff --git a/agents/researcher/work/queue-88/r1/out/script-release.log b/agents/researcher/work/queue-88/r1/out/script-release.log new file mode 100644 index 000000000..1d6ed0503 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-release.log @@ -0,0 +1,7 @@ +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 +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/researcher/work/queue-88/r1/out/script-task.log b/agents/researcher/work/queue-88/r1/out/script-task.log new file mode 100644 index 000000000..cd89fe9b2 --- /dev/null +++ b/agents/researcher/work/queue-88/r1/out/script-task.log @@ -0,0 +1,32 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +skip live recall pair: user recall run, recalled user name (docker daemon unavailable) +OK no agent identity on headless run + +selftest: 26 passed, 0 failed diff --git a/agents/researcher/work/queue-88/r1/phase-b.sh b/agents/researcher/work/queue-88/r1/phase-b.sh new file mode 100644 index 000000000..df6730a0e --- /dev/null +++ b/agents/researcher/work/queue-88/r1/phase-b.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Row 88 phase B: every packages/*/tests suite, then every scripts/test-*.sh, in order. +# Stops at the first nonzero exit (set -e). Each step's output goes to r1/out/. +# Usage: bash phase-b.sh +set -e +W="$1" +OUT="$W/agents/researcher/work/queue-88/r1/out" +export DOCKER_HOST=unix:///nonexistent.sock +mkdir -p "$OUT" + +# Packages whose package.json script globs tests/*.test.mjs; the rest use tests/ as given. +GLOB_PKGS=" bus cli harness tasks " +for p in bus business cli control-board conversation discord harness ledger mosaic queue runs seat tasks webui; do + cd "$W/packages/$p" + if [[ "$GLOB_PKGS" == *" $p "* ]]; then + node --test tests/*.test.mjs > "$OUT/pkg-$p.log" 2>&1 + else + node --test tests/ > "$OUT/pkg-$p.log" 2>&1 + fi + echo "pkg-$p exit=0 $(grep -E '^# (tests|pass|fail) ' "$OUT/pkg-$p.log" | tr '\n' ' ')" +done + +cd "$W" +for s in auth conductor config discord extension-package foundation queue release task; do + scripts/test-"$s".sh > "$OUT/script-$s.log" 2>&1 + echo "script-$s exit=0" +done +echo "PHASE_B_SUITES_PASSED"