agents: Dewey row 151 round 1 packet, launch-notice routing and talk waiting/reply tests (#1645)

Candidate on 686fc1cb6: candidate-manifest.sha256 cb831847...78ed, 2 test files, +44.
Mutants 4/4 killed; owner's gate rc 0 (bus 265, cli 213, every scripts/test-*.sh).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jason.woltjeandClaude Opus 5.5 committed 2026-10-15 19:18:15 -05:00
1 parent dfe7df1cac
commit 20c0ee1c6f
26 files changed
+1505

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
# Row 151 (#1645): tests pin where a launch notice is routed and how talk prints it with a reply
Builder: Dewey. Reviewer: Filbert. Gate owner: Sage.
Brief: docs/plans/2026-10-15_landing-followups.md, the first `##` section (blob 4e620d79, commit
c66c7fb71). Assignment: sage-0454. Sources: Filbert's row 146 round 1 review, note 1 and mutant M1
(comment 28641), and the row 142 round 3 hand probe the brief describes.
Base: 686fc1cb6 (base.txt, origin/refactor after rows 136 and 147). The candidate is build.patch on that
base, and candidate-manifest.sha256 lists both changed paths with their sha256. repro.sh applies the
patch to a fresh detached worktree and checks the digests; it printed DIGESTS_OK.
Built on 02ee7f640 (after row 142). Row 136 landed as d8487b3af and changed packages/bus/src/broker.mjs
under the launch-notice test, and none of this row's paths. Per sage-0463 the worktree moved to
d8487b3af: the patch applied with no change to either file, the manifest digests are the same, and
repro.sh on the new base printed DIGESTS_OK.
Row 147 then landed as 686fc1cb6. Sage's check (`git diff --name-only 02ee7f640 686fc1cb6` on the two
test files, sage-0467) printed nothing, so the base could have stayed. But row 147 changed
packages/cli/src/cli.mjs, the file C1 to C3 mutate and verbs runs, so I moved to 686fc1cb6 so that
the mutants and the gate run on the cli.mjs that lands. The patch and the digests are unchanged
again, each mutant target still occurs once, and repro.sh printed DIGESTS_OK.
## The change
Test-only: no product code changes. The two files the brief names, +44 lines, nothing removed.
- **packages/bus/tests/launch-notice.test.mjs**, test 1. After the second send (human to coder) with
chief, launch.by, running, the latest notice's `routed` delivery has `holder_run` equal to
`chief-run`. The test already asserted a null holder for the first notice, sent while chief was
stopped. message.receive selects on `to_role`, so chief still receives a notice routed elsewhere.
This row's `routed` delivery is the only place the routing shows, and it's the row the Console's
waiting lists read.
- **packages/cli/tests/verbs.test.mjs**, a new test, "talk to a stopped role prints the waiting line
once, skips a non-reply and prints the reply". The business's launch block names pm as launch.by,
and pm runs. `talk cto` with `--wait 5` goes to cto, which has no session. A transport wrapper
injects the other side. On poll 1, pm sends the human an INFO note, "launching cto". On poll 2, cto
is launched and claims, receives the waiting message and replies with RESULT in_reply_to it.
Assertions:
- exit 0;
- the whole stdout equals `sent <id> to cto`, then `cto isn't running, pm has been told`, then the
reply, so the waiting line prints once, the note doesn't print, and nothing else prints;
- polls is 2, so the note did arrive before the reply;
- message.read was called once, for the reply's id;
- `mosaic inbox` then shows 1 unread, pm's note.
This is the case Filbert probed by hand on the row 142 candidate. Row 146's talk test uses
`--wait 0`, which never polls. Row 142's tests talk to coder, which runs, so they never print a
waiting line.
### Choices
- **The non-reply comes from launch.by (pm), not from the target.** That is the message a human
is likely to get first when a stopped role is launched. Row 142's test already covers an
unsolicited note from the target role itself.
- **Exact stdout instead of separate matches.** One equality pins the order (sent, waiting line,
reply), says the waiting line prints once, and says the note never prints. Separate matches would
each need a count.
- **C3, a mutant beyond the brief's two.** The brief says the waiting line prints once. C3 prints it
again on every receive poll, so the "once" has a mutant of its own.
## Mutants
mutants.py applies one fault at a time, runs the owning test file with the spec reporter, and
restores the source after each one. Run 2026-10-16T00:13:46Z to 00:13:59Z on 686fc1cb6 plus the
candidate, in the sage-0470 slot: 4 of 4 killed, `restored True` (mutants-run.log, logs in mutants/).
| Mutant | Fault | Runs | Result | Failing tests |
|---|---|---|---|---|
| B1-notice-routed-to-target | Filbert's row 146 M1: `#notice` routes to `to`, not `by` | bus launch-notice | KILLED | test 1, at the new assertion (line 97: actual null, expected `chief-run`). Nothing else fails, so this assertion is the only thing holding it, as Filbert found. |
| C1-no-reply-filter | talk drops row 142's `m.in_reply_to !== sent.id` skip | cli verbs | KILLED | the new test; row 142's two talk tests |
| C2-no-waiting-lines | talk drops row 146's `waitingLines` write | cli verbs | KILLED | the new test; row 146's talk test |
| C3-waiting-every-poll | talk writes the waiting line on every receive poll | cli verbs | KILLED | the new test only |
C1 and C2 were already killed by the row 142 and row 146 tests. The new test adds what neither
had: both paths in one talk, plus the "once" that only C3 checks.
## Receipts
Single files on the candidate, without the slot, TMPDIR in my scratch, 0 fail and 0 skipped:
- on 02ee7f640: bus launch-notice 7/7, cli verbs 8/8;
- on d8487b3af, 2026-10-16T00:05:44Z to 00:05:48Z: bus launch-notice 7/7, cli verbs 8/8. B1's target
string still occurs once in broker.mjs.
- on 686fc1cb6, 2026-10-16T00:12:33Z to 00:12:38Z: bus launch-notice 7/7, cli verbs 8/8.
Slot granted by Sage in sage-0470 (00:13:38Z).
- **Mutants**, 00:13:46Z to 00:13:59Z: 4 of 4 killed, source restored. See the kill table above.
- **Gate**, gate.sh from ~/dewey-scratch/r151 on 686fc1cb6, 2026-10-16T00:14:06Z to 00:16:58Z,
exit 0 (gate-run.log, logs under gate/). It covers the bus and cli suites and every
scripts/test-*.sh, as the brief's gate names them:
| Step | Result |
|---|---|
| bus | 265 pass, 0 fail |
| cli | 213 pass, 0 fail |
| test-auth | 15 passed, 0 failed |
| test-conductor | 17 passed, 0 failed |
| test-config | 24 passed, 0 failed |
| test-discord | 78 passed, 0 failed |
| test-extension-package | 18 passed, 0 failed |
| test-foundation | 44 passed, 0 failed |
| test-land-work | 84 passed, 0 failed |
| test-queue | 28 passed, 0 failed |
| test-release | 14 passed, 0 failed |
| test-task | 98 passed, 0 failed |
No recurrence of flakes #258, #261, #262 or #263. gate.sh exports
`COMPOSE_PROJECT_NAME=dewey-r151`, and its EXIT trap printed `removed network
dewey-r151_default`. Afterwards no network named dewey* or wt* exists.
- **Candidate:** candidate-manifest.sha256, sha256
cb831847239e9ea60bd77ed8c765dafbf607a03cadeed3d0fc8d8284846878ed, 2 files.
+1
View File
@@ -0,0 +1 @@
686fc1cb6d4ce77a441b2953aa09dfc441ce9a39
+66
View File
@@ -0,0 +1,66 @@
diff --git a/packages/bus/tests/launch-notice.test.mjs b/packages/bus/tests/launch-notice.test.mjs
index 3ac83e702..0bf1703a2 100644
--- a/packages/bus/tests/launch-notice.test.mjs
+++ b/packages/bus/tests/launch-notice.test.mjs
@@ -92,6 +92,12 @@ test('a message to a stopped role sends launch.by one notice, and the result say
launcherRunning: true,
});
assert.equal(ctx.notices().length, 2);
+ // Row 151 (#1645): the notice is routed to launch.by's holder, which the Console's waiting lists read.
+ // message.receive selects on to_role, so only this row shows a notice routed anywhere else.
+ assert.equal(
+ ctx.store().get("SELECT holder_run FROM deliveries WHERE message=? AND op='routed'", ctx.notices().at(-1).id).holder_run,
+ 'chief-run',
+ );
// A role's message to a stopped role tells launch.by too.
ctx.launch('coder', 'coder');
ctx.claim('coder');
diff --git a/packages/cli/tests/verbs.test.mjs b/packages/cli/tests/verbs.test.mjs
index f29922e0e..c9d0b9d17 100644
--- a/packages/cli/tests/verbs.test.mjs
+++ b/packages/cli/tests/verbs.test.mjs
@@ -118,6 +118,44 @@ test("talk says when the role isn't running, whether launch.by was told, and how
assert.equal(await text(s, "coder"), "");
});
+// Row 151 (#1645): rows 146 and 142 in one talk. The waiting line prints once after "sent", a
+// message that isn't the reply arrives first and stays unread, then the reply prints and talk exits 0.
+test("talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply", async (t) => {
+ const s = setup(t, {}, { demo: { ...BUSINESSES.demo, launch: { by: "pm", instances: ["cto", "coder"], models: {}, limits: {} } } });
+ const pm = s.b.bindLaunch({ business: "demo", role: "pm", run: "pm-run", harness: "pi" });
+ call(s.b, pm, "role.claim");
+ // pm's note arrives on the first poll; cto comes up and answers on the second.
+ let polls = 0;
+ let note;
+ let reply;
+ const transport = (business) => {
+ const inner = s.transport(business);
+ return async (verb, args) => {
+ if (verb === "message.receive" && ++polls === 1) note = call(s.b, pm, "message.send", { to: "human", body: "launching cto", class: "INFO" });
+ if (verb === "message.receive" && polls === 2) {
+ const cto = s.b.bindLaunch({ business: "demo", role: "cto", run: "cto-run", harness: "pi" });
+ call(s.b, cto, "role.claim");
+ const [m] = call(s.b, cto, "message.receive");
+ reply = call(s.b, cto, "message.send", { to: m.from_role, body: "on it", class: "RESULT", in_reply_to: m.id });
+ }
+ return inner(verb, args);
+ };
+ };
+ const r = await s.run(["talk", "cto", "review the plan", "--wait", "5", "--business", "demo"], io(), { transport });
+ assert.equal(r.code, 0, r.error);
+ const sent = s.store.get("SELECT id FROM messages WHERE from_role='human' ORDER BY seq DESC LIMIT 1");
+ assert.equal(
+ r.io.out.text,
+ `sent ${sent.id.slice(0, 8)} to cto\ncto isn't running, pm has been told\ncto (RESULT, re ${sent.id.slice(0, 8)}) ${reply.id.slice(0, 8)}:\non it\n`,
+ );
+ assert.equal(polls, 2, "the note came first");
+ assert.deepEqual(s.calls.filter((c) => c.verb === "message.read").map((c) => c.args.id), [reply.id], "only the reply was marked read");
+ const inbox = io();
+ await main(["inbox", "--business", "demo"], inbox, { system: { dataRoot: s.dataRoot }, transport: s.transport });
+ assert.match(inbox.out.text, /messages: 1 unread\n/);
+ assert.match(inbox.out.text, new RegExp(`pm \\(INFO\\) ${note.id.slice(0, 8)}:\nlaunching cto\n`));
+});
+
test("talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors", async (t) => {
const s = setup(t);
const agent = () => io({ ...io().env, CLAUDECODE: "1" });
@@ -0,0 +1,2 @@
eb496122a8aa58ca8c3b63ec0c77ebe28654828f090add98ea723cdcf0ca29ca packages/bus/tests/launch-notice.test.mjs
756e2e733a96aed0d9050ae6096f1176ee84de7b0bdf73765842110ecc60c4ad packages/cli/tests/verbs.test.mjs
+15
View File
@@ -0,0 +1,15 @@
gate start 2026-10-16T00:14:06Z
pkg-bus start=2026-10-16T00:14:06Z end=2026-10-16T00:14:18Z rc=0 tests=265 pass=265 fail=0 skipped=0
pkg-cli start=2026-10-16T00:14:18Z end=2026-10-16T00:14:38Z rc=0 tests=213 pass=213 fail=0 skipped=0
test-auth start=2026-10-16T00:14:38Z end=2026-10-16T00:14:38Z rc=0
test-conductor start=2026-10-16T00:14:38Z end=2026-10-16T00:14:44Z rc=0
test-config start=2026-10-16T00:14:44Z end=2026-10-16T00:14:46Z rc=0
test-discord start=2026-10-16T00:14:46Z end=2026-10-16T00:14:52Z rc=0
test-extension-package start=2026-10-16T00:14:52Z end=2026-10-16T00:14:54Z rc=0
test-foundation start=2026-10-16T00:14:54Z end=2026-10-16T00:15:32Z rc=0
test-land-work start=2026-10-16T00:15:32Z end=2026-10-16T00:15:44Z rc=0
test-queue start=2026-10-16T00:15:44Z end=2026-10-16T00:16:15Z rc=0
test-release start=2026-10-16T00:16:15Z end=2026-10-16T00:16:24Z rc=0
test-task start=2026-10-16T00:16:24Z end=2026-10-16T00:16:58Z rc=0
removed network dewey-r151_default
gate end 2026-10-16T00:16:58Z rc=0
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Row 151 owner's gate (sage-0454): the bus and cli suites and every scripts/test-*.sh, on base.txt plus
# build.patch. One log per step under gate/.
set -uo pipefail
WT=${1:-$HOME/dewey-scratch/r151/wt}
OUT=$(cd "$(dirname "$0")" && pwd)/gate
mkdir -p "$OUT"
export TMPDIR=$HOME/dewey-scratch/tmp
# test-task.sh's live cases run `docker compose run` from $WT (mosaic-task.mjs passes its env through).
# An explicit project name keeps them off the dir-named `wt` project; its network goes at exit (Mos, sage-0447).
export COMPOSE_PROJECT_NAME=dewey-r151
trap 'docker network rm "${COMPOSE_PROJECT_NAME}_default" >/dev/null 2>&1 && echo "removed network ${COMPOSE_PROJECT_NAME}_default"' EXIT
status=0
for p in bus cli; do
start=$(date -u +%FT%TZ)
(cd "$WT/packages/$p" && node --test tests/*.test.mjs) >"$OUT/pkg-$p.log" 2>&1
rc=$?
counts=$(grep -E '^ℹ (tests|pass|fail|skipped) ' "$OUT/pkg-$p.log" | awk '{printf "%s=%s ", $2, $3}')
echo "pkg-$p start=$start end=$(date -u +%FT%TZ) rc=$rc $counts"
[ "$rc" -eq 0 ] || status=1
done
for s in "$WT"/scripts/test-*.sh; do
n=$(basename "$s" .sh)
start=$(date -u +%FT%TZ)
(cd "$WT" && bash "scripts/$n.sh") >"$OUT/$n.log" 2>&1
rc=$?
echo "$n start=$start end=$(date -u +%FT%TZ) rc=$rc"
[ "$rc" -eq 0 ] || status=1
done
exit $status
@@ -0,0 +1,273 @@
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (120.337424ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (236.10335ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (342.028896ms)
✔ agents: working wins over waiting, and Input needed still shows (180.930349ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (223.304101ms)
✔ agents: state reads only its own business, even for a run name both use (205.720718ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (209.298105ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (241.989695ms)
✔ agents: stranded is oldest first and capped at 50 for a role (1122.622583ms)
✔ agents: stranded is per role and per business (396.325154ms)
✔ agents: a message routed to a held role and not yet received is not stranded (164.490172ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (296.676593ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (190.537558ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (290.461762ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (253.973049ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (294.597641ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (366.320044ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (182.76576ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (298.785483ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (300.483364ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (175.652242ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (203.826509ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (280.006595ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (191.264372ms)
✔ task action subjects and linked decision trail are complete and ordered (203.618744ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (120.583386ms)
✔ business isolation includes inherited object names and cross-business message references (313.709529ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (368.749769ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (177.383849ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (246.164487ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (192.003233ms)
✔ both arbiters require human resolution when their cross-role route is themselves (296.80443ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (222.040456ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.274531ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (286.982142ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (338.032958ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (256.453408ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (256.035535ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (271.429837ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (328.095175ms)
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (269.925519ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (411.936337ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (373.954576ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (338.94336ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (472.604423ms)
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (657.472064ms)
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (1007.735632ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (136.063226ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (137.232948ms)
✔ explicit 3b copy migration preserves evidence and opens as 3e; normal startup never migrates (894.789636ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1782.994497ms)
✔ named migration registry chains 3b through 3e to a fixture-only future version in order (925.447555ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (1008.069346ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (930.053437ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (105.730174ms)
✔ credentials refuses a role session and any argument (192.643933ms)
✔ credentials reads the status on each request, and a broker without one lists none (235.190218ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (220.079688ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.916867ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.662913ms)
✔ expiry refuses use and env references never become client data (0.840564ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.948585ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.604049ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (2.51994ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (516.83888ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (483.993628ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (425.952182ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (499.059434ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (645.148748ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (458.600784ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (569.870542ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (557.729033ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (523.990355ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (528.301878ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (474.362384ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (386.660934ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (439.922413ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (433.00763ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (448.849809ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (275.202245ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (239.138701ms)
✔ a human withdraw sends one message with the note (254.274775ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (307.309098ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (301.815534ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (316.760762ms)
✔ a raising role nobody holds at the close gets the message on the next claim (269.887201ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (370.36077ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (331.483097ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (297.997392ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (376.700152ms)
✔ a refused supersede withdraws nothing (327.237208ms)
✔ the assistant's within-role and gated raises still work (319.708622ms)
✔ roles lists open decisions routed to a role, and never the human inbox (254.514555ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (300.145587ms)
✔ recent gives the closing event as closed when the decision was seen first (319.232461ms)
✔ recent stops at RECENT rows (878.606728ms)
✔ every row is what trail already gives the same session for that id (279.88558ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (617.925294ms)
✔ decisions is a read: one business, strict arguments, nothing written (453.330153ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (152.535099ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (167.064819ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (153.524761ms)
✔ endLaunch leaves a claim another run took alone (188.146227ms)
✔ refuse records action.refused against the caller with the code only (143.165399ms)
✔ launches off refuses role.launch with launch-revoked until launches on (199.023043ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (340.400628ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.602623ms)
✔ process reader gets own kernel identity without exposing environment values (0.489221ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.635023ms)
✔ real pid 1 remains inspectable when its environment is protected (0.257571ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (238.034723ms)
✔ a message to a running role sends no notice and its result is unchanged (148.150794ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (253.269261ms)
✔ a run launched but not yet claimed counts as no holder (153.33514ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (280.313936ms)
✔ a business with no launch block tells nobody and says there is no launcher (137.558758ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (388.938786ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (243.910901ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (286.135801ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (436.848255ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (238.332908ms)
✔ missing and foreign-business citations refuse and roll back message and grant (187.159ms)
✔ cross-role sends still need a matching resolved decision and consume it once (267.719117ms)
✔ messages pages all human messages newest first without writes; read state is history (2952.676963ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (299.045227ms)
✔ views messages passes only its paging arguments through (0.270901ms)
✔ messages isolates businesses even with a valid foreign cursor (203.491166ms)
✔ the assistant sends to the user and to the chief of staff only (334.867872ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (390.930871ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (381.659241ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (445.456355ms)
✔ a sender-side list alone refuses a send to a role that has no list (438.419302ms)
✔ a target-side list alone refuses a send from a role that has no list (388.876484ms)
✔ the sender list is checked before the target list (361.902135ms)
✔ roles without the field behave exactly as before (529.37908ms)
✔ a business with no role definitions at all is unlimited (453.833005ms)
✔ a malformed messaging field refuses at boot (121.869077ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (378.981644ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (425.972925ms)
✔ review requests and verdicts between roles without the field are unchanged (343.142962ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (211.476438ms)
✔ runtime: the switch changes the organization file, the receipt carries the actor, and a bad shape, an unknown module and a stale revision are refused with nothing written (511.682538ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (438.708873ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (233.721972ms)
✔ startup token refusal returns safe code without value or partial listening broker (63.614037ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (220.861017ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (242.677472ms)
✔ trusted host registers later launches; socket clients never have a registration verb (263.582196ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (60.064915ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (246.238924ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (259.627384ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (199.460571ms)
✔ a message with no reply gives an empty list (219.836853ms)
✔ replies come oldest first and stop at REPLIES (978.858185ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (356.703793ms)
✔ strict arguments; a role session gets human-required (459.820447ms)
✔ views passes replies through as one call with the id (0.239939ms)
✔ requirements returns the committed list to a holding role session, and no one else (216.079867ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (205.348646ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (267.883134ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (241.624182ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (369.673343ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (390.924564ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (418.497977ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (271.586155ms)
✔ review.verdict answers one request once, to the role that asked (381.240433ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (492.527486ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (347.461728ms)
✔ v3b prototype refusals, views and append-only mutations (1818.734793ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (427.878971ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (252.424739ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (186.454692ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (379.024988ms)
✔ class drift gated to cross-role refuses before consumption (268.98431ms)
✔ class drift cross-role to gated refuses before consumption (309.009736ms)
✔ class drift gated to within-role refuses before consumption (288.833621ms)
✔ class drift cross-role to within-role refuses before consumption (360.25217ms)
✔ class drift within-role to gated refuses before consumption (255.711236ms)
✔ class drift within-role to cross-role refuses before consumption (237.629154ms)
✔ message.send consumes approval and prevents a later send or authorize (256.929027ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (274.916425ms)
✔ a business that never switched launches reads allowed with no time and no event (183.938801ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (187.34609ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (163.713081ms)
✔ a tombstone is not open (157.647915ms)
✔ the open list stops at the cap and says so; total still counts every open task (179.260281ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (164.606129ms)
✔ a human session reads the same state, with the same cap (203.412287ms)
✔ reading state writes nothing: every table keeps its row count (185.493496ms)
✔ creates private WAL store and excludes a second writer until explicit close (156.845554ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (210.599099ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (198.188117ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (189.916588ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (122.783855ms)
✔ async transactions refuse before invoking their function (100.583117ms)
✔ the session default fits under the reply cap and pages to the end with next (349.0754ms)
✔ the short form: no description, assignees by role, every listed key (268.084897ms)
✔ each filter, alone and together, and total counts every match (367.669581ms)
✔ task_ref reads one task in full, as the whole board has it (343.789627ms)
✔ no arguments is the read the Console and mosaic make, unchanged (318.772374ms)
✔ an unknown argument or a bad value refuses invalid-request (1067.819115ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (365.230926ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (403.313319ms)
✔ an empty business and one with snapshots but no task.created read cleanly (177.887673ms)
✔ recordTask keeps sync reads and a role write apart (229.065081ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (127.751567ms)
✔ a bad entry refuses the whole record (123.958861ms)
✔ taskView reads the projection for one business (162.579452ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (355.111458ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (435.370416ms)
✔ without an adapter the server refuses every task verb (235.424712ms)
✔ the runtime refuses an invalid adapter and closes a valid one (232.972038ms)
✔ the process loads the S3 adapter from plain-data trackers (335.109654ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1046.358063ms)
✔ a business may not name a role tester, so the tester address is never a role (283.159515ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (603.8502ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (248.744456ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (264.45102ms)
✔ the tester reads the human views a reader reads (169.204384ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.123755ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.327183ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.190133ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (112.53608ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (903.690894ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1216.625423ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (872.609043ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (849.912089ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (401.551652ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1341.154433ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (352.38586ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3958.521652ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (106.105684ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (155.408372ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (174.541422ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (181.78263ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (173.949515ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (302.747585ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (156.068023ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (114.110125ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (148.454449ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (463.777514ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (177.455282ms)
✔ two wire claims serialize; a lost reply never automatically retries (259.058602ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (167.07144ms)
✔ client preserves UTF-8 when a response divides a multibyte character (10.820041ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (174.81851ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (218.752174ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (200.729373ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (283.400395ms)
✔ a broker with no web service refuses a granted role (154.063919ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (232.461615ms)
✔ a session that is not the role holder is refused (126.999869ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (438.097711ms)
✔ web.search with no search source configured is a refused result, not an error (149.946357ms)
✔ a search source that fails with its own refusal, or that throws, is handled (180.076413ms)
✔ a slow search source ends in a refusal at the deadline (187.188926ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (121.075396ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (238.296211ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.25467ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (232.358448ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (168.10247ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (470.89933ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (236.773852ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (230.319161ms)
✔ one query limit: over SEARCH_MAX_QUERY_CHARS or empty is refused with the same reason before any backend, and the tool schema carries the same number (170.899761ms)
ℹ tests 265
ℹ suites 0
ℹ pass 265
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11950.614617
@@ -0,0 +1,223 @@
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1392.94054ms)
✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (65.226836ms)
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (144.899792ms)
✔ NO_COLOR: no escape codes, except reverse video on the selected row (234.950305ms)
✔ 1-9 shows the decide flow's own lines and question; only y resolves (241.603231ms)
✔ the app shows the same decide text as mosaic decide (159.256084ms)
✔ any key but y or Y at the question answers No; nothing is sent (3057.084473ms)
✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (789.037736ms)
✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (1920.329552ms)
✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (93.514219ms)
✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (91.282545ms)
✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (799.179184ms)
✔ quitting at the question answers No and sends nothing, armed or not (174.363391ms)
✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (154.893943ms)
✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (116.478809ms)
✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (1063.518736ms)
✔ at every size the decide box shows each line y depends on, or says it is hidden (8492.504374ms)
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (149.956101ms)
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (108.958373ms)
✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (55.629888ms)
✔ keys: views, moving, trail of a selection, the command line, help and quit (242.1676ms)
✔ one option: the status line says Press 1, not 1-1 (95.121636ms)
✔ no business: the app says so and :business picks one (49.227595ms)
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.679998ms)
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (167.59024ms)
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.440173ms)
✔ decide with --note still asks; a No sends nothing (60.406093ms)
✔ the app's business comes from a live bus host only (0.940807ms)
✔ 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.12598ms)
✔ draw is pure: same state, same frame (0.787061ms)
✔ recordBootRefused keeps the last good load across refusals; recordBootOk clears the refusal (4.658559ms)
✔ an unknown key and a JSON syntax error each leave the bus down with the reason and path in the status (790.352828ms)
✔ a missing file is recorded too, and a valid file clears the status on the next start (429.188557ms)
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (245.883893ms)
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (188.461524ms)
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (183.011459ms)
✔ decide prints a declining choice as declining (177.014866ms)
✔ an unknown outcome is reported once and never resent (120.320351ms)
✔ a decision closed before the answer arrives exits 2 and points at its trail (130.554787ms)
✔ a prefix that matches two open decisions exits 2 and resolves neither (201.80951ms)
✔ without --business a command uses the live host's business, and a stale host.json is not a host (144.798923ms)
✔ every human command refuses inside an agent run before it touches the bus (304.606361ms)
✔ usage errors exit 4; no business and no host is a usage error (210.235063ms)
✔ agents and tasks print through the broker (121.435062ms)
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.187012ms)
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (69.054575ms)
✔ trackers come from the tracker.* variables of the one project that names a tracker project (94.547776ms)
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (48.172781ms)
✔ two projects that each name a tracker project refuse, since the boot shape holds one (49.025591ms)
✔ a business without tracker.baseUrl gets no trackers entry (29.987987ms)
✔ an unknown business and a broken system config refuse with exit 3 (73.708115ms)
✔ follow prints an unsolicited message after plain talk returned (314.369522ms)
✔ talk --follow continues after reply and prints a later unsolicited message (268.445895ms)
✔ inbox lists all unread human messages under decisions, does not deliver or read them (4726.697749ms)
✔ follow refuses agent context and bad arguments (118.473089ms)
✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (147.003549ms)
✔ plain talk leaves the process SIGINT handlers unchanged (148.483513ms)
✔ empty views say so (0.988468ms)
✔ the trail keeps the broker's order and names a decision's task without its rows (1.187932ms)
✔ tasks print the tracker fields the snapshot carries (0.195363ms)
✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (927.813386ms)
✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (948.043884ms)
✔ the size cap refuses a push before any byte (405.621208ms)
✔ a fetch over the cap refuses before any byte reaches the reviewer (1137.685949ms)
✔ the socket refuses a request with no capability, an unknown one, and a bad request line (1465.057799ms)
✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (561.15317ms)
✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (655.659359ms)
✔ a review of a project the business no longer has refuses unknown-project (545.965099ms)
✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (529.561844ms)
✔ close() answers host-closing to a connection still sending its request line (418.643691ms)
✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (823.77607ms)
✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (679.45238ms)
✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (643.122073ms)
✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (42.491165ms)
✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (640.928111ms)
✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (24.630106ms)
✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (302.065489ms)
✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (467.767121ms)
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1054.563808ms)
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (284.466681ms)
✔ a second host for the same data root refuses with exit 3 while the first runs (366.282613ms)
✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (621.564236ms)
✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (3.201968ms)
✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (0.72421ms)
✔ a live host's lock stays, however old the file is (0.497506ms)
✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (1066.838911ms)
✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (197.082997ms)
✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (25.808777ms)
✔ a broker fork that throws leaves no host.lock and no host.json (101.889508ms)
✔ a broker send that throws leaves no host.lock and no host.json (28.839161ms)
✔ a broker that misses its boot wait leaves no host.lock and no host.json (42.588331ms)
✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (562.827472ms)
✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (480.130978ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, before the notifier starts, and leaves no host.lock and no live broker (185.107972ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, with no notifier, and leaves no host.lock and no live broker (186.455062ms)
✔ a host.json that can't be written stops both children and leaves no host.lock (343.226185ms)
✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (27.326386ms)
✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (204.20097ms)
✔ an error event on the broker while the notifier starts fails the start once the notifier replies (504.081093ms)
✔ an error event on the broker once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (289.50521ms)
✔ an error event on the notifier once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (492.283162ms)
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1273.66017ms)
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (378.274055ms)
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (181.439564ms)
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (234.659889ms)
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (218.086301ms)
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (148.519775ms)
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (221.028947ms)
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (168.978998ms)
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (224.390891ms)
✔ watchChildren reports a child that died before it was called, and one that dies later (20.183837ms)
✔ bus stop refuses to signal a live pid that is not a bus host (207.219312ms)
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (309.16616ms)
✔ bus start refuses with exit 3 without a notifier config (109.475353ms)
✔ bus start runs until bus stop; status reports it while it runs (751.713416ms)
✔ bus-service.sh renders the unit and installs it into a given directory (52.307392ms)
✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (62.38137ms)
✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (12.73607ms)
✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (8.157995ms)
✔ each launch rereads limits and model selections; lowered limits keep sessions and log exact hit (42.126085ms)
✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1518.570731ms)
✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (986.900622ms)
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (3499.319054ms)
✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (385.912573ms)
✔ a runner that stops at once ends its launch with the runner's reason (496.611983ms)
✔ a Claude Code launch with no session credential refuses before it starts (302.022704ms)
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (335.285531ms)
✔ a runner killed from the host ends its launch as killed, not failed (451.293215ms)
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (192.806205ms)
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1468.291273ms)
✔ 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 (1000.581475ms)
✔ 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 (3759.58585ms)
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (162.013829ms)
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (165.490432ms)
✔ a launch client that never closes its side doesn't hold the host's close (168.876957ms)
✔ a runner that ignores SIGTERM is killed when the host closes (1297.168149ms)
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.347185ms)
✔ migration needs no bus/config, prints whole converted document, is idempotent and never writes (55.850343ms)
✔ limits view/set use the human bus transport and pass only the explicit patch (2.050896ms)
✔ post-119 example and Jason fixture migrate with checked inherited models; wrong or absent config refuses (215.863942ms)
✔ plugins view/set use the human bus transport and pass only the explicit patch (vikunja:19/305) (2.743303ms)
✔ zoned uses the IANA zone across DST (25.05817ms)
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (257.129311ms)
✔ two blocking decisions get two DMs with different nonces (194.321353ms)
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.31809ms)
✔ a failed DM is journaled, backs off, and is retried until it lands (134.380921ms)
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (203.477636ms)
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (122.090147ms)
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (129.41985ms)
✔ 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 (279.877692ms)
✔ a restart after the second refusal does not send before that refusal's 30 min are up (237.706663ms)
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (353.538065ms)
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (121.156114ms)
✔ an inbox read failure is logged and the next poll retries (0.643421ms)
✔ no Discord id reaches the journal or the log (124.116058ms)
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (45.489613ms)
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (55.878993ms)
✔ the journal: a whole file that is one torn line truncates to empty (22.760038ms)
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.053679ms)
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.695123ms)
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (4.179566ms)
✔ the journal: a symlinked directory refuses and says it is a link (0.608345ms)
✔ 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.538904ms)
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.367424ms)
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.424274ms)
✔ digest content stays within Discord's 2000 characters (0.26115ms)
✔ runLoop never overlaps ticks and stops after the one in flight (111.254108ms)
✔ a human command refused with human-required prints the other-tree line and exits 3 (4.078583ms)
✔ a --tester command refused with tester-required prints the other-tree line and exits 3 (1.27028ms)
✔ other exit-3 refusals print no other-tree line (1.841352ms)
✔ the line names no path (0.151386ms)
✔ project init/check use declared business repo and never contact the bus (174.807814ms)
✔ project commands validate args and refuse undeclared projects and agent init (35.120857ms)
✔ project dispatcher reaches the human CLI (109.170593ms)
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (602.470712ms)
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (497.416017ms)
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (474.083201ms)
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (242.85511ms)
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (4427.326853ms)
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (485.646118ms)
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (245.411102ms)
✔ install never shows a template name on systemd 261; template drop-ins still refuse (260.938784ms)
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (208.166502ms)
✔ inherited mosaic-.service.d override refuses without changing the unit (208.42477ms)
✔ inherited service.d override refuses without changing the unit (188.261193ms)
✔ inherited drop-in unreadable directory or file refuses before writing (181.245858ms)
✔ talk --tester sends a [mos test] message as tester from inside an agent and prints the role's reply (243.220357ms)
✔ talk --tester refuses text without the [mos test] prefix before any call (146.169384ms)
✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (210.62822ms)
✔ decide, stop and launches take no --tester (119.324579ms)
✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (0.335903ms)
✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (0.543418ms)
✔ talk --tester to a stopped role says nobody is told to launch it (128.011642ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it (6570.100902ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't (1797.660661ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it (4465.478219ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it (2898.899915ms)
✔ REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session (113.811563ms)
✔ REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message (209.209175ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing (250.264362ms)
✔ REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread (86.949411ms)
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 (528.459841ms)
✔ the transport writes {business, verb, args} to the child and reads its JSON (38.07345ms)
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2157.315908ms)
✔ busExit and refuseInsideAgent (0.456975ms)
✔ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (329.68791ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1136.70871ms)
✔ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2438.797063ms)
✔ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (624.268703ms)
✔ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (247.745199ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (117.451626ms)
✔ launches off and on go to the broker and change the business's launch state (140.114369ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (148.514659ms)
ℹ tests 213
ℹ suites 0
ℹ pass 213
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 20099.024308
@@ -0,0 +1,17 @@
OK status with missing harness credential exits 3 and still lists accounts
OK status reports harness credential (read-only) + mosaic accounts
OK api key material never reaches output
OK oauth token material never reaches output
OK unparseable credential file exits 2
OK symlinked credential file exits 4
OK env-side credential names reported
OK env var values never reach output
OK accounts without an accounts dir reports none and creates nothing
OK accounts lists files and marks the active one
OK loose account perms flagged in listing
OK agent --auth with missing account file refuses (exit 4)
OK agent --auth with non-0600 account file refuses
OK agent --auth with invalid account name refuses
OK auth.sh without valid config refuses
selftest: 15 passed, 0 failed
@@ -0,0 +1,55 @@
Note: switching to '686fc1cb6d4ce77a441b2953aa09dfc441ce9a39'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
Not currently on any branch.
nothing to commit, working tree clean
Note: switching to '686fc1cb6d4ce77a441b2953aa09dfc441ce9a39'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
OK dry-run committed nothing
OK apply: allowed change exits 0 (exit 0)
OK apply: attribution in commit subject
OK apply: target tree clean after commit
OK disallowed path refused (exit 1)
OK disallowed path: target untouched
OK syntax gate refused broken .mjs (exit 1)
OK syntax gate: target untouched
OK suite failure refused (exit 1)
OK suite failure: target reverted to clean
OK disabled policy refused (exit 2)
OK disabled policy: target untouched
OK failed run refused (exit 1)
OK failed run: target untouched
OK missing run exits 4 (exit 4)
OK invalid policy exits 2 (exit 2)
selftest: 17 passed, 0 failed
@@ -0,0 +1,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
@@ -0,0 +1,82 @@
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/copy-service.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/offline-check.test.mjs
OK syntax: packages/discord/tests/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 183)
OK scripts/discord.sh --help exits 0
OK scripts/discord.sh check without a binding exits 4
OK scripts/discord.sh recover without a binding exits 4
OK scripts/discord.sh reload without a binding exits 4
OK scripts/discord-service.sh without a command exits 4
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
OK service install writes the rendered unit (0644) and leaves no temp file
OK service install a second time reports unchanged
OK systemd-analyze verify accepts the rendered unit
OK service uninstall removes the unit file
OK service install with an unknown flag exits 4
OK service install with USER unset finishes and names the account for lingering
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
OK pinned install writes the rendered unit in a temporary unit directory
OK pinned install refuses short prerequisite and writes no unit
OK pinned install refuses missing prerequisite and writes no unit
OK pinned install refuses cli prerequisite and writes no unit
OK pinned install refuses pi prerequisite and writes no unit
OK status names the copy from fake systemctl effective cwd
OK checkout install explicitly recommends --copy
OK wrapper check --offline passes local fixture and makes no network call
OK wrapper run --offline refuses usage
discord suite: 78 passed, 0 failed
@@ -0,0 +1,21 @@
OK initial ordinary-file install
OK installed tree matches canonical source
OK installed tree has no symlinks
OK check detects installation drift
OK sync refuses to overwrite installation drift
OK check detects an extra destination file
OK check detects an extra destination directory
OK check rejects a destination symlink
OK sync accepts a canonical source update
OK updated installation matches canonical source
scripts/test-extension-package.sh: line 14: 2935078 Killed "$@" > /dev/null 2>&1
OK forced interruption kills the replacing process
OK next invocation recovers old consistent installation
OK interrupted replacement rolled back
OK sync succeeds after interruption recovery
OK unlocked stale lock file does not block
OK active lock refuses a concurrent sync
OK source symlink fails closed
OK nested second entrypoint fails closed
extension package selftest: 18 passed, 0 failed
@@ -0,0 +1,53 @@
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
OK syntax: scripts/foundation-inspect.mjs
OK syntax: scripts/foundation/strict-json.mjs
OK syntax: scripts/foundation/canonical.mjs
OK syntax: scripts/foundation/resolve.mjs
OK syntax: scripts/foundation/validate-record.mjs
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
OK syntax: scripts/foundation/canonical.test.mjs
OK syntax: scripts/foundation/cli.test.mjs
OK syntax: scripts/foundation/fixtures.test.mjs
OK syntax: scripts/foundation/resolve.test.mjs
OK syntax: scripts/foundation/strict-json.test.mjs
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
OK fixture generator runs
OK checked-in fixtures/bundles equal a fresh generation
OK checked-in fixtures/raw equal a fresh generation
OK checked-in fixtures/index.json equal a fresh generation
OK checked-in demo bundles equal a fresh generation
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test scripts/foundation/ (ℹ pass 80)
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
OK oracle: zero schema-column disagreements with the pinned checker
OK oracle: strict-only profile refusals are counted and asserted
OK demo: permitted read preview exits 0 (exit 0)
OK demo: permitted file.change preview exits 0 (exit 0)
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
OK demo: revoked registration is refused (exit 3) (exit 3)
OK demo: message is not authority (exit 3) (exit 3)
OK usage: no arguments exits 2 (exit 2)
OK io: missing file exits 4 (exit 4)
OK io: directory exits 4 (exit 4)
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
OK bound: oversize fixture exits 2 (exit 2)
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
OK text output starts with the disclaimer
OK json output is valid JSON with result allowed and exactly the charter §7 fields
OK json golden matches byte-for-byte
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
OK canary never printed (bundle run and credential-file run)
OK a non-bundle JSON file is refused at the shape gate, not read into output
OK no field of the non-bundle file is echoed
selftest: 44 passed, 0 failed
@@ -0,0 +1,86 @@
OK args: exit 2, target-branch
OK args: exit 2, target-branch
OK args: exit 4
OK args: exit 4
OK args: exit 4
OK --base next refuses before the config is read (got 2)
OK a missing system config exits 3 (got 3)
OK nobus: exit 3
OK schema-table: exit 2, bus-schema
OK schema-meta: exit 2, bus-schema
OK openBus opens the database read-only
OK other-business: exit 2, decision-not-found
OK no-raise: exit 2, decision-not-found
OK not-a-merge: exit 2, not-a-merge
OK open: exit 2, not-resolved
OK withdrawn: exit 2, not-resolved
OK by-agent: exit 2, not-human
OK via-webui: exit 2, not-human
OK no-answer: exit 2, not-human
OK answer-role: exit 2, not-human
OK answer-run: exit 2, not-human
OK answer-choice: exit 2, not-human
OK two-answers: exit 2, not-human
OK chose-no: exit 2, not-merge-option
OK two-refs: exit 2, work-ref
OK bad-ref: exit 2, work-ref
OK task-ref-disagrees: exit 2, work-ref
OK raise-task-disagrees: exit 2, work-ref
OK target-main: exit 2, target-branch
OK superseded-question: exit 2, superseded
OK superseded-task: exit 2, superseded
OK superseded-by: exit 2, superseded
OK no-verdict: exit 2, no-verdict
OK changes-later: exit 2, not-approved
OK short-commit: exit 2, not-approved
OK wrong-task-body: exit 2, not-approved
OK args: exit 2, decision-not-found
OK earlier-merge: exit 0
OK ref-moved: exit 2, ref-moved
OK root-top: exit 2, root-file
OK root-newdir: exit 2, root-file
OK root-src: exit 2, root-file
OK unrelated: exit 2, base-conflict
OK empty: exit 2, base-conflict
OK an empty reviewed diff says so
OK dirty-changed: exit 2, checkout-dirty
OK dirty-staged: exit 2, checkout-dirty
OK dirty-untracked: exit 2, checkout-dirty
OK conflict: exit 2, base-conflict
OK a refusal after the worktree is added leaves no worktree or scratch
OK cleanup looks for the worktree's Docker network
OK dirty-during: exit 2, checkout-dirty
OK a reviewed path dirtied during the gate moves no ref
OK drift: exit 2, patch-drift
OK patch-drift leaves no worktree or scratch
OK gate-pkg: exit 2, gate-failed
OK gate-failed names the failing step's log, and it exists
OK gate-failed leaves no worktree or scratch
OK gate-script: exit 2, gate-failed
OK a failing scripts/test-*.sh fails the gate
OK a failed gate moves no ref
OK nomods: exit 4
OK a target without node_modules leaves no worktree or scratch
OK moved: exit 2, base-moved
OK base-moved keeps the base where it moved to
OK dry: exit 0
OK --dry-run writes no ref
OK --dry-run prints the commit with --subject and --attribution
OK --dry-run runs the gate
OK --dry-run leaves no worktree or scratch
OK land: exit 0
OK the landing is one commit on the old base
OK its diff is the reviewed diff by patch-id
OK its tree is the dry run's
OK the message has the option's subject, the citations and the attribution last
OK it prints the line for the PM
OK the checked-out base has the reviewed paths and keeps the unrelated edit
OK only refs/heads/refactor exists in the target
OK the work ref is unchanged
OK the bus database is unchanged
OK the gate ran each package suite but queue, and each scripts/test-*.sh
OK the landing leaves no worktree or scratch
OK the target's node_modules is still there, with its contents
OK the worktree's Docker network is removed
---
land-work selftests: 84 passed, 0 failed
@@ -0,0 +1,36 @@
toolchain: node v26.8.1, git version 2.55.0
OK syntax: packages/queue/src/cli.mjs
OK syntax: packages/queue/src/errors.mjs
OK syntax: packages/queue/src/io.mjs
OK syntax: packages/queue/src/lock.mjs
OK syntax: packages/queue/src/queue.mjs
OK syntax: packages/queue/src/review.mjs
OK syntax: packages/queue/src/store.mjs
OK syntax: packages/queue/tests/commit.test.mjs
OK syntax: packages/queue/tests/data.test.mjs
OK syntax: packages/queue/tests/dispatch.test.mjs
OK syntax: packages/queue/tests/gate-owner.test.mjs
OK syntax: packages/queue/tests/helpers.mjs
OK syntax: packages/queue/tests/lock.test.mjs
OK syntax: packages/queue/tests/migration.test.mjs
OK syntax: packages/queue/tests/review.test.mjs
OK syntax: packages/queue/tests/store.test.mjs
OK syntax: packages/queue/tests/write.test.mjs
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
OK syntax: scripts/queue-commit.sh
OK syntax: scripts/git-hooks/pre-commit
OK syntax: scripts/mosaic
OK queue-commit.sh, the guard and scripts/mosaic are executable
OK packages/queue declares no dependencies
ℹ tests 156
ℹ pass 156
ℹ fail 0
OK node --test packages/queue/tests/
OK scripts/mosaic queue help
skip queue verify and render --check: this checkout (/home/jwoltje/dewey-scratch/r151/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
queue suite: 28 passed, 0 failed
@@ -0,0 +1,16 @@
OK valid RELEASE resolves (exit 0)
OK invalid RELEASE exits 1 (exit 1)
OK missing RELEASE exits 1 (exit 1)
OK valid RELEASE leaves image tag consistent with version
OK status safe on empty state (exit 0)
OK status created no pointer
OK fault-injected activation refuses (exit 1)
OK refused activation wrote no pointer
OK refusal logged exactly once with valid fields
OK healthy activation succeeds (exit 0)
OK pointer written with valid fields
OK repeat activation succeeds (log grows) (exit 0)
OK log is append-only across activations
OK rollback without previous refuses (exit 1)
selftest: 14 passed, 0 failed
@@ -0,0 +1,101 @@
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)
OK mock adapter: gate passes on matching mock response (exit 0)
OK mock adapter: expect-mismatch recorded (exit 1)
OK mismatch reason recorded
OK unknown adapter fails closed (exit 1)
OK mission task runs via mock adapter (exit 0)
OK mission section injected into generated prompt
OK retry of mission run succeeds (exit 0)
OK retriedFrom lineage recorded
OK mission section present after retry (relative path resolved)
OK retry of missing run exits 4 (exit 4)
OK skill install bundled ms-tools (exit 0)
OK installed to skills-available
OK double install refuses (exit 1)
OK activate enables skill (exit 0)
OK enabled dir populated
OK uninstall while enabled refuses (exit 1)
OK deactivate moves back to available (exit 0)
OK uninstall removes from available (exit 0)
OK seat with enabled skill launches
OK skill path delivered to adapter
OK shipped researcher contract resolves (ceiling + network)
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
OK resolve-role refuses name/filename mismatch (exit 2)
OK resolve-role refuses unknown network declaration (exit 2)
OK resolve-role refuses duplicate role tool (exit 2)
OK resolve-role refuses unsupported tool (exit 2)
OK resolve-role refuses missing contract file (exit 4)
OK version 1 role prints no contract line
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
OK resolve-role accepts a minimal version 2 role (exit 0)
OK resolve-role refuses an action outside the vocabulary (exit 2)
OK resolve-role refuses a gated-only action in a role file (exit 2)
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
OK resolve-role refuses a contract that is a symbolic link (exit 2)
OK seat launches under role ceiling
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
OK narrowing recorded loudly
OK missing role contract refuses launch (exit 2)
OK missing-contract refusal names the role
OK empty ceiling intersection -> tool-free seat
OK tool-free outcome recorded loudly
OK overridden agents dir without seat definition refuses (exit 4)
OK seat-resolution refusal names the seat
OK policy: mission only -> mission tools
OK policy: task only -> task tools
OK policy: both -> intersection (task narrowed)
OK policy: empty intersection -> tool-free
OK invalid mission capabilities rejected (exit 2)
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
OK task run with user layer present (exit 0)
OK user context dispatched into generated prompt
OK workspace+tools task runs via mock (exit 0)
OK workspace path + tools delivered to adapter
OK persistent workspace created on host
OK plain task still runs (no workspace/tools) (exit 0)
OK workspace var present but empty when absent
OK tools empty when absent
OK unknown tool exits 2 (exit 2)
OK workspace traversal exits 2 (exit 2)
OK live hello task succeeds with exact marker
OK result.json written in run dir
OK result.json contents are correct
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
OK each run gets a distinct run dir (no clobber)
OK list shows both runs
OK fork base: teach succeeds (exit 0)
OK fork child recalls ancestor context (exit 0)
OK forked child recalled ancestor code word
OK ancestor session untouched by fork
OK child session dir has its own branch file
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
OK user recall run succeeds
OK recalled user name (response: Jason)
OK no agent identity on headless run
selftest: 98 passed, 0 failed
@@ -0,0 +1,7 @@
start 2026-10-16T00:13:46Z
B1-notice-routed-to-target KILLED rc=1 | a message to a stopped role sends launch.by one notice, and the result says so
C1-no-reply-filter KILLED rc=1 | a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it | talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread | talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply
C2-no-waiting-lines KILLED rc=1 | talk says when the role isn't running, whether launch.by was told, and how to start launch.by | talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply
C3-waiting-every-poll KILLED rc=1 | talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply
restored True
end 2026-10-16T00:13:59Z rc=0
+43
View File
@@ -0,0 +1,43 @@
import subprocess, sys, os, signal
# Row 151 (#1645): one mutant per pinned path. Each applies to its source in the worktree, runs the
# owning test file, and the source is restored after every mutant.
W=os.path.expanduser('~/dewey-scratch/r151/wt')
OUT=os.path.expanduser('~/dewey-scratch/r151/mut')
os.makedirs(OUT, exist_ok=True)
BROKER='packages/bus/src/broker.mjs'
CLI='packages/cli/src/cli.mjs'
LOOP=' for (const m of await call("message.receive")) {\n'
M={
# Filbert's row 146 M1: the notice is routed to the target instead of launch.by.
'B1-notice-routed-to-target': (BROKER, 'packages/bus', 'tests/launch-notice.test.mjs',
'this.#route(business, nid, by, null);', 'this.#route(business, nid, to, null);'),
# Row 142's reply filter is dropped: talk prints and reads every message.
'C1-no-reply-filter': (CLI, 'packages/cli', 'tests/verbs.test.mjs',
' if (!follow && m.in_reply_to !== sent.id) continue;\n', ''),
# Row 146's waitingLines write is dropped.
'C2-no-waiting-lines': (CLI, 'packages/cli', 'tests/verbs.test.mjs',
' if (sent.waiting) io.stdout.write(', ' if (false) io.stdout.write('),
# The waiting line is printed again on every receive poll instead of once.
'C3-waiting-every-poll': (CLI, 'packages/cli', 'tests/verbs.test.mjs',
LOOP, ' if (sent?.waiting) io.stdout.write(waitingLines(sent.waiting, () => "demo"));\n' + LOOP),
}
env=dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
origs={p: open(os.path.join(W,p)).read() for p in (BROKER, CLI)}
try:
for name,(path,pkg,test,a,b) in M.items():
orig=origs[path]
assert orig.count(a)==1, name
open(os.path.join(W,path),'w').write(orig.replace(a,b))
p=subprocess.Popen(['node','--test','--test-reporter=spec',test],cwd=os.path.join(W,pkg),env=env,
stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,start_new_session=True)
try:
out,_=p.communicate(timeout=300)
except subprocess.TimeoutExpired:
os.killpg(p.pid, signal.SIGKILL); out,_=p.communicate(); out+='\nTIMEOUT\n'
fails=sorted({l.strip()[2:].split(' (')[0] for l in out.splitlines() if l.lstrip().startswith('✖') and 'failing tests' not in l})
print(name, 'KILLED' if p.returncode!=0 else 'SURVIVED', 'rc=%d'%p.returncode, '| '+' | '.join(fails))
open(os.path.join(OUT,f'{name}.log'),'w').write(out)
open(os.path.join(W,path),'w').write(orig)
finally:
for path,orig in origs.items(): open(os.path.join(W,path),'w').write(orig)
print('restored', all(open(os.path.join(W,p)).read()==o for p,o in origs.items()))
@@ -0,0 +1,38 @@
✖ a message to a stopped role sends launch.by one notice, and the result says so (71.619393ms)
✔ a message to a running role sends no notice and its result is unchanged (54.574255ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (92.993429ms)
✔ a run launched but not yet claimed counts as no holder (50.656281ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (119.144025ms)
✔ a business with no launch block tells nobody and says there is no launcher (45.072513ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (158.137067ms)
ℹ tests 7
ℹ suites 0
ℹ pass 6
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 649.821244
✖ failing tests:
test at tests/launch-notice.test.mjs:68:1
✖ a message to a stopped role sends launch.by one notice, and the result says so (71.619393ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ null
- 'chief-run'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/bus/tests/launch-notice.test.mjs:97:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: null,
expected: 'chief-run',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,69 @@
✖ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (137.637526ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1062.429822ms)
✖ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2078.389494ms)
✔ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (131.77928ms)
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (128.132667ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (45.537858ms)
✔ launches off and on go to the broker and change the business's launch state (56.013202ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (59.829141ms)
ℹ tests 8
ℹ suites 0
ℹ pass 5
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 3787.405698
✖ failing tests:
test at tests/verbs.test.mjs:32:1
✖ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (137.637526ms)
AssertionError [ERR_ASSERTION]: another role's note and the same role's unsolicited note are not printed
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:59:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'sent 7810611c to coder\ncto (INFO) 7f8d1144:\nfyi\ncoder (INFO) 2157a83c:\nside note\ncoder (RESULT, re 7810611c) bec1e61d:\ndone: 3 files\n',
expected: /fyi|side note/,
operator: 'doesNotMatch',
diff: 'simple'
}
test at tests/verbs.test.mjs:80:1
✖ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2078.389494ms)
AssertionError [ERR_ASSERTION]: the late reply answers the first message, not this one
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:90:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'sent c04043bb to coder\ncoder (RESULT, re 5f5561b2) 7e17ed0e:\nlate answer\n',
expected: /late answer/,
operator: 'doesNotMatch',
diff: 'simple'
}
test at tests/verbs.test.mjs:123:1
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (128.132667ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
'sent f15cf912 to cto\n' +
"cto isn't running, pm has been told\n" +
+ 'pm (INFO) 9244dcf1:\n' +
+ 'launching cto\n' +
'cto (RESULT, re f15cf912) 5f56e30d:\n' +
'on it\n'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:147:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "sent f15cf912 to cto\ncto isn't running, pm has been told\npm (INFO) 9244dcf1:\nlaunching cto\ncto (RESULT, re f15cf912) 5f56e30d:\non it\n",
expected: "sent f15cf912 to cto\ncto isn't running, pm has been told\ncto (RESULT, re f15cf912) 5f56e30d:\non it\n",
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,59 @@
✔ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (140.435947ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1064.919362ms)
✔ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2089.02205ms)
✖ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (65.218102ms)
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (127.913466ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (47.996606ms)
✔ launches off and on go to the broker and change the business's launch state (54.711986ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (45.47145ms)
ℹ tests 8
ℹ suites 0
ℹ pass 6
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 3728.268613
✖ failing tests:
test at tests/verbs.test.mjs:99:1
✖ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (65.218102ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ ''
- "cto isn't running, and the business names no launch.by\n"
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:109:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '',
expected: "cto isn't running, and the business names no launch.by\n",
operator: 'strictEqual',
diff: 'simple'
}
test at tests/verbs.test.mjs:123:1
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (127.913466ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 'sent dbd10b6c to cto\ncto (RESULT, re dbd10b6c) bf8a3623:\non it\n'
- 'sent dbd10b6c to cto\n' +
- "cto isn't running, pm has been told\n" +
- 'cto (RESULT, re dbd10b6c) bf8a3623:\n' +
- 'on it\n'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:147:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'sent dbd10b6c to cto\ncto (RESULT, re dbd10b6c) bf8a3623:\non it\n',
expected: "sent dbd10b6c to cto\ncto isn't running, pm has been told\ncto (RESULT, re dbd10b6c) bf8a3623:\non it\n",
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,41 @@
✔ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (118.78593ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1057.049691ms)
✔ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2097.311374ms)
✔ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (210.456924ms)
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (131.942575ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (46.679533ms)
✔ launches off and on go to the broker and change the business's launch state (55.369609ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (47.506821ms)
ℹ tests 8
ℹ suites 0
ℹ pass 7
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 3854.040573
✖ failing tests:
test at tests/verbs.test.mjs:123:1
✖ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (131.942575ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
'sent 8da5c43f to cto\n' +
"cto isn't running, pm has been told\n" +
+ "cto isn't running, pm has been told\n" +
+ "cto isn't running, pm has been told\n" +
'cto (RESULT, re 8da5c43f) e0d0497e:\n' +
'on it\n'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r151/wt/packages/cli/tests/verbs.test.mjs:147:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "sent 8da5c43f to cto\ncto isn't running, pm has been told\ncto isn't running, pm has been told\ncto isn't running, pm has been told\ncto (RESULT, re 8da5c43f) e0d0497e:\non it\n",
expected: "sent 8da5c43f to cto\ncto isn't running, pm has been told\ncto (RESULT, re 8da5c43f) e0d0497e:\non it\n",
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,25 @@
05202775dd9fb4566e7d977467beb4bc02cdbeff19800a5800b61d2d8d362c6e ./BUILD.md
f8e07a482fdf9ef493695ab6deffb296247f808e93510ea8de8618f770b4bde7 ./base.txt
51065038dce8fdfb59745c82a1553d8db0376e79001941271e56ad52e082906f ./build.patch
cb831847239e9ea60bd77ed8c765dafbf607a03cadeed3d0fc8d8284846878ed ./candidate-manifest.sha256
8636de52c30c165c7ae3185ebda90db6031f04715d6626639208211b4ad51cea ./gate-run.log
e403e2002984b207c1e95580aa9633b8d4f133d7654ca0c38ea4a577f2040064 ./gate.sh
faf8ec1da03477575f9ff882724f2d5cf982face0edf3061ebbd4acf35138fec ./gate/pkg-bus.log
0324a6d7cc4c19b9a1d1a33528a0f31a145940691d89363ec15ee44040e56192 ./gate/pkg-cli.log
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 ./gate/test-auth.log
bac0e22a2527e95c2e533937e5dd9c790f67efea53e37e2f0e96f5231e9a286c ./gate/test-conductor.log
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 ./gate/test-config.log
87d2e24ce0be7586e31ea1f2c9ce5618b3127515b3c3cd3893f2a0ed899d5822 ./gate/test-discord.log
827dfb83d0b2864e8705f317bab0c7082d863f522108a6dd86533c2317737f74 ./gate/test-extension-package.log
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f ./gate/test-foundation.log
8e2254aa3e1bb9b205ab72c68273d447c0328c9f156ccba2a46f3a0476468b58 ./gate/test-land-work.log
400e651550c42a8580e572b31c4096aa779d34ee3c1eae185d77b854f926ea81 ./gate/test-queue.log
fa832794ee35025fb4559d58d01a270f6bff2eb65e863e9075b797f47fa68545 ./gate/test-release.log
ccea37f4ffde1f160dcd1a1864988083a645d974e892065c956b338d0f8c853a ./gate/test-task.log
785da1cb9963a1bec9ee926b37d151df3c0681fe351fa7ae6fbcf9c40335292c ./mutants-run.log
4cdf6bc76a9b2e7a19c83864a2d384c1605a6fe1830f9de9ad31268c3c73a0e1 ./mutants.py
99a47ce7a736301380ac59b0fbc04185c1533f6a7eee9d7d34e29e86643bb953 ./mutants/B1-notice-routed-to-target.log
36a4ab87bffc837a3bf1fdc3b8394c63068147e5af5fe0757c43a7ff194cbe08 ./mutants/C1-no-reply-filter.log
824dd2f25605b7756a81961f68dac5d12eac125b4e223cd0b23c68706a902e1a ./mutants/C2-no-waiting-lines.log
7eadad60aa4b8b4367df0452b0174101aa6f9a763f9c3ea7a0e7ed60b8ba28e9 ./mutants/C3-waiting-every-poll.log
bae1a9f6c74fcb7461e0e0b5e277b86383ca1f25fea0b2ff1d57fb00c8c677ce ./repro.sh
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Row 151: apply build.patch to a detached worktree at base.txt and check the candidate manifest.
# Usage: repro.sh <canonical checkout> <new worktree path>
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
REPO=$1 WT=$2
git -C "$REPO" worktree add -q --detach "$WT" "$(cat "$HERE/base.txt")"
git -C "$WT" apply "$HERE/build.patch"
(cd "$WT" && sha256sum -c --quiet "$HERE/candidate-manifest.sha256") && echo DIGESTS_OK