Compare commits

...
Author SHA1 Message Date
jason.woltje d4ec998cce launchpad: row 200 packet, N2 guard kept and mutant 12 ruling (sage-1446) 2026-10-20 15:29:41 -05:00
jason.woltje e3fd9f3a08 stack-release: round 2, slot and proof checks resolve REPO, config_checks refuses a config that doesn't parse (row 200, #1698, sage-1432) 2026-10-20 15:25:04 -05:00
jason.woltje d163cfccb5 launchpad: row 200 packet, webby-0028 notes N1-N4 (#1698) 2026-10-20 15:16:08 -05:00
jason.woltje 39b52b71f5 launchpad: row 200 packet, BUILD.md, mutants, base and manifest (#1698) 2026-10-20 15:09:17 -05:00
jason.woltje 5dc0b5534b stack-release: activate --sso, its refusals before any stop, and the rollback to a Console without it (row 200, #1698)
activate --sso drops --public-host, --auth-header, --allow-user and any proof pair
and puts --sso before --send and --tester-port; --plan prints an sso: line. Before
any stop: sso-target, sso-config (the target's own checkConsole; from another copy
with no key it names the re-pin without --sso as the first step) and sso-slot
(lstat/stat/realpath only). A rollback from --sso to a Console without it takes the
flags from the arrival activation's rollback set (sage-1389) or refuses
sso-rollback-flags. Activate and rollback refuse config-console-key when the target's
loader refuses the config's console key. T56, TOOLS.md.
2026-10-20 15:08:19 -05:00
jason.woltje 4d487dfdc4 webui: Q175 phase 1 round 2, absolute post-sign-in Location checked on publicOrigin (row 192, #1690)
webby-0019 B1, sage-1403: returnURL builds the Location from publicOrigin plus the
parsed pathname and search, checks the origin again and returns the href, so a
dot-segment `//host` path stays on the Console. Test 14 covers Webby's three cases
and asserts each Location's origin; mutant 22 restores the r1 returnPath (killed).
N1 design blob cite, N2 listed under Deviations, N3 SIGN_IN_COOKIE comment.
2026-10-20 14:57:55 -05:00
jason.woltje 48359dbfaf webui: Q175 phase 1, the Console verifies sign-in itself under --sso (row 192, #1690)
The Console is an OIDC relying party on oauth4webapi 3.8.8 (pinned, root lock).
console.sso in config.json, client secrets from slot files, session cookie,
public Host only on the human listener, tester listener refuses every sign-in
header. Rulings sage-1340 F1/F2/F4 and sage-1347 F7/F8. Tests use a fake OP on
loopback only. Packet: agents/launchpad/work/queue-192/BUILD.md.
2026-10-20 14:35:52 -05:00
jason.woltje aa1bd209e3 gate-lane: row 189 gate timing table for row 197 (Herb) 2026-10-20 14:25:17 -05:00
jason.woltje ace27dc5eb row 189 (#1687): stack-release.sh activate --no-send drops the Console's --send (Sage; Dewey approve 29485; Sage gate passed) 2026-10-20 14:21:56 -05:00
jason.woltje a4a42dbac2 plans: stack-release --sso brief (#1698, Launchpad, reviewer Webby, row 192 F3; chained on 192) 2026-10-20 14:18:08 -05:00
jason.woltje a31f1a0f8b plans: row 198 brief records Mos's call, in-process board reads behind the Console's auth (mos-1019) 2026-10-20 14:17:03 -05:00
jason.woltje 992650232f plans: agent credentials and git identity brief (MVP T1 item 6, #1697, Megavolt, reviewer Duckworth); Megavolt seat dir 2026-10-20 14:14:33 -05:00
jason.woltje a7661ada06 plans: row 195 acceptance script takes Gizmoduck's gate item list; B8 issuer and TLS front end (mos-1011) 2026-10-20 14:12:19 -05:00
jason.woltje f871cb3096 plans: retire the control board writer on 7331 brief (#1696, Bushroot, reviewer Webby, mos-1008 item 2); Bushroot seat dir 2026-10-20 14:11:21 -05:00
jason.woltje 0bbd23c619 plans: row 195 brief adds the identity provider check and flags any new model account for Jason (mos-1008) 2026-10-20 14:10:28 -05:00
jason.woltje 0217b71c8f plans: row 195 brief rewritten as the hub on its own host, host left to Jason (#1693, mos-0998, mos-1005) 2026-10-20 14:07:57 -05:00
jason.woltje c6f528a32f plans: second gate lane brief (#1695, Herb, reviewer Webby, mos-0977 item 6); Herb seat dir; gate template and busy check copies for the row 2026-10-20 14:03:36 -05:00
jason.woltje 96f243fafc plans: human boundary row brief (P19 333, #1694, Gander, reviewer Duckworth, mos-0989); Gander seat dir; seat READMEs name the credentials row, not row 195 (now 190b) 2026-10-20 14:00:58 -05:00
jason.woltje c862699352 sessions: Filbert row 190 design review r3, approved 2026-10-20 14:00:05 -05:00
jason.woltje 48ebee0ffe plans: row 190 brief scopes isolation to addressing on one uid, points to row 190b (Filbert N7, mos-0986) 2026-10-20 13:58:07 -05:00
jason.woltje 66bdd84b8f sessions: Filbert row 190 design review r2, changes requested 2026-10-20 13:57:10 -05:00
jason.woltje 6966164f0c plans: row 190b brief, Melanie's hub under its own Linux user (#1693, Darkwing, reviewer Duckworth), mos-0986 2026-10-20 13:56:34 -05:00
jason.woltje 53bb594621 sage: common start for MVP T1 builder seats (mos-0974, mos-0977, mos-0981) 2026-10-20 13:55:47 -05:00
jason.woltje 5e01c36833 agents: seat dirs and roster rows for Launchpad, Morgana, Honker (Sage's T1 builders) and Webby, Duckworth (Mos's reviewers); row 193 brief takes Mos's B1 starting rule (mos-0985) 2026-10-20 13:52:28 -05:00
jason.woltje 9f56d2098d plans: rows 192 and 194 review by Webby, not Filbert or Dewey (mos-0981) 2026-10-20 13:51:25 -05:00
jason.woltje 0599b119f4 plans: MVP T1 briefs, SSO phase 1 (#1690, Launchpad), human users (#1691, Morgana), agent lifecycle (#1692, Honker), mos-0974/0977 2026-10-20 13:49:15 -05:00
jason.woltje b416d2ede9 sage: Q175 SSO design, Mos C1 condition (mos-0977): straggler check before Jason step 4 in the phase 2 window 2026-10-20 13:49:15 -05:00
jason.woltje 5fc73a729c revs 2237-2247: row 189 added, briefed, started, in-review round 1 (Dewey, comment 29457); rows 190 (Darkwing) and 191 (Rocko) added and briefed for MVP T1 (mos-0974); darkwing-190-start (rev 2245, Darkwing's) and rocko-191-start (rev 2244, made for Rocko) 2026-10-20 13:47:51 -05:00
jason.woltje 99772421ab sage: row 189 candidate manifest for Dewey's review (d6116771f) 2026-10-20 13:46:35 -05:00
jason.woltje da230ca489 plans: MVP T1 briefs, several instances per host (#1688, Darkwing) and the kill switch (#1689, Rocko), mos-0974 2026-10-20 13:42:33 -05:00
jason.woltje 4f2d1be68a sessions: Filbert Q175 SSO design D1/D2 ack 2026-10-20 13:41:20 -05:00
jason.woltje d55d86bb04 sage: Q175 SSO design r3 fix, Filbert r3 D1 (loader checks slot shape only) and D2 (slot from config dir, scratch Console dataRoot and unreachable issuer) 2026-10-20 13:40:49 -05:00
jason.woltje c56ccf916e sessions: Filbert Q175 SSO design review r3, approve with D1 2026-10-20 13:40:20 -05:00
jason.woltje 817e8f51b0 sage: Q175 SSO design r3 for #412, Filbert r2 C1 (shared loader, config.json inside the phase 2 window, rollback order, test 25) and C2 (401 signed-out page, test 24), notes and Q-E 2026-10-20 13:39:21 -05:00
jason.woltje 5d019d8768 sessions: Filbert Q175 SSO design review r2, changes 2026-10-20 13:34:53 -05:00
jason.woltje 59475fc5a8 sage: row 163 landing record, P19 comments 372/373 on 337/412, and P19 #413 filed for the --no-send row 2026-10-20 13:32:55 -05:00
jason.woltje 1a268249e6 sage: Q175 SSO design r2 for #412, Filbert r1 B1-B6, rulings Q-B/Q-C/Q-D, config in config.json (mos-0973) 2026-10-20 13:32:26 -05:00
jason.woltje def2e79327 plans: brief for stack-release activate --no-send (Q175 interim, mos-0972/0973, #1687, P19 413) 2026-10-20 13:21:30 -05:00
jason.woltje c30b36fffb sessions: Filbert Q175 SSO design review r1, changes 2026-10-20 13:19:06 -05:00
jason.woltje 9ae8c73acb queue: revs 2235-2236: row 163 Sage landing note (8ecc7a239, patch-id b8d5f8a37c24, gate passed on eaa0f2666) and done 2026-10-20 13:18:46 -05:00
jason.woltje 8ecc7a239d row 163 (#1660): stack-release.sh rehearse, the D2 rehearsal on a scratch root from a backup, and bus start --no-launch (Sage; Filbert approve 29447; Sage gate passed) 2026-10-20 13:17:07 -05:00
jason.woltje 7f57419b38 sage: Q175 SSO design for #412, OIDC relying party on oauth4webapi 3.8.8, for Filbert review (mos-0970, mos-0971) 2026-10-20 13:13:38 -05:00
jason.woltje 4ca8d6e949 sage: row 42 run log, S7 reachability pass (51cebff7 answered by cff1e9ad after a chief launch, mos-0969) 2026-10-20 12:54:16 -05:00
jason.woltje 22e5e825b0 sage: row 42 run log, plain mosaic on a pinned host supersedes the 10-12 full-path line (mos-0968) 2026-10-20 12:48:53 -05:00
jason.woltje e6dea74b22 sessions: Sage row 163 r2 approval intake; P19 337 comment 368 script and record 2026-10-20 10:42:00 -05:00
jason.woltje cd2b377bbf queue: rev 2234: row 163 r2 approve from Filbert (op filbert-163-r2, comment 29447, candidate c6f4f4c12596) 2026-10-20 10:41:33 -05:00
jason.woltje ceccb0ef26 sessions: Filbert row 163 r2 review, approve 2026-10-20 10:40:36 -05:00
jason.woltje 10ab8ea624 sessions: Sage row 163 r2 in-review; P19 337 comment 367 script and record 2026-10-20 10:26:33 -05:00
jason.woltje c539995cf4 queue: revs 2231-2233: row 163 r2 in-review from Sage (ops sage-163-r2-start, sage-163-inreview-2, .outcome; request comment 29446, reviewer Filbert, manifest c6f4f4c12596) 2026-10-20 10:26:02 -05:00
jason.woltje 92cc6011c8 queue: rev 2230: row 163 r1 changes from Filbert (op filbert-163-r1, comment 29445, candidate 430212299beb) 2026-10-20 10:23:12 -05:00
jason.woltje 7af1665748 sage: row 163 r2 packet (candidate 2c090eefd on code 2a5f3a3a0, base 4f02486d5: diff, manifest c6f4f4c12596, gate passed, mutants K34-K41 killed) 2026-10-20 10:22:02 -05:00
jason.woltje 2244e8bafc sessions: Filbert row 163 r1 review, changes 2026-10-20 10:06:03 -05:00
jason.woltje bde0fdec9f sessions: Sage row 163 r1 in-review; P19 337 comment 366 script and record 2026-10-20 09:49:45 -05:00
jason.woltje def677bc76 queue: revs 2228-2229: row 163 r1 in-review from Sage (ops sage-163-inreview-1, .outcome; request comment 29444, reviewer Filbert, manifest 430212299beb) 2026-10-20 09:49:08 -05:00
jason.woltje 51f67f1f87 sage: row 163 packet (candidate 4be3f6aec on 580500699: diff, manifest 430212299beb, gate passed, mutants K1-K33 killed after four survivors got tests) 2026-10-20 09:44:55 -05:00
jason.woltje 5b298bd00b sessions: Dewey row 66 r1 (changes) and r2 (approve) reviews 2026-10-20 09:12:15 -05:00
jason.woltje 565bc32c2f queue: revs 2223-2227: row 66 r2 round from Sage (2223-2225, ops sage-r66-r2-start, sage-r66-inreview-2); r2 verdicts from Filbert (2226, op filbert-66-r2, approve) and Dewey (2227, op dewey-66-r2, approve) 2026-10-20 09:12:10 -05:00
jason.woltje 5140aa9e88 sessions: Filbert row 66 r2 review, approve 2026-10-20 08:56:35 -05:00
jason.woltje 973cb5a872 queue: revs 2220-2222: row 163 Sage start (2220); row 66 r1 verdicts from Filbert (2221, op filbert-66-r1) and Dewey (2222, op dewey-66-r1) 2026-10-20 08:50:44 -05:00
jason.woltje b82ec93536 sage: row 66 r2 packet (candidate 0f4a57956: diff, manifest cee63e06fdea, gate, mutants K1-K4 killed, singles rerun) 2026-10-20 08:49:55 -05:00
jason.woltje 3560c450a6 sessions: Filbert row 66 r1 review, changes 2026-10-20 08:18:38 -05:00
jason.woltje 4f02486d56 sage: row 66 r1 packet (candidate 8ec03b45a: diff, manifest e68c6bce5ec4, gate r2), row 42 run log (trigger (a), mos-0960), SESSIONS line 2026-10-20 08:14:25 -05:00
jason.woltje 263ea8a9a3 queue: revs 2215-2219: row 66 Sage start (2215), in-review round 1 on candidate manifest e68c6bce5ec4 (8ec03b45a) and request comment 29437 (2216-2217); row 163 brief re-pinned to b1500d96ad (6020bb43a) and briefed (2218-2219) 2026-10-20 08:14:11 -05:00
jason.woltje 6020bb43ac plans: D2 brief owner is sage (row 163, #1660), and D2 lands after the Part B window (mos-0958, mos-0959) 2026-10-20 08:12:39 -05:00
jason.woltje 5487ed39d1 sage: row 188 landing record: P19 411 (N1) and comments 361-363 script and record, 409/410 description restore after a partial done update, SESSIONS line 2026-10-20 06:14:11 -05:00
jason.woltje 31e3289580 queue: revs 2209-2214: row 188 Darkwing start, in-review and request 29432 (2209-2211); Filbert approve 29433 (2212); Sage landing note (8fc0ee216, patch-id c9a56b167c07, stack-release.sh blob 2586a2a37c, gate passed) and done (2213-2214) 2026-10-20 06:12:21 -05:00
jason.woltje 8fc0ee2162 row 188 (#1686, P19 409, 410): stack-release T54, the proof file checks on a carried pair and a proof file inside the repository (Darkwing; Filbert approve 29433; Sage gate passed) 2026-10-20 06:08:42 -05:00
jason.woltje 23a609c5ce sessions: Filbert row 188 r1 review, approve 2026-10-20 06:08:34 -05:00
jason.woltje b9c1c9fe22 sage: Part B draft 2 A2 passed (mos-0958), row 188 launched, Vikunja comments 358-360 script and record, SESSIONS line 2026-10-20 05:36:55 -05:00
jason.woltje 525990ecb5 queue: revs 2207-2208: row 188 add and briefed (Sage), #1686, P19 409/410, gates Part B (mos-0956) 2026-10-20 05:35:36 -05:00
jason.woltje 148a9503d8 sage: Part B window file draft 2 for mos-0957 (F1: B6 and B-R1 name the stop's endLaunch rows, PB5 counts per step; F2: PB6, PB7 and B-R1 matrices run (a)-(h)); Q2 answered, Q1 to Jason's list 2026-10-20 05:34:25 -05:00
jason.woltje 240f21650f brief: stack-release tests for a carried proof pair and a proof file inside the repository (P19 409, 410, #1686, mos-0956) 2026-10-20 05:31:21 -05:00
jason.woltje 742e989766 sage: Part B window file, the read window's end stamp from the clock (10:29Z, not 10:40Z) 2026-10-20 05:29:51 -05:00
jason.woltje 288ab1b5a7 sage: Part B window file for Mos's A2 check (live-copy B3 and B-R1, Q1 on J2 order), SESSIONS lines for Darkwing (row 187, 10:25:22Z) and Sage 2026-10-20 05:29:37 -05:00
jason.woltje a843ae284e sage: row 187 landing, P19 407-410 filed and P19 385 comment 357, #1685 closed (comment 29425), SESSIONS line 2026-10-20 05:24:32 -05:00
jason.woltje 652d6f3a85 queue: revs 2201-2206: row 187 Darkwing start, in-review and request comment 29423 (2201-2203); Filbert approve comment 29424 (2204); landing note (9a71e4e80, patch-id 6b57c47bc661, gate passed) and done (Sage 2205-2206) 2026-10-20 05:24:01 -05:00
darkwingandjason.woltje 9a71e4e801 row 187 (#1685): activate sets, carries and drops the Console's proof pair
Row 178 gave the Console --proof-header NAME --proof-file PATH, but on the pinned stack only
activate --tester-port could write a new Console flag into the pointer. A hand install of the pair
would make every later activate, pm-on and rollback refuse unit-flags-drift. activate now takes
the pair, which replaces any pair in the pointer, and --no-proof, which drops it. The pair goes
after the preview flags and before --send and --tester-port, the order webui-service.sh render
writes and flags_drift compares. With neither flag, a target Console that takes --proof-header
carries the pair; a rollback to one that doesn't drops it and says so, and an activate to one
refuses proof-target unless --no-proof is given.

An activate may name the pointer's copy only when it sets a pair or drops one with --no-proof, so
Part B's B3 and its corrected rollback line B-R1 (activate <live copy> --pm --no-proof) can run;
every other activate of the live copy still refuses already-active. Before anything stops, the run
refuses proof-needs-preview, proof-header-invalid (proofOptions' own rules) and proof-file-invalid:
not absolute, a character the unit can't carry, a symlink, missing, not a regular file, inside the
repository or the copies, mode other than 600 or 400, or another owner. The file gets lstat, stat
and realpath only; the script never opens it and no line it writes holds the value. --plan prints
one proof: line. docs/TOOLS.md "Pinned stack update" lists the flags and describes the pair.

stack-release.test.mjs adds T53, eleven subtests. Each ends with a scan of every written file and
output for the proof file's sentinel and an inotify watch that counts no open or read of it. The
five brief mutants and Darkwing's two are killed. Filbert's M8 (a carried pair skips the checks)
and M9 (the repository case dropped) survive T53; both are follow-ups on P19, with a stale comment
in proof_for_target and the stale row 178 paragraph in docs/TOOLS.md.

This is step A0 of mos-0928 Part B and builds the tool only; nothing turns the proof on for the
live Console. Filbert approved round 1 on #1685.
2026-10-20 05:22:10 -05:00
jason.woltje d17328a555 sessions: Filbert row 187 r1 review, approve 2026-10-20 05:13:44 -05:00
jason.woltje f81ebfbae9 sage: row 186 follow-ups filed as P19 405 and 406, P19 404 closed (mos-0955) 2026-10-20 04:15:29 -05:00
jason.woltje 9760d4967a sage: row 186 landing, P19 404 comment 354 and SESSIONS line, with Darkwing's row 186 SESSIONS line (09:13:21Z) 2026-10-20 04:13:27 -05:00
jason.woltje 16a936c79b queue: revs 2198-2200: row 186 Filbert approve comment 29419 (2198); landing note (269134d8d, patch-id c97b9f6e25cd, gate passed) and done (Sage 2199-2200) 2026-10-20 04:11:59 -05:00
darkwingandjason.woltje 269134d8dd row 186 (#1684): the activate plan's backup line says what the run will do
activate --plan printed the backup line whenever the store file existed. But run_steps rolls back
with store-newer or migration-needs-flag before backup_store, so in those two cases the plan said
both that the run rolls back and that it backs up the store. The plan now prints
"backup: none; the run rolls back before the backup" there. The no-store line comes first, since
with no store file the run skips the whole store block. When S_CHANGED is set the plan keeps the
backup line: a wrong backup line costs nothing, a wrong none line hides the directory a --restore
needs. run_steps, backup_store, refusals, exit codes and flags are unchanged; the diff to
stack-release.sh is inside plan only. docs/TOOLS.md "Pinned stack update" step 3 names the
backup line. These are Filbert's N1 and N2 on row 185 (P19 404).

stack-release.test.mjs updates T26's not-given case and adds T52, six activate cases that plan,
check the one backup line and that nothing was written, then run and check the backup exists
exactly when the plan said so. The three brief mutants are killed, and so is Filbert's M7 (an
S_CHANGED read counted as early, by T29). Filbert's M6 (the early test ahead of the no-store test)
survives, since no test plans a missing store with an out-of-chain pointer schema; that is a
bounded follow-up with the store-unclean reading, on P19 404.

Filbert approved round 1 in #1684 comment 29419, with two non-blocking notes.
2026-10-20 04:10:22 -05:00
jason.woltje 3c5da3a914 sessions: Filbert row 186 r1 review, approve 2026-10-20 03:59:58 -05:00
jason.woltje b367f5730a sage: rows 186-187 Vikunja comments (P19 404 351, P19 385 352-353) and SESSIONS line (mos-0954) 2026-10-20 03:45:27 -05:00
jason.woltje 5a768d82c6 queue: row 187 added and briefed (Part B A0, #1685), with Darkwing's row 186 start (rev 2193) 2026-10-20 03:43:59 -05:00
jason.woltje 6de3c86beb plans: brief for row 187, activate carries the row 178 proof pair (mos-0928 Part B A0, #1685) 2026-10-20 03:42:11 -05:00
jason.woltje 1d7fd75e23 queue: row 186 added and briefed (P19 404, #1684) 2026-10-20 03:39:33 -05:00
jason.woltje f5c72a5c3c plans: brief for row 186, the activate plan's backup line (P19 404, #1684) 2026-10-20 03:38:06 -05:00
jason.woltje b724b7b0bc sessions: Sage Part A window line, with Darkwing's 06:53:53Z and 08:28:01Z lines 2026-10-20 03:32:53 -05:00
jason.woltje e24a13ad87 sage: Vikunja scripts and read-back records 2026-10-13 to 10-20 (decision 110, mos-0882)
102 files under agents/sage/work/vikunja-setup/: the comment, close and file scripts Sage ran on P19 and their
output records, including P19 386 comment 350 for the mos-0928 Part A window (rows 177 and 180-185). Each script
refuses to write a record that holds a key; a scan for both key values found none.
2026-10-20 03:32:46 -05:00
jason.woltje 21aa1cab0b darkwing: re-pin window rows 177-185 packet, STATE.md (sha256 b586f11c9b6f1329), W.txt (edd4622c5c45ef3f), S1/S2/S2b/B snapshots, matrices, W10 browser check and P3 rehearsals (#1683; P19 386)
NEW 490e2771d live for mosaic-stack, activation 20261020T075800Z-490e2771d3d0, store 3i. W0-W10 pass;
stops at W9 (assistant idle stop, mos-0948) and S2b/B (old chief end rows in the backup, mos-0950).
2026-10-20 03:27:30 -05:00
jason.woltje 9ddd8a92c9 plans: repin-rows-177-185 draft 4, R1 snapshot after the plain rollback (mos-0943)
The rollback's own bus start launches a real chief on OLD, since the pointer keeps pm true, so the
store after the rollback can't equal S2 plus tester refusals. R1 (snapshot.mjs --held) now runs right
after the rollback passes: S2's rows unchanged, new rows only the PM launch's, listed by table. R1post
compares against R1 and expects only the tester action.refused rows for the refused reads.
2026-10-20 02:51:40 -05:00
jason.woltje e7637b99c9 plans: repin-rows-177-185 draft 3, R1post expects one tester action.refused per refused read (mos-0935, mos-0942)
P3's limits rerun and two proofs (lim-20261020T074644Z, 074854Z, 074916Z) show OLD's tester limits
400 came from the emulated chief session with no provider (launch.mjs usageCounts), on both copies.
The rollback section now says what R1post expects: no limits refusal on live, one action.refused
(actor tester) per refused read such as replies with an unknown id, none per 200. W11 lists the three
P3 scratch deviations.
2026-10-20 02:50:37 -05:00
jason.woltje 2ea05700bf plans: mos-0928 plan draft 2, NEW is the row 185 landing 490e2771d
NEW moves from the 9f5153deb candidate to 490e2771d, which lands row 185 (#1683), 52 commits past
89af38a83. The tool blob at NEW is f8051d81cc. P5 now checks that REPO's four service scripts
match NEW instead of HEAD equal to NEW, since record commits after NEW move HEAD. Sage's SESSIONS
line for the row 185 landing.
2026-10-20 02:14:16 -05:00
jason.woltje 0106660742 queue: revs 2185-2190: row 185 start and in-review on bf87dbba with request comment 29403 (Darkwing 2185-2187); Filbert approve comment 29404 (2188); landing note (490e2771d, patch-id 774cd23186d5, gate passed) and done (Sage 2189-2190) 2026-10-20 02:11:42 -05:00
darkwingandjason.woltje 490e2771d3 row 185 (#1683): every activate backs up the stopped store
stack-release.sh activate now backs up the stopped store under its activation id on every run
with a store file, before any migration. Before this, backup_store ran only when the schemas
differed, so a same-schema activate took no backup and rollback --restore had nothing to name.
store-newer and migration-needs-flag still roll back before the backup, since those runs never
touch the store, and a missing store takes no backup.

BACKUP and TAKEN now mean two things. BACKUP is set by every backup, so the log line names it and
from_log backup finds it. TAKEN is set only right before a migration, so a gate failure after a
same-schema activate puts back the units and the wrapper and leaves the store as the failed run
left it. activate --plan prints a backup line, or "backup: none" with no store file. Rollback and
pm-on are unchanged; a same-schema rollback --restore already works through the row 181 guard.
docs/TOOLS.md "Pinned stack update" steps 5-7 say so.

stack-release.test.mjs updates T5, T11, T26 and T29 and adds T47-T51, with one fixture fault
(start-writes-row-<copy>). The three brief mutants are killed, and Filbert's two more (the backup
ahead of the schema checks, the plan line on a rollback) are killed by T6, T9 and T50.

This is the precondition for mos-0928 Part A, the re-pin from 89af38a83.

Filbert approved round 1 in #1683 comment 29404, with two non-blocking notes.
2026-10-20 02:09:49 -05:00
jason.woltje 6c88bc666f sessions: Filbert row 185 r1 review, approve 2026-10-20 02:09:31 -05:00
jason.woltje b5d662cabe plans: mos-0929 rulings on the mos-0928 re-pin plan (Q1-Q3, Part B list) 2026-10-20 01:37:34 -05:00
jason.woltje 52ce57dfe2 sessions: Sage mos-0928 plan, row 185 filed and briefed 2026-10-20 01:35:58 -05:00
jason.woltje f381869439 queue: revs 2183-2184: row 185 (#1683) added and briefed (Sage) 2026-10-20 01:35:50 -05:00
jason.woltje 10e1bb4950 plans: row 185 brief, every activate backs up the stopped store (#1683); mos-0928 window plan
A same-schema activate takes no store backup: backup_store runs only when
the schemas differ, and the two live backups (3f, 3h) don't match 3i, so
rollback --restore refuses both. Row 185 makes every activate back up the
stopped store before any migration and keeps the in-run restore
migration-only. Part A of the mos-0928 plan (re-pin 89af38a83 to the
refactor head) needs it first. Part B, the row 178 proxy proof
activation, is drafted for Jason and runs nothing.
2026-10-20 01:32:18 -05:00
jason.woltje 9f5153deb2 sessions: Dewey row 184 rebase and in-review; Sage mos-0870 assignment
Sweeps Dewey's uncommitted SESSIONS line for the row 184 rebase and in-review, and adds
Sage's line for the mos-0870 assignment (rows 177, 178, 180 and 181 and follow-ups 182-184).
Additions only. No existing line changes.
2026-10-20 01:14:37 -05:00
jason.woltje 055ca6938b queue: revs 2178-2182: row 184 in-review on e7e2c763 and request comment 29395 (Dewey 2178-2179); Filbert approve comment 29396 (2180); landing note (87aca4cb8, patch-id ad846db177b3, gate passed) and done (Sage 2181-2182) 2026-10-20 01:13:54 -05:00
deweyandjason.woltje 87aca4cb8d row 184 (#1682): tester thread verbs in the CLI
mosaic thread create|open|read and threads take --tester and run as the tester through
tester-cli.mjs. talk --thread and follow --thread take --tester too, and the row 177 refusal of
--thread with --tester is gone. The bus already checks thread ownership (thread-not-owned), so
the CLI adds no rule of its own, and every verb it sends as the tester is in TESTER_VERBS. follow
already receives with message.receive, so a tester reply arrives without polling inbox.
docs/TOOLS.md and packages/cli/README.md list the new flags.

The new tester-threads.test.mjs runs an in-process Broker. It covers create, list, open, send,
read and follow on a tester thread; thread-not-owned on a human thread both ways; and the human
forms unchanged with no tester transport call. A real-process test runs scripts/mosaic as the
tester. thread-cli.test.mjs and tester-reads.test.mjs drop the argvs that pinned the old
refusal. Brief mutants (talk sent as the human, follow without the thread filter) are killed.

Filbert approved round 1 in #1682 comment 29396 with no findings or notes. Their run on 3da362982,
with row 182 in the broker, added one more mutant (no thread read before the first receive in
follow), and it was killed.
2026-10-20 01:12:07 -05:00
jason.woltje dd7656cbf3 sessions: Filbert row 184 r1 review, approve 2026-10-20 01:03:26 -05:00
jason.woltje 3da3629829 queue: revs 2173-2177: row 178 landing note (b2b33aee5, patch-id 88e56a8a3afd, gate passed) and done (Sage 2173, 2175); row 182 Filbert approve comment 29388 (2174), landing note (33ab5eeb9, patch-id fa9a8988ebfc, gate passed) and done (Sage 2176-2177) 2026-10-20 00:52:54 -05:00
jason.woltje ef09f8b3f9 sessions: Darkwing rows 180-182 and Rocko rows 138-178 pending lines
Sweeps lines the seats appended but didn't commit: Darkwing's SESSIONS lines for rows 180, 181
and 182, and Rocko's SESSIONS and BUILD-LOG lines for rows 138, 164, 171, 176 and 178. Additions
only, as the seats wrote them. No existing line changes.
2026-10-20 00:49:34 -05:00
darkwingandjason.woltje 33ab5eeb91 row 182 (#1680): a tester message reaches only a thread the tester owns
#threadFor puts a message to a threaded instance in the thread found up its in_reply_to chain. It
checked the thread's instance but not its owner, so a tester reply to a message in a human's thread
landed in the human's thread. A new #testerOwns check refuses thread-not-owned when the sender is the
tester and the thread's owner isn't. It runs on both the thread branch and the row 143 spec-notice
branch. A role's reply into a tester's thread is unchanged, since that's how the answer comes back.
The bus README says so in its row 177 paragraph.

tester-threads.test.mjs adds four T9 tests: a tester reply into the human's thread refuses and
stores nothing, one into its own thread lands, the chief's reply into a tester thread lands, and a
tester reply to a spec notice refuses while the chief's relay still lands. messaging.test.mjs:203
used to send as the tester into a human thread. It now sends from a tester-owned thread.

Filbert approved round 1 in #1680 comment 29388. Their notes N1 (thread-instance-mismatch is
checked first, as before) and N2 (BUILD.md missed project-runtime.test.mjs:13 as a text reader of
broker.mjs, now corrected) need no change.
2026-10-20 00:47:49 -05:00
jason.woltje 7fc98cf190 sessions: Filbert row 182 r1 review, approve 2026-10-20 00:37:53 -05:00
rockoandjason.woltje b2b33aee55 row 178 (#1676, P19 385): the Console checks a proxy proof header on public Hosts
webui serve takes --proof-header NAME --proof-file PATH, only with the --public-host preview flags.
Before listen it reads the file once through O_NOFOLLOW and fstat: a regular file owned by the
Console uid, mode 0400 or 0600, at least 32 base64url bytes with one optional final LF. Any file
problem gives one fixed refusal that names neither the path nor the contents. The header name can't
collide with Host, Origin, auth or tester names.

On a public Host the guard runs before any auth header is read. It needs exactly one proof header
and compares equal-length buffers with timingSafeEqual. Missing, wrong and repeated proofs get the
same fixed 401. A proof header on another Host or on the tester listener gets 403. The existing
auth, Origin, Fetch Metadata and human write checks are unchanged.

webui-service.sh renders both flags and checks the file's metadata and canonical location without
opening it, and the send-status regex accepts the proof pair. stack-release.sh only gains a comment:
it already carries Console flags through re-pin and rollback, and new tests pin that.

This is a partial fix. Same-uid seats can still read the proof file. Activation (middleware, unit
flags, restart, smoke) is a separate Mos-checked window and isn't part of this commit.

Dewey approved round 2 in #1676 comment 29386, candidate 9300a670. Round 1 changes were in 29374.
2026-10-20 00:37:08 -05:00
dewey 3cfa333e43 sessions: Dewey row 178 r2 review, row 184 build 2026-10-20 00:28:49 -05:00
jason.woltje 02482094a9 queue: revs 2154-2172: rows 182 and 184 briefs, reviewers filbert, owners Darkwing and Dewey, briefed (Sage 2154-2161); 182 start (Darkwing 2162), 184 start (Dewey 2163); row 183 landing note (d1272e714, patch-id af1cad0940dc) and done (Sage 2164-2165); row 178 r2 in-review (Rocko 2166-2167), review request comment 29384 (Sage 2168-2169), Dewey approve comment 29386 (2170); row 182 in-review, request comment 29387 (Darkwing 2171-2172) 2026-10-20 00:28:03 -05:00
Researcher 74ce7061d6 sessions: Researcher row 183 build, review and landing 2026-10-20 00:05:52 -05:00
Researcherandjason.woltje d1272e714f row 183 (#1681): chat.mjs names the transport it is given
The chat.mjs header comment now says the seam takes the bus transport it is given: the human one
from the signed-in listener, the tester one from the row 176 listener (row 177). The TypeError for a
missing or non-function call says 'chat needs a bus transport function' and no longer says human.
No behavior changes. The webui README doesn't repeat either phrase and is unchanged.

A new chat.test.mjs test passes undefined, null, a string, a number, an object, an array and no call,
and reads the new TypeError text. Putting the old text back fails it.

Dewey approved round 1 in #1681 comment 29376. Their notes (the tester line names no file, and the
test title says both transports pass when only a stand-in function does) need no change.
2026-10-20 00:03:28 -05:00
jason.woltje 841c56d052 plans: rows 182 and 184 briefs set owners and reviewers 2026-10-19 23:55:44 -05:00
dewey 9a3f101b05 sessions: Dewey rows 180 r2, 178 r1, 183 r1 reviews 2026-10-19 23:55:06 -05:00
jason.woltje 931651f2de queue: revs 2144-2153: row 181 Filbert approve #1679 comment 29372 (2144), landing note (ef48597d0, patch-id 1fef78100554, gate passed) and done (Sage 2151-2152); row 178 Dewey changes comment 29374 (2145), reopened (Rocko 2146); row 183 in-review (Researcher 2147-2148), review request comment 29375 (Sage 2149-2150), Dewey approve comment 29376 (2153) 2026-10-19 23:53:20 -05:00
darkwingandjason.woltje ef48597d0e row 181 (#1679, P19 390): the restore guard counts every row a restore drops
COUNTED, next to STORE_JS, lists the 11 tables a bus writer fills without an event in the same
transaction (role_claims through usage_records, in schema order). STORE_JS count and heldcount
return events after STAMP plus each listed table after STAMP from the same open as before. A
listed table the store lacks counts 0, and one that exists but can't be read refuses
restore-mismatch.

rollback --restore refuses post-backup-events unless the total is 0 or --events equals it, and
the message names each table with rows. The in-run recount compares the same total. An
events-only store prints the old text. T46 reads every table in schema.sql and fails on a table
in no list or in two.

Filbert approved round 1 in #1679 comment 29372. Follow-ups F1 (task_comment_cursors) and F2
(the refusal text at stack-release.sh:909) are recorded in agents/darkwing/work/queue-181/BUILD.md.
2026-10-19 23:49:44 -05:00
jason.woltje d7184aeac1 sessions: Filbert row 181 r1 review, approve 2026-10-19 23:34:21 -05:00
jason.woltje cbe4c82657 queue: revs 2135-2143: row 181 round 1 in-review #1679 comment 29370 (Darkwing 2135-2136); rows 182-184 brief re-pins to 3459e01c33 (Sage 2137-2139); row 183 reviewer Dewey, owner Researcher, briefed (Sage 2140-2142), started (Researcher 2143) 2026-10-19 23:34:17 -05:00
jason.woltje 5a8c1340c4 plans: row 183 brief sets owner Researcher and reviewer Dewey 2026-10-19 23:26:14 -05:00
jason.woltje fcfb82da2c queue: revs 2129-2134: row 181 briefed (Sage 2129) and started (Darkwing 2130); row 178 round 1 in-review (Rocko 2131-2132) and review request #1676 comment 29368 (Sage 2133-2134) 2026-10-19 23:25:08 -05:00
jason.woltje 311705d5bd sessions: Sage rows 177 landing, 178 briefing, 180 landing, 181 briefing, 182-184 added 2026-10-19 22:56:34 -05:00
jason.woltje a757f8fe64 queue: revs 2127-2128: row 180 landing note (1905e385c, patch-id 31b1d502444e, gate passed) and done (Sage) 2026-10-19 22:54:47 -05:00
darkwingandjason.woltje 1905e385c4 row 180 (#1678, P19 391): activation ids within one second and plan-mode refusals
next_activation() takes the UTC second once and tries the base id, then -1 through -9, against
stack-rollback/, backups/stack/ and the post-attempt store, all under the lock. ACT and POST share
the suffix, and with all ten taken it refuses capture-failed with nothing stopped. Adopt uses the
same helper. --restore takes the old and the suffixed form.

Under --plan, a bad --tester-port reports tester-port-invalid through check_refuse and
tester-port-target goes through check_refuse in both modes, so the plan lists every refusal.
Without --plan the output is the usage block, byte for byte as before. webui-service.sh counts the
tester port's digits as written before stripping zeros, so 007333 refuses as serve does.

Round 2 adds subtests C1 (adopt takes the next free id) and C2 (a bad port is usage before any
pointer read) for Dewey's round 1 (#1678 comment 29352). Dewey approved round 2 in comment 29361.
2026-10-19 22:53:14 -05:00
jason.woltje 828d14a0c9 queue: revs 2113-2126: row 178 brief re-pin, owner Rocko, reviewer Dewey, briefed (Sage 2113-2114, 2116-2117), started (Rocko 2125); row 180 round 2 in-review and resolve (Darkwing 2115, 2118), Dewey approve #1678 comment 29361 (2126); rows 182-184 added with issues #1680-#1682 (Sage 2119-2124) 2026-10-19 22:44:11 -05:00
jason.woltje c7421cabda plans: row 178 brief sets owner Rocko and reviewer Dewey and orders its landing after rows 180/181; briefs for the three row 177 follow-ups (N1 tester thread owner check, N2 chat.mjs strings, tester CLI thread verbs) 2026-10-19 22:27:57 -05:00
dewey 895c7270cd sessions: Dewey row 177 round 2 build and row 180 round 1 review, changes 2026-10-19 22:24:37 -05:00
jason.woltje 7f519242e1 queue: revs 2103-2112: row 180 to in-review round 1 (Darkwing 2103-2104), Dewey changes #1678 comment 29352 (2109), round 2 start (Darkwing 2110); row 177 round 2 in-review #1675 comment 29350 (Dewey 2105-2107), Filbert approve 29351 (2108), landing note and done (Sage 2111-2112) 2026-10-19 22:24:02 -05:00
deweyandjason.woltje 9d9d95485a row 177 round 2 (#1675): a tester-thread run's decision.raise refuses tester-thread-human when it routes to the human; F2 dispatch comment, F3 tester-writes header and name 2026-10-19 22:21:55 -05:00
deweyandjason.woltje 1f994257a5 row 177 (#1675, Q173): tester threads
The tester gets threads of its own: thread.create, thread.open, thread.send, thread.messages and
assistant.stop join TESTER_VERBS, with owner `tester`, and neither the tester nor the human reaches
or lists the other's threads. A tester thread.send is from tester-cli with no human.input; in a
tester thread the lists read the tester as the human, the run answers at `tester`, and its send to
`human` refuses tester-thread-human. The tester stop revokes only tester threads, by `tester`. A
business whose human is `tester` refuses invalid-business at start.

The tester listener (7333) serves chat through the TESTER_CLI caller, its fence admitting only the
four chat write routes, with the listener's own Origin required; 7332's chat is unchanged. The
Console composer is enabled for the tester. READMEs state calls (a)-(c), N1, N2 and where a tester
reads replies (sage-1148).

Design 99ce5780 approved in sage-1146 (#1675 comment 29333).
2026-10-19 22:21:55 -05:00
jason.woltje a2eebf997e sessions: Filbert row 177 r2 review, approve 2026-10-19 22:14:19 -05:00
jason.woltje c0d6bc7d5c queue: rev 2102: row 177 round 1 verdict changes, #1675 comment 29347 (Filbert) 2026-10-19 21:53:18 -05:00
jason.woltje 72c0e22ccf sessions: Filbert row 177 r1 review, changes 2026-10-19 21:47:17 -05:00
jason.woltje 9241a97388 sessions: Darkwing W5-W11, cleanup and packet push lines (33), Rocko (9) and Researcher (1) pending lines, and Sage after-window rows 180/181 2026-10-19 21:35:12 -05:00
jason.woltje a6a7d8a90f queue: revs 2096-2100: rows 180 (#1678, P19 391 + row 176 N1/N2, Darkwing, briefed) and 181 (#1679, P19 390 restore guard, after 180) added by Sage (2096-2098); row 177 to in-review round 1, request #1675 comment 29343 (Dewey 2099-2100) 2026-10-19 21:34:25 -05:00
jason.woltje c7f3beb171 docs: row B brief amended per mos-0927 R1: item 4 classifies every table in the target schema into one of four lists, item 1 records UPDATEs that do not move the time column (#1679) 2026-10-19 21:30:56 -05:00
jason.woltje 236af52710 docs: brief for two stack-release rows after the merged B+C window: row A (P19 391 activation ids, Dewey row 176 N1/N2), row B (P19 390 restore guard, after row A) 2026-10-19 21:29:19 -05:00
jason.woltje c5ae4d7ac4 darkwing: merged B+C window packet, STATE.md (sha256 7be42b6bcab5657d), W0-W11 transcripts, S1/S2/S2b snapshots, P3 rehearsals and snapshot.mjs checks (#1673, #1674; P19 386, 382)
Evidence for the STATE report Sage checked and filed in 88f3253d9 (sage-1151). The skills-enabled
tar stays out of git; it is the W0b restore point under $WIN only. No credential or Environment
values: unit reads record ExecStart only, and the P3 credential files were fake placeholders.
2026-10-19 21:26:49 -05:00
jason.woltje 88f3253d98 sage: merged B+C window STATE checked and filed (P19 386 c324, 382 c325 done, #1673 29334, #1674 29335); #394 doc fixes: carry-past-row64.md runs from REPO held to the pinned sha, plan line 222 and W8 row amended (mos-0924)
carry-past-row64.md sha256 6799542cb23aba4c, repin-merged-bc.md 99f86c078d01663f (draft 9 was 523f20e03be8aa11). BUILD-LOG phase and SESSIONS line.
2026-10-19 21:23:57 -05:00
dewey ac1e16a99a sessions: Dewey row 177 design sent to Sage 2026-10-19 20:45:24 -05:00
jason.woltje e9aa33a77b sessions: Sage merged B+C plan draft 9, W0 go, row 177 start 2026-10-19 20:32:26 -05:00
jason.woltje c49de08e86 sage: merged B+C window plan draft 9 (sha256 523f20e03be8aa11), Mos's formal check closed with the W0 go (mos-0907); A1-A4 text fixes to draft 8 (mos-0906) 2026-10-19 20:31:36 -05:00
jason.woltje d3996adea7 queue: revs 2093-2094: row 177 brief re-pinned to blob f146cd772 at dfd3e5823 and moved to briefed for Dewey (Sage 2093, 2094) 2026-10-19 20:31:29 -05:00
jason.woltje dfd3e5823c sage: row 177 brief re-pinned to 89af38a83 after rows 175 and 176 moved broker.mjs; point 6 names the tester listener's chat refusal (serve.mjs:442) and 7332's x-mosaic-tester refusal (:454) 2026-10-19 20:26:57 -05:00
jason.woltje b61335ad62 sessions, build-log: Sage row 176 gate, landing and records, merged window plan draft 8 2026-10-19 20:25:50 -05:00
jason.woltje 3faa30184c queue: revs 2083-2092: row 176 round 2 claimed and moved to in-review on candidate a4c728fc (Rocko 2083, 2085, 2086), Dewey's round 1 note and round 2 approval, comment 29322 (Dewey 2084, 2089), the round 2 review request posted as jarvis (Sage 2087, 2088), landing notes and the done move on 89af38a83 (Sage 2090-2092) 2026-10-19 20:22:37 -05:00
jason.woltje 89af38a83a webui, bus, scripts: a loopback-only Console listener for the tester; 7332 refuses x-mosaic-tester (row 176, #1674)
serve --tester-port N starts a second listener on 127.0.0.1:N in the same
process, with a bus caller through TESTER_CLI. N is 1024..65535, not
--port and never 7332, and the flag needs the bus configured. The tester
listener takes only instances.set, routes.set and name.set; settings
save, project-file save, assistant stop, bus message and chat writes
refuse 403 with no bus call. It refuses the main listener's auth header,
and every other listener refuses any request carrying x-mosaic-tester.
/api/config answers tester: true and the page shows a tester banner with
the routes, instances and seat name editors only. tester-off and
tester-required map to 403 in a serve.mjs coreBusStatus wrapper, so
bus.mjs stays pinned (row 164).

The broker lets a tester read credentials (with #secretCheck), routes,
project.files and project.file; project.file.save stays human-only.
webui-service.sh takes --tester-port with serve's checks. stack-release
activate --tester-port N writes the flag to the pointer and the unit,
refuses tester-port-target when the target copy's Console lacks it, and
activate or rollback to such a copy drops the flag and says so in --plan.
docs/TOOLS.md rewords the stale row 175 two-phase step (#1673 F1).

Built by Rocko on 473258a51 (manifest a4c728fc...52c9), approved by Dewey
in round 2 (#1674 comment 29322) after round 1 changes (29309), Sage's
gate green on 473258a51 plus the patch in run 2. Run 1 failed T34's
failed-pm_launched subtest with capture-failed: activate and pm-on name
the activation by the second, so the two ran in one second and collided
on the rollback set. That race is row 169's (the base fails the same way
with the id pinned), and a follow-up row fixes it (Vikunja P19 391).
2026-10-19 20:18:14 -05:00
dewey 06a82c9fb0 sessions: Dewey row 176 r2 review, approve (comment 29322) 2026-10-19 19:56:27 -05:00
dewey 548b61a5bd sessions: Dewey row 176 r1 review, changes (comment 29309) 2026-10-19 19:50:23 -05:00
jason.woltje 0a478dfc36 queue: revs 2079-2082: row 176 round 1 in-review on candidate b05cbf08 (Rocko 2079), the request failed without a token as expected (Rocko 2080), posted as jarvis, comment 29305 (Sage 2081, 2082) 2026-10-19 19:37:21 -05:00
jason.woltje 60d6e6755e queue: revs 2077-2078: row 176 claimed by Rocko on 473258a51 (Rocko 2077); busStatus ruling note, the tester-off and tester-required 403 goes in a serve.mjs coreBusStatus wrapper and bus.mjs stays pinned (Sage 2078) 2026-10-19 19:28:45 -05:00
jason.woltje 8e888014d4 log: row 175 landed (98d29769e), row 176 brief amended and launched, merged window drafted; sessions: Sage 2026-10-19 19:10:21 -05:00
jason.woltje 473258a51e queue: revs 2072-2076: row 175 round 3 approve on 2e30f2a1 (Filbert 2072, comment 29279), landing note and done after 98d29769e (Sage 2073, 2074); row 176 brief re-pinned to 0f7119af95 for the stack-release --tester-port pointer change and row 175 F1 (Sage 2075), queued to briefed (Sage 2076) 2026-10-19 19:08:55 -05:00
jason.woltje 2fecddfba5 sage: row 176 brief: activate --tester-port N writes the flag into the pointer's Console flags, a rollback or an activate to a copy whose Console lacks it drops it, tester-port-target refuses before anything stops; tests and three mutants; row 175 F1 (TOOLS.md step 2) rides along 2026-10-19 19:06:35 -05:00
jason.woltje 98d29769e2 bus, cli: tester reads and the W1-W7 tester writes, recorded as tester; scripts/mosaic sends plugins, name and names to the CLI (row 175, #1673)
TESTER_VERBS gains usage, names, limits, plugins, instances, project.list,
thread.list, name.set and project.create. A tester reads limits, plugins
and instances and sends plugins.set, instances.set and routes.set, each
logged with actor tester. The project file verbs refuse a tester
tester-refused. The broker and CLI drop the [mos test]
prefix rule (mos-0872 Q1). The CLI takes --tester on the seven reads and
the W1-W7 write forms and refuses it (exit 4) elsewhere, bus start and
stop included.

project init --tester checks the project area, refuses the area root
itself, binds the tester with one agents read, then commits the layout as
tester (mos-0872 Q2). scripts/mosaic sends plugins, name, names, follow,
threads and thread to packages/cli (sage-1081). The row 164 pins are
untouched.

Built by Dewey on 9e74925c2 (three commits to 989699f7c, manifest
2e30f2a1...29d5f), approved by Filbert in round 3 (#1673 comment 29279),
Sage's gate green on 716ddcf1e plus the patch.
2026-10-19 19:04:50 -05:00
jason.woltje 1d81c54ff1 sessions: Filbert row 175 r3 review, approve 2026-10-19 18:56:56 -05:00
jason.woltje 716ddcf1eb queue: revs 2058-2071: row 179 start, in-review and request (Darkwing 2058, 2060, 2061), issue #1677 (Sage 2059), r1 approve (Dewey 2062, comment 29271), landing note and done after 9e74925c2 (Sage 2063, 2064); row 175 r2 request on f9e41b918 (Dewey 2065-2067, comment 29275), back to in-progress to rebase onto 9e74925c2 (Sage 2068, 2069), round 3 request on the rebased candidate 2e30f2a1 (Dewey 2070, 2071, comment 29276) 2026-10-19 18:50:16 -05:00
dewey 0f45a3e6e3 sessions: Dewey row 175 round 2 to in-review on 989699f7c, row 179 approved 2026-10-19 18:49:15 -05:00
jason.woltje 9e74925c2a docs: TOOLS.md follow-ups F1-F3 from the row 169 review (row 179, #1677)
F1: pm-on-down stops the Console, so the paths section now says what each
path does to it. F2: the working `node <copy>/packages/cli/src/cli.mjs
plugins set` form and the PATCH shape, until row 175's fixed `mosaic
plugins set` lands. F3: the business-unreadable refusal on activate and
rollback.

Darkwing's candidate on ce5807989, manifest 2b161c51, approved by Dewey
(#1677 comment 29271). Doc-only: no test reads docs/TOOLS.md
(stack-release.test.mjs:428 names it in an expected message string), so
the approval is the gate (decision 137).
2026-10-19 18:42:31 -05:00
jason.woltje ce5807989a log: row 169 landed (264756a30), row 179 briefed, row 175 to round 2 (1bfc9833b) 2026-10-19 18:30:38 -05:00
jason.woltje 1bfc9833b1 queue: revs 2052-2057: row 179 added and briefed (Sage 2052, 2057), row 169 landing notes and done after 264756a30 (Sage 2053, 2054, 2056), row 175 r1 approve recorded (Filbert 2055, comment 29264) 2026-10-19 18:29:50 -05:00
dewey c98a5d17ae sessions: Dewey row 175 round 1 approved 2026-10-19 18:28:52 -05:00
jason.woltje 8f2ac5236c sessions: Filbert row 175 r1 review, approve 2026-10-19 18:27:47 -05:00
jason.woltje 264756a303 stack-release: activate --pm-after-harness, pm-on and the pending pointer, so a re-pin past c216f47db starts the PM after the harness kind is set (row 169, #1666)
activate ID BUSINESS --pm-after-harness installs the bus without --pm
and writes a pointer with pm false and pmPending true. It refuses
pm-after-harness-target when the target has no harness kind, and
pm-after-harness-set when the business file already sets plugins.harness.

pm-on BUSINESS refuses not-pending and harness-unset, then reinstalls the
bus with --pm on the pointer's copy and runs the full gate. A failure puts
the phase-1 unit back (pm-on-failed, exit 1), or stops both units
(pm-on-down, exit 2).

A plain activate refuses pm-pending. status prints the pending state and
the next step. activate and rollback refuse business-unreadable when the
target's code can't read the business file's plug-ins.

Built by Darkwing on 4b738e2da (patch-id 17a90c4ad795, manifest
c6aaac1f...54af), approved by Filbert (#1666 comment 29263), Sage's gate
green on e7d47c346 plus the patch.
2026-10-19 18:26:17 -05:00
jason.woltje b2a75f3189 sage: brief for the row 169 TOOLS.md follow-ups F1-F3 (Filbert, #1666 comment 29263), owner Darkwing, reviewer Dewey, after row 169 2026-10-19 18:19:33 -05:00
jason.woltje e7d47c3464 log: rows 169 and 175 in-review, row 169 r1 approve, queue revs 2046-2051 (cb4a8bf1f) 2026-10-19 18:17:36 -05:00
jason.woltje cb4a8bf1fa queue: revs 2046-2051: row 169 in-review round 1 (Darkwing 2046-2047), row 175 brief re-pin for the wrapper fix (Sage 2048), row 175 in-review round 1 on 7b27ab3f9 (Dewey 2049-2050), row 169 r1 approve recorded (Filbert 2051, comment 29263) 2026-10-19 18:17:12 -05:00
jason.woltje c7939a5680 sessions: Filbert row 169 r1 review, approve 2026-10-19 18:15:51 -05:00
dewey 423ff5c864 sessions: Dewey row 175 round 1 in-review 2026-10-19 18:12:42 -05:00
jason.woltje 3dfaea286e sage: row 175 brief carries the scripts/mosaic dispatch fix (plugins, name, names, follow, threads, thread go to packages/cli), test 12 and a mutant; found in Darkwing's row 169 rehearsal 2026-10-19 17:59:24 -05:00
jason.woltje d63cfca222 log: row 178 brief for mos-0881 (30622a229, c6ef51ee1) 2026-10-19 17:47:53 -05:00
jason.woltje c6ef51ee13 queue: row 178 re-pinned to the mos-0881 brief (rev 2045, Sage) 2026-10-19 17:47:40 -05:00
jason.woltje 30622a2290 sage: row 178 brief, the turn-on doesn't wait for Jason (mos-0881), with roll-back-at-once on a failed signed-in check; bc310ee pod-label part recorded 2026-10-19 17:46:39 -05:00
jason.woltje 47a351add8 log: rows 176 and 178 briefs revised for mos-0878 (80cabd935, c4e049319) 2026-10-19 17:45:34 -05:00
jason.woltje c4e0493195 queue: rows 176 and 178 re-pinned to the mos-0878 briefs, row 178 piece updated and after set to 169, 175, 176 (revs 2041-2044, Sage, mos-0878) 2026-10-19 17:45:04 -05:00
jason.woltje 80cabd935d sage: row 176 brief adds the credentials-body and project-root tests (mos-0878 Q1); row 178 brief rewritten for mos-0878 Q2: partial fix after the window rows, console-proof.token slot, ops-01 SealedSecret and middleware under R27, console-allowed-uid recorded as decision 101's uid and left alone 2026-10-19 17:42:48 -05:00
jason.woltje 0750b6a495 log: rows 176-178 filed (#1674-#1676, mos-0872, mos-0874), row 175 briefed and claimed 2026-10-19 17:35:06 -05:00
jason.woltje 58e1735357 queue: rows 176 (#1674), 177 (#1675) and 178 (#1676) added (revs 2038-2040, Sage, mos-0872, mos-0874) 2026-10-19 17:34:05 -05:00
jason.woltje 6ee7285c0d sage: briefs for rows 176 (#1674, Console tester listener on 127.0.0.1, 7332 refuses x-mosaic-tester, mos-0872/0874), 177 (#1675, tester threads, scoping) and 178 (#1676, Traefik proof for signed-in requests, Vikunja #385, mos-0874) 2026-10-19 17:32:16 -05:00
jason.woltje 835941b7c4 queue: row 175 widened to tester writes and briefed with no after (revs 2032-2036, Sage, mos-0870 N2, mos-0872); also Dewey's rev 2037 (dewey-175-claim, row 175 in-progress) 2026-10-19 17:29:55 -05:00
jason.woltje 1c90c68c53 sage: row 175 brief widened to tester reads and writes (#1673, Q173, mos-0870 N2, mos-0872 Q1-Q3): seven writes recorded as tester, nine human-only items, prefix lifted, project init --tester inside the data root's project area only 2026-10-19 17:22:54 -05:00
jason.woltje d195d171c6 log: row 166 landed (4b738e2da, #1663 closed), row 175 filed (#1673, mos-0868) 2026-10-19 17:09:36 -05:00
jason.woltje 6b8f945249 queue: row 166 done with landing note (revs 2029-2030), row 175 added after 169 (#1673, mos-0868, rev 2031); Sage revs only 2026-10-19 17:09:12 -05:00
jason.woltje f8ac674cc6 sage: brief for row 175 (#1673, --tester on the window reads, mos-0868); Window B doc after mos-0866 F1-F3 and mos-0868: W4 from the unit template, agents to Mos with --tester, Jason's reads in J1 and J2 2026-10-19 17:07:57 -05:00
jason.woltje 4b738e2da9 stack-release: refuse unreadable drop-ins and quoted units, recheck the bus after a held read, gate on the target copy (row 166, #1663)
drop_in_override refuses drop-in-override when a unit path, a drop-in
directory or a .conf exists but can't be read, as install_guard in
bus-service.sh would after the stop. A unit path that isn't absolute
refuses too.

plan_store and the rollback --restore event count check is-active again
after reading the store the running bus holds. If the bus stopped
meanwhile, --plan prints "store schema: unknown" with no migration lines,
and rollback refuses restore-mismatch before anything stops.

The gate's copy-check step compares the check's bus line with the copy
this run installed and names both when they differ.

read_units returns 1 when the bus Environment= line or either unit's
ExecStart argv holds a quote or a backslash, so activate and rollback
refuse unit-flags-drift, adopt refuses check-failed and status prints
that it cannot read the units. Nothing parses systemd quoting.

Tests T28 to T31 in packages/cli/tests/stack-release.test.mjs.
2026-10-19 16:55:18 -05:00
jason.woltje 64922106d5 sage: carry-past-row64 names Vikunja P19 #383, no pi window before the pi credential is settled (mos-0865 D2) 2026-10-19 16:36:49 -05:00
jason.woltje c7424db50e log: row 64 closed, D2 coder sessions on both harnesses, #1557 closed (mos-0863, mos-0864) 2026-10-19 16:34:10 -05:00
jason.woltje 5cabf919ee queue: row 64 done, landing note with D2 #1557 comment 29228 (mos-0864; Sage revs 2027-2028) 2026-10-19 16:33:03 -05:00
jason.woltje c87865ee0e sage: row 64 D2 evidence, scratch-root builder, driver, check and both coder manifests (mos-0863 D2, #1557 comment 29228) 2026-10-19 16:32:22 -05:00
jason.woltje 3510c3cbf8 queue: row 174 for #1672, skill read BOM and fs errors after row 173, not started (mos-0863 D4; Sage rev 2026) 2026-10-19 16:20:16 -05:00
jason.woltje 9c80ba7f17 plans: brief for #1672, skill reads keep a leading BOM and report fs errors as skill-refused (row 64 N2/N3, mos-0863 D4) 2026-10-19 16:18:46 -05:00
jason.woltje 46de608936 sage: window carry step for any pin past row 64, install and activate the seven role skills from the pinned copy (mos-0863 D3, #1557) 2026-10-19 16:18:46 -05:00
jason.woltje ebfc23c431 sage: row 64 packet, build notes, both gates, mutants and the landing manifest; BUILD.md item 5 corrected for Darkwing N1 (mos-0863 D5, #1557) 2026-10-19 16:18:46 -05:00
jason.woltje 6b6567b8b4 harness: README names bundle-refused for the skill content refusals, skill-refused at the launcher (row 64 F1, #1557 comment 29218) 2026-10-19 16:11:42 -05:00
jason.woltje 9f00397110 roles, harness, cli: a managed session gets its role's skills as prompt text on both harnesses (row 64, #1557)
Author and gate owner: Sage (mos-0855).

Role files take an optional skills list. The bundle reads each named skill
from <dataRoot>/skills-enabled, copies it into the bundle and renders every
file into prompt.md between skill-file markers, so the prompt text matches
the bundle copy byte for byte. The role's skills together are capped at
SKILL_BYTES (64 KiB): the claude adapter passes prompt.md as one argument,
and Linux caps one argument at 128 KiB. Symlinks are refused, not followed.
The launcher checks the list before a run exists: skill-not-enabled names
the activate command, anything else refuses skill-refused.

Pi keeps --no-skills; Claude Code keeps --disable-slash-commands and
--restricted. The two harness modules and MOSAIC_SKILLS are unchanged.
First lists: coder, reviewer, cto and pm, ms-unslop on each.
2026-10-19 16:11:36 -05:00
jason.woltje 1a01f63ead queue: row 64 round 1 approvals, Filbert rev 2024 and Darkwing rev 2025 on manifest 7689cd9b (#1557) 2026-10-19 16:10:42 -05:00
jason.woltje 7c81d5e9c1 sessions: Filbert row 64 r1 review, approve 2026-10-19 16:03:07 -05:00
jason.woltje 12ac2c428b sessions: Sage line for row 64 candidate, gate and in-review round 1 2026-10-19 15:58:19 -05:00
jason.woltje e76c73f725 queue: row 64 started and in-review round 1 on c6d7d538f, reviewers darkwing and filbert (#1557, Sage revs 2022-2023 and the start rev) 2026-10-19 15:58:06 -05:00
jason.woltje 087d202bc3 queue: row 173 for the Seen click intent gap after row 172 (#1671, mos-0861 A1; Sage rev 2021) 2026-10-19 15:44:18 -05:00
jason.woltje a0da43dab2 plans: brief for the Seen click intent gap after row 172 (#1671, mos-0861 A1) 2026-10-19 15:43:03 -05:00
jason.woltje b5d4d0f225 sessions: Sage line for the row 172 landing and close-out 2026-10-19 15:38:53 -05:00
jason.woltje c24fe5d0ff build-log: row 172 landing, the dropped Seen click and the row 165 reload race (#1670) 2026-10-19 15:36:23 -05:00
jason.woltje 83db85aa3a queue: row 172 round 1 Darkwing approve (darkwing rev 2018), landing note and done (Sage revs 2019-2020) 2026-10-19 15:35:49 -05:00
jason.woltje be10e0c838 webui: a Seen click during a Board read waits for the read and posts, and the row 165 test loads the page instead of reloading from an evaluate (row 172, #1670)
The click handler returned on busy, which refresh() holds for the whole /api/board read, so a click during a
poll did nothing and the sign-out test waited 5 s for a sign-out that was never asked for. The row 165 test's
location.reload() from an evaluate let the next wait start on the old document, and the reload killed it.
2026-10-19 15:34:00 -05:00
jason.woltje 7f38a2f549 sage: row 172 packet, base control and trace lines corrected (Darkwing D1, D2 on #1670) 2026-10-19 15:34:00 -05:00
jason.woltje 0ed9412fca sessions: Sage line for the 170/171/134 build-log and row 172 round 1 2026-10-19 15:21:23 -05:00
jason.woltje 9d6e47dd58 queue: row 172 brief re-pin, briefed, start and round 1 in-review on 15c3d8ca1 (Sage revs 2013-2017) 2026-10-19 15:21:04 -05:00
jason.woltje 17d5663054 sage: row 172 packet, candidate 15c3d8ca1 (cause trace, mutants, 10-run loop, gate) 2026-10-19 15:19:31 -05:00
jason.woltje 164e1f97fe row 172 brief: the cause is a Seen click dropped during a Board read, and the reload race in the row 165 test (P19 366, #1670) 2026-10-19 15:03:56 -05:00
jason.woltje bab069b3c5 build-log: rows 170, 171 and 134 landings, with the row 134 range-diff evidence (mos-0859/0860) 2026-10-19 14:53:51 -05:00
jason.woltje b42934b85c queue: row 134 done on 3a600cf5b (Sage revs 2011-2012: note, done) 2026-10-19 14:48:47 -05:00
jason.woltje a85e573d4f cli README: the chief of staff reads the organization revision with organization_revision, not from the human (row 134 follow-up, Filbert #1669 29193) 2026-10-19 14:47:10 -05:00
jason.woltje 3a600cf5b2 bus, harness: a role holding organization.change reads the organization revision itself with organization.revision and the organization_revision tool (row 134, #1669)
Researcher's candidate 8977d907 (patch 843fdee9), approved by Filbert in #1669 comment 29193. Built on d58c9c5cd and applied on 04155deff without fuzz; the patch's +/- lines hash the same on both bases. Row 171's one broker.mjs line (project-name-is-repo, at :2256) is the only change on a manifest path between them.
2026-10-19 14:35:31 -05:00
jason.woltje 04155deff6 queue: row 171 done on 078518e4e, plus revs 1996-2010 (Sage: 171 reopen/request/note/done, 172 add, 134 review request; Rocko 1997-1998 row 171 round 2 in-review; Researcher 2002-2004 row 134 start and round 1 in-review; Dewey 2007 row 171 approve; Filbert 2008 row 134 approve) 2026-10-19 14:34:30 -05:00
jason.woltje 078518e4e1 bus, cli, webui: a thread project named like a business-file repo refuses project-name-is-repo unless --repo matches, with a clearer CLI refusal (row 171, #1668) 2026-10-19 14:31:36 -05:00
jason.woltje b2d3b6c475 sessions: Filbert row 134 r1 review, approve 2026-10-19 14:28:54 -05:00
jason.woltje ed045ac84a dewey: SESSIONS line for the row 171 round 2 review (#1668) 2026-10-19 14:24:40 -05:00
jason.woltje 4d9114ab4c sage: row 172 brief, the board-held sign-out wait under gate load (P19 366, #1670, mos-0858); cause section open until traced 2026-10-19 14:21:35 -05:00
jason.woltje d58c9c5cda queue: row 170 done on cb43dcb45, plus revs 1970-1994 (Sage: 171 scope/re-pin/request/note, 134 re-pins/assign/issue/briefed, 64 and 66 re-pins, 64 start, 170 note and range-diff; Rocko 1970, 1973-1974 row 171 start and round 1; Dewey 1971, 1981-1982 row 170 claim and in-review, 1990 row 171 approve; Filbert 1986 row 170 approve) 2026-10-19 14:14:33 -05:00
deweyandjason.woltje cb43dcb45e bus, cli: a tester message to a role nobody holds refuses tester-target-not-running, and talk --tester exits 3 (row 170, #1667) 2026-10-19 13:59:15 -05:00
jason.woltje 228aeb5037 sage: rows 64/66 brief, skills reach both harnesses as prompt text (pi read is workspace-confined, Claude Code Skill tool offers its own skills; probe in agents/sage/work/queue-64/probe.md) 2026-10-19 13:58:02 -05:00
jason.woltje c9f6e214f1 dewey: SESSIONS line for the row 171 round 1 review (#1668) 2026-10-19 13:52:51 -05:00
jason.woltje 2cef8cd350 sage: row 134 brief owns the three stale revision-from-the-human sentences (Researcher build report) 2026-10-19 13:46:29 -05:00
jason.woltje 9b11b87313 sessions: Filbert row 170 r1 review, approve 2026-10-19 13:42:36 -05:00
jason.woltje 2e9fe381f6 docs: register Sage's rows 171, 134, 64 and 66 brief work and the b0721a0b0 push correction 2026-10-19 13:41:54 -05:00
jason.woltje e0bf9960dd sage: rows 64 and 66 brief refreshed to 7319bd867 after row 65 (harness module files owned, plugins suite in the gates), row 64 candidate names Sage as author and gate owner (mos-0855) 2026-10-19 13:40:44 -05:00
jason.woltje b0721a0b0d dewey: SESSIONS line for the row 170 build and in-review (#1667) 2026-10-19 13:39:39 -05:00
jason.woltje 7319bd8673 sage: row 171 brief names cli.mjs (sage-1021, mos-0855), row 134 brief refreshed to 000730949 with owner Researcher, row 42 run log takes mos-0855's triggers (#1668) 2026-10-19 13:34:05 -05:00
jason.woltje 0007309492 queue: rows 170 and 171 briefed on Mos's approval, row 171 re-pinned to the mos-0854 brief (mos-0854, #1667, #1668) (revs 1964-1969) 2026-10-19 13:23:54 -05:00
jason.woltje fcfa577f20 sage: row 171 brief cites mos-0854 (D1 withdrawn, option B approved) in place of its open decision section (#1668) 2026-10-19 13:21:48 -05:00
jason.woltje 92160cb301 queue: rows 170 (P19 380, #1667) and 171 (P19 379, #1668) added queued, briefs 97cc7f0a3 await Mos; Darkwing's row 169 claim (revs 1961-1963) 2026-10-19 13:20:28 -05:00
jason.woltje 97cc7f0a32 sage: briefs for P19 380 (tester refusal, #1667, row 170) and P19 379 (project names vs repos, option B, #1668, row 171), for Mos's approval 2026-10-19 13:18:59 -05:00
jason.woltje 42817065e1 queue: row 169 after cleared, in-review waits on 166's landing by note (mos-0852 point 5, #1666) (revs 1959-1960) 2026-10-19 13:12:56 -05:00
jason.woltje 56534526b2 sage: Window B last section rewritten for the approved two-phase path, row 169 (mos-0848 (b), mos-0852, #1558, #1666) 2026-10-19 13:11:26 -05:00
jason.woltje cbdcf1967f queue: row 169 briefed on Mos's brief approval (mos-0852, #1666) (rev 1958) 2026-10-19 13:10:17 -05:00
jason.woltje 3ff2360dc3 sage: Window B P3 recorded done from Darkwing's rehearsal, with what it didn't exercise (#1558) 2026-10-19 13:06:33 -05:00
jason.woltje c5b74bef20 darkwing: Window B P3 scratch rehearsal, activate --pm --migrate 3h to 3i and rollback --restore on blob 4b16e848 (sage-1004, mos-0838 D2, #1558) 2026-10-19 13:06:33 -05:00
jason.woltje 3b5c964eb0 queue: row 169, the stack-release.sh two-phase PM start (P19 382, #1666), brief pinned at bfc5fa7ba3 (revs 1956-1957) 2026-10-19 13:06:06 -05:00
jason.woltje b201760c5a sage: W6 business instances from $C_NEW and the project list channel note in plan A, verb routes checked in A and B (mos-0851), row 169 in the 382 brief (#1558, #1666) 2026-10-19 13:04:43 -05:00
jason.woltje bd8416bc21 plans: brief for P19 382, activate past c216f47db with the PM launched after the harness kind is set (mos-0848, #1666) 2026-10-19 13:01:36 -05:00
jason.woltje 4212174112 sage: Window A P2 CONSOLE_PASS and the post-W5b channel undo before rollback (mos-0849, #1558) 2026-10-19 13:00:20 -05:00
jason.woltje 6e7c23d7d9 sage: Darkwing's Window A and B scratch --plan and load checks, cited in both plans (#1558)
Darkwing's queue-162 packet for sage-1004/1006/1008: the two scratch
--plan runs on blob 4b16e848 and the load checks of the usage-source
file under 5fa9cc8f2 and 8e7ea89a1 (mos-0845 D3, mos-0847). Secrets
scan clean.

Both plans cite the files. W3 notes that the scratch store line reads
the pointer, since the scratch root has no store.
2026-10-19 12:55:51 -05:00
jason.woltje 855c9eb019 sage: Window A and B plans with mos-0846 U1, the 382 bus-exit finding, mos-0845 P19 comments (#1558)
U1: usage checks read total and byRun, since `mosaic usage --json` prints
no record list. W7 wants one new byRun entry for the chief run with
input > 0, output > 0 and unknownTurns 0.

Window B's last section: the D4 design can't work as ruled. A refused
PM launch exits 3 and the unit won't restart it (sage-1007).

The comment script and its record put the mos-0845 rulings on P19
379-382 (comments 273-276).
2026-10-19 12:51:52 -05:00
jason.woltje 6dbc673022 sage: Window A and B plans with mos-0845 rulings, P19 378-382 filing scripts (#1558)
Window A (repin-5fa9cc8f2.md): 3h, no --migrate, Jason's usage-source
patch with the 8e7ea89a1 setter before activate and the channel patch
after, copy-CLI calls for the verbs scripts/mosaic misroutes (F1),
threads --business expecting [] (F2), no pm smoke (F3), a usage record
check after the chief smoke (F4). Window B (repin-ca15d73e9.md): row 126
only, 3h to 3i with --migrate, the D3 restore report, and a section for
the first window past c216f47db that waits on P19 382.
2026-10-19 12:44:18 -05:00
jason.woltje 894ce3f370 sage: file row 65 follow-ups as P19 376 and 377 (mos-0842 item 4, #1558) 2026-10-19 11:41:36 -05:00
jason.woltje 73bbbee81b sessions: Sage row 65 landing, c216f47db with gate g3 green (#1558) 2026-10-19 11:37:56 -05:00
jason.woltje a5810fc849 build-log: row 65 landed as c216f47db, gate g3 green, mos-0841 conditions met, not live (#1558) 2026-10-19 11:37:32 -05:00
jason.woltje 71fd9b9227 queue: row 65 done, landed as c216f47db on #1558 (sage rev 1955) 2026-10-19 11:35:45 -05:00
jason.woltje aed9062b4b sage: row 65 landing notes, c216f47db with gate g3 green, the mos-0840 Routing view, the plugins set patch for Jason, follow-up 5 (#1558) 2026-10-19 11:35:02 -05:00
jason.woltje c216f47dbb plugins, cli, bus, business, webui: harness, model and thinking from the instance vars, a fallback route, and a harness plug-in kind (row 65, #1558)
Adds the harness plug-in kind (interface 1). packages/plugins/harness/pi
and claude-code hold what core branched on: the adapter, thinking levels,
preflight, version probe, sandbox mounts, builtins line, typed tool names,
gate timeout, bundle files, adapter env, turn end and session-files path.
Core reaches a module only through the loader.

A session's route is the instance's harness, model and thinking vars, or
the three fallback. vars; a missing one refuses no-route, and the system
file's execution keys are never read for a session. The launcher checks
the primary route, then the fallback: harness-off, harness-refused,
thinking-unsupported, then preflight. A failed preflight falls back when
a fallback is set, and any refusal of the fallback names the primary's
failure too. Account checks run
only when the route's module counts an account. The manifest records
route, routeReason and harnessVersion, replacing piVersion and
claudeVersion. pi gets --thinking, Claude Code --effort, both from
MOSAIC_THINKING.

The business file refuses a partial fallback. routes and routes.set are
human-only bus verbs outside ORGANIZATION_KEYS; checkRoutes is shared by
routes.set, instances.set and limits.set and checks only changed routes.
The chief of staff changes an instance's model, and the assistant's whole
vars (Q169), through limits.set, so its route write runs the same checks.
A model outside launch.models refuses unknown-model, primary or fallback,
and routes.set passes that code through.
A new run binds only on a switched-on harness module name and records
its transcript path, which the usage reader uses. Settings gains a
Routing table with six selects per instance and loses the Role models
fieldset; it posts changed rows to /api/routes. webui/src/bus.mjs is
unchanged.

Until Jason's `mosaic plugins set` adds "harness": ["claude-code"], a new
launch refuses harness-off.
2026-10-19 11:27:19 -05:00
jason.woltje 866f3c5a80 queue: row 65 round 2 in review on #1558 with candidate 8d7c9e2f1, request 29143 (sage revs 1951-1952); darkwing approve (rev 1953, comment 29144) and dewey approve (rev 1954, comment 29145), both on manifest 8df15e39 2026-10-19 11:26:04 -05:00
jason.woltje 24320f80c5 sessions: Dewey row 65 round 2 review, approve (comment 29145, rev 1954) 2026-10-19 11:25:09 -05:00
jason.woltje b4c9cb3597 sage: row 65 round 2 packet, candidate 8d7c9e2f1 on 89ddb0225, manifest, diff, public/ diff (digest a91cf866 unchanged), build notes, gate and 18 mutants (#1558) 2026-10-19 11:07:55 -05:00
jason.woltje 89ddb02255 queue: row 65 round 1 verdicts, dewey changes (rev 1947, comment 29136) and darkwing changes (rev 1948, comment 29137); sage moves row 65 back to in-progress (rev 1949) and re-pins the brief to blob 0cdff87390 (rev 1950) for round 2 (#1558) 2026-10-19 10:58:46 -05:00
jason.woltje 0235fa9fd9 brief: row 65 routing table names the chief of staff's Q169 assistant exception and its tests (round 2 ruling, sage-1006, #1558) 2026-10-19 10:55:56 -05:00
jason.woltje ff9edc7782 sessions: Dewey row 65 round 1 review, changes (comment 29136, rev 1947) 2026-10-19 10:48:59 -05:00
jason.woltje 9014f9f728 sessions: Sage row 65 build, Mos decision 123 go (mos-0841) and in-review 2026-10-19 10:41:03 -05:00
jason.woltje 4c66494548 queue: row 65 in review, round 1 on #1558 with candidate ccdbc2232, request 29133 (sage revs 1945-1946) 2026-10-19 10:40:51 -05:00
jason.woltje 1d5897024d sage: row 65 packet, candidate ccdbc2232 manifest, diff, public/ diff, build notes, gate and mutants (#1558) 2026-10-19 10:36:42 -05:00
jason.woltje d8336daef2 queue: row 65 brief re-pinned for the human-only routes.set verb (sage rev 1944) 2026-10-19 08:36:54 -05:00
jason.woltje 841e0b0a2a brief: row 65 routing table saves through a human-only routes.set verb, outside ORGANIZATION_KEYS 2026-10-19 08:35:48 -05:00
jason.woltje 4d856a0cdd queue: row 65 brief re-pinned to 54cbf1a2 for the Console routing table, and its gate names Mos's decision 123 check (sage revs 1942-1943) 2026-10-19 08:33:55 -05:00
jason.woltje d3af6db973 brief: row 65 adds a Console routing table for harness, model, thinking and fallback (mos-0840), and refuses thinking a harness can't apply 2026-10-19 08:32:07 -05:00
jason.woltje b65e6d5fe9 sessions: Sage row 168 landing, row 65 brief amendment (decision 151), row 7 posts 2026-10-19 08:29:32 -05:00
jason.woltje e1bfc2cd45 queue: row 65 in progress and brief re-pinned to 802e046c with its gate, row 168 approved by Dewey and done (sage revs 1937, 1939-1941; dewey rev 1938) 2026-10-19 08:26:01 -05:00
jason.woltje 8e2cbb5558 brief: row 65 keeps the instance vars as its one source, adds fallback vars and the full harness list (decision 151, #1558) 2026-10-19 08:24:25 -05:00
jason.woltje efb51169de ledger: count only stack issue numbers, a tracker-name skip and a --first-issue floor (#1665) 2026-10-19 08:24:06 -05:00
jason.woltje 54786df03a sessions: Dewey row 168 review round 1, approve (29121) 2026-10-19 08:10:16 -05:00
jason.woltje c34deedf92 sessions: Sage row 168 build and review request, P19 371 rulings 2026-10-19 08:06:53 -05:00
jason.woltje 3bf16114a1 queue: row 168 in review round 1, manifest candidate, request 29120 (sage, revs 1935-1936) 2026-10-19 08:06:31 -05:00
jason.woltje 6da8fdaa98 sage: row 168 packet, candidate f3a966ea4 manifest, diff, build notes and gate (#1665) 2026-10-19 08:04:58 -05:00
jason.woltje e2ad3aa10d queue: row 168 in progress, brief re-pinned to 52ac1975 and piece updated (sage, revs 1932-1934) 2026-10-19 07:54:31 -05:00
jason.woltje 03586947af docs: row 168 brief amended before build, floor moves to a --first-issue option (#1665); window-plan: mos-0838 rulings in the 3i draft 2026-10-19 07:52:47 -05:00
jason.woltje e69e646b40 window-plan: P19 371 3i window draft for Mos, one or two windows and the restore caveat 2026-10-19 07:49:00 -05:00
jason.woltje c16ba2f0c4 queue: row 168 ledger stack refs added and briefed, #1665 (sage) 2026-10-19 07:46:55 -05:00
jason.woltje a1572a8f7d docs: brief for the ledger counting only stack issue numbers (#1665, P19 372, row 7 follow-up) 2026-10-19 07:45:35 -05:00
jason.woltje 15357b4ba7 sessions: Sage AM standup and row 7 week 10-11 ledger rerun record 2026-10-19 07:43:41 -05:00
jason.woltje b90d944c8c queue: row 7 note, 10-11..10-17 ledger rerun 29089 (sage) 2026-10-19 07:43:27 -05:00
jason.woltje 296972652a queue: row 126 Filbert r2 approve and done (sage) 2026-10-18 20:37:38 -05:00
jason.woltje a267a64b19 sessions: Sage row 126 landing, P19 371 and 372, row 7 week ledger; Dewey queue-126 packet 2026-10-18 20:36:53 -05:00
jason.woltje ca15d73e9f limits: a daily token quota for the account the stack uses, and bus schema 3h to 3i (row 126, #1620)
The business file can name the account claude-code launches use,
`launch.account` = {name, credential}. `name` is an identifier and
`credential` names one login set's environment variables, never a value.
`launch.limits.tokens.daily.<account>` = {limit, since, reset, timeZone}
sets a daily quota for that account. A daily key that isn't the account's
name refuses the business read.

The new plug-in limit-check/daily-quota (interface 2, pure) starts a day at
the first instant at or after `reset` on that local date in `timeZone`. Across
DST that makes a 23- or 25-hour day, and a reset in a skipped hour starts at
the gap's end. It counts the account's known records of all four kinds since
the later of the day start and `since`, and counts unknown turns apart.

The launcher refuses account-credential-missing, account-credential-ambiguous
and account-is-credential (an account name equal to a session credential's
value). Messages name variables, never values. The account rides mayLaunch,
bindLaunch, the usage records, mustStop and the launch record. A pi launch
has no account, records NULL, and no daily quota covers it. `mosaic limits`
and the Console's Settings show one fixed line when a daily row shows:
"Daily quotas count only this stack's own use of the account, not its use
anywhere else."

Bus schema 3h to 3i: 3h reserved usage_records.account as
CHECK (account IS NULL). The new step 3h-3i.sql rebuilds the table in one
transaction, keeping every row's seq and the sqlite_sequence value, and
recreates the three append-only triggers. All of sqlite_master equals 3h's
apart from the account line. A 3h engine refuses a 3i store.
packages/bus/tests/account-migration.test.mjs covers all three, with a
step that fails partway and leaves 3h intact. 3i goes live only through
`stack-release.sh activate ... --migrate` with Jason present (P19 371); the
activate path takes the pre-migrate backup, and the way back is restoring
it (agents/dewey/work/queue-126/MIGRATION.md).

Mos's go mos-0835, six conditions: (1) one transaction, account-migration
test 2 and mutant D29; (2) sqlite_master against 3h, test 1 and mutant D28;
(3) account is never a credential, the launcher's account-is-credential
refusal (launch-account.test.mjs) and the business name pattern
(token-limit-settings.test.mjs); (4) MIGRATION.md, accepted by Mos in
mos-0837; (5) the packet's DESIGN.md "no schema change" line corrected;
(6) the public/ digest below.

The public/ change in bus.js (the daily line under the token-limit table)
carries Mos's decision 123 go mos-0833 on the +/- digest 03e6d9da...ba78,
unchanged at landing.

Owner Dewey, brief docs/plans/2026-10-14_prd-1.2-build.md, "Daily quota for
the credential the stack uses today". Review round 2 rebases the round 4
build onto 1df04f52f (row 167). Reviewer: Filbert approve 29068 on round 1
(manifest d18314f7...8e65); Filbert approve 29080 on
round 2 (manifest f332db76...6fa6).
Seat run on round 2: touched 158/0, singles 1388/0 with 1 esbuild skip,
test-land-work.sh 86/0, 30/30 mutants killed.
Sage gate on 54319f552: GATE_PASSED, 2474+422/0, all 15 packages, the queue
suite and 10 script selftests.

Nothing live.
2026-10-18 20:35:02 -05:00
jason.woltje 54319f5521 sessions: Filbert row 126 r2 review, approve 2026-10-18 20:27:32 -05:00
jason.woltje 4c385db45e sessions: Dewey row 126 round 4 build and review rounds 1-2 2026-10-18 20:27:16 -05:00
jason.woltje 2124cfa761 queue: row 126 Filbert r1 approve, brief re-pin for 3h to 3i, row 7 week note, round 2 in-review (sage) 2026-10-18 20:26:41 -05:00
jason.woltje e264db51ba plans: row 126 brief names the 3h to 3i step and the account guard (#1620)
Filbert's r1 note (a): the scope line didn't name the 3i rebuild or the
account-is-credential guard, which sage-0970 and mos-0835 authorize.
Item 6 describes both. The acceptance list gains the mos-0835 tests, and
the scope bullet now covers the migration step, the version bump and the
guard.
2026-10-18 20:14:03 -05:00
jason.woltje 1df04f52f7 sessions: Filbert row 126 r1 review, approve 2026-10-18 20:09:59 -05:00
jason.woltje 9e2b268660 queue: row 167 Filbert approve and done, row 126 round 1 in-review (sage) 2026-10-18 20:09:07 -05:00
jason.woltje 640a1ee978 sessions: Sage row 167 landing and row 126 3i ruling 2026-10-18 20:08:26 -05:00
jason.woltje 5fa9cc8f2c webui: one settings snapshot and a names read that rethrows a refusal (row 167, #1664)
The Settings page held the launch settings twice, one copy for the launch form and the
token-limit reset and one for the Assistant section. A save in one form left the other with
the old revision, so its next save was refused with revision-conflict. Now one snapshot
serves both. Every successful save or reset sets it from the answer, a read replaces it only
while no form has a draft, and reload clears it.

The names read now rethrows a 403 or signed-out like the usage, instances and limits reads,
so a refusal gives the refused page instead of an empty name field.

Tests: the names refusal, one per snapshot path (launch save, assistant save, reset), and a
null body on /api/name and /api/assistant-stop. limits-browser.test.mjs:54 now expects the
reset's revision on the following save.

Owner Darkwing, brief docs/plans/2026-10-18_settings-reads-after-q169.md.
Mos's decision 123 go mos-0836 on the public/ +/- digest
82a8a810...e984. Mos's F1 and Filbert's notes 1-2 are P19 370.
Reviewer on candidate manifest dba38330...59cc: Filbert approve 29062.
Seat run: 231/0 on all 38 webui files singly, 8/8 mutants killed.
Sage gate on ee3f1fb3f: 2443 package tests and 422 script selftests, 0 failed,
GATE_PASSED 01:06:56Z.

Nothing live.
2026-10-18 20:07:31 -05:00
jason.woltje ee3f1fb3f6 sessions: Filbert row 167 r1 review, approve 2026-10-18 19:37:26 -05:00
jason.woltje 0434ecfef0 sessions: Darkwing lines from 2026-10-16 to 2026-10-19 (rows 137, 141, 150, 87, 155, 162, 165, 166) 2026-10-18 19:12:47 -05:00
jason.woltje 81f26551b4 queue: row 165 r3 in-review, approvals and done, row 166 r2 in-review and Filbert approve (sage) 2026-10-18 19:11:54 -05:00
jason.woltje cff5f31eda sessions: Sage row 165 landing 2026-10-18 19:11:13 -05:00
jason.woltje 9466f3e5ed business: the human adds and changes role instances from the CLI and the Console (row 165, Q171, #1662)
`mosaic business instances BUSINESS` reads the role instances, and
`mosaic business instance add|set` writes one add or change through the
broker's new human-only `instances` and `instances.set` verbs. The writer is
packages/business/src/instance-settings.mjs. It takes the caller's revision,
validates the next document, refuses a definition that declares credentials
(organization-credentials-out-of-scope), keeps protected instances as they
are, logs the human as the actor, and says when the running bus needs a
restart. Neither command restarts the bus.

The writer refuses the instance ids human and tester with invalid-request,
because the broker refuses a business whose roles use either name when the
bus starts.

The Console's Settings view shows the instances and, under --send only,
posts to POST /api/instances with the exact Origin and same-origin Fetch
Metadata. A refusal keeps the draft. An answer without the view's shape is
outcome-unknown (503). The page's public/ diff carries the decision 123 go
in mos-0832.

Neither verb is in ORGANIZATION_KEYS, so no agent tool reaches them.

Owner Darkwing, brief in row 165 (#1662). Round 3 rebases round 2 onto
4f45cadc0 (row 156). The public/ +/- digest is 939ecd28...9444, unchanged
at landing. Reviewers on candidate manifest e30b54da...f580: Dewey
approve 29052, Filbert approve 29053. Seat run on 4f45cadc0: 1436/0
singles, 37/37 mutants killed. Sage gate on e8b09735a: GATE_PASSED 25/25,
packages 2282/0, queue node 156/0, script selftests 422/0, 23:59:13Z to
00:05:35Z. Rebased onto 50a57e454 over two SESSIONS-only commits.

Nothing live.
2026-10-18 19:10:20 -05:00
jason.woltje 50a57e454b sessions: Filbert row 166 r2 review, approve 2026-10-18 19:09:30 -05:00
jason.woltje 7f9b976c97 sessions: Filbert row 165 r3 review, approve 2026-10-18 19:07:43 -05:00
jason.woltje e8b09735a5 sessions: Sage row 156 landing 2026-10-18 18:43:21 -05:00
jason.woltje d6a008ad21 agents/dewey: row 156 packet, round 2 (#1651) 2026-10-18 18:43:13 -05:00
jason.woltje 1cf77e4663 queue: row 156 done, row 165 r2 in-review and approvals, row 166 r1 in-review and Filbert approve (sage) 2026-10-18 18:41:30 -05:00
jason.woltje 4f45cadc09 bus, cli, harness: the host starts the landing run once a merge decision is approved (row 156, #1651)
Until now a landing ran only when the chief of staff or merge-gate
followed row 149's manual steps. The host now does it.

Broker. landHolders(business) lists the approved merge decisions each
instance holds, sharing the #held generator with landHeld.
landRelease(record) releases a run's claim with by='land-relaunch'.
Delivered and unread messages keep the run, and a run that hasn't had
its first message.receive answers {released:false, landing:true}
(#received, round 2), so a landing turn in progress is never cut. A
run that never reaches its first receive, a hung landing turn or a
broker restart, keeps the release closed, and the decision falls back
to row 149's path.
usage.begin accepts a decision id that landHolders lists for the run.
The bus process answers both ops from their own launchOp lines and
refuses any op it doesn't name.

Launcher. start() reads landHolders once for a non-thread instance and
takes land_work and the session's landing list from that one answer.
landPass() runs after threadPass() on each tick.

Runner. One turn per landing entry before the message loop, with a
usage bracket on the decision id.

Docs. A "Landing runs" section in the cli README and docs/TOOLS.md.
roles/merge-gate.md, cto.md and chief-of-staff.md say the host starts
the run, and row 149's steps are the fallback.

Owner Dewey, brief docs/plans/2026-10-18_landing-run-launch.md, design
DESIGN.md in the packet (sage-0808). Round 2 answers Filbert's F1-F3
(29033, rulings sage-0942). Reviewer on candidate manifest
72a5c1b6...b700: Filbert approve 29039. Seat run 4: 290/290 singles,
36/36 mutants killed. Sage gate on a618528b2 (candidate 524bfcc80): GATE_PASSED 25/25. Package tests 2258 plus queue 156, script selftests 422, 23:30:07Z to 23:38:04Z. Run 1 stopped at pkg-webui on board-held.test.mjs:163, the P19 366 timeout, 220/1. Run 2 resumed at pkg-webui on the same tree: webui 221/0 and every scripts/test-*.sh green.

Nothing live.
2026-10-18 18:38:57 -05:00
jason.woltje 4d92151bcf sessions: Filbert row 166 r1 review, approve 2026-10-18 18:38:50 -05:00
jason.woltje c9c8c14d41 sessions: Filbert row 165 r2 review, approve 2026-10-18 18:35:00 -05:00
jason.woltje a618528b26 sessions: Filbert row 156 r2 review, approve 2026-10-18 18:14:28 -05:00
jason.woltje 37dbf08620 sessions: Dewey rows 156 r2 accepted, 165 r1 review, 126 packet and Mos go request 2026-10-18 18:13:57 -05:00
jason.woltje c3cd273454 queue: row 165 r1 in-review and Filbert approve, row 156 round 2 in-review (sage) 2026-10-18 18:12:22 -05:00
jason.woltje de34256c66 sessions: Filbert row 165 r1 review, approve 2026-10-18 17:57:13 -05:00
jason.woltje 9d6aca5716 sessions: Sage queue commit 756bc1af1 rev range correction and PM standup 2026-10-18 17:52:07 -05:00
jason.woltje 756bc1af14 queue: revs 1882-1885, row 156 round 1 in-review and Filbert changes (sage) 2026-10-18 17:51:34 -05:00
jason.woltje 139e28673b sessions: Filbert row 156 r1 review, changes 2026-10-18 17:27:43 -05:00
jason.woltje 70c9131f0d queue: revs 1879-1881, row 154 round 5 approvals and done (sage) 2026-10-18 17:19:56 -05:00
jason.woltje 76e2b6e6f8 sessions: Sage row 154 landing 2026-10-18 17:17:05 -05:00
jason.woltje 9353675d1c webui: a WYSIWYG Markdown editor in the Console docs view, built from reviewable source (row 154, Q168, #1648)
Jason's Q168: a visual editor for the Console's project documents,
held to row 148's rendering rules.

Library. ProseMirror with prosemirror-markdown and markdown-it, chosen
in the phase 1 library report (LIBRARY.md, REPORT.md in the packet).
packages/webui/third_party/editor/ holds 19 upstream packages as source,
byte for byte, each with its license, plus NOTICE and PROVENANCE.json.

Build. packages/webui/editor/ holds the model, view, entry and
build.mjs. esbuild 0.28.2 is an exact devDependency of packages/webui
with its own lock; editor/toolchain.mjs checks the version and the
binary's sha256 before any build. MOSAIC_REQUIRE_EDITOR_BUILD=1 turns a
missing or different esbuild into a failure, and the rebuild test then
checks the committed src/public/editor.js and editor.css match a fresh
build. The built bundle is committed under Mos's conditions
(mos-0755, mos-0765).

Console. The docs view gets Visual and Source modes. A save still
writes Markdown through the existing project write path, with the same
stale-base and read-only handling. A mode switch reuses the draft and
reads nothing from the project. Links and images keep row 148's
target rules, and no raw HTML runs. serve.mjs serves /editor.js and
/editor.css; index.html loads them.

Owner Researcher, brief docs/plans/2026-10-16_q168-wysiwyg-editor.md.
Round 5 is a rebase onto d88613bac (row 155); only docs/TOOLS.md,
webui/README.md, public/bus.js and serve.mjs differ from the round
before. Reviewers on candidate manifest 3a4bc21c...3b05: Dewey
(security) approve 29027, Filbert approve 29026. Mos's decision 123 go
for public.diff 902f0244 (handwritten part ea59079e): mos-0824. Sage gate
on 72c3fa598 with MOSAIC_REQUIRE_EDITOR_BUILD=1: GATE_PASSED 25/25
(node 2397, selftests 422), 22:07:29Z to 22:13:50Z; the rebuild test
ran and passed.

Nothing live.
2026-10-18 17:14:39 -05:00
jason.woltje 72c3fa598f sessions: Filbert row 154 r5 review, approve 2026-10-18 16:45:02 -05:00
jason.woltje df078c9c4b queue: revs 1877-1878, row 154 round 5 review request posted as comment 29024 (sage) 2026-10-18 16:44:11 -05:00
jason.woltje 31788ef85b sessions: Researcher row 154 rounds 3 to 5 and Sage row 154 round 5 request, row 165 credentials ruling 2026-10-18 16:43:12 -05:00
jason.woltje 81b0041e36 agents/researcher: row 154 (#1648) packet through the d88613bac rebase round (manifest 3a4bc21c), without the five superseded 2 MB candidate.patch files (root, r2, r3, rebase-7bd2a348c, rebase-850484a7d), which stay untracked on disk 2026-10-18 16:42:44 -05:00
jason.woltje 5e2a1bc118 queue: revs 1869-1874, row 165 briefed, claimed and brief re-pinned on b3cf580a (Sage 1869-1870, 1872-1873; Darkwing 1871); 154 back to in-progress for the d88613bac round (Sage 1874) 2026-10-18 16:39:30 -05:00
jason.woltje b8036b6089 plans: row 165 brief correction, the second reviewer waits on P19 367 (mos-0823)
reviewer, coder, pm and cto declare gitea and vikunja credentials, and the
row keeps credentials out. The writer refuses an added instance or a
definition change whose definition declares credentials, with a named
refusal that says why, and never writes an instance without its binding.
One added test line. Acceptance 314 is covered: security-reviewer and
merge-gate declare none. Jason rules on credential binding in daylight.
2026-10-18 16:32:51 -05:00
jason.woltje d88613bac2 queue: revs 1866-1868, row 155 done, 167 add and briefed (sage) 2026-10-18 16:22:45 -05:00
jason.woltjeandClaude Opus 5.5 ab78898997 sessions: Sage row 155 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:20:36 -05:00
jason.woltjeandClaude Opus 5.5 68fc31a944 plans: brief for the Settings reads and snapshot follow-up to row 155 (Darkwing's draft, Mos's mos-0816 notes, Dewey's F1)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:19:11 -05:00
jason.woltjeandClaude Opus 5.5 53f3f29a34 sage: window plan repin-8e7ea89a1 records P2, CONSOLE_PASS on a scratch copy (mos-0819, mos-0820)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:19:11 -05:00
jason.woltjeandClaude Opus 5.5 66f9e9d0c6 darkwing: row 155 packet, rounds 1-3 and the follow-up brief draft (#1649)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:19:11 -05:00
jason.woltjeandClaude Opus 5.5 b2f57ca70d bus, business, harness, webui: the human and the chief of staff configure the assistant (row 155, Q169, #1649)
Jason's Q169: only the chief of staff removes the assistant; the chief
and the human configure it and its seat name.

Configure. A new organization key, assistants, replaces the vars,
tracker or definition of an instance whose definition is already the
assistant. holder and credentials are refused, and the whole document
still goes through validateBusinessDocument. set() refuses
organization-definition-refused if any other instance's definition
changed. THREAD_DEFINITION and END_PROTECTED move to
packages/business/src/vocabulary.mjs, so the broker and
launch-settings.mjs read one list. The human writes it with
mosaic limits set --file or the Console Settings view's Assistant
section; the chief writes it with change_organization. Both carry the
file's revision.

Seat name. The chief gets a typed set_name tool (name.set,
instance.name). The Console gets POST /api/name on the settings
boundary, through human-cli.

Remove. role.end and role.revoke of a threaded instance revoke every
thread of that instance whose latest claim is a claim, and report
threads: N. The raise and the check both record a missing holder run as
null. A new human-only verb, assistant.stop, revokes the human's own
threads of an assistant instance; the Console calls it from POST
/api/assistant-stop. The bundle, the end_run description and
roles/chief-of-staff.md now say the chief ends and configures the
assistant. No schema step.

Owner Darkwing, brief docs/plans/2026-10-16_q169-assistant-config.md.
Reviewers: Dewey (security) r1 approve 29012, r2 29014, r3 29018; Filbert r1 29010,
r2 29015, r3 29017, on candidate manifest 96cae28e...9016. Round 2 is
confirm-only after rows 162 and 125 moved the base under docs/TOOLS.md
and the cli README. Round 3 bumps row 164's webui/src/bus.mjs byte
baseline in packages/bus/tests/channel-proof.test.mjs for the eight
STATUS entries; Sage's gate on r2 failed only there. Mos's decision 123 go for the public/bus.js diff
(mos-0816) covers 9f9ce31b, unchanged here. Sage gate on 197722caa:
GATE_PASSED 25/25 (node 2370, selftests 422) 21:11:38Z, a rerun on the same tree after a first run stopped at pkg-webui on a board-held.test.mjs timeout that passes singly.

Nothing live. Follow-up brief in the packet: the names read rethrow, one
shared settings snapshot, null tests for the two new routes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:18:47 -05:00
jason.woltjeandClaude Opus 5.5 197722caa2 queue: revs 1861-1865, row 155 r3 in review (darkwing) and confirm-only approvals (filbert, dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:05:27 -05:00
jason.woltjeandClaude Opus 5.5 f742175515 sessions: Filbert row 155 r3 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:04:05 -05:00
jason.woltjeandClaude Opus 5.5 0473574b02 queue: revs 1846-1860, rows 155 r1-r2 reviews (darkwing, dewey, filbert), 162 done, 166 add and briefed (sage), 154 r4 in review (researcher)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:51:56 -05:00
jason.woltjeandClaude Opus 5.5 359adff563 sessions: Filbert row 155 r2 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:48:43 -05:00
jason.woltjeandClaude Opus 5.5 449cadd1f1 sessions: Filbert row 155 r1 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:40:53 -05:00
jason.woltjeandClaude Opus 5.5 7bd2a348ca sessions: Sage row 162 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:40:28 -05:00
jason.woltjeandClaude Opus 5.5 c70f360ae7 plans: D1 brief item 8 re-pinned to the held or stopped store read; stack-release hardening brief (row 162 follow-up)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:39:38 -05:00
jason.woltjeandClaude Opus 5.5 f5b5aa509f sage: window plan repin-8e7ea89a1 on row 162's verbs, de352dfaf superseded (mos-0814, mos-0815)
W2 adopt on Mos's go with Jason present, W2b pointer check (copy 52f35e7a0,
schema 3f) before W3, W3 plan line-by-line match against Darkwing's scratch
rerun, and the W2 undo: remove the pointer only. Landing commit 7e7f8e208.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:39:38 -05:00
jason.woltjeandClaude Opus 5.5 d132b369ea darkwing: row 162 packet, rounds 1-2, scratch plans and follow-up draft (#1659)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:39:38 -05:00
jason.woltjeandClaude Opus 5.5 7e7f8e208e scripts: health-gated stack activate and rollback, stack-release.sh (row 162, D1, #1659)
New scripts/stack-release.sh with four verbs. adopt records the running
copy, the unit flags and the store schema in state/stack-active.json; it
stops nothing. activate ID BUSINESS [--pm] [--migrate] stops the units,
checks the stopped store, backs it up, migrates when the target's schema
differs, installs and starts the target, then gates on both units active,
a live bus host whose pid is the bus unit's MainPID with boot ok, Console
/healthz, stack-copy.sh check and one launched journal line. A failed
gate rolls back inside the same run and keeps the attempted store as
bus.post-attempt-<stamp>. rollback [--restore ACT [--events N]] goes back
to the log's previous copy and counts the events written after the
backup. status prints the pointer, the log tail and the copy check.
--plan prints what a run would do and writes nothing; it reads the store
schema from the store the running bus holds, or from a stopped store.
Exit codes: refusal 3, gate failure with rollback 1, rollback failed 2,
usage 4. Nothing is pruned.

stack-copy.sh gets a reachable function split from copy_commit and a
guard so its helpers can be sourced. TOOLS.md "Pinned stack update" and
the cli README pinned-stack paragraph describe the verbs.

Owner Darkwing, brief docs/plans/2026-10-18_stack-release.md (D1).
Reviewer Filbert: r1 approve (comment 28997) on 091693c5, r2 confirm-only
approve (29004) on candidate manifest c213a801...bd15c after row 125
moved the base under TOOLS.md and the cli README. Sage gate on 23622980f:
25 of 25 steps rc=0 on c96a95320 (15 packages + 10 scripts), node pass 2353 fail 0, selftests 422 passed 0 failed, 2026-10-18T20:32:13Z to 20:38:55Z, network sage-r162_default removed, 0 containers.

Nothing live. The 8e7ea89a1 window (repin-8e7ea89a1.md) runs these verbs
on Mos's go with Jason present.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:39:09 -05:00
jason.woltje 23622980f4 queue: revs 1838-1845, row 125 done (Sage 1838); 156 in progress (Dewey 1839); 162 r2 in review (Darkwing 1840-1842); 126 brief re-pin on 7ce156259 and briefed (Sage 1843-1844); 162 r2 Filbert approve on c213a801 (1845, comment 29004) 2026-10-18 15:30:16 -05:00
jason.woltjeandClaude Opus 5.5 6b21085c4c sessions: Filbert row 162 r2 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:29:17 -05:00
jason.woltjeandClaude Opus 5.5 7ce156259d plans: row 126 brief rewritten on Dewey's tokens.daily design after row 125 landed (#1620)
Row 125's core lets a check see only launch.limits and accept hits on the
four DEPTH kinds, so the quota gets its own depth-1 kind,
tokens.daily.<account>, in the two DEPTH tables and the organization-file
schema. Display is a fixed line keyed on the daily path; no report field.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:27:07 -05:00
jason.woltjeandClaude Opus 5.5 de74dd4735 sessions: Sage row 125 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:22:53 -05:00
jason.woltjeandClaude Opus 5.5 df4d5702fb dewey: row 125 packet, rounds 1-3 (#1619)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:22:02 -05:00
jason.woltjeandClaude Opus 5.5 aa625565f6 bus, cli, webui: token limits per instance, model, provider and key (row 125, #1619)
Adds token limits under launch.limits.tokens (instances, providers, keys,
models.P.M). Core (packages/bus/src/limits.mjs) reads the organization
file and the per-turn usage records row 124 writes, then asks every
switched-on limit-check plug-in; the first refusal wins. A limit no
switched-on check reports reads "not enforced". The limit-check kind is
interface 2 (mayLaunch, mustStop, report); a v1 check is refused at load.
The shipped token-limit check is pure and counts known end records only;
unknown turns add nothing and are shown as their own count.

The launcher refuses limit-reached through the host's mayLaunch op, or
limit-check-failed naming the check. The broker asks mustStop after a
usage end and stops the run at its next receive or state. Settings stamps
since on a new or changed limit and runs reset; launch.mjs and
business.mjs validate the shape. Readers: mosaic limits and mosaic limits
reset, the bus limits view, the Console /api/bus/limits route and the
Settings token table with Reset. Key limits stay "not enforced" until
row 126 names accounts on records.

Owner Dewey, design under lead decision 150.
Reviewer Filbert: r1 changes (comment 28965), r2 approve (28981), r3
approve (28996) on candidate manifest 8b71c405...fe56 after the rebase
onto row 164 (37 of 38 paths with identical +/- lines, TOOLS.md:621
bracket). Decision 123 go on the bus.js hunk from Mos (mos-0814). Sage
gate on b6827d19e: 25 steps, all rc 0 (15 packages, 10 script suites), 2295 node tests and 422 script selftests, 0 failed, 20:03:25Z to 20:08:08Z, network sage-r125_default removed.

Live stays as pinned. Not part of the 8e7ea89a1 window.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:21:49 -05:00
jason.woltje b6827d19ea queue: revs 1836-1837, row 125 r3 Filbert approve on 8b71c405 (1836, comment 28996); row 162 r1 Filbert approve on 091693c5 (1837, comment 28997) 2026-10-18 15:03:14 -05:00
jason.woltjeandClaude Opus 5.5 e972b5969a sessions: Filbert row 162 r1 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:01:58 -05:00
jason.woltjeandClaude Opus 5.5 5f8e5f0413 sessions: Dewey rows 125 r3, 154 r3 and 164 r2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:58:10 -05:00
jason.woltjeandClaude Opus 5.5 4c2ff00cd8 sessions: Filbert row 125 r3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:57:11 -05:00
jason.woltjeandClaude Opus 5.5 99d2b49c9c queue: revs 1828-1835, row 154 r3 Filbert approve (1828); 164 done (Sage 1829); 165 brief re-pin after Jason's Q4 (Sage 1830); 125 r3 in review (Dewey 1831-1833); 162 r1 in review (Darkwing 1834-1835)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:50:41 -05:00
jason.woltjeandClaude Opus 5.5 8e47e5e3b3 sessions: Sage row 164 landing, row 165 brief
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:27:44 -05:00
jason.woltjeandClaude Opus 5.5 606fafd431 plans: row 165 brief, Jason's Q4 confirmation relayed (mos-0805), hold lifted
Records Jason's "Q4: yes" (DECISIONS Q171, Q172) and lifts the hold.
The row moves to briefed once row 155 is done. No test reads this file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:26:39 -05:00
jason.woltjeandClaude Opus 5.5 cfde62f9ff agents: Rocko row 164 r2 packet (#1661)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:24:42 -05:00
jason.woltjeandClaude Opus 5.5 008746b6ed plugins: channel kind for the CLI and Console chat paths (row 164, #1661)
Adds plugin kind `channel` (interfaceVersion 1, dispatch(request, {call}))
with cli and webui modules. Project and thread surfaces and thread
talk/follow in the CLI and the Console chat go through the selected
channel. An empty or absent channel selection is off: the CLI exits 3 and
the Console answers 503, each naming `mosaic plugins` and `mosaic plugins
set` with the business selector. Management plugins and plugins.set stay
on the core transport. The loader gains a synchronous snapshot(kind)
getter; loaded(), modules() and last-good handling are unchanged. No lock
is held across dispatch. serve.mjs, public/ and the bus are untouched.

Owner Rocko. Reviewer Dewey: r1 changes (comment 28980, B1: the recovery
sentence was never asserted), r2 approve (comment 28984) on candidate
manifest ae2fcfda...1fc8, the B1 assert killing mutant D1. Design accepted
by Mos (mos-0768, mos-0773). Fixture rewrite at project.test.mjs:47 acked
by Mos (mos-0789) on Dewey's line-by-line check: lines 48-51 identical,
one added assert. Sage gate on e5b10d275: 25 steps green, 2081 package tests, 156 queue node tests, 422 script selftests, 0 failed.

Live stays as pinned. The re-pin that takes this live puts `mosaic
plugins set` in Jason's window right after activate (mos-0768).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:24:42 -05:00
jason.woltjeandClaude Opus 5.5 e5b10d275e sessions: Filbert row 154 r3 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:17:35 -05:00
jason.woltjeandClaude Opus 5.5 27f01be2ec queue: revs 1805-1827, row 164 r1 in review (Rocko 1806-1807, request by Sage 1808-1809), r1 changes (Dewey 1814), r2 in review (Rocko 1816, 1820-1821, request by Sage 1824-1825) and approve (Dewey 1826); 125 r2 in review (Dewey 1805, 1811-1812) and approve (Filbert 1815); 154 r2 changes (Filbert 1810, Dewey 1813), r3 in review (Researcher 1817-1819, request by Sage 1822-1823) and Dewey approve (1827)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 14:17:18 -05:00
jason.woltjeandClaude Opus 5.5 1dbf456101 sessions: Filbert row 125 r2 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:55:45 -05:00
jason.woltjeandClaude Opus 5.5 6f963fd657 sessions: Filbert row 154 r2 review, changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:36:10 -05:00
jason.woltje ed92e2e1e8 logs: correct Rocko row 164 resume stamp from recorded clock 2026-10-18 13:20:31 -05:00
jason.woltje 3d527871c7 docs: Rocko row 164 candidate packet and own verification logs 2026-10-18 13:19:50 -05:00
jason.woltjeandClaude Opus 5.5 370c4d897c sessions: Researcher row 154 phase 1, phase 2 and round 2 lines, row 128 mutants line (their own lines)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:19:01 -05:00
jason.woltjeandClaude Opus 5.5 5ec790f141 queue: revs 1798-1804, row 165 added queued (Sage 1798, P19 332), 154 r2 in review (Researcher 1799-1801, request posted by Sage 1803-1804), 125 r1 changes (Filbert 1802)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:18:13 -05:00
jason.woltjeandClaude Opus 5.5 7a7d7442d9 sessions: Filbert row 125 r1 review, changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:16:35 -05:00
jason.woltjeandClaude Opus 5.5 ab9f23d9ef docs: row brief for adding and changing role instances (P19 332, #1662); I6 brief records Jason's P19 333 ruling
Mos relayed Jason's answers in mos-0787. P19 332: a CLI command and a
Console view for role-instance changes, held until Jason confirms Mos's
reading. P19 333: accepted as a documented limit, so the I6 brief and the
design's limits line now say I6 writes it as a known limit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 13:10:41 -05:00
jason.woltje d62c61fbd8 logs: Rocko row 164 focused pass and full-gate fixture boundary (own lines) 2026-10-18 13:07:49 -05:00
jason.woltjeandClaude Opus 5.5 fe7eef8fb0 queue: revs 1791-1797, row 129 r2 approve (Filbert 1791) and done (1792), 164 claimed (Rocko 1793), 154 r1 changes (Filbert 1794, Dewey 1797), 125 r2 in review (Dewey 1795-1796)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:58:47 -05:00
jason.woltjeandClaude Opus 5.5 20b5030b5d sessions: Dewey row 125 in-review and row 154 r1 security review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:54:34 -05:00
jason.woltje fdb1d4b34f logs: Rocko row 164 test preparation and landed reference check (own lines) 2026-10-18 12:28:48 -05:00
jason.woltjeandClaude Opus 5.5 e79ba6c944 sessions: Sage row 129 landing (850484a7d, #1623 closed)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:19:54 -05:00
jason.woltjeandClaude Opus 5.5 3cbfbaac45 sessions: Filbert row 154 r1 review (changes, comment 28954)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:19:41 -05:00
jason.woltjeandClaude Opus 5.5 850484a7d2 webui: Console chat area over the bus's project and thread verbs (row 129, #1623)
Adds the Console chat view: project and thread lists, named create forms,
per-thread drafts, explicit Resume, paging and signed-in sends. The one
HTTP-to-bus seam is packages/webui/src/chat.mjs, with fixed routes in
chat-http.mjs: exact HTTPS Origin, same-origin fetch site, JSON content
type, and 403 for each. Every label and message body renders inert,
including bidi controls. CSP unchanged, no inline script.

Owner Rocko. Reviewer Filbert: r1 changes (comment 28933), r2 approve
(comment 28950) on candidate manifest 9a665434...4034, with the four r1
survivors killed by new assertions. Mos decision 123 go on the public/
diff, sha 78574095 (mos-0780). Sage gate on 19fe85c3e: 25 steps, node
2216/0, scripts 422/0, webui 179/179.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:16:42 -05:00
jason.woltjeandClaude Opus 5.5 8e7ea89a1e sessions: Filbert row 129 r2 review (approve, comment 28950)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:11:12 -05:00
jason.woltjeandClaude Opus 5.5 19fe85c3e2 queue: revs 1757-1790, row 124 done (Dewey r2 approve 1757), rows 125-127/129 re-pinned, 125 and 156 to dewey, 164 to rocko, row 149 done (Filbert r4 1771), 129 r1 changes (1780) and r2 in review (1787-1790), 154 phase 2 in review (Researcher 1782-1784, request 1785-1786)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 12:00:49 -05:00
jason.woltje 81109fdd88 logs: Rocko row 129 r2 verification and row 164 accepted design picks (own lines) 2026-10-18 11:58:48 -05:00
jason.woltje c202d57a1f docs: Rocko channel design, cut-over and own log lines (sage-0766/0769) 2026-10-18 11:46:45 -05:00
jason.woltjeandClaude Opus 5.5 5370251458 sessions: Filbert row 129 r1 review (changes, comment 28933)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:45:07 -05:00
jason.woltje d2df4d934a logs: Rocko row 164 design handoff (own lines through temporary index) 2026-10-18 11:42:31 -05:00
jason.woltjeandClaude Opus 5.5 c2f16240c0 docs: row 156 owner dewey, after row 125 (landing run brief)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:41:30 -05:00
jason.woltjeandClaude Opus 5.5 03bbd2c981 roles, harness: security-reviewer and merge-gate roles, proactive contracts (row 149, #1643)
New roles:
- roles/security-reviewer.json and .md: read tools only, review.verdict
  within its role, no cross-role grant and no credentials, one verdict per
  request.
- roles/merge-gate.json and .md: read tools only, cross-role
  git.merge.protected only. It raises with domain technical and the task
  ref, and lands with land_work once the decision resolves, the verdicts
  approve and the gate passes. The chief of staff messages the relaunched
  instance with the task ref and decision id (round 3 finding 28913).

Contracts:
- All 13 roles/*.md carry the proactive line in their Authority section.
- pm and chief-of-staff carry the REQ-DEC-2 routing paragraph. Its eight
  hard waits are pinned to GATED_ONLY by a test.

Harness: raise_decision takes an optional domain, technical or delivery,
defaulting to delivery. The tool description says where each kind of raise
goes. end_run is unchanged (row 155).

Tests are new files only: business and bus workflow-roles tests and
harness raise-domain. Builder Dewey; Filbert approved round 4 (comment
28929) on manifest 6b4af9c2685b, base 0f715a054 plus build.patch
df0fec543dba.

Gate on 806ab89df plus the candidate, 16:34:57Z to 16:39:43Z: 2027
package tests and 422 script selftests (plus the queue suite's 156 node
tests), 0 failed. Doc-only follow-ups: business README:194-196 and the
launch.instances doc line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:40:05 -05:00
jason.woltjeandClaude Opus 5.5 c682d5f7ad docs: row 164 owner rocko, design packet before the claim (channel kind brief)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:35:03 -05:00
jason.woltjeandClaude Opus 5.5 806ab89df4 sessions: Filbert row 149 r4 review (approve, comment 28929)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:34:13 -05:00
jason.woltje 23f86feda6 logs: Rocko row 129 build, verification and review handoff (own lines via temporary index) 2026-10-18 11:32:40 -05:00
jason.woltjeandClaude Opus 5.5 0f715a0542 docs: lead decision 150, limit-check interface 2 with report (row 125, dewey-0593)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:21:22 -05:00
jason.woltjeandClaude Opus 5.5 25eb9e9768 sessions: Sage row 124 landing, rows 125-126 sequencing, row 162 rulings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:13:47 -05:00
jason.woltjeandClaude Opus 5.5 5b203b614b docs: row 125 to dewey, row 126 after step 8 (PRD 1.2 build brief)
Row 126's daily quota is a second limit check with no launcher or bus
change, so it needs row 125's launcher and bus calls. Its after listed only
row 124. Row 125 moves from researcher, who is on row 154, to dewey, so the
two limit-check rows run in order with one owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:11:13 -05:00
jason.woltjeandClaude Opus 5.5 70504d23a4 bus, cli, webui: token accounting per run, schema 3h usage_records (row 124, #1618)
Rocko's round 2 candidate e09313aba4d155a1c5af7bfb227ec397e96c0d57444782cd0b48593102bcc684
(38 paths, base 23533625d), approved by Dewey in #1618 comment 28923 (queue rev 1757).
Packet: agents/rocko/work/queue-124/r2/.

- Schema 3g to 3h: packages/bus/src/migrations/3g-3h.sql adds an append-only
  usage_records table; nothing earlier changes. usage_records is in timedTables.
- The transcript usage-source module reads token counts from Pi and Claude
  Code session transcripts and follows no symlink.
- `mosaic usage` in the CLI and a read-only usage section in the Console's
  Settings (one GET route; 400 on a query string).

Decision 123 go: Mos, mos-0737. Activation (3g to 3h) is a migrating run
with Jason present, in Mos's combined re-pin; landing changes nothing live.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:08:17 -05:00
jason.woltjeandClaude Opus 5.5 d523272292 sessions: Dewey row 124 round 2 review, approve (comment 28923, rev 1757)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:57:50 -05:00
jason.woltjeandClaude Opus 5.5 7a13129be2 queue: revs 1731-1756, row 124 round 1 changes and round 2 in review (Rocko, Dewey 1738), row 129 claimed (1737), rows 157-161 re-pinned for the I4 checks, D1/D2 re-pinned, row 162 briefed to darkwing, row 164 channel kind added (decision 149)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:54:32 -05:00
jason.woltje cbe071125c logs: Rocko row 124 round 2 (own lines via temporary index) 2026-10-18 10:53:19 -05:00
jason.woltjeandClaude Opus 5.5 810d1a1917 sessions: Sage mos-0716 answers, D1 decision 96 conditions, decision 149 and row 164
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:50:21 -05:00
jason.woltjeandClaude Opus 5.5 c742ecec2e docs: lead decision 149 and the channel kind brief, after row 129 (Q156 item 4, Mos mos-0726, #1661)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:49:07 -05:00
jason.woltjeandClaude Opus 5.5 cd9c071ae6 plans: D1 owner is darkwing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:45:35 -05:00
jason.woltjeandClaude Opus 5.5 d503552eab plans: D1 brief carries decision 96's four conditions and records the claude pin and Console flags in the pointer (Mos mos-0716, Darkwing survey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:44:59 -05:00
jason.woltjeandClaude Opus 5.5 23533625d4 sessions: Dewey row 124 round 1 review, changes (comment 28920, rev 1738)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:42:18 -05:00
jason.woltjeandClaude Opus 5.5 e51d8f2042 plans: row 159 I4 tests add the container-address and network-membership checks (Mos, mos-0716)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:42:08 -05:00
jason.woltje 5e0437659b logs: Rocko row 129 design and row 124 count correction (own lines) 2026-10-18 10:39:25 -05:00
jason.woltje 1f3208d4eb logs: Rocko row 124 build and verification (own lines via temp index) 2026-10-18 10:28:43 -05:00
jason.woltjeandClaude Opus 5.5 640f4cef5b sessions: Sage row 155 ruling, D1/D2 rows 162-163, row 154 phase 1 to Mos, queue revs 1722-1730
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:27:35 -05:00
jason.woltjeandClaude Opus 5.5 6a2409f214 queue: revs 1722-1730, row 134 after 155, row 149 round 4 in review (Dewey 1723-1724), D1 and D2 rows 162-163 with P19 notes, row 157 after 163, row 154 brief re-pin
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:27:18 -05:00
jason.woltjeandClaude Opus 5.5 2b5c76eeb4 plans: row 154 brief owns two serve.mjs static routes and round-trips the goals review (Researcher phase 1)
Researcher's phase 1 report needs /editor.js and /editor.css route
entries in serve.mjs, and spec/north-star.md doesn't exist. The brief
now owns those two entries only and names
docs/plans/2026-09-27_goals-review.md for the round-trip test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:23:35 -05:00
jason.woltjeandClaude Opus 5.5 0df05f2c5c plans: D1 and D2 brief, health-gated stack activate and rollback and the scratch-root rehearsal (#1659, #1660)
Decision 96 D track. D1 turns the de352dfaf window scripts and the
release.sh pointer-and-log shape into stack-release.sh adopt, activate,
rollback and status. D2 adds rehearse on a scratch root with launches
disabled and no model calls. A standing staging stack is left to Jason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:13:48 -05:00
jason.woltjeandClaude Opus 5.5 e270538675 sessions: Dewey row 149 round 4 in review (revs 1721-1724, comment 28916)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:10:54 -05:00
jason.woltjeandClaude Opus 5.5 7dfda51829 sessions: Sage P19 334-335, queue revs 1713-1721, row 156 brief wake gap, row 149 round 4
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:04:54 -05:00
jason.woltjeandClaude Opus 5.5 9cc0ba0e15 plans: row 156 brief takes Filbert's three edits (task ref in landHolders, land and landing from one read, decision-consumed in flight)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:04:32 -05:00
jason.woltjeandClaude Opus 5.5 957fbfd410 plans: row 156 brief wakes the landing run with a landing turn (Filbert, comment 28913)
A relaunched merge-gate run has land_work but no message to turn on: the
raising run's poll consumes the approval DECISION. The host writes the
instance's landHolders entries into session.json as landing, and the
runner takes one fixed-template turn per entry before its receive loop.
Row 149's chief-of-staff message stays as the fallback; a second land
gets decision-consumed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:03:28 -05:00
jason.woltjeandClaude Opus 5.5 a132b46058 queue: revisions 1713-1720 (rows 124, 154, 155 briefed and 124, 155 re-pinned by Sage; rocko 124 and darkwing 155 claims; filbert row 149 round 3 changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:03:16 -05:00
jason.woltjeandClaude Opus 5.5 a3c56343ac sessions: Filbert row 149 r3 review (changes, comment 28913)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:00:14 -05:00
jason.woltjeandClaude Opus 5.5 9098c33f7f sessions: Sage row 87 rounds 5-8 and done, row 149 rulings, row 156, I2-I6, row 128 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:52:31 -05:00
jason.woltjeandClaude Opus 5.5 6f85a55189 queue: revisions 1675-1712 (row 87 rounds 5-8 and done, I2-I6 as rows 157-161, row 156 added, row 128 round 1 and done, row 149 round 3 in review)
Other seats' revisions in this sweep: Darkwing 1675, 1680, 1685, 1691,
1699 (row 87 verdicts); Filbert 1678 (row 149 r2), 1681, 1686, 1690,
1701 (row 87), 1702 (row 128 r1); Researcher 1692-1693 (row 128
in-review); Dewey 1709-1711 (row 149 round 3 in review).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:50:42 -05:00
jason.woltjeandClaude Opus 5.5 ade628c8b5 cli: mosaic makes projects and threads and talks in a named thread (row 128, #1622)
Row 128 round 1, Researcher's candidate (agents/researcher/work/queue-128/,
row128.patch, manifest 9eed6a78, 8 paths, base 0f3eb25ea). Filbert
approved round 1 (comment 28895).

`mosaic project create` and `project list` call project.create and
project.list; `init` and `check` keep their arguments and refusals.
`mosaic threads`, `thread create`, `thread open` and `thread read` cover
the thread verbs, and `mosaic talk --thread` sends thread.send as a
REQUEST and prints only the reply to the sent message. `mosaic follow
--thread` is the same loop without the send. A thread reference is its
id, an id prefix of 8 or more characters, or <project>/<name>; unknown,
short and ambiguous references refuse with exit 2 before any send. Every
verb refuses inside an agent run. There is no default thread, so a plain
`talk assistant` still meets thread-required-use-thread-send.
Acceptance 314 step 1 now creates the project and the assistant thread.
New: packages/cli/src/thread.mjs, packages/cli/tests/thread-cli.test.mjs.

Sage's landing gate ran on 77b1a6e8a plus the candidate: 25 steps, all
rc 0 (15 packages, 10 script suites), 14:44:02Z to 14:48:42Z, network
sage-r128_default removed. The commit sits on 7de8b7270, which adds one
docs/SESSIONS.md line that no test reads.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:49:14 -05:00
jason.woltjeandClaude Opus 5.5 7de8b7270a sessions: Dewey row 149 round 3 build, slot runs and review request (comment 28902)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:45:03 -05:00
jason.woltjeandClaude Opus 5.5 77b1a6e8a9 plans: row 87 follow-ups on the start limit, and decisions 147 (B1) and 148 (B2) (#1581)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:41:43 -05:00
jason.woltjeandClaude Opus 5.5 db1519637b sessions: Filbert row 87 r8 and row 128 r1 reviews (approve, comments 28894 and 28895)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:40:24 -05:00
jason.woltjeandClaude Opus 5.5 bb0766826d plans: brief for the host starting the landing run once a merge decision is approved (#1651)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:35:56 -05:00
jason.woltjeandClaude Opus 5.5 917ef55e18 plans: row 87 round 8 counts a stop's no-such-container as gone, bounds each wait per container and gives I6 the unit comment (#1581)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:33:41 -05:00
jason.woltjeandClaude Opus 5.5 3fd0b387f3 sessions: Filbert row 87 r7 review (approve, comment 28889)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:30:59 -05:00
jason.woltjeandClaude Opus 5.5 4c1fee85a5 plans: row 87 round 7 makes the leftover rule wait by id until each core container is gone and names the start order (#1581)
Answers Darkwing's round 6 review (28884) and Filbert's doc-only
follow-up (28883).

- After docker stop, the rule waits for "no such container" by id,
  bounded by the core stop timeout, then rm -f and the same wait; a
  container still there refuses exit 1, which the unit retries.
- A name conflict is now only a hand-made container without the store's
  labels; it stays exit 3, which the unit does not retry.
- Start order: host.lock, store path, rootless check, leftover rule,
  docker run.
- I6 gains two tests: run straight after a stop boots, and a leftover
  never removed refuses exit 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:28:56 -05:00
jason.woltjeandClaude Opus 5.5 e1f13a54d2 sessions: Filbert row 87 r6 review (approve, comment 28883)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:25:57 -05:00
jason.woltjeandClaude Opus 5.5 80ebe2afc4 plans: row 87 round 6 gives the store one real-path spelling, starts each core fork on its first line and states the name-conflict refusal (#1581)
Answers Darkwing's round 5 review (28880) and Filbert's doc-only
follow-ups (28881).

- connected follows the channel from the wrapper's creation, as a forked
  child's does; the entry starts the broker on boot and the notifier on
  start, so host.mjs:217 and :244 never wait on a pid line.
- Bus start makes <dataRoot>/bus at 0700 and takes its real path; the
  bind source, mosaic.store label, leftover filter and hostStatus all use
  it.
- A name conflict refuses with exit 3, names the container and the
  client's exit code, gives host.lock back, and removes nothing.
- The host.pids test list names what each test does with the pid.
- I6 gains four lifecycle tests for these.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:23:53 -05:00
jason.woltjeandClaude Opus 5.5 b04014e592 sessions: Filbert row 87 r5 review (approve, comment 28881)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:20:16 -05:00
jason.woltjeandClaude Opus 5.5 75b4af0bcc sessions: Filbert row 149 r2 review (changes, comment 28878)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:17:24 -05:00
jason.woltjeandClaude Opus 5.5 232e054499 plans: row 87 round 5 keys the core container on the store, sets the stop timeout to cover both close waits, and defines connected and host.pids (#1581)
Answers Darkwing's round 4 review (comment 28877) and Filbert's
doc-only follow-up (28876).

- P: the core container's name is mosaic-core-<16 hex of SHA-256 of the
  real path of <dataRoot>/bus>, keyed on the store like writer.lock, so
  two data roots with the same business don't collide.
- Q: close() waits CLOSE_TIMEOUT_MS twice (host.mjs:30, 343, 347). The
  host passes MOSAIC_CLOSE_TIMEOUT_MS to the entry; the core stop
  timeout is two close timeouts plus 5 s (45 s today), used by the
  leftover rule's docker stop -t.
- R: {from, pid} start line; connected turns false on the exit line,
  client stdout close or exit, or a failed stdin write; host.pids is
  null for both forks in containerized mode and adds container; the
  pid-signalling host tests stay local, I6 uses docker exec kill.
- Nit: firstReply (129) and ended (148) cited apart from watchChildren
  (159-165).
- I6 tests and design section 9 add the slow-notifier leftover, two data
  roots, the missing-timeout refusal and connected.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:16:30 -05:00
jason.woltjeandClaude Opus 5.5 bf7d09c6cd sessions: Sage row 87 rounds 3-4, row 128 brief, queue sweep
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:13:22 -05:00
jason.woltjeandClaude Opus 5.5 c95c437cb6 queue: revs 1659-1674, row 87 rounds 3 and 4 (round 4 approved by Filbert), row 128 assigned to Researcher, row 149 round 2
Sage: 1659-1661 (row 87 round 3 to in-review, comment 28868), 1667-1669
(row 128 assigned to researcher, brief re-pinned, briefed), 1671-1673
(row 87 round 4 to in-review on e5dbcf626, comment 28874).

Other seats' revs, swept in by the snapshot: Filbert 1662 (row 87 r3
changes, 28870) and 1674 (row 87 r4 approve, 28876), Dewey 1663, 1665, 1666 (row 149 round 2 in-review, 28872),
Darkwing 1664 (row 87 r3 changes, 28871), Researcher 1670 (row 128
in-progress).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:12:46 -05:00
jason.woltjeandClaude Opus 5.5 125531d5fc sessions: Filbert row 87 r4 review (approve, comment 28876)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:11:37 -05:00
jason.woltjeandClaude Opus 5.5 e5dbcf6264 plans: row 87 round 4 adds the core container's lifecycle and bus status liveness, answering Darkwing's and Filbert's round 3 reviews (#1581)
Design section 3 gains "The core container's lifecycle": the fixed name,
labels and --rm; tagged fork lines that carry each fork's replies, kills
and exit codes, so the host's per-child stop, start-failure kill and
watchChildren keep their logic; what the entry does when one fork exits;
the client ending first; SIGTERM; the host dying without closing stdin
(not spiked, I6 measures it); the leftover removal at bus start; and
bus status liveness from the engine instead of the lock's container pid.
B2 now names caps on the entry's engine-relayed stdout. Section 9 and
I6's files and tests carry the new rules. Broker cites move to
0f3eb25ea after row 143; LAUNCH_OPS is process.mjs:5-20.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:08:45 -05:00
jason.woltjeandClaude Opus 5.5 d5025c8f2e sessions: Dewey row 149 round 2, rebase onto ac55e84fe
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:05:13 -05:00
jason.woltjeandClaude Opus 5.5 0f3eb25ea0 plans: refreshed row 128 brief, the bus project and thread verbs and talk --thread (#1622)
The 2026-10-14 brief predates rows 121, 142 and 143. This one names the
verbs on the existing broker ops, keeps plain talk asking for a thread
with no default, and targets acceptance 314 steps 1 and 3. Owner moves
to researcher.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:03:57 -05:00
jason.woltjeandClaude Opus 5.5 4af594fea0 sessions: Filbert row 87 r3 review (changes, comment 28870)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 09:00:59 -05:00
jason.woltjeandClaude Sonnet 5.5 3e91d528f3 sessions: Researcher row 148 lines of 2026-10-16 (steps 1 and 1b, rounds 1 to 4)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-18 08:59:28 -05:00
jason.woltjeandClaude Sonnet 5.5 2d6681a4d4 sessions: Researcher row 143 build, rounds 1 and 2, landing and worktree removal
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-18 08:58:52 -05:00
jason.woltjeandClaude Opus 5.5 ac55e84fe4 plans: row 87 round 3 adds the host channel and keeps bindLaunch, answering Darkwing's and Filbert's round 2 reviews (#1581)
Darkwing L (comment 28863): section 3 gains "The host channel, a
boundary change". The bus host starts the core container with
`docker run -i` as its own child; core-entry.mjs forks process.mjs and
notifier-process.mjs with IPC inside it and relays tagged JSON lines over
the client's stdin and stdout. The core container runs with
--log-driver none, since replies carry caps; I6 tests that. The channel
adds no authentication of its own, so it is boundary change B2.

Darkwing M: the registry keeps the docker run client's pid and
startTime, so bindLaunch, runtime.mjs and broker.mjs are unchanged and
I5 changes no file in packages/bus. The human proof's ancestry check
never meets the client, so the mount list carries that boundary and I5
tests that a session can't reach sock/human or sock/relay. recover()
removes the container, kills a leftover client and binds as today.

Darkwing nits: the start check's temp names are patterns from their
writers, five state files, and the git-socket.mjs split across I3, I5
and I6.

Filbert (comment 28864): section 10 states the Console's outbound
reach on the mosaic-console bridge, no wider than today, with the
--network none forwarder as the not-spiked follow-up named on P19 333.
Section 7 says four networks and lists mosaic-console.

Section 11 lists B1 (the Console bind, before I4) and B2 (the host
channel, before I6); I4's and I6's gates name their ruling.

Doc-only: no test reads either file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:56:57 -05:00
jason.woltjeandClaude Opus 5.5 e1964c854b docs: BUILD-LOG for the row 143 landing; Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:56:31 -05:00
jason.woltjeandClaude Opus 5.5 6e7fdf7ffc queue: revisions 1645-1658 (row 143 done)
Sage: 1645-1646 (re-pins of 143 and 149 to 8fcd0887e), 1650-1651 (row 143 round 2 review request), 1652-1654 (row 87 in-review r2), 1658 (sage-143-done; landed as 0278b9e0b).
Researcher: 1647-1649 (row 143 round 2 in-progress and in-review).
Dewey: 1655 (dewey-143-verdict-2, round 2 approve, comment 28862).
Darkwing: 1656 (darkwing-r87-r2-verdict, row 87 round 2 changes, comment 28863).
Filbert: 1657 (filbert-87-r2, row 87 round 2 changes, comment 28864).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:55:09 -05:00
jason.woltjeandClaude Opus 5.5 0278b9e0be bus, harness, business, roles: agents review Jason's spec commits, and the assistant summarizes (row 143, #1637)
Row 143 round 2, Researcher's candidate (agents/researcher/work/queue-143/,
row143.patch aff7cff0, manifest a34136bf, 11 paths, base 8fcd0887e).
Dewey approved round 2 (comment 28862) after round 1's changes (28853).

A human save of spec/north-star.md through project.file.save stores one
notice per pm and cto instance, from _bus in no thread, in its own
transaction after the commit. If the notice can't be stored the save
answers outcome-unknown and the commit stands. pm and cto answer the
chief of staff once, a BLOCK naming each conflict or an INFO beginning
"Accept"; the broker checks a BLOCK's sender, recipient and in_reply_to
notice. The chief's relay that replies to the BLOCK is filed in Jason's
most recently opened assistant thread in the notice's project. With no
such thread the relay is refused and the commit still stands. No other
save, commit or verb sends a notice, and a notice to an unrun role waits
for a claim and launches nobody. No schema change: notices and answers
are messages rows.

Contracts: roles/pm.md and roles/cto.md say how to answer,
roles/chief-of-staff.md says to relay in_reply_to the carrying message,
roles/assistant.md says to tell Jason in its own words that the change
stands. New tests: packages/bus/tests/spec-review.test.mjs and
packages/business/tests/spec-review-contracts.test.mjs.

Sage's landing gate ran on 80a2e442b plus the candidate: 25 steps, all
rc 0 (15 packages, 10 script suites), 13:46:52Z to 13:51:25Z, network
sage-r143_default removed. The commit sits on 970e88bf2, which adds one
docs/SESSIONS.md line that no test reads.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:53:52 -05:00
jason.woltjeandClaude Opus 5.5 970e88bf21 sessions: Filbert row 87 r2 review (changes, comment 28864)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:47:43 -05:00
jason.woltjeandClaude Opus 5.5 80a2e442bf sessions: Dewey row 143 r2 review (approve, comment 28862)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:46:14 -05:00
jason.woltjeandClaude Opus 5.5 47b030d9bc plans: row 87 round 2 answers Filbert's and Darkwing's reviews of the containerized install design (#1581)
The Console binds 0.0.0.0 in its own namespace behind a host-loopback
publish (a decision 107 enforcement change, reach unchanged). It mounts
businesses/ and its other mounts at their host paths, checks bus-host/
against an allowlist, turns the queue panel off, and answers 404 on every
board route. The git socket's bundles and land logs move to git-host/.
recover() still ends leftover runs host-lost. RESOLVED_VIA gets a
human-socket entry, and every container kind gets mount-list and
hardening tests. Spike citations are corrected, and the root case and
two-network setup are marked as not spiked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:41:55 -05:00
jason.woltjeandClaude Opus 5.5 8fcd0887e0 plans: row 143 round 2 adds the chief-of-staff relay bullet, and acceptance 314 needs row 128's thread (#1637)
Dewey's F5 on comment 28853: nothing tells the chief of staff to relay a
BLOCK or an accept in_reply_to the message that carried it, and the bus
refuses a relay without that link. Row 143's brief now owns one bullet in
roles/chief-of-staff.md.

Dewey's F1: the assistant is the threaded instance, so a plain talk is
refused thread-required-use-thread-send, and no CLI code creates a project
or a thread before row 128. Acceptance 314 adds row 128 to its
prerequisites and a step that creates and opens Jason's assistant thread
in the project.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:28:33 -05:00
jason.woltjeandClaude Opus 5.5 c550efa278 queue: revisions 1632-1644 (re-pins of 149 and 143; row 143 in-review r1 for Researcher with the request 28849 and Dewey's changes 28853; row 87 in-review r1 with Filbert's 28851 and Darkwing's 28852 changes; row 149 in-review r1 for Dewey, request 28855)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:27:04 -05:00
jason.woltjeandClaude Opus 5.5 8c0fdc8071 sessions: Dewey row 149 in-review and row 143 r1 review (changes, comment 28853)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:25:25 -05:00
jason.woltjeandClaude Opus 5.5 510382ea38 sessions: Filbert row 87 r1 review (changes, comment 28851)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:20:28 -05:00
jason.woltjeandClaude Opus 5.5 2034817252 plans: containerized install design and the I2 to I6 briefs (#1581)
Row 87, the I1 design. The design turns decisions 105 to 108 into
interfaces: one rootless endpoint named on every call, three images,
two broker listeners with a human-socket via value, a host relay with a
peer-pid check, sessions as hardened containers, file-held tokens,
egress only through an allowlist proxy, and the Console in a container
on the human socket. Every container claim cites spikes S0 to S9 or HP.

Mos ruled on the host-process reading (mos-0657): routes open to a
process under Jason's uid are decision 105's accepted limit; the
other-uid reach to the Console port goes to Jason as P19 333, default a
documented limit, and only I6's limits section waits on it.

The rows file holds the I2 to I6 briefs on the template, each with
owner, reviewers, files, what ships and gate. Doc-only: no test reads
either file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:11:17 -05:00
jason.woltjeandClaude Opus 5.5 c0af7f2486 plans: no verb adds a role instance, so the 149 brief and acceptance 314 name Jason's business-file edit (#1643)
Dewey 0572: organization.set's instances field only sets the model of an
instance the business file already declares (launch-settings.mjs:46-52),
and business.mjs:130 refuses a launch.instances name the roles don't
declare. The row 149 brief said chief's organization change adds the
merge-gate and security-reviewer instances, and acceptance 314's
before-the-run list repeated it. Both now say Jason adds them by editing
the business file and validates it. Doc-only; no test reads either file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 08:01:29 -05:00
jason.woltjeandClaude Opus 5.5 8517e5f451 queue: revisions 1629-1631 (rows 143 and 149 in progress for Researcher and Dewey, row 87 in progress for Sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 07:46:00 -05:00
jason.woltjeandClaude Opus 5.5 8d47d61848 docs: acceptance 314 step 8 reads task trails, so mosaic trail gets its argument (#1524)
Filbert's row 135 round 3 note 1: step 8 ran mosaic trail with no
argument, but the CLI takes <task|decision>. The step now lists the
tasks and reads each task's trail, which includes the decisions filed
under it and the spec commit each cites. Doc-only: no test reads
docs/acceptance/314.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 07:43:26 -05:00
jason.woltjeandClaude Opus 5.5 4b1795a8c4 queue: revisions 1627-1628 (briefs for 149 and 143 re-pinned to 3a16dfac8, row 149 carries the raise_decision domain parameter)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 07:35:33 -05:00
jason.woltjeandClaude Opus 5.5 3a16dfac8b plans: row 149 carries Dewey's row 139 finding 1, the raise_decision domain parameter (#1643)
The harness raise_decision tool has no domain parameter, so no agent
reaches arbiters.technical and merge-gate's technical raise can't be
made. Row 149 now owns that tool (not end_run, which stays row 155's),
plus the pm and chief Authority text that still sends every ungranted
action to the human. Mos accepted the carry on 2026-10-16.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 07:34:02 -05:00
jason.woltjeandClaude Opus 5.5 b9c1dcd82f docs: re-pin plan for rows 148 and 141 with the bus 3f to 3g migration (mos-0638)
One shared window: copy de352dfaf and the 52f35e7a0 rollback set made,
business check under both copies, rehearsal and restore scripts tested on
fixture stores, row 141 manifest checks and a row 148 Console check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:50:19 -05:00
jason.woltjeandClaude Opus 5.5 d4c3b2838d docs: BUILD-LOG for the row 141 landing; Sage SESSIONS line; row 141 r2 packet; P19 follow-ups 321-322; row 155 carry line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:37:22 -05:00
jason.woltjeandClaude Opus 5.5 28eeef8eac queue: revisions 1625-1626 (row 141 done)
Filbert: 1625 (filbert-141-r1, round 1 approve, comment 28841).
Sage: 1626 (sage-141-done; landed as de352dfaf, #1635 closed 28844).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:36:48 -05:00
jason.woltjeandClaude Opus 5.5 de352dfaff bus, cli, business, harness: messages addressed by agent name (row 141, #1635)
Row 141 round 1, Darkwing's candidate (agents/darkwing/work/queue-141/,
build.patch bb036c6d, manifest d474b330, 26 paths, base 18aee8fa1),
rebased without change by Sage onto 7e40fb83b as round 2
(agents/sage/work/queue-141-r2/, build.patch 72b890d9, manifest
cae29087). Row 148 moved two of the paths, packages/bus/README.md and
packages/bus/src/broker.mjs; the interdiff touches only index and @@
lines. Filbert approved round 1 (comment 28841).

Schema 3f->3g adds instance_names, an append-only log whose latest row
per instance is its current name. message.send resolves a to that is
not an instance key or a role through the names table, and records
to_name. The new bus writer name.set and read names back the CLI verbs
mosaic name INSTANCE NAME (human-only) and mosaic names [--json].

`instance.name` is routed under REQ-MSG-2 (1.5). It joins ACTIONS and
ROUTED in the vocabulary and the broker, and roles/chief-of-staff.json
grants it within-role, as row 139's brief asks of whichever of 139 and
141 lands second. The chief has no tool for it until row 155, so the
chief's untypedActions and bundle untyped list are ["instance.name"]
(harness tools.test.mjs:43, bundle.test.mjs:94).

Follow-ups: Vikunja P19 tasks for TOOLS.md and acceptance 314 step 10
(Filbert, doc-only) and for refusing a new key or definition equal to a
current name. Row 155's brief carries the return of untyped to [].

Sage's landing gate ran on 7e40fb83b plus the candidate: 25 steps,
all rc 0 (15 packages, 10 script suites), 22:30:48Z to 22:35:15Z,
network sage-r141_default removed. The commit sits on 2a85b41da,
which adds one docs/SESSIONS.md line that no test reads.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:35:27 -05:00
jason.woltjeandClaude Opus 5.5 2a85b41dac docs: Filbert SESSIONS line for the row 141 round 2 rebase check
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:31:43 -05:00
jason.woltjeandClaude Opus 5.5 7e40fb83b5 docs: Filbert SESSIONS line for the row 141 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:28:19 -05:00
jason.woltjeandClaude Opus 5.5 d49da64a95 queue: revision 1624 (row 148 done)
sage 1624.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:28:07 -05:00
jason.woltjeandClaude Opus 5.5 625afe15f7 docs: BUILD-LOG for the row 148 landing; Sage SESSIONS line; P19 follow-ups 318-320 script and record
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:26:09 -05:00
jason.woltjeandClaude Opus 5.5 d4ea7e5a69 webui, bus, project: a Console editor for the project's spec and docs (row 148, #1642)
Row 148 round 3, Researcher's candidate (agents/researcher/work/queue-148/,
row148.patch 1b63164b, manifest 95ae4736, 27 paths, base 18aee8fa1).
Dewey approved round 3 (comment 28832) and Filbert approved it with no
findings (28835). Mos gave the decision 123 go on public diff 8360122b
(mos-0636): no vendored code, no innerHTML or other HTML sinks.

The Docs page #/docs lists each project's spec/north-star.md and the
Markdown files directly under docs/. Save writes the typed text as one
local commit authored by business.human, through a new human-only bus
verb project.file.save that carries the opened commit as base and
refuses stale-base if the file moved. The preview is a textarea plus a
renderer built with createElement and textContent only; raw HTML shows
as source text and images never load. CSP is unchanged.

Round 3 replaced the backreference HR regex, which overflowed the
regexp stack, with an isRule scan. Round 4 added a textContent fallback
when paintDoc throws, and the submit handler resets saving in a finally.
Mos's rule (ceiling 1 s at 1 MiB, growth x3 per doubling over 1 to
4 MiB): 130/130 and 138/138 cells pass, worst 513 ms at 1 MiB, worst
ratio x2.50, control x2.01; the 756-cell grid has 0 timeouts. Mutants
43/44, v21 equivalent.

Follow-ups: Vikunja P19 tasks 318 (Save stays off after a non-render
paint throw), 319 (dead NUL check in bus.js) and 320 (the 1 MiB + 64 KiB
buffer before the human proof, Gitea #1650).

Sage's landing gate ran on a4779d2a5 plus the candidate: 25 steps,
all rc 0 (15 packages, 10 script suites), 22:19:24Z to 22:24:09Z,
network sage-r148g_default removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:24:39 -05:00
jason.woltjeandClaude Opus 5.5 a4779d2a55 queue: revisions 1588-1623 (rows 137, 141, 148, 150)
darkwing 1588 1590 1591 1594 1596-1598 1600 1607 1608 1621 1622; filbert 1592 1599 1602 1609 1611 1623; dewey 1595 1610 1613 1620; researcher 1603 1604 1615-1617; sage 1589 1593 1601 1605 1606 1612 1614 1618 1619.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:15:01 -05:00
jason.woltjeandClaude Opus 5.5 fc5a8b74d2 docs: Filbert SESSIONS line for the row 148 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:14:15 -05:00
jason.woltjeandClaude Opus 5.5 e1e8dda0b1 docs: Dewey SESSIONS line for the row 148 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:04:40 -05:00
jason.woltjeandClaude Opus 5.5 862af30793 docs: BUILD-LOG for the 52f35e7a0 re-pin and the row 148 round 3 send-back; Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 16:22:47 -05:00
jason.woltjeandClaude Opus 5.5 dc12dc9c0c sage: window plan for the 52f35e7a0 re-pin, rows 135, 139, 150 and 137
Re-pin 3190ad762 -> 52f35e7a0, no migration, per Mos's 20:58Z message.
The plan puts row 137's refs/heads/root write in the window. One working
repo, mosaic-stack/stack.git. Its seed root is the canonical checkout.
Jason's ~/.mosaic-dev/projects/mosaic-stack/stack is named and is not
touched. working-refs.sh records the refs before and after; root-rollback.sh
deletes only the seed-created refs/heads/root, matched by sha, and leaves
main alone. Copy and rollback set (sage-pin-rollback-3190ad762) are made
and checked. Nothing live ran.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 16:10:06 -05:00
jason.woltjeandClaude Opus 5.5 22dbc2b281 dewey: row 137 live smoke plan packet (#1631)
SMOKE.md for the shared 139/137 re-pin window (sage-0613), from the code at
18aee8fa1. Doc only; nothing run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 16:01:59 -05:00
jason.woltjeandClaude Opus 5.5 52f35e7a09 docs: BUILD-LOG for the row 150 and 137 landings and the row 148 round 2 send-back; Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:58:10 -05:00
jason.woltjeandClaude Opus 5.5 18aee8fa17 sage: row 137 round 5 rebase packet (#1631)
Rebase-only round onto 435d1c616 after row 150, checked by Filbert (28818).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:56:07 -05:00
jason.woltjeandClaude Opus 5.5 f1ee9a9895 bus, business: agents land approved merges into the business working repository (row 137, #1631)
Row 137 round 4, Darkwing's candidate (agents/darkwing/work/queue-137/r4/,
build.patch 0cabe175, manifest 548ee477, 31 paths, base 63c82f1c4).
Filbert approved round 4 (comment 28809) and Dewey approved it (28816)
after round 3 changes (28801, 28805). Darkwing's round 4 ran 9/9 mutant
kills, bus 317, seat 150, land-work 86.

Round 5 is a rebase only (agents/sage/work/queue-137-r5/, build.patch
d6cbaaa8, manifest f73598fe). Row 150 changed docs/TOOLS.md, broker.mjs
and cli/README.md under it. The round 4 patch applied unchanged onto
435d1c616, and with index and @@ lines removed the two patches are
byte-identical. Filbert checked the interdiff (comment 28818).

A landing moves refs/heads/main in <dataRoot>/working/<business>/
<project>.git, never the project root or canonical refactor. The seed now
fetches the root as refs/heads/root and fast-forwards main only when it
is an ancestor, so a landed main survives the next push. Only a
cross-role merge decision lands. Its squash names the real channel:
"via the CLI" for the human, and "as its arbiter, via the broker" for an
arbiter's decision.resolve. Every other class refuses not-authorized
before the human check.

This carries row 139's two items. REQ-LAND-1: a gated merge decision
never lands. REQ-DEC-2: the host land op accepts an arbiter-resolved
merge. land-work's read.mjs stays human-only, and land-work.sh still
accepts only a human CLI resolution.

Sage's landing gate ran on 435d1c616 plus the candidate: 25 steps,
all rc 0 (15 packages, 10 script suites), 20:51:00Z to 20:55:34Z,
network sage-r137_default removed. Cherry-picked onto 5d98fb194, which
added only a docs/SESSIONS.md line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:56:00 -05:00
jason.woltjeandClaude Opus 5.5 5d98fb1944 sessions: Filbert row 137 round 5 rebase check
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:51:55 -05:00
jason.woltjeandClaude Opus 5.5 435d1c616e sessions: Dewey row 137 round 4 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:49:56 -05:00
jason.woltjeandClaude Opus 5.5 7e0147adf3 sessions: Filbert row 148 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:48:51 -05:00
jason.woltjeandClaude Opus 5.5 b0424e9cc0 sessions: Dewey row 148 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:47:12 -05:00
jason.woltjeandClaude Opus 5.5 0f7bca083f cli, webui: the human marks skipped messages read, and the Console says what talk prints (row 150, #1644)
Row 150 round 2, Darkwing's candidate (agents/darkwing/work/queue-150/r2/,
manifest 7556dda0, 7 paths), a rebase-only round onto 63c82f1c4 after
row 139 changed broker.mjs. Filbert approved round 1 (comment 28800) and
the rebase (comment 28806): only broker.mjs's index line and hunk offset
moved. Darkwing's r2 round ran 6 files, ROUND_PASSED.

`mosaic inbox --read <id>...` and `--read-all` mark the human's unread
messages read. Each argument is tried; refusals print their code and the
command exits 2. The Console's send section now names `mosaic inbox` and
`mosaic inbox --read`. broker.mjs changes a comment only.

The public/ diff is sha256 096d922c, unchanged from round 1 and covered
by Mos's decision 123 go.

Sage's landing gate ran on 4ebef2284 plus the candidate: 25 steps,
all rc 0 (15 packages, 10 script suites), 20:41:55Z to 20:46:24Z,
network sage-r150g_default removed. Cherry-picked onto 1ad48035c, which
added only a docs/SESSIONS.md line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:46:40 -05:00
jason.woltjeandClaude Opus 5.5 1ad48035cc sessions: Filbert row 137 round 4 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:46:20 -05:00
jason.woltjeandClaude Opus 5.5 4ebef22846 sessions: Filbert row 150 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 15:00:00 -05:00
jason.woltjeandClaude Opus 5.5 6ab4e374a8 sessions: Filbert row 137 round 3 correction
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:58:55 -05:00
jason.woltjeandClaude Opus 5.5 579728e608 sessions: Filbert row 137 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:57:46 -05:00
jason.woltjeandClaude Opus 5.5 79faf97ad2 sessions: Dewey row 137 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:56:10 -05:00
jason.woltjeandClaude Opus 5.5 690224f4a9 sessions: Filbert row 150 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:51:06 -05:00
jason.woltjeandClaude Opus 5.5 c492f4aaad docs: BUILD-LOG for the row 139 landing (Q169 hard waits, #1633); Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:33:29 -05:00
jason.woltjeandClaude Opus 5.5 9752d18189 queue: revs 1582-1587, row 139 done, row 139 and 148 verdicts, row 150 in review
Filbert's 1582 (row 139 round 2 approve) and 1585 (row 148 round 1
approve), Darkwing's 1583-1584 (row 150 in review), Dewey's 1587 (row
148 round 1 verdict) and Sage's 1586 (row 139 done after landing
63c82f1c4).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:32:25 -05:00
jason.woltjeandClaude Opus 5.5 4d6cc8537c sessions: Dewey row 148 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:31:39 -05:00
jason.woltjeandClaude Opus 5.5 63c82f1c47 bus, business: hard waits reach only the human, and only the chief of staff removes the assistant (row 139, #1633)
Row 139 round 2, Dewey's candidate a4c5657f (14 paths) on 5661bd233.
Filbert approved round 2 (comment 28786) after round 1 changes. Dewey's
gate: 14 runs rc 0. Mutants m1-m9 killed. Filbert ran 7 files singly:
0 fail, 0 skipped.

The broker's GATED and the business's GATED_ONLY are now the hard waits.
A cited gated decision goes to the domain arbiter unless it is a hard
wait, which always goes to the human. Under Jason's ruling Q169, a
role.revoke of the assistant is a hard wait. Its end gets past
END_PROTECTED only for the chief of staff, or when the cited decision
was resolved by the human through the CLI. PRD version 1.6 records Q169.
Mos approved the 1.6 text, including the REQ-ROLE-2 sentence on the
chief's end and revoke. scripts/test-land-work.sh adds the not-human
cases for arbiter-resolved merges. land-work's read.mjs is unchanged.

Row 137 lands second, so it carries the gated-merge refusal and the host
land op's arbiter acceptance. Making an end or revoke stop an assistant
run, and configuring the assistant, are row 155 (#1649).

Sage's landing gate ran on 26b78d483 plus the candidate: 25 steps, all rc 0 (15 packages, 10 script suites), 19:23:57Z to 19:28:28Z, network sage-r139_default removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:30:23 -05:00
jason.woltjeandClaude Opus 5.5 8986f41aa2 sessions: Filbert row 148 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:29:36 -05:00
jason.woltjeandClaude Opus 5.5 26b78d4838 sessions: Filbert row 139 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:22:07 -05:00
jason.woltjeandClaude Opus 5.5 e7158ab141 queue: revs 1577-1581, rows 139 and 137 round 2 in review, row 155 re-pin
Dewey's revs 1577-1578, Darkwing's revs 1580-1581, my rev 1579.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:20:34 -05:00
jason.woltjeandClaude Opus 5.5 b05de8258b plans: row 155 refuses a definition change into END_PROTECTED and tests the chief's revoke as a hard wait
Dewey's two edges from row 139 round 2 (dewey-0546).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:18:23 -05:00
jason.woltjeandClaude Opus 5.5 8ec1cc1a29 sessions: Dewey, row 139 round 2 in-review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:17:30 -05:00
jason.woltjeandClaude Opus 5.5 c83ee5d82b agents: Dewey row 139 round 2, Mos's REQ-ROLE-2 sentences (#1633)
PRD 1.6 REQ-ROLE-2 gains Mos's two sentences (sage-0545): the chief ends
the assistant with no decision, and any revoke of it is a hard wait.
Checked against broker.mjs; the code already does both. New candidate:
patch a5f21ead, manifest a4c5657f. Both PRD-reading tests pass; repro OK.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:16:13 -05:00
jason.woltjeandClaude Opus 5.5 9e1ec7bf2f queue: revs 1572-1576, row 137 review round 1 and round 2 start, row 150 start
Darkwing's revs 1572-1574 and 1576, Filbert's rev 1575.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:12:58 -05:00
jason.woltjeandClaude Opus 5.5 96fd8593e6 sessions: Dewey, row 139 round 2 for Q169
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:11:58 -05:00
jason.woltjeandClaude Opus 5.5 c740f56ee6 agents: Dewey row 139 round 2 packet, Jason's ruling Q169 (#1633)
Candidate manifest 7a1fcae1 (patch 7a29d911) on 5661bd233. Only the chief of
staff, or a decision the human resolved through the CLI, ends or revokes the
assistant; a revoke of it is a hard wait. PRD 1.6 text in PRD-1.6.md for Mos.
Gate 14/14 rc=0 and mutants m1-m9 killed under slot sage-0533. SMOKE.md plan,
not run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:11:43 -05:00
jason.woltjeandClaude Opus 5.5 39f1e9c51d sessions: Filbert row 137 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:08:04 -05:00
jason.woltjeandClaude Opus 5.5 eff8417b63 sessions: Sage, Q169 and Q168 follow-through
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:57:06 -05:00
jason.woltjeandClaude Opus 5.5 cc1f38f2eb queue: revs 1545-1571, rows 154 and 155 added, Q169 re-pins, row 148 review request
Sweeps other seats' pending revs: Dewey 1545-1546 and 1555, Filbert 1547,
Researcher 1559-1560. Sage: 1548-1554, 1556-1558, 1561-1571.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:56:54 -05:00
jason.woltjeandClaude Opus 5.5 b71268b8e5 plans: row 139 records Q169 as PRD 1.6 and leaves assistant-run execution to row 155
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:52:51 -05:00
jason.woltjeandClaude Opus 5.5 2857006be4 plans: Q169 brief, the human and the chief of staff configure the assistant (#1649)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:52:02 -05:00
jason.woltjeandClaude Opus 5.5 9aa90193d9 plans: row 139 follows Jason's Q169, only the chief of staff or the human removes the assistant
Q169 replaces Mos's sage-0523 ruling. The chief of staff's end of the
assistant succeeds, another role's arbiter-resolved revoke of it is
refused, a human-resolved one still works, and other cited revokes route
by REQ-DEC-2. Reconfiguring the assistant is a separate row.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:45:48 -05:00
jason.woltjeandClaude Opus 5.5 04c5720039 plans: row 139, a revoke of the assistant stays with the human (Mos ruling, sage-0523)
Filbert's row 139 note 2: under 139 an arbiter could approve a cited
role.revoke of the assistant. Mos ruled it stays human-only, overridable
by Jason. Row 139 now owns the PRD note under REQ-WAIT-1 and a test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:40:05 -05:00
jason.woltjeandClaude Opus 5.5 fcea7d015c plans: Q168 brief, a WYSIWYG Console editor built from reviewable source (#1648)
Jason's ruling Q168 via Mos: one more library search against the row 148
bar, then an open-source WYSIWYG built from source in the repo if nothing
passes. Mos's decision 123 go on the license and candidate comes before
any code is imported.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:38:17 -05:00
jason.woltjeandClaude Opus 5.5 54aac2bd05 sessions: Filbert row 139 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:38:01 -05:00
jason.woltjeandClaude Opus 5.5 0f29b19e92 docs: SESSIONS line for Dewey's row 139 round 1 in-review (#1633)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:34:26 -05:00
jason.woltjeandClaude Opus 5.5 62f354746d agents: Dewey row 139 round 1 packet (#1633)
Candidate on 5661bd233: GATED and GATED_ONLY become PRD 1.5's eight hard
waits (data.delete, network.expose and account.create are new), derived
as ACTIONS minus an explicit ROUTED list (REQ-WAIT-3). A gated raise
citing the current spec goes to the domain arbiter unless it is a hard
wait. land-work refuses an arbiter-resolved merge with not-human.
Mutants 5/5 killed; gate green (bus, business and every scripts/test-*.sh).
Row 137 has not landed, so its REQ-LAND-1 refusal and the host-op
arbiter acceptance are left out, as BUILD.md says.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:33:30 -05:00
jason.woltje 63025db0fa queue: revisions 1538-1544 (briefs for 137, 139, 143, 148 and 149 re-pinned to b2a2d741c; row 139 briefed; Dewey's 1544 row 139 start) 2026-10-16 13:33:11 -05:00
jason.woltjeandClaude Opus 5.5 b2a2d741c4 plans: rows 137 and 139, land-work keeps its human-only check; only the host land op accepts an arbiter, carried by whichever lands second (Dewey 0515)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:18:38 -05:00
jason.woltjeandClaude Opus 5.5 5661bd233b docs: BUILD-LOG correction to the row 135 landing entry (review comment ids)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:16:02 -05:00
jason.woltjeandClaude Opus 5.5 c19f183b22 docs: BUILD-LOG for the row 135 landing (PRD 1.5, #1629); Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:15:38 -05:00
jason.woltje a041a5f0f4 queue: revisions 1530-1537 (Researcher's 1530 row 148 start; Filbert's 1531 row 135 round 3 approve; briefs for 137, 139, 143, 148 and 149 re-pinned to c07c87fc2; row 135 done at 7473dcd2a) 2026-10-16 13:15:21 -05:00
jason.woltjeandClaude Opus 5.5 7473dcd2a0 docs: PRD version 1.5 under Q167 and the task 314 acceptance run (row 135, #1629)
Version 1.5 records Jason's Q167 ruling and added text as its source
block. It replaces REQ-DEC-2 routing: a gated decision citing the
project's current spec commit goes to the domain arbiter, and only an
uncited decision or a hard wait goes to the human. It adds the hard
waits (REQ-WAIT-1 to 3, with data.delete, network.expose and
account.create), projects and the spec file (REQ-PROJ-1 to 4), agent
landing (REQ-LAND-1), approvals that outlive the run (REQ-DEC-5),
settled decisions under a new spec (REQ-DEC-6), the four workflow roles
and proactive contracts (REQ-ROLE-5, 6), staff requests (REQ-COS-2),
assistant summaries (REQ-ASSIST-3) and name addressing (REQ-MSG-2).

docs/acceptance/314.md is the run Jason does in the Stack once the
rows are live.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:09:43 -05:00
jason.woltjeandClaude Opus 5.5 c07c87fc22 plans: rows 137 and 139 briefs, whichever lands second carries the REQ-LAND-1 refusal (Filbert 28767 note 2)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:09:29 -05:00
jason.woltjeandClaude Opus 5.5 d02c5982bc sessions: Filbert row 135 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:07:38 -05:00
jason.woltjeandClaude Opus 5.5 1d62aa5418 sessions: Sage row 135 round 3, row 148 library ruling, brief re-pin
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:07:36 -05:00
jason.woltjeandClaude Opus 5.5 335723b068 queue: revisions 1512-1529 (row 137 briefed and claimed by Darkwing; row 135 rounds 1 to 3 with Filbert's round 2 verdict; briefs for 135, 137, 139, 143, 148 and 149 re-pinned to 1d0386dd0)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:07:24 -05:00
jason.woltjeandClaude Opus 5.5 1d0386dd08 plans: re-pin rows 135, 137, 139, 143, 148 and 149 for PRD 1.5 round 3 (Filbert 28765, Dewey 0503); row 148 drops Toast UI for a textarea and preview
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:03:52 -05:00
jason.woltjeandClaude Opus 5.5 c74511fe90 sessions: Filbert row 135 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:57:42 -05:00
jason.woltjeandClaude Opus 5.5 7a2987cf51 docs: BUILD-LOG for the 3190ad762 window (rows 152, 138, bus 3e to 3f); Sage SESSIONS line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:36:10 -05:00
jason.woltjeandClaude Opus 5.5 4eca829205 agents: Sage window plan for the re-pin to 3190ad762 (rows 152, 138, bus 3e to 3f)
The plan for Mos's A4 window: stopped-bus backup with sha256, rehearsal on
disposable copies, live migration with migrate-schema.mjs, NEW units and
wrapper, verify-new.sh and check-launch.mjs for the NEW copy (read_spec now
required), and restore steps A and B. Nine scripts committed with
SCRIPTS.sha256 so Mos can check the scratch copies before running. Not run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 21:09:07 -05:00
jason.woltje 7158d62ec1 queue: revisions 1510-1511 (row 138 round 2 approved by Dewey; done, landed as 3190ad762) 2026-10-15 20:51:28 -05:00
jason.woltjeandClaude Opus 5.5 3190ad762b bus, project: file-backed north-star specs with spec.read and record.write (row 138, #1632)
Row 138 round 2, Rocko's candidate e3f9a400 (43 paths) on 26a0b0fab.
Dewey approved (comment 28754) after round 1 changes (comment 28752):
D1-D5 mutants killed, the 17 touched test files run singly passed
124/124. Rocko's own run: 753 tests and 59/59 mutants killed.

The bus gains the project adapter through runtime.mjs, the way it
already injects the organization, so broker.mjs takes no new imports
and refuses with project-not-configured when no adapter is set. The
project socket timeout is 120s. Roles gain record.write. The schema
moves from 3e to 3f; the live migration follows MIGRATION.md and
needs Mos's window.

Sage's landing gate ran on c842686d5 plus the candidate: 25 steps,
all rc 0 (15 packages, 10 script suites), network sage-r138_default
removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:49:44 -05:00
jason.woltjeandClaude Opus 5.5 9cdf444c5a sessions: Dewey row 138 round 1 and round 2 reviews
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:43:14 -05:00
jason.woltjeandClaude Opus 5.5 de3e93ca62 agents: Dewey row 138 round 2 review packet (#1632)
Approve: D1-D4 and D5 (N-a) mutants killed on e3f9a400.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:42:46 -05:00
jason.woltje c842686d5c queue: revisions 1504-1509 (row 138 round 1 changes by Dewey; round 2 claimed and in review by Rocko, request posted by Sage) 2026-10-15 20:39:17 -05:00
jason.woltje 08d965dacc logs: Rocko row 138 round 2 verification (own lines via temp index) 2026-10-15 20:37:54 -05:00
jason.woltjeandClaude Opus 5.5 47a2925375 agents: Rocko row 138 round 2 packet r4 (#1632)
Round 2 candidate on 26a0b0fab, manifest digest e3f9a400...8f70, 43
paths. It adds tests for Dewey's D1-D4 (comment 28752), the N-a project
socket timeout fix, and the title and N-f text fixes (sage-0492).
Rocko's run: 753 package tests passed, 59/59 mutants killed. Packet
inventory 4a2a9c57...683f verifies 85/85; scanned for secrets and
binaries, none found.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:37:41 -05:00
jason.woltjeandClaude Opus 5.5 ff86a4473e agents: Dewey row 138 round 1 review packet (#1632)
Request changes: tests killing four surviving mutants (D1-D4).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:28:46 -05:00
jason.woltjeandClaude Opus 5.5 79ad018786 sessions: Sage row 152 landing and row 138 review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:16:42 -05:00
jason.woltje ed6e5a7fc7 queue: revisions 1498-1503 (row 152 approved by Filbert and done; row 138 round 1 in review by Dewey, request posted by Sage) 2026-10-15 20:16:16 -05:00
jason.woltjeandClaude Opus 5.5 0406321202 agents: Rocko row 138 r3 final verification and packet inventory (#1632)
BUILD.md gains the final suite and mutant results (747 passed, 54/54
killed); rebuild-manifest.txt gains its last entries. The packet
inventory is rebuilt to cover all 99 packet files (sha256 1a536101...b6ef,
sage-0490). Candidate digest unchanged: 75e07b65...47d1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:14:58 -05:00
jason.woltjeandClaude Opus 5.5 1e3575be66 agents: Rocko row 138 packets r1, r3-before-152 and r3 (#1632)
Round 3 candidate on 26a0b0fab, manifest digest 75e07b65...47d1, 42
paths, with the runtime.mjs project adapter Sage approved in sage-0488.
Rocko's run: 747 package tests passed, 54/54 mutants killed. Packet
scanned for secrets and binaries, none found.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:12:17 -05:00
jason.woltjeandClaude Opus 5.5 26a0b0fab4 bus, cli: a message to a role launch.by can't launch says so at once (row 152, #1646)
Row 152 round 1, Dewey's candidate on 56639c99b. Filbert approved
(comment 28741). The bus marks a waiting message launchable or not;
when the target isn't in the launch block, launch.by is still told,
with a notice saying the launch would be refused, and talk prints that
launch.by can't launch the role. Sage's landing gate ran on 5f57d85e5
plus the candidate: 25 steps, all rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:58:56 -05:00
jason.woltjeandClaude Opus 5.5 ccfcc31512 docs: BUILD-LOG for the 146-153 landings and the 02ee7f640 and 56639c99b re-pins; re-pin plans; Sage and Dewey SESSIONS lines
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:53:23 -05:00
jason.woltjeandClaude Opus 5.5 5f57d85e57 sessions: Filbert row 152 r1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:51:18 -05:00
jason.woltjeandClaude Opus 5.5 628398f717 queue: revisions 1489-1497 (rows 151 and 153 approved by Filbert; 140, 151 and 153 done; 152 briefed, claimed and in review by Dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:51:02 -05:00
jason.woltjeandClaude Opus 5.5 ecf1cec070 agents: Dewey row 152 round 1 packet (#1646)
Candidate on 56639c99b: waiting gains launchable, and a message to a role
outside launch.instances gets a notice saying the launch is refused and a
talk line saying launch.by can't launch it. Mutants 8/8 killed; gate green
(bus, cli, business, webui and every scripts/test-*.sh).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:47:11 -05:00
jason.woltjeandClaude Opus 5.5 56639c99b0 project: init cleans up after a failure and refuses a nested records repo (row 153, #1647)
Row 153 round 1, Rocko's candidate on 686fc1cb6. Filbert approved
(comment 28727). projectRepo lives in packages/project/src/paths.mjs, so
business.mjs is unchanged (sage-0469). Sage's landing gate ran on this
commit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:32:58 -05:00
jason.woltjeandClaude Opus 5.5 810186578a tests: launch notice routing and talk waiting/reply pinned (row 151, #1645)
Row 151 round 1, Dewey's candidate on 686fc1cb6. Filbert approved
(comment 28726). Test-only. Sage's landing gate ran on this commit
plus row 153.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:32:58 -05:00
jason.woltjeandClaude Opus 5.5 c9df996ac1 agents: Rocko row 153 round 1 packet (#1647)
Build packet for row 153, project init cleanup and the nested records repo
refusal. Filbert approved round 1 (comment 28727). Committed by Sage for
Rocko, who has no git authority; secrets scan clean, no binaries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:28:02 -05:00
jason.woltjeandClaude Opus 5.5 411b5eab5e harness, bus: pm asks chief for staff, and chief hires and launches (row 140, #1634)
Row 140 round 3, Researcher's candidate rebased onto d8487b3af after row 136.
Filbert approved round 3 (comment 28724). Sage's landing gate on 4e2148aba
plus the candidate passed 25/25, 00:22:43Z to 00:27:09Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:27:50 -05:00
jason.woltjeandClaude Opus 5.5 caf59613bb docs: register Filbert row 153 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:25:38 -05:00
jason.woltjeandClaude Opus 5.5 b92cbf78d9 docs: register Filbert row 151 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:23:09 -05:00
jason.woltjeandClaude Opus 5.5 4e2148abab queue: revisions 1405-1488 (rows 135-153: briefs, review rounds, landings of 146, 142, 136 and 147)
Pending revisions from sage (45), researcher (14), filbert (9), dewey (8)
and rocko (8), swept in one snapshot. Sage: rows 148-150 and the #1645-1647
rows 151-153 added and briefed, r3 re-pins for 135, 137-139, 141 and 143,
review requests for credential-less seats (142, 140 r1-r3, 147 r1-r2,
153 r1), done moves for 146, 142, 136 and 147. Researcher: 142 r2/r3 and
140 r1-r3 moves. Filbert: verdicts 142 r2/r3, 140 r1-r3, 146 r1, 147 r1/r2,
136 r2. Dewey: 146 r1, 136 r2, 151 start and r1 review. Rocko: 147 r1/r2
moves, 153 start and r1 review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:21:45 -05:00
jason.woltje afa961e8df docs: Rocko row147 verification and row153 build/review logs
Record Rocko's row147 mutants/rebase/review and row153 build, 342 passing
package tests, eight killed mutants and review request.

Also carries existing appended SESSIONS lines, named per sage-0412:
Dewey 2026-10-15 23:18:47Z (row146/136), 23:52:18Z (row136), and
2026-10-16 00:19:13 UTC (row151); Researcher 2026-10-15 23:36:11Z
(row142). Preserve these concurrent append-only entries unchanged.
2026-10-15 19:20:53 -05:00
jason.woltjeandClaude Opus 5.5 3d920ec703 docs: register Filbert row 140 round 3 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:20:08 -05:00
jason.woltjeandClaude Opus 5.5 20c0ee1c6f 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]>
2026-10-15 19:18:15 -05:00
jason.woltjeandClaude Sonnet 5.5 dfe7df1cac researcher: row 140 round 3 packet, candidate rebased onto d8487b3af after row 136 (#1634)
No behavior of mine changed. Round 2's patch applies to d8487b3af with no
conflict; only broker.mjs and bus/README.md blobs differ, from row 136's
edits. 66 of 66 bus, harness and cli test files pass singly; 25 of 25
mutants killed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 19:14:15 -05:00
jason.woltjeandClaude Opus 5.5 686fc1cb6d project: records layout, init, check and writer (row 147, #1641)
Round 2 candidate 7a585aa9606863a0d44a26094b585160914e3d8fd5f13436b56470cd69e6d867
(Rocko, packet fafee2f22), approved by Filbert in comment 28707.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:10:52 -05:00
jason.woltjeandClaude Opus 5.5 d8487b3af8 bus: approvals bind to the decision and the role instance, not the run (row 136, #1630)
Round 2 candidate db463a7b18962ee4afa393565abac9c4dd694dba49b18db20db064cac21fab89
(Dewey, packet 8b4cad1f2), approved by Filbert in comment 28704. Sage's landing
gate on 05ad2e7d0: 24 steps rc 0, 23:58:57Z to 00:03:24Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:03:49 -05:00
jason.woltjeandClaude Opus 5.5 2e05483c90 docs: register Filbert row 140 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:01:41 -05:00
jason.woltjeandClaude Opus 5.5 fafee2f228 agents: Rocko row 147 packets r1, r2 and r2-before-142 (#1641)
Round 2 candidate 7a585aa9606863a0d44a26094b585160914e3d8fd5f13436b56470cd69e6d867,
packet manifest 4292da90162c801a9c56716a0539f8fa072e67e93e4f4d9a0f1e107b84b652ea,
base 02ee7f640. Filbert approved round 2 in comment 28707. Committed by Sage
for Rocko, who has no git access; secrets grep clean, no binaries (one empty file).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:00:06 -05:00
jason.woltjeandClaude Opus 5.5 2b4b6f7513 docs: register Filbert row 147 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:59:25 -05:00
jason.woltjeandClaude Sonnet 5.5 1d8aabd3cc researcher: row 140 round 2 packet, staff-request tests judge sender and launcher by definition, rebased onto 02ee7f640 (#1634)
Test-only round for Filbert's changes request (comment 28639): three new
staff-request tests, mutants N1, N2, N2b and n2f added (25 of 25 killed),
chief-of-staff.md conflict with row 146 resolved keeping both bullets.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 18:57:47 -05:00
jason.woltjeandClaude Opus 5.5 05ad2e7d09 docs: register Filbert row 136 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:54:44 -05:00
jason.woltjeandClaude Opus 5.5 8b4cad1f2a agents: Dewey row 136 round 2 packet, rebased onto 277d1fa21 with the pre-121 thread test (#1630)
Candidate db463a7b…ab89 (5 files) on 277d1fa21. Adds the note-2 test for a thread
decision raised before row 121; mutants 6 of 6 killed after fixing the test to
supersede an open decision (first run left M6 alive, recorded). Owner's gate exit 0;
gate.sh now sets COMPOSE_PROJECT_NAME=dewey-r136 and removes its network at exit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:51:15 -05:00
jason.woltjeandClaude Opus 5.5 c66c7fb71d docs: brief for the row 146, 142 and 147 review follow-ups (#1645, #1646, #1647)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:45:13 -05:00
jason.woltjeandClaude Opus 5.5 02ee7f6403 cli: mosaic talk prints only the reply to its own message (row 142, #1636)
Researcher's row 142 round 3 (agents/researcher/work/queue-142/r3/
row142.patch, packet dcc2b2936), applied unchanged on dcc2b2936.
Candidate manifest c847d8fe...dd81, 4 paths, rebased onto 277d1fa21
after row 146. Sage's landing gate passed, 24 steps. Researcher's 6
mutants fail by assertion on the new base. Filbert approved round 3 in
comment 28652.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:41:48 -05:00
jason.woltjeandClaude Opus 5.5 7908ebc65e docs: register Filbert row 142 round 3 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:40:32 -05:00
jason.woltjeandClaude Sonnet 5.5 dcc2b29369 researcher: row 142 round 3 packet, candidate rebased onto 277d1fa21 after row 146 (#1636)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 18:36:11 -05:00
jason.woltjeandClaude Opus 5.5 e2a7cb38df docs: register Filbert row 147 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:32:20 -05:00
jason.woltjeandClaude Opus 5.5 277d1fa218 bus: a message to a stopped role tells launch.by and the sender (row 146, #1640)
Dewey's row 146 work (agents/dewey/work/queue-146/build.patch, packet
08e327bf5), applied unchanged on 625c21f41. Candidate manifest
616f953f...9607 on dda7e31b0; no commit since touches its 10 paths.
Dewey's gate passed and 17 of 17 mutants were killed. Filbert approved
round 1 in comment 28641.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:31:52 -05:00
jason.woltjeandClaude Opus 5.5 c619cc02da docs: register Filbert row 146 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:23:12 -05:00
jason.woltjeandClaude Opus 5.5 625c21f41f docs: register Filbert row 140 round 1 review (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:17:49 -05:00
jason.woltjeandClaude Opus 5.5 08e327bf58 agents: Dewey row 146 build packet, a message to a stopped role tells launch.by and the sender (#1640)
Candidate manifest 616f953fe09bfc198645886bc4c7228a0fdeafa8c89b1729f2be8a5222689607 on dda7e31b0.
Owner's gate 23:11:36Z-23:15:46Z exit 0; mutants 17 of 17 killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:17:32 -05:00
jason.woltjeandClaude Opus 5.5 41b096af67 docs: register Filbert row 142 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:09:16 -05:00
jason.woltjeandClaude Opus 5.5 9c23777390 docs: receipt for issue #1644, row 150
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:09:11 -05:00
jason.woltjeandClaude Opus 5.5 5250d0b456 docs: brief for the talk follow-ups after row 142, Filbert's notes 1 and 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:08:24 -05:00
jason.woltjeandClaude Sonnet 5.5 deacc1ab45 docs: Researcher SESSIONS lines for the row 140 build and the row 142 round 2 packet
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 18:05:52 -05:00
jason.woltjeandClaude Sonnet 5.5 2118b3fdbb researcher: row 142 round 2 packet, candidate rebased onto dda7e31b0 (#1636)
Row 145 added 3 lines to cli.mjs after the approved round 1 base; same change, new base, regenerated
candidate manifest, TOOLS.md line rewrapped. Every cli test file passes singly; 6/6 mutants killed on the new base.
Packet only; the candidate is row142.patch and is not applied to the tree. Nothing pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 18:05:45 -05:00
jason.woltjeandClaude Sonnet 5.5 d10a1a4677 researcher: row 140 packet, the pm asks the chief of staff for staff and the chief launches within limits (#1634)
Candidate on dda7e31b0: staff.request verb (pm only, STAFF-REQUEST class no other writer can produce),
request_staff pm tool and prompt line, pm and chief-of-staff contract text, 7 bus tests, tools and bundle
tests, one launcher end-to-end test. 21/21 mutants killed; node suites and all ten scripts/test-*.sh green.
Packet only; the candidate itself is row140.patch and is not applied to the tree. Nothing pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 18:05:45 -05:00
jason.woltjeandClaude Opus 5.5 3c4b78c52b docs: receipt for issues #1642 and #1643, rows 148 and 149 (vikunja:19/314)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:05:29 -05:00
jason.woltjeandClaude Opus 5.5 d5a9bf4c3b docs: Q167 rows after comment 225, the editor (148), the workflow roles (149) and rescoped 135, 137, 138, 139, 141 and 143 (vikunja:19/314)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:00:38 -05:00
jason.woltje 81e59d25c7 docs: register Rocko row 147 build, gate pending 2026-10-15 18:00:33 -05:00
jason.woltje 276965d6d5 docs: register Rocko paused Q167 rows 2026-10-15 17:57:48 -05:00
jason.woltjeandClaude Opus 5.5 dda7e31b0c docs: BUILD-LOG for the 3e window and the vikunja-19 landings; Sage and Dewey SESSIONS lines
BUILD-LOG: the live bus window 3b to 3e and rows 144 and 145.
SESSIONS: Sage's window and landing lines, and Dewey's row 136 round 1
line, left for this sweep. Rocko's 22:26 and Researcher's 22:44 lines stay
uncommitted for their seats.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:53:52 -05:00
jason.woltjeandClaude Opus 5.5 e941e3905e docs: queue revisions 1353-1404 (Q167 rows 135-143 and 146-147 added and briefed; rows 144 and 145 landed; 136 and 142 round 1 reviews)
Swept revisions from other seats: Rocko 1373 and 1394, Dewey 1375, 1380,
1381 and 1393, Researcher 1376, 1398, 1399 and 1402, Filbert 1382, 1385,
1395 and 1403. The rest are Sage's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:53:13 -05:00
jason.woltjeandClaude Opus 5.5 f57c80b4cf agents: Sage row 144 and 145 packets (vikunja-19 landings, #1638 #1639) and Q167 issue receipts
Row 145 BUILD.md records both review rounds, the gate on 9357baac9
(GATE_PASSED, 24 steps) and the landed commits 92938ab43..fba879f6a.
q167/ holds the issue receipts for rows 135-143 and 146-147.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:52:12 -05:00
jason.woltjeandClaude Opus 5.5 fba879f6a1 mosaic plugins set: a patch changes only the kinds it names; null removes a kind (vikunja:19/309, row 145)
Jason-approved merge, rebased: git diff f4d7b91a9..0e7d1d8b8 from
work/vikunja-19-309, stacked on 305. Business reviewer verdict dfd44af2
approve on 0e7d1d8b8; Jason resolved merge decision 12a20e47 via the CLI.

No conflict. packages/plugins/README.md, which refactor also changed,
merged with the source commit's +/- lines; the other four paths are the
source blobs. Filbert reviews the rebased diff.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:50:59 -05:00
jason.woltjeandClaude Opus 5.5 467a313c7d plugins: mosaic plugins and the plugins verbs switch a module through the human-only path (vikunja:19/305, row 145)
Jason-approved merge, rebased: git diff 813671596..f4d7b91a9 from
work/vikunja-19-305, stacked on 295. Business reviewer verdict 57d7f0b7
approve on f4d7b91a9; Jason resolved merge decision 0eebf83f via the CLI.

Conflict resolution only. packages/bus/src/broker.mjs conflicted with
row 123 (89fd2d6fe): both sides extend the limits branch of request().
Kept row 123's organization.set role path and ORGANIZATION_KEYS, and
added 305's plugins and plugins.set to the verb list and its keys lines.
plugins and plugins.set take the human-only path as in 305: role is true
only for organization.set, the actor is the human, and the verb reaches
the organization writer unchanged. organization.set is unchanged.
packages/plugins/tests/searxng.test.mjs conflicted with 307: both add a
test at the end of the file. Kept 307's AbortSignal test, then 305's
hasModule test. Three other paths that refactor also changed merged
without conflict, with the source commit's +/- lines; the other six
paths are the source blobs. Filbert reviews the rebased diff.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:50:59 -05:00
jason.woltjeandClaude Opus 5.5 ae74c25fdd web verbs: grant before the long wait, abort signal to the backend, one query limit (vikunja:19/307, row 145)
Jason-approved merge, rebased: git diff 813671596..6219c5c8c from
work/vikunja-19-307, stacked on 295. Business reviewer verdict 84c9c4be
approve on 6219c5c8c; Jason resolved merge decision 17bb9efc via the CLI.

Conflict resolution only. packages/harness/src/tools.mjs conflicted with
row 132 (0e947c216): both sides add a constant after TASK_PAGE_MAX. Kept
row 132's ORGANIZATION_KEYS, then 307's WEB_QUERY_MAX. Three other paths
that refactor also changed merged without conflict. For each, the +/-
lines on the new base equal the source commit's. The other nine paths are
the source blobs. Filbert reviews the rebased diff.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:50:59 -05:00
jason.woltjeandClaude Opus 5.5 e9f4431ec1 harness: web_search and web_fetch as broker-served tools, search-source plug-ins and the assistant's web grant (vikunja:19/295, row 145)
Jason-approved merge, rebased: git diff 07c12b51b..813671596 from
work/vikunja-19-295, its seven commits as one (9ede6935b through
813671596, which include task 296's searxng check). Business reviewer
verdict 51c71b46 approve on 813671596; Jason resolved merge decision
29ff6589 via the CLI.

Conflict resolution only. packages/harness/src/tools.mjs conflicted with
row 132 (0e947c216): both sides add a block after review_verdict. Kept
row 132's end_run and change_organization block, then 295's web_search and
web_fetch block, each with its own closing brace. Seven other paths that
refactor also changed merged without conflict. For each, the +/- lines on
the new base equal the source commit's. The other 20 paths are the
source blobs. Filbert reviews the rebased diff.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:50:59 -05:00
jason.woltjeandClaude Opus 5.5 92938ab43a roles: C-suite messaging limited to human, chief-of-staff, pm and cto (vikunja:19/306, row 145)
Jason-approved merge, landed unchanged: git diff bbf61d6dd..3c80fea74
from work/vikunja-19-306. Business reviewer verdict 8ee5be8b approve on that
commit; Jason resolved merge decision 0eebf83f via the CLI. Applied cleanly
on the parent landing; no conflict. Filbert reviews the rebased diff.

Integration with row 121, found in Filbert's round 1 (#1639, comment 28623):
packages/bus/tests/csuite-messaging.test.mjs setup() binds the assistant to a
thread, since row 121 made assistant a thread definition and bindLaunch refuses
an assistant run without one. The human creates a project and thread first.
No assertion or code changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:50:59 -05:00
jason.woltjeandClaude Opus 5.5 5a5509839a docs: register Filbert row 142 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:49:18 -05:00
jason.woltjeandClaude Sonnet 5.5 df5bbfb82a researcher: row 142 round 1 packet, mosaic talk prints only the reply to its own message (#1636)
Candidate on 8e2c5c2d0: 4 files, tree 91503e62b8e0ce7b1895ffb7f3b1bbe534b52bcb.
6 of 6 mutants killed, 3 controls pass, cli suite and every scripts/test-*.sh green.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 17:44:23 -05:00
jason.woltjeandClaude Opus 5.5 10d4b526a3 docs: row 147 brief owns the project dispatch in scripts/mosaic and packages/project in stack-copy.sh
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:42:44 -05:00
jason.woltjeandClaude Opus 5.5 2a4849a2d4 docs: register Filbert row 145 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:42:40 -05:00
jason.woltjeandClaude Opus 5.5 8686c7e84c docs: Q167 revised-rows brief, launch notice and project layout (vikunja:19/314, rows 146-147)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:38:10 -05:00
jason.woltjeandClaude Opus 5.5 e7726e2d1a docs: register Filbert row 145 round 1 review (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:38:04 -05:00
jason.woltjeandClaude Opus 5.5 8eae4eae6c docs: register Filbert row 136 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:32:54 -05:00
jason.woltjeandClaude Opus 5.5 8e2c5c2d03 agents: Dewey row 136 build packet, approvals bind to the role instance and thread (#1630)
Candidate manifest 38b0d3cda5b7f69a0a4c60fc0434c66ba8cc8e7b834fcb865383dd2ee6d711e8 on e37d6e583.
Owner's gate 22:25:05Z-22:29:11Z exit 0; mutants 4 of 4 killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:29:54 -05:00
jason.woltjeandClaude Opus 5.5 193ae70c63 roles: the five C-suite role files and contracts, launched by the chief of staff (vikunja:19/301, row 144)
Jason-approved merge, landed unchanged: git diff 07c12b51b..bbf61d6dd
from work/vikunja-19-301. Business reviewer verdict abcd2c87 approve on that
commit; Jason resolved merge decision 29ff6589 via the CLI. No commit on
refactor since 07c12b51b touches these paths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:21:19 -05:00
jason.woltjeandClaude Opus 5.5 91b963e256 bus start records why it failed closed; Console banner and bus status show it (vikunja:19/302, row 144)
Jason-approved merge, landed unchanged: git diff 07c12b51b..abda248a8
from work/vikunja-19-302. Business reviewer verdict 4998de97 approve on that
commit; Jason resolved merge decision 29ff6589 via the CLI. No commit on
refactor since 07c12b51b touches these paths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:20:09 -05:00
jason.woltjeandClaude Opus 5.5 19fabe47c0 prd: version 1.4, REQ-WEB-3 and REQ-INST-1 (vikunja:19/297, row 144)
Jason-approved merge, landed unchanged: git diff 07c12b51b..b3dd8de83
from work/vikunja-19-297. Business reviewer verdict 0d4c1c68 approve on that
commit; Jason resolved merge decision 29ff6589 via the CLI. No commit on
refactor since 07c12b51b touches these paths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:19:49 -05:00
jason.woltjeandClaude Opus 5.5 e37d6e5836 docs: briefs for the Q167 north-star rows 135-143 and the eight vikunja-19 landings
Rows 135-143 are Vikunja P19 task 314 (Q167). The landing brief covers
Jason's merge decisions 29ff6589, 0eebf83f, 17bb9efc and 12a20e47, landed
through Sage rows as Mos relayed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:15:02 -05:00
jason.woltjeandClaude Opus 5.5 fc5b342f83 agents: Sage window plan, row 121 update stamp from the commit clock
The update was stamped 19:10Z by estimate. Commit f365a1da9 that
carries it is 19:07:47Z, so the stamp becomes 19:07Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 14:08:41 -05:00
jason.woltjeandClaude Opus 5.5 f365a1da9d docs: register Darkwing and Dewey row 121 sessions and the Sage landing; window plan at 3e; P19 task 311
SESSIONS: Darkwing 18:54:54Z and its 18:56:04Z correction, Dewey
19:00:29Z, Sage's landing line. The schema window plan records row 121
landed 2c16d6b93, a rehearsal of the migration on a copy of the backup
(row 121 gate condition 4), and a role_claims check for an open assistant
claim. P19 task 311 holds row 121's follow-ups (sage-0369).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 14:07:47 -05:00
jason.woltjeandClaude Opus 5.5 496e26e0e5 docs: queue revisions 1349-1352 (row 121 round 2 review and landing)
Darkwing 1349-1350: row 121 in-review with the round 2 candidate
710d5086, request comment 28614. Dewey 1351: approve, comment 28615.
Sage 1352: row 121 done, landed 2c16d6b93.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 14:07:15 -05:00
jason.woltjeandClaude Opus 5.5 2c16d6b932 bus: projects and threads, schema 3d to 3e (row 121, #1615)
Darkwing's row 121 round 2 (agents/darkwing/work/queue-121/r2, base
20d2b5788, manifest 710d5086...e67e9b, 30 files), applied unchanged on
d7ebaa9a5; no commit since the base touches any of the 30 paths. Dewey
asked for changes on round 1 in comment 28591 (F1 to F6) and approved
round 2 in 28615. Landing gate on d7ebaa9a5 plus the candidate: all 24
suites passed, 0 failed.

F6 and Dewey's three round 2 notes are follow-ups (sage-0369, P19 task
311). One of them corrects the packet: BUILD.md says the decision-route
setup's projects key is needed, and Dewey's probe showed project.create
reads it only with repo, so the key is dead setup.

The live bus stays at 3b until Mos's schema window migrates it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 14:06:05 -05:00
jason.woltjeandClaude Opus 5.5 d7ebaa9a51 agents: Darkwing row 121 round 2 BUILD.md, the summary line names Dewey too
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:55:52 -05:00
jason.woltjeandClaude Opus 5.5 dd7c222953 agents: Darkwing row 121 round 2 BUILD.md names Dewey as the reviewer (sage-0367)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:55:48 -05:00
jason.woltjeandClaude Opus 5.5 9d74161969 agents: Darkwing row 121 round 2 BUILD.md names the business chief-of-staff test change
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:54:21 -05:00
jason.woltjeandClaude Opus 5.5 e3885f0334 agents: Sage P19 task 310, business tests tmp() leaks its directories
Darkwing's row 121 round 2 gate left 201 mosaic-business-* directories.
Filed as a follow-up, not in row 121.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:54:07 -05:00
jason.woltjeandClaude Opus 5.5 58e85d0fa1 agents: Darkwing row 121 round 2 packet (#1615), rebased on 20d2b5788
Candidate manifest sha256 710d50866e77055b7231a53e3601bd2bab7686b722160f81ddda030875f67e9b,
30 files. Targeted gate green, 77 of 77 mutants killed, repro DIGESTS_OK.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:54:02 -05:00
jason.woltjeandClaude Opus 5.5 a921aeb010 agents: Sage schema window plan, rows 123, 132 and 133 landed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:28:14 -05:00
jason.woltjeandClaude Opus 5.5 20d2b57881 docs: register Dewey row 133, Researcher row 132 and Sage landing sessions; row 134 P19 filing
SESSIONS lines: Dewey 18:00:14Z (row 133 round 2), Researcher 18:05:08Z
(row 132 round 1 build), Sage 18:26:44Z (rows 133, 70 and 132 landed).
vikunja-setup: the script and read-back that filed row 134 as P19 task 308.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:27:02 -05:00
jason.woltjeandClaude Opus 5.5 ab3a57a3cf docs: queue revisions 1318-1348 (rows 70, 132, 133 and 134; 121 round 2 start)
Sage 1318-1319 (row 132 brief pin, briefed), 1321-1322 (row 70 review),
1327 (row 134 add), 1329-1331 (row 70 round 2), 1333 (row 134 P19 note),
1336-1337 (row 132 review request), 1339 (row 133 done, a622f426c), 1340
(row 70 done, 73fa83a08), 1342 (row 132 round 2 start), 1345-1346
(row 132 round 2 request, comment 28605) and 1348 (row 132 done,
0e947c216).
Researcher 1320 (row 132 claim); 1334-1335 and 1343-1344 are claimant
moves for row 132 made by Sage as researcher, with no token, so the
request outcome is failed and Sage posted it.
Dewey 1323-1325 (row 133 round 2, request 28597).
Filbert 1326 (row 70 round 1 changes, 28598), 1328 (row 133 round 2
approve, 28600), 1332 (row 70 round 2 approve, 28602), 1338 (row 132
round 1 approve, 28604) and 1347 (row 132 round 2 approve, 28606).
Darkwing 1341 (row 121 round 2 start).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:26:35 -05:00
jason.woltjeandClaude Opus 5.5 0e947c216d harness: end_run and change_organization tools for the chief (row 132, #1628)
Researcher's row 132 work (agents/researcher/work/queue-132/row132.patch),
rebased unchanged on 73fa83a08 as round 2 (agents/sage/work/queue-132/r2,
manifest 0a0ebbf6...048e). Filbert approved round 1 in comment 28604 and
the round 2 rebase in 28606. Landing gate on 73fa83a08 plus the
candidate: all 24 suites passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:25:34 -05:00
jason.woltjeandClaude Opus 5.5 334744ddb8 docs: register Filbert row 132 round 2 confirm review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:25:01 -05:00
jason.woltjeandClaude Opus 5.5 17a66cdb09 agents: Sage row 132 round 2 packet (#1628, rebase on row 133, confirm only)
Researcher's round 1 patch applied unchanged on 73fa83a08. Only
packages/bus/src/broker.mjs differs from the approved manifest, because
row 133 (a622f426c) landed on it. Full landing gate passed, 24 suites.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:22:19 -05:00
jason.woltjeandClaude Opus 5.5 73fa83a08b docs: mosaic on PATH can be the pinned copy wrapper (row 70, #1564)
Sage's row 70 round 2 candidate (agents/sage/work/queue-70/r2, manifest
098b9aef...604e), approved by Filbert in comment 28602. Landing gate on
4a34fb0d6 with rows 133 and 70 applied: all 24 suites passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:13:37 -05:00
jason.woltjeandClaude Opus 5.5 a622f426c0 bus: a decision raise checks the route may message the raiser; arbiters can't declare messaging lists (row 133, #1627)
Dewey's row 133 round 2 candidate (agents/dewey/work/queue-133/r2,
manifest 1c0e6f8f...0ef0), approved by Filbert in comment 28600.
Landing gate on 4a34fb0d6 with rows 133 and 70 applied: all 24 suites
passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:13:09 -05:00
jason.woltjeandClaude Opus 5.5 4842791638 docs: register Filbert row 132 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:11:28 -05:00
jason.woltjeandClaude Sonnet 5.5 8a73f713cf agents: Researcher row 132 packet (#1628, end_run and change_organization harness tools)
Round 1 packet in agents/researcher/work/queue-132/: row132.patch (8 files, +162 -19) on b6a588cd2,
candidate manifest, build.md, 28 mutants killed, full gate green. No candidate change applied here.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 13:05:03 -05:00
jason.woltjeandClaude Opus 5.5 feca383207 docs: register Filbert row 70 round 2 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:04:08 -05:00
jason.woltjeandClaude Opus 5.5 4a34fb0d67 agents: Sage row 70 round 2 packet (#1564, Filbert 28598 findings)
F1: the scripts/mosaic header names mosaic-old. Both doc-only follow-ups
are done too, plus the test comment wording. Same base, same 4 files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:02:24 -05:00
jason.woltjeandClaude Opus 5.5 7a32a1228f docs: register Filbert row 133 round 2 delta review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:01:48 -05:00
jason.woltjeandClaude Opus 5.5 d9bf6b6bc5 docs: register Filbert row 70 round 1 review (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 13:00:11 -05:00
jason.woltjeandClaude Opus 5.5 26f4f97017 docs: brief for the organization revision read (row 132 follow-up)
Researcher's row 132 build found that a role holding organization.change
must send the organization revision, and only the human's limits read
returns it. organization.set fails closed on a stale revision, so this is
a follow-up row, not a blocker for the 3d window.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:59:59 -05:00
jason.woltjeandClaude Opus 5.5 346347c950 agents: Dewey row 133 round 2 packet (#1627, rebase onto rows 49 and 123)
Base 89fd2d6fe. r1's build.patch on the new base gives the r2 tree byte for
byte, so delta.patch is empty; the interdiff shows only index lines and hunk
offsets. Mutants 5/5 killed with controls passing, targeted gate rc 0.
Candidate manifest sha256 1c0e6f8f260ae51b31c2235193c36ed1c005faefe317e1452a351a3bb5690ef0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:59:16 -05:00
jason.woltjeandClaude Opus 5.5 593b437fdf docs: register Sage rows 49 and 123 landings and row 70 review request, with Dewey's two lines
Dewey: row 133 r1 build (17:34:00Z) and row 121 r1 review (17:43:32Z).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:57:18 -05:00
jason.woltjeandClaude Opus 5.5 5dc0c7de74 agents: Sage row 70 round 1 packet (#1564, docs for mosaic on PATH)
Candidate 1126314bd522de838aad03689bed41f6b12a33c2d7f30b2eff1b38653f559ae0,
4 files on 48b893871. The first gate stopped at script-queue: the pre-A2
fixture check pinned the old header comment. The check now skips comment
lines (brief amended in 06e964026). Second gate: 24 suites, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:56:04 -05:00
jason.woltjeandClaude Opus 5.5 b6a588cd20 agents: Sage schema window plan update and P19 task 304 filing
The window plan records row 49 landed and row 121's round 1 changes, and
adds Darkwing's 28587 note 2: the first start reads every comment list.
Task 304 is Darkwing's note 1, push a task's comment cursor only when
consider runs for it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:52:06 -05:00
jason.woltjeandClaude Opus 5.5 48b8938719 queue: rows 49 and 123 done (3a4afdc03, 89fd2d6fe), sweep revs 1308-1317
Dewey 1308-1309 (row 133 r1 in review) and 1315 (row 121 r1 verdict,
changes, 28591). Darkwing 1310 (row 49 verdict, approve, 28587) and
1311-1312 (row 121 r1 in review). Filbert 1313 (row 133 r1 verdict,
approve, 28589). Sage 1314 (row 70 brief re-pin), 1316 (row 49 done)
and 1317 (row 123 done).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:50:45 -05:00
jason.woltjeandClaude Opus 5.5 89fd2d6fee bus: the chief of staff ends runs and changes the organization file, schema 3c to 3d (row 123, #1617)
The chief of staff role can revoke a run with a reason (role.revoke) and
write the organization file (organization.set), each under a broker
grant. organization.set checks the grant before the writer runs and
consumes it only after the writer returns, so a refused change leaves no
action.allowed. migrations/3c-3d.sql takes the store from 3c to 3d;
migrate-schema.mjs chains it after 3b to 3c. roles/chief-of-staff.json
and the business vocabulary carry the new actions. The harness tools for
them are row 132, which must land before 3d goes live.

Candidate 255a960582f1018b8d400e8d193e8e95713a078930277652d59ef4153197d9f6,
Researcher packet agents/researcher/work/queue-123/r2 (b959160f9), built
on eaeb5ebf2 plus row 49 r1. Filbert approved round 2 in 28578. Landing
gate at 3a4afdc03: 24 suites green, 0 failed. 14 package suites passed
1611 tests (bus 191, business 92, harness 84, tasks 97, webui 112). The
10 script suites passed (land-work 84, queue 156).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:49:33 -05:00
jason.woltjeandClaude Opus 5.5 3a4afdc03a bus: comment cursors survive a restart, schema 3b to 3c (row 49, #1535)
The broker stores the highest comment id seen per business and task in an
append-only table, task_comment_cursors. The tasks adapter restores those
ids before its startup reconcile, and the first reconcile reads each
task's comment list even with no cached count, so a comment posted while
the bus was down is found. Cursor rows, snapshots and comment events
commit in one transaction. migrate-schema.mjs takes a stopped store from
3b to 3c offline; the store refuses a 3b database until it runs.
scripts/land-work/read.mjs reads SCHEMA_VERSION from store.mjs.

The live bus is 3b. From this commit on, land-work refuses it with
bus-schema until Mos migrates in the schema window
(agents/sage/work/window-plan/schema-chain-3c-3e.md).

Candidate 87ef1321a1a08be64a20499889fc24b5549b47d6d33abfd86db95f62fb3cb6d0,
Rocko packet agents/rocko/work/queue-49/r1 (ea5e70fa8), base 7b166a0f1.
Filbert approved in 28579 and Darkwing in 28587. Landing gate at
ebbbf62da: 24 suites green, 0 failed. 14 package suites passed 1598
tests (bus 178, tasks 97, business 92, cli 195, webui 112). The 10
script suites passed (land-work 84, queue 156). Follow-ups: P19 tasks 300
(duplicate cursor) and 304 (push a task's cursor only when consider runs
for it).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:43:56 -05:00
jason.woltjeandClaude Opus 5.5 06e9640261 docs: row 70 brief allows narrowing the pre-A2 fixture check
The first row 70 gate stopped at script-queue: dispatch.test.mjs requires
every pre-A2 fixture line, comments included, in scripts/mosaic, and the
fixture holds the header this row corrects.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:39:28 -05:00
jason.woltjeandClaude Opus 5.5 4866bd28af docs: register Darkwing row 49 r1 verdict and row 121 r1 in review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:37:36 -05:00
jason.woltjeandClaude Opus 5.5 e637371656 docs: register Filbert row 133 r1 review (approve, 28589)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:36:06 -05:00
jason.woltjeandClaude Opus 5.5 ef8c3fe713 agents: Darkwing row 121 round 1 packet (candidate manifest, gate, 62 mutants)
Round 1 is a code and test review and won't land. land-work fails at
BASE on the read.mjs 3b pin, which row 49 removes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:35:28 -05:00
jason.woltjeandClaude Opus 5.5 ebbbf62dac docs: Dewey row 133 round 1 packet (#1627, decision closes and messaging lists)
Validator refuses a messaging-limited arbiter; decision.raise refuses a route
that can't message the raiser (decision-route-refused). Candidate on e6e5b41bf,
manifest sha256 edb43aed. Targeted gate green, 5/5 mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:33:27 -05:00
jason.woltjeandClaude Opus 5.5 55b66d123a docs: register Sage lead line and Researcher's row 123 r2 line
Researcher's row 123 round 2 line was pending in the working tree; it is
committed as written.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:31:28 -05:00
jason.woltjeandClaude Opus 5.5 31d4cf077a queue: revs 1297-1307
Researcher 1297, 1298 (row 123 r2 to in-review); Sage 1299, 1300 (row 123
review request as jarvis); Filbert 1301 (row 123 r2 approve, 28578); Sage
1302, 1303 (row 133 added and briefed, #1627); Filbert 1304 (row 49 r1
approve, 28579); Dewey 1305 (row 133 started); Sage 1306, 1307 (row 70
brief re-pinned at f04bcaddb, row 70 started).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:30:55 -05:00
jason.woltjeandClaude Opus 5.5 4c1b11d934 docs: row 70 brief update, the PATH wrapper runs a pinned copy since row 85
Rows 85 (#1579) and 130 (#1625) changed what the three doc places must
say: the wrapper execs a pinned copy's scripts/mosaic, the human commands
run only from the bus's tree, and queue writes stay on the checkout.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:18:08 -05:00
jason.woltjeandClaude Opus 5.5 9175fe5259 agents: Sage schema window plan for 3b to 3e, P19 follow-ups 299 and 300
The window plan records Filbert's row 49 note 2 (28579): land-work refuses
the live 3b bus from the row 49 landing until Mos migrates. Task 300 is
note 1, the duplicate comment cursor in one pass. Task 299 is row 123's
consume follow-up (28578). Also the row 121 and 131 filing script and
its record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:16:56 -05:00
jason.woltjeandClaude Opus 5.5 e6e5b41bf0 docs: register Filbert row 49 r1 review (approve, 28579)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:13:31 -05:00
jason.woltjeandClaude Opus 5.5 07c12b51bb docs: brief for decision closes and messaging lists (#1627), row 120 follow-up
Ruling: the business validator refuses a messaging-limited arbiter, and
decision.raise checks that the route may message the raiser. The close
insert stays unchecked, so no decision is left that its resolver can't
close. Owner dewey, reviewer filbert.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:12:47 -05:00
jason.woltjeandClaude Opus 5.5 5f457b2ff9 docs: register Filbert row 123 r2 review (approve, 28578)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:08:51 -05:00
jason.woltjeandClaude Sonnet 5.5 b959160f9a docs: Researcher row 123 round 2 packet (#1617, F1 fix, candidate on eaeb5ebf2 + row 49 r1)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 12:05:43 -05:00
jason.woltjeandClaude Opus 5.5 be2ea5f7cc docs: register Sage row 131 landing (eaeb5ebf2)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:58:45 -05:00
jason.woltjeandClaude Opus 5.5 8879588df3 queue: row 131 done (eaeb5ebf2), sweep revs 1291-1296
Revs: Filbert 1291 (row 131 r2 approve) and 1294 (row 123 r1 changes),
Sage 1292 (row 132 added, #1628) and 1296 (row 131 done), Darkwing 1293
(row 131 r2 approve), Researcher 1295 (row 123 r2 in progress).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:58:40 -05:00
jason.woltjeandClaude Opus 5.5 eaeb5ebf2b plugins: the plug-in layer, kind registry and loader (row 131, #1626)
packages/plugins holds the kind registry and the loader for in-repo
plug-in modules (REQ-PLUG-1). The first two kinds are usage-source and
limit-check (REQ-LIMIT-4). The business file's plugins key turns modules
on per kind, through plugin-settings.mjs and step 5's write path
(REQ-ROLE-3). A refused load is logged and keeps the kind's old modules.
Any filesystem error other than ENOENT or ENOTDIR refuses as unreadable.
The organization writer validates the bytes it writes as well as the
document. stack-copy ships packages/plugins. Nothing is wired into a
host process yet.

Candidate 466c835f7dce79d20c7ab74c197a52b70e7fdeb0efae0456222dc6d0a633f219,
Dewey packet 80797a539 (round 2), base 0bdd573ff. Filbert approved
round 2 in 28568 and Darkwing in 28570. Landing gate at f7d249634: 24 suites green, 0 failed.
14 package suites passed 1586 tests (plugins 21, business 92,
bus 171, cli 195, webui 112). The 10 script suites passed. Follow-up:
P19 task 293, coded writer refusals.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:57:54 -05:00
jason.woltjeandClaude Opus 5.5 f7d249634b docs: register Filbert row 123 r1 review (changes, 28572)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:51:40 -05:00
jason.woltjeandClaude Opus 5.5 0b5ea8695a docs: register Darkwing row 131 r2 review (approve, 28570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:48:40 -05:00
jason.woltjeandClaude Opus 5.5 9886dfb1f8 docs: brief for the harness tools row (#1628), row 123 follow-up
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:46:47 -05:00
jason.woltjeandClaude Opus 5.5 dcb704b297 docs: register Filbert row 131 r2 review (approve, 28568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:46:12 -05:00
jason.woltjeandClaude Opus 5.5 93c5c689c2 docs: register Dewey row 131 r2 and Sage row 123 review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:45:39 -05:00
jason.woltjeandClaude Opus 5.5 2ad109a29c queue: revs 1285-1290 (row 123 in-review by researcher, review request posted by sage as 28564; row 131 r2 in-review by dewey, request 28566)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:45:28 -05:00
jason.woltjeandClaude Opus 5.5 80797a5398 docs: add Dewey row 131 r2 packet (plug-in loader and setter fixes)
Round 2 for #1626 per sage-0314: F1a setter refuses a non-plain prototype,
F1b organization writer validates and logs JSON.parse(bytes), F2 loader
refuses unreadable paths, README top-level-code limit, control run.
Gate 591/0, 0 skipped; controls pass; mutants 47/47 killed (slot sage-0316).
Candidate manifest sha256 466c835f7dce79d20c7ab74c197a52b70e7fdeb0efae0456222dc6d0a633f219,
14 paths on base 0bdd573ff; no commit in base..1376caab9 touches them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:44:04 -05:00
jason.woltjeandClaude Opus 5.5 1376caab95 docs: register Dewey row 131 r1, Researcher row 123 build, Sage slot and rulings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:41:17 -05:00
jason.woltje 703f2307de queue: revs 1280-1284 (row 131 r1 in-review by dewey, verdicts filbert changes and darkwing approve, row 131 r2 started) 2026-10-15 11:40:54 -05:00
jason.woltjeandClaude Sonnet 5.5 2209ac4c17 docs: add Researcher row 123 packet (chief of staff ends runs and changes the organization file)
Candidate on 0bdd573ff + row 49 r1 (manifest 87ef1321...), 21 mutants killed,
targeted gate green. Nothing landed or pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-15 11:39:36 -05:00
jason.woltjeandClaude Opus 5.5 8bcd4418f7 docs: register Darkwing row 131 r1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:29:20 -05:00
jason.woltjeandClaude Opus 5.5 6b729880cb docs: register Filbert row 131 r1 review (changes, 28560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:29:03 -05:00
jason.woltjeandClaude Opus 5.5 09447eb677 docs: add Dewey row 131 r1 packet (plug-in kind registry and loader)
Candidate packet for #1626: build.patch on 0bdd573ff, candidate manifest,
targeted gate (588/0 over plugins, business, bus, webui, cli) and 42/42
mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:23:33 -05:00
jason.woltje 6daf6ac74c queue: revs 1278-1279 (row 49 review request posted as jarvis, 28557) 2026-10-15 11:23:09 -05:00
jason.woltjeandClaude Opus 5.5 ea5e70fa8f docs: register Sage row 121 approval and row 49 review; add Rocko row 49 r1 packet
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:21:33 -05:00
jason.woltje 8a4c8fca67 docs: register Rocko row 49 candidate and gate 2026-10-15 11:20:14 -05:00
jason.woltje cf12c25e96 queue: revs 1269-1275 (rows 121, 123, 131 briefed; claims by rocko 49, dewey 131, researcher 123, darkwing 121) 2026-10-15 11:20:07 -05:00
jason.woltjeandClaude Opus 5.5 0bdd573fff docs: register Sage launch of rows 121, 123, 131, 49
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:58:40 -05:00
jason.woltje ffb7d3f5f6 queue: rev 1268 (row 49 assigned to rocko; rows 121, 123, 131 launched) 2026-10-15 10:58:33 -05:00
jason.woltjeandClaude Opus 5.5 7b166a0f19 docs: register Sage decision 146
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:55:15 -05:00
jason.woltjeandClaude Opus 5.5 be9377a919 docs: lead decision 146, ready for decision 78 is row 42's acceptance run
Jason via Mos (sage-0204, DECISIONS Q161): "D4 yes". refactor is ready
to merge to next when row 42's acceptance run passes with Jason on the
live mosaic-stack business. The merge itself stays Jason's (R2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:55:06 -05:00
jason.woltjeandClaude Opus 5.5 35a2b42709 docs: register Sage window receipt and plan fix
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:49:42 -05:00
jason.woltjeandClaude Opus 5.5 9056fe5ad6 agents: window plan check 2 reads the bundle manifest file
The launch line's manifest field is a path, so l.manifest.typed threw a
TypeError in the 2026-10-15 window. Mos caught it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:49:36 -05:00
jason.woltjeandClaude Opus 5.5 2f57e3c599 docs: register Sage row 120 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 21:09:20 -05:00
jason.woltje 76007b9080 queue: revs 1266-1267 (row 120 Filbert approve 28548, done, landed a771ce24b) 2026-10-14 21:09:15 -05:00
jason.woltjeandClaude Opus 5.5 a771ce24bb bus: the broker limits who the assistant sends to and hears from (row 120, #1614)
Roles may declare messaging.sendTo and messaging.receiveFrom. The broker
checks them in message.send, review.request and review.verdict before
anything is recorded; a refusal rolls back and logs action.refused.
A role without the field is unchanged. roles/assistant.json declares both.

Candidate ae7515b97bb92fdde63e9d2e6fd8473700d3e0c12d9e70fff67e06129fbac1a9,
Researcher packet f758a8e92, Filbert round 2 approve (comment 28548).
Landing gate at df5b48cae: 23 suites green. The decision-close DECISION
insert stays a follow-up, #1627.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 21:08:25 -05:00
jason.woltjeandClaude Opus 5.5 df5b48caea docs: register Filbert row 120 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 21:03:14 -05:00
jason.woltjeandClaude Opus 5.5 95460cf080 docs: register Researcher and Sage row 120 round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 21:00:41 -05:00
jason.woltje 29a6e17c51 queue: revs 1261-1265 (row 120 round 2 in-review, request 28547) 2026-10-14 21:00:32 -05:00
jason.woltjeandClaude Sonnet 5.5 f758a8e927 researcher: row 120 round 2 packet (review paths gated, rebased on 66ea40580)
Candidate diff and manifest against 66ea40580; messaging suite now 12 tests;
gate green; 21/21 mutants caught (m16, m17 added). No push.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-14 20:58:25 -05:00
jason.woltjeandClaude Opus 5.5 66ea40580e docs: register Sage row 122 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:52:13 -05:00
jason.woltje cb1f5497d8 queue: rev 1260 (row 122 done, landed 7b5b682ae) 2026-10-14 20:51:59 -05:00
jason.woltjeandClaude Opus 5.5 7b5b682ae8 business: agent limits as settings with one organization write path (row 122, #1616)
launch.max becomes launch.models plus launch.limits (total, providers,
exact provider/model). The launcher re-reads limits on every attempt.
Settings and `mosaic limits` write through one locked, journaled
organization writer. `mosaic limits migrate` is print-only.

Candidate 00b84e311475f97ced0d9a9599fba23a28442971ed15d8d489d9422d84a8812e,
Rocko packet 4b6e50636, Filbert round 2 approve (comment 28542).
Landing gate at 0c56cc2d4: 23 suites green. Decision 123 go from Mos.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:50:52 -05:00
jason.woltjeandClaude Opus 5.5 c0c1e8646e agents: window plan, Jason runs the PM-idle check, no --checkout flag
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:46:46 -05:00
jason.woltje 0c56cc2d49 queue: revs 1249-1259 (row 120 round 1 changes, row 122 round 2 approve) 2026-10-14 20:43:53 -05:00
jason.woltjeandClaude Opus 5.5 9baf9e1d49 agents: Sage window plan for rows 119 and 122, with the first chief session
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:43:21 -05:00
jason.woltjeandClaude Opus 5.5 d9dbc5b849 docs: register Filbert row 122 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:42:26 -05:00
jason.woltje 4b6e506361 agents: Rocko row 122 round 2 packet after row 119 and review fixes 2026-10-14 20:36:00 -05:00
jason.woltje 2fd2ad84f1 docs: Rocko row 122 round 2 verification logs 2026-10-14 20:35:30 -05:00
jason.woltjeandClaude Opus 5.5 1a2d5072a4 docs: register Filbert row 120 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:28:34 -05:00
jason.woltjeandClaude Sonnet 5.5 07a967aec2 agents: Researcher row 120 candidate and verification packet
Broker send limits for the assistant (REQ-ASSIST-2, #1614): optional role
messaging lists, broker refusals logged both ways. Gate and 19 mutants green
on 85540a74e; patch applies to 9f1c14e57.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-14 20:24:21 -05:00
jason.woltje 9f1c14e57f queue: revs 1246-1247 (row 120 briefed, row 122 round 1 changes) 2026-10-14 20:18:13 -05:00
jason.woltjeandClaude Opus 5.5 0afbd818e7 docs: register Filbert row 122 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:14:10 -05:00
jason.woltje 85540a74e8 agents: Rocko row 122 round 1 candidate and verification packet 2026-10-14 20:12:48 -05:00
jason.woltjeandClaude Opus 5.5 33acefdce8 docs: SESSIONS, Sage row 119 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:11:33 -05:00
jason.woltjeandClaude Opus 5.5 c6a46ba78a queue: revs 1240-1245 (rows 119, 122)
Filbert 1240 (row 119 round 2 approve); Rocko 1241-1242 (row 122
in-review, request failed for want of a token); Sage 1243-1244 (row 122
request as comment 28533) and 1245 (row 119 done).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:11:18 -05:00
jason.woltjeandClaude Opus 5.5 94e41ae29e roles: add the assistant role, move role.launch from the PM to the chief of staff (row 119, #1613)
roles/assistant.json and assistant.md are new. The assistant holds only
message.send and relays each ruling verbatim to the chief of staff. The
chief of staff gains role.launch; the PM drops it. Test fixtures move
launch.by from "pm" to a `chief` instance.

This does not change the live host. Jason's organization file names
launch.by "pm", which the new role files refuse, so his edit and the
re-pin happen together in one Mos window (decision 144; the order is in
agents/researcher/work/queue-119/build.md).

Lands Researcher's round 2 candidate, approved by Filbert in comment
28528 (rev 1240). Candidate manifest f77c4917, all 22 files match.
Landing gate on 46117feb8: 23 suites, GATE_PASSED 01:09:35Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:10:11 -05:00
jason.woltje 32221d90d0 docs: Rocko row 122 design and candidate verification logs 2026-10-14 20:06:18 -05:00
jason.woltjeandClaude Opus 5.5 46117feb87 docs: register Filbert row 119 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:48:40 -05:00
jason.woltjeandClaude Opus 5.5 270648507b docs: SESSIONS, Sage row 130 landing and row 119 rounds; Rocko and Researcher lines
Sweeps Rocko's 00:23:57 line and Researcher's 00:40:35Z and 00:45:46Z
lines with Sage's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:48:13 -05:00
jason.woltjeandClaude Opus 5.5 92bb554309 queue: revs 1223-1239 (rows 119, 122, 130)
Darkwing 1223, 1226-1227 (row 130 start and review request); Researcher
1224 (row 119 in-progress); Rocko 1225 (row 122 design start); Filbert
1228 (row 130 approve) and 1233 (row 119 round 1 changes). Sage, for
Researcher: row 119 in-review 1229-1230, review request 1231-1232
(comment 28521), round 2 start 1235, in-review 1236-1237 and request
1238-1239 (comment 28526). Sage: row 130 done 1234.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:47:38 -05:00
jason.woltjeandClaude Sonnet 5.5 f529cf485c agents: researcher row 119 round 2 packet (#1613): assistant relays rulings to the chief of staff only, pinned
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-14 19:45:46 -05:00
jason.woltjeandClaude Opus 5.5 bc31acf830 docs: register Filbert row 119 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:44:40 -05:00
jason.woltjeandClaude Opus 5.5 30d008090e cli: hint at the pinned mosaic when the bus refuses another tree's proof (row 130, #1625)
When the broker answers human-required or tester-required, the CLI
now prints a second line saying the bus accepts these commands only
from its own tree and to run `mosaic` on PATH on a pinned host. The
entry block becomes an exported run(); exit codes are unchanged.

Lands Darkwing's row 130 round 1 candidate, approved by Filbert in
comment 28520 (rev 1228). Candidate manifest 9a5de54d, all four files
match. Landing gate on 9e837ce5f: 23 suites, GATE_PASSED 00:44:07Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:44:35 -05:00
jason.woltjeandClaude Sonnet 5.5 58643299e0 agents: researcher row 119 packet (#1613): chief-of-staff and assistant role files, PM drops role.launch, org-file entry and one-window landing order
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-14 19:40:28 -05:00
jason.woltjeandClaude Opus 5.5 9e837ce5f0 docs: register Filbert row 130 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:31:31 -05:00
jason.woltjeandClaude Opus 5.5 f6fd8204d4 docs: SESSIONS, darkwing row 130 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:29:35 -05:00
jason.woltjeandClaude Opus 5.5 21e8f64ea7 agents: darkwing row 130 round 1 packet (#1625)
Build patch against c6570f142, candidate manifest (4 files), repro,
mutant (killed, 2 of 191), targeted gate (cli 191/0, webui 23/0) and
build.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:29:04 -05:00
jason.woltjeandClaude Opus 5.5 f8932fa176 docs: decision 145 (row 122 migration is print-only; Jason installs the output in Mos's window)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:26:02 -05:00
jason.woltjeandClaude Opus 5.5 c6570f1421 docs: SESSIONS, dispatch of rows 119, 122 and 130
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:21:29 -05:00
jason.woltje cd3a83ec81 docs: queue revs 1218-1222, row 130 re-pinned to 8089b98c4 and moved to Darkwing (1218-1219), rows 119, 122 and 130 briefed (1220-1222, Mos sage-0198) 2026-10-14 19:20:45 -05:00
jason.woltjeandClaude Opus 5.5 8089b98c47 docs: row 130 brief owner moves from Rocko to Darkwing so it runs alongside row 122 (Mos sage-0198)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 19:19:21 -05:00
jason.woltjeandClaude Opus 5.5 d6eb419774 docs: SESSIONS, decision 144 and the row 65 harness scope
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:52:04 -05:00
jason.woltje 65b2ad36bb docs: queue revs 1214-1217, rows 64-66 briefs re-pinned to 419eae3a2 (row 65 harness plug-in scope, Q156), row 65 after 49 and 131 2026-10-14 18:51:56 -05:00
jason.woltjeandClaude Opus 5.5 419eae3a25 docs: decision 144 (decision 104's hold closed, Jason edits the business file); row 65 brief gains the harness plug-in scope (Q156) and waits on row 131
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:50:46 -05:00
jason.woltjeandClaude Opus 5.5 ecf4ce6249 docs: SESSIONS, PRD 1.3 landing and the #287 row
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:42:41 -05:00
jason.woltje d3077e1669 docs: queue revs 1192-1213, row 130 added for #1625 (1192), row 127 unblocked and parked by Jason (1193-1194, Jason's), row 131 added for the plug-in layer #1626 (1195), row 124 after 131 (1196), briefs of rows 64-66, 119-129 re-pinned to PRD 1.3 240d25518 (1197-1210), rows 65, 64, 66 reordered per Q156 (1211-1213) 2026-10-14 17:42:31 -05:00
jason.woltjeandClaude Opus 5.5 240d255187 docs: PRD 1.3, usage plug-ins and the app-wide plug-in layer (Q153-Q155)
PRD version 1.3 records Jason's rulings Q153 to Q155, relayed by Mos
(lead decision 143), and Mos reviewed the text before it landed. Adds
REQ-LIMIT-4 (usage sources and limit checks as the first plug-in kinds;
an unknown turn is never zero, no source switched on means every turn is
unknown, an unenforced limit shows as not enforced) and a Plug-ins
section with REQ-PLUG-1 (kind registry and loader in packages/plugins,
in-repo modules only until Jason rules, Vikunja #286) and REQ-PLUG-2
(new call-outs go through a kind; hard-coded parts convert as rows touch
them). REQ-LIMIT-2 and REQ-LIMIT-3 count through usage sources.
Technical considerations list where each kind is hard-coded today.

requirements.json moves to 1.3 with 41 ids; the tasks and bus id-count
tests follow. The PRD 1.2 build plan gains step 6b, the plug-in layer
(#1626), and rows 124 to 127 ship their counting and checks as modules.

Jason approved the conversion order for hard-coded parts in Q156. Lead
decision 143 and the build plan record it, and the managed-session
config brief notes that row 65 now runs before 64 and 66.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:39:37 -05:00
jason.woltjeandClaude Opus 5.5 9edac91073 docs: brief for human and tester commands from the checkout (Vikunja #287)
Mos asked for a row after the #284 go-live: the checkout's scripts/mosaic
is refused tester-required and human-required because the bus proofs
accept only the pinned copy's entry paths. README text, a TOOLS.md
sentence and one stderr hint line; no redirect to the live copy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:24:56 -05:00
jason.woltjeandClaude Opus 5.5 7651e6b1dd docs: row 114 landed (a1ddeabdf, #1608); BUILD-LOG, SESSIONS
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:45:08 -05:00
jason.woltje 1a323c28f2 docs: queue revs 1185-1191, row 114 started and moved in-review by Rocko (1185-1187, Rocko's), review requested (1188-1189), approved by Filbert (1190, Filbert's) and done (landed a1ddeabdf, #1608) 2026-10-14 15:44:42 -05:00
jason.woltjeandClaude Opus 5.5 a1ddeabdfc discord: unit can run from a pinned copy; check --offline; status reads the effective tree (row 114, #1608)
Candidate manifest 68c60a79914692f3f6d4923f7bcff41de109ccca33bf6fba687fb88efa24bcac
from Rocko's round 1 packet (a94a943c9), on BASE 72c9ac36c. No
manifest path changed between BASE and the landing target. Filbert
approved round 1 in 28497 with no findings; the TOOLS.md paragraph
break is a doc-only follow-up (decision 137). Sage reran the full gate
at the landing target with the candidate applied and Docker live.
Nothing is installed or restarted; the live switch is Mos's step after
the SetSpark freeze lifts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:43:46 -05:00
jason.woltjeandClaude Opus 5.5 a94a943c92 work: Rocko row 114 round 1 packet (#1608), candidate manifest 68c60a799146
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:39:45 -05:00
jason.woltjeandClaude Opus 5.5 19c6ab6793 docs: register Filbert row 114 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:38:32 -05:00
jason.woltje 618d71ebbb logs: Rocko row 114 Discord pinned copy build and gate (#1608) 2026-10-14 15:34:33 -05:00
jason.woltjeandClaude Opus 5.5 72c9ac36c3 docs: row 108 landed (0d030011a, #1602); BUILD-LOG, SESSIONS
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:24:03 -05:00
jason.woltje 96b50ba319 docs: queue revs 1183-1184, row 108 approved by Filbert (1183, Filbert's) and done (landed 0d030011a, #1602) 2026-10-14 15:23:25 -05:00
jason.woltjeandClaude Opus 5.5 0d030011af scripts: bus unit drop-in guard covers inherited globs; script usage and copy wrapper comments (row 108, #1602)
Candidate manifest 19ef7133db592787c24fa473357687a5741ccd7823627b970624c930a7611dbc
from Rocko's round 1 packet (a6e4fe747), on BASE 234121b30. No
manifest path changed between BASE and the landing target. The
bus-service.sh drop-in guard now refuses inherited mosaic-.service.d
and service.d drop-ins, the header lists every flag, the unreachable
install-show fakes are gone, and the stack-copy.sh wrapper carries its
copy and lease comments after the line-2 marker. Filbert approved in
28489 with no findings. Sage reran the full gate at the landing target
with the candidate applied.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:22:40 -05:00
jason.woltjeandClaude Opus 5.5 abe5903e58 docs: register Filbert row 108 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:17:26 -05:00
jason.woltjeandClaude Opus 5.5 d064d584bb docs: SESSIONS for the row 108 review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:14:24 -05:00
jason.woltje a50591b379 docs: queue revs 1178-1182, row 108 started and in review on #1602 (1178-1180 Rocko's; request 28485 posted as jarvis) 2026-10-14 15:13:56 -05:00
jason.woltjeandClaude Opus 5.5 a6e4fe747a work: Rocko row 108 round 1 packet (#1602), candidate manifest 19ef7133db59
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:12:31 -05:00
jason.woltje d743048362 logs: Rocko row 108 build, gate and review packet (#1602) 2026-10-14 15:11:45 -05:00
jason.woltjeandClaude Opus 5.5 234121b302 docs: SESSIONS for the row 108 brief re-pin
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 15:00:40 -05:00
jason.woltje af1e512655 docs: queue rev 1177, row 108 brief re-pinned to 0968e7bd1 (the file gained the row 111 brief; this section only lost a trailing blank line) 2026-10-14 15:00:23 -05:00
jason.woltjeandClaude Opus 5.5 8b315ad1e6 docs: re-pin to c2525dd33 (row 118, PRD 1.2 requirements); BUILD-LOG, SESSIONS
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:58:18 -05:00
jason.woltjeandClaude Opus 5.5 07798db16a docs: row 118 landed (c2525dd33, #1612); BUILD-LOG, SESSIONS with Dewey's review line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:53:31 -05:00
jason.woltje c28760a5c0 docs: queue revs 1175-1176, row 118 approved by Dewey (1175, Dewey's) and done (landed c2525dd33, #1612) 2026-10-14 14:52:52 -05:00
jason.woltjeandClaude Opus 5.5 c2525dd335 seat: the host's git runs no background maintenance in a role clone (row 118, #1612)
Candidate manifest eeb67779c919b7c1f25b4449df7207d85634e5175b16eef315346cf5a284c63c
from Darkwing's round 1 packet (5a68df727), on BASE 2727dd3ec. No
manifest path changed between BASE and the landing target. Dewey
approved in 28479, with no blocking or doc-only findings. Sage reran
the full gate at the landing target with the candidate applied.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:52:07 -05:00
jason.woltjeandClaude Opus 5.5 a076f28c00 docs: SESSIONS for the row 118 brief and slot
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:42:34 -05:00
jason.woltje 04cac7d8c4 docs: queue revs 1171-1174, row 118 briefed, started and in review on #1612 (1172-1174 Darkwing's) 2026-10-14 14:42:34 -05:00
jason.woltjeandClaude Opus 5.5 ee72ce13d7 docs: register Darkwing row 118 round 1 build
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:41:19 -05:00
jason.woltjeandClaude Opus 5.5 5a68df7275 agents: darkwing row 118 round 1 packet (#1612)
SAFE in packages/seat/src/checkout.mjs adds maintenance.auto=false and
gc.auto=0; probe, tests, mutants m1-m3 (all killed), targeted gate
(seat 132/0, cli 184/0) and the leftover sweep. Candidate is build.patch
against 2727dd3ec; manifest digests check out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:41:09 -05:00
jason.woltjeandClaude Opus 5.5 2727dd3ec4 docs: row 116 landed (1f2ac5b84, #1610); BUILD-LOG and SESSIONS
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:26:22 -05:00
jason.woltje 06083fe3e8 docs: queue rev 1170, row 116 done (landed 1f2ac5b84, #1610) 2026-10-14 14:25:56 -05:00
jason.woltjeandClaude Opus 5.5 1f2ac5b848 scripts: land-work turns a resolved merge decision into a gated local commit (row 116, #1610)
Candidate manifest 8b4ab31014a124ec0e3abba44238976d6f4c9af48cbdb36c2c77d18f0137f888
from Darkwing's round 2 packet (0a4076e8e), on BASE 1223a0f37. No
manifest path changed between BASE and the landing target. Filbert
approved round 2 in 28468, closing C1 and D1 from round 1 (28464).
Sage reran the full gate at the landing target with the candidate
applied. The replay acceptance follows ruling sage-0204.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:24:57 -05:00
jason.woltjeandClaude Opus 5.5 922cb8170b queue: row 116 round 2 approved by Filbert on #1610 (rev 1169, Filbert's)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:18:29 -05:00
jason.woltjeandClaude Opus 5.5 1ff2254be5 docs: register Filbert row 116 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:16:59 -05:00
jason.woltjeandClaude Opus 5.5 088ac3f60b docs: SESSIONS for the row 116 round 2 review slot
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:14:34 -05:00
jason.woltjeandClaude Opus 5.5 f58fdec9a4 docs: SESSIONS for the row 116 round 2 review handoff
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:13:35 -05:00
jason.woltjeandClaude Opus 5.5 67d508588d queue: row 116 round 2 in-review, Darkwing's request on #1610 (revs 1166-1168, Darkwing's)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:13:18 -05:00
jason.woltjeandClaude Opus 5.5 b4b60c1701 docs: SESSIONS for the row 116 round 2 review handoff
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:12:25 -05:00
jason.woltjeandClaude Opus 5.5 0a4076e8e6 row 116 round 2 builder packet: C1 cases and mutants, D1
Six cases for Filbert's C1 (f1 split into actor_role and actor_run), the
earlier-merge dry run, a node_modules canary with s27, and the TOOLS.md
usage line. 84 cases, 55 of 56 mutants killed (s26 equivalent), targeted
gate green. Candidate is build.patch against 1223a0f37; not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 14:11:59 -05:00
jason.woltjeandClaude Opus 5.5 a2ee3a80fd docs: SESSIONS for the row 116 round 2 slot grant
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:57:25 -05:00
jason.woltjeandClaude Opus 5.5 ac6b64c722 docs: SESSIONS for the row 116 round 1 verdict
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:56:40 -05:00
jason.woltjeandClaude Opus 5.5 b532f1fcb1 queue: row 116 round 1 verdict changes by Filbert on #1610 (rev 1165, Filbert's)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:56:21 -05:00
jason.woltjeandClaude Opus 5.5 a67ec1bcce docs: register Filbert row 116 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:55:28 -05:00
jason.woltjeandClaude Opus 5.5 f1f6f913a6 docs: SESSIONS for the row 116 review handoff
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:49:40 -05:00
jason.woltjeandClaude Opus 5.5 04796a7e5a queue: row 116 in-review, Darkwing's round 1 request on #1610 (revs 1162-1164, Darkwing's)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:49:14 -05:00
jason.woltjeandClaude Opus 5.5 a264397fe5 docs: BUILD-LOG correction for the row 116 replay acceptance; row 116 briefed; #1624
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:47:24 -05:00
jason.woltjeandClaude Opus 5.5 2350d1745a queue: row 116 to briefed for Darkwing's round 1 (rev 1161)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:47:14 -05:00
jason.woltjeandClaude Opus 5.5 f3c925ae4a docs: SESSIONS for row 116 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:44:48 -05:00
jason.woltjeandClaude Opus 5.5 82943bf61d docs: row 116 round 1 packet, land-work build, mutants, gate and 19/265 replay (#1610)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:44:35 -05:00
jason.woltjeandClaude Opus 5.5 02494c3ed6 docs: BUILD-LOG and SESSIONS for PRD 1.2 step 1 and rows 119-129
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:43:54 -05:00
jason.woltjeandClaude Opus 5.5 dc459a5b74 queue: rows 119-129 for PRD 1.2 build steps 2-11 (#1613-#1623), row 127 blocked on #237; row 116 brief round 2 (revs 1148-1160)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:43:29 -05:00
jason.woltjeandClaude Opus 5.5 f7ef24f0eb docs: lead decision 142 and the PRD 1.2 build-row briefs (#1613-#1623)
Decision 142 records the PRD 1.2 approval, Mos's rulings on choices 6 to
9, the token-unit draft position, the sage-0188 proxy facts and 9b left
unfiled. The brief file holds one section per build row, steps 2 to 11
with 9a and 9c split out, each tied to its Gitea issue.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:41:04 -05:00
jason.woltjeandClaude Opus 5.5 7eb5ad0cb8 docs: PRD 1.2, chief of staff, assistant, threads and agent limits (Q147-Q151)
PRD version 1.2, approved by Jason through Mos on 2026-10-14 (lead
decisions 141 and 142). Adds REQ-COS-1, REQ-ASSIST-1/2, REQ-LIMIT-1/2/3,
REQ-USER-2 and REQ-THREAD-2, rewrites the role, launch, message, CLI and
WebUI requirements, and quotes Q147 to Q151 in the source line.
REQ-LIMIT-2 marks the token unit for the daily quota as a draft position.

requirements.json moves to 1.2 with 38 ids; the tasks and bus id-count
tests follow. Full gate green at bea5dfa47 plus these four files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:39:58 -05:00
jason.woltjeandClaude Opus 5.5 bea5dfa47f docs: SESSIONS for the PRD 1.2 draft rounds
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:20:29 -05:00
jason.woltjeandClaude Opus 5.5 d2ef582811 docs: lead decision 141, Q147/Q148 chief of staff and assistant roles
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:09:39 -05:00
jason.woltjeandClaude Opus 5.5 2e2adcf3ef docs: BUILD-LOG correction for the row 116 brief round 2 digest check; Q147 facts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:58:36 -05:00
jason.woltjeandClaude Opus 5.5 1223a0f37d docs: BUILD-LOG and SESSIONS for the row 117 re-pin and row 116 brief round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:50:30 -05:00
jason.woltjeandClaude Opus 5.5 68ce7f3548 docs: row 116 brief round 2, land-work reads the bus database read-only (#1610)
Darkwing asked how land-work reads the decision. The Console's path is
human-only (human-cli refuses agent environments, #225), so the
conductor can't use it. land-work now opens bus.sqlite readOnly, runs
SELECTs only and refuses bus-schema on a digest mismatch. The brief
records what f71c73c9 holds: task_ref is null, the raise's target and
approvalChoice give the branch and the merge option, and the work ref
comes from the question. agents/sage/work/row116/live-rows.json holds
the live rows for the replay fixtures.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:49:41 -05:00
jason.woltjeandClaude Opus 5.5 621a2599ba docs: BUILD-LOG and SESSIONS for the row 117 landing and row 118
Carries Dewey's uncommitted SESSIONS line for the row 117 review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:44:05 -05:00
jason.woltjeandClaude Opus 5.5 bf93806e75 docs: queue revs 1139-1147, row 117 filed and done (landed e62712e7a, #1611); row 118 filed (#1612)
Revs 1142-1144 are Darkwing's (row 117 start and in-review), 1145 is
Dewey's approval; the rest are Sage's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:43:42 -05:00
jason.woltjeandClaude Opus 5.5 e62712e7aa seat: fetch a role clone that is on another branch or dirty (#1611)
Row 117. At a launch, update() kept a clone that was on another branch
or dirty and ran no git in it, so its refs/remotes/origin/* stayed as
old as the last clean launch. Now those two reasons get the clean
path's fetch, with the same arguments and environment, and no merge.
The result is a new outcome, fetched, with the reason, the clone's
branch and HEAD, and origin { branch, commit } after the fetch. Every
earlier check still keeps the clone with no fetch. The launcher writes
a checkout-fetched launch-log line, and the bundle prompt names a
fetched clone's branch, commit and origin. Nothing in unsafe(),
allowed(), gitEnv() or SAFE changed.

Candidate manifest 9fa839edfb2bd04771bbac1913b142cb16aef227a1265120e0d318fb64d6a502
from Darkwing's round 1 packet (17d801f48), on BASE 559ef2287. Dewey
approved in 28443. Sage reran the full gate at the landing target with
the candidate applied.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:42:29 -05:00
jason.woltjeandClaude Opus 5.5 606b9e5087 docs: row 118 brief, the host's git runs no background maintenance in a role clone (#1612)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:39:05 -05:00
jason.woltjeandClaude Opus 5.5 ae2ad7128e docs: lead decisions 138 (merge-train SOP), 139 (Q144) and 140 (chief-of-staff model)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:33:17 -05:00
jason.woltjeandClaude Opus 5.5 bc97f80eb5 docs: SESSIONS, darkwing row 117 round 1 in review (#1611)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:32:43 -05:00
jason.woltjeandClaude Opus 5.5 17d801f482 work: row 117 round 1 packet, fetch a clone on another branch or dirty (#1611)
Candidate manifest, patch against 559ef2287, build notes, five killed
mutants and the targeted gate (seat, cli, harness, two scripts).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:31:50 -05:00
jason.woltjeandClaude Opus 5.5 8d586bdb5b docs: BUILD-LOG and SESSIONS for the fed66feff re-pin, row 117 and decisions 136/137
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:25:15 -05:00
jason.woltjeandClaude Opus 5.5 0b106518ad docs: lead decisions 136 (CI changes) and 137 (doc-only findings don't block)
Jason's 17:16Z rulings, relayed by Mos: the #271 CI steps (Vikunja
#273-#278) and Q143. Decision 137 is the reviewer rule for Vikunja #279;
the queue README's verdict paragraph points to it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:24:14 -05:00
jason.woltjeandClaude Opus 5.5 559ef22875 docs: row 117 brief, fetch a role clone that is on another branch or dirty (#1611)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:20:17 -05:00
jason.woltjeandClaude Opus 5.5 fed66feff6 docs: row 115 landed as d90482030 (#1609)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:11:00 -05:00
jason.woltje d479f043eb docs: queue revs 1131-1138, row 115 done (landed d90482030, #1609); row 116 filed (#1610) 2026-10-14 12:10:43 -05:00
jason.woltjeandClaude Opus 5.5 d90482030a bus: a closed decision sends the raising role a message (#1609)
Row 115. When a decision closes by resolve or withdraw, and the closer is
a human or another run, the broker routes a DECISION message to the
raising role's holder: "Decision <id> on <action> was resolved by <by>:
<choice> (<text>)" or "withdrawn by <by>", plus the note if there is one.
A seen op sends nothing. #request falls back to the human.input that
closed the decision, filtered to resolved and withdrawn.

Candidate manifest 1910eb5a672e670cb7c8a197ee3fdd888412dd803fe52a0a15680103c26c078c
from Darkwing's round 2 packet (40e5917f9), on BASE 94ece491f. Filbert
approved in 28428 and Sage in 28427. Sage reran the full gate at the
landing target with the candidate applied.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:09:43 -05:00
jason.woltjeandClaude Opus 5.5 2330074bbe docs: register Filbert row 115 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:02:38 -05:00
jason.woltjeandClaude Opus 5.5 a4519fbc46 docs: register Darkwing row 115 round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:01:25 -05:00
jason.woltjeandClaude Opus 5.5 40e5917f9b docs: row 115 round 2 packet, the seen-before-close request test (#1609)
Candidate on base 94ece491f: 5 files, manifest 1910eb5a; only
decision-close.test.mjs changed since round 1. Mutants m1-m4 killed
(m4 is Filbert's f4); gate passed with no flakes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:00:51 -05:00
jason.woltjeandClaude Opus 5.5 66e101b7bd docs: PRD v1.1 merge and re-pin to f7c8537b3, row 116 filed (#1610)
BUILD-LOG records the squash-merge of decision f71c73c9, the re-pin with
the install-flag correction, and row 116. SESSIONS registers Sage.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:56:26 -05:00
jason.woltjeandClaude Opus 5.5 6f2404e100 docs: merge path brief, a resolved merge decision lands as a gated local commit (#1610)
Filed at Mos's request after the PRD v1.1 merge. The slice is conductor-only:
push and any stack-role landing authority stay out of scope.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:55:35 -05:00
jason.woltjeandClaude Opus 5.5 f820fef57a docs: register Filbert row 115 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:51:34 -05:00
jason.woltje b8e6e60bfd queue: row 115 start and in-review, row 7 note (#1609, revs 1127-1130) 2026-10-14 11:50:58 -05:00
jason.woltjeandClaude Opus 5.5 f7c8537b32 prd: version 1.1, the Organization requirements (decision 03130d08, task 19/265)
Squash of the PM's work/vikunja-19-265 branch, net diff 0c3826910..8ae5a1491
(patch sha256 47b34e6054a74eedd8a5b6577e40537a07b2fb8fe5846f309e100146238ef743).
The PRD moves to version 1.1. "Business" becomes "organization" in
REQ-ROLE-3, REQ-ROLE-4 and REQ-LAUNCH-1. REQ-DEC-3 and REQ-DEC-4 name an
authorized human instead of Jason. Non-goal D adds that v1 has no hosted
SaaS, and "Where it runs" says Mosaic Stack stays self-hosted open source.
A new Organizations section adds REQ-ORG-1, REQ-ORG-2, REQ-USER-1,
REQ-THREAD-1, REQ-TPL-1 and REQ-FED-1. packages/tasks/requirements.json
matches at version 1.1 with 30 ids, and the tasks and bus requirements
tests expect 30.

The branch's 010d30cbe (parseRequirementId, task 19/229) is reverted on
the branch and is not part of this squash.

Jason approved the PRD in decision 03130d08 and the merge in decision
f71c73c9 (resolved yes via the CLI, 2026-10-14T16:35:32Z). The stack
reviewer's verdict d3f97f9d approves 8ae5a1491. Sage's gate on 1c9a4910d
plus the patch passed: every package suite and every scripts/test-*.sh.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:49:36 -05:00
jason.woltjeandClaude Opus 5.5 4bc908991c docs: register Darkwing row 115 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:45:29 -05:00
jason.woltjeandClaude Opus 5.5 b3fca1f1a6 docs: row 115 round 1 packet, decision close message (#1609)
Candidate on base 94ece491f: 5 files, manifest f37bc933. Three mutants
killed; gate passed with no flakes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:44:46 -05:00
jason.woltjeandClaude Opus 5.5 1c9a4910d9 docs: build-log row 113 landing and the full 2026-10-04 ledger week; register Sage session
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:40:01 -05:00
jason.woltjeandClaude Opus 5.5 94ece491f0 queue: row 113 approved and done (#1607, revs 1125-1126)
Filbert's rev 1125 (round 1 approve, comment 28407) rides with Sage's 1126 (done). Landed as a5b8a99a1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:36:23 -05:00
jason.woltjeandClaude Opus 5.5 a5b8a99a18 ledger: skip T3 reasoning summaries and read issue pages up to a ceiling (row 113, #1607)
T3 messages with role reasoning and an id starting "reasoning:" are now
skipped before any other check and counted as reasoningMessages. Any other
unknown role, including reasoning with another id prefix, still refuses.
Issues updated since the start date are read in pages of 50 until a short
page, at most 10 pages. A full tenth page, a duplicate issue number across
pages, a page over 50 rows or any failed page refuses with exit 2, so
partial totals are never reported. The report prints how many pages it
read. The README describes the skip, the page loop and its refusals.

Candidate manifest 4eed1d6405a505e88cde5062c61f768639d899b8f301019b1403d68fb15d9d36
(Darkwing round 1, packet d6ea56831), BASE cc48b124f. Approved by Filbert
(comment 28407). Sage gate rerun at 644ef5310 plus the candidate passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:35:33 -05:00
jason.woltjeandClaude Opus 5.5 644ef5310e docs: register Filbert row 113 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:29:35 -05:00
jason.woltjeandClaude Opus 5.5 54b7c7b3e3 queue: row 113 start and in-review, rows 114 and 115 added and briefed (#1607, #1608, #1609, revs 1118-1124)
Darkwing's revs 1118 (row 113 start), 1123 (in-review) and 1124 (review request) ride with Sage's 1119-1122.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:27:40 -05:00
jason.woltjeandClaude Opus 5.5 6f04595abb docs: register Darkwing row 113 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:26:45 -05:00
jason.woltjeandClaude Opus 5.5 d6ea568312 docs: Darkwing row 113 round 1 packet (#1607)
Ledger skips T3 reasoning summaries and reads issue pages with a
10-page ceiling. Candidate manifest, patch against cc48b124f, mutants
(both killed) and a passing full gate.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:25:55 -05:00
jason.woltjeandClaude Opus 5.5 2b8abe11ac docs: brief for the decision-close message to the raising role (#1609)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:25:09 -05:00
jason.woltjeandClaude Opus 5.5 ce8669219d docs: BUILD-LOG and SESSIONS for row 112 landing and the cc48b124f re-pin (#1606, #1607, #1608)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:14:45 -05:00
jason.woltjeandClaude Opus 5.5 d7e1d5ed5d docs: brief for the Discord connector on a pinned copy (#1608)
Mos asked on 2026-10-14 for the connector unit to follow row 85's rule.
install --copy, status names the copy, check --offline, tests and docs.
The live repoint waits for Mos's go after the SetSpark freeze lifts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:13:43 -05:00
jason.woltje 5b86abe47d queue: row 112 round 2 approvals and done (#1606, revs 1115-1117) 2026-10-14 11:09:12 -05:00
jason.woltjeandClaude Opus 5.5 cc48b124f1 bus, harness: decision.raise takes one recommendation key and says which field failed (row 112, #1606)
A recommendation that isn't exactly one option key now refuses
invalid-recommendation, checked after the options, instead of sharing
invalid-options with bad option lists. The raise_decision tool text says
recommendation is one key with the reason in question, and that
approvalChoice defaults to yes. The bus README lists the three codes,
the field each names and the check order; a malformed option object
refuses invalid-request. No schema change.

Candidate manifest 013c82c2fd99d23d92f6bed543a788aa04ff97b164019ccb15252ae808b4138e
(Darkwing round 2, packet fee89d52f), BASE 96656dd21. Approved by Filbert
(comment 28383) and Sage (comment 28384). Sage gate rerun at f63fc20e4
plus the candidate passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:07:12 -05:00
jason.woltjeandClaude Opus 5.5 f63fc20e48 docs: register Filbert row 112 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:01:44 -05:00
jason.woltjeandClaude Opus 5.5 825fb2083d queue: row 112 round 2 in review, row 113 added and briefed, row 7 note (#1606, #1607, #1508, revs 1108-1114)
Filbert's round 1 changes verdict 1108; Darkwing's round 2 start and
in-review revs 1109, 1113-1114; Sage's row 113 add and brief 1110-1111
(ledger reasoning rows and issue pages, #1607) and row 7's partial-run
note 1112.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:00:55 -05:00
jason.woltjeandClaude Opus 5.5 7265acd3ba docs: register Darkwing row 112 round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:00:23 -05:00
jason.woltjeandClaude Opus 5.5 fee89d52fe darkwing: row 112 round 2 packet, invalid-options README row (#1606)
README invalid-options cell lists only count, key, text and duplicates; a
non-{key,text} option refuses invalid-request (Sage 28378, Filbert C1 28379).
Broker test pins the option shape cases. m1 and m2 killed; full gate passed
15:53:56-15:59:33Z on BASE 96656dd21.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:59:57 -05:00
jason.woltjeandClaude Opus 5.5 960988bae2 docs: brief for the ledger reasoning-row and issue-page fix (#1607)
Two fail-closed refusals stop every full weekly ledger run: T3's new
role 'reasoning' summary rows, and more than 50 issues updated since
the week's start. Owner darkwing after row 112, reviewer filbert.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:54:59 -05:00
jason.woltjeandClaude Opus 5.5 717e2d35b8 docs: register Filbert row 112 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:52:57 -05:00
jason.woltjeandClaude Opus 5.5 2d5b59d08e queue: row 112 round 1 in review, Sage verdict and correction note (#1606, revs 1103-1107)
Darkwing's start and in-review revs 1103-1105, Sage's round 1 record
1106 (approve, comment 28377) and note 1107 recording its replacement
by changes in comment 28378.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:52:47 -05:00
jason.woltjeandClaude Opus 5.5 837f996fc1 docs: register Darkwing row 112 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:48:34 -05:00
jason.woltjeandClaude Opus 5.5 dbf72a2a3e darkwing: row 112 round 1 packet, decision.raise recommendation fix (#1606)
Candidate on 96656dd21: broker split refuses invalid-recommendation after the
option checks, raise_decision text asks for one key and names the yes default,
bus README codes table, broker and tools tests. Mutants m1 (old shared line)
and m2 (order swapped) killed; full gate passed 15:42:03-15:47:36Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:48:10 -05:00
jason.woltjeandClaude Opus 5.5 28c08328ed docs: register Sage row 112 filing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:38:03 -05:00
jason.woltje 96656dd210 queue: row 112 added and briefed, decision.raise recommendation fix (#1606, revs 1101-1102) 2026-10-14 10:37:35 -05:00
jason.woltjeandClaude Opus 5.5 1f88223b53 docs: brief for row 112, decision.raise recommendation fix (#1606)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:36:25 -05:00
jason.woltjeandClaude Opus 5.5 29d34fd1a0 docs: register Sage re-pin steps 1-2 at 179697a8a
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:50:40 -05:00
jason.woltjeandClaude Opus 5.5 a905d0a7d6 docs: register Sage row 110 round 2 review and landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:45:26 -05:00
jason.woltjeandClaude Opus 5.5 9a373b51ff queue: row 110 landed as 179697a8a and done (revs 1099-1100)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:45:18 -05:00
jason.woltjeandClaude Opus 5.5 179697a8a3 bus: tester identity for PM tests (row 110, #1604)
A tester binds with a process proof from the bus's own tester-cli.mjs
when the business var bus.tester is true. It may send [mos test] messages
to a role, receive replies on the tester address and use the read verbs;
every other verb refuses. Messages store from_role tester and write no
human.input event. turnRequest() marks them as never Jason's instruction.
human.mjs is unchanged.

Candidate manifest bdaf7f476abd0b2b5095ab2f7f43e38540e6ba48ee14a02a4f4995a1469b79df
(Darkwing round 2, packet 9f1563ec1), BASE badfa52b9. Approved by Filbert
(comment 28360) and Sage (comment 28361). Sage gate rerun passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:43:40 -05:00
jason.woltjeandClaude Opus 5.5 67ac176f2a queue: row 110 round 2 in review, Filbert and Sage approve (revs 1094-1098)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:43:13 -05:00
jason.woltjeandClaude Opus 5.5 55669e5751 docs: register Filbert row 110 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:30:18 -05:00
jason.woltjeandClaude Opus 5.5 edbd9dbef4 docs: register Darkwing row 110 round 2 packet and review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:29:13 -05:00
jason.woltjeandClaude Opus 5.5 9f1563ec16 work: row 110 round 2 packet, the role-cap envelope case (#1604)
Folds Filbert's N1 per Sage's comment 28355: the envelope test sends a
role cap with a tester proof. The narrowing mutant at server.mjs:50
survives the full bus suite on round 1 and is killed on round 2. Full
gate passed with the real bwrap tester case running.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:28:46 -05:00
jason.woltjeandClaude Opus 5.5 eb3fcd24b0 docs: register Sage row 110 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:20:59 -05:00
jason.woltjeandClaude Opus 5.5 826001405f queue: row 110 round 1 in review, Filbert approve, Sage changes (revs 1090-1093)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:20:35 -05:00
jason.woltjeandClaude Opus 5.5 551654ee16 docs: register Filbert row 110 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:17:44 -05:00
jason.woltjeandClaude Opus 5.5 4416a42f03 docs: register Darkwing row 110 round 1 packet and review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:11:48 -05:00
jason.woltjeandClaude Opus 5.5 0bfc166542 work: row 110 round 1 packet, bus tester identity for PM tests (#1604)
Build notes, patch and candidate manifest on BASE badfa52b9, the gate
logs (GATE_PASSED; the real bwrap tester case in seat sandbox.test.mjs
ran and passed) and the mutant runs (94 killed, b44 and b45 equivalent).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 20:11:16 -05:00
jason.woltjeandClaude Opus 5.5 94ebfcac30 docs: register Sage rows 111 and 109 landings and the re-pin to badfa52b9
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:27:11 -05:00
jason.woltjeandClaude Opus 5.5 84211e4ae5 docs: register Researcher row 109 candidate and round 3, Dewey row 111 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:24:54 -05:00
jason.woltjeandClaude Opus 5.5 ab0e7ae92e queue: revs 1081-1089, rows 109 and 111 landed and done, row 110 brief re-pinned and started (1081-1082 Sage's row 109 round 3 request, comment 28332; 1083 Filbert's approve, comment 28333; 1084-1085 Sage's row 111 note and done; 1086-1087 Sage's row 109 note and done; 1088 Sage's row 110 brief re-pin; 1089 Darkwing's row 110 start). Correction: be6f2ed8c also swept 1079-1080, Researcher's row 109 round 3 in-review move and failed request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:24:39 -05:00
jason.woltjeandClaude Opus 5.5 badfa52b9d harness: a new PM run reads current launch state and open tasks before each turn, and old messages show their age, row 109 (#1603)
The broker gains a read-only state verb (launch state with the event that set it, open tasks capped). The runner fetches it before each turn and puts a "Current state" block at the top of the request; a failed read fails the turn. turnRequest() adds the message time. roles/pm.md gets one rule: the block is the truth for launch state and task status. Seat session, seat sandbox and cli launcher tests re-anchored to the new request shape.

Built by Researcher, packet 875c515e9. Filbert approved round 3, comment 28333, candidate manifest 0a62225e (11 paths). Sage gate on 9774d70db plus row109.patch: every package node suite and every scripts/test-*.sh green on the first run, skipped 0 in bus, harness and seat. Vikunja #259.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:12:54 -05:00
jason.woltjeandClaude Opus 5.5 848ceb68a4 docs: row 111 landing note, Dewey note a recorded for the next host.mjs row (#1605)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:02:17 -05:00
jason.woltjeandClaude Opus 5.5 9774d70db5 cli: bus host start releases host.lock and the broker on every failure after the reply, and handles the child error event, row 111 (#1605)
Dewey approve, comment 28331, manifest 5e888f93. Sage gate on be6f2ed8c
plus the patch: every package suite green (cli 178); test-queue failed
review.test.mjs:123 once under load (16s vs 3.7s), passed 28/28 rerun alone;
release 14, task 98. Dewey note a (ended() rejects on a child error and skips
release()) is recorded for the next row that touches host.mjs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:59:06 -05:00
jason.woltjeandClaude Opus 5.5 e60410566c docs: register Filbert row 109 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:52:40 -05:00
jason.woltjeandClaude Opus 5.5 be6f2ed8cc queue: revs 1066-1078, row 109 rounds 1-2 and round 3 start, row 111 in review and approved (1066, 1076-1077 Darkwing's start, in-review move and request; 1067-1071, 1075 Researcher's moves and failed requests; 1074 Filbert's changes, comment 28329; 1078 Dewey's approve, comment 28331; 1072-1073 Sage's row 109 round 2 request, comment 28328)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:49:43 -05:00
jason.woltjeandClaude Sonnet 5.5 875c515e92 docs: Researcher row 109 round 3 packet (#1603): seat session, cli launcher and seat sandbox tests re-anchored, N1 comment, two N2 tests, 11-file candidate manifest
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 18:48:58 -05:00
jason.woltjeandClaude Opus 5.5 80bc9d618e docs: amend row 110 ruling 2, tester binds with a process proof (#1604, Darkwing af191a3a6)
Role sessions can reach broker.sock, so "no proof" is withdrawn. tester-cli
shim, path-bound, own nonce; verifyTester in tester.mjs; verifyHuman unchanged;
the real bwrap case must run, not skip.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:47:32 -05:00
jason.woltjeandClaude Opus 5.5 aafd9bbc78 docs: register Darkwing row 110 sandbox reach request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:47:05 -05:00
jason.woltjeandClaude Opus 5.5 af191a3a64 docs: Darkwing row 110 sandbox reach: a role session can open broker.sock, so the tester bind needs a process proof (#1604)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:46:46 -05:00
jason.woltjeandClaude Opus 5.5 503a81b506 docs: rule row 110 tester switch placement (#1604, Darkwing proposal 8eac0acb9)
bus.tester business var, default false; tester socket field with no proof,
refused on role- or reader-bound connections; tester reply address; the
"never Jason's instruction" line in turnRequest() after row 109 lands.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:43:51 -05:00
jason.woltjeandClaude Opus 5.5 5520ce3481 docs: register Darkwing row 110 placement proposal
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:43:27 -05:00
jason.woltjeandClaude Opus 5.5 8eac0acb90 docs: Darkwing row 110 switch placement proposal for Sage's ruling (#1604)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:43:11 -05:00
jason.woltjeandClaude Opus 5.5 d0cc1ac905 docs: register Darkwing row 111 round 1 review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:40:55 -05:00
jason.woltjeandClaude Opus 5.5 67e4160742 docs: Darkwing row 111 round 1 candidate packet (#1605, bus host start: one failure path after the broker's reply, and the child error event)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:40:17 -05:00
jason.woltjeandClaude Opus 5.5 017a7bc93b docs: register Filbert row 109 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:17:03 -05:00
jason.woltjeandClaude Sonnet 5.5 55c6cec736 docs: Researcher row 109 round 1 manifest fix: candidate manifest lists the 8 candidate files, packet digests moved to packet-manifest.sha256
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 18:11:54 -05:00
jason.woltjeandClaude Sonnet 5.5 8ea93f556e docs: Researcher row 109 candidate packet (#1603, PM reads current launch state and task status before it plans)
Candidate patch (state verb, runner state block, header time, pm.md rule, fake-adapter fixture), six new harness tests, six mutants, full suite outputs, candidate manifest.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 18:10:24 -05:00
jason.woltjeandClaude Opus 5.5 02fe2d9d55 docs: register Dewey row 106 review and Sage row 106 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:04:01 -05:00
jason.woltje 614e820936 queue: revs 1059-1065, row 106 done (landed 8b58cb7e4, #1600 closed), follow-up row 111 (#1605) briefed for Darkwing (1059-1060 Darkwing's in-review move and request, 1061 Dewey's approve) 2026-10-13 18:03:53 -05:00
jason.woltjeandClaude Opus 5.5 c77ef50aba docs: brief the bus host start follow-up (#1605, Dewey 28319 note a and Darkwing's error event)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:02:57 -05:00
jason.woltjeandClaude Opus 5.5 8b58cb7e47 cli: release host.lock on a late fork or send throw; retry a vanishing lock (row 106, #1600)
Darkwing round 1, approved by Dewey in #1600 comment 28319 on manifest
ae38cbe280aa5885aec7648d38143920753db86ea1ca3934d0a8885fb61e5105.
Sage gate rerun on f63c965f9 plus build.patch: every package suite and
every scripts/test-*.sh green (cli 168, harness 73, test-release 14).
readHostState now treats a host.json that vanishes mid-read as no host
for stopHost and hostStatus too (Dewey note b); Sage accepts it as
correct. Nothing goes live until a re-pin under Mos's go.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 18:01:02 -05:00
jason.woltjeandClaude Opus 5.5 f63c965f99 docs: register Darkwing row 106 round 1 in review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:51:10 -05:00
jason.woltjeandClaude Opus 5.5 5c0f0bd468 docs: register Sage sage-0148 follow-through, rows 100, 109 and 110
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:50:28 -05:00
jason.woltjeandClaude Opus 5.5 7109e39e00 work: Darkwing row 106 round 1 packet (#1600)
Bus host lock follow-ups: release on a fork or send throw, retry a lock
that vanishes before the read, the have order test fixed to directory
order, the third-racer README sentence and the reviewer wrap. Gate passed
on 0069949ff; 9 mutant runs as expected.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:50:20 -05:00
jason.woltjeandClaude Opus 5.5 c2801fdf0f queue: revs 1053-1058, row 100 done on Jason's "row 100 ok" (Vikunja #257 c144), rows 109 (#1603) and 110 (#1604) queued and briefed with gate owner sage (1058 Researcher's row 109 start)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:50:15 -05:00
jason.woltjeandClaude Opus 5.5 774fc54ac4 docs: briefs for the PM fresh-state row (#1603, Vikunja #259) and the bus tester identity row (#1604, Q134 A, Vikunja #260)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:47:45 -05:00
jason.woltjeandClaude Opus 5.5 fbe0da4cc5 docs: register Dewey row 104 verdict and attribution notes, Sage row 104 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:43:48 -05:00
jason.woltjeandClaude Opus 5.5 3c1ce286ef queue: revs 1047-1052, row 104 landed and done, row 106 unblocked (1049 Darkwing's row 106 start, 1050 Dewey's row 104 approve record)
1047-1048 Sage: row 106 after none, with a note. The note's time reads "22:2xZ", a placeholder Sage left in. The actual time is in SESSIONS.
1051 Sage: row 104 attribution note (Dewey's 28293 posted under jarvis, confirmed as dewey in 28294).
1052 Sage: row 104 done. The landing note was refused for length, so 0069949ff and #1598 comment 28298 are the landing record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:43:38 -05:00
jason.woltjeandClaude Opus 5.5 0069949ffc seat: give role sessions a git identity and report the checkout commit (row 104, #1598)
sandboxArgv sets GIT_AUTHOR_NAME/EMAIL and GIT_COMMITTER_NAME/EMAIL from
the instance and business ids (name = instance id, email
<instance>@<business>.mosaic.invalid); spawnSession and the launcher pass
the ids through. checkout results carry the clone's commit (a kept clone
gets none), and the bundle prompt names it. allowed() is unchanged.
Packet agents/darkwing/work/queue-104/r1/ (manifest f99f2ae7). Dewey
approved round 1 in 28293 (posted under jarvis by mistake; 28294 is
Dewey's confirmation). Sage's gate on f00613106 plus the candidate:
every package suite and scripts/test-*.sh green (test-release failed 3
activation checks once, not reproduced; rerun 14/0).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:40:33 -05:00
jason.woltjeandClaude Opus 5.5 f006131061 docs: register Researcher row 107 build and Sage row 107 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:18:58 -05:00
jason.woltjeandClaude Opus 5.5 665816ea61 docs: register Darkwing row 104 round 1 in review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:18:52 -05:00
jason.woltje 6ebad7eaaa queue: revs 1037-1044, row 107 landed and done (1037-1039 Researcher's start and in-review, 1042 Filbert's approve record) 2026-10-13 17:18:44 -05:00
jason.woltjeandClaude Opus 5.5 b2ae135524 docs: Darkwing row 104 round 1 packet, seats commit under their own name (#1598)
Candidate against 817ddfea6: git identity in the sandbox environment,
commit field in checkout results, one prompt line. Gate green, 19 of 19
mutants killed. The candidate itself is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:17:53 -05:00
jason.woltjeandClaude Opus 5.5 fe27adea1c queue: pin the add retry order and drop row history from the gate-owner refusal (row 107, #1601)
A retried privileged add recorded with no gate owner answers with its
receipt before the --gate-owner check runs; a test pins that order and
a hoisting mutant fails it. The refusal no longer cites row 100.
SEMANTICS stays 2. Packet: agents/researcher/work/queue-107/ (manifest
b70dfe4a). Filbert approved round 1 in comment 28286. Sage's
canonical-root gate: queue 156/0, test-queue 30/0 with queue verify.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:16:51 -05:00
jason.woltjeandClaude Opus 5.5 ae4ae7102a docs: register Filbert row 107 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:05:39 -05:00
jason.woltjeandClaude Sonnet 5.5 c6f90c47c1 docs: Researcher row 107 packet, retry-order test and refusal text for queue add
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 17:02:13 -05:00
jason.woltjeandClaude Opus 5.5 93580172c6 docs: register Sage post-window cleanup and follow-up rows
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:58:46 -05:00
jason.woltje b561515648 queue: revs 1030-1036, rows 106-108 filed and briefed (1030 is Darkwing's row 104 start) 2026-10-13 16:58:27 -05:00
jason.woltjeandClaude Opus 5.5 aedc5db8f4 docs: brief the follow-ups from rows 85, 101 and 103
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:56:51 -05:00
jason.woltjeandClaude Opus 5.5 847897636e docs: register Sage row 85 live window and row 96 gate
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:52:06 -05:00
jason.woltje 35c9022328 queue: revs 1028-1029, row 96 done 2026-10-13 16:51:32 -05:00
jason.woltjeandClaude Opus 5.5 0e9eae68c7 logs: refactor pushed to 817ddfea6, row 96 packet scan exception (sage-0144)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:46:35 -05:00
jason.woltjeandClaude Opus 5.5 817ddfea65 logs: rows 101, 105, 85, 102, 103 landed; row 103 verify-commit exception (sage-0143)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:43:06 -05:00
jason.woltjeandClaude Opus 5.5 7e86dddbc6 docs: register sessions, researcher rows 96/102, dewey rows 96/103, sage landings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:41:26 -05:00
jason.woltjeandClaude Opus 5.5 8862cd5fd8 queue: revs 975-1027, rows 85, 101, 102, 103 and 105 done
Rows 101 (056a20397), 105 (3a1e11b83), 85 (305a012b1), 102 (2a9b77b56)
and 103 (465bbdfbb) landed and moved to done; rows 42 and 99 brief
re-pins (1020, 1021). Swept revisions from other seats:
darkwing 975 1005 1006 1015; rocko 976 980 981 989 1009 1010;
researcher 977-979 985 986 991 992 996-998 1001 1002;
filbert 984 990 995 1007 1014; dewey 1013 1019.
Sage: 982 983 987 988 993 994 999 1000 1003 1004 1008 1011 1012
1016-1018 1020-1027.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:41:12 -05:00
jason.woltjeandClaude Opus 5.5 465bbdfbbc seat, cli, harness: git kill reach, close bound and host.lock (#1597)
Row 103, the row 100 follow-up. Built by Darkwing, packet
agents/darkwing/work/queue-103/r1/, candidate manifest
6e5ac8c0782bc595d7c0d711d457738ffb42f1ba5f38253912943dd55e55888a.
Sage approved on #1597, comment 28252; Dewey approved, comment 28260.

Host git runs through runGit: a detached group, SIGTERM at the limit,
SIGKILL after the grace, and one more SIGKILL to the group after a
signalled git closes. git-socket close() waits at most 2 s. startHost
claims bus-host/host.lock by hard link before the broker fork.
packages/cli/README.md and tests/host.test.mjs carry row 85's hunks
too; row 85 lands first.

Sage's gate rerun on 3a1e11b83 with rows 85, 102 and 103 applied,
Docker up: node suites bus 128, business 67, cli 165, control-board
124, conversation 182, discord 178, harness 73, ledger 78, mosaic 69,
queue 155, runs 43, seat 113, tasks 92, webui 108; scripts auth 15,
conductor 17, config 24, discord 66, extension-package 18,
foundation 44, queue 28, release 14, task 98. All pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:34:47 -05:00
jason.woltjeandClaude Opus 5.5 2a9b77b56b tasks, webui: field table check, line length and bidi escapes (#1596)
Row 102, the row 88 follow-up. Built by a Haiku subagent under
Researcher (decision 129), packet agents/researcher/work/queue-102/,
row102.patch in candidate manifest
8dedcec601ee79a08b4421842af694c0acdae6af3da31b26505c581d3dbc5558.
Filbert approved on #1596, comment 28251. Mos gave the go for the one
public/ line, bus.js's UNSEEN pattern (decision 123, sage-0140).

Sage's gate rerun on 3a1e11b83 with rows 85, 102 and 103 applied,
Docker up: node suites bus 128, business 67, cli 165, control-board
124, conversation 182, discord 178, harness 73, ledger 78, mosaic 69,
queue 155, runs 43, seat 113, tasks 92, webui 108; scripts auth 15,
conductor 17, config 24, discord 66, extension-package 18,
foundation 44, queue 28, release 14, task 98. All pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:34:47 -05:00
jason.woltjeandClaude Opus 5.5 305a012b13 cli, webui: bus, Console and mosaic CLI on one pinned copy (#1579)
Row 85 round 2. Built by Rocko, packet agents/rocko/work/queue-85/r2/,
candidate manifest
bd7ea899c7cacef43b415b8e9ecb164e1f36718ffbe64347881a2c5653170c98.
Filbert approved on #1579, comment 28256; Darkwing approved, comment
28258.

stack-copy.sh builds shared read-only copies under
XDG_DATA_HOME/mosaic-stack/copies; the bus and Console services render
and install against a copy with --copy and --pm. No live step is run
by this commit; those need Mos's go.

Sage's gate rerun on 3a1e11b83 with rows 85, 102 and 103 applied,
Docker up: node suites bus 128, business 67, cli 165, control-board
124, conversation 182, discord 178, harness 73, ledger 78, mosaic 69,
queue 155, runs 43, seat 113, tasks 92, webui 108; scripts auth 15,
conductor 17, config 24, discord 66, extension-package 18,
foundation 44, queue 28, release 14, task 98. All pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:34:47 -05:00
jason.woltjeandClaude Opus 5.5 3a1e11b83e bus: review.request reports whether a reviewer holds the role (#1599)
Row 105. Built by a Haiku subagent under Researcher (decision 129),
packet agents/researcher/work/queue-105/r1/, candidate manifest
cd6d5eb3e4093ac157f63d05cb639d243d76c6bde1a03e78efb8f29e191b6b44.
Filbert approved on #1599, comment 28247.

The review.request reply and the review.requested event carry held,
the run id holding the reviewer role or null. The request_review tool
description says what null means. The broker change is live only at
a bus restart.

Sage's gate rerun on 79e126be0 with rows 101 and 105 applied, Docker
up: node suites bus 128, business 67, cli 146, control-board 124,
conversation 182, discord 178, harness 70, ledger 78, mosaic 69,
queue 155, runs 43, seat 109, tasks 92, webui 108; scripts auth 15,
conductor 17, config 24, discord 66, extension-package 18,
foundation 44, queue 28, release 14, task 98. All pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:26:12 -05:00
jason.woltjeandClaude Opus 5.5 056a20397d queue: queue add by jason or sage requires --gate-owner (#1595)
Row 101. Built by a Haiku subagent under Researcher (decision 129),
packet agents/researcher/work/queue-101/r1/, candidate manifest
ee1335a6e44610f676ade116733d180ac6f0b3603a58306110cbd1dd1e9c1f69.
Filbert approved on #1595, comment 28245.

The check runs at write time in store.mjs mutateAs, with no SEMANTICS
bump, so the live Console keeps reading the log.

Sage's gate rerun on 79e126be0 with rows 101 and 105 applied, Docker
up: node suites bus 128, business 67, cli 146, control-board 124,
conversation 182, discord 178, harness 70, ledger 78, mosaic 69,
queue 155, runs 43, seat 109, tasks 92, webui 108; scripts auth 15,
conductor 17, config 24, discord 66, extension-package 18,
foundation 44, queue 28, release 14, task 98. All pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:26:12 -05:00
jason.woltjeandClaude Opus 5.5 a2714a680e docs: register Darkwing row 85 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:25:54 -05:00
jason.woltjeandClaude Opus 5.5 93e8ae0c73 docs: register Filbert row 85 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:23:29 -05:00
jason.woltje ff16635cff logs: Rocko row 85 round 2 review packet and full gate (#1579) 2026-10-13 16:19:52 -05:00
jason.woltjeandClaude Opus 5.5 0f136a3f0a docs: register Filbert row 102 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:17:13 -05:00
jason.woltjeandClaude Opus 5.5 1153355f88 docs: register Darkwing row 103 round 1 in review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:14:25 -05:00
jason.woltjeandClaude Opus 5.5 d38a7a3b6b logs: Darkwing row 103 build.md, test-task ran without Docker (#1597)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:13:44 -05:00
jason.woltjeandClaude Sonnet 5.5 486c4aad34 research: row 102 build packet, row 88 follow-up N1-N4 (#1596)
Header sentence, line 13 wrap, bidi escapes in three files, inertCheck U+200E/U+200F. Patch and evidence only; the change is not applied to the tree.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 16:12:24 -05:00
jason.woltjeandClaude Opus 5.5 0f7df31901 logs: Darkwing row 103 round 1 packet, host.lock claim, gate and mutants (#1597)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:12:11 -05:00
jason.woltjeandClaude Sonnet 5.5 587e64a27f research: row 96 stand-in-key test of CLIProxyAPI for role sessions (#1590)
Packet under agents/researcher/work/i1-proxy-test: private-netns harness, stand-in
upstream, report, evidence. Stand-in keys only; running proxy untouched.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 16:07:07 -05:00
jason.woltjeandClaude Opus 5.5 79e126be04 docs: register Filbert row 105 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:56:19 -05:00
jason.woltjeandClaude Opus 5.5 2b0867bee1 docs: register Filbert row 101 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:48:32 -05:00
jason.woltjeandClaude Opus 5.5 09241ffb6f docs: register Filbert row 85 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:45:54 -05:00
jason.woltje 99472cf7ea logs: Rocko row 85 isolated build and gate (#1579) 2026-10-13 15:40:57 -05:00
jason.woltjeandClaude Opus 5.5 0d71b9ddde docs: register Sage re-test follow-through, rows 104-105
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:14:38 -05:00
jason.woltje 7cf85459bd queue: revs 968-974, rows 101-103 briefed, rows 104-105 added and briefed (#1598 seat git identity, #1599 review request held), gate owner sage 2026-10-13 15:14:26 -05:00
jason.woltjeandClaude Opus 5.5 511e4f3d95 docs: CURRENT.md, Jason's PM re-test outcome and the chat formatting direction
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:13:55 -05:00
jason.woltjeandClaude Opus 5.5 18c29c28d3 docs: briefs for the re-test faults, seat git identity and review request held
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:12:49 -05:00
jason.woltjeandClaude Opus 5.5 0c3826910e docs: register Sage follow-up rows 101-103
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:25:21 -05:00
jason.woltje 7d39a6218d queue: revs 965-967, rows 101-103 queued (#1595 queue add gate owner, #1596 row 88 notes, #1597 row 100 notes), gate owner sage 2026-10-13 12:24:39 -05:00
jason.woltjeandClaude Opus 5.5 ec6e398817 docs: post-retest follow-up briefs, queue add gate owner, row 88 and row 100 notes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:23:41 -05:00
jason.woltjeandClaude Opus 5.5 dff7e52610 docs: register Dewey row 100 review, with a correction for 9d14b7a2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:21:55 -05:00
jason.woltjeandClaude Opus 5.5 9d14b7a271 docs: register Dewey row 100 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:21:05 -05:00
jason.woltjeandClaude Opus 5.5 22da9ec57b docs: register Sage row 100 landing and bus restart
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:20:57 -05:00
jason.woltje 0da89ac2f8 queue: revs 960-964, row 100 review request (Darkwing revs 960-961, #1594), approvals (Sage 962, Dewey 963) and waiting-on-jason (964) 2026-10-13 12:19:24 -05:00
jason.woltjeandClaude Opus 5.5 664199f794 cli, seat, harness: git socket hardening and refusal tests, row 100 (#1594)
Row 93 follow-up: SIGTERM then SIGKILL for host git, rollback-failed,
a total body deadline, fetch-aborted for a reviewer gone mid-fetch,
working refs first in have, and tests for five untested refusals.
Dewey approved in 28226 and Sage in 28225; Sage's full gate passed
16:57:32-17:01:32Z. The host side is live at the next bus restart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 12:15:52 -05:00
jason.woltjeandClaude Opus 5.5 c8302a0675 docs: register Darkwing row 100 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:56:42 -05:00
jason.woltjeandClaude Opus 5.5 76762c9f2b queue: revs 956-959, row 88 approval (Filbert 28214) and done (82414385, #1582), row 100 in review (Darkwing revs 958-959, #1594)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:56:32 -05:00
jason.woltjeandClaude Opus 5.5 ac430b5cfb darkwing: row 100 round 1 packet, git socket hardening and refusal tests on 8e0fbca4 (#1594)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:55:33 -05:00
jason.woltjeandClaude Opus 5.5 a50c9d23ae docs: register Sage rows 80 and 88 landings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:23:52 -05:00
jason.woltjeandClaude Opus 5.5 824143858f tasks, webui: field table notes and tests, scroll hint label, row 88 (#1582)
Row 61 follow-up. Filbert approved round 1 in comment 28214; Sage's
full gate passed 16:18:32-16:22:34Z. The public/bus.js change is the
one aria-label line Mos released under decision 123.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:22:55 -05:00
jason.woltjeandClaude Opus 5.5 97097efce0 docs: register Filbert row 88 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:20:34 -05:00
jason.woltjeandClaude Opus 5.5 13005d305c queue: revs 949-955, row 100 in progress, row 80 approval (Filbert 28206) and done (8e0fbca4, #1574), row 88 in review (#1582, comment 28213)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:16:57 -05:00
jason.woltjeandClaude Opus 5.5 f0bab46e9a researcher: row 88 round 1 packet, Haiku subagent build on 8e0fbca4 (#1582)
Five paths: tasks fields.mjs and fields.test.mjs, webui README, public/bus.js
(aria-label only) and bus-browser.test.mjs. Phase B: 14 node suites and 9
scripts green, 8 of 8 mutants killed. The literal U+202E is now an escape.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:16:12 -05:00
jason.woltjeandClaude Opus 5.5 8e0fbca420 bus: list messages stranded by a holder change in agents, row 80 (#1574)
A holder that releases a role without reading what it was delivered leaves
those messages where no later holder receives or reads them. The agents
view now lists them for human and reader sessions under `stranded`, as
{id, from, run, at}, oldest first, at most 50. The bus doesn't redeliver,
route or mark them read; a person who still needs one sends it again.

Round 2 is a mechanical rebase of round 1 onto row 93. Reviewed by Filbert
(28206, approve); Sage gate record 28207. Gate on 7496251c: 14 node suites
and 9 scripts green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:01:50 -05:00
jason.woltjeandClaude Opus 5.5 177b49ebf9 docs: register Filbert row 80 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:58:15 -05:00
jason.woltjeandClaude Opus 5.5 7496251cf6 queue: revs 943-948, row 100 briefed, row 80 round 2 in review (#1574, comment 28205)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:57:01 -05:00
jason.woltjeandClaude Sonnet 5.5 199984f29c researcher: row 80 round 2 packet, mechanical rebase onto row 93 (#1574)
Base da6f1166, r1 patch applied clean with --index, +/- and context lines identical.
Bus suite 127/127, mutants 15 of 15 killed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 10:55:33 -05:00
jason.woltjeandClaude Opus 5.5 b3d43db3a3 docs: register Sage row 93 landing and bus restart
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:54:18 -05:00
jason.woltjeandClaude Opus 5.5 b610ecf761 docs: register Dewey rows 71 and 93 round 1 reviews
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:52:49 -05:00
jason.woltjeandClaude Opus 5.5 da6f1166d6 queue: revs 939-942, row 93 approvals (Dewey 28196, Sage 28197), row 93 done (4da68152, #1587), row 100 queued (#1594)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:52:02 -05:00
jason.woltjeandClaude Opus 5.5 8fda96112e docs: row 93 follow-up brief, git socket hardening and refusal tests (#1594)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:51:27 -05:00
jason.woltjeandClaude Opus 5.5 4da6815293 review loop: working repositories, git socket, push and review verdicts, row 93 (#1587)
Commits move as v2 bundles over bus-host/git.sock (decision 135). The bus
host keeps one bare working repository per business and project, seeds main
from the project root under a per-repository lock, fetches a pushed bundle
with fsck into a temp ref, requires history shared with main, and moves the
task's work ref only as a fast-forward. Host and runner git run with no
system or global config, hooks at /dev/null and local transports only. The
socket binds only for roles holding git.push.working or review.verdict.

The broker records a push as the action.allowed for git.push.working with
its commit, takes review.request for the latest push and one review.verdict
per request, and refuses task.close on a pushed task without an approve
verdict for the latest push. recordEvent reserves both shapes, so agents
can't forge either.

Reviewed by Dewey (28196) and Sage (28197), both approve. Gate on 7a729aef:
14 node suites and 9 scripts green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:48:34 -05:00
jason.woltjeandClaude Opus 5.5 7a729aefa3 queue: revs 928-938, row 84 done (7be720fd, #1578), row 93 re-pin and in review, row 80 approval
928 Darkwing's row 84 approval; 929-932 row 80 round 1 move and review
request; 933 Filbert's row 80 approval; 934 Sage re-pins row 93's brief
to 1550f78d (the row 99 append left the row 93 section unchanged); 935
and 937-938 Darkwing's row 93 in-progress and in-review moves; 936 row 84
done.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:39:37 -05:00
jason.woltjeandClaude Opus 5.5 dbe537e29b docs: register Darkwing row 93 round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:39:05 -05:00
jason.woltjeandClaude Opus 5.5 7be720fd72 roles: chief-of-staff role file, contract and tests, row 84 (#1578)
Lands the Haiku subagent build from agents/researcher/work/queue-84/r1/
(candidate manifest f72730de, base ac6b56ee). roles/chief-of-staff.json
and .md follow decision 104's list; packages/business/tests/
chief-of-staff.test.mjs covers the contract, and the business README
names the role.

Approved by Filbert (comment 28176) and Darkwing (comment 28183). Sage's
gate on 11bf4cb0 plus the patch: 14 node suites and all nine
scripts/test-*.sh green, 15:33:26-15:37:31Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:38:03 -05:00
jason.woltjeandClaude Opus 5.5 11bf4cb00a researcher: row 80 BUILD.md, Console follow-up and fixes (#1574)
Filbert's R1 (comment 28188): the brief requires the packet to name what
the Console would need to show the stranded list. Added as a Console
follow-up section by the subagent, read-only, with unchecked items named.
Also N1 (M12 note) and N2 (round 1 title). Candidate unchanged, manifest
52c41526. Sage corrected one count: M12 failed 6 of 11 tests, not 13 (the
13 was the count of x-marker lines).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:26:12 -05:00
jason.woltjeandClaude Opus 5.5 c98344e30a docs: register Filbert row 80 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:22:14 -05:00
jason.woltjeandClaude Opus 5.5 3182fdf113 researcher: row 80 round 1 packet (#1574)
Stranded delivery list on the bus agents view. Built by Sage's Haiku
subagent (decision 129), rebased onto 57a2ddc5. Phase B, three runs:
the first failed the new test (node:sqlite null-prototype rows, fixed
by mapping to plain objects), the second stopped at cli on a missing
node_modules link, the third passed: 14 node suites, nine scripts,
15/15 mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:19:38 -05:00
jason.woltjeandClaude Opus 5.5 57a2ddc597 docs: register Sage rows 71 and 84 round 1, row 71 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:03:36 -05:00
jason.woltjeandClaude Opus 5.5 cd2f3573ec queue: revs 926 and 927, row 71 done (efd2b57a, #1565), Filbert's row 84 approval
Swept Filbert's rev 926, row 84 round 1 approve on f72730de (comment
28176). Rev 927 moves row 71 to done after the landing and close.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:03:25 -05:00
jason.woltjeandClaude Opus 5.5 efd2b57af1 tasks: confirm-read tests for 429, 401 and a failed call, and the fake's view 404 is 3014 (#1565)
Row 71. Test 18 gains three steps after the 404/503 step: the child's task
list answers 404 and its confirm read answers 429, 401 or status 0 (the
fake throws fetch failed). Each tick refuses with tracker-not-found and
the child stays. The fake answers a missing kanban view with code 3014,
the live v2.7.0 answer in the tick-62 record.

Built by Researcher's Haiku subagent (decision 129). Approved by Filbert
(28171) and Dewey (28172) on manifest 9ac3a56e. Sage's gate on e635ef1b:
every node suite and scripts/test-*.sh green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 10:02:31 -05:00
jason.woltjeandClaude Opus 5.5 03828cc41e docs: register Filbert row 84 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:59:43 -05:00
jason.woltjeandClaude Opus 5.5 e635ef1b89 queue: revs 916 to 925, rows 71 and 84 round 1 in review (#1565, #1578)
Rows 71 and 84 moved to in-review as researcher (916, 917, 922, 923; the
seat has no token, so the requests failed). Sage reposted both requests
as jarvis (918, 919: comment 28168; 924, 925: comment 28173). Swept
Filbert's rev 920 and Dewey's rev 921, row 71 approvals on 9ac3a56e.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:57:51 -05:00
jason.woltjeandClaude Opus 5.5 7198d336cd researcher: row 84 round 1 packet on ac6b56ee, business 67/0, nine mutants killed (#1578)
Haiku subagent build under decision 129. Candidate manifest f72730de
names roles/chief-of-staff.json and .md, the business README and
packages/business/tests/chief-of-staff.test.mjs. Gate and mutants ran
in Sage's slot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:56:33 -05:00
jason.woltjeandClaude Opus 5.5 d5c656464d docs: register Filbert row 71 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:53:27 -05:00
jason.woltjeandClaude Opus 5.5 471f5cf202 researcher: row 71 round 1 packet on ac6b56ee, tasks 89/0, bus 115/0, mutants killed (#1565)
Haiku subagent build under decision 129. Candidate manifest 9ac3a56e
names packages/tasks/src/fake.mjs and packages/tasks/tests/tree.test.mjs.
Gate and both named mutants ran in Sage's slot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:50:00 -05:00
jason.woltjeandClaude Opus 5.5 ac6b56ee55 docs: register Sage rows 98 and 97 landings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:42:28 -05:00
jason.woltje fae14e1bcf queue: revs 914 and 915, rows 98 (bb45f75e, #1592) and 97 (17217c77, #1591) done 2026-10-13 09:42:20 -05:00
jason.woltjeandClaude Opus 5.5 17217c7788 webui: Board tests fail fast and the README lists every redirect (#1591)
Row 97, the row 95 follow-up. Researcher's build, round 3 (agents/researcher/work/queue-97/r3/), a
confirm-only rebase of round 2 onto row 94. Darkwing (comment 28140) and Filbert (comment 28139) approved
round 3. Researcher's check on bb45f75e: webui 107 pass, bus.js mutants 26 and 27 killed (r3/check.md).
Sage's gate rerun on 865a8fc2: all 14 package node suites and all 9 scripts/test-*.sh green. Tests and
README only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:41:29 -05:00
jason.woltjeandClaude Sonnet 5.5 865a8fc2ea researcher: row 97 round 3 check on bb45f75e, webui 107/0, mutants 26 and 27 killed (#1591)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 09:36:59 -05:00
jason.woltjeandClaude Opus 5.5 bb45f75ed2 webui: four refusal cases in the Console service tests (#1592)
Row 98, the row 77 follow-up. Researcher's build (agents/researcher/work/queue-98/), approved by Filbert
in round 1 on manifest 352a3034 (comment 28110). Tests only: three new mv and rename cases in
pinned-copy.test.mjs beside the fixture repo they need, and added assertions in service.test.mjs.
Sage's gate rerun on 47a8190d: all 14 package node suites and all 9 scripts/test-*.sh green, webui 107 pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:32:45 -05:00
jason.woltjeandClaude Opus 5.5 097dec3314 filbert: row 91 round 2 gate helper from the Codex seat, kept as evidence (no verdict; decision 130 moved row 91 to Dewey)
Scanned by Sage for secret markers, none. Filbert's Opus seat left it to Sage under decision 130.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:29:20 -05:00
jason.woltjeandClaude Opus 5.5 5a21f3ef30 filbert: commit untracked verdict packets for rows 97 r2/r3 and 98 r1, row 33 manifest, s6 follow-up draft
Verdict and comment readbacks only; reconstructed candidate/ copies and
queue-91/r2 left untracked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:28:54 -05:00
jason.woltjeandClaude Opus 5.5 47a8190d3c docs: register Sage row 91 landing; file its follow-up as Vikunja #256
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:27:42 -05:00
jason.woltje 00e760d40b queue: revs 909 to 913, row 91 done (landed d240645d, #1585); row 93 to darkwing, reviewers dewey and sage, gate and decision 135 note (decision 131) 2026-10-13 09:26:38 -05:00
jason.woltjeandClaude Opus 5.5 d240645df0 cli, seat, harness: a project clone in each role workspace (#1585)
Row 91, round 3. Darkwing built it, Dewey approved it (comment 28150) on
manifest ca570607 (8 paths, base 2b2a9e14). At each launch the host
makes or updates a git clone of the project in the role's workspace,
and the session prompt names each clone. Round 3 closes Filbert's round
2 escape: a clone with a submodule is kept as it is, and no host git
recurses into one (decision 130 thread). Live at the next bus restart;
the prompt lines appear only when the launcher passes checkouts.
Sage gate on fc6d5822 plus the patch, 14:18:25Z to 14:22:27Z: bus 115,
business 60, cli 131, control-board 124, conversation 182, discord 178,
harness 67, ledger 78, mosaic 69, queue 150, runs 43, seat 89, tasks 89,
webui 104, all fail 0. Every scripts/test-*.sh exited 0; test-release
ran 14 cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:23:10 -05:00
jason.woltjeandClaude Opus 5.5 fc6d58220e docs: register Dewey row 91 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:17:29 -05:00
jason.woltjeandClaude Opus 5.5 d4dd41db2b dewey: row 91 round 3 review packet, approve (comment 28150, #1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:17:21 -05:00
jason.woltjeandClaude Opus 5.5 f29e4eb200 queue: rev 908, row 91 round 3 approve by dewey, comment 28150 (#1585); sweeps revs 903 to 907, row 99 round 1 request, outcome and block (researcher, sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:17:11 -05:00
jason.woltjeandClaude Opus 5.5 f3b175e1a9 docs: decision 135, row 93 bundles over a git socket (amends brief dff83c09)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:13:06 -05:00
jason.woltjeandClaude Opus 5.5 56007dcfd0 docs: register Filbert Opus thread onboarding (decision 134)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:11:50 -05:00
jason.woltjeandClaude Opus 5.5 bc49fb2c03 docs: decisions 133 (redeploy without rows 91 and 93) and 134 (Filbert to Opus 5.5), P19 #250 close record
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:11:18 -05:00
jason.woltjeandClaude Opus 5.5 8ac8055c4d researcher: row 99 round 1 packet, text-mode inbox fallback, phase B green (#1593)
Built by a Haiku subagent (decision 129). cli 133/0, nine scripts green,
7 of 7 mutants killed. Manifest 5c979e49.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:02:24 -05:00
jason.woltjeandClaude Opus 5.5 974c121633 vikunja: Console sign-out race from row 97 round 3 review filed as P19 #253 (decision 110, #1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:20:50 -05:00
jason.woltjeandClaude Opus 5.5 701cafca77 docs: register Darkwing row 97 round 3 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:20:16 -05:00
jason.woltjeandClaude Opus 5.5 c075bd0481 queue: revs 896 to 902, row 97 round 3 reopen and request (researcher, sage), filbert and darkwing approve, comments 28139 and 28140 (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:20:05 -05:00
jason.woltje f5a5e7be18 docs: register Filbert row 97 round 3 review 2026-10-13 08:18:03 -05:00
jason.woltjeandClaude Sonnet 5.5 8812bf831d researcher: row 97 round 3 packet, confirm-only rebase onto cd976b09 (#1591)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 08:15:43 -05:00
jason.woltjeandClaude Opus 5.5 e94bb289cf agents: darkwing row 91 round 3 build.md correction, 2b2a9e14 is row 94 (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:14:59 -05:00
jason.woltjeandClaude Opus 5.5 120372c5ca docs: register Darkwing row 91 round 3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:13:59 -05:00
jason.woltjeandClaude Opus 5.5 372651716f agents: darkwing row 91 round 3 packet, submodule escape finding and fix on 2b2a9e14 (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:13:51 -05:00
jason.woltjeandClaude Opus 5.5 3c641c0822 queue: revs 893 to 895, darkwing row 91 round 3 in review, comment 28133 (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:13:47 -05:00
jason.woltjeandClaude Opus 5.5 cd976b09ac queue: revs 887 to 892, row 94 done, rows 80, 88 and 99 started, row 99 added (#1588, #1593)
887 sage row 94 done (landed 2b2a9e14). 888 and 889 researcher (Haiku
subagents) start rows 80 and 88. 890 and 891 sage add and brief row 99.
892 researcher starts row 99.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:11:39 -05:00
jason.woltjeandClaude Opus 5.5 2b75ef4183 ledger: weekly run 2026-10-04 to 2026-10-10, issue call refused; P19 #252 (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:08:54 -05:00
jason.woltjeandClaude Opus 5.5 ba7553ec7a vikunja: row 94 follow-ups filed as P19 #251 (decision 110)
Dewey's notes 6 to 10 from #1588 comments 28075 and 28104.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:07:17 -05:00
jason.woltjeandClaude Opus 5.5 0cada46e79 BUILD-LOG: row 94 landed, with the text-inbox version-skew correction (#1588)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:06:17 -05:00
jason.woltjeandClaude Opus 5.5 4233534da8 decisions: 132, text-mode inbox fallback; brief for row 99 (#1593)
Mos's ruling: text-mode inbox prints the decisions and a one-line
notice when the pre-94 bus refuses the messages verb. The bus restart
does not move earlier.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 08:04:34 -05:00
jason.woltjeandClaude Opus 5.5 2b2a9e14ee bus, cli, webui: every message to the human, read state, Messages page (#1588)
Row 94, round 2. Rocko built it, Dewey approved it (comment 28104) on
manifest 53b01ea9 (16 paths, base e918a915). The bus keeps a messages
view with read state, where delivered but not read stays read:false. The
CLI gains messages and follow, and inbox leaves out messages already
read while inbox --json stays byte-compatible. The Console gets a
Messages page and an unread count in the nav. Mos gave the go under
decision 123 (public/ files). Sage gate on 645489e4 plus the patch,
12:52:14Z to 12:55:44Z: bus 115, cli 129, webui 104, all fail 0. Every
scripts/test-*.sh exited 0 (auth 15, conductor 17, config 24, discord
66, extension-package 18, foundation 44, queue 27, release 14, task 98).
Manifest 16/16 before and after.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:58:36 -05:00
jason.woltjeandClaude Opus 5.5 645489e4cc queue: rev 886, filbert row 98 round 1 approve, comment 28110 (#1592)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:56:23 -05:00
jason.woltje b803d2c039 docs: register Filbert row 98 static review 2026-10-13 07:54:49 -05:00
jason.woltjeandClaude Opus 5.5 6df295df9d decisions: 131, Darkwing builds row 93, Dewey and Sage review (Mos approved)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:52:50 -05:00
jason.woltjeandClaude Opus 5.5 879caeacb7 queue: rev 885, filbert row 97 round 2 approve, comment 28105 (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:52:09 -05:00
jason.woltje 33425de677 docs: register Filbert row 97 round 2 review 2026-10-13 07:50:11 -05:00
jason.woltjeandClaude Opus 5.5 fbbcf0b79a sessions: dewey, row 94 round 2 approve (#1588)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:49:31 -05:00
jason.woltjeandClaude Opus 5.5 7d29c97c79 review: dewey row 94 round 2 approve, manifest 53b01ea9, comment 28104 (#1588)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:49:23 -05:00
jason.woltjeandClaude Opus 5.5 e19ea81596 queue: revs 881-884, row 91 reviewer filbert to dewey, gate and note (decision 130, #1585); sweeps dewey's rev 884, row 94 round 2 approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:49:14 -05:00
jason.woltjeandClaude Opus 5.5 ce0def14e7 agents, decisions: fresh Filbert Codex thread f712d8af for non-sandbox reviews, Dewey reviews sandbox rows (decision 130)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:48:24 -05:00
jason.woltjeandClaude Opus 5.5 5098e7a0a5 sessions: darkwing, row 97 round 2 approve (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:45:13 -05:00
jason.woltjeandClaude Opus 5.5 2d4015cf61 queue: rev 880, row 97 round 2 approve by darkwing, comment 28097 (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:45:04 -05:00
jason.woltjeandClaude Opus 5.5 ea87adc165 queue: revs 875-879, row 97 round 2 in review, comment 28094 (#1591); researcher's revs 875-877 (ops researcher-r97-r2-reopen-20261013, researcher-r97-r2-review-20261013), sage's request 878-879 as jarvis
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 07:41:06 -05:00
jason.woltjeandClaude Sonnet 5.5 13212ae7eb sessions: researcher, row 97 round 2 in review
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 07:40:10 -05:00
jason.woltjeandClaude Sonnet 5.5 5aa38c3a25 researcher: row 97 round 2 build packet (#1591)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 07:40:06 -05:00
jason.woltjeandClaude Opus 5.5 8eda2ac50a queue: rev 874, row 84 in-progress, built by a Haiku subagent as researcher (decision 129); row 71's rev 872 rode in dff0ca15
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:13:23 -05:00
jason.woltjeandClaude Opus 5.5 e354d112bc sessions: darkwing, row 91 round 2 in review (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:12:42 -05:00
jason.woltjeandClaude Opus 5.5 dff0ca15e9 queue: revs 870-873, row 91 round 2 in review by darkwing, comment 28087 (#1585); sweeps researcher's rev 872 (op researcher-h71-start-20261013)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:12:32 -05:00
jason.woltjeandClaude Opus 5.5 dac20d8bf9 agents: darkwing row 91 round 2 packet, rebased onto 6b0215aa after row 90 (#1585)
Round 1's patch on the new base, no change of its own (decision 127).
Manifest 6b58085b, 8 paths; only the two bundle files' digests move,
by row 90's lines. checkout.mjs stays out of RUNNER_FILES: it runs on
the host. Gate 00:07:08Z to 00:10:09Z, seat 83, cli 125, bus 111,
harness 67, tasks 89, every scripts/test-*.sh rc=0 with Docker off.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:11:42 -05:00
jason.woltjeandClaude Opus 5.5 a388f87799 agents, decisions: Haiku rows as Sage-dispatched subagents, c98aaf4a retired (decision 129)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:10:03 -05:00
jason.woltjeandClaude Opus 5.5 0d3eaa2659 queue: rev 869, row 94 round 2 review request posted as jarvis, comment 28086 (#1588); sweeps rocko's revs 866-867 (row 94 in-review and the expected tokenless request failure, op rocko-94-r2-review-20261013)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 19:08:48 -05:00
jason.woltje 18674ab0f3 logs: Rocko row 94 round 2 rebase and gate (#1588) 2026-10-12 19:07:13 -05:00
jason.woltje 4f85812d38 review: preserve row 91 static findings pending round 2 (#1585) 2026-10-12 19:03:54 -05:00
jason.woltjeandClaude Opus 5.5 58a3766d48 sessions: darkwing, row 97 round 1 review, changes (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:59:03 -05:00
jason.woltje 15373266b1 queue: rev 865, row 97 round 1 changes by darkwing, comment 28083 (#1591); sweeps rocko's rev 864 (row 94 in-progress, op rocko-94-r2-start-20261012) 2026-10-12 18:58:55 -05:00
jason.woltjeandClaude Opus 5.5 6b0215aa15 logs, vikunja-setup: row 90 landed, follow-ups filed as Vikunja #245 (#1584)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:57:54 -05:00
jason.woltje e2fe22fc3a queue: row 90 done, landed e918a915 (#1584) 2026-10-12 18:56:58 -05:00
jason.woltjeandClaude Opus 5.5 e918a915a8 tasks, bus, harness, seat: the approved requirement ids as data (#1584)
Row 90, round 2. Dewey built it, Filbert approved it (comment 28068) on
manifest 95611274 (21 paths, base b8539480). The approved PRD's
requirement ids live in packages/tasks/requirements.json. task_create
refuses an id outside it, and the PM session gets a list_requirements
tool. Round 2 adds requirements.mjs to the sandbox's RUNNER_FILES
(decision 127). Sage gate on c047d028 plus the patch, 23:50:16Z to
23:54:26Z: tasks 89, bus 111, harness 66, webui 101, cli 123, seat 33,
business 60, control-board 124, queue 150, conversation 182, discord 178,
ledger 78, runs 43, mosaic 69, all fail 0. Every scripts/test-*.sh
exited 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:55:49 -05:00
jason.woltjeandClaude Opus 5.5 e4ce090716 sessions: dewey, row 94 round 1 changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:51:34 -05:00
jason.woltjeandClaude Opus 5.5 2f0b52fad0 dewey: row 94 round 1 review, changes (comment 28075)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:51:34 -05:00
jason.woltje 503d7ae923 queue: rev 862, row 94 round 1 changes by dewey, comment 28075 (#1588) 2026-10-12 18:51:26 -05:00
jason.woltje c047d02821 filbert: row 90 round 2 approve 28068, full gate and session 2026-10-12 18:48:19 -05:00
jason.woltjeandClaude Opus 5.5 cfc7fb1aa4 decisions, vikunja-setup: decision 128, recover() gap filed as Vikunja #244 (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:47:59 -05:00
jason.woltje d199c51f62 queue: rev 861, row 90 round 2 approve by filbert, comment 28068 (#1584) 2026-10-12 18:46:54 -05:00
jason.woltjeandClaude Opus 5.5 c9119bcfc9 sessions: darkwing, row 91 round 1 packaged (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:45:33 -05:00
jason.woltje 9d4decca97 queue: rev 860, row 91 in review round 1, candidate 39d6eb7d, request comment 28067 (#1585) 2026-10-12 18:45:22 -05:00
jason.woltjeandClaude Opus 5.5 f759432eaf darkwing: row 91 round 1 packet, base c974cafa, manifest 39d6eb7d (#1585)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:44:43 -05:00
jason.woltjeandClaude Opus 5.5 c974cafa4f logs, vikunja-setup: row 69 landed, follow-up filed as Vikunja #243 (#1562)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:40:27 -05:00
jason.woltjeandClaude Opus 5.5 7f0f54ab9d queue: rev 857, row 69 done, landed 90d7ed66 (#1562)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:39:53 -05:00
jason.woltje d538dcfad1 filbert: row 90 partial gate receipt, hold for actual row 91 slot clear 2026-10-12 18:38:56 -05:00
jason.woltjeandClaude Opus 5.5 90d7ed6628 cli: terminal app follow-up, narrow-terminal guard test, hidden-box way out and width tests (#1562)
Row 69, built by Darkwing, approved by Dewey (comment 28048) and Filbert
(comment 28055) on manifest c68e6582. Sage gate on c6711453: cli 123/0,
queue 150/0, every scripts suite green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:36:52 -05:00
jason.woltje 14c1484da4 filbert: row 90 round 2 static review, full gate pending reserved slot 2026-10-12 18:36:26 -05:00
jason.woltje c6711453b7 filbert: row 69 round 1 approve 28055, verification and session 2026-10-12 18:33:05 -05:00
jason.woltje 557707c19d queue: rev 856, row 69 round 1 approve by filbert, comment 28055 (#1562) 2026-10-12 18:32:32 -05:00
jason.woltjeandClaude Opus 5.5 1dd5e57b80 sessions: dewey, row 90 round 2 in review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:31:49 -05:00
jason.woltje ee00386ba1 queue: revs 853-855, row 90 round 2 in review, request comment 28054 (#1584) 2026-10-12 18:31:24 -05:00
jason.woltjeandClaude Opus 5.5 2f367900ea dewey: row 90 round 2 packet, 21 paths on b8539480, manifest 95611274
Round 1 rebased on row 63 (b8539480) plus one RUNNER_FILES line in
packages/seat/src/sandbox.mjs for row 92 (decision 127).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:30:26 -05:00
jason.woltjeandClaude Opus 5.5 0016283fd5 decisions: 127, row 90 round 2 adds row 92's sandbox file (#1584)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:28:27 -05:00
jason.woltje e608c233c0 filbert: rows 69 and 97 static reviews, verification pending test slot 2026-10-12 18:20:59 -05:00
jason.woltjeandClaude Opus 5.5 291638f839 sessions: dewey, row 69 round 1 approved
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:18:05 -05:00
jason.woltjeandClaude Opus 5.5 e9988d2677 queue: rev 852, row 69 round 1 approved by dewey, comment 28048 (#1562)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:17:58 -05:00
jason.woltjeandClaude Opus 5.5 e2ca558c3b queue: revs 847-851, row 98 round 1 in review, request comment 28047 (#1592)
Revs 847-849 are Researcher's (start, in-review move, failed tokenless
request op researcher-r98-review-20261012t231549z); 850-851 are my
request as jarvis.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:17:06 -05:00
jason.woltjeandClaude Opus 5.5 47292b571a dewey: row 69 round 1 review receipts, approve (#1562)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:16:28 -05:00
jason.woltjeandClaude Sonnet 5.5 00ef59c3c2 sessions: researcher, rows 97 and 98 in review
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 18:16:00 -05:00
jason.woltjeandClaude Sonnet 5.5 452ea3cade researcher: row 98 build packet (#1592)
Packet commit, agents/researcher/work/queue-98 only. Secrets scan clean. Not pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 18:15:44 -05:00
jason.woltjeandClaude Opus 5.5 c25afd2210 queue: revs 842-846, row 97 round 1 in review, request comment 28042 (#1591)
Revs 842-844 are Researcher's (start, in-review move, failed tokenless
request op researcher-r97-review-20261012t231305z); 845-846 are my
request as jarvis.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:14:07 -05:00
jason.woltjeandClaude Sonnet 5.5 81c2906c68 researcher: row 97 build packet (#1591)
Packet commit, agents/researcher/work/queue-97 only. Secrets scan clean. Not pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 18:12:57 -05:00
jason.woltjeandClaude Opus 5.5 db27ca49fd queue: revs 839-841, rows 97 and 98 briefs accepted, row 98 test placement note (#1591, #1592)
Only my revs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:12:38 -05:00
jason.woltjeandClaude Opus 5.5 6e1f93350b queue: rev 838, row 63 done, landed b8539480 (#1556)
Only my rev.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:10:43 -05:00
jason.woltjeandClaude Opus 5.5 2401a2fb2e logs: sage, row 63 landed, row 94 requested, row 91 re-pinned (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:10:13 -05:00
jason.woltjeandClaude Opus 5.5 b8539480aa tasks, harness: task verbs across the tree (#1556)
Row 63, round 2. Task verbs act in any installed project of the tree;
a project the tree doesn't hold refuses task-project as before, and a
watch-only one refuses with the new code. The harness task_create tool
takes an optional integer project. Round 2 adds Dewey's five proof
tests and Filbert's N1 child-path tests; the code is round 1's.

Candidate manifest 2a22fd3f (agents/darkwing/work/queue-63/r2/), base
cca5f23e, no drift on the six paths. Builder Darkwing. Approved by
Dewey (comment 28024) and Filbert (comment 28026). Sage's gate on
1117d19b plus the patch 2026-10-12T23:07:33Z-23:09:22Z: tasks 84,
harness 65, every scripts/test-*.sh green.

Lands before row 90 (decision 125). The harness change is live at the
next role session launch (decision 109); Mos had the notice.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:09:39 -05:00
jason.woltje 6bb92d04a3 filbert: row 63 round 2 approve 28026, clean verification and session 2026-10-12 18:08:30 -05:00
jason.woltje 6eef3e6a09 queue: rev 837, row 63 round 2 approve by filbert, comment 28026 (#1556) 2026-10-12 18:08:11 -05:00
jason.woltje 1117d19b47 logs: Rocko row 94 candidate verification and session (#1588) 2026-10-12 18:07:18 -05:00
jason.woltjeandClaude Opus 5.5 49e0f990b7 queue: revs 834-836, row 94 round 1 request comment 28025 (#1588), row 91 re-pin to 844832c3 (decision 126)
Only my revs. Correction: 16d5310b's subject says rev 830, but its
snapshot was rev 832 and carried Rocko's revs 830 (row 94 in-review) and
831 (the failed tokenless request) along with my rev 832.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:06:39 -05:00
jason.woltjeandClaude Opus 5.5 020ad78c2c docs: row 91 brief, host git treats the clone as hostile; decision 126 (#1585)
Darkwing found the brief had the host run unconfined git in a clone the
session writes. The brief now requires a clean git environment, hook and
fsmonitor overrides, an lstat walk of .git, a config-key allowlist and a
session-live check, and adds bundle.mjs and a harness test to the files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:06:02 -05:00
jason.woltjeandClaude Opus 5.5 646e3f77d7 sessions: dewey, row 63 round 2 approved (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:04:40 -05:00
jason.woltje 7ef5cf3ac3 queue: rev 833, row 63 round 2 approve by dewey, comment 28024 (#1556) 2026-10-12 18:04:29 -05:00
jason.woltjeandClaude Opus 5.5 f504fc7b56 dewey: row 63 round 2 review packet, approve, candidate 2a22fd3f (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:03:37 -05:00
jason.woltjeandClaude Opus 5.5 16d5310be0 queue: rev 830, row 69 note, short texts accepted, help pointer follow-up
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:02:56 -05:00
jason.woltjeandClaude Opus 5.5 e2fe658be5 sessions: darkwing, row 69 round 1 requested (#1562)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:01:14 -05:00
jason.woltje 3629c92609 queue: revs 827-829, row 69 round 1 in review, request 28023, packet 544563e3 (#1562) 2026-10-12 18:01:05 -05:00
jason.woltje 595d0a7a89 filbert: row 63 round 2 static review, verification pending test slot 2026-10-12 17:59:53 -05:00
jason.woltjeandClaude Opus 5.5 544563e3f5 agents: darkwing row 69 build packet (#1562)
Hidden decide box names the way out, its status line stops promising y,
tests for the narrow-terminal guard, U+18D08 and a cut help or detail
box. Candidate manifest c68e6582. Mutants 6/6 killed; gate 13 suites
green on dad20531 plus build.patch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:59:51 -05:00
jason.woltje 3bd3f85c70 filbert: row 90 round 1 review, approve 28021, probes and session 2026-10-12 17:56:44 -05:00
jason.woltje e6948d92f7 queue: rev 826, row 90 round 1 approve by filbert, comment 28021 (#1584) 2026-10-12 17:56:37 -05:00
jason.woltjeandClaude Sonnet 5.5 85aa676f32 researcher: I1 container survey report
Packet commit, one path. Secrets scan clean. Not pushed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 17:55:28 -05:00
jason.woltjeandClaude Opus 5.5 01618e930d lead decisions 124-125: rows 97 and 98 on Researcher's current model, row 63 lands before row 90
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:54:52 -05:00
jason.woltjeandClaude Opus 5.5 46833e450c queue: revs 822-825, rows 97 and 98 on Researcher's current model (decision 124), row 63 before row 90 (decision 125)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:54:52 -05:00
jason.woltjeandClaude Opus 5.5 a0005d6250 build-log, sessions: row 86 landed (#1580)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:52:10 -05:00
jason.woltjeandClaude Opus 5.5 15ed35130d queue: rev 821, row 86 done, landed ad0bdc20 (#1580)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:51:32 -05:00
jason.woltjeandClaude Opus 5.5 66e0c05e0f sessions: dewey, row 90 round 1 requested (#1584)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:48:55 -05:00
jason.woltje 829c00fbc0 queue: revs 819-820, row 90 round 1 in review, request 28013, packet 950e8403 (#1584) 2026-10-12 17:48:44 -05:00
jason.woltjeandClaude Opus 5.5 950e84030b dewey: row 90 round 1 packet, base 23338c7b, manifest 26d1fbdf (#1584)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:47:32 -05:00
jason.woltjeandClaude Opus 5.5 1e4cfa960a queue: revs 812-818, rows 97 and 98 to Researcher, Filbert reviews 97
Rev 812 is Rocko's: row 94 briefed to in-progress, on the Codex thread.
Revs 813-818 are Sage's. Rows 97 and 98 go to Researcher to build on
Haiku after row 86 lands (decision 122). Filbert joins row 97 as the GPT
reviewer, and its gate names both reviewers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:45:26 -05:00
jason.woltjeandClaude Opus 5.5 dad2053167 sessions: darkwing, row 63 round 2 requested (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:43:32 -05:00
jason.woltjeandClaude Opus 5.5 77f6cc581e queue: revs 809-811, row 63 round 2 in review, request 28005 (#1556)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:43:24 -05:00
jason.woltjeandClaude Opus 5.5 5b98eba0b5 agents: Rocko moves to Codex gpt-6.1-sol, thread d99326cc (decision 122)
Claude thread b84bb264 is retired. Rocko builds rows 94 and 85.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:42:39 -05:00
jason.woltjeandClaude Opus 5.5 7bdf6aecff queue: revs 807-808, rows 94 and 93 re-pinned after c7b07064
c7b07064 appended row 95's follow-up section to the brief. The sections
for rows 94 and 93 are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:41:55 -05:00
jason.woltjeandClaude Opus 5.5 557b28fb84 darkwing: row 63 round 2 packet, rebased onto cca5f23e, candidate 2a22fd3f (#1556)
Four child-path tests in verbs.test.mjs for Dewey's and Filbert's round 1
survivors; the task.create README paragraph rewrapped. Code unchanged.
Row 89 (ce104b27) edited tools.mjs and tools.test.mjs after round 1's
base, so this round is built on cca5f23e. Mutants 25 of 25 killed; gate
15 of 15 green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:41:25 -05:00
jason.woltjeandClaude Opus 5.5 dc2e753c49 build-log, sessions: row 77 landed, row 98 queued (#1571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:38:08 -05:00
jason.woltje 2f4b4ea2c2 queue: rev 806, row 98 queued, row 77 follow-up tests (#1592) 2026-10-12 17:37:37 -05:00
jason.woltjeandClaude Opus 5.5 df3896859e plans: row 77 follow-up brief, four refusal cases in the service tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:36:46 -05:00
jason.woltje 18c6436087 queue: rev 805, row 77 done, landed 0d10cf07 (#1571) 2026-10-12 17:36:02 -05:00
jason.woltjeandClaude Opus 5.5 0d10cf0798 webui: Console pinned copy, update step and service hardening (#1571)
Row 77, round 3 (round 2 rebased onto row 95). Rocko built it. This
covers row 75's N1-N5 and Vikunja 146 and 147. `install --copy ID`
refuses unless every ExecStart path of the bus unit, read from
`systemctl --user show` so drop-ins count, lies inside that copy. It
also refuses when the unit isn't loaded, no unit is loaded, ExecStart
has no path, or systemctl fails. COPY_PATHS gains packages/tasks/src and
packages/tasks/fields.json. Every copy directory is 0555. TOOLS.md and
the webui README give the removal command and the rollback floor.

Candidate manifest d9880f4c92d1d976027562a03cc5dd07512954e86f49f1da0ebb34e642a955f5
(10 paths, 8 applied by the patch, 2 packet files already committed),
base 2e26fbe1, packet agents/rocko/work/queue-77/r2b/. Approvals on round
3: Darkwing 27979 and Filbert 27985 (Filbert round 2: 27898). Sage's
gate rerun 22:32:51Z-22:35:12Z on 459c4fc6 plus the patch (row 92
included), manifest 10/10: webui 101, control-board 124, and every
scripts/test-*.sh exited 0 (test-release 4, Docker cases skipped at a
dead socket).

Live at the next Console install and restart (decision 109). Mos makes
that call.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:35:26 -05:00
jason.woltjeandClaude Opus 5.5 cca5f23eaf sessions: darkwing row 86 round 1 review, approve (#1580)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:33:00 -05:00
jason.woltjeandClaude Opus 5.5 459c4fc681 darkwing: row 86 round 1 review packet, approve (#1580)
Review notes and the rerun of S1, S2, S4, S8, S9 and S0 in a detached
worktree of ad0bdc20. Verdict comment 27986, rev 803 (swept in 85fd3286).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:32:48 -05:00
jason.woltjeandClaude Opus 5.5 d5bca87a1e build-log, sessions, agents: row 92 landed, Filbert on Codex (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:32:24 -05:00
jason.woltje 85fd3286e5 queue: rev 804, row 92 done, landed 57948a81 (#1586); sweeps darkwing rev 803, row 86 approve 27986 2026-10-12 17:31:42 -05:00
jason.woltjeandClaude Opus 5.5 57948a81f3 seat, cli, harness: role sessions confined to their own files (#1586)
Row 92, round 2. Darkwing built it. A role session runs in a bwrap
sandbox that sees only its own workspace, a private /proc, /dev and IPC
namespace, and the variables ENV_ALLOW passes. The launcher checks the
session credential before a Claude launch, records a runner killed from
the host as killed rather than failed, and the bus unit template carries
the credential file (decision 119). The harness gate and the cli, seat
and harness READMEs say what the sandbox does and does not hide.

Candidate manifest 8bf4141cbe69fc7bed188555bcc88dcab0b1ea2149912cb6f970ba8c2629dd97
(13 paths), base ac37cd17, packet agents/darkwing/work/queue-92/r2/.
Approvals: Dewey 27978, Filbert 27984 (round 1: Dewey 27942, Filbert
asked for changes in 27951). Sage's gate rerun 22:27:37Z-22:30:35Z on
1b7f3781 plus the patch, manifest 13/13: seat 33, cli 120, harness 65,
bus 108, webui 86, control-board 124, and every scripts/test-*.sh exited 0
(test-release 4, Docker cases skipped at a dead socket).

Live at the next bus restart and the next role session launch (decision
109). Installing the new unit and restarting is Mos's call.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:30:53 -05:00
jason.woltje 4835a8e282 sessions: filbert Codex onboarding, queue idle 2026-10-12 17:28:55 -05:00
jason.woltjeandClaude Opus 5.5 d7d94b686b filbert: handoff note update, rows 92 and 77 approved, row 95 landed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:28:40 -05:00
jason.woltjeandClaude Opus 5.5 1b7f37811a sessions: filbert row 77 (#1571) round 3 review on d9880f4c (base 2e26fbe1)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:27:32 -05:00
jason.woltjeandClaude Opus 5.5 560f640a84 queue: rev 802, row 77 round 3 approve by filbert, comment 27985 (#1571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:27:32 -05:00
jason.woltjeandClaude Opus 5.5 84f2d86246 sessions: filbert row 92 (#1586) round 2 review on 8bf4141c (base ac37cd17)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:25:55 -05:00
jason.woltjeandClaude Opus 5.5 2a930d9583 queue: rev 801, row 92 round 2 approve by filbert, comment 27984 (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:25:52 -05:00
jason.woltje ca00894728 queue: rev 800, row 93 note, runner.mjs overlap with row 90 2026-10-12 17:25:05 -05:00
jason.woltjeandClaude Opus 5.5 07aa14da52 sessions: darkwing row 77 round 3 review (#1571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:24:20 -05:00
jason.woltjeandClaude Opus 5.5 e748ec3b0c darkwing: row 77 round 3 review, approve (comment 27979, #1571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:24:13 -05:00
jason.woltjeandClaude Opus 5.5 3e43afe7c2 queue: row 77 round 3 approve by darkwing, comment 27979 (rev 799)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:24:06 -05:00
jason.woltjeandClaude Opus 5.5 23338c7b0e queue: row 90 in progress, dewey (rev 798)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:18:47 -05:00
jason.woltjeandClaude Opus 5.5 838304692f sessions: dewey row 92 round 2 review (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:17:25 -05:00
jason.woltjeandClaude Opus 5.5 730c615a5f queue: row 92 round 2 approve by dewey, comment 27978 (rev 797)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:17:19 -05:00
jason.woltjeandClaude Opus 5.5 bd3dbaf91d sessions: darkwing, row 92 round 2 requested (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:15:16 -05:00
jason.woltjeandClaude Opus 5.5 b695503b49 queue: revs 794-796, row 92 round 2 in review, request 27975 (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:14:36 -05:00
jason.woltjeandClaude Opus 5.5 a4fa775a51 darkwing: row 92 round 2 rebased onto ac37cd17, candidate 8bf4141c (#1586)
Row 89 landed on packages/harness/README.md. The patch applies cleanly;
gate 14/14 green and 34/34 mutants killed on the new base.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:13:47 -05:00
jason.woltjeandClaude Opus 5.5 e4ef608f7d sessions: correction, e13edda8 swept rocko's revs 790-791 unnamed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:07:47 -05:00
jason.woltje 1fb438228d queue: revs 792-793, row 77 round 3 review request as jarvis, comment 27973 (#1571) 2026-10-12 17:07:46 -05:00
jason.woltje e13edda828 queue: rev 789, row 77 back to in-progress for a round 3 on the README rebase (#1571) 2026-10-12 17:06:55 -05:00
jason.woltjeandClaude Opus 5.5 729a804b09 rocko: row 77 round 2 README rebase on 17ad6f35, candidate d9880f4c (#1571)
Rocko's packet, committed by Sage (Rocko has no credentials). Only
packages/webui/README.md moved, onto row 95's landing; Sage checked
the README's added and removed lines are identical to round 2's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:06:02 -05:00
jason.woltjeandClaude Opus 5.5 96a4a7033d build-log, sessions: row 89 landed (#1583)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:05:06 -05:00
jason.woltje ac37cd175e queue: rev 788, row 89 done, landed ce104b27 (#1583) 2026-10-12 17:04:46 -05:00
jason.woltjeandClaude Opus 5.5 ce104b27cc bus, harness: PM task reads that fit a session (#1583)
Row 89, round 1. The broker's `tasks` read takes optional filter and
page arguments and returns a short form. The harness adds `list_tasks`
arguments (open tasks by default), a `get_task` tool, and compact JSON
replies capped below the 50000-character threshold where Claude Code
2.1.296 saves a tool result to a file outside the workspace, which was
the PM's fault in Jason's test. The bundle's tools line names the
harness's own tools.

Candidate manifest b8c5bb3a (agents/dewey/work/queue-89/), base
a3f6e9e6, no drift on the 12 paths. Builder Dewey. Approved by
Darkwing (comment 27940). Sage's gate on f04185fc plus the patch
2026-10-12T22:00:55Z-22:03:38Z: bus 108, harness 65, webui 86, cli
115, control-board 124, every scripts/test-*.sh green.

Live at the next bus restart (broker) and role session launch
(harness), both Mos's call (decision 109); Mos had the notice.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:04:15 -05:00
jason.woltjeandClaude Opus 5.5 4643911ac1 darkwing: row 92 round 2 packet, candidate 8166bade (#1586)
Filbert's six changes and Dewey's N1-N3: five table rows, two launcher
tests, README sentences. 34/34 mutants killed, 14 suites green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 17:03:58 -05:00
jason.woltje f04185fcbd queue: rev 787, row 97 added, row 95 follow-up (#1591) 2026-10-12 17:00:24 -05:00
jason.woltjeandClaude Opus 5.5 c7b07064b9 plans: row 95 follow-up brief, Darkwing's N1-N3 (#1591)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:59:42 -05:00
jason.woltjeandClaude Opus 5.5 2e26fbe186 build-log, sessions: row 95 landed (#1589)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:58:12 -05:00
jason.woltje 03b76b827e queue: rev 786, row 95 done, landed 17ad6f35 (#1589) 2026-10-12 16:57:33 -05:00
jason.woltjeandClaude Opus 5.5 17ad6f35e2 webui: the Console's Board shows held roles, machine sessions behind a toggle, a signed-out line (#1589)
Row 95, round 1. The Board's first section lists each role a run holds
on the bus (from /api/bus/agents), with a Message button on a Console
started with --send. Machine sessions sit behind a "Show machine
sessions" checkbox, off by default, kept in localStorage; while off the
page never reads /api/board. Every Console fetch passes
redirect: 'manual'; the first opaqueredirect signs the page out: timers
stop, reads are dropped, one line says to reload, and the send form
keeps its draft.

Candidate manifest 246c0557 (agents/filbert/work/queue-95/), base
a3f6e9e6, no drift on the manifest paths. Builder Filbert. Approved by
Darkwing (comment 27953). Mutants 30/35 killed, 5 redundant guards.
Sage's gate 2026-10-12T21:52:13Z-21:54:35Z: webui 86/0, control-board
124/0, every scripts/test-*.sh green.

Live on landing: serve.mjs reads src/public on every request with
no-store (decision 123). Mos gave the go (sage-0115).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:56:43 -05:00
jason.woltjeandClaude Opus 5.5 ac4f617a45 decisions: 123, correction to 109, Console page files are live on landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:55:06 -05:00
jason.woltjeandClaude Opus 5.5 3eb1e98065 sessions: filbert Codex move handoff (decision 122)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:52:51 -05:00
jason.woltjeandClaude Opus 5.5 2624d1897c filbert: Codex move handoff note (decision 122)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:52:46 -05:00
jason.woltjeandClaude Opus 5.5 da7c4cb709 decisions: 122, Haiku for low-to-mid work, GPT for review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:51:20 -05:00
jason.woltjeandClaude Opus 5.5 ee22c2146b queue: decision 122 routing, rows 71 80 84 88 to researcher, notes (revs 774-785)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:51:20 -05:00
jason.woltjeandClaude Opus 5.5 1b6a989f95 sessions: darkwing row 95 review (#1589)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:48:59 -05:00
jason.woltjeandClaude Opus 5.5 10e285b3e2 queue: row 95 round 1 approve by darkwing, comment 27953 (rev 773)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:48:52 -05:00
jason.woltjeandClaude Opus 5.5 469d044bf1 sessions: filbert row 92 (#1586) round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:47:31 -05:00
jason.woltjeandClaude Opus 5.5 e93ddb676e queue: row 92 round 1 changes by filbert, comment 27951 (rev 772)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:47:18 -05:00
jason.woltjeandClaude Opus 5.5 8483a4ca8e sessions: dewey row 92 review (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:45:00 -05:00
jason.woltjeandClaude Opus 5.5 cbd24fc764 queue: row 92 round 1 approve by dewey, comment 27942 (rev 771)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:44:52 -05:00
jason.woltjeandClaude Opus 5.5 64777fc67d decisions: 121, Q129 Codex for about half of new coding work until 10-18
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:43:11 -05:00
jason.woltjeandClaude Opus 5.5 f4a6302522 queue: Q129 Codex routing, row 88 to rocko, notes on 88, 94, 96 (revs 767-770)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:43:11 -05:00
jason.woltjeandClaude Opus 5.5 5757b6e58c sessions: darkwing row 89 review (#1583)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:40:20 -05:00
jason.woltjeandClaude Opus 5.5 9392a48d74 queue: row 89 round 1 approve by darkwing, comment 27940 (rev 766)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:40:13 -05:00
jason.woltjeandClaude Opus 5.5 c76a92d8c2 sessions: darkwing row 92 round 1 build (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:35:56 -05:00
jason.woltje a7ed41f223 queue: row 92 round 1 review request as darkwing, comment 27936 (revs 764-765) 2026-10-12 16:35:47 -05:00
jason.woltjeandClaude Opus 5.5 4194f05673 darkwing: row 92 (#1586) session confinement packet, round 1, manifest bc132570
Build, patch and manifest for the bwrap sandbox, the credential check and
the unit's EnvironmentFile= line. Gate 14 suites rc=0; 26 mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:34:44 -05:00
jason.woltjeandClaude Opus 5.5 5fa65304b8 sessions: filbert row 95 build (#1589)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:32:41 -05:00
jason.woltjeandClaude Opus 5.5 61927a5abb queue: row 95 in-review round 1, request comment 27928 (revs 762-763)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:32:33 -05:00
jason.woltjeandClaude Opus 5.5 27eda72309 filbert: row 95 build packet (#1589), manifest 246c0557
Candidate on base a3f6e9e6, not committed here. Gate green 21:27:35Z-21:30:19Z;
mutants 30/35 killed, 5 equivalent guards explained in build.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 16:31:43 -05:00
jason.woltjeandClaude Opus 5.5 f9db19d7af sessions: dewey row 89 round 1 build (#1583)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:46:33 -05:00
jason.woltje 39c9ab186b queue: row 89 round 1 review request as dewey, comment 27902 (revs 760-761) 2026-10-12 15:46:08 -05:00
jason.woltjeandClaude Opus 5.5 4a2e18c90d dewey: row 89 (#1583) PM task reads packet, round 1, manifest b8c5bb3a
Candidate on base a3f6e9e6: tasks takes filters and paging, list_tasks and get_task,
a 40000-character reply cap with compact JSON, and the built-in tools line per harness.
Gate and 34 mutants in the packet.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:45:08 -05:00
jason.woltje 57a2d7aba2 queue: row 77 note, README overlap with row 95 (rev from sage-77-readme-overlap) 2026-10-12 15:38:26 -05:00
jason.woltjeandClaude Opus 5.5 84cd4c8774 sessions: filbert row 77 round 2 review (#1571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:37:12 -05:00
jason.woltjeandClaude Opus 5.5 736b773e98 queue: row 77 round 2 approve by filbert, comment 27898 (rev 758)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:35:28 -05:00
jason.woltje aad2ebf29c queue: row 87 note, human-proof research packet (rev from sage-87-human-proof) 2026-10-12 15:32:07 -05:00
jason.woltjeandClaude Opus 5.5 ec60937650 researcher: row 87 human-proof research packet, manifest 1ae3020d
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:30:42 -05:00
jason.woltje d4e8e05690 queue: row 96 added and briefed, CLIProxyAPI stand-in-key test (#1590, #237) (revs 755-756) 2026-10-12 15:22:32 -05:00
jason.woltjeandClaude Opus 5.5 718a0518a1 plans: decision 120 (Q127, #237 proxy, row 96)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:21:55 -05:00
jason.woltjeandClaude Opus 5.5 1e169c207b plans: brief for the CLIProxyAPI stand-in-key test (#237, Q127)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:21:20 -05:00
jason.woltje ada3df50e1 queue: row 92 note, Q126 answered (rev from sage-92-q126) 2026-10-12 15:16:28 -05:00
jason.woltjeandClaude Opus 5.5 b66bfe71dd plans: decision 119 (Q126, row 92 credential file, swappable)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:15:06 -05:00
jason.woltje 70b7b8d5ef queue: row 86 round 1 review request as jarvis, comment 27880 (revs 752-753); sweeps researcher's 750-751 (in-review, failed request) 2026-10-12 15:07:03 -05:00
jason.woltjeandClaude Opus 5.5 ad0bdc2095 researcher: row 86 (#1580) I1 spike packet, manifest 0381c233
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:05:20 -05:00
jason.woltjeandClaude Opus 5.5 bb909a6bf2 rocko: row 77 (#1571) packet, round 2; BUILD-LOG and SESSIONS appends
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:04:42 -05:00
jason.woltje 3268d5034d queue: row 77 round 2 review request as jarvis, comment 27875 (revs 748-749); sweeps rocko's 745-747 (rebase, in-review, failed request) 2026-10-12 15:04:18 -05:00
jason.woltjeandClaude Opus 5.5 a3f6e9e66c queue: row 95 started by filbert (rev 744); sweeps pending rev 743
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:58:56 -05:00
jason.woltjeandClaude Opus 5.5 6380e7ce69 sessions: sage Q125 rows, Console cause, Mos reply
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:57:52 -05:00
jason.woltjeandClaude Opus 5.5 40067b323f sessions: dewey correction, row 63 review line stamp
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:56:44 -05:00
jason.woltjeandClaude Opus 5.5 647d8295e9 sessions: dewey row 63 round 1 review, changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:56:35 -05:00
jason.woltjeandClaude Opus 5.5 6c45239197 sessions: filbert row 63 round 1 review (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:56:25 -05:00
jason.woltje af01465cb3 queue: rows 89, 90 and 92 brief blobs and notes for faults 10 and the session credential (revs 736-738, 741-742); also sweeps filbert's 739 and dewey's 740 (row 63 review) 2026-10-12 14:55:57 -05:00
jason.woltjeandClaude Opus 5.5 ca4585d792 plans: decision 118 (Q125 rows), row 92 adds the session credential line (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:54:14 -05:00
jason.woltjeandClaude Opus 5.5 7a3e85a38c dewey: row 63 (#1556) round 1 review packet, changes (test-only)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:53:35 -05:00
jason.woltjeandClaude Opus 5.5 4d095b773e plans: row 90 covers PM fault 10, asking for a stop with no task (#1584)
A non-blocking role.revoke routes to the human without a task; the
tool description and session text say so, and how a run is stopped.
The broker's blocking-needs-a-task rule stays (Mos sage-0107, #228
c91).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:53:20 -05:00
jason.woltje 6d0f3701fb queue: rows 93-95 added and briefed (#1587, #1588, #1589), 93 after 91 and 95, 85 after 94, row 89 brief blob and note for fault 9, row 92 note (revs 724-735) 2026-10-12 14:52:42 -05:00
jason.woltjeandClaude Opus 5.5 397e904772 plans: rows 93-95 from Jason's PM test (#1587, #1588, #1589), fault 9 into row 89
The review loop through the bus (findings faults 3 and 5), every
message to the human live (Mos sage-0103), and the Console Board
showing the stack's seats with a signed-out line (fault 8, Mos
sage-0105 and sage-0106). Row 89 also fixes the session text's
built-in tools line for Claude Code (fault 9).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:50:16 -05:00
jason.woltje 2f57a091a3 queue: row 69 on hold for row 92 (note), row 92 started by darkwing (revs 722-723) 2026-10-12 14:47:38 -05:00
jason.woltje c83cc10616 queue: row 69 started by darkwing (rev 721) 2026-10-12 14:46:01 -05:00
jason.woltjeandClaude Opus 5.5 e222acae92 sessions: darkwing row 63 build and review request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:44:53 -05:00
jason.woltje 6aebcef385 queue: row 92 confinement (#1586) added and briefed, row 91 after 92, brief blobs refreshed for rows 89-92 (revs 711-718) 2026-10-12 14:44:21 -05:00
jason.woltjeandClaude Opus 5.5 cca0d1aa1f plans: row 92 owner line, row 63's packet is in (#1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:43:26 -05:00
jason.woltjeandClaude Opus 5.5 e176bb538b darkwing: row 63 (#1556) packet, task verbs across the tree, manifest ec28d736
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:43:17 -05:00
jason.woltjeandClaude Opus 5.5 1f5689a378 plans: row 91 builds after row 92's confinement (#1585, #1586)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:42:25 -05:00
jason.woltjeandClaude Opus 5.5 cb2608717f plans: role session confinement brief (#1586, Vikunja P19 #232)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:41:56 -05:00
jason.woltje 452136a1e6 queue: rows 89 task reads (#1583), 90 requirement ids (#1584), 91 role checkout (#1585) added and briefed (revs 705-710) 2026-10-12 14:40:30 -05:00
jason.woltjeandClaude Opus 5.5 04db738a35 plans: PM task flow brief, task reads, requirement ids, role checkout (#1583, #1584, #1585)
Triage of Vikunja P19 #228 and its c78, the gaps Jason's first PM
request hit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:38:24 -05:00
jason.woltjeandClaude Opus 5.5 ef5d75e9ad build-log, sessions: row 81 landed (#1575)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:34:47 -05:00
jason.woltje 5849de76b8 queue: rev 704, row 81 done, landed c9a5650e (#1575) 2026-10-12 14:34:09 -05:00
jason.woltjeandClaude Opus 5.5 c9a5650ed0 bus, webui, cli: the Console shows the reply to a web send (#1575)
Row 81. The broker gains a `replies` read verb for human and reader
sessions: `{id}`, a message UUID, returns that business's messages whose
in_reply_to is the id, oldest first, at most 50, and writes no
deliveries row, so the reply still reaches the next `mosaic talk`. An
unknown id refuses message-not-found. The Console serves it through
BUS_VERBS with an id check, and the Sent receipt shows the replies to a
web send. The cli's talk timeout message and README now say the reply
may still come and the next `mosaic talk` prints it.

Dewey's round 2, candidate manifest 3ce7527f, base 1e5f5958, a rebase
after row 61. Approved by Darkwing (27783 round 1, 27821 round 2).
Sage's gate green 19:25:18Z to 19:30:39Z. Live at the next bus restart
(the broker verb) and Console restart (the view), bus first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:33:11 -05:00
jason.woltjeandClaude Opus 5.5 9438880e67 sessions: darkwing row 81 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:24:48 -05:00
jason.woltje 703558476d queue: row 81 round 2 approved by darkwing, candidate 3ce7527f, comment 27821 (rev 703) 2026-10-12 14:23:36 -05:00
jason.woltjeandClaude Opus 5.5 bb33b1c06d darkwing: row 81 (#1575) round 2 review, approve 3ce7527f (comment 27821)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:22:33 -05:00
jason.woltjeandClaude Opus 5.5 80d5b2a307 sessions: filbert row 77 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:21:19 -05:00
jason.woltjeandClaude Opus 5.5 79d8a6207a queue: row 77 round 1 review by filbert, changes, comment 27818, candidate 4927de66 (rev 702)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:21:10 -05:00
jason.woltjeandClaude Opus 5.5 1166e0def7 plans: lead decision 117, Q123, the PM stays PM and C-suite hiring goes to Mos
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:14:33 -05:00
jason.woltjeandClaude Opus 5.5 74085d6581 sessions: dewey row 81 round 2 request
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:14:15 -05:00
jason.woltje d0238966cb queue: row 81 in-review round 2, candidate 3ce7527f (dewey packet e244f4f5, rebased on 1e5f5958), request comment 27816 (revs 699-701) 2026-10-12 14:14:09 -05:00
jason.woltjeandClaude Opus 5.5 e244f4f50d dewey: row 81 (#1575) round 2 packet, rebased on 1e5f5958, manifest 3ce7527f
Row 61 landed on three shared webui paths; build.patch applied with --3way
clean. Folds in the cli README talk wording and the bus.mjs comment wrap.
Gate green 19:05:31-19:10:13Z; round 1 mutants 17/17 killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:12:52 -05:00
jason.woltje 08941d8b5b queue: row 88 row 61 follow-up (#1582) added and briefed (revs 697-698); sweeps darkwing's rev 695 (row 63 start) and researcher's rev 696 (row 86 start) 2026-10-12 14:10:48 -05:00
jason.woltjeandClaude Opus 5.5 52f3be82d0 plans: row 61 follow-up brief, Darkwing's eight items (#1582)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:09:34 -05:00
jason.woltjeandClaude Opus 5.5 956771d492 build-log, sessions: row 61 landed (#1550)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:08:29 -05:00
jason.woltje a8e9b10119 queue: rows 86 I1 spikes (#1580) and 87 I1 design (#1581) added and briefed (revs 691-694) 2026-10-12 14:08:20 -05:00
jason.woltjeandClaude Opus 5.5 46325d6f69 plans: I1 brief, spikes then design (#1580, #1581, Vikunja #153)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:07:20 -05:00
jason.woltjeandClaude Opus 5.5 140fe923b1 darkwing: row 61 follow-up item list (Dewey 1-2, Filbert N1-N5, fields.mjs header)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:07:17 -05:00
jason.woltjeandClaude Opus 5.5 c0d0308f41 sessions: darkwing row 77 review round 1, changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:06:07 -05:00
jason.woltje 6da8d38bb6 queue: row 77 round 1 darkwing changes (comment 27801) 2026-10-12 14:05:58 -05:00
jason.woltjeandClaude Opus 5.5 8b1f9793dc darkwing: row 77 (#1571) review round 1, changes (comment 27801)
Human proof from a copy (bus checks its own human-cli path), row 61's
packages/tasks import missing from COPY_PATHS, directory write bits.
webui 94 pass; 19 mutants, 16 killed, 3 survivors explained.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:05:02 -05:00
jason.woltje 1e5f595865 queue: rev 689, row 61 done, landed 6c42344d (#1550) 2026-10-12 14:03:44 -05:00
jason.woltjeandClaude Opus 5.5 6c42344d9d tasks, webui: the task field-writer table as data (#1550)
Row 61. packages/tasks/fields.json holds the slice 1 field table in two
layers (decision 113): each broker task verb's writer check, and each
field's writing verbs and Vikunja calls. packages/tasks/src/fields.mjs
checks the shape as code and loads it frozen. The tasks test reads the
writer layer from the table and authority from roles/*.json and drives
every task verb against the fake Vikunja. The Console serves the table
at GET /api/task-fields, and the task page shows a "Who may write"
section. The slice 1 doc's field table points to the file.

Darkwing's round 1, candidate manifest 33b96986, base 4f8ac1e7. Approved
by Dewey (27776) and Filbert (27785). Sage's gate green 18:57:31Z to
19:01:58Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:02:56 -05:00
jason.woltjeandClaude Opus 5.5 70ed77da34 S7 run log: PM running (Mos drop-in), both reachability checks answered
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:56:53 -05:00
jason.woltjeandClaude Opus 5.5 ca10ec972c queue: row 85 one-copy (#1579, #225) added and briefed (revs 687-688)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:56:42 -05:00
jason.woltjeandClaude Opus 5.5 d3573e2b52 plans: one-copy brief takes Mos's pm.conf drop-in, row 61 paths, #1579
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:55:53 -05:00
jason.woltjeandClaude Opus 5.5 a731e09b1c plans: one-copy brief for bus, Console and mosaic CLI (#225, Mos sage-0094/0095)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:54:11 -05:00
jason.woltjeandClaude Opus 5.5 1164b39233 sessions: filbert row 61 review round 1, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:54:02 -05:00
jason.woltjeandClaude Opus 5.5 65135ef7ec queue: row 61 round 1 approve by filbert, comment 27785 on #1550, manifest 33b96986 (rev 686)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:53:55 -05:00
jason.woltjeandClaude Opus 5.5 3bcc23f93d sessions: darkwing row 81 review, round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:52:30 -05:00
jason.woltje 36cc222aae queue: row 81 round 1 approve by darkwing, comment 27783 on #1575, manifest 2263ff5f 2026-10-12 13:52:10 -05:00
jason.woltjeandClaude Opus 5.5 3e797633c9 darkwing: row 81 (#1575) review, round 1, approve (comment 27783)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:51:09 -05:00
jason.woltjeandClaude Opus 5.5 3eaa0237f4 sessions: dewey row 61 review round 1, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:51:06 -05:00
jason.woltje 52a7e43d44 queue: row 61 round 1 approve by dewey, comment 27776 on #1550, manifest 33b96986 (rev 684) 2026-10-12 13:50:59 -05:00
jason.woltjeandClaude Opus 5.5 81663f550d S7 run log: 6b5b7aef reachability check, not a piece; no PM session running
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:49:45 -05:00
jason.woltjeandClaude Opus 5.5 f31d68a3a0 queue: row 84 brief re-pinned to da2a7b7e for decision 116 (rev 683); its note rev 682 went in darkwing's 8d7cda4a
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:48:31 -05:00
jason.woltjeandClaude Opus 5.5 41f9b08656 sessions: sage Q120, #225, row 77 round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:47:13 -05:00
jason.woltjeandClaude Opus 5.5 6dff1561a1 build-log: darkwing row 61 (#1550) candidate, round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:45:43 -05:00
jason.woltjeandClaude Opus 5.5 34aa2028b3 vikunja-setup: #225 comment 52, diagnosis and one-copy plan (Mos sage-0094)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:45:34 -05:00
jason.woltjeandClaude Opus 5.5 ed8819eec1 sessions: darkwing row 61 in review, round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:45:27 -05:00
jason.woltjeandClaude Opus 5.5 8d7cda4ac5 queue: row 61 in review, round 1, request comment 27772 on #1550
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:44:47 -05:00
jason.woltjeandClaude Opus 5.5 7ebf2715e0 decisions, M1 brief: 116 (Q120, the chief-of-staff instance is mos); brief names mos
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:43:47 -05:00
jason.woltjeandClaude Opus 5.5 f39023818f darkwing: row 61 (#1550) packet, round 1
The task field-writer table as data: build.patch (12 files on 4f8ac1e7),
candidate manifest, gate summary (all green), mutants 33/33 killed.
The candidate is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:43:39 -05:00
jason.woltjeandClaude Opus 5.5 4918d410f3 sessions: sage row 77 review request, #222 input
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:40:37 -05:00
jason.woltjeandClaude Opus 5.5 974a7e436f queue: row 77 review request round 1 as jarvis, comment 27767 (revs 678-679); carries rocko's revs 676-677 (in-review, failed request)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:40:36 -05:00
jason.woltjeandClaude Opus 5.5 436d581118 rocko: row 77 (#1571) packet, round 1; BUILD-LOG and SESSIONS appends
Rocko's work committed by Sage (Rocko has no git authority). Candidate
manifest sha256 4927de66, base 27a1a2bb.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:39:36 -05:00
jason.woltjeandClaude Opus 5.5 feb4978cf0 vikunja-setup: #222 comment 45, usage.db read-only input from ops-01's #221 plan (Mos sage-0093)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:39:14 -05:00
jason.woltjeandClaude Opus 5.5 75cacecf8a sessions: dewey row 81 in review, round 1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:38:52 -05:00
jason.woltje ed5d7d356b queue: row 81 in-review round 1, candidate 2263ff5f (dewey packet 5afa8b0f), request comment 27763 2026-10-12 13:37:44 -05:00
jason.woltjeandClaude Opus 5.5 5afa8b0fe1 dewey: row 81 (#1575) packet, round 1
Console shows the reply to a web send: bus `replies` read (human and
reader, message-not-found on unknown ids, oldest first, cap 50, writes
nothing), /api/bus/replies, the Sent receipt's reply list, talk's timeout
names only mosaic talk. Base ff11aa4b, 12 paths, manifest 12/12 on a fresh
worktree. Gate green 18:29-18:35Z; 17/17 mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:36:30 -05:00
jason.woltjeandClaude Opus 5.5 1d4ba6bb5e sessions: sage #162 round 1, S7 restart
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:33:55 -05:00
jason.woltjeandClaude Opus 5.5 c58854690f queue: row 42 S7 in progress on the Q118 channel (decision 115)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:32:30 -05:00
jason.woltjeandClaude Opus 5.5 681de77811 decisions, vikunja-setup, S7: 114 (#162 round 1, Q113-Q119), 115 (order around I1, S7 restart); P19 #219-#222; S7 run log
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:31:07 -05:00
jason.woltjeandClaude Opus 5.5 4f8ac1e7eb sessions: sage M1 brief, row 84, decision 113
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:18:51 -05:00
jason.woltjeandClaude Opus 5.5 73ef8ba7d9 queue: row 61 note cites decision 113 (field table section added to files owned)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:18:35 -05:00
jason.woltjeandClaude Opus 5.5 30adbd3eda decisions, vikunja-setup: 113 (row 61 field table follows the code, edited in the row); #154 comment; #218 close-verdict follow-up
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:17:52 -05:00
jason.woltjeandClaude Opus 5.5 95cb51c379 queue: row 84 M1 chief-of-staff role (Gitea #1578, decision 104), briefed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:16:20 -05:00
jason.woltjeandClaude Opus 5.5 ff11aa4bef sessions: dewey row 81 start
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:15:24 -05:00
jason.woltjeandClaude Opus 5.5 bbb9cc40e8 queue: row 81 in progress (dewey); sweeps sage's revs 667 (row 81 briefed) and 668 (row 80 briefed)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:15:20 -05:00
jason.woltjeandClaude Opus 5.5 ff61135e85 briefs: M1 chief-of-staff role file, contract and tests (decision 104, Vikunja 154)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:14:23 -05:00
jason.woltjeandClaude Opus 5.5 bf3190acdd queue: row 61 in progress (darkwing); sweeps rocko's row 77 start
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:14:11 -05:00
jason.woltjeandClaude Opus 5.5 27a1a2bb16 sessions, vikunja-setup: restarted rows filed as #207-#217, row 77 comments on #146 #147 #155
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:11:23 -05:00
jason.woltjeandClaude Opus 5.5 e65af8358f queue: row 77 re-briefed (decision 112); decision 94 holds lifted on rows 49, 61, 63-66, 69-71, 80, 81 (decision 111)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:10:31 -05:00
jason.woltjeandClaude Opus 5.5 274f6b3d78 decisions, briefs: 111 (Q111/Q112 no date gating), 112 and row 77's pinned-copy brief
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:06:17 -05:00
jason.woltjeandClaude Opus 5.5 6f09c5dd5d sessions, vikunja-setup: TARGET.md gap table filed as Vikunja #206
Mos sage-0088, fact-finding only. Filed with Sage's own key (user 13)
and read back with the sync key.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 13:00:47 -05:00
jason.woltjeandClaude Opus 5.5 c2c1a5431d sessions, vikunja-setup: #198 sage and ops-01 bot keys minted
Mos sage-0086: bot-mosaic-stack-sage is user 13 with key 13 and
bot-mosaic-stack-ops-01 is user 14 with key 14, both with key 11's
scopes (Q61a cut). Neither reads a project until ops-01 adds them to
team 1 under Mos's go.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:47:42 -05:00
jason.woltjeandClaude Opus 5.5 e22e0d73f3 sessions, vikunja-setup: #198 launchpad-pm bot key minted, team 1 add via ops-01
mint-bot.mjs generalizes mint-mos.mjs (user, name and key title as
arguments; slot written without a trailing newline). bot-launchpad-pm is
user 12, key 12, scopes identical to key 11 (Q61a cut). The key reads no
project until ops-01 adds user 12 to team 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:46:46 -05:00
jason.woltjeandClaude Opus 5.5 c8a4d46341 sessions, vikunja-setup: #151 Mos bot key minted, team 1 add is Jason's
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:42:50 -05:00
jason.woltjeandClaude Opus 5.5 c444c3ae3f decisions, build-log, sessions: 110, Q109 follow-ups filed in Vikunja P19 (#153-#161)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:38:43 -05:00
jason.woltjeandClaude Opus 5.5 51711d60ad decisions, build-log, sessions: cutover live, 109 landings are live Console changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:27:49 -05:00
jason.woltjeandClaude Opus 5.5 6040d42449 sessions: sage recorded decision 108
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 10:00:59 -05:00
jason.woltjeandClaude Opus 5.5 7c3a8ad841 decisions: 108, Q105 A, subscription token as a read-only file secret (I1-4)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 10:00:59 -05:00
jason.woltjeandClaude Opus 5.5 de64f01c39 sessions: sage recorded decision 107
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:59:20 -05:00
jason.woltjeandClaude Opus 5.5 f9649043dc decisions: 107, Q104 deferred, no WebAuthn in the I1 brief (I1-3)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:59:20 -05:00
jason.woltjeandClaude Opus 5.5 01fe4f8e19 sessions: sage recorded decision 106
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:46:53 -05:00
jason.woltjeandClaude Opus 5.5 be517456a9 decisions: 106, Q103 agreed, rootless daemon beside the rootful one (I1-2)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:46:53 -05:00
jason.woltjeandClaude Opus 5.5 f1cb77cd8c sessions: sage recorded decision 105
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:28:38 -05:00
jason.woltjeandClaude Opus 5.5 ec6befd213 decisions: 105, Q102 agreed, containerize by role (I1-1)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:28:31 -05:00
jason.woltjeandClaude Opus 5.5 6c5f9971d4 sessions: sage recorded decision 104
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:25:35 -05:00
jason.woltjeandClaude Opus 5.5 334bd3eea4 decisions: 104, Q101 agreed, the M1 role file as drafted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:25:27 -05:00
jason.woltjeandClaude Opus 5.5 9a81664396 sessions, sage: I1 survey review, cutover ready sent
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 08:18:52 -05:00
jason.woltjeandClaude Opus 5.5 36e34e5bc9 sessions: sage installed the preview Console unit with --send
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:14:39 -05:00
jason.woltjeandClaude Opus 5.5 777ada5929 decisions: 103, Mos runs the cutover reinstall for mosaic.woltje.com
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:14:04 -05:00
jason.woltjeandClaude Opus 5.5 bf6a93aa4d build-log, sessions: row 83 landed (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:12:27 -05:00
jason.woltje 7a4ca67b43 queue: rev 635, row 83 done, landed 1a8ba587 (#1576) 2026-10-11 23:11:52 -05:00
jason.woltjeandClaude Opus 5.5 1a8ba58737 webui: the unit script takes --send (#1576)
Row 83. scripts/webui-service.sh render and install take --send, only
with the full preview set; otherwise the script refuses before writing
anything. With --send the rendered ExecStart ends in --send; renders
without it are byte-identical to the base. status says whether the
installed unit sends. The script header, install message, README and
TOOLS.md carry the decision 102 start wording: Jason or Mos starts and
restarts the Console, no other agent.

Dewey's round 1, candidate manifest f764cd9e, base d13b9300. Approved
by Darkwing (27721) and Filbert (27722). Sage's gate green 04:07:30Z to
04:10:47Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:10:55 -05:00
jason.woltjeandClaude Opus 5.5 23d3841354 sessions: filbert row 83 round 1 approved (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:06:07 -05:00
jason.woltjeandClaude Opus 5.5 41aee177d9 queue: rev 634, row 83 round 1 approve by filbert, comment 27722 (#1576) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:06:01 -05:00
jason.woltjeandClaude Opus 5.5 d7d416170c sessions: darkwing row 83 round 1 approved (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:05:23 -05:00
jason.woltjeandClaude Opus 5.5 3094623abe darkwing: row 83 round 1 review packet, approve, comment 27721 (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:05:16 -05:00
jason.woltjeandClaude Opus 5.5 cc03711e8e queue: rev 633, row 83 round 1 approve by darkwing, comment 27721 (#1576) (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:05:13 -05:00
jason.woltjeandClaude Opus 5.5 15dcac129a sessions: dewey row 83 round 1 in review (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:00:15 -05:00
jason.woltje 2807a78b50 queue: revs 631-632, row 83 in review round 1 on #1576 (dewey) 2026-10-11 23:00:00 -05:00
jason.woltjeandClaude Opus 5.5 e1bf582a1a dewey: row 83 round 1 packet, unit script takes --send (#1576)
build.patch against d13b9300 (brief blob f8d14d70, rev 630), candidate
manifest, identity and mutant harnesses with results, gate summary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:59:05 -05:00
jason.woltjeandClaude Opus 5.5 a1c5c8fcc1 sessions: sage correction, rev 629 was dewey's own commit (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:48:53 -05:00
jason.woltjeandClaude Opus 5.5 871f312c0f sessions: sage decision 102 and row 83 re-pin (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:48:42 -05:00
jason.woltje 50ac63d68a queue: revs 629-630, row 83 claimed by dewey (629) and brief re-pinned to f8d14d70 for decision 102 (630) (sage) 2026-10-11 22:48:42 -05:00
jason.woltjeandClaude Opus 5.5 1d117112f0 plans: decision 102 (Q99, Mos starts the Console unit); row 83 brief carries the start wording (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:47:57 -05:00
jason.woltje 53b51ebbae queue: rev 629, row 83 in progress (#1576) (dewey) 2026-10-11 22:42:05 -05:00
jason.woltje ba5875cba5 queue: rev 628, row 83 briefed (#1576) (sage) 2026-10-11 22:41:02 -05:00
jason.woltjeandClaude Opus 5.5 d13b9300fd sessions: sage decision 101 and row 83 briefing (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:37:25 -05:00
jason.woltje dafe590d5d queue: rev 627, row 83 added and briefed for #1576, owner dewey (sage) 2026-10-11 22:37:21 -05:00
jason.woltjeandClaude Opus 5.5 6cbaef26be plans: decision 101 and row 83 brief, the unit script takes --send (#1576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:36:35 -05:00
jason.woltjeandClaude Opus 5.5 0c149ce421 build-log: row 76 landed (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:31:56 -05:00
jason.woltje c60839b210 queue: row 76 done (landed c45b948d, #1570) (sage) 2026-10-11 22:29:56 -05:00
jason.woltjeandClaude Opus 5.5 c45b948ded webui: the Console sends a message to a role (#1570)
Row 76 round 2, owner Dewey, candidate manifest 04ecf1b5 on 729da72b.
Carries decisions 99 and 100: Darkwing's and Filbert's round 1 notes,
the README proxy contract line, and a 408 counted as unknown.
Approved by Filbert (27685) and Darkwing (27688); Sage's gate green
03:25-03:29Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:29:25 -05:00
jason.woltjeandClaude Opus 5.5 be6a913b86 sessions: darkwing row 76 round 2 approved (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:25:00 -05:00
jason.woltjeandClaude Opus 5.5 02e73ddef8 darkwing: row 76 round 2 review packet, approve, comment 27688 (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:24:51 -05:00
jason.woltjeandClaude Opus 5.5 5a67dc5e0f queue: rev 625, row 76 round 2 approve by darkwing, comment 27688 (#1570) (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:24:47 -05:00
jason.woltjeandClaude Opus 5.5 484a17808e sessions: filbert row 76 round 2 approved (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:21:50 -05:00
jason.woltjeandClaude Opus 5.5 f808f3c48d queue: rev 624, row 76 round 2 approve by filbert, comment 27685 (#1570) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:21:44 -05:00
jason.woltjeandClaude Opus 5.5 2bbc3097c3 sessions: dewey row 76 round 2 in review (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:40:23 -05:00
jason.woltje 18f22406dd queue: revs 621-623, row 76 round 2 in review, request comment 27672 (#1570) (dewey) 2026-10-11 21:40:13 -05:00
jason.woltjeandClaude Opus 5.5 86821889ed dewey: row 76 round 2 packet, rebase on 729da72b and decisions 99-100 (#1570)
Rebased on row 59 (a8512007). Darkwing's N1-N5 and the Q1 proxy line
(lead decision 99), Filbert's N1-N4 (lead decision 100): a 408 is unknown
on the send box and the board Reply, the receipt shows the code once, focus
and caret kept, a role no longer held kept, Inbox current on a message
trail, README notes. 35 of 35 mutants killed; gate green on 729da72b.
Manifest sha256 04ecf1b57c594a35d34d53277b2df915a571f4fa9f7cc29146dbedfa612f77d4.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:38:09 -05:00
jason.woltjeandClaude Opus 5.5 534fe53e3a build-log, sessions: sage row 82 landed (#1551)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:14:49 -05:00
jason.woltje e16934eff5 queue: row 82 done (landed 0a9692ae, #1551) (sage) 2026-10-11 21:14:34 -05:00
jason.woltjeandClaude Opus 5.5 0a9692aebd runs: list survives a damaged run record (#1551)
Row 82 round 2, owner Rocko, candidate manifest dbf36e92 on 876f1ffe.
Approved by Filbert (27650); Sage's gate green 02:05-02:09Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:13:56 -05:00
jason.woltjeandClaude Opus 5.5 0e6c944bd9 build-log, sessions: sage row 67 landed (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:04:58 -05:00
jason.woltje 01727ccbd3 queue: row 67 done (landed 189410ed, #1560) (sage) 2026-10-11 21:04:28 -05:00
jason.woltjeandClaude Opus 5.5 189410ed09 bus: decisions read follow-up tests, seen-then-resolve, list-before-503 and full rows (#1560)
Row 67 round 2 (rebase only), owner Filbert, candidate manifest 4387c8d5 on f38b7f46.
Approved by Dewey (27648) and Darkwing (27649); round 1 by Dewey (27452) and
Darkwing (27627). Sage's gate green 01:58-02:03Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:03:37 -05:00
jason.woltjeandClaude Opus 5.5 d7922dfc96 sessions: filbert row 82 round 2 review, approve (#1551)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 21:00:04 -05:00
jason.woltje 10599ff336 queue: rev 618, row 82 round 2 approve by filbert, comment 27650 (#1551) (filbert) 2026-10-11 20:59:48 -05:00
jason.woltjeandClaude Opus 5.5 5cac6d1634 sessions: darkwing row 67 round 2 review (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:58:36 -05:00
jason.woltjeandClaude Opus 5.5 820965df3a sessions: dewey row 67 round 2 re-approval (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:58:07 -05:00
jason.woltje d35483e3cb queue: revs 616-617, row 67 round 2 approvals by dewey (comment 27648) and darkwing (#1560) (dewey; carries darkwing's rev 617) 2026-10-11 20:57:54 -05:00
jason.woltjeandClaude Opus 5.5 9391f338d6 review(darkwing): row 67 round 2 review packet, approve (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:56:39 -05:00
jason.woltjeandClaude Opus 5.5 c716cb7f7e sessions: filbert row 67 round 2 request (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:54:51 -05:00
jason.woltje baa3a5e887 queue: revs 613-615, row 67 round 2 in review, request comment 27644 (#1560) (filbert) 2026-10-11 20:54:41 -05:00
jason.woltjeandClaude Opus 5.5 7d032da5b4 filbert: row 67 round 2 packet, rebase onto f38b7f46 (#1560)
Manifest 4387c8d5. Rebase only: same added and removed lines, three files
re-digested after rows 58, 60 and 59. Gate green, round 1 mutants all killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:53:36 -05:00
jason.woltjeandClaude Opus 5.5 a5e8cd1be5 sessions: sage decision 100 (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:48:34 -05:00
jason.woltjeandClaude Opus 5.5 fff031c4a8 decisions: 100, row 76 round 2 also carries Filbert's round 1 notes (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:48:10 -05:00
jason.woltjeandClaude Opus 5.5 76631edd7d sessions: filbert row 76 round 1 review, approve (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:47:34 -05:00
jason.woltjeandClaude Opus 5.5 1e2c793410 queue: rev 612, row 76 round 1 approve by filbert, comment 27637 (#1570) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:47:26 -05:00
jason.woltjeandClaude Opus 5.5 672bf127e5 sessions: rocko row 82 round 2 candidate, sage review request (#1551)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:44:01 -05:00
jason.woltje d1e364e3e4 queue: revs 607-611, row 82 round 2 in review (rocko), request posted as jarvis, comment 27634 (sage) 2026-10-11 20:43:55 -05:00
jason.woltjeandClaude Opus 5.5 7e6f25e4de packet(rocko): row 82 round 2 candidate dbf36e92, packet manifest 2afecca7, BUILD-LOG entry (#1551)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:43:09 -05:00
jason.woltjeandClaude Opus 5.5 16a847da1f sessions: sage row 82 brief amendment (#1551)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:42:02 -05:00
jason.woltje 455df62944 queue: revs 604-606, row 82 round 1 in review and request failure (rocko), brief re-pinned to fb9da977 (sage) 2026-10-11 20:40:22 -05:00
jason.woltjeandClaude Opus 5.5 09a8e0eee0 brief: row 82 (#1551) owns the README paragraph above the damaged-records table
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:39:20 -05:00
jason.woltjeandClaude Opus 5.5 f38b7f4660 sessions: darkwing row 67 round 1 review (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:31:55 -05:00
jason.woltje f4cd02a9d6 queue: revs 602-603, row 67 round 1 approve by darkwing, comment 27627 (#1560); includes rocko's rev 602, row 82 start (rocko) 2026-10-11 20:31:48 -05:00
jason.woltjeandClaude Opus 5.5 4a00aaca1b review(darkwing): row 67 round 1 review packet, approve (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:30:54 -05:00
jason.woltjeandClaude Opus 5.5 876f1ffe09 sessions: sage decision 99 and row 82 briefing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:27:58 -05:00
jason.woltje 9cdb17bc38 queue: revs 600-601, row 82 added and briefed for #1551, owner rocko (sage) 2026-10-11 20:27:49 -05:00
jason.woltjeandClaude Opus 5.5 e7511da32d lead decision 99 (row 76 round 2 carries Darkwing's notes); brief for #1551 runs list
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:26:53 -05:00
jason.woltjeandClaude Opus 5.5 c314b8ee45 sessions: darkwing row 76 round 1 review (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:25:49 -05:00
jason.woltjeandClaude Opus 5.5 a949b3b9b7 build-log: correct the row 79 correction comment id (#1573)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:25:09 -05:00
jason.woltjeandClaude Opus 5.5 3b753a0cc0 build-log, sessions: sage row 79 landed (#1573)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:25:01 -05:00
jason.woltje c9ff939bf7 queue: row 79 done (landed 3780e05d, #1573) (sage) 2026-10-11 20:24:44 -05:00
jason.woltjeandClaude Opus 5.5 3780e05df7 roles: drop the coder's stale task.reassign text, pin the untyped list (#1573)
Row 79 round 1, owner Rocko, candidate manifest 16b12387 on 331d037c.
Approved by Dewey (27611); Sage's gate green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:23:35 -05:00
jason.woltjeandClaude Opus 5.5 29124395f4 review(darkwing): row 76 round 1 review packet, approve (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:23:26 -05:00
jason.woltjeandClaude Opus 5.5 be1f5f666f sessions: dewey row 79 round 1 review, approve (#1573)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:17:51 -05:00
jason.woltje c4183bcf78 queue: rev 597, row 79 round 1 approve by dewey, comment 27611 (#1573) (dewey) 2026-10-11 20:17:39 -05:00
jason.woltjeandClaude Opus 5.5 729da72bd3 build-log, sessions: sage row 59 landed (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:14:42 -05:00
jason.woltje b9bfa0de6a queue: row 59 done (landed a8512007, #1548) (sage) 2026-10-11 20:14:24 -05:00
jason.woltjeandClaude Opus 5.5 a85120070b bus: agent state per role, and the Console shows it (#1548)
Row 59 round 2, owner Filbert, candidate manifest 9acfac19 on 3d298920.
Approved by Darkwing (27591) and Dewey (27595); Sage's gate green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:13:43 -05:00
jason.woltjeandClaude Opus 5.5 cb8ca8574f sessions: dewey row 59 round 2 review, approve (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:13:30 -05:00
jason.woltje 654a5e4e39 queue: rev 595, row 59 round 2 approve by dewey, comment 27595 (#1548) (dewey) 2026-10-11 20:13:24 -05:00
jason.woltjeandClaude Opus 5.5 1ab6e181e5 sessions: dewey row 76 round 1 request (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:11:37 -05:00
jason.woltje f1b8b36d14 queue: revs 593-594, row 76 round 1 in review on #1570, request comment 27594, manifest ca870314 (dewey) 2026-10-11 20:11:27 -05:00
jason.woltjeandClaude Opus 5.5 1deb614344 review(dewey): row 76 round 1 packet, Console sends a human message to a role, on 3d298920 (#1570)
Built on bbd22f1b, rebased on row 60 (3d298920): the bus.mjs header
comment conflict merged, serve.mjs BUS_VERBS and the README credentials
paragraphs merged cleanly. 21 of 21 mutants killed, full gate green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 20:10:35 -05:00
jason.woltjeandClaude Opus 5.5 146043eb53 sessions: darkwing row 59 round 2 review, approve (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:53:31 -05:00
jason.woltje 54ebf3a8aa queue: row 59 round 2 approve by darkwing, comment 27591 (darkwing) 2026-10-11 19:53:24 -05:00
jason.woltjeandClaude Opus 5.5 8c704ddde1 review(darkwing): row 59 round 2, approve (#1548)
Rebase-only round on 3d298920. Merged hunks against rows 58 and 60 keep
both sides; 21 round 1 mutants give round 1's results. Comment 27591.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:52:53 -05:00
jason.woltjeandClaude Opus 5.5 f3c8b2e248 sessions: filbert row 59 round 2 request (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:48:43 -05:00
jason.woltjeandClaude Opus 5.5 34c5c289f3 queue: revs 589-591, row 59 round 2 in review on #1548, request comment 27586, manifest 9acfac19 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:47:31 -05:00
jason.woltjeandClaude Opus 5.5 93fc00e4a9 review(filbert): row 59 round 2 packet, rebased on 3d298920 after rows 74, 58 and 60 (#1548)
Rebase only, per rev 571. Two conflicts resolved: the agents branch in
broker.mjs next to row 58's tasks line, and the bus.css widths line.
Merged hunks in broker.mjs and the bus README are named in build.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:46:40 -05:00
jason.woltjeandClaude Opus 5.5 3d298920bd build-log, sessions: sage row 60 landed (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:37:54 -05:00
jason.woltjeandClaude Opus 5.5 80640cb59c queue: rev 588, row 60 done (landed 1771bf98, #1549) (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:37:46 -05:00
jason.woltjeandClaude Opus 5.5 1771bf98b2 feat(bus,webui): bus read of credential status; the Console shows it (#1549, row 60)
The credentials read returns service, account, role, date kind, date and
state for the capability's business, to human and reader sessions only.
It never returns a token, file path or variable name. Author Filbert,
reviewers Dewey (27558) and Darkwing (27575), candidate manifest 13541657.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:37:06 -05:00
jason.woltjeandClaude Opus 5.5 6a0d43854a sessions: darkwing row 60 round 2 re-review, approve (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:36:47 -05:00
jason.woltje 9eaad27e81 queue: rev 587, row 60 round 2 approve, comment 27575 (#1549) (darkwing) 2026-10-11 19:36:40 -05:00
jason.woltjeandClaude Opus 5.5 a030b895fc darkwing: row 60 round 2 review, approve, comment 27575 (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:36:08 -05:00
jason.woltjeandClaude Opus 5.5 74e6be7067 build-log, sessions: sage row 78 landed, rows 78 and 79 requests, decision 98 (#1572, #1573, #1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:25:29 -05:00
jason.woltjeandClaude Opus 5.5 8c87cd30a6 queue: rev 586, row 78 done (landed eacfd608, #1572) (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:25:15 -05:00
jason.woltjeandClaude Opus 5.5 eacfd6084a test(cli): the default Claude pin goes through the exec and character checks (#1572, row 78)
Two default-pin cases (no --claude): the only claude on PATH is mode 0644,
and claude in a PATH dir with a space. render exits 1 with stdout empty
for each. PATH holds only the test's dirs. Author Rocko, reviewer Filbert
(27560), candidate manifest 3016dbf1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:24:32 -05:00
jason.woltjeandClaude Opus 5.5 e379cca4ec sessions: filbert row 78 round 1 review (#1572)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:21:08 -05:00
jason.woltjeandClaude Opus 5.5 1a71869479 queue: revs 581-585, row 78 round 1 approved by filbert, comment 27560 (#1572)
Rev 583 is mine (filbert-78-review-1). The snapshot also carries
Rocko's 581-582 (row 79 in review) and Sage's 584-585 (row 79 review
request), which were pending in the working file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:20:46 -05:00
jason.woltjeandClaude Opus 5.5 feabde9574 sessions: dewey row 60 round 2 re-review (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:19:41 -05:00
jason.woltjeandClaude Opus 5.5 1735f27d37 rocko: row 79 round 1 packet, BUILD-LOG and SESSIONS (#1573)
Packet agents/rocko/work/queue-79/ (packet manifest b6b11e7b, 42 files,
candidate manifest 16b12387), Rocko's BUILD-LOG and SESSIONS appends.
Committed by Sage for Rocko, who has no credential.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:19:14 -05:00
jason.woltjeandClaude Opus 5.5 1392fb23c8 queue: rev 580, row 60 round 2 approve by dewey, comment 27558 (#1549) (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:19:06 -05:00
jason.woltjeandClaude Opus 5.5 179129c5f4 lead decisions: 98, the preview serves the credentials read as built (#1549, Dewey N2)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:15:29 -05:00
jason.woltjeandClaude Opus 5.5 a8032d6a0e sessions: filbert row 60 round 2 request (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:13:54 -05:00
jason.woltjeandClaude Opus 5.5 0be618892a queue: revs 577-579, row 60 round 2 in review on #1549, request comment 27553, manifest 13541657 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:13:46 -05:00
jason.woltjeandClaude Opus 5.5 5fc7635cd8 review(filbert): row 60 round 2 packet, rebased on bbd22f1b, decisions row and Dewey's N1 (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:12:07 -05:00
jason.woltjeandClaude Opus 5.5 bd4dff33ed queue: revs 572-576, row 78 in review (rocko 572-573), row 79 in progress (rocko 574), row 78 request posted as jarvis, comment 27549 (sage 575-576)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:10:57 -05:00
jason.woltjeandClaude Opus 5.5 2ece44919e rocko: row 78 round 1 packet, BUILD-LOG and SESSIONS (#1572)
Packet agents/rocko/work/queue-78/ (packet manifest 9e067f40, 32 files,
candidate manifest 3016dbf1), Rocko's BUILD-LOG and SESSIONS appends.
Committed by Sage for Rocko, who has no credential.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:10:16 -05:00
jason.woltjeandClaude Opus 5.5 331d037cec queue: revs 570-571, row 78 in progress (rocko, rev 570), row 59 re-round order after row 60 (sage, rev 571)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:06:18 -05:00
jason.woltjeandClaude Opus 5.5 bbd22f1b09 build-log, sessions: sage row 58 landed, rows 78-79 briefed and re-pinned (#1547, #1572, #1573)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:03:28 -05:00
jason.woltje c02d9f432e queue: revs 566-569, row 58 done (landed aaf87213, #1547), rows 78 and 79 re-pinned to 4784ee65, row 60 re-round scope note (sage) 2026-10-11 19:03:12 -05:00
jason.woltjeandClaude Opus 5.5 aaf8721395 feat(bus,webui): bus read carries task priority and requirement; the Console shows them (#1547, row 58)
The bus tasks read returns each task's priority and requirement, and the
Console's task list shows them. Files: packages/bus/src/broker.mjs and
README, packages/bus/tests/task-read.test.mjs, packages/webui/README.md,
src/public/bus.css and bus.js, tests/bus.test.mjs and
bus-browser.test.mjs.

Author: Darkwing (packet agents/darkwing/work/queue-58/, round 2 in
48b0b648, rebased on row 74's tree). Candidate manifest sha256 eb2b26c1,
base 424ada49, no drift on the 8 paths through eace3fef.
Approvals, round 2: Filbert comment 27529, Dewey comment 27530 on #1547.
Gate (Sage, /tmp/sage-g58 on eace3fef, 23:57-00:01Z): bus 84, tasks 71,
webui 61, control-board 124, cli 113 node tests, 0 failed; every
scripts/test-*.sh exit 0, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:01:34 -05:00
jason.woltjeandClaude Opus 5.5 082678b8c7 sessions: darkwing row 60 round 1 review, approve (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:58:10 -05:00
jason.woltjeandClaude Opus 5.5 f2b963b816 sessions: dewey row 58 round 2 re-review, approve (#1547)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:58:05 -05:00
jason.woltje 6b5a4fbeb6 queue: rev 565, row 60 round 1 approve, comment 27531 (#1549) (darkwing) 2026-10-11 18:58:01 -05:00
jason.woltjeandClaude Opus 5.5 eace3fefec darkwing: row 60 round 1 review packet, approve, comment 27531 (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:57:16 -05:00
jason.woltje 2007632414 queue: revs 562-563, rows 78 and 79 briefed (#1572, #1573) (sage); rev 561 is filbert's row 58 round 2 receipt (comment 27529) 2026-10-11 18:57:01 -05:00
jason.woltjeandClaude Opus 5.5 6c6f7fb8b2 sessions: filbert row 58 round 2 review, approve (#1547)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:56:24 -05:00
jason.woltjeandClaude Opus 5.5 e525fedcde queue: rev 561, row 58 round 2 approve by filbert, comment 27529 (#1547) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:56:17 -05:00
jason.woltjeandClaude Opus 5.5 407628adbc build-log, sessions: sage row 73 landed, decision 97, queue subject correction (#1567, #1575)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:55:08 -05:00
jason.woltje db6b7691c5 queue: revs 558-560, row 81 held behind the cutover, reason restored at 560 (decision 97); rev 557 (row 81 added) rode in darkwing's bd9330e5 (sage) 2026-10-11 18:54:48 -05:00
jason.woltjeandClaude Opus 5.5 c7d879792b sessions: darkwing row 58 round 2 candidate and review request (#1547)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:53:37 -05:00
jason.woltje bd9330e545 queue: revs 553-556, row 73 done (sage, 553); row 58 round 2 on row 74's tree, request comment 27511 (#1547) (darkwing) 2026-10-11 18:53:15 -05:00
jason.woltjeandClaude Opus 5.5 e16fb57849 plans: lead decision 97 and the Console reply-view brief (#1570, #1575)
Row 76 builds as briefed; its brief's 'replies show in the inbox' line
was wrong. A read-only bus read of replies, shown on the Console receipt,
is row 81 (#1575), held behind the cutover. Option (b), a Console
message.receive, is refused because receive marks delivery.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:52:25 -05:00
jason.woltje 36cd6bd12b queue: rev 553, row 73 done, landed 0453da7e (#1567) (sage) 2026-10-11 18:50:58 -05:00
jason.woltjeandClaude Opus 5.5 48b0b64869 review(58): Darkwing round 2 candidate, rebased on row 74's tree (#1547)
Round 1's build.patch applies cleanly on 424ada49 (row 74 landed as 351464ce).
Only packages/webui/README.md's digest changes; the bullet text is the same.
Manifest eb2b26c1. Gate 23:41:10Z-23:44:25Z: bus 83, tasks 71, webui 61, every
scripts/test-*.sh green. 16 of 17 mutants fail, output identical to round 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:49:52 -05:00
jason.woltjeandClaude Opus 5.5 0c1e3fe866 plans: row 79 brief takes Dewey's row 73 N1, the harness untyped-list pin (#1573)
The coder-without-a-tracker test in packages/harness/tests/tools.test.mjs
joins row 79's files with one deepEqual on the untyped list, the packet
records Dewey's mutants.sh header correction, and the harness suite
joins the gate. Queue note rev 551.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:49:19 -05:00
jason.woltjeandClaude Opus 5.5 0453da7e10 feat(roles): coder drops the task.reassign grant the broker always refuses (#1567, row 73)
roles/coder.json crossRole goes from ["task.reassign", "task.scope.change"]
to ["task.scope.change"]. The broker refuses a non-pm task.reassign at the
writer check before it looks at authority, so the grant could never be
used. A coder's task.reassign now classifies as gated and goes to the
human. Backed by lead decision 90.

Tests: business role and resolve (the narrowing test moves to the pm,
which still holds two cross-role actions), a bus test on the shipped role
files, a harness test that a coder with a tracker gets no task_reassign,
the tasks fixture, and a verbs line showing a coder's task.reassign
refuses field-writer with no action.allowed. mutants.sh is Rocko's packet
rerun script, already committed.

Author: Rocko (packet agents/rocko/work/queue-73/, commit 2137dd7f).
Candidate manifest sha256 bf4e5ed6, base 324c064b, no drift on the
candidate paths through 8a42e819.
Approval: Dewey, round 1, comment 27506 on #1567.
Gate (Sage, /tmp/sage-g73 on 8a42e819, 23:42-23:48Z): tasks 71, bus 81,
business 60, harness 59, cli 113, webui 61, seat 27 node tests, 0 failed;
every scripts/test-*.sh exit 0, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:49:09 -05:00
jason.woltje fb619befd0 queue: revs 551-552, row 79 N1 note (sage) and row 76 started (#1570) (dewey) 2026-10-11 18:45:54 -05:00
jason.woltjeandClaude Opus 5.5 8a42e819fc sessions: dewey row 73 round 1 review (#1567)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:42:03 -05:00
jason.woltjeandClaude Opus 5.5 af29701296 queue: rev 550, row 73 round 1 approve (comment 27506) (#1567) (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:41:58 -05:00
jason.woltjeandClaude Opus 5.5 abf2b4f6ad sessions: sage row 80 stranded-delivery ruling (#1574)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:39:59 -05:00
jason.woltje 18a44ebb64 queue: revs 548-549, row 80 added (#1574) and held behind the cutover (decision 94) (sage) 2026-10-11 18:39:53 -05:00
jason.woltjeandClaude Opus 5.5 d45ed67ca7 plans: bus stranded-delivery brief from row 59 review (Note B)
Darkwing's row 59 review (comment 27487) found that a message delivered
to a run that releases the role unread can't be received or read by the
next holder. Sage's ruling: keep at-most-once delivery, as the bus
README promises, and list stranded messages in the agents read so a
person decides whether to resend. Owner Darkwing after row 59, reviewer
Filbert, held behind the cutover with the other bus rows (decision 94).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:38:56 -05:00
jason.woltjeandClaude Opus 5.5 424ada49c0 sessions: darkwing row 59 round 1 review, approve (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:37:41 -05:00
jason.woltjeandClaude Opus 5.5 0c3817587e build-log: row 74 landed (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:34:09 -05:00
jason.woltje b1c4acf327 queue: revs 545-547, row 74 done (351464ce), row 76 README notes (sage); darkwing's 545, row 59 round 1 review record 2026-10-11 18:33:51 -05:00
jason.woltjeandClaude Opus 5.5 351464ce42 feat(webui): signed-in read-only Console preview behind the edge proxy (#1568, row 74)
serve takes --public-host NAME --auth-header HEADER --allow-user ID, all
three or none. With them, a request whose Host is NAME must carry HEADER
equal to ID or gets 403; a request that carries HEADER with any other Host
gets 403, so a proxy that rewrites Host can't reach the loopback path with
a signed-in request; every POST is refused read-only. A repeated Host gets
400 in both modes. Host and header names compare case-insensitively
(Traefik sends X-Authentik-Uid). The README states the proxy contract:
the edge forwards Host unchanged and strips client copies of HEADER.

Author: Dewey. Round 2 candidate manifest
17b6d7f62d106a23d99441eefa46e34ab2e9bd1442a1d02e219f126a82a8668d (base
17ea0dbc, no drift on the 4 paths through 60c8dcf9). Approved by Filbert
(comment 27485) and Darkwing (comment 27486) on #1568. Their wording
notes on the README (first signed-in request; the tunnel sentence) go to
row 76, which owns the README next.

Sage gate rerun on a worktree of a55915f0 with the patch: manifest OK;
webui 61/61, control-board 124/124, cli 113/113; test-auth 15, conductor
17, config 24, extension-package 18, foundation 44, queue 27, release 14,
task 98, all 0 failed. test-discord failed one timing test (engine: a
timed-out run outlives the grace, 280 ms at load 30) on the sequential
run and passed 66/66 on a solo rerun of the same worktree; the patch
touches no discord file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:32:51 -05:00
jason.woltjeandClaude Opus 5.5 3417d21483 review(59): Darkwing round 1 packet, approve (#1548 comment 27487)
Candidate 90d5bddc on f2f2861b. bus 86/0, webui 40/0, every scripts/test-*.sh
green 23:23:30Z-23:26:25Z; 20 of 21 mutants killed, any-role-msg equivalent.
Notes: a claim that outlives its runner, a stranded delivered message (bus
follow-up to Sage), overlap with row 58.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:32:46 -05:00
jason.woltjeandClaude Opus 5.5 60c8dcf970 sessions: filbert row 74 round 2 review, approve (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:32:20 -05:00
jason.woltjeandClaude Opus 5.5 d754869702 sessions: darkwing row 74 round 2 review, approve (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:31:50 -05:00
jason.woltje 5fbb681b7e queue: revs 543-544, row 74 round 2 approvals, Filbert (543) and Darkwing comment 27486 (#1568) (darkwing) 2026-10-11 18:31:41 -05:00
jason.woltjeandClaude Opus 5.5 3a2b051736 review(74): Darkwing round 2 packet, approve (#1568 comment 27486)
Round 2 candidate 17b6d7f6 on 17ea0dbc. 22 of 23 mutants killed, joined-value
equivalent; probes cover repeated Host, proxy Host, HEAD and absolute-form.
r1/ holds the round 1 findings sent to Dewey in place of a verdict.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:30:48 -05:00
jason.woltjeandClaude Opus 5.5 a55915f067 sessions: dewey row 74 round 2 candidate and review request (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:24:56 -05:00
jason.woltjeandClaude Opus 5.5 821e183d19 queue: revs 540-542, row 74 in review round 2, comment 27484 (#1568) (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:24:49 -05:00
jason.woltjeandClaude Opus 5.5 fce04c1556 review(dewey): row 74 round 2 packet, repeated-Host 400 and proxy-Host 403 (#1568)
Round 2 per Sage's notes revs 521 and 523: a repeated Host is 400 before
any check; HEADER with a Host other than NAME is 403 proxy Host refused;
HEAD, x.NAME and --auth-header tests; the README proxy contract lines.
Candidate manifest 17b6d7f6, base 17ea0dbc. 34 of 34 mutants killed;
gate green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:23:26 -05:00
jason.woltjeandClaude Opus 5.5 0cd7e6cf08 build-log: row 72 landed (#1566)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:21:43 -05:00
jason.woltje 4376f50582 queue: revs 537-539, row 73 round 1 request posted as jarvis (comment 27467), row 79 added (#1573) (sage) 2026-10-11 18:21:22 -05:00
jason.woltjeandClaude Opus 5.5 2137dd7f5d review(rocko): row 73 round 1 packet, coder role drops task.reassign (#1567)
Rocko's packet agents/rocko/work/queue-73/ (candidate manifest bf4e5ed6,
packet manifest 41ffcd5c, 53 files), their BUILD-LOG entry and SESSIONS
line. Sage's SESSIONS line for the review request (comment 27467) and
row 79 (#1573) for the stale text the packet names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:20:34 -05:00
jason.woltjeandClaude Opus 5.5 4e714509d1 plans: coder grant stale text brief from row 73 packet
Rocko's row 73 packet names three places outside the row's files that
still show the coder's task.reassign grant: the business README example,
the tasks README paragraph and live/run.mjs. They go to a follow-up row
after 73, owner Rocko, reviewer Dewey, so row 73's round 1 isn't
restarted for a scope change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:20:14 -05:00
jason.woltje e4bf7d10a7 queue: revs 531-536, row 78 added (#1572), row 72 done (d436fe8b), landing order 74-58-60 (sage); rocko's 533-534, row 73 in review round 1, request failed (no credential) 2026-10-11 18:18:05 -05:00
jason.woltjeandClaude Opus 5.5 d436fe8bdc test(cli, harness): Claude pin tests, a directory pin, a symlink default, a pin with a space (#1566, row 72)
One test in packages/cli/tests/host.test.mjs: a directory given with
--claude is refused, and a symlink default stays the symlink path. One
in packages/harness/tests/claude-session.test.mjs: a directory pin exits
2 before claude runs, and a pin under a directory with a space runs.
agents/rocko/work/queue-72/mutants.sh (committed with the packet in
036afdb6) reruns the six row 68 mutants: all killed on the candidate,
all surviving on the base. Accepted deviation: bash's command -v never
returns a directory, so a directory named claude on PATH takes the
no-claude branch, which the test asserts.

Author: Rocko. Candidate manifest e6531a5311f17ea147700e417dfdbd766a9c38acf3c3b44755267c285b78e006
(base a0080da8, no drift on the candidate paths, bus-service.sh or
adapter.sh). Approved by Filbert, comment 27460 on #1566; their N1 is
row 78, #1572.

Sage gate rerun on a worktree of df676fa1 with the patch: manifest OK;
cli 113/113, harness 58/58, 0 skipped; test-auth 15, conductor 17,
config 24, discord 66, extension-package 18, foundation 44, queue 27,
release 14, task 98, all 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:17:03 -05:00
jason.woltjeandClaude Opus 5.5 f6b9ea4e3f sessions: dewey row 58 round 1 review, approve (#1547)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:14:22 -05:00
jason.woltjeandClaude Opus 5.5 05d9863c7a queue: rev 530, row 58 round 1 approve by dewey, comment 27461 (#1547) (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:14:13 -05:00
jason.woltjeandClaude Opus 5.5 950a965aef plans: Claude pin default-path tests brief from row 72 review (N1)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:13:34 -05:00
jason.woltjeandClaude Opus 5.5 df676fa154 sessions: filbert row 72 round 1 review, approve (#1566)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:12:50 -05:00
jason.woltjeandClaude Opus 5.5 0a4b7a4e59 queue: rev 529, row 72 round 1 approve by filbert, comment 27460 (#1566) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:12:43 -05:00
jason.woltje 324c064b2a queue: revs 524-528, row 72 in review round 1, comment 27454 (#1566) (rocko's 524-526, incl. row 73 started; sage's request as jarvis 527-528) 2026-10-11 18:06:36 -05:00
jason.woltjeandClaude Opus 5.5 036afdb686 review(rocko): row 72 round 1 packet, Claude pin tests (#1566)
Rocko's packet agents/rocko/work/queue-72/ (candidate manifest e6531a53,
base a0080da8) with their BUILD-LOG and SESSIONS appends, and Sage's
SESSIONS line for the jarvis review request (comment 27454).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:06:07 -05:00
jason.woltje 93e8c19327 queue: rev 523, row 74 round 2 note for Darkwing's finding (sage) 2026-10-11 18:05:40 -05:00
jason.woltjeandClaude Opus 5.5 17ea0dbc78 sessions: dewey row 67 round 1 review, approve (#1560)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:04:43 -05:00
jason.woltje 01ef77b04a queue: rev 522, row 67 round 1 approve by dewey, comment 27452 (#1560) (dewey) 2026-10-11 18:04:35 -05:00
jason.woltje ebd905540d queue: revs 520-521, row 74 round 2 note (sage); rev 520 row 72 started (rocko) 2026-10-11 18:01:23 -05:00
jason.woltjeandClaude Opus 5.5 a0080da8c9 docs(sessions): row 74 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:55:02 -05:00
jason.woltjeandClaude Opus 5.5 09edf65110 build-log, sessions: row 75 landed (743c01e3, #1569)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:54:17 -05:00
jason.woltje f0c37f7502 queue: revs 517-519, row 77 added (#1571), row 75 done (743c01e3) (sage); rev 518 row 74 round 1 approve by filbert, comment 27433 2026-10-11 17:54:05 -05:00
jason.woltjeandClaude Opus 5.5 743c01e34b feat(webui): the Console as a user service (#1569, row 75)
scripts/webui-service.sh render|install|uninstall|status and the
packages/webui/systemd/mosaic-webui.service.in template, on the
bus-service.sh pattern. install writes the unit to a temp file, renames
it and runs only `systemctl --user daemon-reload`. There is no start,
stop, enable or restart verb: the Console reads the bus through the human
transport, so Jason starts it from his own shell (lead decisions 62 and
94). Serve's flags pass through, including row 74's preview set, each
held to a character set the unit can carry. The unit sets no MOSAIC_*,
CLAUDE*, CODEX* or PI_* variable and does not restart on serve's exit 2.
Tests in packages/webui/tests/service.test.mjs; TOOLS.md entry.

Author: Rocko. Candidate manifest 7dc571dbe87a484f465c82e1965e499c57d9af047bfe702faa3fe041138dd154
(agents/rocko/work/queue-75/, base 43d2328f, no drift on the manifest
paths to 8b351c3d). Approved by Filbert, comment 27430 on #1569, with
five non-blocking notes placed in row 77.

Sage gate rerun on a worktree of 8b351c3d with the patch: manifest OK;
webui 50/50; test-auth 15, conductor 17, config 24, discord 66,
extension-package 18, foundation 44, queue 27, release 14, task 98, all
passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:49:51 -05:00
jason.woltjeandClaude Opus 5.5 8b351c3d7a plans: Console service follow-ups brief from row 75 review (N1-N5)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:45:52 -05:00
jason.woltjeandClaude Opus 5.5 b7d9cfeb54 docs(sessions): row 75 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:43:57 -05:00
jason.woltjeandClaude Opus 5.5 fc196fe4f1 sessions: dewey row 60 round 1 review, approve (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:43:43 -05:00
jason.woltje 51d42e5ea5 queue: rev 516, row 60 round 1 approve by dewey, comment 27431 (#1549) (dewey) 2026-10-11 17:43:37 -05:00
jason.woltjeandClaude Opus 5.5 bfa2b317e0 queue: revs 513-514, row 76 note on the 403 message, row 60 landing order after 74 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:42:10 -05:00
jason.woltjeandClaude Opus 5.5 7b37e27609 sessions: dewey row 74 round 1 candidate and review request (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:40:08 -05:00
jason.woltje b4dd7305ac queue: revs 511-512, row 74 in review round 1, comment 27428 (#1568) (dewey) 2026-10-11 17:38:43 -05:00
jason.woltjeandClaude Opus 5.5 ea07e19c5a review(dewey): row 74 round 1 packet, WebUI preview behind a sign-in proxy (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:37:14 -05:00
jason.woltjeandClaude Opus 5.5 99db499483 sessions, build-log: rocko row 75 candidate and sage request (#1569)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:34:50 -05:00
jason.woltjeandClaude Opus 5.5 ff20f14469 queue: revs 507-510, row 75 in review round 1, comment 27427 (#1569) (rocko's 507-508; sage's request as jarvis 509-510)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:34:50 -05:00
jason.woltjeandClaude Opus 5.5 bce20fcbbe review(rocko): row 75 round 1 packet, the Console as a user service (#1569)
Committed by sage for rocko (no credential). Candidate manifest 7dc571db, packet manifest 0bc9a870, base 43d2328f.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:33:54 -05:00
jason.woltjeandClaude Opus 5.5 2d04426c5b docs(sessions): row 58 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:32:12 -05:00
jason.woltjeandClaude Opus 5.5 cf2d9c00e9 queue: rev 506, row 58 round 1 approve by filbert, comment 27426 (#1547) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:32:05 -05:00
jason.woltjeandClaude Opus 5.5 c160de3dac decisions: 96, Q97 yes, next-target plan confirmed as written
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:31:04 -05:00
jason.woltje 3f3276bc05 queue: rev 505, row 74 started (dewey) 2026-10-11 17:30:55 -05:00
jason.woltjeandClaude Opus 5.5 ec67c2215a sessions: sage timestamp correction for a3d17005
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:29:19 -05:00
jason.woltjeandClaude Opus 5.5 a3d1700539 sage: M1 draft role brief and I1 survey dispatch (next-target, waits on Q97)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:29:11 -05:00
jason.woltjeandClaude Opus 5.5 3c2ed3fb96 sessions: sage next-target plan to Mos
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:27:41 -05:00
jason.woltjeandClaude Opus 5.5 c7b890e1c5 sessions: darkwing row 58 round 1 in review (#1547)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:25:15 -05:00
jason.woltje d7954e87d7 queue: revs 502-504, row 58 in review round 1, comment 27425 (#1547) (darkwing; includes rocko's rev 502, row 75 in progress) 2026-10-11 17:24:07 -05:00
jason.woltjeandClaude Opus 5.5 7f92a1039b review(darkwing): row 58 round 1 packet, task priority and requirement on the bus read (#1547)
Candidate build.patch on ae428a54, manifest e63e4c4f. Gate on f2f2861b: bus 83,
tasks 71, webui 40, every scripts/test-*.sh exit 0. Mutants 16 of 17 killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:23:08 -05:00
jason.woltjeandClaude Opus 5.5 e8ad8b4121 sessions: sage decisions 94-95, rows 75-76, deferrals (#1569, #1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:22:33 -05:00
jason.woltje 43d2328f3a queue: rev 501, row 75 brief re-pinned to the shared file's new blob (#1569) 2026-10-11 17:21:42 -05:00
jason.woltje d455ad0af9 queue: rev 500, row 76 brief re-pinned for Sec-Fetch-Site (#1570) 2026-10-11 17:21:04 -05:00
jason.woltjeandClaude Opus 5.5 ff03ba69e6 docs: row 76 brief requires Sec-Fetch-Site same-origin on the send POST (#1570)
From ops-01's route proposal via Mos (sage-0057).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:20:31 -05:00
jason.woltje a2488cbcb7 queue: rev 499, row 76 unblocked on Q95 (lead decision 95; #1570) 2026-10-11 17:19:40 -05:00
jason.woltjeandClaude Opus 5.5 f914adbe77 docs: lead decision 95, Q95 yes for a Console message to a role (#1570)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:19:07 -05:00
jason.woltje 57f36a9663 queue: revs 482-498, rows 75-76 added, 74 and 42 re-pinned, nine rows deferred, row 5 note (lead decision 94; #1569, #1570) 2026-10-11 17:18:46 -05:00
jason.woltjeandClaude Opus 5.5 8334a52331 docs: lead decision 94, dogfood cutover rows, Jason starts the Console (#1569, #1570)
Decision 94 records Q94 yes and Mos's sage-0054/0055 directions: no broker
row (the Console already reads as the human), Jason starts the Console,
nine rows deferred, S7's line amended, Gate E partial pass on the dogfood
day, no rollback hostname. New brief for the Console service row and the
send-to-a-role row. Row 74's start line and S7's request line amended.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:17:35 -05:00
jason.woltjeandClaude Opus 5.5 b977dc2873 docs(sessions): row 67 round 1 review request (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:12:46 -05:00
jason.woltjeandClaude Opus 5.5 8f1c0e6d12 queue: revs 480-481, row 67 in review round 1 (#1560) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:12:32 -05:00
jason.woltjeandClaude Opus 5.5 173e5f0bb0 sessions: sage cutover plan for Mos sage-0053 (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:11:49 -05:00
jason.woltjeandClaude Opus 5.5 69761f73a2 review(filbert): row 67 round 1 packet, decisions read follow-up tests (#1560)
Manifest pins 4 files on base 970ef729; candidate source is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:11:46 -05:00
jason.woltjeandClaude Opus 5.5 e8f94e0b73 sessions: sage row 74 Authentik correction and brief re-pins (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:05:50 -05:00
jason.woltje 96e6309699 queue: row 74 brief re-pinned, header case and proxy Host rule (#1568) (sage) 2026-10-11 17:05:32 -05:00
jason.woltjeandClaude Opus 5.5 9f6c63b202 docs: row 74 brief, header name case and proxy Host rule (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:04:48 -05:00
jason.woltje e1837ec689 queue: row 74 brief re-pinned, Authentik correction (#1568) (sage) 2026-10-11 17:04:29 -05:00
jason.woltjeandClaude Opus 5.5 8f857b93d7 docs: decision 93 correction, Authentik not Pocket ID; row 74 brief follows (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:03:42 -05:00
jason.woltjeandClaude Opus 5.5 bc8e1ee780 sessions: sage Q91 A, decision 93, row 74 unblocked (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:03:15 -05:00
jason.woltjeandClaude Opus 5.5 970ef72935 docs(sessions): row 67 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:02:32 -05:00
jason.woltje 511a1b7c3e queue: rev 476, row 74 brief re-pinned for decision 93 (#1568) (sage) 2026-10-11 17:02:11 -05:00
jason.woltje 13a011925e queue: row 74 brief re-pinned, gate text, unblocked by Q91 A, decision 93 (#1568) (sage) 2026-10-11 17:01:15 -05:00
jason.woltjeandClaude Opus 5.5 70b9345ad3 docs: lead decision 93, Q91 A, row 74 brief amended for sign-in on 10-12 (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:00:10 -05:00
jason.woltjeandClaude Opus 5.5 c79a680c3f docs(sessions): row 60 round 1 review request (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 17:00:04 -05:00
jason.woltjeandClaude Opus 5.5 ac99d8f2bf queue: revs 472-473, row 60 round 1 review request, comment 27383 (#1549) (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:59:53 -05:00
jason.woltjeandClaude Opus 5.5 e7e6950819 review(filbert): row 60 round 1 packet, broker credential status read (#1549)
Manifest pins 9 files on base 4100fa68; candidate source is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:57:51 -05:00
jason.woltjeandClaude Opus 5.5 c949f9ef71 docs(sessions): row 59 round 1 review, approve (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:57:28 -05:00
jason.woltjeandClaude Opus 5.5 dcdd3d6d90 queue: rev 471, row 59 round 1 approve, comment 27382 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:57:23 -05:00
jason.woltjeandClaude Opus 5.5 a3ba78b525 docs: lead decision 92, WebUI preview path; sessions line (#1568)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:55:22 -05:00
jason.woltje 1786136002 queue: revs 468-470, row 74 WebUI preview added, blocked on Q91 (#1568) (sage) 2026-10-11 16:55:09 -05:00
jason.woltjeandClaude Opus 5.5 620fb174f6 docs: WebUI preview brief, signed-in read-only Console behind the edge proxy
Mos approved the cuts 2026-10-11: forward auth at the edge, a user unit
beside the bus, a preview hostname, read-only. No code until Jason
answers Q91 (amending lead decision 48).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:53:21 -05:00
jason.woltjeandClaude Opus 5.5 c26341b830 sessions: sage row 68 landed, decisions 90-91, rows 72-73 (#1561)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:49:57 -05:00
jason.woltje ef44d9b92f queue: revs 464-467, rows 72 and 73 added and briefed (#1566, #1567) (sage) 2026-10-11 16:49:04 -05:00
jason.woltjeandClaude Opus 5.5 32aa572788 docs: lead decision 91, row 68 build log, pin and coder grant brief (#1561)
Decision 91 amends 89: the bus pins ~/.local/bin/claude through
MOSAIC_CLAUDE_BIN, so the package upgrade is no longer needed for the
first PM launch. The brief covers two Rocko rows: tests for row 68's
surviving mutants, and dropping the coder's dead task.reassign grant.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:47:15 -05:00
jason.woltje 4100fa68c0 queue: rev 463, row 68 done (11dae0c7, round 2) (sage) 2026-10-11 16:44:27 -05:00
jason.woltjeandClaude Opus 5.5 ba67025050 docs(sessions): row 68 round 2 review, approve (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:43:45 -05:00
jason.woltjeandClaude Opus 5.5 b639c07c93 sessions: filbert row 60 started; 02a23462 also carries dewey rev 461 (#1549)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:43:43 -05:00
jason.woltjeandClaude Opus 5.5 02a2346269 queue: rev 461, row 60 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:43:18 -05:00
jason.woltjeandClaude Opus 5.5 11dae0c7d8 feat(bus): the bus host runs an explicit Claude Code binary (#1561, row 68)
MOSAIC_CLAUDE_BIN names the claude binary by absolute path instead of
finding it on PATH. scripts/bus-service.sh render|install --claude PATH
writes it into the unit (default: command -v claude in the installing
shell); the claude adapter, the launcher's version read and the seat
session environment all use it. Mos's Q18 ask: the unit's PATH found
/usr/bin/claude 2.1.259, which refuses claude-opus-5-5.

Candidate: round 2 manifest 21c1ab05, round 1 (a2008d8b) rebased onto
f2f2861b after row 55 changed docs/TOOLS.md and packages/cli/README.md;
the changed lines are byte-identical. Approved by Filbert (27349, 27374)
and Dewey (27350, 27375). Sage's gate on f2f2861b plus the patch: cli
112, seat 27, harness 57, nine scripts/test-*.sh 0 failed, Docker on.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:42:23 -05:00
jason.woltjeandClaude Opus 5.5 6ee63518a3 sessions: filbert row 68 round 2 approved (#1561)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:41:59 -05:00
jason.woltjeandClaude Opus 5.5 b48499e3e2 queue: rev 460, row 68 round 2 approved by filbert, comment 27374 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:41:52 -05:00
jason.woltjeandClaude Opus 5.5 5d04925b4e sessions: filbert row 59 round 1 in review (#1548)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:40:19 -05:00
jason.woltjeandClaude Opus 5.5 43f93c0bab queue: revs 458-459, row 59 in review round 1, comment 27371 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:40:01 -05:00
jason.woltjeandClaude Opus 5.5 e82f7c32e2 row 59 (#1548): round 1 packet, agent state per role (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:10 -05:00
jason.woltje 16c49998f7 queue: revs 455-457, row 68 round 2 on the rebased manifest 21c1ab05, comment 27369 (sage) 2026-10-11 16:38:57 -05:00
jason.woltjeandClaude Opus 5.5 b31cc8a90c row 68 (#1561): round 2 packet, round 1 rebased onto f2f2861b after row 55
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:37:32 -05:00
jason.woltjeandClaude Opus 5.5 21122c3b0e docs(plans): slice 1 field table follows the code for comments and title (lead decision 90, #1550)
Darkwing found, reading ahead on row 61, that message.send never writes a
tracker comment and that task.scope.change has no writer named. The
prose moves to what the verbs enforce; row 61 rules out changing writers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:34:56 -05:00
jason.woltjeandClaude Opus 5.5 f2f2861b7f docs(log): row 62 landed, the adapter reads a project tree (#1555)
BUILD-LOG entry and SESSIONS line: feat 390d437b, gate, the live read
(902b3d0e) and the follow-up row 71 (#1565).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:32:49 -05:00
jason.woltje 86f65766fa queue: revs 452-454, row 62 done (390d437b), row 71 added and briefed (#1565) (sage) 2026-10-11 16:31:05 -05:00
jason.woltjeandClaude Opus 5.5 9d71527622 docs(plans): brief for the tree read follow-up after row 62 (#1565)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:30:23 -05:00
jason.woltjeandClaude Opus 5.5 902b3d0eac vikunja: row 62 live tick on #19 with descendants on, GETs only (sage)
tick-62.mjs runs the candidate's discover(), reconcile() and tick()
against tasks.woltje.com with the sync key, a real Store and Broker in a
scratch root, and a fetch guard that refuses anything but GET. The
startup scope probes are not sent. Reconcile 15 GETs, 42 snapshots; tick
8 GETs, 0 snapshots; 18, 20 and 21 watch-only. A missing view id on #18
answers 404 code 3014 while #18 reads 200 (the fake says 13001).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:29:18 -05:00
jason.woltjeandClaude Opus 5.5 390d437ba2 feat(tasks): the adapter reads a project tree (#1555, row 62)
With tracker.descendants on, the tracker project is the root of a tree.
Each reconcile reads the sync bot's project list (archived projects and
negative pseudo-project ids left out) and walks it by parent_project_id.
A child that passes the install checks is read through its board, and
one that doesn't is watch-only (bucket 0) and never refuses the
business. A tick reads each project in the tree. A child answering 403
or 404 leaves the tree only if GET /projects/<id> then answers 403 or
404 too, and its open tasks get the one-by-one read. Task refs carry
the project, so a task moved inside the tree shows under its new ref
and the old ref gets its tombstone. tracker.descendants reaches the adapter
through the business var and packages/cli/src/config.mjs (lead decision
87). Off, the adapter reads as v0 did.

Darkwing built it (agents/darkwing/work/queue-62), in three rounds.
Round 3 was approved by Filbert (27317) and Dewey (27338) on manifest
8b81e836, 13 paths. Their non-blocking notes and Darkwing's follow-ups
(two surviving mutants in test 18, the fake's view-404 code) go to a
follow-up row.

Sage's gate on 918a4a19, sequential and teed: node suites tasks 71,
business 60, bus 80, cli 110 and queue 150, 0 failed; webui 39 of 40,
then 40 of 40 on three reruns (row 62 touches no webui file; a timing
test under load 17 with Dewey's gate running beside it); test-auth 15,
test-conductor 17, test-config 24, test-discord 66,
test-extension-package 18, test-foundation 44, test-queue 27,
test-release 14, test-task 98, all 0 failed (Docker on). edc5de71..HEAD
touches none of the 13 paths.

Live read, 2026-10-11T21:28:03Z, tasks.woltje.com v2.7.0, root #19,
descendants on, sync key only, GETs only (the startup scope probes not
sent): reconcile 15 GETs, 42 snapshots; tick 8 GETs, 0 snapshots.
Children 18, 20 and 21 are watch-only. A missing view id on #18 answers
404 (code 3014) while #18 reads 200, as the README says. Record:
agents/sage/work/vikunja-setup/2026-10-11_tick-62.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:29:13 -05:00
jason.woltjeandClaude Opus 5.5 45fe40f759 docs(sessions): row 68 round 1 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:23:13 -05:00
jason.woltjeandClaude Opus 5.5 e93674e5c0 docs(sessions): row 68 round 1 review, approve (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:22:26 -05:00
jason.woltjeandClaude Opus 5.5 9b6d983164 queue: revs 448-451, row 68 round 1 approve, comment 27350 (dewey)
Includes Sage's pending revs 448 (sage-q81-docs-add) and 449
(sage-70-briefed), and Filbert's rev 451 (filbert-68-review-1).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:22:17 -05:00
jason.woltje c4399c8b0d queue: revs 448-449, row 70 added and briefed (#1564), Q81 docs follow-up (sage) 2026-10-11 16:21:30 -05:00
jason.woltjeandClaude Opus 5.5 19e38d7f2d docs(plans): brief for the mosaic-on-PATH docs after Q81
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:18:57 -05:00
jason.woltjeandClaude Opus 5.5 d3ca2c22d6 ops(q81): the wrapper forwards __lease-capability to mosaic-old (Mos)
Mos amended ruling (2): with no environment location for
MOSAIC_LEASE_VERSION_PROBE_COMMAND, the wrapper sends only
`__lease-capability` to ~/.npm-global/bin/mosaic-old, so the old CLI's
lease gate still gets its answer. Every other subcommand reaches the
pinned worktree; undo removes the forward with the wrapper. update now
rewrites the wrapper, and status flags a wrapper that differs from what
the script writes. Checked live at 21:17:58Z: the probe answered
{"name":"lease-runtime-activation","version":1}, exit 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:18:21 -05:00
jason.woltjeandClaude Opus 5.5 b93211e37e docs(queue-62): the correction's fail-closed sentence overclaimed; name the assumption
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:17:16 -05:00
jason.woltjeandClaude Opus 5.5 ba5e74ad36 docs(queue-62): correct round 3's live-instance claim, which came from the fake
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:17:04 -05:00
jason.woltjeandClaude Opus 5.5 918a4a1937 ops(q81): mosaic on PATH runs the pinned mosaic-stack CLI (sage-0042)
agents/sage/work/q81/mosaic-cli.sh does install, update, undo and status.
~/.npm-global/bin/mosaic is now a wrapper that execs scripts/mosaic in a
detached worktree at ~/.local/share/mosaic-cli/stack, pinned to pushed
ee129aa6. The estate CLI symlink was renamed in place to mosaic-old.
Only commits on origin/refactor can be pinned. `mosaic queue` refuses
from the worktree (the queue runs only in the canonical checkout), so
queue work stays on scripts/mosaic. status flags a wrapper replaced by
`npm i -g --force`.

lease-gate-notes.md: no single environment location for
MOSAIC_LEASE_VERSION_PROBE_COMMAND exists outside fleet state, so the
old-path lease gate fails closed, as Mos ruled for that case.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:16:28 -05:00
jason.woltjeandClaude Opus 5.5 08c9b37739 docs(sessions): row 62 round 3 review, approve (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:14:23 -05:00
jason.woltjeandClaude Opus 5.5 c42479f049 queue: rev 447, row 62 round 3 approve, comment 27338 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:14:16 -05:00
jason.woltjeandClaude Opus 5.5 ee129aa690 docs(sessions): row 62 round 3 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:09:05 -05:00
jason.woltjeandClaude Opus 5.5 bd3cf272b9 queue: rev 446, row 62 round 3 approve, comment 27317 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:08:56 -05:00
jason.woltjeandClaude Opus 5.5 febfdb4826 docs: row 55 landing in BUILD-LOG and SESSIONS (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:07:52 -05:00
jason.woltje fef76fe67e queue: revs 443-445, row 69 added and briefed (#1562), row 55 done (79d9fc2d) (sage) 2026-10-11 16:07:01 -05:00
jason.woltjeandClaude Opus 5.5 79d9fc2d56 feat(cli): full-screen terminal app (#1544, row 55)
Bare `mosaic` on a terminal opens the app. It has Inbox, Tasks, Agents
and Trail screens, help, and a decide overlay, and it uses the alternate
screen, raw mode, resize and SIGTERM, and restores the terminal on exit.
draw(state) in screens.mjs is pure. cli.mjs exports resolveDecision,
which `mosaic decide` and the app share, so both write the same lines
and ask the same question. A decision resolves on `y` only while
decideShown() says the whole decide box is on screen. The app reads that
at the key. A box that doesn't fit is replaced by "Too small to show this
decision", and any key answers No. scripts/mosaic sends no arguments to
packages/cli (a decided scope addition), so the queue dispatch test
follows. The cli README and docs/TOOLS.md describe the app.

Darkwing built it (agents/darkwing/work/queue-55), in three rounds.
Round 3 was approved by Dewey (27308) and Filbert (27309) on manifest
59f1b9b4, 9 paths. Their non-blocking notes go to row 69 (#1562): a
narrow-terminal test for the decideShown MIN_COLS guard, a way out from
the hidden box, a U+18D08 width test, a cut box keeping Esc back, and
the README row count stated as an example.

Sage's gate on 3634d06e, sequential and teed: node suites cli 110 and
queue 150, 0 failed; test-auth 15, test-conductor 17, test-config 24,
test-discord 66, test-extension-package 18, test-foundation 44,
test-queue 27, test-release 14, test-task 98, all 0 failed (Docker on).
122b1223..3634d06e touches none of the 9 paths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:06:11 -05:00
jason.woltjeandClaude Opus 5.5 1e71d74670 docs(plans): terminal app follow-up brief after row 55
Filbert N10-N12 and Dewey's round 3 survivors: a narrow-terminal
decideShown guard test, a way out in the hidden decide box, a U+18D08
width test, a cut-box Esc back test, and the README row count as an
example. Owner Darkwing; reviewers Dewey and Filbert.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:02:16 -05:00
jason.woltje 3634d06ec8 queue: revs 440-442, row 68 round 1 in review, request comment 27312 (sage) 2026-10-11 15:57:31 -05:00
jason.woltjeandClaude Opus 5.5 ccccc16932 row 68 (#1561) round 1 packet: bus host pins MOSAIC_CLAUDE_BIN
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:57:01 -05:00
jason.woltjeandClaude Opus 5.5 8e33490f0f docs(sessions): row 62 round 3 in review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:55:36 -05:00
jason.woltje 8f6b06d0ae queue: revs 436-439, row 59 started (filbert), row 62 round 3 in review on 8b81e836, request comment 27310 (darkwing) 2026-10-11 15:55:27 -05:00
jason.woltjeandClaude Opus 5.5 90e568624f row 62 (#1555) round 3 packet: confirm read before a child drops, candidate 8b81e836
R2/B1: after a forbidden or not-found refusal on a child, the tick reads
GET /projects/<id> and drops the child only on 403 or 404; a deleted
kanban view (project read 200) fails the tick as in round 1. Tests 18
and 19, 23/23 mutants killed, gate on ae428a54 green (14 suites).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:54:19 -05:00
jason.woltjeandClaude Opus 5.5 8584e84cfd docs(sessions): row 55 round 3 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:49:40 -05:00
jason.woltjeandClaude Opus 5.5 99469f781b queue: rev 435, row 55 round 3 approve, comment 27309 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:49:33 -05:00
jason.woltjeandClaude Opus 5.5 cc78a32cbd docs(sessions): correct the row 55 round 3 line's queue commit (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:48:44 -05:00
jason.woltje a86de1f17b queue: revs 432-434, row 55 dewey r3 approve (dewey), row 68 added and briefed (#1561) (sage) 2026-10-11 15:48:38 -05:00
jason.woltjeandClaude Opus 5.5 ae428a5409 docs(sessions): row 55 round 3 review, approve (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:47:37 -05:00
jason.woltjeandClaude Opus 5.5 e1b90ed696 docs(plans): bus host explicit Claude Code binary brief (Q18)
Mos withdrew decision 89's package upgrade: Jason's Claude Code is the
native ~/.local/bin/claude (2.1.296), not /usr/bin/claude 2.1.259. The
unit will carry MOSAIC_CLAUDE_BIN and the adapter and manifest version
check will use it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:46:52 -05:00
jason.woltjeandClaude Opus 5.5 677c3c63b5 docs(sessions): row 55 r2, row 62 r1 and r2, row 57 r1 reviews (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:41:03 -05:00
jason.woltjeandClaude Opus 5.5 5d131aa76f docs: row 57 landing in BUILD-LOG and SESSIONS (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:40:19 -05:00
jason.woltje ec1b0add4c queue: revs 429-431, row 57 done (f78ba57d), row 67 added and briefed (#1560) (sage) 2026-10-11 15:39:09 -05:00
jason.woltjeandClaude Opus 5.5 e5af27e982 docs(plans): decisions read follow-up brief after row 57
Darkwing N1/N2 (27290), Dewey survivors recent-closed-first-event and
web-503-first (27300), and Filbert's trail comment note. Owner Filbert;
reviewers Darkwing and Dewey. No roles LIMIT here; paging is the design
row's call.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:37:54 -05:00
jason.woltjeandClaude Opus 5.5 f78ba57d70 feat(bus): decisions routed to roles and recent decisions (#1546, row 57)
The broker gains the reader verb `decisions` with `{list: 'roles' |
'recent'}`: the open decisions routed to each role, and the last 50
decisions (RECENT). views.mjs exposes it in the Q1 client module, and
webui serves `GET /api/bus/decisions?list=roles|recent`. Any other list
is 400 and never reaches the bus. No Inbox tab uses the route yet; the
tabs are the design rows' work. The bus and webui READMEs list the verb,
the route and who may call it.

Filbert built it (agents/filbert/work/queue-57). Round 1 approve from
Darkwing (27290) and Dewey (27300) on manifest af3b03cf, 8 paths. Their
non-blocking notes (no seen-then-resolve test, no webui test of ?list=
before the no-bus 503, test 4 compares fewer fields, roles has no LIMIT,
3 of 14 mutants survived) go to a follow-up row.

Sage's gate on 3c79809c, sequential and teed: node suites bus 80 and
webui 40, 0 failed; test-auth 15, test-conductor 17, test-config 24,
test-discord 66, test-extension-package 18, test-foundation 44,
test-queue 27, test-release 14, test-task 98, all 0 failed.
958668a8..3c79809c touches none of the 8 paths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:36:52 -05:00
jason.woltjeandClaude Opus 5.5 3c79809ca0 queue: rev 428, row 57 round 1 approve, comment 27300 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:30:39 -05:00
jason.woltjeandClaude Opus 5.5 a1d021da0c queue: rev 427, row 62 round 2 changes, comment 27297 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:15:57 -05:00
jason.woltjeandClaude Opus 5.5 1ea4db8844 docs(sessions): row 62 round 2 review, changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:13:06 -05:00
jason.woltjeandClaude Opus 5.5 e1e716c269 queue: rev 426, row 62 round 2 changes, comment 27296 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:12:59 -05:00
jason.woltjeandClaude Opus 5.5 72b16a5ba6 docs(sessions): row 57 round 1 review, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:09:14 -05:00
jason.woltjeandClaude Opus 5.5 e4169a7f34 queue: rev 425, row 57 round 1 approve, comment 27290 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:09:02 -05:00
jason.woltjeandClaude Opus 5.5 192717a645 decisions: 89, Q18 PM-launch unblock by config, bus claude package upgrade; sessions line (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:04:19 -05:00
jason.woltjeandClaude Opus 5.5 495b3ad462 docs(sessions): row 57 in review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:02:23 -05:00
jason.woltjeandClaude Opus 5.5 65c5b30635 queue: revs 422-424, row 57 in review, comments 27287 and 27289 (filbert)
Rev 422 is Darkwing's row 58 start (darkwing-58-start), swept by the snapshot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:02:16 -05:00
jason.woltjeandClaude Opus 5.5 fa3cc6e1df docs(queue-57): row 57 candidate packet, decisions read for roles and recent (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:00:47 -05:00
jason.woltjeandClaude Opus 5.5 ab83bd2bbf docs(sessions): row 55 round 3 in review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:00:24 -05:00
jason.woltjeandClaude Opus 5.5 0506765606 queue: revs 419-421, row 55 round 3 in review, comment 27286 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 15:00:11 -05:00
jason.woltjeandClaude Opus 5.5 26926d183a docs(queue-55): row 55 round 3 candidate packet, R3 and B1 (darkwing)
One rule for Filbert's R3 and Dewey's B1: y resolves only while the
whole decide box is shown. Adds N9 widths, the start-unguarded and
timeout fixes, and two defects found by the cheap tests (wrap at
graphemes, answer render throw). 51/51 mutants killed; gate on
122b1223 green, cli 110.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:59:13 -05:00
jason.woltjeandClaude Opus 5.5 0d79e04b4d docs(sessions): row 62 round 2 in review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:56:22 -05:00
jason.woltje a18338406d queue: revs 416-418, row 62 round 2 in review, comment 27283 (darkwing) 2026-10-11 14:56:12 -05:00
jason.woltjeandClaude Opus 5.5 620c0e89fd docs(queue-62): row 62 round 2 candidate packet, R1 and six test gaps (darkwing)
Filbert's R1 (27279) and Dewey's matching blocking item (27280): a child
that answers 403 or 404 in a tick leaves the tree, and the root still
refuses. Tests for Filbert's N1-N3 and Dewey's three gaps. 18 mutants
killed; all 13 suites green at 122b1223. Manifest sha256 55316d88.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:55:12 -05:00
jason.woltjeandClaude Opus 5.5 63fb219b7c decisions: 88, rows 64-66 for Q82-Q84; sessions line (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:54:42 -05:00
jason.woltjeandClaude Opus 5.5 7cab133bd3 queue: revs 410-415, rows 64-66 briefed for Q82 skills, Q83 routing, Q84 unslop (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:54:21 -05:00
jason.woltjeandClaude Opus 5.5 26cc4e1960 brief: managed session config rows, skills (Q82), model routing (Q83), unslop (Q84) (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:52:45 -05:00
jason.woltjeandClaude Opus 5.5 02da0929c4 vikunja: Q80, delete #19 view 76 extra buckets 55, 56, 158, 160 with a one-time key (sage)
Jason: "Q80: Have the PM decide. Then fix". project-manager decided to
delete all four and moved their tasks to todo. Mos ruled no standing
delete scope on any bot key. svc-mosaic-stack minted key 10 for the pm
user with only projects.views_buckets_delete and a 30-minute expiry,
held in memory only. The pre-read showed all four buckets empty. Four
DELETEs answered 200, and key 10 was revoked (204) at once. The pm user
now lists only key 8. The read-back shows buckets 57 done, 159 todo
(default), 161 in-progress, 248 in-review and 162 blocked, with task
counts unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:47:29 -05:00
jason.woltjeandClaude Opus 5.5 122b122374 queue: rev 409, row 62 round 1 changes requested, comment 27280 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:39:49 -05:00
jason.woltjeandClaude Opus 5.5 958668a847 docs(sessions): row 62 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:24:59 -05:00
jason.woltjeandClaude Opus 5.5 a8ae533a08 queue: rev 408, row 62 round 1 changes requested, comment 27279 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:24:47 -05:00
jason.woltjeandClaude Opus 5.5 c809c1fded queue: rev 407, row 55 round 2 changes requested, comment 27278 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:19:08 -05:00
jason.woltjeandClaude Opus 5.5 6aecd41239 docs(sessions): row 62 probe and rulings (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:15:29 -05:00
jason.woltjeandClaude Opus 5.5 ef69a2c4ac vikunja: row 62 gate-read probe, cross-project task read works for the sync key (sage)
GETs only, sync key 9. Project list 200, total 30 (31 with archived #15).
#18, #20 and #21 have parent 19, none archived. GET /tasks with
filter project in 19, 18, 20, 21: 200, 42 tasks across all four. The
per-project open reads give 18, 4, 3 and 1. A follow-up row moves a tick
to the single read after row 62 is done.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:14:43 -05:00
jason.woltjeandClaude Opus 5.5 9a51b34d68 docs(sessions): row 62 in review round 1 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:13:18 -05:00
jason.woltjeandClaude Opus 5.5 445d5c1239 queue: revs 405-406, row 62 in review round 1, request comment 27274 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:13:09 -05:00
jason.woltjeandClaude Opus 5.5 6b83edc36a docs(queue-62): row 62 candidate packet, project trees (darkwing)
Candidate manifest sha256 ec3f7a42f3bf6436634a5a83a12bb5678ca1e58a10ff4f66a23dfca7ff3f375f,
13 files on edc5de71. Gate on 6ae8b844: 13 suites green. The candidate
itself is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:12:05 -05:00
jason.woltjeandClaude Opus 5.5 90587fa900 queue: rev 404, row 57 in progress (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:09:35 -05:00
jason.woltjeandClaude Opus 5.5 cda3cd4868 docs(sessions): row 55 round 2 verdict (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:08:16 -05:00
jason.woltjeandClaude Opus 5.5 906298d63d queue: rev 403, row 55 round 2 changes requested, comment 27269 (filbert)
R3: below 40x10 a y answers a question the screen does not show. Candidate
3f2d8f8ea3fea1cf7a002297ee9b23059f1522e8060c1d981fba19c85baeabd7.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:08:06 -05:00
jason.woltjeandClaude Opus 5.5 6ae8b84423 docs(sessions): row 41 close-out, format correction (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:52:29 -05:00
jason.woltjeandClaude Opus 5.5 0827e64fba docs(sessions): row 41 close-out (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:52:19 -05:00
jason.woltjeandClaude Opus 5.5 1725358066 queue: revs 400-402, row 41 done (recorded run met, 892e765f), rows 42 and 49 briefed (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:51:39 -05:00
jason.woltjeandClaude Opus 5.5 9277de3341 decisions: 87, row 62 adds tracker.descendants to the host tracker entry (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:50:00 -05:00
jason.woltjeandClaude Opus 5.5 85258f5046 docs(sessions): row 41 acceptance verdict (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:42:18 -05:00
jason.woltjeandClaude Opus 5.5 892e765f37 review(41): recorded acceptance run met, ACCEPTANCE.md and run evidence (filbert)
Jason ran run.sh (sha256 c609c682) from a human shell on fe5c68fa,
2026-10-11T18:32:45Z to 18:32:54Z. The PM launched coder r2810c6373900,
recorded by pm in the launch log, and mosaic agents shows it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:42:00 -05:00
jason.woltje edc5de7166 queue: rev 399, row 62 in progress (darkwing) 2026-10-11 13:41:52 -05:00
jason.woltjeandClaude Opus 5.5 3b30e53528 docs(sessions): row 55 round 2 in review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:40:12 -05:00
jason.woltje 742e310e82 queue: revs 396-398, row 55 round 2 in review, request comment 27259 (darkwing) 2026-10-11 13:40:03 -05:00
jason.woltjeandClaude Opus 5.5 fa6d889cb7 queue-55: round 2 candidate packet (darkwing)
Answers Filbert's comment 27251 and Dewey's comment 27253: R1 type-ahead
window, R2 terminal cell widths, B1 cut inside the question, 30 mutants
killed, N1-N8. Candidate manifest sha256 3f2d8f8e. Gate on c506c6ec:
cli 106/106 and every scripts/test-*.sh exit 0. The feature stays
uncommitted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:39:05 -05:00
jason.woltjeandClaude Opus 5.5 c506c6ec12 docs(sessions): Q61a steps 1-3 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:23:41 -05:00
jason.woltjeandClaude Opus 5.5 c8a7389615 vikunja: Q61a #19 install, bus on #19, SetSpark #35 and Launchpad #36; shares read (sage)
install-19.mjs renames 57 done, 159 todo, 161 in-progress, 162 blocked,
adds 248 in-review and moves the default to 159; no task moved. Writes go
to /api/v1 (v2.7.0 405s bucket writes under /api/v2). create-33.mjs adds
#35 and #36 under #33. Records hold no secret; shares-read titles removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:22:50 -05:00
jason.woltjeandClaude Opus 5.5 5628885aa5 config: tracker.project 19 (Mosaic Stack) for the stack project (Q61a, decision 86)
Project 32 was deleted; the bus refused at startup with tracker-project.
#19's kanban got the five buckets in agents/sage/work/vikunja-setup/
2026-10-11_install-19.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:19:28 -05:00
jason.woltjeandClaude Opus 5.5 78023d79c6 queue: rev 395, row 55 round 1 changes requested (dewey)
Verdict comment 27253 on #1544, candidate manifest 93fa48c1cdfe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:17:50 -05:00
jason.woltje edc1452250 queue: revs 391-394, rows 62 and 63 for Q61a multi-project sync (sage) 2026-10-11 13:14:31 -05:00
jason.woltjeandClaude Opus 5.5 8060426035 vikunja: Q61a re-mint, pm key 8 and sync key 9; rows A and B brief; decision 86 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:12:39 -05:00
jason.woltjeandClaude Opus 5.5 b644f3c230 docs(sessions): row 55 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:12:27 -05:00
jason.woltjeandClaude Opus 5.5 4d25476432 queue: rev 390, row 55 round 1 changes requested (filbert)
Comment 27251 on #1544 names candidate 93fa48c1cdfe. R1: the decide
question takes type-ahead and ignores stray keys. R2: cells() undercounts
emoji below U+1F300.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:12:17 -05:00
jason.woltjeandClaude Opus 5.5 5edaa96d75 docs(sessions): Q58a/Q60 teams, row 55 gate (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:03:58 -05:00
jason.woltje d48ec635b7 queue: rev 389, row 55 Sage gate green (sage) 2026-10-11 13:03:40 -05:00
jason.woltjeandClaude Opus 5.5 c175440f0e docs(decisions): 85, agent teams on Jason's projects, svc out (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:56:59 -05:00
jason.woltjeandClaude Opus 5.5 20735a3706 vikunja: Q58a/Q60 teams 1 and 2, pm key 7 replaces key 2 (sage)
svc-mosaic-stack created mosaic-agents (team 1, bots pm/cto/coder/reviewer)
and mosaic-sync (team 2, bot sync), made jason.woltje team admin, then left
both teams so a team share gives svc no project access. The pm key gained
projects read_all, read_one and update; no key has task or project delete
or a share route (read back via GET /tokens?owner_id). Old pm key 2 revoked,
now 401. Record holds ids and scopes only; checked for secrets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:56:04 -05:00
jason.woltjeandClaude Opus 5.5 4acadaae4e review(55): Sage's dispatch test ruling and the transport follow-up note
BUILD.md records Sage's 2026-10-11 ruling that packages/queue/tests/
dispatch.test.mjs is in scope. FOLLOWUP-transport-code.md writes up the
transport's did-not-finish error with no .code, for the 10-12 batch.
The candidate (manifest 93fa48c1cdfe) is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:54:43 -05:00
jason.woltjeandClaude Opus 5.5 5ee797937a docs(sessions): row 55 in review round 1 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:52:31 -05:00
jason.woltjeandClaude Opus 5.5 6a6e6634fd queue: revs 387-388, row 55 in review round 1, request comment 27237 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:52:16 -05:00
jason.woltjeandClaude Opus 5.5 93bd679069 review(55): Darkwing round 1 build packet (candidate manifest 93fa48c1cdfe)
The full-screen terminal app (#1544): BUILD.md, build.patch over fe5c68fa
(9 files), candidate manifest, the gate logs on 23d566d6 and the pty smoke
script. The candidate itself is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:51:09 -05:00
jason.woltjeandClaude Opus 5.5 23d566d62d queue: rev 386, row 55 in progress (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:29:25 -05:00
jason.woltjeandClaude Opus 5.5 c938b40cbb docs: row 41 landing, decision 77 restart, Q14 forward (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:28:04 -05:00
jason.woltje 558699bc60 queue: row 55 back to briefed after the Q14 hold (sage) 2026-10-11 12:25:14 -05:00
jason.woltjeandClaude Opus 5.5 fe5c68fa03 feat(harness): meta-harness and PM launching off T3 (#1523, row 41)
Slice 1 S6. packages/harness runs a role session under the pi or claude
adapter behind a tool gate, and packages/seat holds the session and its
process. The bus host gains a launch socket: `mosaic bus start
<business> --pm <harness>:<model>` runs the PM without a window, the PM
launches role sessions through the broker within the business file's
`launch.max`, and every launch, refusal and end is a launch-log line and
a broker event. `mosaic talk`, `mosaic launches off|on|list` and
`mosaic stop <run>` are new CLI verbs. packages/bus gains the IPC ops
identity, authorize, refuse, endLaunch and credentialStatus, plus
Broker.endLaunch (decision 77); no socket verb, event kind or schema
changed. The session environment is an allowlist, and the runner exits
before claiming if a founder credential reaches it.

Filbert built it (agents/filbert/work/s6). Darkwing reviewed on #1523:
round 1 changes (26991/26995), round 2 changes (27004/27010), round 3
changes (27012/27032), round 4 approve (27033/27034) on manifest
08a78972, 42 paths.

Sage's gate on 64b0f9f1 (12:32Z), sequential and teed: node suites
business 60, bus 74, cli 83, control-board 124, conversation 182,
discord 178, harness 56, ledger 78, mosaic 69, queue 149, runs 41,
seat 27, tasks 51, webui 39, every one 0 failed; test-auth 15,
test-conductor 17, test-config 24, test-discord 66,
test-extension-package 18, test-foundation 44, test-queue 27,
test-release 14, test-task 98 and 26 without Docker, all 0 failed.
64b0f9f1..ee7f909b touches none of the 42 paths. It landed after the
Q14 hold ended at 2026-10-11T17:19:23.857Z. The recorded PM-to-coder
run on a scratch data root is still owed before row 41 moves to done.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 12:22:50 -05:00
jason.woltjeandClaude Opus 5.5 ee7f909bd3 docs(sessions): Q14 not run, row 35 closed, row 41 gate (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:53:06 -05:00
jason.woltje 2faa973e84 queue: rev 384, row 35 done on Jason's "close 35" (sage) 2026-10-11 10:52:40 -05:00
jason.woltjeandClaude Opus 5.5 64b0f9f1dd docs(sessions): row 54 rounds 1 and 2 build (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:12:19 -05:00
jason.woltje 87f3a7d7d3 queue: rev 383, row 54 done (sage) 2026-10-10 17:12:10 -05:00
jason.woltjeandClaude Opus 5.5 89854be58b docs(build-log): row 54 rounds 1 and 2 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:11:47 -05:00
jason.woltjeandClaude Opus 5.5 10225d6185 review(54): Dewey rounds 1 and 2 build packet (candidate afb2ae0e)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:11:26 -05:00
jason.woltjeandClaude Opus 5.5 cbd79cf666 feat(webui): read-only Queue, Business and Settings views (#1543, row 54)
The Console gains three read-only views. Queue lists every row from
rows() in packages/queue (lead decision 83: the same lock-free read as
`queue list`, writing nothing), run in a child with a timeout and an
output cap; a row page shows the full row. Business shows names, an
allowlist of vars, arbiters, authority per action and credential
metadata (service, account, role, date, never a value, file or
variable). Settings shows RELEASE, the board origin and the notifier
binding. Every route is GET only, and every string in a body or
refusal passes a redactor: the config directory and dataRoot become
<config> and <dataRoot>, other absolute, ~/ and file:// paths <path>,
and 17 to 20 digit runs <id>. A queue-read that fails to start sends a
fixed message, never node's stderr.

Four fixes to HEAD behaviour, each with a test: the bus view's refresh
timer is cleared at render, a same-page refresh keeps focus on the H1,
#conv-pick is emptied when a conversation opens, and error() returns
focus to <main> only when something had focus. Filbert's T8 tests the
failed first read.

Dewey built it in two rounds. Round 1 (dba2429e) got changes from
Filbert (27185: After rendered [object Object], ~/ and :/ paths leaked)
and Darkwing (27186: Arbiters always none, queue-read import failure
leaked a file:// path and stack). Round 2 (afb2ae0e) was approved by
Filbert (27190) and Darkwing (27191, correction 27192). Sage's gate on
d64f434f plus the candidate: 13 package node suites 0 failed (queue
149, webui 39), every scripts/test-*.sh 0 failed (task 98/0 with
Docker, 26/0 without; release 14/0), build-tokens --check current.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:11:17 -05:00
jason.woltjeandClaude Opus 5.5 0a329be00b docs(sessions): row 54 round 2 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:10:25 -05:00
jason.woltjeandClaude Opus 5.5 bfbcf626de review(row54): round 2 packet, approve (darkwing)
Comment 27191 on #1543, correction 27192. Candidate afb2ae0e.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:10:15 -05:00
jason.woltjeandClaude Opus 5.5 6921d77469 queue: rev 382, row 54 round 2 approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:10:11 -05:00
jason.woltjeandClaude Opus 5.5 57e8650733 docs(sessions): row 54 round 2 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:04:54 -05:00
jason.woltjeandClaude Opus 5.5 6ee3e1d3e5 queue: rev 381, row 54 round 2 approve (filbert)
Comment 27190 on #1543; candidate afb2ae0e.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 17:04:42 -05:00
jason.woltjeandClaude Opus 5.5 60f90a9416 docs(lead): decision 84, a Console refusal forgets only the refusing source (#1554)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 16:18:26 -05:00
jason.woltjeandClaude Opus 5.5 6637bca54b queue: revs 378 to 380, row 54 in progress then in review round 2 (dewey)
Round 2 answers Filbert 27185 and Darkwing 27186. Candidate manifest
afb2ae0e, request comment 27187, packet comment 27188. No other seat's
revisions pending.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 16:16:56 -05:00
jason.woltjeandClaude Opus 5.5 8f4f536d3a docs(sessions): row 54 round 1 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:48:06 -05:00
jason.woltjeandClaude Opus 5.5 8a3ded5335 review(row54): round 1 packet, changes (darkwing)
Review of #1543 candidate dba2429e: gate green with Docker, 31 mutants
(26 killed), Node, import and Chromium probes. Posted as comment 27186.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:47:54 -05:00
jason.woltjeandClaude Opus 5.5 712baf49ba queue: rev 377, row 54 round 1 changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:47:51 -05:00
jason.woltjeandClaude Opus 5.5 89cafe9ae6 docs(sessions): row 54 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:30:17 -05:00
jason.woltjeandClaude Opus 5.5 cd76805549 queue: rev 376, row 54 round 1 changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:30:04 -05:00
jason.woltjeandClaude Opus 5.5 c6b214e5f0 queue: revs 373 to 375, row 54 in progress then in review round 1 (dewey)
Candidate manifest dba2429e, 14 files on d64f434f. Request comment 27182,
packet comment 27183 on #1543. No other seat's revision is in this snapshot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 15:01:25 -05:00
jason.woltjeandClaude Opus 5.5 d64f434f14 docs(sessions): row 53 rounds 1 to 3 build (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:50:35 -05:00
jason.woltje 58bfd59a1b queue: rev 372, row 53 done (sage) 2026-10-10 14:50:30 -05:00
jason.woltjeandClaude Opus 5.5 172472b239 docs(build-log): row 53 rounds 1 to 3 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:50:04 -05:00
jason.woltjeandClaude Opus 5.5 abf71275ab review(53): Dewey rounds 1 to 3 build packet (candidate 2562c05d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:49:52 -05:00
jason.woltjeandClaude Opus 5.5 1bdb6f9b13 feat(webui): console tokens and shell restyle (#1542, row 53)
The console takes the design package's tokens.css and icons.svg as byte
copies, guarded by a test, and a new shell.css holds the components:
command bar with freshness line, section list (a strip that scrolls
inside itself below 760px), dense table, inspector, status mark, chips,
copy command, toast, Ctrl+K palette, not found, banners and skeleton
rows. JetBrains Mono 2.304 ships from the official archive with sha256
and OFL. Harbor following the system setting is the default palette.
A refusal (403, not-configured, no-bus-host, or the board's 403) fails
closed everywhere: no kept rows, counts, inspector, palette entries or
conversation history, and a later 500 or 503 doesn't bring them back.

Dewey built it in three rounds. Round 1 (75569953) and round 2
(78c3aef9) got changes from Darkwing (27148, 27165) and Filbert (27143,
27163) for refusal leaks outside the main view; round 3 (2562c05d) was
approved by Darkwing (27171) and Filbert (27174). Sage's gate on
ea080fd8 plus the candidate: runs 41/0, queue 148/0, webui 27/0,
conversation 182/0, control-board 124/0, build-tokens --check current,
every scripts/test-*.sh 0 failed (task 98/0 and release 14/0 with
Docker). The round 2 gate had one discord engine timing failure in the
full run, 66/0 alone, filed as #1553.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:49:48 -05:00
jason.woltjeandClaude Opus 5.5 1bb8ab54fe docs(sessions): row 53 round 3 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:49:05 -05:00
jason.woltjeandClaude Opus 5.5 05222ed405 queue: rev 371, row 53 round 3 approve (filbert, #1542 comment 27174, candidate 2562c05d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:48:50 -05:00
jason.woltjeandClaude Opus 5.5 db90655e0f docs(sessions): row 53 round 3 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:42:45 -05:00
jason.woltjeandClaude Opus 5.5 f5083132f5 review(53): Darkwing round 3, approve (#1542 comment 27171)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:42:32 -05:00
jason.woltjeandClaude Opus 5.5 f134348672 queue: rev 370, row 53 round 3 approve (darkwing, #1542 comment 27171, candidate 2562c05d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:42:28 -05:00
jason.woltjeandClaude Opus 5.5 ea080fd85c queue: revs 367-369, row 53 round 3 in-review (dewey, #1542 comment 27168, candidate 2562c05d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:19:33 -05:00
jason.woltjeandClaude Opus 5.5 03969219a9 docs(sessions): row 53 round 2 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:05:40 -05:00
jason.woltjeandClaude Opus 5.5 d277234237 review(53): Darkwing round 2, request changes (#1542 comment 27165)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:05:29 -05:00
jason.woltjeandClaude Opus 5.5 e2b4e6f5da queue: rev 366, row 53 round 2 changes (darkwing, #1542 comment 27165, candidate 78c3aef9)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:05:25 -05:00
jason.woltjeandClaude Opus 5.5 0a9ace0f58 docs(sessions): row 53 round 2 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 13:52:35 -05:00
jason.woltjeandClaude Opus 5.5 70479bd87a queue: rev 365, row 53 round 2 changes (filbert, #1542 comment 27163, candidate 78c3aef9)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 13:52:17 -05:00
jason.woltjeandClaude Opus 5.5 fb77f5c99a queue: revs 362-364, row 53 round 2 in-review (dewey, #1542 comment 27156, candidate 78c3aef9)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 13:20:50 -05:00
jason.woltjeandClaude Opus 5.5 2d4d4e7dbc docs(sessions): row 53 round 1 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:59:16 -05:00
jason.woltjeandClaude Opus 5.5 70768fe548 review(53): Darkwing round 1, request changes (#1542 comment 27148)
Refusal leaves board data in the inspector, project tree, footer and
scan line, and bus data in the Inbox count and the Ctrl+K palette.
Gate green, 13 of 20 mutants killed, fonts match the upstream archive.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:59:06 -05:00
jason.woltjeandClaude Opus 5.5 71af13092b queue: rev 361, row 53 round 1 changes (darkwing, #1542 comment 27148, candidate 75569953)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:59:01 -05:00
jason.woltjeandClaude Opus 5.5 98a59aee21 docs(sessions): row 53 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:53:12 -05:00
jason.woltjeandClaude Opus 5.5 ada8bd4ccc queue: rev 360, row 53 round 1 changes (filbert, #1542 comment 27143, candidate 75569953)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:53:01 -05:00
jason.woltjeandClaude Opus 5.5 52c5a1f8e2 docs(decisions): 83, row 54 may add a rows() read to packages/queue
Dewey's row 54 stop: store.show costs ~450 ms a row. Row 54 adds
export function rows(opts) plus a test; queue suites join its gate.
Path redaction and Dewey's recorded view choices accepted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:36:25 -05:00
jason.woltjeandClaude Opus 5.5 bbfded43c7 queue: revs 358-359, row 53 round 1 in-review (dewey, #1542 comment 27138, candidate 75569953)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:30:08 -05:00
jason.woltjeandClaude Opus 5.5 8a7871ff00 docs(sessions): row 56 rounds 1 and 2 build (rocko) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:23:29 -05:00
jason.woltje fdf23ec5c0 queue: rev 357, row 56 done (sage) 2026-10-10 12:23:29 -05:00
jason.woltjeandClaude Opus 5.5 938677363a docs(build-log): row 56 rounds 1 and 2 (rocko) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:22:54 -05:00
jason.woltjeandClaude Opus 5.5 8dff857f64 review(56): Rocko round 1 and round 2 build packet (packet manifest 5eb2706d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:22:54 -05:00
jason.woltjeandClaude Opus 5.5 71d874764a feat(runs): read-only runs and releases reader module (#1545, row 56)
New packages/runs reads run records, result.json, the release pointer
and the activation log without writing, pruning or following a link out
of the data root. mosaic-task.mjs list and show use it, so a malformed
result.json or a run that is a regular file no longer crashes them.
Deliberate deltas are listed in the README.

Rocko built it. Round 1 (ed3c5392) was approved with notes by Darkwing
(27115) and Filbert (27116); round 2 (2727198f, tests and wording only)
was approved by Darkwing (27123) and Filbert (27124). Sage's gate on
c9a25a47 plus the candidate: runs 41/0, queue 148/0, webui 22/0,
conversation 182/0 (181/1 in the full run on the K12 cgroup timing
test under load, 182/0 alone), control-board 124/0, every
scripts/test-*.sh 0 failed (task 98/0 with Docker, 26/0 without).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:22:37 -05:00
jason.woltjeandClaude Opus 5.5 61ec09822f docs(sessions): row 56 round 2 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:20:33 -05:00
jason.woltjeandClaude Opus 5.5 a7be12dbdf queue: rev 356, row 56 round 2 approve (filbert, #1545 comment 27124, candidate 2727198f)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:20:25 -05:00
jason.woltjeandClaude Opus 5.5 3265383f3f docs(sessions): row 56 round 2 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:17:22 -05:00
jason.woltjeandClaude Opus 5.5 8e46a8619d review(56): Darkwing round 2 packet, approve (#1545 comment 27123)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:17:11 -05:00
jason.woltjeandClaude Opus 5.5 cab5f10e11 queue: rev 355, row 56 round 2 approve (darkwing, #1545 comment 27123, candidate 2727198f)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:17:05 -05:00
jason.woltjeandClaude Opus 5.5 c9a25a4787 docs(lead): decision 82, Q14 F9 answered by gate V; #1552 brief deferred to after the hold
Corrects decision 80's journal claim as a new entry. Brief for the
adapter ready line (#1552) is queued after the Q14 hold ends.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:15:31 -05:00
jason.woltje f1dfb4b505 queue: revs 350-354, row 56 round 2 in-review (rocko 350-352, request re-posted as jarvis 353-354, #1545 comment 27120, candidate 2727198f) 2026-10-10 12:10:23 -05:00
jason.woltjeandClaude Opus 5.5 75ab164698 docs(sessions): row 56 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:01:44 -05:00
jason.woltjeandClaude Opus 5.5 060c3b4d77 queue: rev 349, row 56 round 1 approve (filbert, #1545 comment 27116, candidate ed3c5392)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:01:33 -05:00
jason.woltjeandClaude Opus 5.5 f46d7a3495 docs(sessions): row 56 round 1 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:00:55 -05:00
jason.woltjeandClaude Opus 5.5 6417a3e7a6 review(56): Darkwing round 1 packet, approve (#1545 comment 27115)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:00:44 -05:00
jason.woltjeandClaude Opus 5.5 12e18a6063 queue: rev 348, row 56 round 1 approve (darkwing, #1545 comment 27115)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 12:00:40 -05:00
jason.woltje 14b2f21490 queue: revs 343-347, row 56 claimed and in review (rocko 343-345, request failed without a credential; sage 346-347, request posted as jarvis, #1545 comment 27113) 2026-10-10 11:53:17 -05:00
jason.woltje 923957e2d2 queue: rev 342, row 55 blocked on the Q14 hold (sage) 2026-10-10 11:39:47 -05:00
jason.woltjeandClaude Opus 5.5 dc96f87b17 queue: rev 341, row 53 claimed (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 11:38:54 -05:00
jason.woltjeandClaude Opus 5.5 ff10c04d67 docs(sessions): design implementation queue (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 11:37:39 -05:00
jason.woltje a81915e825 queue: revs 323-340, rows 53-61 added and briefed (sage, decision 81) 2026-10-10 11:37:03 -05:00
jason.woltjeandClaude Opus 5.5 f824fcc9e5 docs(plans): design implementation brief, lead decisions 80 and 81
Brief for rows 53 to 61 implementing docs/design (Jason's directive via
Mos, DECISIONS 1a312ff). Decision 80 records the Q14 window, rev7 item 6
checks and the freeze; decision 81 the row shape and the design's open
decisions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 11:35:37 -05:00
jason.woltjeandClaude Opus 5.5 811e7ba562 docs(design): handoff package for implementing the design
Jason accepted the draft 1 direction on 2026-10-10; the refined Relay
mark stays as a placeholder. Adds IMPLEMENTING.md (gap map against
row 40 and the bus/CLI modules, strings, boundaries, acceptance,
proposed rows for Sage), tokens.json and generated tokens.css using the
webui's existing names, icons.svg, and tools/build-tokens.mjs and
tools/check-design.mjs. Coverage statuses corrected against the code
(digest implemented; mosaic stop is row 41; priority and agent state
not in the bus reads). No packages/ change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 11:22:06 -05:00
jason.woltjeandClaude Opus 5.5 d40a35393b docs(sessions): row 48 round 1 build (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:56:09 -05:00
jason.woltje 3e4cdf6d02 queue: rev 322, row 48 done (sage) 2026-10-10 03:56:08 -05:00
jason.woltjeandClaude Opus 5.5 99d9cff92d docs(build-log): row 48 round 1 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:55:40 -05:00
jason.woltjeandClaude Opus 5.5 f7c2d5cfe3 test: commit.test close wait and test-task recall Docker skip (#1534, row 48)
pausedCommit waits for the child's `close`, not `exit`, so the
`cannot lock ref 'HEAD'` match sees git's stderr. The test-task live
recall pair runs only when `docker info` succeeds, like the three live
blocks above it; otherwise it prints a skip line and counts as neither
a pass nor a failure.

Dewey built it. Darkwing (27099) and Filbert (27100) approved round 1
on manifest 285a4ec1 (3 files). Sage's gate on 560373c3 plus the
candidate: queue 148/0, conversation 182/0, webui 22/0, control-board
124/0, every scripts/test-*.sh 0 failed; test-task 98/0 with Docker and
26/0 without, with the recall-pair skip line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:55:24 -05:00
jason.woltjeandClaude Opus 5.5 a07b37840a docs(sessions): row 48 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:50:10 -05:00
jason.woltjeandClaude Opus 5.5 560373c3f6 queue: rev 321, row 48 round 1 approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:50:00 -05:00
jason.woltjeandClaude Opus 5.5 2a56c31c08 docs(sessions): row 48 round 1 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:49:19 -05:00
jason.woltjeandClaude Opus 5.5 1355592f2b docs(queue-48): darkwing round 1 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:49:12 -05:00
jason.woltjeandClaude Opus 5.5 85fac90139 queue: rev 320, row 48 round 1 approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:49:12 -05:00
jason.woltjeandClaude Opus 5.5 5e87c41f16 queue: revs 317-319, row 48 claimed and in review round 1 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:41:47 -05:00
jason.woltje 2d935f594c queue: rev 316, row 48 assigned to dewey (sage) 2026-10-10 03:33:40 -05:00
jason.woltjeandClaude Opus 5.5 ae5373c749 docs(sessions): row 52 round 1 build (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:32:32 -05:00
jason.woltje c4a2b71f2c queue: rev 315, row 52 done (sage) 2026-10-10 03:32:23 -05:00
jason.woltjeandClaude Opus 5.5 32df6036c5 docs(build-log): row 52 round 1 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:31:59 -05:00
jason.woltjeandClaude Opus 5.5 0aa5e7b51b feat(conversation): release at engine exit through a proven engine-exit stop (#1538, row 52)
The controller now starts a CHAT-01 `engine-exit` stop when the engine
exits on its own. engineExitCohort proves the cohort from the shim's
recorded engine identity (pid, start ticks, boot) and an empty member
list, under a 5 s deadline that frees the escalation slot. A proven stop
releases the scope; anything else ends `uncertain`. close() no longer
signals a stopped binding.

Dewey built it. Filbert (27087) and Darkwing (27088) approved round 1 on
CHAT-01 cc83ee4f and manifest deac7434 (6 files). Sage's gate on
c92cfb8f plus the candidate: conversation 182/0, webui 22/0,
control-board 124/0, every scripts/test-*.sh 0 failed (task 98/0),
chat-01 PASS.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:31:39 -05:00
jason.woltjeandClaude Opus 5.5 c92cfb8faf docs(sessions): row 52 round 1 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:25:33 -05:00
jason.woltjeandClaude Opus 5.5 aeab166edb docs(queue-52): darkwing round 1 review, approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:25:24 -05:00
jason.woltjeandClaude Opus 5.5 886367ed55 queue: rev 314, row 52 round 1 approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:25:21 -05:00
jason.woltjeandClaude Opus 5.5 72000fe717 docs(sessions): row 52 round 1 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:17:44 -05:00
jason.woltjeandClaude Opus 5.5 2db99a70e6 queue: rev 313, row 52 round 1 approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:17:34 -05:00
jason.woltjeandClaude Opus 5.5 56e08721b4 queue: revs 310-312, row 52 claimed and round 1 in review (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 03:03:52 -05:00
jason.woltjeandClaude Opus 5.5 cc83ee4f6a docs(chat-01): engine-exit stop mode (#1538, row 52)
A server-started stop for an engine that exits on its own: no request,
no confirmation; it closes admission and supersedes the current stop
(H17), but refuses stop-owned under a force stop or another engine
exit. The binding follows it like a force stop at startStop,
advance-stop and confirm-stopped, and its proof needs a member.
Dispatched or acknowledged input goes delivery-unknown; working input
keeps its state. Five lifecycle sequences and two shape cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:55:57 -05:00
jason.woltjeandClaude Opus 5.5 945440dbb4 docs(sessions): row 51 rounds 1-2 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:07:19 -05:00
jason.woltje c99af837cc queue: rev 309, row 51 done (sage) 2026-10-10 02:07:13 -05:00
jason.woltjeandClaude Opus 5.5 8f78aa2bf4 docs(build-log): row 51 rounds 1-2 (dewey) and landing (sage) (#1537)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:06:46 -05:00
jason.woltjeandClaude Opus 5.5 717e3404f4 feat(conversation): release a stopped cohort scope on start and recover, no kill after a proven stop (#1537)
Row 51, owner Dewey, candidate c34039dc (5 files). A claim recorded
stopped on a cohort proof whose scope is still listed is released on
start (classify's stopped branch and the free path's session and seat
heads) and on a confirmed recover, after every recover check. A release
that ended unavailable or still listed is retried; the proof check stays
in #releaseScope. close({ killEngine: true }) no longer signals the
recorded PID after a proven stop. Tests R7-R12 kill relok, closeany,
noprooffkind and nopush; R9 pins the recover order.

Reviews: Filbert approve (27070, 27074), Darkwing changes then approve
(27071, 27075). Gate green on 032b5408 plus the candidate.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:06:20 -05:00
jason.woltjeandClaude Opus 5.5 032b54086b docs(sessions): row 51 round 2 review, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:00:49 -05:00
jason.woltjeandClaude Opus 5.5 e5c32b81da review(queue-51): round 2, approve, recover release after the confirmation (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:00:39 -05:00
jason.woltjeandClaude Opus 5.5 3b5da5f0a2 queue: rev 308, row 51 round 2 review record, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 02:00:36 -05:00
jason.woltjeandClaude Opus 5.5 288c81d449 docs(sessions): row 51 round 2 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:52:46 -05:00
jason.woltjeandClaude Opus 5.5 535fcc5f6a queue: revs 304-307, row 51 round 2 review record, approve (filbert)
Sweeps Dewey's uncommitted revs 304-306 (dewey-r51-rework-2,
dewey-51-review-2, dewey-51-review-2.outcome: row 51 round 2 in review).
Rev 307 is mine: filbert-r51-review-2, approve, #1537 comment 27074.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:52:39 -05:00
jason.woltjeandClaude Opus 5.5 b7e9efb766 docs(sessions): row 51 round 1 review, changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:12:00 -05:00
jason.woltjeandClaude Opus 5.5 2f60daa358 review(queue-51): round 1, changes, recover release before confirmation (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:11:47 -05:00
jason.woltjeandClaude Opus 5.5 55233456c0 queue: rev 303, row 51 round 1 review record, changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:11:42 -05:00
jason.woltjeandClaude Opus 5.5 42d86aa180 docs(sessions): row 51 round 1 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:03:19 -05:00
jason.woltjeandClaude Opus 5.5 7ec4be94ee queue: rev 302, row 51 round 1 review record, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 01:03:11 -05:00
jason.woltjeandClaude Opus 5.5 d947ec1113 queue: revs 298-301, row 51 round 1 in review (dewey)
Rev 299 is a failed first request: no credential file was set, so nothing
was sent. Rev 301 posted the request as comment 27068.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:48:23 -05:00
jason.woltjeandClaude Opus 5.5 bbb2167f2a docs(sessions): row 52 brief takes Darkwing's CHAT-01 points (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:25:03 -05:00
jason.woltje 2c2995bef2 queue: revs 296-297, rows 51 and 52 briefs re-pinned for Darkwing's CHAT-01 points (sage) 2026-10-10 00:25:03 -05:00
jason.woltjeandClaude Opus 5.5 481bc081cd docs(plans): row 52 brief takes Darkwing's four CHAT-01 points
No engine-exit stop over an unfinished force stop, the binding follows
the stop at all three force-stop mode checks, the proof names the engine
as a member, and the proof has a deadline that frees the escalation slot.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:24:35 -05:00
jason.woltjeandClaude Opus 5.5 b9fcb568ae docs(sessions): decision 79, row 52 split from row 51 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:22:16 -05:00
jason.woltje 48499ea9bd queue: revs 293-295, row 52 CHAT-01 engine-exit stop (#1538) added and briefed, row 51 brief re-pinned (sage, decision 79) 2026-10-10 00:22:07 -05:00
jason.woltjeandClaude Opus 5.5 5a7d5f1c2a docs(plans): decision 79, CHAT-01 engine-exit stop mode as row 52
Row 51 needs a CHAT-01 rule changed to record `stopped` at engine exit.
The engine-exit proof moves to its own row with a CHAT-01 amendment and
contract review; row 51 keeps the crash window, the close PID fix and
the mutant tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:21:21 -05:00
jason.woltjeandClaude Opus 5.5 2edee47b12 queue: rev 292, row 51 in progress (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:17:12 -05:00
jason.woltjeandClaude Opus 5.5 de7d093c23 docs(sessions): row 50 round 1 in review (dewey) and landed (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:15:10 -05:00
jason.woltje dfc5af1820 queue: revs 289-291, row 50 done (sage), row 51 cohort release follow-ups (#1537) added and briefed 2026-10-10 00:15:05 -05:00
jason.woltjeandClaude Opus 5.5 2b279ae6c3 docs(build-log): row 50 in review (dewey) and landed (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:14:13 -05:00
jason.woltjeandClaude Opus 5.5 a9cc522a0c conversation: release a cohort scope after a proven stop (row 50, #1536)
releaseCohort sends `release` only to a scope shim that answers hello
with the recorded invocation ID, then waits for systemd to drop the
unit. The controller releases once per claim, after a proven force stop
and on close of a proven-stopped binding; every uncertain path keeps the
scope as evidence. The harness's killShims refuses units outside
^mosaic-chat-, R5 checks liveShims positively, and K19's wait on
proc.exited is bounded.

Dewey's candidate, manifest 375594fc (8 files), approved by Filbert
(comment 27053) and Darkwing (comment 27055). Normal engine exit still
leaves the scope; the follow-up is #1537.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:14:00 -05:00
jason.woltjeandClaude Opus 5.5 ef70ba6a64 docs(plans): brief for the cohort release follow-ups after row 50
Engine exit proof and release, the crash window and release retry,
close's SIGKILL of a recorded PID, and the reviewers' surviving mutants
(relok, closeany, noprooffkind, nopush).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:10:38 -05:00
jason.woltjeandClaude Opus 5.5 ac7acd6852 docs(sessions): row 50 round 1 review, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:09:26 -05:00
jason.woltjeandClaude Opus 5.5 5a55109d89 review: row 50 round 1, approve (darkwing)
Packet for #1536 comment 27055: suites, 13 mutants, review notes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:09:15 -05:00
jason.woltjeandClaude Opus 5.5 f8d27f9650 queue: rev 288, row 50 round 1 review record, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:09:11 -05:00
jason.woltjeandClaude Opus 5.5 1c397c8f5c docs(sessions): row 50 round 1 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 00:00:09 -05:00
jason.woltjeandClaude Opus 5.5 4c8952b723 queue: rev 287, row 50 round 1 review record, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:59:59 -05:00
jason.woltjeandClaude Opus 5.5 a400f52184 queue: revs 285-286, row 50 round 1 in review (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:47:58 -05:00
jason.woltjeandClaude Opus 5.5 16a8d0385a queue: rev 284, row 50 in progress (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:13:46 -05:00
jason.woltjeandClaude Opus 5.5 aaf6f020c3 docs(sessions): row 47 round 1 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:11:48 -05:00
jason.woltje 7134a46192 queue: rev 283, row 47 done (landed ee5597e1) 2026-10-09 23:11:42 -05:00
jason.woltjeandClaude Opus 5.5 80c7e573ac docs(build-log): row 47 round 1 (dewey) and landing (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:11:06 -05:00
jason.woltjeandClaude Opus 5.5 ee5597e112 test(conversation): Mr flows test and no shim left after the suite (#1533)
Row 47, the S5 follow-up. flows.test gains a test for a hold set inside
held input, which kills mutant Mr. claim W5 and W13 reap their own
force-stopped scopes, K19 kills its scope if its kill or release fails,
and the harness gains liveShims, killShims and shimsGone with a final
sweep in reap. claim, cohort and races each end by asserting no shim
outlives the file.

Author: Dewey. Approved round 1 by Filbert (comment 27041) and Darkwing
(comment 27043) on candidate 0587e393. The src side, where a force stop
never sends release, is row 50 (#1536).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:10:52 -05:00
jason.woltjeandClaude Opus 5.5 0dc9218dc4 docs(sessions): row 47 round 1 review, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:06:49 -05:00
jason.woltjeandClaude Opus 5.5 3b5024216f docs(review): row 47 round 1 review packet, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:06:17 -05:00
jason.woltjeandClaude Opus 5.5 46b87c5df2 queue: rev 282, row 47 round 1 review record, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:06:14 -05:00
jason.woltjeandClaude Opus 5.5 124fd57042 docs(sessions): row 47 round 1 review, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 23:00:01 -05:00
jason.woltjeandClaude Opus 5.5 05ea5f45d8 queue: rev 281, row 47 round 1 review record, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:59:48 -05:00
jason.woltjeandClaude Opus 5.5 4083080d12 queue: revs 279-280, row 50 cohort scope release (#1536) added and briefed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:50:20 -05:00
jason.woltjeandClaude Opus 5.5 d378a445d9 docs(plans): brief for releasing the cohort scope after a force stop
Dewey's row 47 finding: forceStopCohort and controller close never send
release, so each force stop leaves a mosaic-chat scope with an idle shim.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:49:45 -05:00
jason.woltjeandClaude Opus 5.5 10351f6a6d queue: revs 277-278, row 47 round 1 in review (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:48:11 -05:00
jason.woltjeandClaude Opus 5.5 4ee3dc8a86 docs: session line, row 41 round 4 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:40:37 -05:00
jason.woltjeandClaude Opus 5.5 e45f0c98ff docs(sessions): row 41 round 4 approved (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:40:34 -05:00
jason.woltjeandClaude Opus 5.5 c38bc54663 docs(review): row 41 round 4 review packet, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:39:59 -05:00
jason.woltjeandClaude Opus 5.5 9f761f126d queue: rev 276, row 41 round 4 approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:39:51 -05:00
jason.woltjeandClaude Opus 5.5 21e0f1a31c docs: row 41 round 4 in review, build-log and session line (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:31:20 -05:00
jason.woltjeandClaude Opus 5.5 6f102777a6 queue: revs 274-275, row 41 round 4 in review (filbert)
Candidate agents/filbert/work/s6/candidate-manifest.sha256 (08a78972),
request comment 27033 on #1523.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:30:53 -05:00
jason.woltjeandClaude Opus 5.5 55bff3b274 docs(s6): row 41 round 4 candidate packet, R5 fix (filbert)
Candidate manifest 08a78972 (42 files). Gate at 0a4c8f13.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:30:16 -05:00
jason.woltjeandClaude Opus 5.5 7ab2f92d02 queue: rev 273, row 47 started (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:26:53 -05:00
jason.woltjeandClaude Opus 5.5 0a4c8f13b0 docs(build-log): row 41 round 4 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:23:09 -05:00
jason.woltjeandClaude Opus 5.5 0b855ae1fe queue: rev 272, row 41 round 4 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:22:53 -05:00
jason.woltjeandClaude Opus 5.5 5dbb81b41b docs: session line, row 41 round 3 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:19:34 -05:00
jason.woltjeandClaude Opus 5.5 974da6edd5 docs(review): row 41 round 3 review packet, changes (darkwing)
R4 holds. R5: a relative Pi path resolves against the given workspace in
the gate and against the real path in Pi, so a workspace on a symlink lets
.. reach outside. Comment 27032, queue rev 271.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:19:02 -05:00
jason.woltjeandClaude Opus 5.5 43909c2078 queue: rev 271, row 41 round 3 review record, changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:18:42 -05:00
jason.woltjeandClaude Opus 5.5 01314446cc queue: revs 269-270, rows 47 and 48 briefed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:13:48 -05:00
jason.woltjeandClaude Opus 5.5 f6c8f9671c docs(sessions): Sage lands row 40; Dewey's row 40 lines
Dewey's four row 40 SESSIONS lines (built, round 2, round 3, approved)
and my landing line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:10:48 -05:00
jason.woltjeandClaude Opus 5.5 e345640e79 queue: revs 265-268, row 40 done (#1522, candidate d0aa0ded landed as 08b428ec); rows 47-49 follow-ups (#1533-#1535)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:10:37 -05:00
jason.woltjeandClaude Opus 5.5 5488ff62a8 docs(plans): brief for the S5 follow-up, test hygiene and bus comment cursor rows
Three follow-up rows from the row 40 and row 41 reviews: the Mr flows
test and shim cleanup (Dewey), commit.test exit wait and the test-task
Docker skip, and persisting the bus comment counts across restarts
(after S6).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:07:19 -05:00
jason.woltjeandClaude Opus 5.5 48f76e7674 docs(build-log): row 40 landed; Dewey's round 1-3 entries and manifests
Dewey's four BUILD-LOG entries for row 40 (started, round 1, round 2,
round 3) and their three candidate manifests under
agents/dewey/work/queue-40/, plus my landing entry with the gate counts
and the Mr follow-up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:06:14 -05:00
jason.woltjeandClaude Opus 5.5 08b428ecf1 feat(webui,conversation): S5 WebUI views and CHAT-03 follow-ups (row 40, #1522)
Dewey's round 3 candidate, manifest
agents/dewey/work/queue-40/candidate-manifest-r3.sha256 (d0aa0ded,
27 files, checked OK in the canonical tree).

- WebUI inbox, tasks, agents and trail views, read-only over /api/bus.
  The README says the bus proof ends at the Console process.
- CHAT-03 seal: the engine command is fixed, the engine environment is
  explicit, SEAL_FLAGS has --no-approve, escalating is cleared on throw.
- Terminal input typed after Ctrl-T or Ctrl-O is held. Only the run whose
  own parse set held drains it (T1), and #run catches errors per action.
- DEFERRED keeps N2 and moves F2 to done, citing T1.

Reviews: Filbert approve (comment 27011, rev 260), Darkwing approve
(27013, rev 264). Landing gate on 8cad7722 plus the candidate: webui 22,
conversation 161, control-board 124, every scripts/test-*.sh green,
test-task 98/0. Mutant Mr survives; its flows test is the first
follow-up row.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:05:43 -05:00
jason.woltjeandClaude Opus 5.5 8cad772282 docs: session line, row 40 round 3 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 22:00:33 -05:00
jason.woltjeandClaude Opus 5.5 67630cc41f docs(review): row 40 round 3 review packet, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:59:58 -05:00
jason.woltjeandClaude Opus 5.5 09bcd1a9d0 queue: rev 264, row 40 round 3 review record, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:59:54 -05:00
jason.woltjeandClaude Opus 5.5 74d9810389 docs: row 41 round 3 build-log entry and session line (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:51:25 -05:00
jason.woltjeandClaude Opus 5.5 a8ee6cc1df queue: revs 262-263, row 41 round 3 in-review and review request (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:50:49 -05:00
jason.woltjeandClaude Opus 5.5 0f38236fa6 docs(build): row 41 slice 1 S6 round 3 candidate packet (filbert)
R4: the gate checks a read under every spelling Pi's read would open
(AM/PM U+202F, NFD, U+2019, NFD with U+2019), from the workspace and its
real path, for Pi read and Claude Read. Mr test, launcher test timeouts.
Candidate manifest 9f0593af (42 files), gate at 66b9e082 green but
test-task Docker recall, same on base. Candidate source not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:50:18 -05:00
jason.woltjeandClaude Opus 5.5 66b9e082e9 docs: build-log, row 41 round 3 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:35:14 -05:00
jason.woltjeandClaude Opus 5.5 e211d384cd queue: rev 261, row 41 round 3 started (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:34:58 -05:00
jason.woltjeandClaude Opus 5.5 ea676e8b18 docs: session line, row 40 round 3 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:32:12 -05:00
jason.woltjeandClaude Opus 5.5 a248ec64b3 queue: rev 260, row 40 round 3 review record, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:32:00 -05:00
jason.woltjeandClaude Opus 5.5 0c0c84f7ad docs: session line, row 41 round 2 review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:31:21 -05:00
jason.woltjeandClaude Opus 5.5 2d36c6ff61 docs: row 41 round 2 review packet, changes (darkwing)
R4: Pi read opens a spelling variant of a missing name, which the gate
never checked; a variant-named workspace link reads outside. Comment 27010.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:30:14 -05:00
jason.woltjeandClaude Opus 5.5 c26feca7c3 queue: rev 259, row 41 round 2 review record, changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:30:09 -05:00
jason.woltjeandClaude Opus 5.5 b7362672dc queue: revs 256-258, row 40 round 3 in-progress→in-review and review request (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:25:45 -05:00
jason.woltjeandClaude Opus 5.5 86b22cc2e6 docs: session line, row 40 round 2 review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:00:23 -05:00
jason.woltjeandClaude Opus 5.5 27805a4e96 queue: rev 255, row 40 round 2 review record, changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:00:02 -05:00
jason.woltjeandClaude Opus 5.5 ac7dae3e9e docs: row 41 round 2 build-log entry and session line (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:54:25 -05:00
jason.woltjeandClaude Opus 5.5 b887ee1ef1 queue: revs 252-254, row 41 round 2 in-review and review request (filbert)
Row 41 in-review→in-progress→in-review, round 2 on #1523, candidate
manifest fd21bdc2...eb02, request posted as comment 27004. No other
seat's revs pending.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:53:53 -05:00
jason.woltjeandClaude Opus 5.5 779e97805a docs(build): row 41 slice 1 S6 round 2 candidate packet (filbert)
R1 request ids on host-broker IPC, R2 launch.sock close bound, R3 dangling
symlinks refused, notes 1-5. 42 files, +4679/-63, base 915e00e5, gate at
8dd5ff00. Candidate manifest digest fd21bdc2...eb02. Source uncommitted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:53:06 -05:00
jason.woltjeandClaude Opus 5.5 54cb7ab8f8 docs: session line, row 41 and row 40 reviews (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:50:10 -05:00
jason.woltjeandClaude Opus 5.5 c31bb13d1e docs(review): row 40 slice 1 S5 round 2 review, changes (darkwing)
Candidate 5cbf22c0, comment 27001 on #1522. T1 blocks: the terminal's
input hold is drained by an earlier chunk's run, so text typed after
Ctrl-G then Ctrl-T is refused and cleared. Suites, probes and 16 mutants
(13 killed; the 3 survivors checked by hand) in the packet.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:48:54 -05:00
jason.woltjeandClaude Opus 5.5 753e71ee05 queue: rev 251, row 40 round 2 review changes requested (darkwing)
Verdict on candidate 5cbf22c0 (comment 27001 on #1522): T1, input held
behind a Ctrl-T is drained by an earlier chunk's run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:48:48 -05:00
jason.woltjeandClaude Opus 5.5 8dd5ff004b queue: revs 248-250, row 40 round 2 in-progress→in-review and review request (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:26:36 -05:00
jason.woltjeandClaude Opus 5.5 f19787ee60 docs(review): row 41 slice 1 S6 round 1 review, changes (darkwing)
R1 and R3 block: a late broker reply shifts later replies on the host IPC
channel, and a dangling workspace symlink passes the gate. R2 (close waits
on an open launch.sock client) should be fixed in the same round. Probes,
mutants and suite outputs alongside. Comment 26995 on #1523, queue rev 247.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:21:28 -05:00
jason.woltjeandClaude Opus 5.5 e44d96e29e queue: rev 247, row 41 round 1 review changes requested (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:21:17 -05:00
jason.woltjeandClaude Opus 5.5 9ed25be9db docs: session line, row 40 S5 review round 1 changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:06:02 -05:00
jason.woltjeandClaude Opus 5.5 375135853a queue: rev 246, row 40 round 1 review changes requested (filbert)
Verdict comment 26993 on #1522, candidate bcc2b772.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 20:05:52 -05:00
jason.woltjeandClaude Opus 5.5 0697ba1258 docs: row 41 S6 round 1 in review, BUILD-LOG after entry and session line (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:57:27 -05:00
jason.woltjeandClaude Opus 5.5 c7a2c7039a queue: revs 244-245, row 41 in-progress→in-review and review request round 1 (filbert)
Request posted as #1523 comment 26991; candidate agents/filbert/work/s6/candidate-manifest.sha256 (9b670e27).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:56:36 -05:00
jason.woltjeandClaude Opus 5.5 9b670e27b2 docs(s6): row 41 slice 1 S6 round 1 candidate packet (filbert)
build.patch dc346027…4454, candidate-manifest 5b3a934d…27f3, base 915e00e5,
41 files, +4302/-55. Gate at 2855e628 in out/: every node suite and every
test-*.sh green except test-task's live recall (needs Docker; the base
fails the same two). The recorded PM launch waits on the Q14 cutover.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:56:01 -05:00
jason.woltjeandClaude Opus 5.5 2855e628ee queue: revs 242-243, row 40 in-progress→in-review and review request round 1 (dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:12:05 -05:00
jason.woltjeandClaude Opus 5.5 1e11100bdb docs(design): Mosaic Stack design draft 1, brand, web console and mosaic TUI
docs/design/ holds the preview page (brand, 13 console views in five
states, 13 terminal screens, 39-feature coverage with status), the
README spec and the Relay mark. Design only; no package code changed.
Open for Jason: default palette and mode, final mark, web resolution,
one mosaic app.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:00:51 -05:00
jason.woltjeandClaude Opus 5.5 ecb7da0e75 docs: lead decision 78, stay on refactor until Jason calls it ready; merge plan waits; row 41 (S6) is the dogfood milestone and gets review priority
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 18:27:10 -05:00
jason.woltjeandClaude Opus 5.5 be65cff380 docs: lead decision 77, row 41 (S6) may change packages/bus and packages/cli; launcher in the trusted host accepted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 18:03:11 -05:00
jason.woltjeandClaude Opus 5.5 b6d2fe2b28 docs: row 41 S6 started (filbert): plan, BUILD-LOG before entry, session line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 18:02:55 -05:00
jason.woltjeandClaude Opus 5.5 fa8bc5c050 config: .mosaic/project.json for the stack project (tracker.project 32, working branch refactor)
Darkwing approved it as is: keys allowed by validateProjectDocument, id
matches the business file's projects.stack, clean-worktree suites green
(business 60/60, cli 66/66, bus 67/67, test-config 24/0). Without it the
bus host gives the broker no task verbs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 18:01:42 -05:00
jason.woltjeandClaude Opus 5.5 915e00e548 docs: lead decision 76, mosaic-bus@mosaic-stack running against tasks.mosaicstack.dev; business file written, project file awaits darkwing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:58:48 -05:00
jason.woltjeandClaude Opus 5.5 c54be5c41b queue: rev 241, row 41 briefed→in-progress (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:56:36 -05:00
jason.woltjeandClaude Opus 5.5 4566458279 queue: revs 238-240, rows 40 and 41 briefed for dewey and filbert (sage); rev 240 is dewey's row 40 start, swept by the snapshot
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:55:57 -05:00
jason.woltjeandClaude Opus 5.5 09a3427f8d docs: BUILD-LOG correction, Sage pushed Dewey's 1f4882d2 unreviewed; checked after the fact, own work dir only
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:55:15 -05:00
jason.woltjeandClaude Opus 5.5 1f4882d2b4 docs(wui): Dewey's design work: brand board, five mockups, slice 1 S5 views and checks
Brand board revisions, the five dashboard mockups with their verify
script and evidence, the slice 1 S5 design note (SLICE1-VIEWS.md, rev 3
plus the S2c section 10 update), the Console mockup of inbox, tasks,
agents and trail (mockups/slice1/) and checks/slice1-verify.mjs (16
checks; check 16 pinned to bus at d27042fa). Also the CHAT-03 progress
log and the 2026-09-26 dirty-tree manifest. Design and evidence only;
no package code. Row 40 (#1522), committed on its own at Sage's
direction, 2026-10-09.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:54:50 -05:00
jason.woltjeandClaude Opus 5.5 557c26ba00 queue: revs 236-237, row 35 gate met by the S3 live run, back to waiting-on-jason for his close (sage, decision 75)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:54:14 -05:00
jason.woltjeandClaude Opus 5.5 f0a30d65ef docs(slice1): S3 live run on tasks.mosaicstack.dev, startup ready and 17/17 steps; row 35 gate met (#1517, lead decision 75)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:53:27 -05:00
jason.woltjeandClaude Opus 5.5 fef08f803a queue: revs 227-235, row 35 gate and gate-owner to sage, blocked on row 38's broker probe, repin slice 1 brief for rows 35, 40-42 after a6382f7b (sage, decision 75)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:51:39 -05:00
jason.woltjeandClaude Opus 5.5 a6382f7bdf docs(slice1): tasks.mosaicstack.dev on Vikunja 2.7.0, runbook sections 2-3 by Sage (row 35, #1517, lead decision 75)
Jason asked for tasks.mosaicstack.dev operational with agents configured in it.
Sage backed it up, tested the restore, pinned 2.7.0 (infra PR #325) and ran
sections 2 and 3 through the API: owner id 4, svc-mosaic-stack id 5, project 32,
bots 6-10, scoped tokens to 2027-01-07. Probes pass. Scope is Mosaic Stack only
(Mos relaying Jason, his Q9 open). OIDC has been broken since a 2026-04-27
NetworkPolicy; infra PR #326 is with ops-01. BUILD-LOG also records the cert
renewal fix and the last-applied annotation slip.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:49:40 -05:00
jason.woltje ecbf59e555 queue: revs 223-226, repin slice 1 brief for rows 35, 40-42; only SR's out-of-scope and gate lines changed (sage, decision 74) 2026-10-09 17:27:42 -05:00
jason.woltje f5192de2c1 queue: rev 222, row 35 note and gate, section 1 done by Sage (lead decision 74, #1517) 2026-10-09 17:25:56 -05:00
jason.woltjeandClaude Opus 5.5 5fe6a051d2 docs(slice1): runbook section 1 run through the Gitea admin API (row 35, #1517, lead decision 74)
Jason ruled that agents run the steps his admin grant to the jarvis
Gitea token covers. Sage created the four mosaic-stack bots (ids
114-117, restricted, non-admin), added them as collaborators (W/W/W/R),
and minted one scoped token each (ids 191-194). The tokens were written
0600 outside the repo. Scripts and receipt are in
agents/sage/work/gitea-setup/. The guide and SR brief now say who
runs which section. Sections 2 to 4 (Vikunja) stay with Jason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:25:08 -05:00
jason.woltje d404a3bab3 queue: rev 220, row 7 note, 2026-10-09 ledger rerun (#1508 comment 26910) 2026-10-09 17:09:07 -05:00
jason.woltjeandClaude Opus 5.5 befd26e366 docs(records): 2026-10-09 ledger rerun for week 2026-09-27 to 2026-10-03, two runs (row 7, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:08:31 -05:00
jason.woltjeandClaude Opus 5.5 814426c6eb docs(sessions): Sage lands row 45
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:29:13 -05:00
jason.woltje f345ae851f queue: rev 219, row 45 done (#1527, candidate 5b067a9d landed as 9cdb6d82) 2026-10-09 09:28:24 -05:00
jason.woltjeandClaude Opus 5.5 5c680166df docs(build-log): row 45 landed; Rocko's round 1 and 2 entries and SESSIONS line
Rocko's three BUILD-LOG entries (row 45 started, round 1 ready, round 2
ready) and their round 2 SESSIONS line, plus my landing entry with the
gate counts and the reviewers' non-blocking follow-ups.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:27:54 -05:00
jason.woltjeandClaude Opus 5.5 2be51cf5b1 chore(sage): drop my trackers-boot copy, landed in packages/cli/tests (row 45)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:27:34 -05:00
jason.woltjeandClaude Opus 5.5 9cdb6d82e3 feat(cli): S4 follow-up, refusal backoff and tracker boot (row 45, #1527)
Rocko's round 2 candidate, packet agents/rocko/work/s4-follow-up/
(build.patch c8cec070, candidate manifest 5b067a9d, 8/8 OK).

- Definite DM refusals wait the full 30-minute cap, counted from the
  journal's last refusal, so five refusals span about two hours before
  gave-up (lead decision 73). Unknown outcomes keep doubling.
- Broker close sends at host.mjs:144/150/184/188 pass a callback, which
  closes the EPIPE window both reviewers found in round 1.
- README documents manual recovery for an open decision.
- trackers-boot test, X9, X14, and Darkwing's round 1 notes 1-4.
- The append type check stays out; Rocko's reason holds (both reviewers
  agree).

Reviews: Darkwing approve (comment 26884), Filbert approve (26886).
Landing gate on b13fef4c plus the patch: every node suite and every
scripts/test-*.sh green, test-task 98/0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:27:34 -05:00
jason.woltjeandClaude Opus 5.5 f4714aa03b docs(sessions): Filbert row 45 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:26:32 -05:00
jason.woltjeandClaude Opus 5.5 9b067be173 docs(review): row 45 round 2 review record, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:26:24 -05:00
jason.woltjeandClaude Opus 5.5 5eb9fa8ae7 queue: rev 218, row 45 round 2 approve by filbert on #1527 (comment 26886, candidate 5b067a9d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:26:19 -05:00
jason.woltjeandClaude Opus 5.5 b13fef4c28 docs(review): row 45 round 2 review record, approve (darkwing)
Comment 26884 on #1527, candidate 5b067a9d, queue rev 217.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:20:38 -05:00
jason.woltjeandClaude Opus 5.5 354fa9f6bb queue: rev 217, row 45 round 2 approve by darkwing on #1527 (comment 26884, candidate 5b067a9d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:20:34 -05:00
jason.woltje 19dcc5531d queue: rev 216, row 45 in review round 2 on #1527 (request posted by sage, comment 26882); includes revs 212-214 (rocko: in-progress, in-review round 2, candidate 5b067a9d) and rev 215 (request) 2026-10-09 09:11:22 -05:00
jason.woltjeandClaude Opus 5.5 af1377d7e8 docs(cli): row 45 S4 follow-up round 2 candidate packet (rocko)
build.patch c8cec070…6756, candidate-manifest 5b067a9d…0e0e, base d539d8d2.
Covers Darkwing 26872, Filbert 26880 and lead decision 73.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:10:55 -05:00
jason.woltjeandClaude Opus 5.5 d539d8d2cc docs(sessions): Filbert row 45 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:32:43 -05:00
jason.woltjeandClaude Opus 5.5 8bcdd9a6f8 docs: SESSIONS, Dewey row 46 review and Sage network cleanup, row 46 landing, row 45 rulings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:32:43 -05:00
jason.woltjeandClaude Opus 5.5 46baf3f854 docs(review): row 45 round 1 review record, changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:32:38 -05:00
jason.woltjeandClaude Opus 5.5 6e4af4b7c2 queue: rev 211, row 45 round 1 changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:32:35 -05:00
jason.woltjeandClaude Opus 5.5 a690f09132 docs(plans): lead decision 73, row 45 round 1 rulings (definite refusals wait the full cap; close send callback)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:31:23 -05:00
jason.woltje 1aad1e09ee queue: rev 210, row 46 done (sage, landed 2d308abd); includes rev 208, row 45 changes from darkwing (comment 26872), and rev 209, row 46 approve from dewey (comment 26873) 2026-10-09 08:30:36 -05:00
jason.woltjeandClaude Opus 5.5 2d308abd16 test(conversation): fake-pi tool child signals ready after its TERM handler (row 46, #1528)
Fixes the K1, K3 and K10 race: a child could get SIGTERM before it
installed its handler. Author: Darkwing. Reviewer: Dewey (approve,
comment 26873). BUILD-LOG landing entry with gate counts and the
correction to decision 72's probe claim.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:30:01 -05:00
jason.woltjeandClaude Opus 5.5 1a481a1f4d docs(cli): row 45 round 1 review, changes on #1527 (darkwing)
R1: the guarded close send at host.mjs:144 and :150 can still fail with
EPIPE after a broker SIGKILL; reproduced, fix is a send callback.
R2: give-up lands 7.5 min after the first refusal, against decision 72's
reason; Sage's ruling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:29:50 -05:00
jason.woltjeandClaude Opus 5.5 9e172fb494 queue: rev 208, row 45 round 1 changes by darkwing on #1527 (comment 26872)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:29:41 -05:00
jason.woltjeandClaude Opus 5.5 e52493f7ae docs: BUILD-LOG, Docker network removal result (17 removed, 32 to 15)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:26:29 -05:00
jason.woltjeandClaude Opus 5.5 7525431978 docs: BUILD-LOG, unused Docker networks listed before removal (Mos's ruling)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:26:07 -05:00
jason.woltje 112071c74b queue: rev 207, row 45 in review round 1 on #1527 (rocko; request posted by sage, comment 26869) 2026-10-09 08:16:10 -05:00
jason.woltjeandClaude Opus 5.5 156eb07add docs(cli): row 45 S4 follow-up candidate packet (rocko)
Packet for #1527, base 521597bb: build.patch, candidate manifest
b329fdcb, packet manifest, BUILD.md, gate and mutant receipts. The
candidate itself is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:15:47 -05:00
jason.woltjeandClaude Opus 5.5 7478ee2c02 docs(conversation): row 46 cohort K1/K3/K10 diagnosis, receipts and candidate packet (darkwing)
The ignoreTerm tool child was TERMed before Node installed its handler
when the claim store sits on a fast disk. Candidate fixes the fixture;
the patch itself is not committed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:11:48 -05:00
jason.woltjeandClaude Opus 5.5 76506c7e96 queue: rev 203, row 46 in review round 1 on #1528 (darkwing); includes rev 201, row 45 in progress (rocko)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 08:11:43 -05:00
jason.woltjeandClaude Opus 5.5 521597bbe0 queue: rev 200, row 46 in progress (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:56:57 -05:00
jason.woltjeandClaude Opus 5.5 41c5a8563f docs: SESSIONS, Sage lands rows 38 and 39 and opens rows 45 and 46
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:55:50 -05:00
jason.woltje d0c20a2f24 queue: rev 199, rows 45 and 46 added and briefed (sage, decision 72) 2026-10-09 07:55:41 -05:00
jason.woltjeandClaude Opus 5.5 ec2a2c6773 docs: lead decision 72, the S4 follow-up and cohort briefs, and the trackers boot test
Decision 72 rules on F2 (five definite refusals stop a DM; unknown
outcomes keep retrying) and J4 (type-check confirmed journal lines),
and opens two rows: the S4 follow-up for Rocko and the K1, K3 and K10
cohort failures for Darkwing. The trackers boot test from the row 39
gate is kept under agents/sage/work/s4-follow-up/ until the follow-up
moves it into packages/cli/tests/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:54:52 -05:00
jason.woltjeandClaude Opus 5.5 e580ee2239 queue: rev 195, row 39 S4 done (sage, 2f5303c1)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:50:18 -05:00
jason.woltjeandClaude Opus 5.5 a78f64b1cf docs: BUILD-LOG, S4 round 1 and round 2 entries, the M28 correction and the row 39 landing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:49:52 -05:00
jason.woltjeandClaude Opus 5.5 2f5303c1c7 feat(cli): the mosaic CLI, broker host and decision notifier (row 39, S4, rocko)
packages/cli adds mosaic inbox, decide, tasks, agents and trail over the
human-cli transport, and mosaic bus start, stop and status as the trusted
host (unit mosaic-bus@<business>, scripts/bus-service.sh). The host boots
packages/bus/src/process.mjs, passes config.trackers from the tracker.*
variables (lead decision 70), and runs a notifier child. The notifier DMs
each open blocking decision once and sends an 08:00 America/Chicago
digest, journaled in notify/<business>/sent.jsonl at 0600 with no Discord
ids. A torn journal tail is copied aside and truncated; a malformed line,
a directory looser than 0700 or a symlinked journal refuses (lead
decision 71). packages/discord gains dmRecipient, createDm and notify.mjs.

Candidate agents/rocko/work/slice1-s4, base b9b6cf00, build.patch
b52f7d68, manifest e858504e (29 files). Darkwing approved round 2 on
#1521 (comment 26855), Filbert approved round 2 (comment 26856). The
packet's mutant table lists M28 as killed; it survived, and BUILD-LOG
records the correction.

Integration gate in a worktree on 2557e29d with the patch applied:
bus 67, business 60, cli 49, control-board 124, discord 178, ledger 78,
mosaic 69, queue 148, seat 19, tasks 51 and webui 14, all with no
failures. Conversation is 149/3, the same K1, K3 and K10 cases that fail
on the base; S4 doesn't touch the package. Every scripts/test-*.sh is
green, with test-release 14/14 and test-task 98/98 on the existing gate2
compose network. A scratch test, not in this commit, booted the real
host with trackers against S3's fake Vikunja: the adapter went ready and
a task.close on a missing task answered task-not-found after a Vikunja
read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:49:38 -05:00
jason.woltjeandClaude Opus 5.5 2557e29dc7 queue: rev 194, row 38 S3 done (sage, 7e73c2cd)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:41:46 -05:00
jason.woltjeandClaude Opus 5.5 ffd3a9d86c docs: BUILD-LOG, S2, S2b, S2c and S3 entries and the row 38 landing
The seats' uncommitted S2 to S3 entries land with row 38. Rocko's two S4
entries stay uncommitted until row 39 lands.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:41:17 -05:00
jason.woltjeandClaude Opus 5.5 7e73c2cd13 feat(tasks): the Vikunja v2 adapter, broker task verbs and sync (row 38, S3, darkwing)
packages/tasks adds the Vikunja v2 client, the eight task verbs, the
board-plus-cursor poll with its 60 s window and the digest. The broker
gains the task verbs and boots trackers from the boot config (lead
decisions 66 to 68). Due dates are truncated to the second and recorded
as truncated (B1). A write that lands but whose final read fails counts
as landed, in update and in create (B2).

Candidate agents/darkwing/work/slice1-s3, build-r2.patch 71ce87e6,
manifest e10e30e3 (28 files). Filbert approved round 2 on #1520
(comment 26853). Darkwing's post-reset rerun: test-release 14/14,
test-task 98/98 (comment 26857).

Integration gate in a worktree on c4baf779 with the patch applied:
bus 67, business 60, control-board 124, discord 173, ledger 78,
mosaic 69, queue 148, seat 19, tasks 51 and webui 14, all with no
failures. Conversation is 149/3. The three cohort kill cases (K1, K3,
K10) fail the same on the unpatched base, and the patch doesn't touch
the package. Every scripts/test-*.sh is green. test-release 14/14 and
test-task 98/98 ran on the existing gate2 compose network, because the
host's Docker address pools are exhausted. No network was created or
pruned.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:40:48 -05:00
jason.woltjeandClaude Opus 5.5 c4baf77916 docs(slice1): row 38 S3 round 2 gate rerun, test-release 14/14 and test-task 98/98
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 07:33:26 -05:00
jason.woltjeandClaude Opus 5.5 f5eb85ea4e docs: sessions, filbert row 39 S4 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:16:29 -05:00
jason.woltjeandClaude Opus 5.5 2de56a471c docs(slice1): filbert row 39 S4 round 2 review record (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:16:18 -05:00
jason.woltjeandClaude Opus 5.5 1608e3da45 queue: rev 193, row 39 round 2 approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:16:12 -05:00
jason.woltjeandClaude Opus 5.5 9bdee45517 queue: rev 192, row 39 round 2 darkwing approves (comment 26855)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:01:45 -05:00
jason.woltjeandClaude Opus 5.5 3c18473d11 docs(slice1): darkwing row 39 S4 round 2 review record (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:01:06 -05:00
jason.woltjeandClaude Opus 5.5 26bd829c1d queue: revs 188-191, row 39 round 2 in review (rocko moves, sage request)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:56:02 -05:00
jason.woltjeandClaude Opus 5.5 fdbb4e34c4 docs: sessions, filbert row 38 S3 round 2 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:41:41 -05:00
jason.woltjeandClaude Opus 5.5 d5c95244c8 docs(slice1): filbert row 38 S3 round 2 review record (approve)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:41:32 -05:00
jason.woltjeandClaude Opus 5.5 5d55cfc156 queue: rev 187, row 38 round 2 approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:41:27 -05:00
jason.woltjeandClaude Opus 5.5 bb267da534 queue: rev 186, row 38 round 2 in review (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:28:10 -05:00
jason.woltjeandClaude Opus 5.5 b144a03d47 docs(slice1): darkwing row 38 S3 round 2 packet
B1 truncates due dates to the second; B2 records what landed when the
final read fails; tests for V9, V21 and S5. Candidate manifest
e10e30e3, 28 files over c9ef7ee4. Mutants 46/63. Live provider cases
in test-release and test-task failed on a zai usage limit (429).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:27:17 -05:00
jason.woltjeandClaude Opus 5.5 c9ef7ee49f queue: rev 184, row 38 back to in-progress for round 2 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:06:44 -05:00
jason.woltjeandClaude Opus 5.5 779569a69d docs: sessions, filbert row 38 S3 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:05:29 -05:00
jason.woltjeandClaude Opus 5.5 dad9b0b00a docs(slice1): filbert row 38 S3 round 1 review record (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:05:20 -05:00
jason.woltjeandClaude Opus 5.5 944bd5770d queue: rev 183, row 38 round 1 changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 19:05:17 -05:00
jason.woltjeandClaude Opus 5.5 e328503bc0 docs: sessions, row 39 round 1 verdicts and decision 71 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:52:36 -05:00
jason.woltjeandClaude Opus 5.5 b9b6cf008a docs: lead decision 71, S4 round 2 scope and the notify journal's torn tail (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:52:11 -05:00
jason.woltjeandClaude Opus 5.5 49f99f99aa queue: rev 182, row 39 back to in-progress for round 2 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:51:15 -05:00
jason.woltjeandClaude Opus 5.5 aba3aa9dad docs: sessions, filbert row 39 S4 round 1 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:49:41 -05:00
jason.woltjeandClaude Opus 5.5 9ec1ac309f docs(slice1): filbert row 39 S4 round 1 review record (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:49:31 -05:00
jason.woltjeandClaude Opus 5.5 60882084cb queue: rev 181, row 39 round 1 filbert asks for changes (comment 26848)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:49:24 -05:00
jason.woltjeandClaude Opus 5.5 37cd4b4771 docs: sessions, darkwing row 38 S3 round 1 and row 39 review
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:36:52 -05:00
jason.woltjeandClaude Opus 5.5 481c6a7d34 queue: revs 179-180, row 38 S3 round 1 review request on #1520 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:35:32 -05:00
jason.woltjeandClaude Opus 5.5 4ac133dd8a docs(slice1): row 38 S3 build packet, probes and live rehearsal (darkwing)
Candidate for #1520 against 81339889: packages/tasks and the bus task
verbs. Live run rehearsed on a scratch v2.7.0 container; the estate run
waits for T236's base URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:33:50 -05:00
jason.woltjeandClaude Opus 5.5 81339889dc queue: rev 178, row 39 round 1 darkwing approves (comment 26843)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:16:54 -05:00
jason.woltjeandClaude Opus 5.5 848b26d1dd docs: sessions, row 39 S4 round 1 (rocko, sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:10:11 -05:00
jason.woltjeandClaude Opus 5.5 703b892966 queue: rev 177, row 39 follow-up note (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:10:11 -05:00
jason.woltjeandClaude Opus 5.5 1423725a73 queue: revs 172-176, row 39 S4 round 1 on #1521 (rocko, sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 18:08:24 -05:00
jason.woltjeandClaude Opus 5.5 d68aa20f27 docs: decision 70, the host's boot config for S3 trackers (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:31:45 -05:00
jason.woltjeandClaude Opus 5.5 020b2c17b3 queue: repin slice 1 brief for rows 35, 38, 40-42; only S4's section changed (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:30:08 -05:00
jason.woltjeandClaude Opus 5.5 284cfcafa5 queue: row 39 briefed under lead decision 70 (sage)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:28:42 -05:00
jason.woltjeandClaude Opus 5.5 084c3a3cee docs: lead decision 70, S4 rulings for Rocko (sage)
human-cli.mjs is the decide transport. S4 owns the broker host and one
notifier for the blocking-decision DM and the 08:00 Central digest.
Darkwing is second reviewer for packages/discord and the host. Trail
prints in broker order. Row 39 no longer waits on S3. SESSIONS gets
Rocko's orientation line and a correction for Sage's estimated stamps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:26:57 -05:00
jason.woltjeandClaude Opus 5.5 bee89d107d docs: lead decision 69, Rocko moves to a Claude thread under R26 (sage)
Jason's R26 via Mos: Claude first, Codex only on gpt-6.1-sol, never
Astra. Rocko's Codex thread ran gpt-6-astra; the seat now runs as T3
thread b84bb264 on Opus 5.5, because its only row, S4, builds mosaic
decide and the gated decision DM. Roster updated. Filbert's launcher
still pins Astra; recorded as a follow-up, unused while Filbert runs in T3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:21:03 -05:00
jason.woltjeandClaude Opus 5.5 7780ef1e54 docs: runbook token listing reads the items envelope (sage)
Darkwing's correction, checked against the 2.7.0 OpenAPI: GET /tokens
returns PaginatedAPIToken, with items possibly null.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:17:18 -05:00
jason.woltjeandClaude Opus 5.5 67d29f7da5 docs: runbook section 5 lists a bot's token ids as svc (sage)
Darkwing's probe-s7f: svc-$BIZ revokes one bot token (204, then 401);
the owner gets 403. A plain GET /tokens shows only svc's own tokens, so
rotation names GET /tokens?owner_id=<bot id> for an old id. Decision 68
records the resolution.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:16:51 -05:00
jason.woltjeandClaude Opus 5.5 28fbdec97d docs: decision 68 correction, Jason creates the svc account (sage)
Mos: the svc-mosaic-stack password is a credential Jason keeps (R5), and
T236 creates no users. The runbook's Path A now has Jason create it with
the command from the instance's ops doc. Records the R20 confirmation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:14:45 -05:00
jason.woltjeandClaude Opus 5.5 bc33faa38f docs: lead decision 68, a service account owns the Vikunja bots (sage)
Darkwing's row 38 labels probe on the pinned 2.7.0 image: a bot created
from the owner's account reads and attaches every label that account
created, in any project (upstream #3592). Bots owned by svc-<business>,
an account with no labels and no shares, see only labels on the shared
project's tasks. The runbook now creates the bots and mints and revokes
their tokens as svc-$BIZ; the owner keeps the project and the shares.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:13:51 -05:00
jason.woltje d228b03298 queue: row 38 S3 in progress (darkwing, LD67) 2026-10-08 17:02:37 -05:00
jason.woltje 341da4932c queue: revs 158-159, row 38 S3 after 36 and 37 only, briefed (sage, LD67) 2026-10-08 16:57:41 -05:00
jason.woltjeandClaude Opus 5.5 af4d3c0f6a docs(plans): lead decision 67, S3 starts before the Vikunja runbook half (sage)
The estate Vikunja (T236) won't serve by Jason's 10-09 run. S3 builds
against the recorded fake and a scratch container; its gate still needs
the live run. R20's PM and Lead match the slice 1 brief's staffing.
Merge plan step 2 no longer holds for T235 (brief c733ac92b).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 16:56:56 -05:00
jason.woltjeandClaude Opus 5.5 ba51c8d867 docs(plans): refactor-to-next merge plan and lead decision 66 (sage)
Jason's 2026-10-08 rulings via Mos (thread 1eba59e5). R10: next stays
the trunk; the plan pins refactor b8db39cf and next 2101c9b4, finds a
clean merge-tree, and names the CI gate, approval identity, frozen
next-lane publishing, root .mosaic/repo.json, 13 stale v1 PRs and the
hardcoded ledger branch as the work. Nothing merged.

R8: Path A on the new estate Vikunja meets slice 1 and replaces
decision 61's Path B. The runbook's Path A text names the estate
instance and the share-only isolation rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 16:53:57 -05:00
jason.woltje b8db39cf1e queue: rev 157, row 44 S2c done (sage) 2026-10-05 18:14:18 -05:00
jason.woltjeandClaude Opus 5.5 bb93a05ffd docs(plans): decision 65 correction, message.send classifies by sender role only
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:13:46 -05:00
jason.woltjeandClaude Opus 5.5 d27042faf2 feat(bus): a within-role message cites a decision without consuming it (row 44, S2c, rocko)
A within-role message.send stores its decision as a citation: the broker
checks it exists in the business, and doesn't class-match it, consume it
or put it in action.allowed (lead decision 65). Sends that aren't
within-role keep the S2b authority rules. The README wording on
within-role sends is fixed.

Candidate agents/rocko/work/slice1-s2c-r1, build.patch 2c4f8d9f,
manifest aa249ad9. Darkwing approved round 1 on #1526 (comment 26778).
Integration gate in a worktree on ac4a6499: every package green on
Node 26; bus 58/58 on Node 24; every scripts/test-*.sh green. Node 24
failures in conversation, ledger, queue, seat and webui match the base.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:13:46 -05:00
jason.woltjeandClaude Opus 5.5 ac4a6499f2 review(bus): darkwing S2c round 1, approve (row 44, #1526)
Verdict comment 26778, queue rev 156. Within-role citations never reach
the authority check or the grant event; other sends keep S2b. 58/58 on
Node 24 and 26, seven of seven mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:06:47 -05:00
jason.woltje 4a8d91edd0 queue: rev 156, row 44 S2c round 1 approve (darkwing) 2026-10-05 18:06:47 -05:00
jason.woltje 2af57513d6 queue: rev 155, row 7 note on the refused 2026-10-05 ledger run (sage) 2026-10-05 18:06:03 -05:00
jason.woltjeandClaude Opus 5.5 e250933de7 docs(records): 2026-10-05 weekly ledger run, refused on a T3 header conflict (row 7, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 18:05:19 -05:00
jason.woltje 24a83cf706 queue: revs 150-153, row 44 S2c round 1 on #1526 (rocko, sage) 2026-10-05 18:03:57 -05:00
jason.woltje 83cfd18c94 queue: rev 149, row 44 S2c briefed (sage) 2026-10-05 17:59:20 -05:00
jason.woltje 1f5d253b2e queue: rev 148, row 44 S2c added (sage, #1526) 2026-10-05 17:58:00 -05:00
jason.woltjeandClaude Opus 5.5 dd35a0abcb docs(plans): lead decision 65 and the S2c brief, within-role messages cite decisions
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:57:23 -05:00
jason.woltje 6b753ff7b1 queue: rev 147, row 43 S2b done (sage, 4afab552) 2026-10-05 17:53:18 -05:00
jason.woltjeandClaude Opus 5.5 4afab55254 feat(bus): decision-backed approvals are single-use (row 43, S2b, rocko)
authorize, message.send and role.revoke consume a cited decision once,
inside the write transaction (lead decision 64). A second use refuses
with decision-consumed; a class mismatch refuses with decision-mismatch.

Candidate agents/rocko/work/slice1-s2b-r2, build.patch 56d572fe,
manifest a89d64ca. Darkwing approved round 2 on #1525 (comment 26769).
Integration gate in a worktree on bba75b4a: every package green on
Node 26; bus 55/55 on Node 24; every scripts/test-*.sh green. Node 24
failures in conversation, ledger, queue, seat and webui are identical
on the unpatched base (container /tmp is overlayfs, no jsonschema).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:52:41 -05:00
jason.woltjeandClaude Opus 5.5 bba75b4a4f review(bus): darkwing S2b round 2, approve (row 43, #1525)
Verdict comment 26769, queue rev 146. R1, R2 and both lead decision 64
rulings are in; round 1 probes now refuse. 55/55 on Node 24 and 26,
nine of ten mutants killed, the survivor equivalent.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:43:09 -05:00
jason.woltje 4699419b8b queue: rev 146, row 43 S2b round 2 approve (darkwing) 2026-10-05 17:43:09 -05:00
jason.woltje e7bcf5a988 queue: revs 141-145, row 43 S2b round 2 on #1525 (rocko, sage) 2026-10-05 17:40:25 -05:00
jason.woltjeandClaude Opus 5.5 577380833c docs(plans): lead decision 64, decision-backed approvals are single-use
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:35:45 -05:00
jason.woltjeandClaude Opus 5.5 4f28c09069 review(bus): darkwing S2b round 1, changes (row 43, #1525)
Verdict comment 26765, queue rev 140. R1: message.send and role.revoke
verbs use gated decisions without consuming them. R2: a cross-role
decision authorizes without limit once policy makes the action gated.
Two rulings for Sage on cross-role reuse and class drift.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:35:15 -05:00
jason.woltje 490344d89c queue: rev 140, row 43 S2b round 1 changes (darkwing) 2026-10-05 17:35:09 -05:00
jason.woltje 975a0a7807 queue: revs 135-139, row 43 S2b review round on #1525 (rocko, sage) 2026-10-05 17:23:39 -05:00
jason.woltjeandClaude Opus 5.5 65d78d1227 docs(sessions): sage, S1 and S2 landed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:16:41 -05:00
jason.woltje 1fe9fc056d queue: row 37 S2 done, row 43 note (sage) 2026-10-05 17:16:40 -05:00
jason.woltjeandClaude Opus 5.5 38828a2cb3 feat(bus): the bus and the broker core (row 37, S2, rocko)
Rocko's round 2 candidate, approved by Darkwing (#1519 comment 26757).
build.patch 40d7e838, manifest 61519059, 24 files under packages/bus,
schema v3b (179ffe35, lead decision 60). Integration gate in a git
worktree of 942dca9e (S1 in the tree) plus the patch: bus 43/43 and
business 60/60 on Node 24 and 26, every package test and every
scripts/test-*.sh green, test-task 98/98 with the live-provider cases.
Rulings from lead decisions 62 and 63: the human proof is cooperative in
slice 1, and a self-raised cross-role decision routes to the human.
Single-use gated approvals follow in row 43.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:15:53 -05:00
jason.woltje 942dca9ea8 queue: row 36 S1 done, row 37 note (sage) 2026-10-05 17:09:58 -05:00
jason.woltjeandClaude Opus 5.5 2d64c71eb2 feat(business): roles v2, business and project files, variable layers (row 36, S1, darkwing)
Darkwing's round 2 candidate, approved by Filbert (#1518 comment 26730).
build-r2.patch a27890d5, manifest 869168c7, 34 files, applied on HEAD and
checked 34/34. Integration gate on an export of HEAD plus the patch:
business 60/60 on Node 24 and 26, every package test and every
scripts/test-*.sh green, test-task 98/98 with the live-provider cases.
Conductor, queue, conversation and discord confirmed in git worktrees of
HEAD with and without the patch, identical results. Lead decision 63
accepts the vocabulary location, the example path and the business
branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:09:07 -05:00
jason.woltjeandClaude Opus 5.5 d1e633b220 queue: revs 126-130, row 37 round 1 approve on #1519 (darkwing)
Revs 126-129 are Sage's round opening (request comment 26756). Rev 130
records Darkwing's approval, comment 26757, record 1e3c06c7.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:02:05 -05:00
jason.woltje f594318d45 queue: revs 126-129, row 37 S2 review round on #1519 (rocko, sage) 2026-10-05 17:01:08 -05:00
jason.woltjeandClaude Opus 5.5 e16faafdc2 docs(sessions): sage, rocko and dewey lines
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 16:45:41 -05:00
jason.woltje d14b2c702e queue: revs 113-125, briefs re-pinned, rows 11 and 34 done, row 43 S2b (sage) 2026-10-05 16:45:30 -05:00
jason.woltjeandClaude Opus 5.5 623d1b6cc4 docs(plans): S2b brief, gated approvals are single-use
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 16:44:10 -05:00
jason.woltjeandClaude Opus 5.5 85e8f97dbf docs(plans): slice 1 brief accepted, S0 rely-on lines in S6; lead decisions 62-63
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 16:43:34 -05:00
jason.woltjeandClaude Opus 5.5 1e3c06c78b docs(review): row 37 S2 round 2, approve (darkwing)
Round 2 of #1519: R1 to R5 fixed and reproduced, 43/43 on Node 26 and
Node 24, 22 of 23 mutants killed against a clean baseline. Records a
reparented-CLI path past the human proof for Sage's ruling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:31:49 -05:00
jason.woltjeandClaude Opus 5.5 79699c143b docs(review): row 37 S2 round 1, changes (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:13:35 -05:00
jason.woltjeandClaude Opus 5.5 dc0c41c426 docs(sessions): filbert row 36 round 2 approve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:10:31 -05:00
jason.woltjeandClaude Opus 5.5 17481148e2 docs(review): row 36 S1 round 2, approve (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:10:26 -05:00
jason.woltjeandClaude Opus 5.5 473155d463 queue: rev 112, row 36 S1 round 2 approve on #1518 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:10:21 -05:00
jason.woltjeandClaude Opus 5.5 95c65b5d06 docs(review): row 34 S0 round 2, approve (darkwing)
Record for #1516 comment 26729: a and b fixed, c to f fixed, ten cc-7
cases reproduce 10/10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:02:20 -05:00
jason.woltje 2b9af160f2 queue: row 34 S0 round 2 approved on #1516 (darkwing) 2026-10-04 23:02:14 -05:00
jason.woltje 768243ccff queue: revs 108-110, row 36 S1 round 2 on #1518 (darkwing) 2026-10-04 22:58:34 -05:00
jason.woltjeandClaude Opus 5.5 8da2ea4b79 docs(sessions): filbert row 34 round 2
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:56:57 -05:00
jason.woltjeandClaude Opus 5.5 db98d12e42 queue: revs 105-107, row 34 S0 round 2 in review on #1516 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:56:44 -05:00
jason.woltjeandClaude Opus 5.5 d9568cec98 docs(slice1): row 36 S1 round 2 packet (darkwing)
Answers Filbert's round 1 (#1518 comment 26724): launch.by must hold
role.launch, launch is null when limits.authority drops it, a test for
cross-role narrowing, and the business file checked on its open
descriptor.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:55:37 -05:00
jason.woltjeandClaude Opus 5.5 4b7405b86d probes(slice1): row 34 S0 round 2, wrapped command hooks (filbert)
Adds ten cc-7 cases (Darkwing's six wrapper cases plus allow, block,
SIGTERM-ignoring gate and inner timeout above the hook timeout), amends
rely-on lines 2 to 4, removes the stale cc-1d-block-bare evidence, adds
compare.sh, reads SDK_ENTRY from the environment, fixes the cc-5c row and
the Pi exit-code sentence.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:54:10 -05:00
jason.woltjeandClaude Opus 5.5 1abebe5321 docs(review): row 34 S0 round 1, changes (darkwing)
Record for #1516 comment 26725: 34/34 cases reproduce, and a wrapped
Claude command hook fails closed on crash, missing path, noexec and hang.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:51:00 -05:00
jason.woltje 75f6358d0e queue: rev 104, row 34 S0 round 1 changes on #1516 (darkwing) 2026-10-04 22:50:54 -05:00
jason.woltjeandClaude Opus 5.5 7cbf78dab9 docs(review): row 36 S1 round 1, changes (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:40:19 -05:00
jason.woltjeandClaude Opus 5.5 779b01bd30 queue: rev 103, row 36 S1 round 1 changes on #1518 (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:40:12 -05:00
jason.woltjeandClaude Opus 5.5 e250750605 docs(prd): version 1.0, approved by Jason; lead decision 61
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:37:57 -05:00
jason.woltje e3b767155c queue: row 35 note, Path B ruled (sage) 2026-10-04 22:37:57 -05:00
jason.woltjeandClaude Opus 5.5 24c81a6142 docs(plans): lead decision 60, schema v3b accepted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:34:37 -05:00
jason.woltje 6050d3da54 queue: row 37 note, S2 pins schema v3b (sage) 2026-10-04 22:34:37 -05:00
jason.woltjeandClaude Opus 5.5 48d76de7c6 docs(slice1): schema v3b, current snapshot view (darkwing)
Lead decision 59. task_current picks each task's current snapshot,
skipping a poll row whose read_at is at or before the latest self row's
at; tasks_open reads it. Prototype on Node 24 and 26, two mutants
caught, and Dewey's fixture replayed: tasks_open now differs from v3a
only on #42 (in-progress). The notes add an S3 poller rule: compare a
read against task_current, not the raw latest row.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:33:09 -05:00
jason.woltjeandClaude Opus 5.5 554b03f565 docs(plans): DEFERRED, Node 24 test dir form
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:31:20 -05:00
jason.woltjeandClaude Opus 5.5 c0e2b99afe docs(plans): DEFERRED, unguarded live call in test-task.sh
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:30:51 -05:00
jason.woltjeandClaude Opus 5.5 47742e0a97 queue: revs 99-100, row 36 S1 in review on #1518 (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:30:18 -05:00
jason.woltjeandClaude Opus 5.5 b851f83c39 docs(slice1): row 36 S1 build packet, v3a trigger count correction (darkwing)
S1 candidate for Filbert's review on #1518: build.md, build.patch and
build-manifest.sha256 (34 files at base fef4b362). The candidate itself
is not committed.

proto-v3a-notes-correction: the schema has 36 triggers; the printed 35 is
counted after the tamper check's DROP TRIGGER. Found by Rocko.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:28:30 -05:00
jason.woltjeandClaude Opus 5.5 e153c3a3b2 docs(plans): lead decision 59, current snapshot view
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:28:17 -05:00
jason.woltjeandClaude Opus 5.5 b0a5dd8002 docs(sessions): row 34 S0 in review (filbert)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:27:03 -05:00
jason.woltje ec65dadf51 queue: revs 97-98, row 34 S0 in review on #1516 (filbert) 2026-10-04 22:26:44 -05:00
jason.woltjeandClaude Opus 5.5 771fc3d270 docs(slice1): row 34 S0 harness probe matrix (filbert)
Pi 0.85.1 and Claude Code 2.1.289 (Agent SDK 0.3.289), 34 cases against a
scripted local model in a loopback-only network namespace. MATRIX.md has
the command, outcome, model-visible message and fail-closed column per
case, and one rely-on line per case. Evidence under evidence/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:25:41 -05:00
jason.woltje fef4b362fc queue: row 35 SR waiting on Jason 2026-10-04 22:17:11 -05:00
jason.woltjeandClaude Opus 5.5 ccbf81b1a8 docs(sessions): row 35 SR approved
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:16:27 -05:00
jason.woltjeandClaude Opus 5.5 8619d36bc4 docs(slice1): row 35 SR round 3 review record (darkwing)
Verdict approve, #1517 comment 26715.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:16:10 -05:00
jason.woltjeandClaude Opus 5.5 e7c6d9f8e0 queue: row 35 SR round 3 approve (darkwing, comment 26715)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:16:10 -05:00
jason.woltje ee628a4072 queue: row 35 SR round 3 requested 2026-10-04 22:14:55 -05:00
jason.woltjeandClaude Opus 5.5 98814a67d2 docs(guides): slice 1 identities round 3, revoke before rm
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:14:10 -05:00
jason.woltjeandClaude Opus 5.5 ee82aa5e4f docs(slice1): row 35 SR round 2 review record (darkwing)
Verdict changes, #1517 comment 26712: the Vikunja rotation removes the
owner header before the revoke that needs it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:13:33 -05:00
jason.woltjeandClaude Opus 5.5 bc81699c58 queue: row 35 SR round 2 changes (darkwing, comment 26712)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:13:33 -05:00
jason.woltje 6352af080c queue: row 35 SR round 2 requested 2026-10-04 22:11:31 -05:00
jason.woltjeandClaude Opus 5.5 104cf4f3f0 docs(guides): slice 1 identities round 2; lead decision 58
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:10:32 -05:00
jason.woltjeandClaude Opus 5.5 4d51c1304d docs(slice1): row 35 SR round 1 review record (darkwing)
Verdict changes, #1517 comment 26709. Gitea 1.27.1 and Vikunja 2.7.0
source checks for Sage's three questions; two mint defects.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:09:21 -05:00
jason.woltjeandClaude Opus 5.5 fe537f68a1 queue: rev 86, row 35 SR round 1 changes (darkwing, comment 26709)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:09:21 -05:00
jason.woltje d0e1bee751 queue: rev 85, row 37 note, S2 builds from v3a 2026-10-04 22:03:36 -05:00
jason.woltjeandClaude Opus 5.5 c9ff79c802 docs(plans): lead decision 57, schema v3a accepted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:03:02 -05:00
jason.woltjeandClaude Opus 5.5 29daa48216 docs(slice1): schema v3a, task event rules (lead decision 56, Q3)
Two triggers: every task.* event names its task in subject, and
task.created cites a human.input event and a requirement id. The task
ref pattern is tightened on snapshots, decisions and subjects. Runs on
Node 24.21.0 and 26.8.1 match apart from the version line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:02:31 -05:00
jason.woltje 5e8f0f4749 queue: revs 80-83, row 35 SR in review on #1517 2026-10-04 21:59:50 -05:00
jason.woltjeandClaude Opus 5.5 c9c1699a05 docs(guides): slice 1 identities runbook (row SR); lead decision 56
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:58:49 -05:00
jason.woltjeandClaude Opus 5.5 8c4460f35e chore(queue): row 34 S0 probe matrix started by filbert
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:58:22 -05:00
jason.woltje 6ecbf00b94 queue: row 36 S1 started by darkwing 2026-10-04 21:57:07 -05:00
jason.woltjeandClaude Opus 5.5 b02aade221 docs(rocko): slice 1 S2 preparation packet (record)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:56:11 -05:00
jason.woltjeandClaude Opus 5.5 06cf0d33d6 docs(plans): lead decision 55, schema v3 accepted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:55:50 -05:00
jason.woltjeandClaude Opus 5.5 7ed831781b docs(slice1): schema v3 prototype, addendum B section 5
task_snapshots gains via and read_at, an integer-bucket CHECK with a
tombstone exception, the read-ordering rule in task_external_changes and
the tasks_open view. task.missing names its task and a reason. Prototype
rerun on Node 24.21.0 and 26.8.1; a mutant without the read_at rule
reports the broker's own move as external. Lead decision 52 (B3).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:55:17 -05:00
jason.woltje 05a1cbf85a queue: revs 64-76, slice 1 rows 34-42, rows 34-37 briefed 2026-10-04 21:53:00 -05:00
jason.woltjeandClaude Opus 5.5 4837e1ea29 docs(plans): lead decision 54, slice 1 rows and owners
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:52:27 -05:00
jason.woltjeandClaude Opus 5.5 43c48d7a23 docs(plans): slice 1 brief, rows S0 to S7
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:50:59 -05:00
jason.woltjeandClaude Opus 5.5 dbfaad72c3 queue: rev 63, row 5 note, Gate E rides the slice 1 WebUI step
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:46:35 -05:00
jason.woltjeandClaude Opus 5.5 a59a976dda docs(prd): PRD draft 0.4 with PRDY round 3; lead decision 53
Tokens by hand from a runbook, Gitea bot users per role, slice 1 agents
as Jason's OS user, DMs through the existing connector, digest at 08:00
Central, Gate E with the slice 1 WebUI step. Approval as 1.0 is still
open.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 21:46:00 -05:00
jason.woltjeandClaude Opus 5.5 b6631e6b4f docs(plans): slice 1 addendum B; lead decision 52
Real Vikunja scope map, a read-only sync bot, a two-request poll that
sees column moves and deletions, and task_snapshots changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 18:42:31 -05:00
jason.woltjeandClaude Opus 5.5 5175ca10f2 docs(plans): Vikunja probe record; lead decision 51
Researcher ran P1-P7 on a scratch Vikunja 2.7.0. Scope names in
addendum A are wrong, polling on updated misses column moves and
deletions, and If-Match isn't enforced. Decision 51 rules on each.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 18:31:47 -05:00
jason.woltjeandClaude Opus 5.5 1595553ad9 queue: revs 61-62, row 5 CHAT-03 I1 committed 243e153c, waiting on Jason for Gate E
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:48:48 -05:00
jason.woltjeandClaude Opus 5.5 243e153c8b feat(conversation): CHAT-03 I1, mediated control of a sealed headless Pi (#1507)
Controller, claim store, live-session guard, engine link and seal,
turn tracker, cohort force stop and recovery, client library,
transcript and mediated terminal, with the fake engine and tests.
Fixtures only; no live cutover.

Dewey built it. Darkwing (comment 26690) and Filbert (comment 26694)
approved round 2. Manifest I1-r2-manifest.sha256 (2b48e333, 27 files).
Suites on an export: conversation 152/152, control-board 124, webui 14,
seat 19, chat-00/01/01c checks, and all nine scripts/test-*.sh green.
Follow-ups for I3 are in DEFERRED. Gate E stays with Jason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:47:53 -05:00
jason.woltjeandClaude Opus 5.5 ddd9cf3635 chore(queue): row 5 round 2 approved by filbert, comment 26694 on #1507
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:42:05 -05:00
jason.woltjeandClaude Opus 5.5 b846c44dba docs(review): Darkwing's CHAT-03 I1 round 2 review record
Approve, no blockers; follow-ups F1-F3 for I3. Comment 26690 on #1507.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:33:21 -05:00
jason.woltjeandClaude Opus 5.5 2cd6aad90f queue: rev 59, row 5 CHAT-03 I1 round 2 approved (Darkwing)
Verdict comment 26690 on #1507, candidate 2b48e333. B1 and B2 fixed;
follow-ups F1-F3 recorded in the review file, none blocking.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:33:02 -05:00
jason.woltjeandClaude Opus 5.5 5e7fa4c5ea docs: Dewey's CHAT-03 I1 build and round-1 rework log entries
BUILD-LOG phases for the I1 build and the round-1 rework, and Dewey's
SESSIONS line. The candidate itself stays uncommitted until both
reviewers approve round 2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:27:08 -05:00
jason.woltjeandClaude Opus 5.5 665866f158 queue: revs 56-58, row 5 CHAT-03 I1 round 2 requested (Dewey)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 15:27:08 -05:00
jason.woltjeandClaude Opus 5.5 f2b8c92a84 docs(plans): slice 1 prototype v2 records; lead decision 50
Darkwing's schema-v2 adds task_snapshots, decisions.blocking and a
closed events.kind list. Sage reran proto-v2 on Node 26 with matching
output. Decision 50 accepts the five choices beyond addendum A.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:59:33 -05:00
jason.woltjeandClaude Opus 5.5 d7723e2237 docs(prd): PRD draft 0.3; slice 1 addendum A; lead decision 49
Darkwing's addendum as a record. Broker verbs enforce field ownership,
broker push from a bare repo, polling without webhooks, Vikunja probes
before the adapter interface is fixed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:56:14 -05:00
jason.woltjeandClaude Opus 5.5 c57998772d docs(prd): PRD draft 0.2 with requirement ids; lead decision 48
PRDY round 2 answers, and Researcher's Vikunja and Pocket ID report as
a record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:48:15 -05:00
jason.woltjeandClaude Opus 5.5 df9e036214 docs(plans): meta-harness survey; lead decision 47
Filbert's survey of Pi, Claude Code and Codex hooks as a record. No hook
is a hard block alone; credential scope, verbs, tool ceiling and a
container are. Two DEFERRED items.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:26:50 -05:00
jason.woltjeandClaude Opus 5.5 bb7e37dda2 docs(plans): slice 1 data model note and prototype; lead decision 46
Darkwing's design note and SQLite prototype as records. Sage accepts
seven of eight open questions; the PM launching sessions goes to Jason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:19:14 -05:00
jason.woltjeandClaude Opus 5.5 9c69f2fba3 docs(prd): Mosaic Stack PRD draft 0.1 from PRDY round 1; lead decision 45
Round 1 answers fill users, problems, hard limits, hosting and the v1
done measure. Decision 45 raises Sage's seat limit to 4 Opus 5.5 and 4
Sonnet 5.5 sessions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:12:38 -05:00
jason.woltjeandClaude Opus 5.5 8a3e672f4a docs(records): row 5 round 1 review files, record-issue gap in DEFERRED
Both reviewers requested changes (26681, 26683). Darkwing's verdict
landed on #1508; pointer 26685 on #1507.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:01:37 -05:00
jason.woltjeandClaude Opus 5.5 14562ac227 docs(plans): lead decision 44, foundation direction ratified
Slice 1 becomes goal 1, working north star sentence on the goals page,
Vikunja local by default with existing or bundled at install, SQLite
for decisions and messages, Pocket ID after slice 1, row 32 parked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:00:58 -05:00
jason.woltje 3270cc4ea9 chore(queue): row 32 parked on Jason's word (rev 55); carries filbert's row 5 r1 changes verdict (rev 54, comment 26683) 2026-10-04 14:00:57 -05:00
jason.woltjeandClaude Opus 5.5 f0b13a2feb chore(queue): row 5 round 1 changes requested by filbert, comment 26683 on #1507
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 13:59:59 -05:00
jason.woltjeandClaude Opus 5.5 2f4b947906 docs(plans): lead decision 43, Jason's first foundation rulings
Vikunja integrate, first roles agreed, Pocket ID recorded as an SSO
candidate, row 32 parking advised.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 13:54:27 -05:00
jason.woltjeandClaude Opus 5.5 0e45ccd586 docs(sessions): sage routes row 5 round 1 rework to dewey
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 13:52:41 -05:00
jason.woltjeandClaude Opus 5.5 5a5013c42e chore(queue): row 5 round 1 changes requested by darkwing, comment 26681 on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 13:51:41 -05:00
jason.woltjeandClaude Opus 5.5 8bcf079a58 docs(plans): foundation direction proposal after Jason's KPI message
Answers the five questions, maps the brain dump against the repo, and
proposes one end-to-end slice before measures. Not ratified.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 13:45:43 -05:00
jason.woltjeandClaude Opus 5.5 1c72495815 docs(records): row 33 landed, cold-start failure reopened (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 01:26:07 -05:00
jason.woltje 93eba94939 chore(queue): row 33 done, candidate committed (#1508) 2026-10-04 01:25:41 -05:00
jason.woltjeandClaude Opus 5.5 6fb50cc0a8 fix(queue): genesis owner-not-reviewer check, assign wording, queue-commit HEAD-moved message, calendar dates (row 33, #1508)
Built by filbert, approved by darkwing in round 1 (#1508 comment 26651).
Manifest fe7da3ef, 10 paths. Suites green on an index export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 01:25:06 -05:00
jason.woltjeandClaude Opus 5.5 862f865a3c chore(queue): row 33 round 1 approved by darkwing, comment 26651 on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 01:10:04 -05:00
jason.woltjeandClaude Opus 5.5 930d27579a chore(queue): row 33 in-review, round 1 requested from darkwing, comment 26648 on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:59:34 -05:00
jason.woltje 3e39a26a5f chore(queue): row 5 note, CHAT-03 resumed (#1507) 2026-10-04 00:28:16 -05:00
jason.woltjeandClaude Opus 5.5 b48fc0f6cb docs(records): add --issue sets closes, session line (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:27:06 -05:00
jason.woltje e048e665d1 chore(queue): row 33 closes narrowed to none (#1508) 2026-10-04 00:26:26 -05:00
jason.woltje 48e9c147ab chore(queue): row 33 queue follow-ups briefed for filbert, row 5 reviewers darkwing and filbert (#1508) 2026-10-04 00:25:44 -05:00
jason.woltjeandClaude Opus 5.5 ec1debd5a9 docs(plans): brief, queue follow-ups from rows 12, 13 and 31 (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:25:04 -05:00
jason.woltje 41f373225c chore(queue): row 32, ledger guideposts, queued for Jason's acceptance (#1514) 2026-10-04 00:23:50 -05:00
jason.woltjeandClaude Opus 5.5 07dd203072 docs(plans): brief, ledger guideposts beyond messages per closed issue (#1514)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:22:56 -05:00
jason.woltjeandClaude Opus 5.5 921063d5d8 docs(records): Gate G pass and row 31 landed, follow-ups (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:17:43 -05:00
jason.woltje 7584fbd055 chore(queue): row 31 done, candidate committed as bb2fa970 (#1508) 2026-10-04 00:17:21 -05:00
jason.woltjeandClaude Opus 5.5 bb2fa9704c feat(queue): refuse a row's owner as its reviewer at add, set reviewers and assign (row 31, #1508)
Built by rocko (Gate G run), approved by filbert in round 1 (#1508 comment
26629). Manifest 35199daf. Suites green on an index export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:16:41 -05:00
jason.woltjeandClaude Opus 5.5 1649f5e2a3 chore(queue): row 31 round 1 approved by filbert, comment 26629 on #1508
Filbert posted the verdict as comment 26629 with the seat's own token and
recorded it. The candidate is manifest 35199daf, uncommitted in the working
tree; no file under docs/plans/reviews/ was added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:12:55 -05:00
jason.woltjeandClaude Opus 5.5 b800a2e60b docs(records): lead decision 42, Jason's unlock rulings (#1508, #1510)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 00:05:56 -05:00
jason.woltje 29f6db6290 chore(queue): Gate G pass, rows 9, 10, 16 done, row 31 request reposted (#1508, #1510) 2026-10-04 00:05:52 -05:00
jason.woltjeandClaude Opus 5.5 2eca954163 docs(plans): project relaunch with roles, idea and SetSpark evidence (goal 4 input)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 23:51:56 -05:00
jason.woltjeandClaude Opus 5.5 7a3c364b41 docs(records): lead decision 41, Gate G setup (row 31, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 08:28:00 -05:00
jason.woltjeandClaude Opus 5.5 c989dfe9d4 chore(queue): row 31, owner can't be a reviewer, briefed for rocko (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 08:27:44 -05:00
jason.woltjeandClaude Opus 5.5 92aeeeef2d docs(plans): brief, a queue row's owner can't be its reviewer (#1508)
Filbert's n2 from the Piece D round 2 review, written as a queue brief.
It is also the piece a fresh seat starts in Gate G.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 08:27:03 -05:00
jason.woltjeandClaude Opus 5.5 0d6658b57d docs(records): 2026-09-28 weekly ledger output (row 7, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 08:26:12 -05:00
jason.woltjeandClaude Opus 5.5 1310805e5d chore(queue): row 7 note, 2026-09-28 weekly ledger run
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 08:26:12 -05:00
jason.woltjeandClaude Opus 5.5 319ee1332c chore(queue): row 13 round 1 approved by filbert, comment 26589 on #1508
Filbert posted the verdict as comment 26589 with the seat's own token and
recorded it (rev 23). The candidate fd72d268 passes verify-commit, and no
file under docs/plans/reviews/ was added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:38:32 -05:00
jason.woltjeandClaude Opus 5.5 ae2cfcb574 chore(queue): row 13 in-review, round 1 request posted as comment 26586 on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:36:23 -05:00
jason.woltjeandClaude Opus 5.5 a91df5ed78 docs(records): Piece E build note and follow-ups (row 13, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:35:21 -05:00
jason.woltjeandClaude Opus 5.5 0a6f58ddd7 chore(queue): row 7 brief re-pinned to the ledger README weekly routine after Piece E
Piece E (fd72d268) moved the weekly routine into packages/ledger/README.md.
Row 7 stays until Jason closes it (lead decision 40).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:35:07 -05:00
jason.woltjeandClaude Opus 5.5 fd72d26899 feat(ledger): Piece E, queue section in the weekly ledger (row 13, #1508)
The ledger prints a queue section above the weekly table. It checks four
things:
- open issues named by done rows;
- owner registrations for active rows;
- closed issues for done rows;
- the age of required rows.
The result is fail, incomplete or reduced pass. It uses its own Gitea
budget of the open list plus at most 10 lookups. A full open page counts
only while an issue in some row's closes has no known state (lead
decision 40). T3 seats are exempt per run with --unsupported-runtime.
The weekly routine is in packages/ledger/README.md.

Built by Darkwing (build.patch ab1f12ca, manifest 0b20bbca). Filbert
reviewed it: round 1 81f26f2e asked for changes (C1, ISO requiredSince
never aged); round 2 ce8ce150 approved. Also carries Filbert's plan
amendment for decision 40 (68a25ffe).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:33:44 -05:00
jason.woltjeandClaude Opus 5.5 2333d837e2 docs(records): lead decision 40, Piece E questions (row 13, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:04:28 -05:00
jason.woltjeandClaude Opus 5.5 f304eaa567 docs(records): row 12 live round and close, queue-commit message follow-up (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:14:35 -05:00
jason.woltjeandClaude Opus 5.5 ff61532aa8 chore(queue): row 12 done, Piece D live round approved (comment 26579, #1508)
Round 1 on f539466f: request comment 26577 by darkwing, approval
comment 26579 by filbert, recorded at rev 17. The rev 17 entry went into
f3f48cfd, whose message doesn't mention it. No file was added under
docs/plans/reviews/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:14:25 -05:00
jason.woltjeandClaude Opus 5.5 f3f48cfdc4 chore(queue): row 13 in-progress, Piece E started (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:12:52 -05:00
jason.woltjeandClaude Opus 5.5 3c9f4244af chore(queue): row 12 in-review, round 1 request posted as comment 26577 on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:10:54 -05:00
jason.woltjeandClaude Opus 5.5 32ddb2f210 chore(queue): row 12 reviewer filbert for the live round on #1508
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:09:35 -05:00
jason.woltjeandClaude Opus 5.5 0550ab21cf docs(records): Piece D build note, TOOLS.md review verbs, DEFERRED updates (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:07:51 -05:00
jason.woltjeandClaude Opus 5.5 f539466fcb feat(queue): Piece D, reviews as issue comments, raw per-seat token helper (row 12, #1508)
queue move ID in-review posts the review request as a Gitea comment and
review record reads verdicts back, so reviews stop being files in
docs/plans/reviews/. On a comment round, in-review to waiting-on-jason
now needs every listed reviewer's approval for the current round, the
same as in-review to done (Filbert r1 C1). scripts/gitea-api.sh reads
the raw per-seat token files (lead decisions 37 to 39): config built and
checked before curl starts, export attribute cleared, fixed base URL.
test-queue.sh skips its live checks outside the canonical root.

Darkwing authored. Filbert approved D r2 (cf1d3fd0) after r1 (a2dc2302)
and corrected the plan (293747cd). Rocko reviewed the helper (e896192f,
2096b0a3), and Sage's lead check passed under decision 38. Manifest
b402fb38, 19 files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:07:29 -05:00
jason.woltjeandClaude Opus 5.5 cdcedb2741 docs(records): lead decision 39, helper lead check passed, C1 in D (row 12)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 21:45:23 -05:00
jason.woltjeandClaude Opus 5.5 4c53f8c738 docs(records): lead decision 38, Gitea helper round 2 disposition (row 12)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 21:39:10 -05:00
jason.woltjeandClaude Opus 5.5 8efc0ff330 chore(queue): row 5 note, CHAT-03 source author Dewey (lead decision 36)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:25:24 -05:00
jason.woltjeandClaude Opus 5.5 d2813a4b6b docs(records): lead decision 37, raw per-seat token files in the Gitea helper (row 12)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:25:02 -05:00
jason.woltjeandClaude Opus 5.5 8ffbd73b76 docs(records): lead decision 36, row 10 build note, export recipe limit (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:24:24 -05:00
jason.woltjeandClaude Opus 5.5 eae341d3ca chore(queue): row 10 to Jason's gate, row 16 check, header points at the goals review (#1508)
Includes Darkwing's revs 3 to 8 (row 9 to waiting-on-jason, row 12 started).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:23:47 -05:00
jason.woltjeandClaude Opus 5.5 5efe28ab01 docs(agents): seats read the queue, not CURRENT.md (row 10, #1508)
AGENTS.md cadence, pointer and recovery rule name `scripts/mosaic queue
next <seat>` and the ratified goal order. The six seat CONTEXT files run
the queue instead of reading CURRENT.md for ownership and gates. The
queue README says why queue-commit.sh calls cli.mjs directly (Filbert
A2 r1 n3).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:23:20 -05:00
jason.woltjeandClaude Opus 5.5 9e23705724 chore(queue): row 9 to waiting-on-jason for Gate G, review round 1 on A2 6ca116b7 (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:19:07 -05:00
jason.woltjeandClaude Opus 5.5 c8236f7b78 docs(records): queue genesis build note and session line (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:17:18 -05:00
jason.woltjeandClaude Opus 5.5 42f3f2d94f chore(queue): assign row 10 to sage, row 13 gate after rows 9 to 12 (lead decision 35, #1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:17:06 -05:00
jason.woltjeandClaude Opus 5.5 3377b877ba chore(queue): genesis from reviewed map (#1508)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:16:10 -05:00
jason.woltjeandClaude Opus 5.5 6ca116b7ba feat(queue): queue as data A2, migration, render and dispatch (#1508)
Filbert approved round 1 (f167b85e). Manifest 782bcb62, 21 files, plus
the QUEUE.md markers and the TOOLS.md section. Lead decision 35.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:14:09 -05:00
jason.woltjeandClaude Opus 5.5 c9539baa0f docs(records): CHAT-03 build note for unexplained aborted, lead decision 34
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:45:26 -05:00
jason.woltjeandClaude Opus 5.5 8a465e891a docs(chat-03): brief pinned at 1ef15ac0 after r3 and scope check, lead decision 33 (#1507)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:45:04 -05:00
jason.woltjeandClaude Opus 5.5 93ee5054f4 docs(plans): north star and goal order ratified by Jason, AGENTS.md pointer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:40:05 -05:00
jason.woltjeandClaude Opus 5.5 77680e0b22 docs(records): CHAT-03 r3 closed with two rulings, lead decision 32
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:39:00 -05:00
jason.woltjeandClaude Opus 5.5 8dba3ff797 docs(records): CHAT-03 seal loads no explicit extensions, lead decision 31
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:34:06 -05:00
jason.woltjeandClaude Opus 5.5 f0296e7763 docs(records): CHAT-03 rescoped against Gate E, lead decision 30
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:31:38 -05:00
jason.woltjeandClaude Opus 5.5 4bdd3fc3b0 docs(queue): close rows 1, 6, 23, 24, 25 and issues 1503, 1509, 1511, lead decision 29
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:28:13 -05:00
jason.woltjeandClaude Opus 5.5 a11e1153d7 docs(records): fix lead decision reference in DEFERRED SetSpark line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:24:28 -05:00
jason.woltjeandClaude Opus 5.5 9da0d0895f docs(records): SetSpark approver fix deployed and surveyed, lead decision 28
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:24:20 -05:00
jason.woltjeandClaude Opus 5.5 4d999e2f61 docs(queue): row 5 CHAT-03 rescope hold, row 7 ledger owner and numbers
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:22:49 -05:00
jason.woltjeandClaude Opus 5.5 f2b9e622da docs(plans): goals review with ledger evidence, lead decision 27
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:21:33 -05:00
jason.woltjeandClaude Opus 5.5 4bbacf63ae docs(queue): rows 9 and 11, queue A1 committed, A2 in progress
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:10:39 -05:00
jason.woltjeandClaude Opus 5.5 08bd3a4cc5 fix(tests): clear NODE_TEST_CONTEXT for nested node --test in two suites (#1508)
test-foundation.sh and test-discord.sh ran a nested node --test that would
exit 0 on failure under a parent runner. Both clear the variable now, and
each has a check that fails the suite if it comes back. Darkwing wrote it,
Filbert approved it (e464be6c). Nine suites green on an index export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:10:31 -05:00
jason.woltjeandClaude Opus 5.5 34a72af912 feat(queue): queue as data A1, journal, lock, CLI and verify (#1508)
packages/queue, scripts/queue-commit.sh, scripts/git-hooks and
scripts/test-queue.sh, plus docs/plans/BRIEF-TEMPLATE.md. There is no
queue.json yet, so verify skips until the genesis commit after A2.

Darkwing built it, and Filbert reviewed R0 (6933b885, changes requested)
and r1 (e464be6c, approved). The 20 files match manifest 85a8a453. The
nine suites passed on an index export, including the new queue suite.
test-queue.sh joins the suite list in AGENTS.md. Lead decisions 20, 23
and 26.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:07:48 -05:00
jason.woltjeandClaude Opus 5.5 91df7d6b54 docs(records): CHAT-03 deviation V-1 accepted with limits, lead decision 25
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:58:22 -05:00
jason.woltjeandClaude Opus 5.5 f87cd6e201 docs(records): SetSpark approver fix landed in shared-signals cc74d92, lead decision 24
Packet, Rocko's R1 and R2 reviews, DEFERRED outcome and SESSIONS.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:52:04 -05:00
jason.woltjeandClaude Opus 5.5 40a02d2bcb docs(records): queue A1 review rulings, lead decision 23
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:25:52 -05:00
jason.woltjeandClaude Opus 5.5 556772ceb2 docs(records): CHAT-03 chartered, SetSpark approver owner, lead decisions 20 to 22
Jason approved CHAT-03 and gave Sage the SetSpark approver owner choice.
Decision 20 records the queue A1/A2 split. Decision 21 makes the SetSpark
fix Mosaic's through a Sage subagent and keeps the sanctioned pending:
approver markers so the cutover migration still works. Decision 22 charters
CHAT-03 with Dewey writing the brief. QUEUE row 5, DEFERRED and SESSIONS
updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:14:02 -05:00
jason.woltjeandClaude Opus 5.5 bc73482045 docs(records): CHAT-02 Console live check passed, WebUI restarted
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:07:27 -05:00
jason.woltjeandClaude Opus 5.5 c9e771cf59 feat(webui): CHAT-02 Console, read-only conversation view (#1507)
History opens a seat's conversation from the Waiting card, table row
and inspector. It pages the whole branch through the CHAT-02 board
routes, renders untrusted text inert, polls with the follow cursor, and
marks every switch (branch, newer, reconcile, gone). The WebUI proxy
passes only the two conversation routes' queries upstream.

Dewey authored it. Filbert asked for changes on r1 (24b046af) and
approved r2 (d06de6a7) in review 160dd68d. A relaunch shows 'newer',
not 'reconcile', a deviation from brief 2.3 item 6 that Filbert
accepted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 18:05:11 -05:00
jason.woltjeandClaude Opus 5.5 3a209eeafe fix(ledger): Gate F follow-up, Filbert's notes 1 to 3 (#1506)
Darkwing's follow-up to the T3 thread source: manifest 382f5bb0 pins
t3.mjs, ledger.test.mjs and README.md. Filbert approved it (review
6fd693b6). Ledger 51/51; the eight suites pass on the index.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:57:51 -05:00
jason.woltjeandClaude Opus 5.5 a4d38a3d93 docs(records): row 25 live check passed, SetSpark approver gap has no owner
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:42:17 -05:00
jason.woltjeandClaude Opus 5.5 a68dc1740a docs(records): CHAT-02 build log, Gate F commit, snapshot isolation gap
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:40:23 -05:00
jason.woltjeandClaude Opus 5.5 136958c98b feat(ledger): Gate F, the ledger's T3 thread source (#1506)
packages/ledger/src/t3.mjs reads ~/.t3/userdata/state.sqlite read-only,
in one transaction. It maps each thread to a seat by title and checks
self-addressed headers. Unmatched threads go in a t3:unmapped row. A
missing or locked database exits 1 and names --no-t3. Gate F is on by
default (lead decision 12). The 6a uppercase-class fix rides here.

Separate item: the Pi session reader splits lines only on \n, so a raw
U+2028 or U+2029 in a string no longer splits a record. Node 26.8.1's
readline split there, and the live ledger refused on HEAD.

Darkwing built to brief R3 (f3c05c1b); manifest ba73a163. Filbert
approved the build (e47ec6da) and the U+2028 fix as its own item; brief
review be1aa414. On an index export: the eight suites
24/90/43/17/14/15/63/18, ledger 47/47. Four nonblocking notes go to a
small follow-up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:39:56 -05:00
jason.woltjeandClaude Opus 5.5 e58783d278 docs(records): CHAT-02 backend commit and board restart
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:37:07 -05:00
jason.woltjeandClaude Opus 5.5 a5beb6d97d feat(conversation): CHAT-02 read-only Pi history reader and two board routes (#1507)
packages/conversation is a library with no server: safe-fs, the Pi session
parser, CHAT-01 pages, pinned snapshots, cursors and follow. The control
board adds GET /api/conversations and /api/conversation behind the Host
and Origin guard. Both are read-only, their queries are validated, and
each refusal code maps to a status.

Dewey authored it (packet 0cf177b1, revision 2). Filbert reviewed the code:
R1 revise (branch ids moving on append, the assumed-link bridge merging
branches, one unreadable seat directory turning the catalogue into a 500),
then R2 approve (3b14d66c). Darkwing reviewed the routes: R1 approve
(07b10ad1), R2 approve (b9d92003). The package lands with the routes,
because serve.mjs imports the reader at load.

On an index export: the eight suites 24/90/43/17/14/15/63/18,
conversation and control-board 153/153, webui 9/9.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:36:20 -05:00
jason.woltjeandClaude Opus 5.5 c5db8c8819 docs(records): row 25 approver fix, second Discord restart, SetSpark approver and docker network gaps
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:31:11 -05:00
jason.woltjeandClaude Opus 5.5 20ea5a0b64 fix(discord): row 25 approvers are user names, never Discord ids in tool text (#1509)
Jason's live check after the 20:58Z restart posted no Approve button. The
Discord Sage wrote DEC-009's required_approvers as names; the SetSpark
service stores approvers as discord:<id> and accepted the names, and the
connector correctly refused the approval request ("bad approver id").

- binding.mjs derives setspark.approvers from the binding's users (name to
  id); a binding-set approvers key and duplicate names are refused. With
  setspark set, a user id or name change refuses the reload (pi's approvers
  are fixed at start).
- setspark.mjs: record_create/record_update map required_approvers names to
  discord:<id> and refuse unknown names, ids, duplicates and non-lists
  before any request, without echoing the value. hideIds turns mentions,
  discord: values and standalone 17-20 digit runs into the user's name or
  "unknown user" in every verb's text and refusal, including the service
  message and code before they are cut. The connector's approval request
  keeps the bare ids.
- tests: boundary test over nested, keyed, numeric, mention and cut ids;
  a local contract fixture from create through validateRequest, with the
  old name-stored shape still refused.

Rocko: R1 revise, R2 revise, R3 approve (81379830..., report da75219f...).
Suites on an index export: 24/90/43/17/14/15/63/18; Discord node tests 173/173.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:30:11 -05:00
jason.woltjeandClaude Opus 5.5 1c5f6bc3a0 docs(queue): queue-as-data plan round 6, Rocko approved (#1508)
Plan 282fabbb (Filbert) and the six adversarial rounds (Rocko, r6 80cde839).
Lead item 15: Gate F first, then A1 and A2 as separate reviewed commits.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:05:16 -05:00
jason.woltjeandClaude Opus 5.5 ffc22c04c6 docs(ledger): Gate F T3 thread source brief R2, Filbert approved (#1506)
Brief e8300cb6 (Darkwing), review bb02d8d3 (Filbert, approve with three
nits), R1 record and R1-to-R2 diff. Lead rulings in item 14.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 16:04:56 -05:00
jason.woltjeandClaude Opus 5.5 34777c56bc docs(records): row 25 SetSpark fix, Discord restart receipt, Gate F rulings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:58:59 -05:00
jason.woltjeandClaude Opus 5.5 6c06a6f358 fix(discord): row 25 SetSpark key reaches pi and the connector (#1509)
resolveToolRoots dropped tools.setspark, so the record verbs never reached
pi's --tools list and the connector never built its SetSpark client. The
resolved config now carries only baseUrl, keyFile, principal and timeoutMs,
the keys the extension's loadSetsparkConfig accepts; the extension restores
the response cap. The connector's client uses the validated binding object,
which keeps the cap. README: principal is required, timeoutMs is optional.

Test (failing first): binding -> resolveToolRoots -> JSON -> loadToolsConfig
gives the binding's config, and the verbs reach enabledToolNames. Eight
suites green on an index export. Rocko approved
(agents/rocko/work/row25-setspark-fix-review-2026-09-26.md, a28df89e).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:57:55 -05:00
jason.woltjeandClaude Opus 5.5 84d0965142 docs(deferred): 6b closes the leaked fake pi and concurrent hang entries; wedge-restart and startup-source follow-ups
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:51:05 -05:00
jason.woltjeandClaude Opus 5.5 3edb15eb96 fix(discord): engine tests stop in finally; a turn pi never started stops pi instead of guessing (#1509)
Every engine test stops its engine in finally, and commands() tolerates a
log that doesn't exist yet, so a failed assertion no longer leaks a fake pi
and hangs the six-package union. A turn that failed client-side stays at
the front of the queue and holds the next prompt. If pi has sent no
agent_start ABORT_GRACE_MS (30 s) after the failure, the engine marks
itself wedged, fails held prompts with engine-wedged, refuses new ones with
engine-down, and stops pi. The exit reaches onExit, the connector exits 1,
and the unit restarts it. Pi's events carry no prompt id, so R1's approach,
dropping the turn and sending on, let a late run answer the next prompt.
Rocko rejected R1 and approved R2.

Tests: engine 17/17 (R1 fails 4, HEAD fails 5). Union 408/408 and the eight
suites green on the committed index. Record:
agents/darkwing/work/discord-engine-busy/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:50:37 -05:00
jason.woltjeandClaude Opus 5.5 f55b94e866 docs(records): board guard live, restart receipt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:40:56 -05:00
jason.woltjeandClaude Opus 5.5 d1629d610d fix(board): refuse foreign Host and Origin on every control-board route (#1507)
After a DNS rebind, a web page could read /api/board and POST /api/reply,
which pastes into a live seat pane. foreignRequest() now runs first and
returns 403 for a non-loopback Host, a wrong port, userinfo or a path in
Host, or any Origin other than http://<Host>. A missing Origin still passes,
which covers the WebUI proxy. Dewey authored it; Rocko approved de9ff942
(review 5a12f08e) with one low wording finding, now fixed in the notes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:40:34 -05:00
jason.woltjeandClaude Opus 5.5 401cc850bb docs(records): correct walkthrough ruling times from the transcript, note 6b R1 verdict
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:36:03 -05:00
jason.woltjeandClaude Opus 5.5 993673865a docs(records): Jason's walkthrough rulings, Sage moved to SetSpark, CHAT-02 go
Jason ruled on seven open items (20:27Z-20:45Z): seat Gitea tokens read in
place, one Discord restart after 6b with row 25 live, row 8 limited to the
dev seats, DYOR in dyor-stack-v4 with Sage moved to SetSpark, skills/aws-*
excluded locally, no second WebUI return defect, and go on CHAT-02 only.

Sage persona files name SetSpark as its business work. Darkwing's SOUL drops
harness names that were wrong for T3. DEFERRED adds the slash-prefix paste
hazard and the board Host/Origin gap, and moves the ledger T3 item to Done.
Dewey's approved CHAT-02 brief (636b0fac) and Filbert's review are recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:35:15 -05:00
jason.woltjeandClaude Opus 5.5 ef0020ad85 fix(ledger): count the T3 header as agent, control-board sender as board (#1506)
messageKind knew only the tmux preamble, so a prompt opening with the T3
header [from: role (id) -> to: role (id)] counted as human in Table 2. The
first line now matches either form; anything short of the full header stays
human. Filbert approved R1 against the frozen hashes.

Proof: packages/ledger/tests/ledger.test.mjs, 22/22; against HEAD's
ledger.mjs it fails exactly the two new tests. No suite runs it. Eight
suites green on the staged tree.

The fix changes zero current counts: no Pi log under .pi/state contains a
T3 header, and the ledger does not read T3 transcripts. Gate F waits on a
T3 thread source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:16:55 -05:00
jason.woltjeandClaude Opus 5.5 d41f81aafe feat(agents): Sage launch files, lead text across seat personas, lead decision record
- agents/sage/ launch files committed after Dewey's review (R1 revise, R2
  approve). The launcher test now covers sage with its zai/glm-5.3 high pin.
  The README states the lead role and its limits, and the seat reads but
  never writes the old DYOR records under ~/.mosaic.
- N6 (Dewey authored, Sage reviewed against pins): seat personas and the
  Rocko launcher name Sage as project lead and Darkwing as a collaborating
  engineering seat, per Jason's 2026-09-26 ruling.
- docs/plans/2026-09-26_lead-decisions.md: the push, no merge into next and
  its conditions, the board restart, queue-as-data rulings, Gate F waiting
  on a T3 source, and what stays with Jason.

Launcher tests 6/6 and 1/1, eight suites green. Not pushed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:11:10 -05:00
jason.woltjeandClaude Opus 5.5 42c08d5285 fix(webui): pending reply notice until the seat answers, relative Age (#1507)
Dewey's return-flow candidate on the #1512 R1 baseline. The inspector used to
show the previous answer while a seat worked on a reply, which looked like the
reply; it now shows a pending notice that clears on the new final answer.
Age shows a relative time beside the ISO time. Filbert approved R2, source
only; the patch reproduces the pinned hashes (app.js d1a51646,
return-flow.test.mjs a598c0d4). webui tests 9/9, all eight suites green.
Known limits are in agents/dewey/work/return-flow-age/NOTES.md. Not pushed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:00:59 -05:00
jason.woltjeandClaude Opus 5.5 0f5b7cb9be docs(records): Sage lead handover, rows 23-25 state, row 8 stub, BUILD-LOG rebuilt on HEAD
Records Jason's 2026-09-26 ruling: Sage leads the project, Darkwing is a
collaborating seat, development stays in T3, and the old ~/.mosaic fleet is
being retired. The lead role adds no push, merge or deploy authority.

- QUEUE rows 23-25 show their commits and pushes; row 8 links a parked stub
  brief listing the rulings Jason must make before fleet seats move.
- Shared records from Darkwing (rows 6, 16, 18, 22, #1511, #1512) and Dewey
  (row 5) that were waiting on one owner for the shared files.
- DEFERRED: T3 headers counted as human in the ledger (#1506), #1509 engine
  test leak and busy gap, #1512 re-run outcome.
- BUILD-LOG.md rebuilt as HEAD plus the uncommitted entries; the working copy
  had dropped the row 23-25 entries. Diff against HEAD is additions only.

All eight suites green. Not pushed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 14:59:24 -05:00
jason.woltjeandClaude Opus 5.5 af4203ca92 feat(board): session attention, Discord rows, task attribution and relaunch activity (rows 18, 22, #1511, #1512)
One cumulative control-board, webui and seat state. The four rows edit the
same files (scan.mjs, page.html, README.md, app.js), so they land together,
each on its own receipt:

- Row 18, Discord connector rows on the board (#1509): R3 approved by
  Darkwing and Dewey, Gitea comment 26257, manifest 254403b8. Jason
  accepted the visual test.
- Row 22, board attention status (#1503): Filbert approved R1, comment
  26248, manifest e40b58ec; restart receipt 26249.
- #1511, task attribution (row 6 code phase): R2 approved by Filbert and
  Dewey, manifest d4c96395. docs/TOOLS.md carries the approved --by usage
  line (tools-usage.patch 86bcba3c).
- #1512, relaunch activity (row 6 pilot): R1 approved by Darkwing and
  Dewey, candidate manifest 47769fad. All seven source files match it.

Row 16, internal development bootstrap (#1510): the seven files outside
shared records match Filbert's R1 pins, receipt 26204 (agents/researcher/*,
scripts/test-darkwing-launch.mjs, the bootstrap plan).

packages/webui/src/public/app.js is committed at its #1512 R1 pin ce7d79a4.
The working copy holds Dewey's unreviewed return-flow candidate on top of
that, and it stays uncommitted.

Also: the four row briefs and Darkwing's evidence records under
agents/darkwing/work, including the 2026-09-26 tree manifest and the #1512
re-run against 21e3e908. Serial acceptance command: 397/397, three runs.
The failures that only show when tests run concurrently are in #1509 engine
tests, and they reproduce on clean HEAD.

Suites on the exact staged tree: config 24, task 90, foundation 43,
conductor 17, release 14, auth 15, discord 63; package union 397/397
(serial); test-darkwing-launch 5/5.

Shared records (BUILD-LOG, QUEUE, CURRENT, DEFERRED, SESSIONS, AGENTS.md,
agents/README.md) follow in Sage's records commit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 14:54:18 -05:00
jason.woltjeandClaude Opus 5.5 21e3e908b6 docs(comms): T3 threads as the temporary development channel beside tmux
Jason, 2026-09-26: while Mosaic Stack development runs in T3, development
agents message each other through T3 threads with the t3 MCP tools, until
Mosaic Stack has its own internal comms. ms-communications now picks the
transport by where the recipient runs, carries the T3 header and reply
rules, and counts a returned turnId as the delivery receipt.
docs/guides/T3-AGENT-COMMS.md is adapted to this repository: status,
thread lookup by agent-name title, replies with wait off, and record
locations; references to guides absent here are replaced.

Suites green before commit: config 24, task 90, foundation 43,
conductor 17, release 14, auth 15, discord 63. unslop-check clean on
both files. Reviewed by Sage (T3 thread 1ef1e4f8).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 14:33:24 -05:00
jason.woltjeandClaude Fable 5.1 43d7574d6a feat(discord): SetSpark record client for the Discord Sage, fixed verbs against setspark-api, connector-verified approvals (#1509)
Row 25, parts 2a and 2b, against the shared-signals contract a5425a2.

Model side: eight fixed verbs in the pi extension (record_list, record_get,
record_create, record_update, resolve_id, open_approval_request,
get_approval_request, create_document), each one HTTP call with arguments
checked before any request. Writes carry an idempotency key
<principal>:<message id>:<call index> and an audit context. The seat key is
read from a 0600 file on every call and never cached, printed or journaled.

Connector side: append-only approval ledger, Approve button and exact
"approve" reply resolved by the connector against the required approvers,
confirmation message posted as button evidence, bind and add_approval through
the service under connector keys, retry of unknown entries on start.

Evidence: node tests 162 pass, scripts/test-discord.sh 63/63. Review by
rev-code-02, round 1 approved (#1509 comment 26467, tree 7872d8c5).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 12:59:39 -05:00
jason.woltjeandClaude Fable 5.1 1949ed8d31 feat(discord): git verbs for the Discord Sage on the shared-signals root, seat identity through a package credential helper, vault record protocol (#1509)
Row 24. A writable root that is a git work tree may carry a git object in
the binding; the seat then has git_status, git_commit (explicit paths, seat
author, Requested-by trailer from the envelope requester, push at once per
D6), git_pull (ff-only) and git_push (one branch, never force), plus
reserve_id and per-write clone locks under protocol vault. Git children run
with no host config and one credential helper, bin/git-credential.mjs,
reading the 0600 seat token file named in the binding; the fleet helper
serves only the Gitea hosts. Suite 58/58, node 143. rev-code-02 APPROVED
round 1 (#1509 comment 26375, tree 82ab962f).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-18 07:52:35 -05:00
jason.woltjeandClaude Fable 5.1 1685deb423 feat(discord): writes on write-marked roots, web fetch and search, held prompts (#1509)
Row 23. write_file and edit_file for roots marked write: true under the
same fence as reads; web_fetch (https only, public addresses, pinned
connection, capped body) and web_search through SearXNG; extension
renamed to tools.mjs. Engine holds a prompt while pi is busy and sends
it as its own run, so a second message mid-turn no longer folds into
the first (live defect). fake-pi models the real follow-up folding.

Suite 52/52, node tests 129. rev-code-02 APPROVED round 3, comment
26362, tree dbd2ce9a. Records: QUEUE rows 23-24, CURRENT, BUILD-LOG
phase, SESSIONS, row 24 brief (git verbs, D5-D7 ruled).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-18 07:27:50 -05:00
jason.woltjeandClaude Opus 5 1ac812d3d5 feat(discord): read-only tools for the Discord Sage through a Mosaic pi extension confined to declared roots (#1509)
A binding may declare `tools` with named roots. pi starts with
--no-builtin-tools and the package's own extension, allowlisting
list_dir, read_file and search. src/tools.mjs holds the rules: names
not paths, per-segment lstat walk, one checked descriptor read that
refuses symlinks, swaps, FIFOs, hard links and oversize files, credential
shapes refusing the whole read, and a per-message call budget. The engine
settles on agent_end and records tool calls in the turn record.

Jason's rulings R1-R7 in the brief, section 7. rev-code-02 approved
round 2 (comment 26276) on tree 43f0329b after four round 1 fixes.
Suite 48/48, node tests 116. Not pushed.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-14 19:52:21 -05:00
jason.woltjeandClaude Fable 5.1 c4fc8e7d7f docs(discord): brief read-only tools for the Discord Sage as row 21, waiting on D1–D3 (#1509)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 19:33:45 -05:00
jason.woltjeandClaude Fable 5.1 1267e6a0a0 docs(discord): rows 19–20 done, Carmen enrolled live by reload; records and receipt (#1509)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 19:03:53 -05:00
jason.woltjeandClaude Fable 5.1 caaef941e6 feat(discord): binding reload without a restart, and a per-user channel allowlist (#1509)
`reload` validates the binding file and sends SIGHUP to the live owner;
the running connector re-reads it and swaps guildName, channels, users
and limits in place. name, seat, guildId, botUserId, tokenFile, engine
and context are fixed for the life of the process; a change there, an
invalid file or a channel outside the guild refuses the reload and keeps
the old binding. Every attempt is one line in reloads.jsonl. The service
unit maps `systemctl --user reload` to the same signal.

A user entry may carry `channels`, an allowlist of listed channel ids;
absent means every listed channel. Outside the list the message is
dropped as channel-not-for-user; threads count as their parent.

Suite 41/41, 101 node tests. QUEUE rows 19 and 20 opened.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 18:59:31 -05:00
jason.woltjeandClaude Fable 5.1 d9745a4510 docs(discord): row 17 operator check passed, service unit verified by Jason (#1509)
Jason ran traffic under the unit, SIGKILL recovery, the brake, and the
release, and reported all verified. Receipt in the private evidence dir.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 16:29:19 -05:00
jason.woltjeandClaude Fable 5.1 3affbab5e2 docs(discord): row 18 assigned to darkwing by Jason's ruling (#1509)
The control board row for the Discord connector is board-side work in
packages/control-board. Jason ruled darkwing builds it under the board
brief; the coordinator answers connector-side questions only.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 16:20:15 -05:00
jason.woltjeandClaude Fable 5.1 90cb31f56f docs(discord): brief the control board row as iteration 3, blocked on ownership (#1509)
QUEUE row 18. The board discovers rows from pi session directories and
decides liveness by tmux; the connector's session and run.lock live
elsewhere and it has no pane since iteration 2. The brief lists what the
board-side change needs. Owner for Jason to rule.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 14:40:29 -05:00
jason.woltjeandClaude Fable 5.1 436ba6ed6b feat(discord): systemd user service with a supervised run; brakes exit 3 and are never retried (#1509)
QUEUE row 17, MVP iteration 2. scripts/discord-service.sh renders and
installs mosaic-discord@<binding> from packages/discord/systemd/. The
unit's main process is `run --supervised`, which applies the new recover
policy first: a lock whose owner is gone is cleared and only the STOP
written for that is removed; an operator STOP or a held binding refuses
with exit 3, which RestartPreventExitStatus never retries. `recover` is
also a CLI verb. First cut used ExecStartPre and looped live, since systemd
honours the never-retry status only from the main process; replaced and
re-verified before any message traffic. Suite 40/40, 95 node tests.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 14:39:11 -05:00
jason.woltjeandClaude Fable 5.1 dc5902aafd docs(discord): read receipt live check passed, QUEUE row 15 done (#1509)
Jason's message in #sage-admin got the eyes reaction before the reply; the
turn record carries receipt.ok true and a private evidence receipt was
written. CURRENT.md gains the pilot narrative line.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 14:22:34 -05:00
jason.woltjeandClaude Fable 5.1 93d6b62457 feat(discord): eyes reaction as a read receipt on every admitted message (#1509)
QUEUE row 15, MVP iteration 1 after the Sage pilot. rest.react is best
effort (2xx true, anything else false, never throws); the connector reacts
at admission before the engine runs and records the outcome in the turn
record as receipt. Drops and refusals get no reaction. Suite 28/28, 90
node tests.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 14:20:30 -05:00
jason.woltjeandClaude Fable 5.1 788515dc69 docs(discord): close the Sage pilot, Gate H passed; correct the Discord profile wording (#1509)
Live pilot receipts summarized in BUILD-LOG (eight steps, private
evidence under the seat's work directory). Jason ruled the replies read
as Sage; QUEUE row 14 done. DISCORD-USER.md now says unlisted senders
are dropped silently rather than refused with a reply, which is what
the connector does.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 13:39:25 -05:00
jason.woltjeandClaude Fable 5.1 786e379c49 feat(discord): connector pilot for the Sage seat, reviewed candidate (#1509)
Zero-dependency Discord connector under packages/discord: binding
validation, REST and gateway clients, pi engine adapter, journal with
append-only inbox, outbox, admissions and notices, and a run.lock
ownership record {pid, start, boot} whose identity is checked three ways
and whose cleanup is gated by STOP. CLI check|run|stop|unlock via
scripts/discord.sh; offline suite scripts/test-discord.sh (28 checks,
87 node tests).

Reviewed by rev-code-02 on #1509 over nine rounds; approved exact tree
4e0feb6758c0a7e4a71483912a8e0d3e3ec95aef at comment 26170. Corrections
(1) to (12) recorded in BUILD-LOG. No listener started, no token read,
no Discord write; the live pilot follows this commit per the brief.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-13 01:15:37 -05:00
darkwing b023841c8a docs: publish reviewed CHAT-01C private readback contracts (#1507) 2026-09-13 00:48:26 -05:00
jason.woltje 28d4e98ad8 docs: publish reviewed CHAT-01 draft contracts and fixtures (#1507) 2026-09-12 23:48:03 -05:00
jason.woltje 370823b354 docs: pin CHAT-00 protocol research and synthetic checks (#1507) 2026-09-12 20:43:35 -05:00
jason.woltje 4c436f4ce4 plan: confirm all-seat WebUI session chat and gated delivery (#1507) 2026-09-12 20:28:22 -05:00
jason.woltjeandClaude Fable 5.1 0e77cfd10a Plan page for #1508: queue as data, pieces A-E, Gate G; QUEUE rows 9-13 link to it
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:45:14 -05:00
jason.woltjeandClaude Fable 5.1 cbf24b01b0 QUEUE rows 9-13 (#1508): process becomes data, one writer, ledger checks; AGENTS.md cadence reads QUEUE.md first
Jason: the process goes off the rails every time because the entry point is
prose. Rows 9-13 are marked required and cannot be parked.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:41:56 -05:00
jason.woltje 11659cf2a1 records: hand Console to Jason for Gate E after publication (#1507) 2026-09-12 19:39:14 -05:00
jason.woltje ea00ec66d9 webui: serve the Console first screen from the control board (#1507) 2026-09-12 19:37:35 -05:00
jason.woltjeandClaude Fable 5.1 7c8e530add QUEUE.md: one task table; CURRENT and DEFERRED point at it
Jason could not find what is next without reading the prose plans. QUEUE.md
is one row per piece with owner, issue, state, gate and brief location.
CURRENT.md becomes the narrative log; DEFERRED.md keeps only gaps.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:33:59 -05:00
jason.woltjeandClaude Fable 5.1 355b4308c3 CURRENT points at the piece 5 brief; DEFERRED: shared-checkout hazard (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:26:19 -05:00
jason.woltjeandClaude Fable 5.1 f80404f541 brief for piece 5, darkwing on point, with Gate F (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:25:53 -05:00
jason.woltje fab40f25f8 records: close ledger and seat registration after owner acceptance (#1506, #1504) 2026-09-12 19:18:47 -05:00
jason.woltjeandClaude Fable 5.1 b7f8d1cc35 Gate D written; brief for piece 4, the WebUI first screen on the Console design (#1506, #1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 19:05:44 -05:00
jason.woltjeandClaude Fable 5.1 46ded3fcba record Jason's comms rule and working preferences in the repository (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 18:59:33 -05:00
jason.woltje cd0aa5fbb3 ledger: count issue-tagged commits and repo seat messages (#1506) 2026-09-12 12:04:15 -05:00
jason.woltjeandClaude Fable 5.1 db0d784bd8 plans: add DEFERRED.md, the one list of gaps found and not yet handled (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:53:24 -05:00
jason.woltjeandClaude Fable 5.1 889d87500f CURRENT: next action is piece 3, the ledger, per Jason's go (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:42:56 -05:00
jason.woltjeandClaude Fable 5.1 0334026e13 control board: brief for piece 3, the ledger, with Gate D (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:42:28 -05:00
jason.woltjeandClaude Fable 5.1 c90ce3a836 control board: Gate C passed, #1505 closed, next action waits for the ledger brief (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:39:23 -05:00
jason.woltjeandClaude Fable 5.1 4f83068097 control board: log Gate C pass for reply-from-board (#1505)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:38:55 -05:00
jason.woltjeandClaude Fable 5.1 867619dca2 control board: reply from the board through agent-send.sh (#1505)
Piece 2 of the MVP (#1503). A one-line reply box and Send in the detail
of rows with a live registration; POST /api/reply runs
tools/tmux/agent-send.sh -s <session> -S <host>:control-board
[-L <socket>] -m <text> once for one seat and returns the exit code,
stdout and stderr. The page shows delivered or failed with the tool's
stderr; other rows say "reply needs a registered seat". No send-keys,
queue, retries, history or broadcast; packages/seat and agent-send.sh
untouched. Every message ends with a fixed trailer telling the seat to
answer in its own session (Jason's refinement after the first Gate C
exchange; the board has no pane). Board suite 98/98.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:30:30 -05:00
jason.woltjeandClaude Fable 5.1 a62ca1904f control board: brief for piece 2, reply-from-board, with Gate C (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:17:28 -05:00
jason.woltjeandClaude Fable 5.1 0bed9ba3db control board: show the model each seat is running (#1503)
Jason asked for the running model (sonnet, opus, gpt-6-astra) on the
board. readSession keeps model and provider from the log's latest
model_change entry or assistant turn, whichever is later, so a /model
switch shows on the next scan; scanAgent exposes both; the page shows the
model under the agent name with the provider in the hover and a Model
detail row. Blank when the log names none.

Live check on a scratch board against the real data root: all 42 rows
carried a model. Board 91/91. Sonnet review caught a double-escaped hover
title; fixed.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:13:32 -05:00
jason.woltjeandClaude Fable 5.1 d0fb5b0f20 control board: log Gate B pass, seat registration shown on the board (#1504)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:13:05 -05:00
jason.woltjeandClaude Fable 5.1 17153fe140 control board: stale registrations and a launch-test leak (#1504)
Two defects in 69f99323, reported by the professor session and verified.

The darkwing launch test's flock-contention spawn ran without the fixture
config, so launch.sh re-entered scripts/mosaic against the real data root
and wrote fixture records for darkwing, dewey and filbert there. That
spawn now names the fixture config, and both launch test files set
MOSAIC_CONFIG to a nonexistent path and clear MOSAIC_LAUNCH_REGISTERED
process-wide, so a spawn that forgets fails instead of polluting.

A registration is written before the launch script's own checks, so a
refused launch left a record with a dead pid that the board honoured. The
scanner now probes the recorded pid (pidAlive, signal 0); a gone pid makes
the record stale: still on the Registered line with alive false, derived
task, project and workspace win, index gains registrationStale, CLI
summary gains a stale count.

Fleet launchers marked not planned per Jason. Board 90/90, seat 15/15,
launch scripts 5/5. Sonnet review APPROVED.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 11:04:49 -05:00
jason.woltjeandClaude Fable 5.1 69f99323c7 Add mosaic launch <seat> with seat registration for the control board (#1504)
New package packages/seat and wrapper scripts/mosaic. `launch <seat>` writes
<dataRoot>/seats/<layout>/<seat>/registration.json and then execs the seat's
launch.sh unchanged; `seat task <seat> <text>` edits the task only. The board
reads registrations, matches by sessions directory, and lets a registered
task, project or workspace override the derived value with a source tag.
The four repository launch scripts register themselves unless already
registered or run with --check. Fleet launchers untouched; one-liner on the
plan page.

Review found the record path keyed by seat name alone (repo and fleet
"darkwing" would collide); fixed by keying on layout. Also: the Pi pin
refusal now names installed and required versions.

Tests: seat 15, control-board 89, launch scripts 5, registry 69, config 24.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 10:56:17 -05:00
jason.woltjeandClaude Fable 5.1 01d9a19612 control board: record Jason's selection of Dewey design 3 (Console) for the WebUI (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 10:39:54 -05:00
jason.woltjeandClaude Fable 5.1 4a7e16c3ec Show task, active project and workspace per control board row (#1503)
Gate A fix asked by the professor session on Jason's behalf. Each row
now carries three derived fields, shown as "unknown" when the log and
tmux do not hold them:

- task: the session's first user message (pi logs have no task envelope)
- workspace: the live pane path of the pane running pi, else session cwd
- activeProject: basename of the nearest git checkout above the workspace

tmuxInspect replaces the bare liveness call in the CLI and returns
{ alive, workspace }; tmuxIsAlive stays as a wrapper. The grouping column
and seen.json keys are unchanged. Fixture test per field, tmux parse
tests, page test; missing launcher signals are recorded in the plan page.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 09:52:45 -05:00
jason.woltjeandClaude Fable 5.1 6ec253de20 Pin the mid-tool-call rule in the control board scanner (#1503)
Acceptance rule (plan page, bf641e22): a seat mid-tool-call is working,
never waiting. The scanner already met it through pi's stopReason values;
deriveState now also checks the content for a toolCall block (working),
after the error stop reasons and before "stop" (waiting). Thinking blocks
do not keep a text turn from being waiting. Three JSONL fixture tests and
three state-table cases pin the rule. Live check on the real board:
orch-01 and rev-code-01 mid-tool-call are working, velma's finished
text-only turn is waiting. Sonnet review: APPROVED.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 09:29:19 -05:00
jason.woltjeandClaude Fable 5.1 bf641e220a control board: park D05/registry-3/new seats and add the mid-tool-call working rule (#1503)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 09:25:32 -05:00
jason.woltjeandClaude Fable 5.1 bf1391a424 Show "shown of total" in control board project headers while rows are hidden (#1503)
A project header now reads "fleet (13 of 38)" while Hide offline or Hide
seen hides at least one row, and "fleet (38)" when nothing is hidden. The
note under the table still says which filter hid how many. Numbers only,
so nothing new needs escaping. Static test pins the expression and the
removal of the raw-length header. Sonnet review: APPROVED, no findings.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 09:00:21 -05:00
jason.woltjeandClaude Fable 5.1 e90d15dd70 Add a per-project "Hide seen" checkbox to the control board page (#1503)
Each project table now has "Hide seen" beside "Hide offline", both on by
default, with a note saying how many rows each one hides. The choice
survives the 10-second refresh. Static test pins the markup, the filter,
the persistence guard, and the change handler. Sonnet review: APPROVED.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 08:55:22 -05:00
jason.woltjeandClaude Fable 5.1 b70749219f Add a collapsed "Seen" section to the control board page (#1503)
Jason asked for a way to recall rows he marked Seen. The page now lists
them under a collapsed "Seen (N)" section between "Waiting on you" and
"By project", each with Unsee. Open/closed state survives the 10-second
refresh because only the section body is re-rendered. Page-only change
plus one static test. Tests: control-board 64/64, registry 69/69.
Review APPROVED, no findings.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 08:45:51 -05:00
jason.woltjeandClaude Fable 5.1 88d21defde Check pi liveness per tmux pane and add "Seen" marks to the control board (#1503)
First step-3 refinement from Jason's daily use. Liveness now lists the
panes of the agent's tmux session and counts it alive only if a pane runs
pi, so killed pi sessions whose tmux session still exists show offline
instead of waiting. A "Seen" button on waiting and error rows stores the
row's lastActivity in <dataRoot>/board/seen.json (clicks only, never
rewritten by a scan, fail closed if corrupt) and drops the row from
"Waiting on you" until the agent writes anything newer; "Unsee" reverses
it. New POST /api/seen route: JSON only, 4 KB limit, 400 on bad input.

Tests: control-board 63/63 (30 new), registry 69/69. Review APPROVED;
receipt docs/plans/reviews/2026-09-12_control-board-step3-seen-marks.md.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 08:24:49 -05:00
jason.woltjeandClaude Fable 5.1 ebedd1281e Add control board web page and local server (#1503)
Step 2 of the control board MVP (MOSAIC-STACK-D-001): `serve` command starts
a loopback-only local server that serves one self-contained page and re-runs
the status scanner on each /api/board request. The page lists sessions
waiting on Jason first (errors on top), then one table per project with
plain-word states, ages, last messages, expandable detail rows, per-project
hide-offline, and a 10-second auto-refresh with pause.

Tests: control-board 33/33 (10 new: loopback rules, host refusal, all routes,
per-request rescan, 500 path, CLI refusals, live serve, page escaping guard);
registry 69/69 unchanged. Receipt:
docs/plans/reviews/2026-09-12_control-board-step2-review.md.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 07:58:38 -05:00
jason.woltjeandClaude Fable 5.1 b9f59a5903 Add control board status scanner and MVP plan (#1503)
Step 1 of the control board MVP (decision MOSAIC-STACK-D-001): a plan page,
Gitea #1503, and packages/control-board, which reads each agent's newest pi
session log plus tmux liveness and writes one status file per agent under
<dataRoot>/board/. 23/23 tests; independent review approved after three
fixes (length stopReason as error, unknown liveness state, secrets-boundary
test). CURRENT.md now points at step 2, the page.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 07:26:03 -05:00
jason.woltjeandClaude Fable 5.1 1993039c76 Record owner acceptance and closure of #1500 fixture increment
Jason reran the two-test fixture demonstration on the canonical checkout
at 5abbabb7 (2/2 pass). Records-only: acceptance receipt, BUILD-LOG,
SESSIONS and CURRENT.md next action (MVP re-plan). No source change.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-12 06:47:24 -05:00
jason.woltje 5abbabb74b Record fixture increment review, publication and owner test gate (#1500) 2026-09-10 20:23:37 -05:00
jason.woltje 3daee5ad89 Add fixture-only execution materialization and refresh (#1500) 2026-09-10 20:20:44 -05:00
jason.woltje 27e4873acc Record reviewed prerequisite correction and separate next approval gate (#1500) 2026-09-10 16:41:36 -05:00
jason.woltje 6335342873 Correct registry validation and contain metadata reads (#1500) 2026-09-10 16:40:16 -05:00
jason.woltje b3fa221060 Record owner acceptance of increment 1 (#1499) 2026-09-10 16:17:22 -05:00
jason.woltje 5446097483 Record increment 1 approval after contamination fix (#1499) 2026-09-10 15:23:58 -05:00
jason.woltje e5a9a05ba2 Drop uncommitted sage agent contamination and dangling export (#1499 I1-F1/F2) 2026-09-10 15:23:02 -05:00
jason.woltje e6e30b6f55 Record increment 1 review request (#1499) 2026-09-10 15:18:27 -05:00
jason.woltje 557aba0f9e Add packages/mosaic registry schemas, validation, read-only CLI; pin pi 0.85.1 (#1499) 2026-09-10 15:18:15 -05:00
jason.woltje 54c1231280 Draft M20 increment 1 charter for owner approval (#1499) 2026-09-10 15:10:20 -05:00
jason.woltje 21461c8674 Register completed registry gate 7 closure 2026-09-10 15:08:58 -05:00
jason.woltje 86e009dde5 Close registry gate 7 with owner-approved refresh mechanism 2026-09-10 15:08:40 -05:00
jason.woltje 224147eccc Record owner rulings resolving registry review gates 2026-09-10 14:42:31 -05:00
jason.woltje d5307d2b0a Register completed registry alignment records 2026-09-10 14:28:07 -05:00
jason.woltje b8008eda5d Record owner account-selection rulings and registry draft revision 2026-09-10 14:27:47 -05:00
jason.woltje 50d2a2eba9 Record independent verification and completion of skill repair (#1498) 2026-09-08 18:13:46 -05:00
jason.woltje f3dce32088 Repair native launcher skill rename and fail-closed regression coverage (#1498) 2026-09-08 18:12:20 -05:00
jason.woltje 12ff5da7df Record owner acceptance and close publication recovery trial (#1497) 2026-09-08 17:57:29 -05:00
jason.woltje 10448e41a2 Record verified publication and owner acceptance handoff (#1497) 2026-09-08 17:20:12 -05:00
jason.woltje 29c1defe29 Publish reviewed development agents and WUI draft with recovery evidence (#1497) 2026-09-08 17:15:03 -05:00
jason.woltje 3b7fd19d08 docs: verify published wave and set registry review next 2026-09-08 12:44:13 -05:00
jason.woltje 69f10a4062 fix(tmux): explicit transport-only dispatch and safe remote quoting (#1496) 2026-09-08 12:41:25 -05:00
jason.woltje 67eaf6fb47 docs(logs): pending session/build records through 2026-09-07
Append-only BUILD-LOG phases, SESSIONS registrations, and CURRENT
checkpoint state accumulated through the consolidation and inspector
review waves.
2026-09-07 14:07:16 -05:00
jason.woltje 3ea385223e feat(skills): six new ms-* skills
ms-archify (evidence-based architectural mapping), ms-sdlc,
ms-proactive-agent, ms-goal, ms-grill-me, ms-frontend-design.
2026-09-07 14:07:16 -05:00
jason.woltje 193479b52d docs: concept annexation, provider/reference docs, ACT-1 groundwork
Mosaic concepts pages now own the adapted content; source/license
metadata under docs/reference/concepts. Adds ACT-1 agent-context
planning capture, pinned concept test package + preparation utility,
foundation observation notes (durability, evidence, federation,
onboarding, workflow), and the #1495 consolidation assessment.
TOOLS.md updated for the host-dev launcher.
2026-09-07 14:07:05 -05:00
jason.woltje 7c580a5625 feat(agents): darkwing host-dev launcher + entry-point consolidation
scripts/agent.sh --host-dev delegates to scripts/agent-host-dev.sh;
agents/darkwing/launch.sh provides the native development TUI
(context, skills, coding tools, /goal). Root SOUL.md is the M14-era
default-collaborator persona captured by the ACT-1 context work.
Launcher regression checks pass (test-darkwing-launch.mjs).
2026-09-07 14:06:54 -05:00
jason.woltje 8ebddd6f93 feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
Rocko-authored, Filbert-reviewed inspector (r6 manifest
a4a44930...) with full review/build/verdict evidence under
docs/plans/reviews. 43/0 selftests, oracle zero-disagreement,
foundation checker PASS. Owner A9 acceptance recorded separately.
2026-09-07 14:06:35 -05:00
jason.woltje 127a54fdff chore: consolidate new foundation and archive v1 (#1495) 2026-09-07 12:32:57 -05:00
Dewey 9a5fbdbda7 fix(goal): quiet waits and unify fleet NG ownership (#56, #57, #58) 2026-09-06 04:07:09 -05:00
jason.woltje 7345f330fc docs: map foundation to integrated rewrite baseline 2026-09-06 02:40:05 -05:00
jason.woltje d4696d09eb feat(extensions): establish canonical goal source (#54, #55) 2026-09-06 02:32:32 -05:00
jason.woltje 44f257cb06 docs: record accepted phase-2 foundation contract 2026-09-06 02:23:15 -05:00
code-infra-01andorch-01 5d27700026 fix(#1257): confirm delivery by draft transition, not prompt detection (adopts #1262) (#1332)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-infra-01 <[email protected]>
2026-09-04 22:25:13 +00:00
jason.woltje 69d1bb3aa4 docs(plan): resolve harness IDs + lifecycle review gate (#50)
Owner adjudication:
- canonical harness IDs match executables: pi, claude, codex, opencode
- agent.json uses one scalar harness ID; registry/manifest resolution, no
  hard-coded schema enum
- target mosaic harness list/detect/install/rm/status lifecycle
- detection recognizes reviewed executables and records compatibility
  without reading/copying harness homes
- installs are exact-version/verified, Mosaic-managed, never global
- detected external harness is available but not container-ready until
  imported/installed, absent a separately reviewed host adapter

Gate 1 resolved. Remaining P0/review gates stay open; no implementation
authorized. Suites 24/15/90/14/17 + verify green; unslop clean.
2026-09-04 12:26:57 -05:00
jason.woltje 9ea7dea711 docs(review): record independent glm-5.3 auth-registry spec review (#50)
Verdict ACCEPT WITH CHANGES. Persist ten-gate recommendations and three
P0 blockers: per-seat launch provider/model resolution; rotating OAuth
persistence for long-running seats; role auth ceiling ∩ settings profile
plus data-map/reset alignment. Reviewer made no repo edits.

Implementation remains blocked pending owner/conductor adjudication.
Prior gate remains green: suites 24/15/90/14/17 + verify.
2026-09-04 12:14:48 -05:00
jason.woltje d9a94f51ba docs(plan): specify harness declaration + centralized auth/provider registry (#49)
Design only; implementation blocked pending owner review.

- agent.json: one harness identifier (pi first), resolved through a
  versioned adapter/harness manifest; reusable settingsProfile reference
- central data-root registry: providers, accounts (metadata + secret
  credential split), reusable settings profiles, audited runtime selection
- per-seat pi auth.json/models.json mechanically generated and atomically
  activated; no seat/provider registration ceremony
- mixed oauth/api-key accounts supported centrally; one active account per
  provider per pi materialization
- host-side centralized OAuth login/refresh; agents never authenticate
- local/remote Ollama modeled as endpoint providers, not accounts
- target mosaic auth/provider/agent settings CLI; secrets never on argv
- migration, fail-closed acceptance suites, and ten explicit review gates

CURRENT.md points only to spec review. Suites 24/15/90/14/17 + verify
green; unslop clean.
2026-09-04 11:57:40 -05:00
jason.woltje 975084abe2 fix(auth): mosaic-managed auth lives under the data root, never ~/.pi (#48)
Owner direction: the stack must never impact default harness usage.
Correction to M19 as shipped (nothing had been created in ~/.pi — the
move breaks nothing).

- Mosaic-managed accounts: <dataRoot>/auth/<account>.json, perms 0600
  enforced (loose perms flagged in listings, refused by --auth — mirrors
  gitea-api.sh credential hygiene).
- ~/.pi is read-only to the stack, permanently; the only interaction
  remains the existing read-only container mount of the default
  credential. Recorded as a ROADMAP standing decision.
- auth.sh is now config-driven (data root from config.json, fail closed,
  consistent with every other tool); status reports both sources labeled.
- agent.sh --auth resolution moved after load_config (needs the data
  root); missing/symlinked/non-0600 accounts refuse.
- test-auth.sh: 15 no-Docker cases (accounts-create-nothing, loose-perms
  refusal, invalid-config refusal added). Test-authoring correction
  recorded in BUILD-LOG (fixture-state mismatch caught before running).

Suites 24/15/90/14/17 + verify green.
2026-09-03 22:53:33 -05:00
jason.woltje 073bbfdb6a feat(auth): M19 harness auth tooling — auth.sh checkpoint + per-launch account injection (#47)
Investigation (pi 0.84.4 docs + host auth.json metadata, values never
read): provider stacking is native (one auth.json keyed by provider;
resolution --api-key > auth.json > env > models.json; OAuth auto-refresh).
Multi-account per provider is NOT native -> named-file design:
auth.<account>.json + per-launch injection.

- scripts/auth.sh: status (provider names, credential types, perms,
  env-side names informational — never credential material) and accounts
  (named files, active marker). Exit codes per convention: 3 missing for
  a read, 2 unparseable, 4 file/environment (symlinks refuse).
- scripts/agent.sh --auth <account>: resolves auth.<account>.json and
  exports PI_AUTH_FILE (the existing compose read-only mount source — no
  new plumbing); missing/invalid account refuses pre-container.
- scripts/test-auth.sh: 13 no-Docker cases; core assertion is the safety
  property itself — fixture key/token/env VALUES never reach output.
- Docs: TOOLS.md Auth section, AGENTS.md command surface + suites.

Headless task runs keep the default credential (worker auth selection is
a separate policy decision). Real-host smoke: anthropic/openai-codex
oauth + zai api_key reported, perms 600, no named accounts yet.

Suites 24/90/14/17/13 + verify green. Agreed sequence M16-M19 complete;
M20 owner-gated.
2026-09-03 19:58:50 -05:00
jason.woltje d1d7b5598d feat(agent): fail-closed seat resolution under MOSAIC_AGENTS_DIR override (#46)
Owner decision after live verification of M18: an explicit agents-dir
override that cannot resolve the named seat now refuses the launch
(exit 4, names the seat and dir) instead of launching seatless and
unbounded. Unsetting the override keeps the M13 plain governed TUI.
MOSAIC_ROLES_DIR needs no symmetric change - the M18 gate already
refuses unresolvable role contracts.

Task suite 88 -> 90 (refusal + refusal-names-the-seat). TOOLS.md Agent
section documents the refusal.

Suites 24/90/14/17 + verify green.
2026-09-03 19:45:10 -05:00
jason.woltje ca8135d70c feat(roles): M18 seat-role progressive capability restriction (#45)
Role contracts (roles/<role>.json): roleVersion, name bound to filename,
tools ceiling (subset of pi built-ins), network declared (none|api-only|
open; enforced when network policy lands). Strict schema, fail closed -
a non-role document refuses resolution.

mosaic-task.mjs resolve-role: config-free contract validation, emits
MOSAIC_ROLE_TOOLS / MOSAIC_ROLE_NETWORK.

agent.sh: a declared role binds to its contract. Missing/invalid contract
refuses the launch (exit 2, names the role - the under-equipped-seat
failure mode, mirroring M17 skills). Effective tools = ceiling ∩ requested
(CLI --tools or agent.json caps); no request -> ceiling stands; narrowing
and tool-free outcomes loud on stderr. Adapters unchanged; headless M9
chain (mission ∩ task) untouched.

Ships roles/researcher.json (existing seat declares the role; without the
contract the fail-closed gate would refuse its launch).

Task suite 74 -> 88: contract resolution, wrong-kind/name/network/
duplicate/unsupported/missing refusals, ceiling narrowing E2E (mock
adapter), tool-free E2E, missing-contract refusal. Test-authoring
correction recorded in BUILD-LOG (a check that registered on one path
only, caught by count arithmetic).

Suites 24/88/14/17 + verify green.
2026-09-03 17:25:17 -05:00
jason.woltje bf56583a49 docs(skills): owner loop-doctrine + collaborator delivery discipline, remediated (#44)
ms-communications: integrated as-authored - owner preamble restructure +
collaborator delivery-discipline hunks from the #43 calibration (own
session output is not a send path; the tool performs the preamble flip;
receiving rule 3 requires actually running agent-send.sh).

ms-conductor: collaborator redraft integrated (canon-aligned tracking
surfaces, one-action cadence, fail-closed core) with one conductor
remediation - step 3 now distinguishes refusal (fail closed, never
bypass) from runner outage (direct dispatch to a qualified live seat via
ms-communications permitted, recorded loudly as degraded: no sandbox, no
run record; suites still gate integration). Preserves the owner's
outage-dispatch intent inside invariant 6.

docs/TOOLS.md: release.sh ensure row added (M16 subcommand existed in
code but not in the doc - flagged by the collaborator, verified in
release.sh usage).

Authorship: owner (ms-conductor doctrine, preamble restructure) +
ms-test collaborator (delivery hunks, redraft); remediation + integration
by conductor (dragon-lin:darkwing). Suites 24/74/14/17 + verify green;
unslop clean.
2026-09-03 17:17:29 -05:00
jason.woltje c8f433131c docs: calibration phase 22 record; CURRENT.md staleness corrected (M16/M17 late-logged, next M18); session registered (#43) 2026-09-03 16:55:31 -05:00
jason.woltje d1e75f855c docs(tools): document tools/ tree in TOOLS.md + fix stale suite counts (#43)
Collaborator-authored via conductor-loop calibration: task dispatched to the
live ms-test seat (glm-5.3-flash) over agent-send.sh; diff reviewed line by
line and every documented flag/exit code independently verified against tool
source by the conductor; suites green at integration (config 24 / task 74 /
release 14 / conductor 17 + verify).

- new 'Tools (host-side)' section: agent-send.sh, agent-watch.sh, unslop-check.js
- intro reading guide now points at tools/ (worker-flagged addition, accepted)
- Maintenance suite counts corrected: test-task.sh 58 -> 74

Authored-by: ms-test collaborator (glm-5.3-flash)
Integrated-by: conductor (dragon-lin:darkwing)
2026-09-03 16:55:25 -05:00
jason.woltje f710bf1a68 docs: KICKSTART file 2026-09-03 16:34:20 -05:00
jason.woltje 2085d75190 docs: ms-communications skill (owner-authored) + session registry entry + KICKSTART recovery file 2026-09-03 16:34:03 -05:00
jason.woltje 2f5a8d2cec feat(skills): skill lifecycle + ms-* skill set completion (#40, #41, #42)
- scripts/skill.sh: install (bundled or path) / activate / deactivate /
  uninstall (refuses while enabled) / list
- skills-enabled + skills-available dirs under the data root; a skill not
  in skills-enabled is not enabled or available for use
- pi adapter: MOSAIC_SKILLS -> --skill per dir; --no-skills when none
- agent.sh: seat definitions declare skills[]; resolution against
  skills-enabled refuses the launch loudly when missing
- ms-* skills completed (owner-authored canon, hands-off): ms-tools
  adapted to the runtime, ms-file-read/write/agent/conductor bodies
  written in the owner's style; ms-agent-watch + ms-unslop untouched
- tasks/USER.md onboarding fixtures; suite hardening (nested def path,
  user seed, mock-adapter dispatch evidence)

Suites: config 24, task 74, release 14, conductor 17, verify PASS.
RELEASE 0.0.12 packaged; health-gated activation on merge.

Closes #40, closes #41, closes #42
2026-09-03 16:23:57 -05:00
jason.woltje e14ad9ab52 Merge: skill lifecycle, skills completion, M16 self-determination hardening, M20 decision
- skill.sh lifecycle (install/activate/deactivate/uninstall/list)
- 8 ms-* skills completed (owner canon preserved)
- seat skills dispatch + enabled-dir resolution
- M16: ensure at launch, drift warnings, recursion guard
- M20 decision: packages/* monorepo at usurpation

Closes #40, closes #41, closes #42
2026-09-03 16:03:05 -05:00
jason.woltje 9fd16b9739 feat(release): recursion guard for the health gate; run-task drift warning; M20 packages/* decision recorded (#39)
- release.sh health gate runs with MOSAIC_ENSURE_SKIP=1: the gated task run
  cannot re-enter release self-determination
- run-task.sh warns on release drift instead of silently using a stale image
- ROADMAP: M20 decision recorded (packages/* monorepo at usurpation,
  continuity-first); restructure sequenced as M20 phase 1

Closes #39
2026-09-03 15:58:46 -05:00
jason.woltje 9051ad179b docs(roadmap): restructure sequencing (M20 phase 1, not first) + skills-as-discipline doctrine 2026-09-03 14:50:59 -05:00
jason.woltje 2f649ed930 docs: README release ensure row 2026-09-03 14:43:25 -05:00
jason.woltje db330c12c7 feat(release): self-determination - ensure at launch, drift warnings, M20 packages/* decision (#38)
- release.sh ensure: aligned no-op; drift -> package-if-needed + health-gated
  activate (M3 gate-then-flip, automated)
- ensure_release_aligned in common.sh: invoked by hello/verify/agent;
  MOSAIC_ENSURE_SKIP guards recursion; run-task warns on drift without
  auto-aligning (workers/suites never trigger builds or model gates)
- ROADMAP: M20 decision recorded - v2 adopts packages/* monorepo at usurpation
- BUILD-LOG Phase 20 + tool-race process note

Live-verified: post-reset pointer loss auto-restored via health-gated
ensure; drift warning fires on desired-version bump; idempotent no-op on
aligned state.

Closes #38
2026-09-03 14:42:09 -05:00
jason.woltje 0a17d29bce docs(roadmap): flush 2026-09-03 14:28:58 -05:00
jason.woltje 3458f6a7ad docs(roadmap): M17 skill lifecycle (skills-enabled/available, role-scoped subsets, --skill negates --no-skills); M20 stack succession path + monorepo question 2026-09-03 14:28:45 -05:00
jason.woltje b033952cd5 docs(harvest): pattern ledger from stack/next + fleet runtime (12 patterns, skips, owner-corrected M17-M19 designs) 2026-09-03 13:55:30 -05:00
jason.woltje c102980ad4 docs(plan): CURRENT.md - queue aligned to ROADMAP (M16 next, CI deferred) 2026-09-03 12:59:28 -05:00
jason.woltje 58b96cb715 docs(plan): ROADMAP.md - M16 release self-determination, M17 ms-tools skill, M18 seat-role restriction, M19 auth tooling; CI deferred per owner 2026-09-03 12:57:26 -05:00
jason.woltje dd3ff944a1 chore(release): 0.0.11 2026-09-03 12:30:52 -05:00
jason.woltje 8eb81ebec1 feat(onboard): user onboarding - no default USER.md, guided creation (#37)
- bootstrap no longer creates user/USER.md (owner direction)
- scripts/onboard.sh: name REQUIRED (interactive loop or --name),
  optional fields prompted (profession, marital, age, gender, education,
  location, timezone, skillset, interests, hobbies, pets); flag-driven
  non-interactive mode for automation
- templates/USER.md: canon skeleton, placeholder rendering, unfilled
  optional = (not provided)
- agent.sh: auto-runs onboarding when profile missing (TTY gate);
  headless run-task warns and continues without user context
- user profile dispatched to all launches (M14 layer)

Closes #37 (onboarding requirements from owner layout review)
2026-09-03 12:30:42 -05:00
jason.woltje 530597cc84 docs(plan): CURRENT.md flush 2026-09-03 11:59:49 -05:00
jason.woltje 9a0d44f96a docs(plan): CURRENT.md - deduplicated completed log (marked correction), M15 review queued 2026-09-03 11:59:37 -05:00
jason.woltje 121b331c6c docs(log): back-fill phases 16-19 (M10, M12, M14, M15) - recorded retroactively with ground-truth sources 2026-09-03 11:59:14 -05:00
jason.woltje 34e06e7de7 Merge M15: agent seats - per-agent SOUL and role contracts
Closes #36
2026-09-03 11:56:38 -05:00
jason.woltje 9bd4f1c405 feat(agents): agent seats - per-agent SOUL, role, definitions dir (#36)
- agents/<name>/ holds agent.json (strictly validated: version, name,
  role?, capabilities?, workspace?, session?) + SOUL.md (persona prose)
- agent.sh: definition loading (quote-safe node defaults file), runtime
  SOUL copy to dataRoot/agents/<name>/, MOSAIC_AGENT_SOUL_FILE ->
  loader fills the SOUL slot from the seat's persona (contract SOUL =
  default persona; governance never overridden)
- seat.json written once at instantiation (seatVersion, name, role, at)
- identity section gains agent role; compose passthrough for role+SOUL
- live user context (M14) + seat SOUL compose the full persona:
  governance -> persona -> identity -> user -> mission
- RELEASE -> 0.0.10; packaged and health-gated activated
- example seat committed: agents/researcher

Closes #36
2026-09-03 11:56:38 -05:00
jason.woltje a7b612435b docs: BUILD-LOG Phase 15, CURRENT.md - M13 shipped 2026-09-03 11:25:41 -05:00
jason.woltje 87f10772ce Merge M13: interactive TUI agent + TOOLS.md
Closes #35
2026-09-03 11:24:56 -05:00
jason.woltje 7db4c5c2ed feat(agent): interactive TUI launcher + identity + TOOLS.md (#35)
- scripts/agent.sh <name>: launches interactive pi TUI in the container
  with contracts + optional mission + agent identity + named session +
  optional workspace/tools; the Mosaic alternative to vanilla pi
- pi adapter: MOSAIC_INTERACTIVE branch (clean TUI, no -p, no initial
  prompt); headless exec rebuilt via positional args (no word-splitting
  on the request); MOSAIC_AGENT_NAME optional in headless
- loader: AGENT IDENTITY section when the launcher names the agent
- compose: fixed command removed (request defaults live in run-agent.sh);
  MOSAIC_INTERACTIVE/MOSAIC_AGENT_NAME passthrough
- docs/TOOLS.md: full on-demand tool reference; AGENTS.md routes to it
- RELEASE -> 0.0.8 (container change); build verified

Closes #35
2026-09-03 11:24:56 -05:00
jason.woltje 0273a84549 docs: AGENTS.md - session recovery shim, invariants canon, session registry
- AGENTS.md at root: pi loads it automatically at every session start
  (conductor-level sessions; workers deliberately exclude it via
  --no-context-files). Deliberately short: invariants, session protocol,
  role model, command surface, data map, pointers - depth stays in docs/.
- docs/SESSIONS.md: append-only session registry, mandatory per session.
- Recovery rule encoded: compaction/restart loses nothing - AGENTS.md +
  CURRENT.md + git log + suites reconstruct state; never guess.
2026-09-03 11:02:19 -05:00
jason.woltje 3b674b7a66 Merge: roles/ directory convention - root is bootstrap-only 2026-09-03 10:56:07 -05:00
jason.woltje 527bc581ca refactor(layout): role contracts move to roles/ - root is bootstrap-only
Owner direction: the repository root holds first-class, bootstrap-required
configuration only. conductor-policy.json is a ROLE contract (the
conductor's authority), one of scores of future role contracts
(agent-policy, coder-policy, ...) - such files get a dedicated home.

- roles/conductor-policy.json (git mv)
- conductor-apply.sh + test-conductor.sh read the new path
- CONDUCTOR.md records the roles/ convention

Closes UX follow-up from owner layout review; no issue (convention change).
2026-09-03 10:56:07 -05:00
jason.woltje 1249714a9a docs(plan): CURRENT.md - M12 shipped 2026-09-03 07:04:37 -05:00
jason.woltje e175616885 feat(conductor): auto-apply policy gate for worker patches (#34)
- conductor-policy.json (tracked, strictly validated): enabled switch,
  path allowlist globs, gating suites - the autonomy decision lives in a
  declarative file the owner controls
- scripts/conductor-apply.sh <runId> [--dry-run]: succeeded-run check ->
  clean target tree -> diff from worker workspace -> allowlist -> syntax
  gates (node/bash/json) -> apply -> policy suites -> attribution commit;
  ANY failure reverts the tree; push is never automatic
- scripts/test-conductor.sh: 17 sandbox cases covering every gate incl.
  suite-failure auto-revert and disabled policy
- policy defaults: scripts/docs/tasks/missions/adapters + README; all
  three suites gate

Closes #34
2026-09-03 07:03:29 -05:00
jason.woltje 6955717612 Merge M11: session forking from a common ancestor
Closes #33
2026-09-03 06:43:02 -05:00
jason.woltje 88d9cf750f feat(sessions): sessionForkFrom - branch conversations from a common ancestor (#33)
- task schema: optional sessionForkFrom (source session name); requires
  session target; self-fork rejected
- runner: resolves source newest .jsonl (fail 4 if none/outside dataRoot);
  passes MOSAIC_SESSION_FORK + MOSAIC_SESSION_DIR; result records lineage
- pi adapter: --fork <source> --session-dir <target> when forking;
  ephemeral default unchanged; plain session resume unchanged
- compose passthrough; RELEASE -> 0.0.7 (adapter changed)
- suite +9 cases (58 total): plumbing via mock stderr, validation
  negatives, live fork - child recalls ancestor code word, ancestor
  session file untouched

Closes #33
2026-09-03 06:43:02 -05:00
jason.woltje c038706eed docs(plan): CURRENT.md - M10 shipped, retention next in review 2026-09-03 06:33:54 -05:00
jason.woltje 8622c9d826 Merge M10: run-record retention
Closes #32
2026-09-03 06:33:27 -05:00
jason.woltje 88eef507b0 feat(retention): run-record pruning - keep newest N, dry-run default (#32)
- mosaic-task.mjs prune [--keep=N] [--yes]: default keep 50; without
  --yes lists candidates without deleting
- only r-* directories under the runs root; symlinks skipped;
  sessions/workspaces/state/config untouched (asserted by suite sentinels)
- append-only receipt runs/.pruned.log records every pruned id
- test-task.sh: +8 retention cases (dry-run no-delete, keep-N, newest
  kept, receipt, isolation, invalid keep, empty no-op)

Also: suite hardening - prune section scopes its config per-command
(no export/unset leaking into later sections); duplicated check()
removed; latest_reason hoisted to helpers; status colors now green OK /
red FAIL (terminal-only, NO_COLOR-aware) per owner UX feedback.

Closes #32
2026-09-03 06:33:27 -05:00
jason.woltje 439bea6915 ui(test): green OK/PASS, red FAIL - terminal-only, NO_COLOR-aware
Owner feedback: grep match-highlighting made the word 'policy' red while
status words were plain - counter-indicative. Suites + verify now emit
ANSI colors (green success, red failure) when stdout is a terminal;
piped/machine-parsed output stays plain, honoring NO_COLOR. Word 'ok'
promoted to 'OK' for scannability.

Verified byte-level via forced-pty run; piped output unchanged; suites
41/24/14 + verify green.
2026-09-03 06:23:47 -05:00
jason.woltje fad8a4718c Merge M9: mission-level capability policy
Closes #30
2026-09-03 06:16:01 -05:00
jason.woltje 2ff49adff4 feat(policy): mission-level capability policy - least-privilege intersection (#30)
- mission schema: optional capabilities.tools (same validation as task)
- merge semantics in runTask: neither -> none; mission only -> mission;
  task only -> task; both -> intersection (task narrows, never widens);
  empty intersection -> tool-free run with an explicit stderr note
- result.json records EFFECTIVE tools; task/mission snapshots remain the
  immutable declaration of intent
- adapters unchanged; host-side only (no image change, 0.0.6 still active)
- task suite +5 cases (41 total): all four merge cases asserted from run
  evidence + invalid mission capabilities rejected

Policy decision recorded: missions govern; tasks cannot escalate.

Closes #30
2026-09-03 06:16:01 -05:00
jason.woltje 44c476ebbf fix(ops): show displays retriedFrom lineage (#29)
result.json recorded lineage correctly; the human-facing show command
omitted the field. Found by owner test: show | grep retriedFrom was
empty on a run whose result.json contained it.

Closes #29
2026-09-03 06:09:51 -05:00
jason.woltje cde480eb60 docs(plan): CURRENT.md — retry lineage shipped, M9 queued for decision 2026-09-03 05:31:24 -05:00
jason.woltje 5808248707 Merge retry lineage + relative mission resolution
Closes #28
2026-09-03 05:30:57 -05:00
jason.woltje afd5827db8 fix(retry): lineage tracking + relative mission path resolution (#28)
- retryRun rewrites a snapshot's relative mission path to the run's own
  recorded mission.json (absolute) before execution — retries stay
  faithful to what originally ran
- runTask accepts options.retriedFrom; retry records lineage in
  result.json (additive optional field, no schema break)
- task suite +4 cases: retry succeeds, lineage recorded, mission section
  present after retry (36 total), missing-run retry exits 4

Closes #28
2026-09-03 05:30:57 -05:00
jason.woltje d9cc990376 Merge M8: conductor loop - self-orchestration
Closes #25, closes #26, closes #27
2026-09-02 22:42:52 -05:00
jason.woltje 83c4e9851e feat(orchestration): retry <runId> — authored by headless pi worker (#26, #27)
Collaboration record (conductor loop, docs/plans/CONDUCTOR.md):
- round 1 (worker session worker-1, 2m28s): retry implemented per spec
- conductor live test exposed spec gap: direct invocation lacked
  launcher env exports
- round 2 (same worker session, 59s): spawnEnv made self-sufficient,
  but used PI_* where compose interpolates MOSAIC_*
- conductor hotfix: 3-line rename to MOSAIC_PROVIDER/MOSAIC_MODEL/
  MOSAIC_DATA_ROOT

Final: node scripts/mosaic-task.mjs retry <runId> re-executes a run's
task snapshot as a new run; live retry replied REMEMBERED; all suites
green (24/32/14 + verify).

Known limitation: retrying a run whose task used a RELATIVE mission path
resolves it against the temp dir; lineage tracking deferred.

Closes #25, closes #26, closes #27
2026-09-02 22:42:52 -05:00
jason.woltje 22508170a2 docs(plan): CURRENT.md — single next-action pointer for cadence-driven work 2026-09-02 22:25:56 -05:00
jason.woltje 90a67d050e Merge M7: operator ergonomics + release 0.0.6
Closes #24
2026-09-02 22:14:18 -05:00
jason.woltje 24bdef75fa feat(ops): run inspection, release 0.0.6, docs (#24)
- mosaic-task.mjs show <runId>: full record + snapshots + artifacts;
  uppercase-tolerant id validation; missing/traversal ids exit 4
- list: task/workspace/session columns
- RELEASE -> 0.0.6; README workspaces/capabilities/sessions sections;
  BUILD-LOG Phases 9-11; autonomous-run tracker results filled

Closes #24
2026-09-02 22:14:18 -05:00
jason.woltje 4e2a413640 Merge M6: named sessions - persistence and resume
Closes #22, closes #23
2026-09-02 22:08:34 -05:00
jason.woltje be55549700 feat(sessions): named persistent sessions with resume (L1) (#22, #23)
- task schema: optional session (named id) -> persistent session dir at
  dataRoot/sessions/<name>, isolated per name
- pi adapter: --session-dir when declared (ephemeral --no-session stays
  the default otherwise); -c resumes the most recent session when present
- compose passthrough; result.json records session
- fixtures: tasks/session-demo-1.json (teach) + session-demo-2.json (recall)
- E2E: teach -> REMEMBERED + host-side session JSONL; resume -> recalled
  'mosaico' exactly; single continued session file

Closes #22, closes #23
2026-09-02 22:08:34 -05:00
jason.woltje ddb1554e5b Merge M5: task workspaces + capability envelope
Closes #20, closes #21
2026-09-02 22:06:43 -05:00
jason.woltje b017e66e17 test(capabilities): workspace/tooling selftests + live demo fixture (#21)
- mock plumbing cases: workspace path + tools delivered (asserted from
  run-record stderr), host workspace created, absent fields = empty vars
- validation negatives: unknown tool, workspace traversal
- tasks/workspace-demo.json: pi uses bash inside the persistent demo
  workspace; host-visible proof.txt verified live

Closes #21
2026-09-02 22:06:43 -05:00
jason.woltje 172368612c feat(capabilities): task workspaces + tools allowlist plumbing (#20)
- task schema: optional workspace (absent | :run ephemeral | named
  persistent under dataRoot/workspaces) and capabilities.tools (pi
  documented tool allowlist); strict validation, traversal-proof names
- runner: creates host workspace, passes MOSAIC_WORKSPACE (container
  path) + MOSAIC_TOOLS; result.json records both
- pi adapter: cds into workspace; --tools when allowlist present else
  --no-tools
- mock adapter: logs delivered MOSAIC_* vars to stderr as deterministic
  plumbing evidence (dash prints 'export K=v', so use env not export)

Closes #20
2026-09-02 22:03:46 -05:00
jason.woltje 1387231e57 docs(plan): autonomous work run tracker (M5-M7 scope, test plan, review checklist) 2026-09-02 21:57:30 -05:00
jason.woltje 0292392e64 Merge M4: runtime adapter seam
Closes #16, closes #17, closes #18, closes #19
2026-09-02 21:30:35 -05:00
jason.woltje 594b8d711c docs(adapters): adapter seam docs + recorded M4 E2E, release 0.0.5 (#19)
- README: Runtime adapters section (contract summary, selection, mission
  injection point); BUILD-LOG Phase 8 entries
- E2E: 24+24+14 selftests green; verify PASS; 0.0.5 packaged and
  health-gated activated; mission-bearing fixture task succeeded through
  the real pi adapter; config checksum unchanged

Closes #19
2026-09-02 21:30:35 -05:00
jason.woltje 3c1ffd2c2d test(adapters): seam selftests — deterministic mock cases + mission injection (#18)
- test-config: absent adapter defaults to pi; mock validates; unknown
  adapter exits 2; env exports MOSAIC_ADAPTER (24 cases total)
- test-task: mock adapter gate pass/mismatch (no provider needed),
  mismatch reason asserted, unknown adapter fails closed, mission section
  injected into generated prompt asserted by content (24 cases total)
- harness fixes: helpers defined before use; per-case config files (no
  cross-case leakage); newest-run selection for the live case; deduped
  accidentally duplicated live block

Closes #18
2026-09-02 21:28:57 -05:00
jason.woltje 4ebb123ba3 feat(adapters): sanctioned mission directives injection (#17)
- load-contracts.sh: MOSAIC_MISSION_FILE (readable) appends a MISSION
  (runtime) section — objective + directives — after the immutable
  contracts; unreadable path is a hard error, absent env changes nothing
- mosaic-task.mjs: exports MOSAIC_MISSION_FILE as the run snapshot's
  container path (/var/lib/mosaic/runs/<id>/mission.json), with an
  outside-dataRoot guard; also exports the configured adapter

Verified: contract-only prompt has no mission section; mission-bearing
run shows objective + directives in the generated prompt, snapshot
recorded, real provider returns exactly MOSAIC_HELLO_OK.

Closes #17
2026-09-02 21:19:22 -05:00
jason.woltje bb5cecb348 feat(adapters): adapter contract, dispatch, pi + mock adapters (#16)
- adapters/README.md: the harness boundary contract (env in, response on
  stdout, diagnostics stderr, exit 0 success)
- adapters/pi: extracted current invocation unchanged
- adapters/mock: deterministic MOSAIC_MOCK_RESPONSE echo (test-only)
- run-agent.sh: name-validated dispatch to adapters/<name>/adapter.sh
- config: optional execution.adapter (pi|mock), default pi, configVersion
  stays 1 — existing configs remain valid; selection authority is the
  config file (load_config exports it)
- compose: MOSAIC_ADAPTER / MOSAIC_MOCK_RESPONSE passthrough; Containerfile
  installs adapters read-only; RELEASE -> 0.0.5

Verified: hello unchanged; mock verbatim via config; unknown adapter and
path-traversal names refused in-container; invalid adapter exits 2.

Closes #16
2026-09-02 21:18:07 -05:00
jason.woltje 88f55d9135 test(task): live failures self-report evidence; wrong-exit no longer masks (#15)
- live hello failure dumps latest run result.json + stderr tail before
  sandbox cleanup destroys them
- wrong-expectExact case asserts reason == expect-mismatch (was: any
  exit 1, which masked compose-level failures)
- repair dangling if/else from the docker-guard refactor

Closes #15
2026-09-02 20:56:58 -05:00
jason.woltje e7e1bd26eb fix(launcher): resolve release identity for direct task runs (#14)
M3 made MOSAIC_IMAGE_TAG required in compose, but run-task.sh never
called load_release — direct task runs failed in compose before any
model call. release.sh paths masked it by exporting the tag to children.

Found by owner-run test-task.sh; failure receipts were in the run
records' stderr.txt.

Closes #14
2026-09-02 20:44:31 -05:00
jason.woltje 5d86b8fa93 Merge M3: release model and safe updates
Closes #10, closes #11, closes #12, closes #13
2026-09-02 20:24:33 -05:00
jason.woltje 35ea464661 docs(release): release model usage + recorded M3 drills (#13)
- README: Release model section (package/activate/rollback/status,
  pointer + append-only log, gate-then-flip guarantee)
- BUILD-LOG Phase 7: drills recorded (update, refusal, rollback),
  two harness/product corrections documented

Drill evidence: 0.0.3 -> 0.0.4 update with unchanged config checksum and
green verify; fault-injected refusal left pointer untouched; health-gated
rollback restored 0.0.3; full append-only event history.

Closes #13
2026-09-02 20:24:33 -05:00
jason.woltje e87ecdb3e5 test(release): release-layer selftests (#12)
14 cases: RELEASE validation (valid/invalid/missing), tag consistency,
status on empty state, fault-injected refusal with no pointer + single
valid refusal log line, healthy activation, pointer fields, repeat
activation append-only log, rollback-without-previous refusal.

Harness fix learned the hard way: restore RELEASE from backup inline
after the missing-file case (mv-back restored the mutated file); single
exit trap self-heals the repo state.

Closes #12
2026-09-02 20:23:18 -05:00
jason.woltje a947db7bfd feat(release): package/activate/rollback/status with health gate (#11)
- activate: image-presence pre-check + M2 task-runner health gate
  (tasks/hello-marker.json exact marker) before atomic pointer replace
  (tmp+rename); every attempt appended to activation-log.jsonl
- --fault-injection flips the health expectation to prove the refusal path
- rollback: health-gated re-activation of the previous activated imageTag
  from the log; refuses when the image is gone or no previous exists
- status: release, tag, pointer, recent log; safe on empty state
- state lives under <dataRoot>/state/ (config-independent, reset-scoped)

Verified: activate OK; fault-injected refuse with pointer unchanged;
rollback-without-previous refuse.

Closes #11
2026-09-02 20:20:32 -05:00
jason.woltje a35ea62ab1 feat(release): RELEASE identity + image tag single-sourcing (#10)
- RELEASE file: single source of release version (0.0.X until declared stable)
- common.sh load_release(): validates version, derives
  MOSAIC_IMAGE_TAG=mosaic-poc-agent:<pi>-r<release> from the pinned pi dep
- compose.yaml: image tag is required env; build/hello/verify call load_release
- verify.sh derives the image name instead of hardcoding it
- package.json version aligned to the same 0.0.X line

Closes #10
2026-09-02 20:18:17 -05:00
jason.woltje f6c93dcb5c Merge M2: mission and task abstraction
Closes #6, closes #7, closes #8, closes #9
2026-09-02 19:58:53 -05:00
jason.woltje 7f0408d417 feat(task): fixture mission/task, docs, recorded M2 E2E (#9)
- missions/hello.json + tasks/hello-marker.json committed fixtures
- README: Missions & tasks section (usage, run record layout, M2 scope note)
- BUILD-LOG: Phase 6 before/after entries

E2E: fixture run succeeded with exactly MOSAIC_HELLO_OK; wrong expectExact
recorded status failed (expect-mismatch) and exited 1; runs listed; config
checksum unchanged.

Closes #9
2026-09-02 19:58:53 -05:00
jason.woltje cfd2a19bd7 test(task): mission/task selftests — schema negatives + live runs (#8)
18 cases: validation negatives (unknown keys, versions, ids, prompt,
expectExact NUL, timeout range, missing/invalid mission, writes-nothing)
plus live cases: exact-marker success, wrong expectExact fails, distinct
run dirs, result.json contents, list output.

Closes #8
2026-09-02 19:57:34 -05:00
jason.woltje d2a9e26395 feat(task): mission/task schemas, validation, and run runner (#6) (#7)
- scripts/mosaic-task.mjs: validate | run | list
- Strict v1 schemas: unknown keys rejected; ids/prompt/expectExact/
  timeoutSeconds bounds enforced; optional mission file resolved against
  the task file and validated too
- run: executes through the config-driven container path with stdin
  detached (issue #5 class), SIGKILL timeout (default 120s), trimmed
  response capture
- Immutable run records under <dataRoot>/runs/r-<utcstamp>-<rand>/:
  task.json + mission.json snapshots (write-once), stderr.txt, result.json
- expectExact gate: mismatch -> status failed, exit 1; result.json is
  always written
- scripts/run-task.sh: load_config + bootstrap_runtime_dir before exec
- M2 scope: mission directives are snapshotted for provenance, not yet
  injected into the runtime prompt (later policy layer)

Closes #6, closes #7
2026-09-02 19:56:29 -05:00
jason.woltje 0734b1f3a5 fix(launcher): detach stdin on agent container run (#5)
pi print mode reads piped stdin until EOF; an attached terminal stdin
blocked the one-shot run forever. Automated contexts (closed stdin)
never exposed it. Request text comes from the compose command.

Proven: tail -f /dev/null | scripts/hello.sh now returns MOSAIC_HELLO_OK
in ~4s (previously timed out at 30s); verify.sh remains green.

Closes #5
2026-09-02 19:50:07 -05:00
jason.woltje ce6420f3de Merge M1: configuration-driven Hello World
Closes #1, closes #2, closes #3, closes #4
2026-09-02 18:32:50 -05:00
jason.woltje 81f58b15c8 fix(launcher): ensure configured data root before verify mount; docs for M1 (#4)
- verify.sh now calls bootstrap_runtime_dir after load_config; previously a
  reset-then-verify flow let Docker auto-create a root-owned mount source
- common.sh: fail with clear guidance when data root exists but is not writable
- README: configuration section, bootstrap usage, selftest entry point
- BUILD-LOG: Phase 5 entries with corrections

E2E (clean slate): 20/20 selftests; bootstrap idempotent; config-driven
hello/verify MOSAIC_HELLO_OK; negative marker exit 1; reset + rerun green;
config checksum unchanged across the entire flow.

Closes #4
2026-09-02 18:32:50 -05:00
jason.woltje 0c2113f710 test(config): sandboxed config-layer selftests (#3)
20 cases: bootstrap create/idempotency, missing config, malformed JSON,
unknown keys/version/backend/environment, relative and non-canonical
dataRoot, filesystem root, home dir, ancestor-of-config, control chars,
symlinked config file, env export resolution, validation-writes-nothing.

Closes #3
2026-09-02 18:30:46 -05:00
jason.woltje 900a506c1f feat(config): wire launcher scripts and compose to config.json (#2)
- common.sh: load_config() exports MOSAIC_DATA_ROOT/PROVIDER/MODEL; fails closed
- compose.yaml: dataRoot mount and provider/model are required env (:? errors)
- build/hello/verify load config before any mutation; no silent bootstrap
- reset.sh: target resolved from configured dataRoot; all safety checks kept

Verified: compose fails without launcher env; verify/reset fail on missing
config; config-driven hello+verify pass; symlink refusal with sandboxed
config (canary survived); config checksum unchanged across reset+rerun.

Closes #2
2026-09-02 18:30:08 -05:00
jason.woltje c3d29e796a feat(config): config module with idempotent bootstrap and strict v1 validation (#1)
- scripts/mosaic-config.mjs: bootstrap | validate | env operations
- Exclusive creation (O_EXCL 'wx'); existing config validated, never rewritten
- Strict schema: unknown keys rejected, configVersion===1, backend docker only
- dataRoot guards: absolute, canonical, not root/home/ancestor-of-config
- MOSAIC_CONFIG override for sandboxed tests; exit codes 0/2/3
- scripts/bootstrap.sh: explicit bootstrap entry point

Closes #1
2026-09-02 18:28:14 -05:00
jason.woltje c2365ae519 chore: baseline container POC and atomic foundation plan
- Containerized Pi hello-world proof (image mosaic-poc-agent:0.84.4, non-root)
- Four immutable contract fixtures loaded into a generated system prompt
- build/hello/verify/reset scripts with exact-match gating and reset safety
- Documented Pi discovery (v0.84.4, -p mode, --system-prompt, container auth)
- Append-only BUILD-LOG with corrections; deferred layers in LAYERS.md
- Architecture plan: docs/plans/2026-09-02_atomic-mosaic-foundation.md
2026-09-02 18:24:36 -05:00
orch-01 d6302f8e6f docs: make Portainer optional deployment path (#1492)
ci/woodpecker/push/publish Pipeline was successful
2026-09-02 23:23:07 +00:00
marcieandorch-01 9aa4983cf2 fix: use canonical dogfood seat identity (#1490)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: marcie <[email protected]>
2026-08-30 23:43:22 +00:00
marcieandorch-01 736b0affc1 compose: add wrapper-first dogfood workspace (#1488)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: marcie <[email protected]>
2026-08-30 22:29:30 +00:00
orch-01 e18d13d36f fleet: split-home-safe mosaic launcher (T110, P5-RM-009 stack side) (#1480)
ci/woodpecker/push/publish Pipeline was successful
2026-08-30 10:05:40 +00:00
marcieandorch-01 60bc5d2022 compose: pin MOSAIC_STORAGE_TIER=standalone (A5d formal-run fix) (#1486)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: marcie <[email protected]>
2026-08-30 07:49:35 +00:00
marcie ea91cfc421 compose: stack profile — one-command standalone deployment (A5) (#1485)
ci/woodpecker/push/publish Pipeline was successful
2026-08-30 06:03:39 +00:00
marcie acf640d00f docs: containerization plan + PRD D15 (tiered deployment, standalone v1 bar) (#1484)
ci/woodpecker/push/publish Pipeline was successful
2026-08-30 05:21:42 +00:00
fredandmarcie 431ead3a18 feat(gateway,cli): agent enrollment command family (M4-4b) (#1483)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: fred <[email protected]>
2026-08-30 04:30:05 +00:00
fred 143ba0f57a db: agent enrollment schema (M4-4a, migration 0021) (#1482)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-30 01:47:06 +00:00
fred ee815a72b1 docs: agent enrollment command family v1 design (M4-4-0) (#1481)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-30 01:21:00 +00:00
marcieandorch-01 94d626dff9 mosaic comms: socket resolution is tool-owned (B2) (#1476)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: marcie <[email protected]>
2026-08-29 23:35:37 +00:00
marcieandorch-01 ee6c842918 R3: --body-file <path>/- across body/comment carriers (#1474)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: marcie <[email protected]>
2026-08-29 22:49:20 +00:00
marcieandorch-01 ba3b854d50 D1/D3: --number canonical on issue wrappers, --labels alias on list wrappers (#1475)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: marcie <[email protected]>
2026-08-29 22:29:16 +00:00
fred c7a7fd07cc brain/gateway: prohibit mission_tasks.status as a write source (M4-3a phase 1) (#1479)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-29 21:59:13 +00:00
fred 5399c6b7e7 docs: P0 field-map currency verification at next@abb0c936 (M4-3a-0) (#1478)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-29 21:17:05 +00:00
marcieandorch-01 e09b8783b4 P1b: read-only viewers join the R1/R4 usage contract (#1472)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: marcie <[email protected]>
2026-08-29 21:03:07 +00:00
marcieandorch-01 abb0c93601 framework tools/tmux: agent-send socket default resolution + ambiguity guard (B1) (#1466)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: marcie <[email protected]>
2026-08-29 20:25:50 +00:00
marcieandorch-01 e67cced273 mosaic fleet logins: per-seat credential pass-through (P4 gap closure) (#1473)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: marcie <[email protected]>
2026-08-29 20:05:23 +00:00
fred 635cb1f666 docs: contract 2 Amendment 1 — company-CRUD capability (S2 follow-up) (#1477)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-29 19:34:48 +00:00
marcieandorch-01 09d24b9275 pr-merge --base-line: gated intra-line exception (B5) (#1471)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: marcie <[email protected]>
2026-08-29 18:14:40 +00:00
marcieandorch-01 ed4c543872 mosaic coord: board subcommand + roll alias (P3 gap closure) (#1470)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: marcie <[email protected]>
2026-08-29 17:52:30 +00:00
fred 215faeda0a feat(hierarchy): M4-1b-ii hierarchy command family, grant evaluation, visibility (#1465)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-29 16:54:39 +00:00
orch-01 5125fe21b0 p0 line landing: coord board/roll + wrapper usage contract + dispatch layer (successor to #1467) (#1468)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: orch-01 <[email protected]>
2026-08-29 16:24:08 +00:00
marcieandorch-01 41e8046371 framework tools/git: issue-comment R1/R4 usage-error contract (sync from brain) (#1462)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: marcie <[email protected]>
2026-08-29 01:58:42 +00:00
fred 6e16675ea2 docs: deployment mode and conversion contract (S2 contract 6) (#1439)
ci/woodpecker/push/publish Pipeline was successful
2026-08-28 22:57:11 +00:00
fred 19ebc422aa docs: tool-gateway mapping contract (S2 contract 5) (#1438)
ci/woodpecker/push/publish Pipeline was successful
2026-08-28 22:04:03 +00:00
fred bd749831b1 docs: company visibility classes (Ruling 4b amendment, contracts 1+3) (#1461)
ci/woodpecker/push/publish Pipeline was successful
2026-08-28 20:17:35 +00:00
fred f8e1b43b5b docs: RBAC grant model contract (S2 contract 2) (#1436)
ci/woodpecker/push/publish Pipeline is running
2026-08-28 19:09:17 +00:00
fred 2148c20d26 feat(hierarchy): audit event + outbox machinery (M4-1b-i, contract 1 §5.2) (#1460)
ci/woodpecker/push/publish Pipeline was successful
2026-08-28 02:22:53 +00:00
fred 5964dab891 feat(db): hierarchy record class schema + witnesses (contract 1, M4-1a) (#1459)
ci/woodpecker/push/publish Pipeline was successful
2026-08-28 00:42:35 +00:00
orch-01 bdb903cf69 docs: T78 official CLI capability migration contract (#1458)
ci/woodpecker/push/publish Pipeline was successful
2026-08-27 19:53:51 +00:00
fred bec2eb118b docs: API contract artifacts contract (S2 contract 9) (#1443)
ci/woodpecker/push/publish Pipeline was successful
2026-08-27 19:18:39 +00:00
fred 07624140e4 docs: hierarchy schema contract (S2 contract 1, D2) (#1435)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-27 16:39:15 +00:00
fred 1c79af25d4 fix(mosaic): retry the Invariant R pi version probe under CI load (#1441)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-27 16:38:53 +00:00
fred e605c83b27 ci(web): Phase P6 — vite build + headless E2E gate on every trunk merge (#1445) (#1454)
ci/woodpecker/push/publish Pipeline was successful
2026-08-27 15:26:10 +00:00
fred b5ee692843 P5: SPA cutover — retire Next.js, gateway serves the Vite bundle (#1444) (#1453)
ci/woodpecker/push/publish Pipeline was successful
2026-08-27 13:06:49 +00:00
fred bf8bc2128d fix(docker): copy scripts/ into appservice builder before pnpm install (#1452)
ci/woodpecker/push/publish Pipeline was successful
2026-08-27 11:28:47 +00:00
fred 01904b8f69 docs: custody pointer and consent schema contract (S2 contract 7) (#1440)
ci/woodpecker/push/publish Pipeline failed
2026-08-27 10:39:32 +00:00
fred 676900bd46 docs: onboarding wizard contract (S2 contract 3) (#1437)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-27 10:39:23 +00:00
fred a3b0770205 docs: roll-up projection contract (S2 contract 8) (#1442)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-27 02:10:12 +00:00
fred 2a30c68b84 docs: identity account-lifecycle contract (S2 contract 4) (#1433)
ci/woodpecker/push/publish Pipeline is running
2026-08-27 00:13:49 +00:00
fred f8f8f97be7 feat(web): port settings and admin surfaces into the SPA (Phase P4-2) (#1434)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-27 00:03:38 +00:00
fred 49b7943420 fix(gateway): scope /api/teams endpoints to team membership (#1428) (#1429)
ci/woodpecker/push/publish Pipeline is pending
2026-08-26 22:45:54 +00:00
fred 19e16bd44f ci: publish web+appservice sha images on next (#1407) (#1427)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-26 22:42:03 +00:00
jason.woltje 3bd490c080 Merge pull request 'docs: north-star PRD rewrite (D1-D14), ROADMAP, kanban SOT Amendment A1' (#1425) from docs/prd-north-star-rewrite into next
ci/woodpecker/push/publish Pipeline was successful
Reviewed-on: #1425
2026-08-26 16:57:16 +00:00
fred 4b448109dd docs/prd: address independent review findings 1-10 (fidelity, A1 record class + carve-out, roadmap completeness)
ci/woodpecker/pr/ci Pipeline was successful
2026-08-25 23:20:40 -05:00
fred bc1149c15e docs: north-star PRD rewrite (D1-D14), ROADMAP.md, kanban SOT Amendment A1
ci/woodpecker/pr/ci Pipeline was successful
- docs/PRD.md: Part I product north star authored from ratified decisions
  D1-D14; Part II preserves all active workstream contracts verbatim
  (KBN-101, FCM #758, FCOM #766, TESS, #756, MOS-PORT, #1150, #1174, #1194,
  RI #1275, M1). Referenced anchors unchanged.
- docs/archive/PRD-v0.1.md: v0.1.0 beta PRD body archived verbatim with
  supersession header.
- docs/ROADMAP.md: all phases P0-P5 present from day one per D11
  (P2-P5 as explicit placeholders).
- docs/requirements/native-kanban-sot.md: Amendment A1 (D13) - hierarchy
  parentage + RBAC chain above workspaces; sections 1-7 untouched.
2026-08-25 22:23:07 -05:00
orch-01 089953a7cf ci: enable turbo remote cache on trusted publish events (#1424)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: orch-01 <[email protected]>
2026-08-25 18:32:55 +00:00
ops-deploy-01 7b25be22e9 fix(#1394): recover-token headless — dual path (--email flag + piped stdin) with documented precedence (#1423)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: ops-deploy-01 <[email protected]>
2026-08-25 16:14:56 +00:00
ops-deploy-01 4e3d179e61 fix(#1390): gateway uninstall headless — --yes/--remove-data; non-TTY without consent fails loud (#1422)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: ops-deploy-01 <[email protected]>
2026-08-25 16:00:17 +00:00
ops-deploy-01andorch-01 ae58482b72 fix(#1392): review-285 N1+N2 follow-up — env has no config authority in schema-check; verification throws fatal at install (#1421)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: ops-deploy-01 <[email protected]>
2026-08-25 15:08:04 +00:00
code-infra-01 b2d40dada0 fix(#1391): boot-time ValidationPipe metatype self-check — fail loud at startup (#1419)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-infra-01 <[email protected]>
2026-08-25 14:01:24 +00:00
code-be-02andorch-01 d30a4cce00 feat(git-tools): consume .mosaic/repo.json declarations in compat mode (T51 WP5b) (#1416)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: code-be-02 <[email protected]>
2026-08-25 12:55:40 +00:00
veronicaandorch-01 4cd280e48d feat(tools/git): grant-reviewer.sh org-team reviewer grant with fail-closed read-back (#1415) (#1417)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: veronica <[email protected]>
2026-08-25 02:30:33 +00:00
code-infra-01andorch-01 8738a03893 fix(#1395): accounts.issuer column + credential-only backfill — password auth on fresh installs (#1401)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: code-infra-01 <[email protected]>
2026-08-25 01:19:29 +00:00
code-be-01andorch-01 04a01be992 ci(publish): serialize workspace-consuming image builds after publish-next-npm (#1411) (#1412)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: code-be-01 <[email protected]>
2026-08-25 01:18:02 +00:00
veronicaandorch-01 812e2df1da fix(#1408): mosaic-agent@ condition arms on either home shape (#1410)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: veronica <[email protected]>
2026-08-25 01:17:15 +00:00
veronicaandorch-01 8c292fb32f fix(#1408): legacy-socket launch guard + seat launch.sh preference (#1409)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: veronica <[email protected]>
2026-08-25 00:49:27 +00:00
code-be-01andorch-01 f45928c311 fix(ci): restore workspace manifests after publish pin transform (#1404) (#1405)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: code-be-01 <[email protected]>
2026-08-25 00:08:40 +00:00
ops-deploy-01andorch-01 4d24ae8618 fix(#1392): hash-ledger migrations + install-time schema verification (closes #1392, closes #1402) (#1403)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: ops-deploy-01 <[email protected]>
2026-08-24 23:11:50 +00:00
code-be-01andorch-01 d790572e2e ci(publish): pin next-channel @mosaicstack deps to exact same-pipeline builds (#1389) (#1400)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 23:05:37 +00:00
code-be-01andorch-01 d7b1dd9601 test(git-tools): wrapper-guard harness distinguishes tool failure from drift (#1380-FF) (#1388)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 21:43:09 +00:00
code-be-01andorch-01 0db2d19a22 feat(framework): mosaic doctor structure-anchor provisioning check (T51 WP0b) (#1379)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 20:15:08 +00:00
code-be-01andorch-01 8eb7e6354e fix(fleet): resolve-then-validate symlink guard + framework helper resolution (#1380) (#1383)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 19:39:13 +00:00
code-be-01andorch-01 9014a510a9 ci(mosaic): repo-structure declaration CI gate (T51 WP5c) (#1378)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 04:43:26 +00:00
code-be-01andorch-01 974e4740ab docs(mosaic): declare repo structure v2 (T51 WP2a) (#1377)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-be-01 <[email protected]>
2026-08-24 03:49:33 +00:00
orch-01 9cd6d39b71 fix(git-tools): pr-create API fallback resolves base from forge default branch (E4) (#1376)
ci/woodpecker/push/publish Pipeline was successful
2026-08-24 02:49:38 +00:00
code-be-01andorch-01 143f925fd8 fix(git-tools): admin-gated --no-ci-expected merge assertion for CI-less repositories (#1373)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: code-be-01 <[email protected]>
2026-08-23 18:54:49 +00:00
fredandgate-merge-01 24294d3b77 fix(git-tools): issue-view shows comment bodies and names the real tea failure (#1357) (#1365)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: fred <[email protected]>
2026-08-22 00:23:22 +00:00
veronicaandgate-merge-01 24caeab057 fix(tmux): locate the REPL input box by shape, not by a Claude-only glyph (#1363)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: veronica <[email protected]>
2026-08-21 23:45:08 +00:00
fredandgate-merge-01 888a6ad29b fix(#1356): tea login resolution fails closed on a declared git identity (#1361)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: fred <[email protected]>
2026-08-21 23:04:31 +00:00
fred 24462f460e docs(git-wrappers): correct the stale --login fallback and document the test suite's identity split (#1354)
ci/woodpecker/push/publish Pipeline was successful
2026-08-21 14:42:36 +00:00
veronicaandfred a480ee83dc docs(W4): document contract — stamp kind and status on 104 live docs (#1350)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: veronica <[email protected]>
2026-08-21 14:07:36 +00:00
fred fd43ed5420 fix(git-wrappers): no --login must use the caller's own credential, not a guessed shared login (#1352)
ci/woodpecker/push/publish Pipeline was successful
2026-08-21 04:10:42 +00:00
fargoandfred 1d84bc3f3d fix(fleet): lease-broker activation, symlink-safe unit placement, named launch refusal — Wall 6 (#1292) (#1297)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: fargo <[email protected]>
2026-08-21 00:40:29 +00:00
fred 6306914965 fix(lease-broker): no lease held is a no-op success, not a denied transition (#1339)
ci/woodpecker/push/publish Pipeline was successful
2026-08-20 23:40:09 +00:00
fred af43a7a63e docs(fleet): tier the north star and declare the tier-0 operator surface (#1337)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-20 23:05:34 +00:00
fred ca97b885b0 fix(#808): never borrow a session identity for non-tmux senders (#1335)
ci/woodpecker/push/publish Pipeline was successful
2026-08-20 21:03:50 +00:00
code-infra-01 6db0bead44 fix(#1327): sentinel-managed PATH block, default-home-only profile writes (#1330)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: code-infra-01 <[email protected]>
2026-08-20 20:20:33 +00:00
GhostfredGhost <>
c671290d77 docs: define main/next branch model, sequencing, responsibilities, and merge process (#1214) (#1215)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: Ghost <>
2026-08-20 20:19:39 +00:00
code-infra-01andfred 6a9b2cf6c1 fix(git-tools): pr-merge queue guard reads CI status from the BASE repo for fork PRs (B1) (#1334)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: code-infra-01 <[email protected]>
2026-08-20 20:18:59 +00:00
Ghostgate-merge-01Ghost <>
6bd93a621d scratchpads: fleet identity/comms/mosaic-tree continuation record (2026-08-17) (#1296)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: Ghost <>
2026-08-20 19:11:59 +00:00
GhostfredGhost <>
e9485c3d96 docs(l0): parameterize hard gates on the project's integration trunk (#1216, Option A) (#1217)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: Ghost <>
2026-08-20 18:38:18 +00:00
Ghostgate-merge-01Ghost <>
d2f0846dcc fleet: put the bootstrapped Node on PANE_PATH (#1256) (#1258)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: Ghost <>
2026-08-20 18:31:30 +00:00
fredandgate-merge-01 4f22a58041 guides: two measurement rules about pinned tool versions (#1316)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: fred <[email protected]>
2026-08-20 16:40:58 +00:00
Ghostgate-merge-01Ghost <>
9b6869fab7 fix(#1179): require security authority wiring (#1189)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: Ghost <>
2026-08-20 16:19:45 +00:00
ops-ci-01 20ad89c86b docs(ci): state the measured push-CI model in ci.yml's when-comment (#1326)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: ops-ci-01 <[email protected]>
2026-08-20 15:48:46 +00:00
ops-01 a55d1a1812 Merge pull request 'merge: absorb main into next — 23-commit divergence (08-05..13 base=main window)' (#1324) from merge/main-into-next into next
ci/woodpecker/push/publish Pipeline was successful
2026-08-20 01:07:05 +00:00
ops-01 9abd7e386f chore: serialize CI run — same tree as 9af456c, fresh pipeline head
ci/woodpecker/pr/ci Pipeline was successful
Per ops-ci-01 (PR #1324 comments 23413/23422) and fred's one-at-a-time
authorization: one serialized run of #1324's tree on the pinned image
(ci-base:lock-9cb7ffcd8828, carried since the cb9a0d1 absorb). This commit
changes no files — the tree is identical to 9af456c; it exists only to mint a
fresh pipeline head instead of restarting 2545.
2026-08-19 19:45:20 -05:00
ops-01 9af456c240 merge: absorb next (pin cb9a0d1 + #1325 + #1129) for the controlled pinned-image rerun
ci/woodpecker/pr/ci Pipeline failed
Per ops-ci-01's procedure (comments 23386/23394, brain D27): pipeline config
travels with the commit under test, so the controlled rerun requires the pinned
anchor in the PR head itself. This merge absorbs everything next took since the
original merge parent (1bdeed62): the ci-base pin (cb9a0d1, lock-9cb7ffcd8828),
the legacy-credential removal (#1325), and the ci-queue-wait statuses:null fix
(#1129 — the branch from the divergence analysis, now landed).

One conflict, same file as round 1: test:framework-shell union — our 54-entry
chain plus next's new test-ci-queue-wait-no-status.sh entry at its position
(55 entries, every target existence-verified). Enumeration guard green:
population 61, enumerated 46, excluded (signed) 16.
2026-08-19 18:43:19 -05:00
ops-ci-01 cb9a0d1642 ci: pin ci-base to immutable lock-9cb7ffcd8828 (Closes #1328) (#1329)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: ops-ci-01 <[email protected]>
2026-08-19 23:42:05 +00:00
fargo 420507da77 fix(#1323): remove legacy credential read and force-merge recipe from mosaic-gitea (#1325)
ci/woodpecker/push/publish Pipeline was successful
2026-08-19 22:51:35 +00:00
ops-01 2508f0aa99 fix(merge): round-2 CI failures — pipefail sites, verify:release mirror, format
ci/woodpecker/pr/ci Pipeline failed
Pipeline 2534 (018d96a), two test-step failures plus the format failure
root-caused by rev-code-01 (id 202):

1. pipefail-early-exit.test.mjs: main's window wrote the test but never
   wired scripts/*.test.mjs into its own CI, so its tools/install.sh pipes
   were never executed against it; next's test wiring runs them and flags
   two load-bearing pipes. Both rewritten pipefail-safe via process
   substitution (newest_matching_file ls|head, check_fleet_transport
   sed|head|tr|awk). Test now 7/7 locally.

2. verify-release.test.mjs: merge kept main's 9-command ci.yml sanitization
   but next's 4-command canonical stage. Canonical updated to mirror ci.yml
   exactly (9=9, order verified). upgrade-guard checked equal (4=4).

3. format: docs/reports/quality/1099-pipefail-sweep.md (from main f158be8)
   prettier-formatted under pinned 3.8.1.

Enumeration guard re-verified: population 60, enumerated 45, excluded 16.
2026-08-19 17:27:30 -05:00
Ghostops-02Ghost <>
d339e8fd21 fix(git-tools): ci-queue-wait — Gitea statuses:null no longer malformed; CI-less repos pushable again (#1129)
ci/woodpecker/push/publish Pipeline was successful
Co-authored-by: Ghost <>
2026-08-19 22:08:34 +00:00
ops-01 018d96a412 fix(merge): drop fleet test-start-agent-session.sh from framework-shell chain — review B1
ci/woodpecker/pr/ci Pipeline failed
Review stack#1324 id 201 (rev-code-01): the union resolution chained main's #1073
fleet test while keeping next's signed exclusion of it (#1271 burn-down: the test
asserts missing-binary behavior but CI provides pi on PATH — fails by design).
check-test-enumeration CONTRADICTORY EXCLUSION, CI pipeline 2532 terminal failure.

Fix per verified review recommendation: remove the chain entry (55 -> 54 parts),
keeping next's exclusion and #1271 state. Guard now passes:
population 60, enumerated 45, excluded (signed) 16.
2026-08-19 16:44:31 -05:00
ops-01 b01950e92f merge: absorb main into next — 23-commit divergence (08-05..13 base=main window)
ci/woodpecker/pr/ci Pipeline failed
17 content commits + 3 merge bubbles were genuinely missing from next (~8,000 lines:
goal controller #1152, framework enforcement #1174/#1195, pr-edit wrapper #1173/#1200,
pipefail series #1100/#1105/#1106/#1107, git-tools fixes #1073/#1085/#1086/#1089,
#991, #1007, enrollment tolerance #1094). 3 commits were already in next by content
(#1060 identical, #1066/#1062 evolved twins — conflicts resolved to next's side).

Per-commit classification and evidence: mosaic-brain fleet/lanes/stack-remediation/main-next-divergence.md.
Conflict resolutions (6 files) itemized in the PR body.
2026-08-19 16:27:17 -05:00
fargo 1bdeed62eb fix(#1320): placeholder-ize private-network topology, drop raw-curl force-merge recipe (#1322)
ci/woodpecker/push/publish Pipeline was successful
2026-08-19 21:15:54 +00:00
fargo 840c2b0d96 Merge pull request 'skills: fold agent-skills into the monorepo, single install path, promote ms-unslop (plan phase D)' (#1319) from fold-agent-skills into next
ci/woodpecker/push/publish Pipeline was successful
2026-08-19 20:08:09 +00:00
fargo 95d5cb32d4 format: cover folded skills' js/ts scripts with repo prettier
ci/woodpecker/pr/ci Pipeline was successful
The repo format:check glob covers ts/js alongside md; four non-markdown
scripts inside the folded tree were flagged after the md pass. Same pinned
prettier 3.8.1, same markup-only class (verified: node --check still passes
on the js files).
2026-08-19 14:41:18 -05:00
fargo d5f3fae896 skills: promote ms-unslop from skills-local into the package
Phase D3 of plan 2026-08-19 (decision S21): skills-local is the local test
bed; promote individually as each proves out. ms-unslop is the only one of
the seven local skills with working enforcement evidence — a checker
(tools/unslop-hook/unslop-check.js), a machine-source list (lists.json), a
19-test suite, a measured corpus, and a regression fixture.

The checker itself stays fleet-local for now (it binds to a specific
harness extension surface); this promotes the skill document only.

Gates verified on the moved file: sanitization denylist clean, prettier
3.8.1 clean (6730 chars was fred's measure at assignment; 7249 as shipped
today — both pass).
2026-08-19 14:39:46 -05:00
fargo 1556982dbc skills: single install path — canonical skills ship with the framework
Phase D2 of plan 2026-08-19: single package, single install, single command.

The framework installer already treats skills/** as a shipped, manifest-owned
framework subtree, so the folded skills now install into
$MOSAIC_HOME/skills with the rest of the framework — no second repository,
no separate sync step:

- mosaic-sync-skills (bash + powershell): the fetch machinery is gone (clone,
  pull, dirty-state migration, rsync from sources/agent-skills). The script
  now only links installed skills into runtime homes. --link-only is a compat
  no-op; --no-link exits having nothing to do.
- catalog.ts: the sources/agent-skills fallback is dead and removed.
- install.sh, launch.ts, defaults/README.md, README.md, skills/README.md:
  references to the second repo rewritten to describe the shipped path.

Verified: clean install into a fresh MOSAIC_HOME produces 102 skills with no
sources/ directory; the linker then links the selected skills into the four
runtime homes with no git involvement.
2026-08-19 14:39:28 -05:00
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00
fargo d2eeb64433 skills: sanitize operator-identity tokens from folded ops skills
Four folded skills carried operator identity tokens that the sanitization
gate (verify-sanitized.sh) forbids in the public framework package:

- kickstart: template path pointed at a private brain checkout; now uses the
  framework-shipped $MOSAIC_HOME/templates/docs/TASKS.md.template
- mosaic-deploy: dropped one estate-specific stack-name row from the example
  table
- mosaic-portainer, mosaic-woodpecker: credentials now name the framework
  credentials store (load_credentials <service>) instead of a private
  checkout path

Estate-specific values can live in a skills-local override, which the linker
applies with precedence over canonical skills.
2026-08-19 14:34:00 -05:00
fargo 5e58597dbe fold: absorb mosaicstack/agent-skills into the framework skills tree
Fold the agent-skills repository into the monorepo as the shipped canonical
skills package (plan 2026-08-19 phase D1, decision S19: single package, single
install, single command).

History is preserved by rewriting each commit's paths from skills/ to
packages/mosaic/framework/skills/ (git fast-export/import) and merging the
rewritten history with --allow-unrelated-histories, so the original commits
with their authors, dates, and messages remain reachable. Blob content is
untouched by the rewrite; tree fidelity was verified blob-sha-for-blob-sha.

This change must be merged with a real merge commit (not squash) or the
history link is destroyed.
2026-08-19 14:33:03 -05:00
jason.woltje fe4fa20309 Merge pull request 'guides: add SEAT-IDENTITY and FLEET-COMMS; harden CODE-REVIEW evidence rules' (#1313) from fred/guides-seat-identity-fleet-comms into next
ci/woodpecker/push/publish Pipeline was successful
Reviewed-on: #1313
Reviewed-by: rev-code-01 <[email protected]>
2026-08-19 15:44:57 +00:00
fred 5e93ef70bd guides: fix the cross-reference direction in SEAT-IDENTITY
ci/woodpecker/pr/ci Pipeline was successful
rev-code-01's non-blocking nit on #1313 round 2. The no-linking-step paragraph
pointed at the bridge explanation as 'described below'; it is above. Now names the
section, which survives further reordering better than a direction word does.

Text-only. Verified with the repo's PINNED prettier (3.8.1 via pnpm-lock.yaml) and
the sanitization gate, both clean.
2026-08-18 19:10:09 -05:00
fred 3884f2de4d guides: address rev-code-01's review of #1313 (B1, B2, S1, S2)
ci/woodpecker/pr/ci Pipeline was successful
All four findings reproduced before fixing. rev-code-01 was right on each.

B2 (blocker, mine). SEAT-IDENTITY provisioning step 4 said to symlink the
framework store entry to the seat slot, while the same file says those bridges
must not be recreated. The same bridge, told both ways, in one document. I
rewrote the resolution and token-location sections when the deploy made them
stale and did not carry the change into the numbered steps. Step 4 is gone and
the file now says explicitly that no provisioning step links the store to the
slot, so the omission cannot read as an oversight.

S1 (mine). The guide claimed the helper "attempts a fleet notification" on
refusal. The shipped helper does no such thing — its only reference to
notification is a comment saying an alert built on the record is best-effort, and
there is no send or wake call anywhere in the file. Now: it writes a durable
record, the record is what exists, and nobody should wait for a notification that
nothing sends. A guide that promises an alert is worse than one that promises
nothing.

S2. Estate-local content removed from files that ship to every estate: the
~/.mosaic/fleet/bin script paths (dead paths elsewhere) and the 2026-08-18 dates,
which dated a specific host's migration rather than describing behavior. The
bridge-removal passage now states the ORDERING that matters — remove bridges only
after a seat-aware helper can reach the slot, never before — which is the part
that transfers.

B1. prettier reformatted all three files. Reproduced the pipeline 2515 failure
locally before and confirmed clean after; the other three guides prettier flags
are untouched by this branch (0 changes vs origin/next) and are pre-existing.

Sanitization gate re-run and passing.

Verified for the record, since I could not verify my own work: rev-code-01
confirmed the no-fallback claim TRUE against helper content on origin/next, and
judged the evidence rules actionable on the grounds that each names an executable
replacement.
2026-08-18 18:51:15 -05:00
fred a3c50d91ca guides: genericize the operator name in SEAT-IDENTITY provisioning
ci/woodpecker/pr/ci Pipeline failed
Pipeline 2514 failed the sanitization gate on 'Jason mints the token into the
seat slot'. The denylist is jarvis|jason|woltje|... and a shipped framework file
must not carry operator identity. My mistake: I generalized the estate paths and
seat names when promoting this guide and did not check the operator name.

Now reads 'the estate operator', with the accompanying rule that an agent does
not ask another agent to mint one either.

Verified by running tools/quality/scripts/verify-sanitized.sh locally rather than
guessing at the pattern: gate passes.
2026-08-18 18:28:37 -05:00
fred 2fd102e6af guides: state the decree, drop the mechanism
ci/woodpecker/pr/ci Pipeline failed
The #1280 prohibition carried an explanation of how the tools misattribute and
why the failure is invisible from inside them. A reader who is not going to use
the tool cannot act on any of it. Same for rule 2's closing clause about what
reviews commonly miss. Both cut to the decree and the corrective action.

Rules 1 and 3-12 keep their trailing sentences: those are corrective actions or
the detail that makes the case recognizable, not justification.
2026-08-18 18:25:47 -05:00
fred efb3c3a10c guides: add SEAT-IDENTITY and FLEET-COMMS; harden CODE-REVIEW evidence rules
ci/woodpecker/pr/ci Pipeline failed
Three guides that existed only as one host's working copy, promoted to framework
templates so every estate gets them. A working copy under ~/.mosaic binds one
host; only a template here binds all of them.

SEAT-IDENTITY.md (new) documents how a seat's git credential is actually
resolved after #1311: identity from MOSAIC_GIT_IDENTITY, then
mosaic.gitIdentity, then the stdin username; host mapped to a store prefix; then
ONE of two stores chosen by whether the seat directory exists, with no
precedence and no fallback between them. A seat with a directory and an empty
slot fails closed rather than reaching the service store, and that is the point.

It also corrects how to find the helper. credential.helper commonly names an
absolute path, so `command -v git-credential-mosaic` answers a different question
than the one git asks, and the two stop agreeing the moment the PATH copy is
removed. Git also tries EVERY configured helper in order, so a fail-closed helper
in front silently hands the request to whatever is configured behind it. The
guide says to read the whole list.

FLEET-COMMS.md (new) documents agent-send.sh: the class table, the addressing
preamble, and the exit codes — including that rc=2 means the text reached the
pane as an unsubmitted draft, so retrying double-sends it. Confirm with
capture-pane instead. It also says to measure the fleet rather than trust
roster.yaml, which on a live host was simultaneously naming a socket that did not
exist, listing seats that were not running, and omitting seats that were.

CODE-REVIEW.md gains an Evidence Discipline section: a green is not a result
until you have shown it could go red, measurement and explanation are separate
sentences, verify by content on the ref that ships rather than by ancestry of a
local sha, and confidence is part of a finding. Plus four shell-measurement rules
earned on #1311, each of which produced a wrong conclusion first — `cmd | tail;
echo rc=$?` reports tail's status, a missed glob under pipefail exits 2 and kills
the run under set -e, nonzero-with-no-output is an environment question before it
is a code question, and `git -C` in a non-repo directory answers from the
enclosing repo.

The estate-specific repository exception that lived in the working copy is not
carried here. The template says an estate may document one, scoped to a named
repository and never precedent for a second.

Both new guides are added to the two routing tables that agents read.
2026-08-18 18:15:50 -05:00
jason.woltje d4d32a80b2 Merge pull request 'git credentials: fail closed, and read a seat's token from its own slot' (#1311) from fred/credential-fail-closed-seat-slots into next
ci/woodpecker/push/publish Pipeline was successful
Reviewed-on: #1311
2026-08-18 22:40:46 +00:00
fred c703cc50eb git-credential-mosaic: escape the escalation record, and stop naming a record that was never written
ci/woodpecker/pr/ci Pipeline was successful
Both defects found in review by rev-code-01 on #1311.

F3 — the JSONL record interpolated every field with a bare %s. An identity comes
from git config or the environment and a cwd is whatever directory git ran in, so
either can contain a quote or a backslash. One such refusal turned the day's spool
into unparseable JSONL, and the operator would only discover it while reading the
record that explains an outage. Fields are now JSON-escaped.

F2 — the diagnostic printed "record: <spool>/<date>.jsonl" unconditionally, but
the record is only written inside the branch where mkdir -p succeeded. When the
spool cannot be created the helper named a file that does not exist, on exactly
the hosts where the escalation was lost. It now reports the real path or says
NOT WRITTEN.

Also: prettier on README.md, which was the format-step failure on pipeline 2508.
It reflowed only the two tables this branch added.

Tests: cases 14 and 15 cover both. Verified discriminating — against the previous
helper with these same tests, case 14 fails with the unparseable record printed
and case 15 fails on both assertions; against this one both pass.

The first draft of case 14 used `ls "$spool"/*.jsonl | head -1`, which under
`set -o pipefail` exits 2 on a missed glob and killed the suite with zero output
— the same silent-nonzero failure rev-code-01 hit from a partial tools/ extraction
and the reason this file exists. Replaced with a glob loop and a comment.
2026-08-18 17:04:55 -05:00
fred 3d2b712355 git credentials: fail closed, and read a seat's token from its own slot
ci/woodpecker/pr/ci Pipeline failed
Two changes to one rule: a credential is resolved from exactly one place,
and an identity that cannot be resolved is refused rather than substituted.

FAIL CLOSED. Both readers ended in an unconditional fall-through to the
shared Gitea account whenever an identity did not resolve. Every seat in a
fleet therefore pushed, opened PRs and filed reviews under one account, and
a record made that way cannot be traced to the agent that made it
afterwards. The fallback now applies only where there is no attribution to
lose: a host with no fleet. Where seats exist, an unresolvable request emits
nothing, exits nonzero, explains itself on stderr, and — in the git helper —
appends a record naming the identity, host, reason and cwd, and no token
value, to ${MOSAIC_CREDENTIAL_SPOOL:-~/.local/state/mosaic-credential-escalations}.

A host runs a fleet when <brain>/fleet/agents exists, which is the signal
packages/mosaic/src/fleet/brain-home.ts already uses to decide a brain is
active, resolved the same way (MOSAIC_BRAIN_HOME, else ~/.mosaic). This is
what keeps the change a no-op for an operator who has not provisioned
per-slot tokens: no fleet directory, shared account, unchanged. It is also
why there is no environment variable to restore the old behavior — one would
reintroduce the substitution being removed.

STORE SELECTION. Both readers hardcoded ~/.config/mosaic/secrets/gitea-tokens,
so a seat's own secrets/ slot was invisible to the framework: a seat could
hold a valid credential and still be served the shared account. The store is
now chosen by what the identity is. An identity with a directory under
<brain>/fleet/agents/ is a seat and is read only from
<brain>/fleet/agents/<id>/secrets/; any other identity is a service identity
and is read from the framework store. There is no precedence between them
and no fallback from one to the other, so a seat with an empty slot is
refused even when a same-named token sits in the framework store. Two copies
of one credential are drift rather than redundancy, and drift surfaces as
the stale copy returning 401, which reads as a revoked token and sends
whoever debugs it somewhere else.

detect-platform.sh is in scope alongside git-credential-mosaic because they
are the two readers of these tokens. Patching only the git helper would make
"one credential, one location" true for push and fetch and false for
pr-create.sh, issue-create.sh and pr-review.sh, which is the harder failure
to notice.

TESTS. The three assertions that pinned the shared-account fall-through are
now fail-closed assertions, and a refusal is checked four independent ways:
nonzero exit, empty stdout, a stderr diagnostic naming identity and host,
and no shared token value anywhere in the output. The exit code alone would
pass against a helper that emitted the credential and then failed. Added:
seat-slot resolution, the no-cross-store-fallback case with a control
proving the framework-store file it declines to read is readable, no-identity
on a fleet host, the fleet gate firing on the default ~/.mosaic and not only
on an injected MOSAIC_BRAIN_HOME, and a cross-host leak check. Both suites
were run against the pre-change code as a control and fail there on exactly
the shared-token emission.

shellcheck is not installed on the authoring host, so the rewritten helper
is unlinted locally and CI is the first lint of it.
2026-08-18 16:19:43 -05:00
fargo 245e0c427d feat(quality-rails): typed evaluator absorbs QC-19/QC-20; verify-release wiring (RI-3-002, #1275) (#1308)
ci/woodpecker/push/publish Pipeline failed
2026-08-18 17:54:47 +00:00
jarvisandfargo ff45f7b5d0 docs(ri-050): forge fail-closed docs + TASKS status catch-up (#1275) (#1299)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: Jarvis <[email protected]>
2026-08-18 16:00:36 +00:00
jarvisandfargo 64350892e7 docs(ri-050): RI-3-001 complete quality-rails probe inventory (#1275) (#1302)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: jarvis <[email protected]>
2026-08-18 15:59:34 +00:00
jason.woltjeandjarvis 6e9df3c640 fix(doctor): greenfield brain lock-in note (fred's #1301 follow-up, #1288) (#1307)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: Jason Woltje <[email protected]>
2026-08-18 07:57:37 +00:00
jarvis f5ba042dfa test(ri-050): RI-1-002 publish-gate negative controls (#1275) (#1305)
ci/woodpecker/push/publish Pipeline failed
2026-08-18 05:57:06 +00:00
jarvis 7c7dab3898 feat(ri-050): RI-5-001 typed freshness states and stale-safe Mission Control surfaces (#1275) (#1300)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-18 05:56:58 +00:00
fargoandjarvis d92de53399 feat(prd): one transitional PRD authority — RI-4-001 (#1275) (#1294)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: fargo <[email protected]>
2026-08-18 05:56:51 +00:00
mos-dt-0andjarvis d7e303d3c0 fix(macp): fail-closed typed gate states — RI-2-002 (#1275) (#1293)
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: mos-dt-0 <[email protected]>
2026-08-18 05:56:38 +00:00
jarvis 726d2ad3a2 fix(ri-050): forge fails closed without providers; explicit typed simulation (#1275) (#1278)
ci/woodpecker/push/publish Pipeline was canceled
2026-08-18 05:52:45 +00:00
jason.woltjeandjarvis e4ee1acf24 feat(doctor): brain-home fleet-state check (#1298 follow-up) (#1301)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: Jason Woltje <[email protected]>
2026-08-18 05:26:01 +00:00
jarvis 5c5a25e4de fix(ci): image pushes read the registry secrets that exist (#1275) (#1306)
ci/woodpecker/push/publish Pipeline failed
Squash-merged by topher (jarvis principal) via break-glass API path (wrapper main-only gap, documented). Gates: CI 2492 green at head e19013ed, review 181 APPROVED (fred) at pinned head. Diagnostic merge: the push pipeline now exercises kaniko with the REGISTRY_* secrets - valid creds yield the first fully green gated publish; invalid yield an explicit 401.

Co-authored-by: Jarvis <[email protected]>
2026-08-18 05:02:23 +00:00
jarvis 7669321ea2 test(gateway): cross-user-isolation cleanup honors dbAvailable — unblocks gated publish verify (#1275) (#1304)
ci/woodpecker/push/publish Pipeline failed
Squash-merged by topher (jarvis principal) via break-glass API path (wrapper main-only gap, documented in #1275 log). Gates: CI 2487 green at head 81f500bd, review 180 APPROVED (fred) at pinned head. Unblocks gated publish: verify's no-DB path now skips cleanly.

Co-authored-by: Jarvis <[email protected]>
2026-08-18 04:24:48 +00:00
jarvis d8e0aec950 feat(ri-050): bind next publication to exact-commit terminal verification — RI-1-001 (#1275) (#1277)
ci/woodpecker/push/publish Pipeline failed
Squash-merged by topher (jarvis principal) via break-glass: pr-merge.sh hard-codes main-only merge targets and cannot express this repo's next trunk. Gates: CI 2476 green at head 46784c8d, review 177 APPROVED (fred) at pinned head. First gated publish: every publish step now depends on verify-release at the exact commit.
2026-08-18 03:57:43 +00:00
jarvis 49d6136b02 docs(ri-050): bootstrap release-integrity workstream for 0.0.50 (#1275) (#1276)
ci/woodpecker/push/publish Pipeline was canceled
Squash-merged by topher (jarvis principal) via break-glass: pr-merge.sh hard-codes main-only merge targets and cannot express this repo's next trunk. Gates: CI 2475 green at head 758659dd, review 176 APPROVED (fred) at pinned head.
2026-08-18 03:57:27 +00:00
jason.woltjeandjarvis a80bae950d feat(fleet): brain-home split — fleet state under ~/.mosaic, templates stay config-home (#1298)
ci/woodpecker/push/publish Pipeline failed
Co-authored-by: Jason Woltje <[email protected]>
2026-08-18 03:23:15 +00:00
jason.woltje 8199261caa Merge pull request 'fix(ci): unwire test-start-agent-session.sh, restore its signed exclusion — unblocks every PR on next' (#1270) from fix/1269-ci-chain-unblock into next
ci/woodpecker/push/publish Pipeline failed
Reviewed-on: #1270
2026-08-17 20:44:59 +00:00
fred 57a2f2b40e docs(ci): point the exclusion at tracking issue #1271, not the closed first filing
ci/woodpecker/pr/ci Pipeline was successful
The first PR for this change was filed under the retired mos-dt-0 principal
(pr-create.sh has no --login flag and find_tea_login_for_host returns the first
host match) and was closed and refiled as #1270. That left in-tree references
pointing at a closed duplicate PR rather than at the burn-down issue, which is
the wrong target for them anyway: the open design question belongs on #1271.
2026-08-16 18:03:02 -05:00
fred 93c1de51e1 fix(ci): unwire test-start-agent-session.sh, restore its signed exclusion (#1269)
ci/woodpecker/pr/ci Pipeline was canceled
The `test` step has failed on every `next` pipeline since #1017 on exactly one
assertion, and it is the same one on unrelated PRs:

    FAIL: host provides 'pi' in the system path; missing-binary cases are not
    measurable here            (framework/tools/fleet/test-start-agent-session.sh:103)

Measured 2026-08-16 across pipelines 2444 (#1256), 2438 (#1240) and 2441
(#1017-quality): exactly one FAIL line in each full log, identical, this line.
Control `zzz-not-present-zzz` -> 0 on all three.

Cause. #1241 (5c35a250) added the guard: the suite shims fake mosaic/pi/npm into
$FAKE_BIN, but the constructed PANE_PATH always ends in the real system path, so
on a host that installs those binaries the missing-binary cases cannot be
measured and a green run would mean nothing. The guard says so instead of
passing. Its own pipeline 2430 was green only because the suite was CI-excluded
at the time, so the guard had never run in CI. #1017 (c56483eb) then enumerated
it and dropped the exclusion. The CI image installs
@earendil-works/[email protected].1 on purpose, so the precondition is
unsatisfiable there. Both commits are mine.

The guard is correct and is not being softened. A check that cannot measure its
property and reports success is the failure mode this repo has been cataloguing
all week; the error was wiring the suite into an image that violates its
precondition, so the wiring is what gets reverted.

Second effect, which is the reason this cost a day rather than an hour:
test:framework-shell is one && chain and this sat at position 44 of 48, so
glpi/test-list-http-status.sh, orchestrator/test-board-roll.sh,
woodpecker/test-ci-wait-exit-matrix.sh and _scripts/test-fleet-transport-check.sh
have not run at all since the merge. The pipeline reported one failure, never
"one failure plus four unrun". All four are green when run directly on
sb-it-1-dt, so the mask hid nothing broken -- but that is a local result on one
host, not a CI-image result.

Verification, with controls:
- enumeration guard OK (population 52, enumerated 36, signed-excluded 16).
- control A, exclusion line removed while unwired -> FAIL UNENUMERATED.
- control B, exclusion line kept while rewired -> FAIL CONTRADICTORY EXCLUSION.
  The gate discriminates in both directions, so its OK is load-bearing.
- the four formerly-masked suites: rc=0 each, run directly.
- the full chain cannot be run to completion on sb-it-1-dt: it stops earlier, at
  the lease-broker Invariant R test, because this host carries the quarantined
  operator-global pi 0.84.2 against a measured 0.84.1. That is host-specific and
  out of scope here -- CI pins 0.84.1, and the single FAIL line in those three
  pipelines proves positions 1-43 passed there.

Burn-down is to control the tail of PANE_PATH inside the test, not to remove pi
from the image. Recorded in the exclusion reason and in #1269.
2026-08-16 17:58:49 -05:00
fred 476db12b92 Merge pull request 'fix(fleet): tell the operator when the fleet transport is missing (#1240)' (#1245) from fix/1240-fleet-transport-check into next
ci/woodpecker/push/publish Pipeline failed
Reviewed by scooby via git comms (terminal ACK 32d986). Merge directed by Jason 2026-08-16. Conflicts from #1229 and #1252 resolved on the branch by a pi seat; pre-merge gate verified: tools/install.sh=5d28f773, framework/install.sh=1578c33b, test:framework-shell=48 links with both #1252 and #1245 suites present.
2026-08-16 18:12:33 +00:00
fred 5198c3f198 merge next into fix/1240-fleet-transport-check
ci/woodpecker/pr/ci Pipeline failed
Resolves conflicts from #1229 (tools/install.sh node provisioning) and #1252
(package.json test:framework-shell). tools/install.sh resolved to the reviewed
composite blob 5d28f773; package.json resolved as a union so both #1252's four
suites and #1245's transport-check suite run (48 links).
2026-08-16 13:11:31 -05:00
fred 19ac0a02d7 Merge pull request 'test(#1017): wire in four CI-fit shell suites, drop their signed exclusions' (#1252) from fix/1017-wire-start-agent-session into next
ci/woodpecker/push/publish Pipeline was canceled
Reviewed by scooby via git comms (no mosaicstack principal on fomo-lin; review is the comms record, terminal ACK 32d986). Merge directed by Jason 2026-08-16. Part of the five-PR greenfield composite verified E2E on two independent bare boxes.
2026-08-16 18:06:53 +00:00
fred 6d9387c857 Merge pull request 'fix(fleet): fail the agent launcher when the pane cannot survive (#1241)' (#1244) from fix/1241-launch-failure-visible into next
ci/woodpecker/push/publish Pipeline was canceled
Reviewed by scooby via git comms (no mosaicstack principal on fomo-lin; review is the comms record, terminal ACK 32d986). Merge directed by Jason 2026-08-16. Part of the five-PR greenfield composite verified E2E on two independent bare boxes.
2026-08-16 18:06:50 +00:00
fred 14cb9c6a1e Merge pull request 'fix(fleet): let ps/install work on a roster-v2 fleet, and refuse add/remove honestly (#1237 piece A)' (#1243) from fix/1237-fleet-v2-dispatch into next
ci/woodpecker/push/publish Pipeline was canceled
Reviewed by scooby via git comms (no mosaicstack principal on fomo-lin; review is the comms record, terminal ACK 32d986). Merge directed by Jason 2026-08-16. Part of the five-PR greenfield composite verified E2E on two independent bare boxes.
2026-08-16 18:06:35 +00:00
fred b5b322f80d Merge pull request 'fix(installer): pin umask and set the 0700 modes the fleet boundary requires (#1236)' (#1242) from fix/1236-installer-dir-modes into next
ci/woodpecker/push/publish Pipeline was canceled
Reviewed by scooby via git comms (no mosaicstack principal on fomo-lin; review is the comms record, terminal ACK 32d986). Merge directed by Jason 2026-08-16. Part of the five-PR greenfield composite verified E2E on two independent bare boxes.
2026-08-16 18:06:21 +00:00
jason.woltje e4674709be Merge pull request 'fix(installer): make a greenfield install actually work — node bootstrap, PATH, wizard profile' (#1229) from fix/installer-path-and-node into next
ci/woodpecker/push/publish Pipeline was canceled
Reviewed-on: #1229
2026-08-16 18:01:07 +00:00
fred c56483eb1b test(#1017): wire in four CI-fit shell suites, drop their signed exclusions
ci/woodpecker/pr/ci Pipeline failed
check-test-enumeration.sh signed four suites as 'likely CI-fit; #1017 burndown'.
Measured all four: each passes standing alone, and each still passes with tmux
removed from PATH entirely (test-start-agent-session.sh writes its own tmux shim
into a fake bin dir, so it never needed the real binary).

Red-first: removing the four exclusion lines makes the guard report exactly four
UNENUMERATED failures. Appending the four to test:framework-shell returns it to
OK, with in-population enumerated going 32 -> 36 and signed exclusions 19 -> 15.

Refs #1017
2026-08-16 01:14:03 -05:00
fred 5c35a250de test(fleet): name what the pane-boundary case's binary check rides on (#1241)
ci/woodpecker/pr/ci Pipeline was successful
Review finding from scooby. This case does not use run_start, so
install_pane_binaries' symlinks land under a home its launcher never consults
(HOME is the trusted parent here). It resolves mosaic and pi through
MOSAIC_RUNTIME_BIN=$FAKE_BIN instead. Valid path, valid green — and a trap for
anyone who later drops that env var believing the symlinks cover it, which
would break the #1241 binary check rather than exercise it.

Comment only; no behavior change. Harness rc=0.

Refs #1241.
2026-08-16 00:21:13 -05:00
fred 10a1f82031 test(fleet): cover the pane-pid-unresolved branch this PR shipped (#1241)
ci/woodpecker/pr/ci Pipeline was canceled
Review finding from scooby: this PR added a failure branch the harness
structurally could not reach. The fake tmux answered `has-session` only for
`=_holder:0.0`, so every non-holder agent landed in the session-is-gone branch
no matter what — the `elif` (tmux still reports the session, no pane PID after
the retries) had zero coverage and no way to get any.

That is the same shape as the bug this PR exists to fix, one layer down: a code
path shipped green where the gate that should measure it cannot. Less severe,
because the branch fails closed at exit 69 rather than reporting success — but
"the harness can't reach it" is the sentence that precedes the next silent
regression, so it gets closed here rather than filed.

`MOSAIC_TEST_HELD_SESSIONS` lets a case name targets the shim should also
answer for. It answers them only AFTER `new-session`, and that detail is the
whole trick: the launcher asks `has-session` about the same name twice — once
at line 255 where a yes means "already running, exit 0", and once at 417 where
a yes means "the session survived". A shim answering yes to both short-circuits
at the first and never reaches the branch under test. It would have looked like
coverage while measuring the idempotency path.

Both failure modes were measured, not reasoned about:
- toggle absent (the old shim): `code=pane-did-not-survive` — the case lands on
  the wrong branch, which is exactly the unreachability being reported.
- toggle answering unconditionally: launcher exits 0 via the idempotency
  short-circuit — "launcher reported success over a session with no resolvable
  pane PID".
- toggle gated on new-session: `code=pane-pid-unresolved`, exit 69.

The case also asserts the diagnostic is not `pane-did-not-survive` and does not
mention the heartbeat, so the two pane faults cannot collapse into one message.

Gates: bash -n · launcher harness rc=0 · test-fleet-units.sh (real tmux) rc=0 ·
fleet specs 342 passed.

Refs #1241.
2026-08-16 00:19:25 -05:00
fred b61789fe26 fix(fleet): tell the operator when the fleet transport is missing (#1240)
ci/woodpecker/pr/ci Pipeline was successful
`mosaic fleet --help` reads "Manage the local Mosaic tmux fleet" and every
roster the CLI scaffolds sets `transport: tmux`, but neither `tools/install.sh`
nor `tools/_scripts/mosaic-doctor` contained the string "tmux" at all. A
greenfield host therefore came out of the installer able to install a fleet,
start a fleet, and run no seat, with `mosaic fleet ps` as the operator's first
and only signal.

Measured on mosaic-sbx-dev (Debian, no tmux, framework installed): `mosaic-doctor`
reported 11 warnings and not one of them named the reason no seat could launch.

The installer gets a warning, not a `require_cmd` hard failure: tmux is required
by the fleet, not by mosaic. Hosts that install this to run `mosaic claude` and
never scaffold a roster are common, and failing their install over a binary they
do not need would be wrong. The check runs in `--check` mode too — "what is the
state of this host" is the question `--check` is asked.

Both checks read the roster's own `transport:` rather than assuming tmux, so a
host declaring something else is pointed at the binary it actually needs instead
of at the wrong package.

The two implementations are deliberately parallel and each carries a comment
pointing at the other. They are separate because the installer must answer this
before the framework's own scripts are guaranteed to be on disk. One harness
drives BOTH from the shipped text — the functions are extracted from the scripts
by awk rather than copied — so the pair cannot drift silently, and the test
cannot keep passing after the shipped copy changes.

The harness is wired into `test:framework-shell`. Without that it would have
tripped the #1017 enumeration guard as UNENUMERATED, which is the guard doing
its job: a check nothing runs is not a check.

Evidence:
- red: the harness fails against origin/next ("could not extract
  fleet_declared_transport"); `grep -ci tmux` on both files at origin/next = 0.
- green on real hosts, all four branches:
  - dev (no tmux, no roster)  -> WARN naming tmux, points at `mosaic fleet init`
  - dev (no tmux, v2 roster)  -> WARN naming the roster, points at `mosaic fleet start`
  - dev installer --check     -> WARN saying start "reports success and no seat comes up"
  - canary (tmux present)     -> `[OK] Fleet transport available: tmux` under --verbose,
                                 silent by default (pass() is verbose-gated), installer silent
- harness green on node:24-alpine/busybox, the CI base image.
- `bash -n` x3, `pnpm typecheck` 45/45, fleet specs 342 passed,
  enumeration guard OK, its self-test OK, prettier clean.

Refs #1240. Upstream of #1237/#1243 and #1241/#1244: a correct fix for either of
those still leaves this host with no live seat.
2026-08-16 00:15:50 -05:00
fred 61a907a12f fix(fleet): fail the agent launcher when the pane cannot survive (#1241)
ci/woodpecker/pr/ci Pipeline was successful
`mosaic fleet start` returned 0 over three dead panes. The launcher knew,
and said the wrong thing at the wrong severity to the wrong layer.

The pane runs `mosaic yolo <runtime>` under PANE_PATH with a cleared
environment. When that binary is absent the pane dies in under a second,
tmux destroys the session, and the diagnostic goes with it. The launcher
then found no PANE_PID, printed a WARNING about the *heartbeat sidecar*,
and exited 0 — so systemd logged "Finished ... successfully" and
`fleet start` reported success. `fleet ps` was the only component telling
the truth.

Two changes, both in start-agent-session.sh:

1. Before any effect, resolve `mosaic` and the roster's runtime against
   PANE_PATH — the pane's own view of the path, not the launcher's.
   `mosaic yolo <runtime>` calls checkRuntime(runtime) and looks for a
   binary named exactly like the runtime, so this asks the same question
   the pane will ask a moment later, while an operator can still see the
   answer. Absent binary -> exit 69, code=missing-binary, no session
   created.

2. Replace the dead-pane WARNING+exit-0. An absent session one second
   after new-session is a runtime that died on startup, not a heartbeat
   problem -> exit 69, code=pane-did-not-survive, with the command to run
   by hand to see why. A present session with no pane PID after five
   attempts -> code=pane-pid-unresolved. Neither branch kills the
   session; destroying a possibly-live pane on a guess is worse than
   leaving it for inspection.

Exit 69 (EX_UNAVAILABLE) is deliberate: the 64s already in this file mean
the projection was bad, and here the data is fine and the host is not
ready. Callers separate the cases by `code=`, the same way fail_env's
codes share 64.

This propagates for free. `fleet start` calls runChecked() for the holder
and each agent, and runChecked throws on non-zero, so layers 4 and 5 stop
lying without a TypeScript change. Two adjacent defects are left for a
follow-up issue rather than widened into this diff: the per-agent loop
aborts on the first failure instead of attempting all and reporting an
aggregate, and runChecked's bare throw surfaces the launcher's message
under a Node unhandled-rejection stack trace because program.parse() is
synchronous.

Tests:

- test-start-agent-session.sh gains three cases: `mosaic` absent from the
  pane path, the runtime absent from the pane path, and a pane that does
  not survive. Each was verified individually red against the unmodified
  origin/next launcher.
- The two cases asserting a valid launch now supply a pane PID. Until now
  the suite's one success path was itself a dead pane the launcher
  reported as fine.
- The harness fakes `npm` so PANE_PATH stops depending on whatever the
  host has installed, and fails loudly if the host provides `mosaic` or
  `pi` in the system path, where the missing-binary cases would not be
  measurable at all.
- test-fleet-units.sh gains a `pi` shim in its runtime bin. The real-tmux
  harness named `pi` in its roster and never installed it; the new
  preflight caught it.

Refs #1241
2026-08-15 23:56:53 -05:00
fred 6f5b4c3dc1 fix(fleet): restore ConditionPathExists dropped by my own red-check
ci/woodpecker/pr/ci Pipeline was successful
Self-inflicted and worth recording rather than quietly amending.

To prove the new tests were red without the fix I ran
`git checkout origin/next -- <fleet.ts> <[email protected]>`. That writes
the *index*, not just the working tree. Copying my versions back afterwards
restored the working tree only, so the unit file sat staged-as-origin/next and
modified-in-tree, and the next commit (67f5014c) committed the index — silently
removing the ConditionPathExists line that 463745e3 had added.

Nothing caught it. The spec reads the file from the working tree, so it stayed
10/10 green against a HEAD that no longer had the guard. Found by reading
`git status` after the push, not by any gate.

Verified by content, not by assumption:
  origin/next  0 occurrences
  463745e3     1
  67f5014c     0   <- the regression
  this commit  1

Refs #1237
2026-08-15 23:35:54 -05:00
fred 67f5014cc0 fix(fleet): refuse v2 add/remove cleanly, and pin the Condition's effect
Two follow-ups from the canary red->green run and scooby's review.

1. The v2 refusal in `add`/`remove` was a bare `throw`, which reaches the CLI
   top level uncaught and prints the guidance under a Node stack trace. The
   message *is* the point of the refusal, so it now goes through
   `command.error()` — the same clean path the roster-config error uses.
   Caught on canary, not in review: the unit tests asserted the message text
   and passed either way.

2. The unit-template test asserted only that ConditionPathExists is present.
   Presence is not effect. Added two tests for the parts that can drift in
   code while that assertion still passes: the condition resolving to exactly
   the file the fleet writes (%h/%i rendered against a real install), and the
   launcher genuinely failing on an absent generated env (exit 64,
   `missing-file`) — which is what makes the condition load-bearing rather
   than decorative.

systemd is not available in the suite, so the effect itself was measured on
canary (2026-08-16), roster v2 generation 3:

  with the condition:    start rc=0, Result=success, ConditionResult=no,
                         journal "skipped, unmet condition check"
  condition removed by
  drop-in, nothing else: start rc=1, Result=exit-code, ExecMainStatus=64,
                         unit failed, "agent environment rejected: missing-file"

Canary red->green for the three commands, same v2 roster, side by side:

  fleet ps               0.0.50-next.2413 rc=1  ->  branch rc=0 (3 agents listed)
  fleet install          0.0.50-next.2413 rc=1  ->  branch rc=0
  fleet remove <name>    0.0.50-next.2413 rc=1  ->  branch rc=1, refusal naming
                                                    delete + apply

All three previously failed with "Fleet roster has unknown field(s):
generation." The #791 negative was measured too: the six existing
*.env.generated files were untouched by `install` (mtimes 20+ minutes older
than the run).

Gates: typecheck 0, eslint 0, prettier clean, fleet specs 382 passed, new spec
10/10 with the fix and 9/10 red against origin/next (the 10th passes there for
an unrelated reason and is annotated as such). Full suite: only
mutator-gate.acceptance.spec.ts fails, pre-existing on origin/next.

Still true and still worth saying: a correct fix here shows install rc=0 and
start rc=0 and STILL no live seat. #1240 (tmux absent) is upstream, #1241
(start reports lifecycle-complete over dead panes) and the missing agent
runtime are downstream.

Refs #1237
Reviewed-by: scooby (by git comms; cannot file a Gitea review from fomo-lin)
2026-08-15 23:34:35 -05:00
fred 463745e314 fix(#1237): let ps/install work on a roster-v2 fleet, and refuse add/remove honestly
On a roster-v2 fleet, `ps`, `install`, `install-systemd`, `add` and `remove`
all failed in the v1 parser. The consequence was that a greenfield v2 box could
never get its unit templates placed, so nothing downstream could start.

The read-only commands get a narrow version-agnostic view of the roster
(version, socket name, holder session, and per agent name/alias/runtime).
This is deliberately not a v2 -> v1 downshift. A downshifted FleetRoster would
be accepted by generateAgentEnvValues, which would make a third writer of
fleet/agents/<name>.env.generated through the v1 mapping and break the #791
single-SSOT invariant that projectRosterV2AgentGeneratedEnv is documented to
hold. The view is too small to write a roster or an env file back from, so that
misuse is unavailable rather than merely discouraged.

So on a v2 roster `install` places the tool files and the unit templates,
enables the units, and writes no generated env at all. Env belongs to `apply`
and `regen`, both already v2-native.

That change alone would have traded an init-time failure for a boot-time one.
`install` enables mosaic-agent@<name>.service (WantedBy=default.target) without
starting it, so a reboot between `install` and the first `apply` would run
ExecStart against an absent env file and fail every seat unit, further from its
cause. The unit template now carries

  ConditionPathExists=%h/.config/mosaic/fleet/agents/%i.env.generated

which skips an enabled-but-unconfigured unit cleanly and starts it on the next
start once the reconciler has written env. On v1 it is a no-op, since v1
`install` writes env itself. Found in review by scooby.

`add` and `remove` are not routed to `create` and `delete`. They are different
operations: the v1 pair edits the roster and drives systemd, the v2 pair is
documented as changing desired state without runtime actions. `add` also
collects four fields where a v2 agent requires eleven, so routing it would mean
inventing an operator's provider, alias, reasoning and tool policy. On v2 both
now fail with the real two-step sequence instead.

Tests: 8 new, 7 of which are red before this change. Includes the greenfield
case scooby asked for — `ps` on a fresh v2 install with nothing running is rc=0
and lists every agent stopped, since that is the command an operator runs to
find out why there is no seat.

Note for anyone verifying this: a correct fix here shows `install` rc=0 and
`start` rc=0 and still no live seat. #1240 (tmux absent) is upstream, #1241
(start reports lifecycle-complete over dead panes) and the missing agent
runtime are downstream. A dead pane after this change is not a regression here.

Refs #1237, #791, #1240, #1241
2026-08-15 23:24:24 -05:00
fredandClaude Opus 5 03eda02c20 fix(installer): warn on a failed credentials/ chmod instead of swallowing it
ci/woodpecker/pr/ci Pipeline was successful
scooby's review flag 1 on #1242. The other three chmods warn; this one was
`|| true`. It is the one directory holding secrets, so a chmod that fails
silently there is the failure most worth a line in the output.

Comment-and-warn only. No behaviour change on the success path.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WYgWocp36goy8hj2ui6ps1
2026-08-15 22:40:47 -05:00
fredandClaude Opus 5 3b4055017e fix(installer): pin umask and set the 0700 modes the fleet boundary requires (#1236)
ci/woodpecker/pr/ci Pipeline was canceled
A greenfield install cannot run `mosaic fleet init --write`. It fails with
`unsafe-permissions` on an unnamed `(directory)` and an unhandled Node throw,
and every mutating `mosaic fleet` command fails the same way. Measured on a
reverted-to-greenfield sandbox VM at CLI 0.0.50-next.2413: `~/.config/mosaic`,
`fleet/` and `credentials/` all land at 0775, and 1735 directories under the
framework root carry `mode & 022`.

Two independent causes, and fixing either one alone leaves it broken.

1. The installer inherited the caller's umask. Debian/Ubuntu ship 002, so every
   `mkdir -p` produced 0775. Fedora/RHEL ship 022 and produced 0755. The
   product therefore worked or did not depending on the operator's login shell,
   with nothing in the install output distinguishing the two. 022 is already
   what this script assumes it produces — `make_durable_snapshot` restores the
   ambient umask specifically so "every later sync copy and new framework dir"
   gets 0644/0755 — so pin it rather than inherit it.

2. Even at a correct 0755, three directories are rejected. The fleet code
   guards its managed paths with two masks in two languages:
   `assertPrivateManagedDirectory` (fleet-reconciler.js, `mode & 0o077`) covers
   MOSAIC_HOME and `fleet/` and runs before the roster lock is taken;
   `assert_private_directory` (tools/fleet/start-agent-session.sh, `mode & 077`)
   covers `fleet/agents` and runs before a pane is spawned. Their laxer
   siblings use `mode & 0o022` and accept 0755. The strict mask wins, so the
   installer states 0700 outright instead of hoping a umask implies it.

The `find -perm /022 -exec chmod go-w` sweep repairs a tree installed before
this change, which the umask alone cannot reach. It strips group/other WRITE
only — never read or execute — and is scoped to directories, so it corrects the
boundary violation without changing who may traverse or read anything. It is
not sufficient for `fleet/agents`: stripping write from 0755 yields 0750 and
`mode & 077` is still non-zero, which is why that path gets its own chmod.

Reported as #1236. The `fleet/agents` half was found by scooby reading
start-agent-session.sh; the umask framing is theirs too — my first report
blamed the distro rather than the umask.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WYgWocp36goy8hj2ui6ps1
2026-08-15 22:29:48 -05:00
coder2andMos 7102ccb93e docs(tools): index pull request edit wrapper (#1200)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: coder2 <[email protected]>
2026-08-13 14:50:38 +00:00
Mos afdaa6d0e6 framework: make tool discoverability, workspace placement and model tiering mechanical (#1174)
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/publish Pipeline was successful
2026-08-13 14:21:22 +00:00
coder3andMos 41749bbd33 fix(framework): detect installed tool drift (#1194) (#1195)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: coder3 <[email protected]>
2026-08-13 10:43:11 +00:00
Mos 120af4e193 feat(git-tools): add pull request edit wrapper (#1080) (#1173)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-13 06:28:01 +00:00
mos-dt-0 ec260e678f Merge pull request 'fix(git): accept http/https as one scheme class in comment URL verification (#991)' (#1022) from fix/991-comment-url-scheme-normalise into main
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-12 01:11:09 +00:00
Jason Woltje b590a5c3d8 fix(git): accept http/https as one scheme class in comment URL verification (#991)
ci/woodpecker/pr/ci Pipeline was successful
issue-comment.sh and pr-review.sh verify a durable write by pinning the
provider-returned object URL's origin and full path. The origin included the
SCHEME verbatim. On a Gitea whose ROOT_URL is configured `http://` while every
client reaches it over `https://`, the provider returns `http://` object URLs,
so the comparison rejects the provider's own truthful answer about a write that
LANDED. The failure is deterministic, not intermittent: every comment, every
time, on such a deployment.

The scheme was never what the check defends. The forgeries it exists to catch —
look-alike host, decoy path prefix, wrong owner/repo/kind/number — all vary the
HOST or the PATH. Both stay strict. `http` and `https` now collapse to one
scheme class; any other scheme (file:, ftp:, javascript:) stays distinguishing,
and an EXPLICIT non-default port still distinguishes, because a different port
is a different service on the same host.

Consequences of the bug, both observed:

- The wrapper reports failure on a comment that is durably on the issue/PR, and
  attributes it to #865 ("no durable comment created"). The write landed; the
  citation is wrong. Reproduced here: the harness's persisted state contains the
  record while the wrapper exits 1.
- pr-review.sh's comment path is worse. On a host where no seat can create a
  review OBJECT, comment-form is the only gate-16 review record obtainable, and
  this check refuses all of it.

Test gap this closes: every URL fixture in both harnesses was `https://`, and
every negative case varied only host or path. The one axis that fails in
production had zero coverage — the fixtures encoded the assumption that breaks.
Added, in both suites:

- scheme-downgrade (http vs https, otherwise correct) — must be ACCEPTED. Fails
  against the unmodified wrappers, passes against the fixed ones; verified in
  both directions, and the negative control's captured output is the #865
  misattribution above.
- explicit non-default port (`:8443`) — must stay REJECTED.
- non-web scheme (`ftp://`) — must stay REJECTED.

Also fixes test-issue-comment-readback.sh hermeticity (#1007), without which the
suite cannot run on any seat that has a per-agent Gitea token: detect-platform's
step-0 identity lookup reads ~/.config/mosaic/gitea-tokens/<identity>, outside
both XDG_CONFIG_HOME and MOSAIC_CREDENTIALS_FILE, so the suite resolved a
PRODUCTION credential and died at HTTP 401 before case 1. Same two-part fix
already merged for test-pr-review-gitea-comment.sh in #1006: a sandboxed HOME
plus an empty REPO-LOCAL mosaic.gitIdentity to shadow the global. Note the
env-var route does NOT work — detect-platform.sh reads `${MOSAIC_GIT_IDENTITY:-}`
and `:-` treats set-but-empty identically to unset.

The owner-side half of #991 (setting the deployment's Gitea ROOT_URL to https)
is not in scope here and is not made unnecessary by this change; this makes the
wrappers correct against a deployment that returns either scheme.
2026-08-11 19:00:31 -05:00
mos-dt-0 540ec5b6ef Merge pull request 'fix(git): #1007 suite hermeticity — pin repo-local mosaic.gitIdentity in five test suites' (#1024) from fix/1007-suite-hermeticity into main
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline failed
2026-08-11 23:19:49 +00:00
mos-dt-0 563d1ac053 Merge pull request 'fix(shell): remove wake validation pipe hazards' (#1107) from fix/1099-pipefail-wake into main
ci/woodpecker/push/publish Pipeline was canceled
ci/woodpecker/push/ci Pipeline was canceled
2026-08-11 23:19:45 +00:00
Mos 4d8ddb9a0a fix: quote SKILL.md descriptions containing colons (silent skill-load failure) (#3) 2026-08-11 22:53:47 +00:00
mos-dt-0 722163671f feat(pi): add persistent Mosaic /goal controller (#1152)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-10 22:54:43 +00:00
f10-coder f158be8003 fix(shell): remove wake validation pipe hazards
ci/woodpecker/pr/ci Pipeline was successful
2026-08-07 06:13:47 -05:00
f10-coderandMos b0f7d26dd9 fix(shell): remove test harness pipe hazards (#1106)
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/manual/ci-image Pipeline failed
ci/woodpecker/manual/publish Pipeline failed
ci/woodpecker/manual/ci Pipeline was successful
Co-authored-by: f10-coder <[email protected]>
2026-08-07 11:12:17 +00:00
f10-coderandMos 3a1203b2f8 fix(shell): remove runtime early-exit pipe hazards (#1105)
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: f10-coder <[email protected]>
2026-08-07 09:38:37 +00:00
be-coder-08 df4c591ab4 fix(fleet): make framework shell assertions SIGPIPE-safe (#1100)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-07 08:21:20 +00:00
be-coder-06andMos 4fa2768962 fix(fleet): propagate roster git identity (#1073)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: be-coder-06 <[email protected]>
2026-08-07 07:07:45 +00:00
Mos aa0a7b5fa2 fix(tools/git): issue-close.sh silently dropped the closing comment (#1085)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-07 05:59:12 +00:00
Mos 42ac19af48 test(gateway): size the enrollment clamp tolerance to CI jitter, not to a fast machine (closes #1090) (#1094)
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/ci Pipeline was successful
2026-08-07 05:33:38 +00:00
Mos f744f32214 feat(tools/git): explain tea's misleading user does not exist error (stale token, not a missing account) (#1086)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-07 05:07:40 +00:00
Mos 8ff7aac0ca fix(tools/git): detect-platform died silently outside a repo, taking every wrapper with it (#1089)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-08-07 04:26:36 +00:00
be-coder-08andMos 80a45b1e1c feat(pr-merge): preserve linked authors in squash messages (#1066)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: be-coder-08 <[email protected]>
2026-08-06 05:36:59 +00:00
be-coder-08andMos 85d2108e4e fix(ci): remove upgrade rollback signal race (#1060)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: be-coder-08 <[email protected]>
2026-08-05 22:14:15 +00:00
be-coder-08andMos 16f91157a1 test(ci): make queue guard harness deterministic (#1062)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: be-coder-08 <[email protected]>
2026-08-05 21:49:44 +00:00
Jason Woltje 2fa6bcd576 fix(git): #1007 — test-issue-comment-readback is a FIFTH affected suite (second census correction)
ci/woodpecker/pr/ci Pipeline was successful
My previous commit said four. It is five. `test-issue-comment-readback.sh` has
the same defect and is fixed the same way, and I had already looked straight at
it and filed it as an *unrelated* silent failure. Correcting that here rather
than folding it in quietly.

WHY IT WAS MISSED — the general lesson, not the excuse. `run_comment()` sends
the wrapper's stdout AND stderr to `$OUTPUT_FILE`, and the `EXIT` trap deletes
`$WORK_DIR`. The suite therefore exits 1 with ZERO bytes on stdout and stderr,
and the one line that says what went wrong —

    Error: Gitea authenticated-identity read failed with HTTP 401

— lives only inside a directory that no longer exists when anyone looks. Every
oracle I had swept the family with greps for a SYMPTOM in surviving output, so
against this suite all of them returned "nothing found", which I read as "clean"
in the first sweep and as "unrelated pre-existing failure" in the second. A
suite that discards or deletes its own evidence converts a post-hoc assay into a
non-measurement, and I wrote that sentence into the previous commit while it was
already false about a file in the same directory.

HOW IT WAS ACTUALLY FOUND. Intercept the identity read at its SOURCE instead of
grepping for its consequence: a PATH shim over `git` that logs every
`mosaic.gitIdentity` read — args, rc, and resolved value — to a file OUTSIDE any
suite's work dir, then execs the real git. Deletion-proof by construction, and
it measures the defect's cause rather than one of its symptoms. Sweeping all 16
suites with it under an ordinary invocation:

  resolves a REAL identity (`mos-dt-0`) before the fix:
    test-issue-comment-readback          1 read   rc=1 (RED on every seat)
    test-pr-review-repo-host-override    6 reads  rc=0
    test-ci-queue-wait-branch-absent     3 reads  rc=0
  the four fixed in the previous commit now read empty; the rest never read at all.

The latter two are NOT affected and are deliberately left alone: under a seat
replica (identity set, no per-slot token) neither reaches `get_gitea_token`'s
fail-loud branch, and under a canary HOME neither carries the canary credential
into any surviving artifact. They read the identity and never enter a credential
path. That residual is structural and belongs to the wrapper half of #1007 —
scoping the read with `git -C "$repo"` removes it for everyone at once.

An earlier version of that sweep reported the four fixed suites as still
resolving a real identity. That was my grep, not the suites: `value=\[..*\]` is
satisfied by `value=[] args=[…]`, because `.*` runs past the empty pair and
matches the closing bracket of the NEXT one. `value=\[[^]]` is the correct test.
Recorded because the wrong pattern failed in the direction that would have sent
me re-fixing four already-correct files.

VERIFICATION of this suite, four HOME arms, all rc=0 with zero non-empty
identity reads and the pass line on stdout: real HOME, seat replica, canary
HOME, and an empty HOME with no identity at all. Full 16-suite sweep after the
change: every suite rc=0.

CONSEQUENCE FOR THE FINDING LIST IN THE PREVIOUS COMMIT: item 2 there — the
"silently red, unrelated to #1007" suite — is withdrawn. It was #1007 all along.
Item 1 (`pr-metadata.sh:89-92`, the anonymous fallback that reports an HTTP 200
carrying valid JSON as "unknown API error") stands and is still unfixed here.

Refs #1007
2026-07-31 07:27:10 -05:00
Jason Woltje 1afe2b36dc fix(git): #1007 suite hermeticity — pin repo-local mosaic.gitIdentity in four test suites
CENSUS CORRECTION: FOUR suites, not the three my own #1007 audit named. The
fourth (test-pr-metadata-gitea.sh) was outside the candidate set that audit
worked from and was found only by sweeping the discriminator across all 16
tools/git/test-*.sh suites. Recording that as a correction to my finding, not
as part of the original claim.

THE DEFECT. get_gitea_token() (detect-platform.sh:502-599) resolves a per-agent
identity at STEP 0, from `git config --get mosaic.gitIdentity`, BEFORE both the
Mosaic credential loader (step 1) and the GITEA_TOKEN env check (step 2). On a
provisioned agent seat that value is set GLOBALLY in ~/.gitconfig and is
inherited by any freshly-`git init`ed repo, so step 0 reads a REAL per-slot
token out of $HOME and returns it without ever consulting the suite's own
MOSAIC_CREDENTIALS_FILE / GITEA_TOKEN fixtures. The suites were running against
production credentials, and the fixture credential each one carefully
constructs was inert.

THE FIX: an empty repo-local `mosaic.gitIdentity`. An empty local value shadows
the global one and reads back empty at rc=0, so step 0 declines. The env route
does NOT work: detect-platform.sh reads "${MOSAIC_GIT_IDENTITY:-}", and `:-`
treats set-but-empty identically to unset.

OPERATIVE vs CONTAINMENT — the two mechanisms are not interchangeable and the
comment in each suite says so. The pin is operative: it prevents the resolution.
The sandboxed HOME each suite now also gets is containment: it bounds a failure
the pin should already have prevented. Conflating them is how this class stays
invisible, because a decoy HOME REMOVES the trigger (~/.gitconfig is where the
global identity lives), so any suite audited under one reads clean however
vulnerable it is. To MEASURE, replicate a seat: a decoy HOME whose .gitconfig
sets mosaic.gitIdentity with no per-slot token, so step 0 reaches its fail-loud
branch. That note is in each file for the next auditor.

SECOND, INDEPENDENT DEFECT in test-pr-metadata-gitea.sh. Applying the pin alone
turned that suite RED — and a control at baseline 826a8b3 under a plain HOME
reproduced the same failure, so it is pre-existing, not introduced. Its
`GITEA_TOKEN="stub-token"` / `GITEA_URL="https://git.example.test"` pair can
never satisfy step 2, because step 2 accepts GITEA_TOKEN only when GITEA_URL
matches the remote host and this repo's origin is git.uscllc.com. The suite had
therefore only ever passed by resolving a REAL credential — step 0 on a seat, or
step 1 from the operator's own credentials.json. A MOSAIC_CREDENTIALS_FILE
fixture is added rather than leaning on the sandboxed HOME making step 1 find
nothing: a test that passes because production configuration is ABSENT fails the
moment it is present. Shipping the pin without this would have moved the failure
rather than removed it.

NO CI ARM. .woodpecker/ci.yml does not run these suites; packages/mosaic/
package.json:28 (test:framework-shell) runs an ENUMERATED list that excludes all
four. They run only by hand — i.e. exclusively on a provisioned seat, the one
environment where the defect is live. "Passes in CI, fails on a seat" does not
apply here; there is no CI observation at all.

VERIFICATION (seat replica = decoy HOME with mosaic.gitIdentity set, no per-slot
token; canary = same plus a marked non-credential at both per-slot paths; plain
= empty HOME; real = ordinary invocation):
  - bash -n clean on all four.
  - Sweep of all 16 suites at baseline 826a8b3 under the seat replica:
    test-gitea-login-resolution rc=1 REACHES-STEP0; test-issue-create-
    interactive-auth rc=1 REACHES-STEP0; test-pr-merge-gitea-empty-uid rc=1
    REACHES-STEP0; test-pr-metadata-gitea rc=1 REACHES-STEP0.
  - Same sweep after: every row rc=0 with step0 absent.
  - test-gitea-token-identity flags REACHES-STEP0 in BOTH arms and is NOT a
    defect: it runs under `env -i HOME="$FAKE_HOME"` (line 77) and its hit is
    its own deliberate assert_failloud fixtures (lines 158-171). The fail-loud
    grep matches the intended behaviour as well as the defect, so it needs the
    second discriminator; recorded here so the next sweep does not re-file it.
  - Durable-argv assay (a PATH shim that tees argv out of each suite's own mock
    curl, because test-pr-merge-gitea-empty-uid truncates its log between phases
    and its EXIT trap removes the sandbox — a post-hoc read of that suite is a
    non-measurement, and "no trace" there is not a clearance):
      test-pr-merge-gitea-empty-uid  before: canary token in argv, fixture never
        used. after: fixture token in argv, canary absent. 5 curl calls both arms.
      test-pr-metadata-gitea         before: canary in argv. after: both calls
        carry the fixture token against git.uscllc.com.
  - test-pr-metadata-gitea across seat/canary/plain HOMEs after the fix: rc=0,
    rc=0, rc=0.
  - All four under the real HOME: rc=0. No regression to ordinary invocation.

The comment block is duplicated across the four files rather than pointing at a
shared note. Deliberate, and matching the merged #1006 precedent
(test-pr-review-gitea-comment.sh:87-95): the reader who needs it is auditing one
file.

TWO FINDINGS DELIBERATELY NOT FIXED HERE (out of this branch's scope, to be
filed):
  1. pr-metadata.sh:89-92 — the anonymous curl fallback does not check ^2, so an
     HTTP 200 carrying valid JSON is reported as "unknown API error" at rc=1.
  2. test-issue-comment-readback.sh exits 1 with ZERO bytes on stdout AND
     stderr, dying at its first seed_state python3 heredoc. Reproduces at
     baseline 826a8b3 under both a seat replica and the real HOME. Silently red
     at main for everyone; unrelated to #1007.

Refs #1007
2026-07-31 07:13:13 -05:00
jason.woltje 63e77887a8 feat: add mosaic-tools skill (fleet toolkit fast path) (#1) 2026-06-19 18:30:58 +00:00
Jarvis 809ca9a1d9 feat: add mosaic ops skills (portainer, gitea, woodpecker, deploy, orchestrator)
- mosaic-portainer: stack list/status/redeploy/logs via Portainer API scripts
- mosaic-gitea: PR/issue/milestone ops for git.mosaicstack.dev
- mosaic-woodpecker: pipeline status, trigger, CI wait
- mosaic-deploy: full end-to-end deploy flow (push → CI → merge → redeploy)
- mosaic-orchestrator: mission init/run/status + worker launch rules
2026-03-22 15:32:05 +00:00
Jason Woltje 57435bb879 switch skill docs to xdg mosaic config path 2026-02-17 14:12:07 -06:00
Jason Woltje 8c51bf7575 remove legacy nested skill link artifacts 2026-02-17 14:08:02 -06:00
Jason Woltje c3d2179ad8 add delegation mode fallback to matrix rail in kickstart 2026-02-17 14:05:43 -06:00
Jason Woltje b47c4024cc standardize skills to mosaic-first paths and docs 2026-02-17 13:09:06 -06:00
Jason Woltje 74d1cdc7c1 migrate kickstart skill to mosaic-first paths 2026-02-17 13:01:49 -06:00
Jason WoltjeandClaude Opus 4.6 8cae9e0883 feat: Add lint skill (zero-tolerance) + strengthen kickstart linting mandate
New skill: lint — zero-tolerance linting enforcement for all code changes.
Detects project linter, fixes ALL violations, never disables rules.

Updated kickstart: linting now explicit standing order #3 in worker template
with "NON-NEGOTIABLE" language and zero-tolerance enforcement.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 17:13:00 -06:00
Jason WoltjeandClaude Opus 4.6 2c524b6da2 feat: Add kickstart skill — orchestrator launcher via /kickstart command
/kickstart [milestone|issue|task] replaces manual orchestrator boilerplate.
Auto-discovers project context, fetches issues from Gitea/GitHub, bootstraps
tracking files, and transforms the session into an orchestrator.

Modes: milestone, issue, task ID, resume, interactive (no args)
Built-in: quality gates, Two-Phase Completion, context handoff protocol

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:59:04 -06:00
Jason WoltjeandClaude Opus 4.6 b4f2019529 security: Remove vercel-deploy (data exfiltration), annotate LD_PRELOAD shims
Security audit findings:
- CRITICAL: vercel-deploy uploaded entire project to external endpoint — REMOVED
- ANNOTATED: docx/pptx/xlsx soffice.py LD_PRELOAD shims — security warnings added
- README updated to 93 skills with full security audit section and Vue/Vite ecosystem

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:39:04 -06:00
Jason WoltjeandClaude Opus 4.6 b1eb1fb2f9 feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:27:42 -06:00
Jason WoltjeandClaude Opus 4.6 dfeb4d9692 feat: Expand fleet to 23 skills across all domains
New skills (14):
- nestjs-best-practices: 40 priority-ranked rules (kadajett)
- fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb)
- architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson)
- python-performance-optimization: Profiling and optimization (wshobson)
- ai-sdk: Vercel AI SDK streaming and agent patterns (vercel)
- create-agent: Modular agent architecture with OpenRouter (openrouterteam)
- proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster)
- brand-guidelines: Brand identity enforcement (anthropics)
- ui-animation: Motion design with accessibility (mblode)
- marketing-ideas: 139 ideas across 14 categories (coreyhaines31)
- pricing-strategy: SaaS pricing and tier design (coreyhaines31)
- programmatic-seo: SEO at scale with playbooks (coreyhaines31)
- competitor-alternatives: Comparison page architecture (coreyhaines31)
- referral-program: Referral and affiliate programs (coreyhaines31)

README reorganized by domain: Code Quality, Frontend, Backend,
Auth, AI/Agent Building, Marketing, Design, Meta.

Mosaic Stack is not limited to coding — the Orchestrator serves
coding, business, design, marketing, writing, logistics, and analysis.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:22:53 -06:00
Jason WoltjeandClaude Opus 4.6 7ea13332ed feat: Add 5 curated skills for Mosaic Stack
New skills:
- next-best-practices: Next.js 15+ RSC, async patterns, self-hosting (vercel-labs)
- better-auth-best-practices: Official Better-Auth with Drizzle adapter (better-auth)
- verification-before-completion: Evidence-based completion claims (obra/superpowers)
- shadcn-ui: Component patterns with Tailwind v4 adaptation note (developer-kit)
- writing-skills: TDD methodology for skill authoring (obra/superpowers)

README reorganized by category with Mosaic Stack alignment section.
Total: 9 skills (4 existing + 5 new).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:17:40 -06:00
Jason WoltjeandClaude Opus 4.6 b032d23889 docs: Add npx install commands and clone instructions
- Add npx skills add commands for single, all, and non-interactive install
- Document .git suffix requirement for Gitea-hosted repos
- Add git clone step to manual installation
- Use ln -sf for idempotent symlinks

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:09:05 -06:00
Jason WoltjeandClaude Opus 4.6 ecde74439c feat: Initial agent-skills repo — 4 adapted skills for Mosaic Stack
Skills included:
- pr-reviewer: Adapted for Gitea/GitHub via platform-aware scripts
  (dropped fetch_pr_data.py and add_inline_comment.py, kept generate_review_files.py)
- code-review-excellence: Methodology and checklists (React, TS, Python, etc.)
- vercel-react-best-practices: 57 rules for React/Next.js performance
- tailwind-design-system: Tailwind CSS v4 patterns, CVA, design tokens

New shell scripts added to ~/.claude/scripts/git/:
- pr-diff.sh: Get PR diff (GitHub gh / Gitea API)
- pr-metadata.sh: Get PR metadata as normalized JSON

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 16:03:39 -06:00
14116 changed files with 2149775 additions and 31211 deletions
+20 -149
View File
@@ -1,153 +1,24 @@
# ─────────────────────────────────────────────────────────────────────────────
# Mosaic — Environment Variables Reference
# Copy this file to .env and fill in the values for your deployment.
# Lines beginning with # are comments; optional vars are commented out.
# ─────────────────────────────────────────────────────────────────────────────
# Non-secret runtime settings for the mosaic-poc-agent container.
# Copy to .env if you want to override the defaults in compose.yaml.
#
# NEVER put credentials in this file. Authentication is supplied at
# runtime only, via one of the two documented paths:
# 1. read-only mounted pi auth file (default: ~/.pi/agent/auth.json,
# override the host path with PI_AUTH_FILE)
# 2. provider API key environment variable (ZAI_API_KEY or
# ANTHROPIC_API_KEY), passed through by compose.yaml when set
# Model provider (built-in pi provider name)
PI_PROVIDER=zai
# ─── Database (PostgreSQL 17 + pgvector) ─────────────────────────────────────
# Full connection string used by the gateway, ORM, and migration runner.
# Port 5433 avoids conflict with a host-side PostgreSQL instance.
DATABASE_URL=postgresql://mosaic:mosaic@localhost:5433/mosaic
# Model ID within the provider
PI_MODEL=glm-5.3-flash
# Docker Compose host-port override for the PostgreSQL container (default: 5433)
# PG_HOST_PORT=5433
# Optional: alternative host path of the pi credential file mounted
# read-only at /home/node/.pi/agent/auth.json in the container
#PI_AUTH_FILE=/home/jwoltje/.pi/agent/auth.json
# ─── Queue (Valkey 8 / Redis-compatible) ─────────────────────────────────────
# Port 6380 avoids conflict with a host-side Redis/Valkey instance.
VALKEY_URL=redis://localhost:6380
# Docker Compose host-port override for the Valkey container (default: 6380)
# VALKEY_HOST_PORT=6380
# ─── Gateway ─────────────────────────────────────────────────────────────────
# TCP port the NestJS/Fastify gateway listens on (default: 14242)
GATEWAY_PORT=14242
# Comma-separated list of allowed CORS origins.
# Must include the web app origin in production.
GATEWAY_CORS_ORIGIN=http://localhost:3000
# ─── Auth (BetterAuth) ───────────────────────────────────────────────────────
# REQUIRED — random secret used to sign sessions and tokens.
# Generate with: openssl rand -base64 32
BETTER_AUTH_SECRET=change-me-to-a-random-32-char-string
# Public base URL of the gateway (used by BetterAuth for callback URLs)
BETTER_AUTH_URL=http://localhost:14242
# ─── Web App (Next.js) ───────────────────────────────────────────────────────
# Public gateway URL — accessible from the browser, not just the server.
NEXT_PUBLIC_GATEWAY_URL=http://localhost:14242
# ─── OpenTelemetry ───────────────────────────────────────────────────────────
# OTLP HTTP endpoint (otel-collector or any OpenTelemetry-compatible backend)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# Service name shown in traces
OTEL_SERVICE_NAME=mosaic-gateway
# ─── AI Providers ────────────────────────────────────────────────────────────
# Ollama (local models — set OLLAMA_BASE_URL to enable)
# OLLAMA_BASE_URL=http://localhost:11434
# OLLAMA_HOST is a legacy alias for OLLAMA_BASE_URL
# OLLAMA_HOST=http://localhost:11434
# Comma-separated list of Ollama model IDs to register (default: llama3.2,codellama,mistral)
# OLLAMA_MODELS=llama3.2,codellama,mistral
# Anthropic (claude-sonnet-4-6, claude-opus-4-6, claude-haiku-4-5)
# ANTHROPIC_API_KEY=sk-ant-...
# OpenAI (gpt-4o, gpt-4o-mini, o3-mini)
# OPENAI_API_KEY=sk-...
# Z.ai / GLM (glm-4.5, glm-4.5-air, glm-4.5-flash)
# ZAI_API_KEY=...
# Custom providers — JSON array of provider configs
# Format: [{"id":"<id>","baseUrl":"<url>","apiKey":"<key>","models":[{"id":"<model-id>","name":"<label>"}]}]
# MOSAIC_CUSTOM_PROVIDERS=
# ─── Embedding Service ───────────────────────────────────────────────────────
# OpenAI-compatible embeddings endpoint (default: OpenAI)
# EMBEDDING_API_URL=https://api.openai.com/v1
# EMBEDDING_MODEL=text-embedding-3-small
# ─── Log Summarization Service ───────────────────────────────────────────────
# OpenAI-compatible chat completions endpoint for log summarization (default: OpenAI)
# SUMMARIZATION_API_URL=https://api.openai.com/v1
# SUMMARIZATION_MODEL=gpt-4o-mini
# Cron schedule for summarization job (default: every 6 hours)
# SUMMARIZATION_CRON=0 */6 * * *
# Cron schedule for log tier management (default: daily at 03:00)
# TIER_MANAGEMENT_CRON=0 3 * * *
# ─── Agent ───────────────────────────────────────────────────────────────────
# Filesystem sandbox root for agent file tools (default: process.cwd())
# AGENT_FILE_SANDBOX_DIR=/var/lib/mosaic/sandbox
# Comma-separated list of tool names available to non-admin users.
# Leave unset to allow all tools for all authenticated users.
# AGENT_USER_TOOLS=read_file,list_directory,search_files
# System prompt injected into every agent session (optional)
# AGENT_SYSTEM_PROMPT=You are a helpful assistant.
# ─── MCP Servers ─────────────────────────────────────────────────────────────
# JSON array of MCP server configs — set to enable MCP tool integration.
# Each entry: {"name":"<id>","url":"<http-or-sse-url>"}
# MCP_SERVERS=[{"name":"my-mcp","url":"http://localhost:3100/sse"}]
# ─── Coordinator ─────────────────────────────────────────────────────────────
# Root directory used to scope coordinator (worktree/repo) operations.
# Defaults to the monorepo root auto-detected from process.cwd().
# MOSAIC_WORKSPACE_ROOT=/home/user/projects/mosaic
# ─── Discord Plugin (optional — set DISCORD_BOT_TOKEN to enable) ─────────────
# DISCORD_BOT_TOKEN=
# DISCORD_GUILD_ID=
# DISCORD_GATEWAY_URL=http://localhost:14242
# ─── Telegram Plugin (optional — set TELEGRAM_BOT_TOKEN to enable) ───────────
# TELEGRAM_BOT_TOKEN=
# TELEGRAM_GATEWAY_URL=http://localhost:14242
# ─── SSO Providers (add credentials to enable) ───────────────────────────────
# --- Authentik (optional — set AUTHENTIK_CLIENT_ID to enable) ---
# AUTHENTIK_ISSUER=https://auth.example.com/application/o/mosaic/
# AUTHENTIK_CLIENT_ID=
# AUTHENTIK_CLIENT_SECRET=
# --- WorkOS (optional — set WORKOS_CLIENT_ID to enable) ---
# WORKOS_ISSUER=https://your-company.authkit.app
# WORKOS_CLIENT_ID=client_...
# WORKOS_CLIENT_SECRET=sk_live_...
# --- Keycloak (optional — set KEYCLOAK_CLIENT_ID to enable) ---
# KEYCLOAK_ISSUER=https://auth.example.com/realms/master
# Legacy alternative if you prefer to compose the issuer from separate vars:
# KEYCLOAK_URL=https://auth.example.com
# KEYCLOAK_REALM=master
# KEYCLOAK_CLIENT_ID=mosaic
# KEYCLOAK_CLIENT_SECRET=
# The web login page discovers configured providers dynamically from
# GET /api/sso/providers. No NEXT_PUBLIC_* provider feature flag is required.
# Optional: documented env-var auth alternative (secret! set in your
# shell or a gitignored .env, never commit)
#ZAI_API_KEY=
#ANTHROPIC_API_KEY=
+6 -23
View File
@@ -1,25 +1,8 @@
logs/
node_modules
dist
.turbo
.next
coverage
# build/deps
node_modules/
# runtime credentials — never commit, never copy into the image
.env
.env.local
*.tsbuildinfo
.pnpm-store
__pycache__/
docs/.obsidian
secrets/
# Step-CA dev password — real file is gitignored; commit only the .example
infra/step-ca/dev-password
# Scratch dirs created by the framework git-wrapper shell test harnesses
.mosaic-test-work/
# Transient config files vite/vitest/esbuild write next to a *.config.ts while
# loading it, then unlink. They are untracked but were not ignored, so turbo's
# package traversal hashed them and intermittently failed CI with "Package
# traversal error: ... .timestamp-*.mjs: No such file or directory" when the
# file vanished mid-scan. Ignoring them removes the race.
*.timestamp-*.mjs
# generated runtime state lives in /home/jwoltje/.mosaic-dev (outside this project)
+12
View File
@@ -0,0 +1,12 @@
{
"projectVersion": 1,
"id": "stack",
"vars": {
"tracker.project": 19,
"git.workingBranch": "refactor",
"git.protectedBranches": [
"main",
"next"
]
}
}
+6
View File
@@ -0,0 +1,6 @@
extensions/
extensions.installed.sha256
.extensions-*
state/
evidence/
native-test-*.log
+34
View File
@@ -0,0 +1,34 @@
# Native goal development copy
From this repository, start a fresh native Pi session:
```sh
bash scripts/goal-dev.sh
```
Canonical source lives under `extensions/`. The launcher first runs `scripts/sync-dev-extensions.sh`, which installs verified ordinary-file copies under `.pi/extensions/`, then loads only the generated goal extension. Global extensions remain unloaded. The launcher keeps your usual native Pi provider authentication; it copies no credentials. Goal state and new conversation files live under `.pi/state/`, which is ignored by Git. Each process gets a fresh incarnation; `/reload` and `/new` in the same process retain its goal. Restarting Pi does not adopt an earlier process's active goal.
Plain `pi` also discovers `.pi/extensions/goal/index.ts` after project trust, but may load global extensions too. Use the launcher to avoid duplicate `/goal` registrations. This is a local development test, not a sandbox or the managed Mosaic runtime. Docker and `~/.mosaic` are unchanged.
## Try it
1. Set `/goal <a long goal with acceptance criteria>`. This starts work immediately.
2. Look below the editor for `Goal: Active`. The old above-editor goal widget is gone.
3. Run bare `/goal`, then press `Alt+G`. Both show the entire stored goal and its status. Tab remains autocomplete.
4. Use `/goal stop` and `/goal resume`. Expect Paused and Active, or Waiting if an untimed wait remains recorded.
5. A blocked `goal_report` displays Blocked. A satisfied report displays Complete and retains the full goal for recall without continuing work.
6. `/goal clear` removes the retained goal. Try `NO_COLOR=1 bash scripts/goal-dev.sh` to check text-only labels.
Use terminal scrollback for recall longer than the screen. At narrow widths Pi may truncate its footer status row; bare `/goal` and Alt+G remain available.
## Checks
```sh
node --test extensions/goal/test/*.test.ts
bash scripts/test-extension-package.sh
python3 scripts/test-goal-native.py
```
Contract tests use ordinary read-only fixture copies in `test/fixtures/skills-local/`, not live brain files. The executive-update fixture SHA-256 matches the parser's pinned contract, `bbea48a46b1f8da7bc759f86856fb52830b7dde456b826317163c6dc6ccab319`.
`SOURCE-SNAPSHOT.json` records the original external-source baseline, not the edited candidate. No symlinks are used. Never edit `.pi/extensions/`; the sync script refuses to overwrite installation drift. Make changes under `extensions/`, run the checks, and relaunch. To disable the test, stop its Pi process and remove `.pi/extensions/`. Keep `.pi/state/` only if you need local test state.
+10
View File
@@ -0,0 +1,10 @@
{
"snapshotVersion": 1,
"copiedAt": "2026-09-06T04:58:22Z",
"source": "~/.mosaic/fleet/extensions",
"goalTreeSha256": "8853f2b72dde3e87c4573648b9a931c1c75da87ccde995c3224e6d2e707a75f0",
"mosaicCoreLibTreeSha256": "d1194dce31209e5773c6cc5ce571cbca3c39b29d943a79dea06665e05d29f319",
"symlinks": false,
"autoDiscoveredExtensions": ["goal"],
"purpose": "Issue #54 native Pi NG development copy; never loaded by Docker"
}
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Compatibility entrypoint for the accepted native test command.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
exec scripts/goal-dev.sh "$@"
-151
View File
@@ -1,151 +0,0 @@
# &node_image is the pre-baked CI base built by .woodpecker/ci-image.yml:
# node:24-alpine + python3/make/g++/postgresql-client + pnpm + a warm pnpm
# store. The install step resolves from the baked store (--prefer-offline)
# instead of paying a ~731s cold fetch + native compile every run.
variables:
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
- &enable_pnpm 'corepack enable'
when:
# PR + manual CI run on any branch — the pull_request pipeline is the merge gate.
# push CI is restricted to protected branches (main) so a feature-branch push no
# longer fires a redundant SECOND pipeline alongside its PR pipeline. This ~halves
# CI load on the storage-constrained runner with zero loss of gating (branch
# protection requires no push/ci status context; main still gets full push CI).
- event: [pull_request, manual]
- event: push
branch: main
# Turbo remote cache (turbo.mosaicstack.dev) is configured via Woodpecker
# repository-level environment variables (TURBO_API, TURBO_TEAM, TURBO_TOKEN).
# This avoids from_secret which is blocked on pull_request events.
# If the env vars aren't set, turbo falls back to local cache only.
steps:
install:
image: *node_image
commands:
- corepack enable
# python3/make/g++ are baked into ci-base; --prefer-offline resolves from
# the baked pnpm store.
- pnpm install --frozen-lockfile --prefer-offline
# Blocking gate: public framework package must contain no operator-specific
# personal data or private $HOME defaults. Runs early (no node_modules needed).
sanitization:
image: *node_image
commands:
- apk add --no-cache bash
- bash packages/mosaic/framework/tools/quality/scripts/verify-sanitized.sh
# Resident line-count ceiling over framework-owned resident files
# (Constitution + dispatcher + each RUNTIME.md slice). See DESIGN §7 / R9.
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh --self-test
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh
# Test-membership guard (#1017): also first link of test:framework-shell.
# Invoked from BOTH surfaces it audits (F2, PR #1018) — the guard is link
# [0] of the pnpm chain, so severing that chain would silence it together
# with everything it guards; this direct line keeps one instrument running.
- bash packages/mosaic/framework/tools/quality/scripts/check-test-enumeration.sh
# Blocking gate (#791): a framework upgrade must never write or delete an
# operator-owned path. The HARD GATE proves an unanticipated operator sentinel
# survives a keep-mode reseed byte-identical (with rsync present AND absent —
# keep mode is a single cp-based path that must not depend on rsync), and that a
# corrupt/empty/missing manifest aborts fail-closed leaving operator files
# untouched (B2/B3). The rollback gate proves a mid-sync failure is rolled back
# from the pre-update snapshot (B1). The durable-snapshot gate (#791 PR2) proves
# the retained, operator-scoped pre-update backup is taken before any mutation
# (0700/0600, secret never logged, retention-pruned) and that the post-sync
# verify net restores any operator file a manifest bug lets the sync touch. The
# migration matrix pins the v2→v3 contract-file semantics. Pure bash, no
# node_modules — runs early alongside sanitization.
upgrade-guard:
image: *node_image
commands:
- apk add --no-cache bash rsync
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-manifest-guard.sh
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh
- bash packages/mosaic/framework/tools/quality/scripts/test-install-migration.sh
typecheck:
image: *node_image
commands:
- *enable_pnpm
- pnpm typecheck
depends_on:
- install
- sanitization
- upgrade-guard
# lint, format, and test are independent — run in parallel after typecheck
lint:
image: *node_image
commands:
- *enable_pnpm
- pnpm lint
depends_on:
- typecheck
format:
image: *node_image
commands:
- *enable_pnpm
- pnpm format:check
depends_on:
- typecheck
test:
image: *node_image
environment:
# Avoid the namespace-level Woodpecker DB service named "postgres".
# The Kubernetes backend exposes service containers by step name.
DATABASE_URL: postgresql://mosaic:mosaic@ci-postgres:5432/mosaic
commands:
- *enable_pnpm
# openssl (#912) is the wake HMAC signer: the digest H1/H2, beacon B12,
# and install I8 legs hard-require it in CI. It is baked into ci-base via
# Dockerfile.ci, but ci-base only rebuilds on push-to-main/tag — this
# `apk add` guarantees openssl is present on PR pipelines too (and is a
# fast no-op once the rebuilt image already ships it).
- apk add --no-cache openssl
# Pi runtime (Invariant R): invariant_r_unittest.py hard-requires an
# installed `pi` binary at exactly this measured version — the test
# boots Pi's real tool registry to prove the read-only carve-out
# resolves to real, unshadowed builtins, and fails loud (by design)
# when the runtime is absent or drifts. The canonical Pi is
# @earendil-works/[email protected] exactly (@mariozechner/* is
# embedded-legacy). Step-level install because ci-base image publishes
# are currently blocked on registry auth; fold into Dockerfile.ci once
# that is fixed, keeping this as a fast no-op guard.
- npm install -g @earendil-works/[email protected]
# postgresql-client (pg_isready) is baked into ci-base.
# Wait up to 60s for CI postgres to be ready; fail fast if it never comes up.
- |
ready=0
for i in $(seq 1 60); do
if pg_isready -h ci-postgres -p 5432 -U mosaic; then
ready=1
break
fi
echo "Waiting for ci-postgres ($i/60)..."
sleep 1
done
if [ "$ready" -ne 1 ]; then
echo "ci-postgres did not become ready" >&2
exit 1
fi
# Run migrations (DATABASE_URL is set in environment above)
- pnpm --filter @mosaicstack/db run db:migrate
# Run all tests
- pnpm test
depends_on:
- typecheck
services:
ci-postgres:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: mosaic
POSTGRES_PASSWORD: mosaic
POSTGRES_DB: mosaic
-296
View File
@@ -1,296 +0,0 @@
# Build, publish npm packages, and push Docker images
# Runs on main for stable publishes and on next for integration-line prereleases/images
variables:
# Pre-baked CI base (see .woodpecker/ci-image.yml): node:24-alpine +
# toolchain + warm pnpm store. Kills the second cold install publish pays.
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
- &enable_pnpm 'corepack enable'
# Heavy kaniko image builds (~25 min) — gate them so a merge that only touches
# the npm-only CLI (@mosaicstack/mosaic) or docs does NOT rebuild the platform
# images (gateway/appservice/web do not depend on @mosaicstack/mosaic). Releases
# (tags) always build everything. Exclude-list keeps the default SAFE: any
# non-excluded change still builds, so no transitive dep can silently go stale.
# (Woodpecker: `when` entries are OR'd; `path` applies to push/PR only — hence
# the separate `event: tag` entry.)
- &image_build_when
- event: tag
- event: [push, manual]
branch: main
path:
exclude:
- 'packages/mosaic/**'
- 'docs/**'
- '**/*.md'
- '.woodpecker/**'
- event: [push, manual]
branch: next
- &main_image_build_when
- event: tag
- event: [push, manual]
branch: main
path:
exclude:
- 'packages/mosaic/**'
- 'docs/**'
- '**/*.md'
- '.woodpecker/**'
when:
- branch: [main, next]
event: [push, manual, tag]
steps:
install:
image: *node_image
commands:
- corepack enable
# Resolve from the baked pnpm store instead of a cold network fetch.
- pnpm install --frozen-lockfile --prefer-offline
build:
image: *node_image
commands:
- *enable_pnpm
- pnpm build
depends_on:
- install
publish-npm:
image: *node_image
# Publish only when a publishable package changed (or on a release tag); a
# pure-docs merge runs no publish. Cheap step, but gated for cleanliness.
when:
- event: tag
- event: [push, manual]
branch: main
path:
include:
- 'packages/**'
environment:
NPM_TOKEN:
from_secret: gitea_token
commands:
- *enable_pnpm
# Configure auth for Gitea npm registry
- |
echo "//git.mosaicstack.dev/api/packages/mosaicstack/npm/:_authToken=$NPM_TOKEN" > ~/.npmrc
echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc
# Publish non-private packages to Gitea.
#
# The only publish failure we tolerate is "version already exists" —
# that legitimately happens when only some packages were bumped in
# the merge. Any other failure (registry 404, auth error, network
# error) MUST fail the pipeline loudly: the previous
# `|| echo "... continuing"` fallback silently hid a 404 from the
# Gitea org rename and caused every @mosaicstack/* publish to fall
# on the floor while CI still reported green.
- |
# Portable sh (Alpine ash) — avoid bashisms like PIPESTATUS.
set +e
pnpm --filter "@mosaicstack/*" --filter "!@mosaicstack/web" publish --no-git-checks --access public >/tmp/publish.log 2>&1
EXIT=$?
set -e
cat /tmp/publish.log
if [ "$EXIT" -eq 0 ]; then
echo "[publish] all packages published successfully"
exit 0
fi
# Hard registry / auth / network errors → fatal. Match npm's own
# error lines specifically to avoid false positives on arbitrary
# log text that happens to contain "E404" etc.
if grep -qE "npm (error|ERR!) code (E404|E401|ENEEDAUTH|ECONNREFUSED|ETIMEDOUT|ENOTFOUND)" /tmp/publish.log; then
echo "[publish] FATAL: registry/auth/network error detected — failing pipeline" >&2
exit 1
fi
# Only tolerate the explicit "version already published" case.
# npm returns this as E403 with body "You cannot publish over..."
# or EPUBLISHCONFLICT depending on version.
if grep -qE "EPUBLISHCONFLICT|You cannot publish over|previously published" /tmp/publish.log; then
echo "[publish] some packages already at this version — continuing (non-fatal)"
exit 0
fi
echo "[publish] FATAL: publish failed with unrecognized error — failing pipeline" >&2
exit 1
depends_on:
- build
publish-next-npm:
image: *node_image
# Durable @next integration-line publish. Runs only on next; never writes
# the latest dist-tag and never commits the computed prerelease versions.
when:
- event: [push, manual]
branch: next
environment:
NPM_TOKEN:
from_secret: gitea_token
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
CI_PIPELINE_NUMBER: ${CI_PIPELINE_NUMBER}
commands:
- *enable_pnpm
- |
if [ "$CI_COMMIT_BRANCH" != "next" ]; then
echo "[publish-next] FATAL: publish-next-npm may only run on next (got '$CI_COMMIT_BRANCH')" >&2
exit 1
fi
if [ -z "$CI_PIPELINE_NUMBER" ]; then
echo "[publish-next] FATAL: CI_PIPELINE_NUMBER is required for prerelease versioning" >&2
exit 1
fi
echo "//git.mosaicstack.dev/api/packages/mosaicstack/npm/:_authToken=$NPM_TOKEN" > ~/.npmrc
echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc
DIST_TAGS_JSON="$(npm view @mosaicstack/mosaic dist-tags --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/ --json)"
DIST_TAGS_JSON="$DIST_TAGS_JSON" node -e 'const tags = JSON.parse(process.env.DIST_TAGS_JSON || "{}"); if (!tags || typeof tags !== "object" || !Object.hasOwn(tags, "latest")) { throw new Error("Gitea npm registry did not return a usable dist-tags object"); } console.log("[publish-next] registry dist-tags OK: latest=" + tags.latest);'
node <<'NODE'
const fs = require('node:fs');
const path = require('node:path');
const pipelineNumber = process.env.CI_PIPELINE_NUMBER;
const roots = ['apps', 'packages', 'plugins'];
const updated = [];
function walk(dir) {
if (!fs.existsSync(dir)) return;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.turbo') continue;
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
const packagePath = path.join(fullPath, 'package.json');
if (fs.existsSync(packagePath)) updatePackage(packagePath);
walk(fullPath);
}
}
}
function updatePackage(packagePath) {
const manifest = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
if (!manifest.name?.startsWith('@mosaicstack/') || manifest.private) return;
const stableMatch = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(manifest.version);
if (!stableMatch) {
throw new Error(manifest.name + " has unsupported semver version '" + manifest.version + "'");
}
const [, major, minor, patch] = stableMatch;
const oldVersion = manifest.version;
manifest.version = major + '.' + minor + '.' + (Number(patch) + 1) + '-next.' + pipelineNumber;
fs.writeFileSync(packagePath, JSON.stringify(manifest, null, 2) + '\n');
updated.push(manifest.name + ' ' + oldVersion + ' -> ' + manifest.version);
}
for (const root of roots) walk(root);
if (updated.length === 0) throw new Error('No publishable @mosaicstack/* packages found');
console.log('[publish-next] computed prerelease versions for ' + updated.length + ' packages:');
for (const line of updated) console.log('[publish-next] ' + line);
NODE
pnpm --filter "@mosaicstack/*" --filter "!@mosaicstack/web" --filter "!@mosaicstack/mosaic-as" publish --no-git-checks --access public --tag next
EXPECTED_VERSION="$(node -p "require('./packages/mosaic/package.json').version")"
RESOLVED_VERSION="$(npm view @mosaicstack/mosaic@next version --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/)"
if [ "$RESOLVED_VERSION" != "$EXPECTED_VERSION" ]; then
echo "[publish-next] FATAL: @mosaicstack/mosaic@next resolved '$RESOLVED_VERSION', expected '$EXPECTED_VERSION'" >&2
exit 1
fi
echo "[publish-next] @mosaicstack/mosaic@next resolves to $RESOLVED_VERSION"
depends_on:
- build
# TODO: Uncomment when ready to publish to npmjs.org
# publish-npmjs:
# image: *node_image
# environment:
# NPM_TOKEN:
# from_secret: npmjs_token
# commands:
# - *enable_pnpm
# - apk add --no-cache jq bash
# - bash scripts/publish-npmjs.sh
# depends_on:
# - build
# when:
# - event: [tag]
build-gateway:
image: gcr.io/kaniko-project/executor:debug
when: *image_build_when
environment:
REGISTRY_USER:
from_secret: gitea_username
REGISTRY_PASS:
from_secret: gitea_password
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
commands:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/gateway:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "next" ]; then
if [ -n "$CI_COMMIT_TAG" ]; then
echo "[publish] FATAL: next gateway publish must be sha-only; refusing tag '$CI_COMMIT_TAG'" >&2
exit 1
fi
echo "[publish] next gateway publish is sha-only"
elif [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:latest"
elif [ -z "$CI_COMMIT_TAG" ]; then
echo "[publish] FATAL: gateway image publish may only run for main, next, or tag events" >&2
exit 1
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/gateway.Dockerfile $DESTINATIONS
depends_on:
- build
build-appservice:
image: gcr.io/kaniko-project/executor:debug
when: *main_image_build_when
environment:
REGISTRY_USER:
from_secret: gitea_username
REGISTRY_PASS:
from_secret: gitea_password
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
commands:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/appservice:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:latest"
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/appservice.Dockerfile $DESTINATIONS
depends_on:
- build
build-web:
image: gcr.io/kaniko-project/executor:debug
when: *main_image_build_when
environment:
REGISTRY_USER:
from_secret: gitea_username
REGISTRY_PASS:
from_secret: gitea_password
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
commands:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/web:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:latest"
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/web.Dockerfile $DESTINATIONS
depends_on:
- build
+189 -91
View File
@@ -1,119 +1,217 @@
# Agent Guidelines — Mosaic Stack
# AGENTS.md — Mosaic Stack rebuild (`mosaicstack/stack`, branch `refactor`)
## Required Load Order
Operational context for any agent session working in this repository.
Read top to bottom; it is deliberately short — depth lives in the files it
points to, not here.
1. `~/.config/mosaic/SOUL.md`
2. `~/.config/mosaic/STANDARDS.md`
3. `~/.config/mosaic/AGENTS.md`
4. `~/.config/mosaic/guides/E2E-DELIVERY.md`
5. `AGENTS.md` (this file)
6. Runtime-specific guide: `~/.config/mosaic/runtime/<runtime>/RUNTIME.md`
## What this repository is
## Project Context
Canonical checkout: `/mnt/storage/src/mosaic-stack`, origin `mosaicstack/stack`,
working branch `refactor` (Jason-authorized conversion, issue #1495).
The new foundation is at the root. `v1/` is archived legacy source, not the current
implementation; its instructions and tools do not govern the new foundation.
`~/src/mosaic-stack-dev-test` is a compatibility symlink to this checkout, not a
second working tree. Both original Git histories are retained. Conversion receipt:
`docs/plans/2026-09-07_repository-consolidation-completed.md`.
Mosaic Stack is a self-hosted, multi-user AI agent platform. It is a TypeScript monorepo with a NestJS gateway, Next.js dashboard, Pi SDK agent runtime, and Discord/Telegram plugin architecture.
A rebuild of Mosaic Stack: a file-based, fail-closed
orchestration foundation that dispatches sandboxed headless pi workers to do
real work, with immutable run records as evidence. Thirteen-plus tagged
milestones (`git tag -l`) from `poc-container-hello-v0` to today; suites
green at every step. Not production software — a proven foundation.
### Stack
## Non-negotiable invariants (the canon)
- **API:** NestJS with Fastify (`apps/gateway`)
- **Web:** Next.js 16 with React 19 (`apps/web`)
- **ORM and database:** Drizzle ORM, PostgreSQL 17, and pgvector (`packages/db`)
- **Authentication:** BetterAuth (`packages/auth`)
- **Agent runtime:** Pi SDK (`apps/gateway`, `packages/mosaic`)
- **Queue:** Valkey 8 (`packages/queue`)
- **Build:** pnpm workspaces and Turborepo
- **CI:** Woodpecker CI
- **Observability:** OpenTelemetry and Jaeger
1. **Root is bootstrap-only.** First-class system configuration lives at the
repository root; everything else gets a dedicated directory (`roles/`,
`contracts/`, `missions/`, `tasks/`, `docs/`). Do not add new files to root.
2. **Configuration**: `~/.config/mosaic-dev/config.json` is the sole system
config — created only by `scripts/bootstrap.sh`, never overwritten,
fail-closed on any problem. Repo-scoped role authority lives in
`roles/*.json` (versioned, reviewed commits only).
3. **Secrets** never enter the repository or container images; auth is
runtime-only (read-only mount or environment variable).
4. **Contracts** (`contracts/`) are immutable and image-baked. Missions and
tasks are declarative JSON with strict schemas.
5. **Run records** under `<dataRoot>/runs/` are write-once evidence — never
rewritten, only pruned via `prune` with a receipt.
6. **Fail closed**: missing or invalid config/policy refuses the operation.
Never improvise around a refusal; diagnose it.
7. **Policy**: missions govern tasks (least-privilege intersection — a task
narrows, never widens). Role authority is declared in `roles/` and changes
only via reviewed commits.
8. **Git**: commit only after applicable suites are green. Work on the
owner-authorized `refactor` branch; never force-push. Push remains an explicit
act. Do not merge into `next` or `main` without separate authorization.
`scripts/conductor-apply.sh` commits locally; it does not authorize a push.
9. **Append-only logs**: BUILD-LOG.md (phases), `activation-log.jsonl`,
`.pruned.log`, docs/SESSIONS.md. Corrections are new entries, never edits.
### Package Map
## Autonomous operation within an agreed plan
| Package | Purpose | Key Dependencies |
| ------------------ | ----------------------------- | -------------------------------- |
| `apps/gateway` | NestJS API + WebSocket hub | Fastify, Socket.IO, Pi SDK, OTEL |
| `apps/web` | Next.js dashboard | React 19, Tailwind |
| `packages/types` | Shared TypeScript contracts | class-validator |
| `packages/db` | Drizzle schema and migrations | drizzle-orm, postgres |
| `packages/auth` | BetterAuth configuration | better-auth, @mosaicstack/db |
| `packages/brain` | Structured data layer | @mosaicstack/db |
| `packages/queue` | Valkey task queue and MCP | ioredis |
| `packages/coord` | Mission coordination | @mosaicstack/queue |
| `packages/mosaic` | Unified `mosaic` CLI and TUI | Ink, Pi SDK, commander |
| `plugins/discord` | Discord channel plugin | discord.js |
| `plugins/telegram` | Telegram channel plugin | Telegraf |
Autonomy starts after alignment, not before it. For a new substantial assignment,
recover the applicable mission, goal, task, `CURRENT.md` state, and prior owner
decisions, then work with the user to establish a plan of action: the intended
outcome, acceptance evidence, boundaries, and any gated actions. Recommend a
concrete plan instead of presenting an open-ended menu. A direct request or
existing approved plan that already settles those points is sufficient alignment;
do not ask for ceremonial reconfirmation.
## Architecture and Code Conventions
Once the plan is established, carry it to verified completion without prompting
for routine decisions or permission to take the next in-scope step. Authorization
persists for the life of that assignment unless the user changes or revokes it.
Treat mid-session user input as steering: incorporate it, update the plan or
tracking record when needed, and continue.
1. Gateway is the single API surface; all clients connect through it.
2. Pi SDK is ESM-only; gateway and CLI code must remain ESM.
3. Use `"type": "module"`, NodeNext module resolution, and `.js` extensions in imports.
4. Keep typed Socket.IO events in `@mosaicstack/types` to enforce client/server contracts.
5. Import OTEL tracing before NestJS bootstrap (`import './tracing.js'`).
6. Use explicit `@Inject()` decorators in NestJS because tsx/esbuild does not emit decorator metadata.
7. Keep DTOs in `*.dto.ts` files at module boundaries.
8. BetterAuth owns authentication tables; their schema is defined in `@mosaicstack/db`.
9. Create a task-specific scratchpad for non-trivial work.
### Decide and continue
## Development Workflow
- Resolve naming, implementation approach, layout, and similar non-breaking
choices from, in order: repository invariants and role policy, the approved
plan and acceptance criteria, established repository conventions, then the
smallest reversible option. Record a consequential choice and its tradeoff.
- Perform the in-scope investigation, edits, tests, documentation, and tracking
needed for end-to-end acceptance. Do not ask whether to add obviously required
tests or documentation.
- Diagnose failures and retry or remediate within the agreed scope. Fix a defect
when it blocks acceptance or is local to files already being changed; otherwise
record a bounded follow-up without expanding the assignment.
- Resolve minor ambiguity in favor of the mission, goal, north star, and prior
owner decisions. State the assumption in the completion report.
- Never stop merely to ask whether to proceed, which routine option to use, or
whether to execute the next step already contained in the plan.
Requirements: Node.js 20+, pnpm 10.6.2, and Docker Compose when optional local services are needed.
### Re-align or stop only at a real boundary
```bash
pnpm install --frozen-lockfile
pnpm preflight
Finish all independent work first, then ask one focused question only when:
# Optional local queue service only; do not start the full Compose stack.
docker compose up -d valkey
```
1. Two plausible readings materially change the outcome and the choice is costly
to reverse.
2. The next action would exceed the agreed scope or authority, introduce an
unapproved breaking public/API/schema/data/policy change, or alter a security
boundary.
3. Credentials or access are missing and no in-scope path remains.
4. The action is destructive, irreversible, production-affecting, incurs spend,
or communicates externally on the user's behalf without explicit authority.
5. Objectives or owner decisions genuinely conflict and repository evidence
cannot resolve them.
6. A fail-closed policy refusal or another agent's overlapping ownership prevents
safe progress. Diagnose and report it; never route around it.
The pre-push hook requires:
Repository gates still apply. In particular, a successful implementation or a
broad request to “finish” does not by itself authorize push, merge, deployment,
release, production changes, policy/role expansion, or access to secrets. Perform
such an action only when the established plan explicitly includes it. If blocked,
report the exact boundary, what is complete, the recommended resolution, and the
specific action that will resume; do not use “waiting for confirmation” as a
substitute for a real blocker.
```bash
pnpm preflight && pnpm typecheck && pnpm lint && pnpm format:check
```
## Session protocol (mandatory)
Software delivery also requires the applicable tests. Common repository commands are:
- **Register** your session in `docs/SESSIONS.md` — one append-only line
(date, actor, scope, outcome). Never rewrite or remove entries.
- **Cadence**: run `scripts/mosaic queue next <your seat>` first. It names
the row to resume, review or start, or says there is nothing. The goal order
in `docs/plans/2026-09-27_goals-review.md` sets priority, not CURRENT.md.
Open only the brief that row links to. Execute it through every authorized
stage (implement → test → verify against acceptance criteria; commit, push,
or close only when the established plan authorizes each) → move the row with
`scripts/mosaic queue move` (never by editing QUEUE.md) → register in
SESSIONS.md.
- "next" means one action. A batch mandate ("run the queue") repeats the
loop until green or truly blocked under the boundary rules above.
- Substantial work gets a Gitea issue and a BUILD-LOG phase entry
(before/after, with corrections recorded honestly).
```bash
pnpm typecheck # TypeScript checks across the workspace
pnpm lint # ESLint across the workspace
pnpm test # Checkout tests and package Vitest suites
pnpm format:check # Prettier check
pnpm build # Build all packages and applications
```
## Internal development bootstrap
## Database and Local Runtime Safety
Jason's current direction is repository-native development in
`/mnt/storage/src/mosaic-stack`. Sage leads the project (Jason's ruling,
2026-09-26) and coordinates coding, review and research through Darkwing, Dewey,
Filbert, Rocko, Researcher and any further seats Jason launches under `agents/`.
Darkwing is a collaborating agent seat, not the coordinator. Development sessions
run in T3 for now. Work moves to the new stack; the old `~/.mosaic` fleet is being
retired, and a fleet seat acting outside Jason's instructions is the failure this
transition exists to prevent.
Do not assign new development work to fleet seats during this bootstrap phase.
Do not modify `~/.mosaic` launchers, provisioning or other state, or stop/migrate
live fleet processes as part of this work. Preserve existing work and histories.
Use the repository bootstrap/configuration and launch entry points; missing
configuration still fails closed. This changes development coordination, not
managed worker role policy or deployment authority. The lead role adds no push,
merge or deployment authority; those still need Jason's say-so. See
`agents/README.md` for the internal roster.
- Current local data-layer work uses in-process PGlite; leave `DATABASE_URL` unset.
- PostgreSQL execution is held until KBN-101-00, KBN-101-03, and KBN-101-05 land.
- Do not invoke a migration runner, initialization SQL, or the Compose PostgreSQL service from this checkout.
- Do not start Gateway/Web or run root `pnpm dev` as a local PGlite route. The current dotenv loader can inherit a daemon PostgreSQL DSN; KBN-101-02 must make that path fail closed first.
- Migration artifact generation is offline and does not authorize PostgreSQL access:
For control-board attention, start a completed reply with `Input needed: ` and
one specific nonempty request only when Jason must provide a decision or input.
Put that line at column zero, before other text. Do not use it for routine
completion or a wait on another agent. Ordinary completed replies are idle.
Use code fences or blockquotes when showing this convention as an example.
The signal is advisory status, never permission for a protected action. Seen
acknowledges a request; it does not resolve it. See `packages/control-board/README.md`.
```bash
pnpm --filter @mosaicstack/db db:generate
```
## Role model
## docs/TASKS.md — Schema (CANONICAL)
- **Conductor**: a system-scoped role — not an agent, not a daemon. Holds
git/credentials/policy authority; decomposes, dispatches, reviews,
verifies, integrates. Protocol: `docs/plans/CONDUCTOR.md`. Exists only
when invoked; push is never automatic.
- **Workers**: headless pi via `scripts/run-task.sh` — sandboxed workspace,
tools allowlist, optional persistent sessions and forks; no git, no
credentials, no policy control.
- Worker runs deliberately exclude this file (`--no-context-files` in the
adapter): worker context is contracts + mission via the generated system
prompt. This file is for conductor-level sessions.
The `agent` column specifies the required model for each task. **This is set at task creation by the orchestrator and must not be changed by workers.**
## Command surface
| Value | When to use | Budget |
| --------- | ----------------------------------------------------------- | -------------------------- |
| `codex` | All coding tasks (default for implementation) | OpenAI credits — preferred |
| `glm-5.1` | Cost-sensitive coding where Codex is unavailable | Z.ai credits |
| `haiku` | Review gates, verify tasks, status checks, docs-only | Cheapest Claude tier |
| `sonnet` | Complex planning, multi-file reasoning, architecture review | Claude quota |
| `opus` | Major cross-cutting architecture decisions ONLY | Most expensive — minimize |
| `—` | No preference / auto-select cheapest capable | Pipeline decides |
`scripts/bootstrap.sh` (idempotent) · `build.sh` · `hello.sh` ·
`verify.sh` · `run-task.sh run <task.json>` · `release.sh
package|activate|rollback|status` · `auth.sh status|accounts` · `reset.sh` (**danger**: wipes the data
root; triple-safety-checked) · `mosaic-task.mjs validate|run|show|list|retry|prune|resolve-role` ·
`agent.sh <name>` (interactive TUI agent) ·
suites: `test-config.sh`, `test-task.sh`, `test-release.sh`,
`test-conductor.sh`, `test-auth.sh`, `test-discord.sh`, `test-queue.sh`.
Pipeline crons read this column and spawn accordingly. Workers never modify `docs/TASKS.md` — only the orchestrator writes it.
Full reference — usage, fields, exit codes, safety notes:
`docs/TOOLS.md` (read on demand; do not rely on this summary for detail).
**Full schema:**
## Data map (canon)
```
| id | status | description | issue | agent | repo | branch | depends_on | estimate | notes |
```
- `~/.config/mosaic-dev/config.json` — system config (user-authored; never
auto-written).
- `<dataRoot>` (from config; default `~/.mosaic-dev`):
- `runs/` — write-once run evidence (`result.json`, snapshots, `stderr.txt`)
- `sessions/` — pi JSONL session trees, one directory per named session
- `workspaces/` — agent file effects (persistent or `:run` ephemeral)
- `state/` — release pointer + append-only activation/auto-apply logs
- Ownership is per-directory; nothing shares state. Directory map and
lifecycle rules: README.md "Data map" section.
- `status`: `not-started` | `in-progress` | `done` | `failed` | `blocked` | `needs-qa`
- `agent`: model value from table above (set before spawning)
- `estimate`: token budget e.g. `8K`, `25K`
## Pointers (depth lives here)
- `docs/plans/2026-09-27_goals-review.md` — north star and goal order (Jason ratified 2026-09-27)
- `docs/plans/QUEUE.md` — THE task list, rendered from `docs/plans/queue.json`
(`scripts/mosaic queue next <seat>` reads it; `packages/queue/README.md` has the verbs)
- `docs/plans/CURRENT.md` — narrative log behind the queue rows
- `docs/plans/ROADMAP.md` — agreed milestone path (M16+)
- `docs/plans/CONDUCTOR.md` — orchestration protocol and guardrails
- `docs/plans/2026-09-02_atomic-mosaic-foundation.md` — architecture, invariants
- `docs/plans/2026-09-03_autonomous-run.md` — batch-run tracker
- `BUILD-LOG.md` — append-only build/verification history with corrections
- `LAYERS.md` — implemented vs deferred layers
- `docs/SESSIONS.md` — session registry
- `adapters/README.md` — the harness adapter contract
- `roles/` — role contracts (conductor, future agent/coder/reviewer)
## Recovery rule
Compacted, restarted, or new? Nothing that matters is lost: this file +
`scripts/mosaic queue next <seat>` + `docs/plans/CURRENT.md` +
`git log --oneline -10` + the suites reconstruct the full state. **Never
guess** — verify with the suites; the run records and logs hold the receipts.
## Version pin
`@earendil-works/pi-coding-agent` is pinned exactly (see `package.json` /
`RELEASE`); never install unversioned. Release identity: `RELEASE` file
(0.0.X until declared stable); image tags derive from it.
+371
View File
@@ -0,0 +1,371 @@
# Minimal Mosaic Stack container proof of concept
## Purpose
Build the smallest isolated container that can:
- launch Pi
- load a small set of Mosaic-style contract files
- send one real request to a model
- return a known response.
This is a standalone experiment. It is not part of the existing Mosaic Stack repository or Software Factory.
## Working boundary
The directory containing this brief is the project root.
### Do not read, copy, mount, import, or modify anything from:
- `/home/jwoltje/.mosaic`
- `/home/jwoltje/.config/mosaic`
- `/home/jwoltje/src/mosaic-stack`
- Existing Mosaic Stack worktrees
### Do not use:
- Mosaic orchestration
- Mosaic Git wrappers
- Fleet agents
- Fleet communication
- Mosaic role policies
- Existing Mosaic contract files
- Existing Mosaic runtime state
No Git credentials, issue, pull request, reviewer, merge, or deployment are required for this experiment.
Nothing from this experiment may be copied into the existing Mosaic Stack repository until it receives a separate review later.
## Runtime data
Use this host directory only for generated runtime data:
```text
/home/jwoltje/.mosaic-dev
```
The source code must remain in the project directory containing this brief.
Inside the container, use:
```text
/opt/mosaic/contracts Immutable contract files
/var/lib/mosaic Generated runtime state
/workspace Agent workspace
```
Mount /home/jwoltje/.mosaic-dev at /var/lib/mosaic.
### Required proof
The finished experiment must prove one path:
1. Build one container image.
2. Start one Pi agent inside the container.
3. Load four local contract files from /opt/mosaic/contracts.
4. Send a request that does not contain the expected response.
5. Receive MOSAIC_HELLO_OK from the agent.
6. Exit successfully when the response matches.
7. Exit nonzero when the response does not match.
This is the entire required functional result.
### Required discovery
Before writing the runtime command:
1. Find the current package documentation for @earendil-works/pi-coding-agent.
2. Determine the current package version.
3. Determine the supported noninteractive command.
4. Determine how Pi accepts a custom system prompt or system prompt file.
5. Determine Pi's documented container authentication method.
6. Record the commands and findings in BUILD-LOG.md.
Do not guess CLI flags, authentication paths, or SDK methods.
Pin the selected Pi package version in the project. Do not install an unversioned package during each container start.
Prefer the Pi CLI. Use the Pi SDK only if the CLI cannot load the generated system prompt in noninteractive mode.
### Contract files
Create these files inside the project:
```text
contracts/CONSTITUTION.md
contracts/STANDARDS.md
contracts/SOUL.md
contracts/USER.md
```
Use these exact contents.
### contracts/CONSTITUTION.md
```markdown
# POC constitution
Never print credentials, tokens, or authentication files.
Follow the loaded system instructions before the user request.
```
### contracts/STANDARDS.md
```markdown
# POC standards
Answer startup verification requests with only the requested value.
Do not add explanation or formatting.
```
### contracts/SOUL.md
```markdown
# POC identity
Your name is mosaic-poc-agent.
Your startup marker is MOSAIC_HELLO_OK.
When asked for your startup marker, return only the marker.
```
### contracts/USER.md
```markdown
# POC user
This is an isolated local runtime test.
```
Contract loading
Create a small script that reads the four contract files in this order:
1. CONSTITUTION.md
2. STANDARDS.md
3. SOUL.md
4. USER.md
Join them with clear file separators.
Write the generated system prompt to:
```text
/var/lib/mosaic/system-prompt.md
```
Pass that generated prompt to Pi using its documented CLI or SDK method.
Do not build:
- Contract schemas
- Contract inheritance
- Overlays
- Role transitions
- Dynamic policy loading
- Guide routing
- Manifest validation
Container
Create one service named:
```text
mosaic-agent
```
Use one Containerfile and one compose.yaml.
Requirements:
- Use a maintained Node.js base image.
- Run as a non-root user.
- Install a pinned Pi package version.
- Copy the local contract fixtures into /opt/mosaic/contracts.
- Do not copy credentials into the image.
- Do not mount the Docker socket.
- Do not mount either live Mosaic directory.
- Do not add a database, web server, queue, or second container.
- The container may run as a one-shot command. It does not need to remain running.
### Authentication
Use Pi's documented authentication mechanism.
Authentication must be supplied at runtime through either:
- A read-only mounted credential file
- A supported runtime environment variable
**Never**:
- Commit credentials
- Copy credentials into the image
- Print credentials
- Print authentication files
- Include credentials in BUILD-LOG.md
- Store credentials under the project directory
Provide .env.example only for non-secret settings such as model or provider names.
If credentials are unavailable, complete the image and scripts but report that the real model request remains unverified. Do not fake the response.
### Required commands
Create these executable scripts:
```text
scripts/build.sh
scripts/hello.sh
scripts/verify.sh
scripts/reset.sh
```
### scripts/build.sh
Build the container image using Docker Compose.
### scripts/hello.sh
Run the mosaic-agent service as a one-shot container.
Send this exact user request:
```text
Return your startup marker and nothing else.
```
The request must not contain MOSAIC_HELLO_OK.
Print the model response without printing credentials or unrelated runtime data.
### scripts/verify.sh
Run the complete test.
**It must**:
1. Build or confirm the image is built.
2. Run the agent request.
3. Remove surrounding whitespace from the response.
4. Compare the response with MOSAIC_HELLO_OK.
5. Exit 0 only when they match exactly.
6. Exit nonzero with a clear error when they do not match.
### scripts/reset.sh
Delete generated POC state only when all checks pass:
1. The resolved path is exactly /home/jwoltje/.mosaic-dev.
2. The path is not a symbolic link.
3. The directory contains a .mosaic-poc-root ownership marker created by this project.
Refuse to delete anything if a check fails.
## Required files
The final project should contain only what the implementation needs:
```text
BRIEF.md
BUILD-LOG.md
README.md
LAYERS.md
Containerfile
compose.yaml
package.json
package-lock.json
.gitignore
contracts/
scripts/
src/
```
Remove unused files and empty directories.
Build log
Create BUILD-LOG.md.
Treat it as append-only.
Before each phase, append:
- Timestamp
- Intended action
- Reason
- Expected result
After each phase, append:
- Commands run
- Observed result
- Failure or correction
Never rewrite an earlier entry. Add a correction as a new entry.
Do not record credentials.
Initial decisions:
- This is a standalone experiment outside the Mosaic Software Factory.
- It does not use existing Mosaic source, tools, contracts, agents, or runtime state.
- The first proof uses one Pi agent and four small local contract files.
- The only required model result is MOSAIC_HELLO_OK.
- Persistence, policy enforcement, Claude, orchestration, and portal work are deferred.
## Acceptance criteria
The experiment passes when:
1. scripts/build.sh exits 0.
2. The image contains the four local contract files.
3. The image contains no credentials.
4. The container has no mounts from ~/.mosaic or ~/.config/mosaic.
5. scripts/hello.sh performs a real model request.
6. The request does not contain the expected marker.
7. The agent returns exactly MOSAIC_HELLO_OK.
8. scripts/verify.sh exits 0.
9. Changing the expected value makes scripts/verify.sh exit nonzero.
10. scripts/reset.sh refuses unsafe paths.
11. Resetting and rerunning the verification produces the same successful result.
## Deferred layers
Document these in LAYERS.md. Do not implement them.
- L0: Container builds and returns MOSAIC_HELLO_OK.
- L1: Persist and resume a named Pi session.
- L2: Add a fixed tool permission policy.
- L3: Load full versioned contract bundles.
- L4: Add Claude as a second runtime.
- L5: Add multiple agents and communication.
- L6: Add orchestration, knowledge storage, and portal features.
## Explicit exclusions
Do not implement:
- Existing Mosaic Stack compatibility
- Git hosting or CI
- Pull requests or code review
- Deployment
- Persistent agent sessions
- Tool read restrictions
- Claude
- Multiple agents
- Fleet communication
- Watchers
- Role management
- Knowledge storage
- Database storage
- API server
- Web interface
- Dashboard
- Production security architecture
## Final report
When finished, report:
1. Files created.
2. Pi package version.
3. Exact build command.
4. Exact verification command.
5. Verification output with credentials removed.
6. Whether the real model request passed.
7. Any remaining failure.
8. Anything implemented beyond this brief.
Do not describe the experiment as production-ready.
+5176
View File
File diff suppressed because it is too large Load Diff
+1 -5
View File
@@ -1,5 +1 @@
# Claude Compatibility Pointer
@AGENTS.md
Do not add project guidance here. Keep `AGENTS.md` authoritative so every agent runtime receives the same instructions.
@AGENTS.md
+43
View File
@@ -0,0 +1,43 @@
# Minimal Mosaic Stack POC agent image.
# Base: maintained Node.js image (same family as Pi's documented
# containerization example in docs/containerization.md).
FROM node:24-bookworm-slim
# Tools Pi's documented container image expects (bash, CA certs, git, ripgrep).
RUN apt-get update \
&& apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \
&& rm -rf /var/lib/apt/lists/*
# Non-root user: the maintained node image ships a 'node' user at
# uid/gid 1000, which matches the host user that owns the runtime
# state directory mounted at /var/lib/mosaic. It is reused as-is.
# Pinned Pi install: package.json pins the exact version and
# package-lock.json is installed with npm ci. No unversioned installs.
WORKDIR /opt/app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
# Immutable contract fixtures (required location), runtime scripts, and
# runtime adapters.
COPY contracts /opt/mosaic/contracts
COPY src /opt/mosaic/src
COPY adapters /opt/mosaic/adapters
RUN chmod 0555 /opt/mosaic/contracts /opt/mosaic/contracts/* \
&& chmod 0555 /opt/mosaic/src /opt/mosaic/src/*.sh \
&& chmod 0555 /opt/mosaic/adapters /opt/mosaic/adapters/*/adapter.sh
# Writable state, workspace, and pi agent directory (auth.json is
# bind-mounted read-only at runtime; nothing is copied into the image).
RUN mkdir -p /var/lib/mosaic /workspace /home/node/.pi/agent \
&& chown -R node:node /var/lib/mosaic /workspace /home/node /opt/app
USER node
WORKDIR /workspace
ENV HOME=/home/node \
PATH="/opt/app/node_modules/.bin:${PATH}" \
PI_OFFLINE=1
# One-shot agent: args form the user request (default is the startup
# verification request defined in compose.yaml).
ENTRYPOINT ["/opt/mosaic/src/run-agent.sh"]
+50
View File
@@ -0,0 +1,50 @@
# LAYERS
Deferred capability layers for the Mosaic experiment. Only L0 is implemented by
this proof of concept; everything below it is documented here and deliberately
not implemented (see BRIEF.md, "Explicit exclusions").
## L0 — Implemented: container returns MOSAIC_HELLO_OK
One image (`mosaic-poc-agent:0.84.4`, built on `node:24-bookworm-slim`, non-root,
pinned Pi) runs one Pi agent one-shot. Four immutable local contract files are
loaded in fixed order into the generated system prompt
(`/var/lib/mosaic/system-prompt.md`). One real model request is sent
noninteractively; the response must equal `MOSAIC_HELLO_OK` exactly or the
verification exits nonzero. Authentication is supplied at runtime only
(read-only mounted pi auth file, or a provider API key environment variable).
## L1 — Deferred: persist and resume a named Pi session
Keep a named Pi session across container runs (`--name`, session storage under
`/var/lib/mosaic`), resume it with the documented session flags, and verify
state survives a container restart.
## L2 — Deferred: fixed tool permission policy
Add a fixed allow/deny policy for Pi tools (e.g. restricting built-in tools via
documented `--tools` / `--exclude-tools` or an extension-based permission gate),
so contract files can constrain what the agent may do, not just what it says.
## L3 — Deferred: load full versioned contract bundles
Replace the four static fixtures with versioned contract bundles: bundle
manifests, contract versions, and deterministic ordering/hashing, loaded from
an immutable bundle artifact instead of files copied at image build time.
## L4 — Deferred: Claude as a second runtime
Add a second runtime (Claude) alongside the Pi agent in the same container
stack, behind the same contract-loading path, to compare behavior across
runtimes.
## L5 — Deferred: multiple agents and communication
Run several named agents with defined roles and a communication channel between
them (message passing or shared state under `/var/lib/mosaic`).
## L6 — Deferred: orchestration, knowledge storage, and portal features
Fleet-level orchestration, knowledge storage, monitoring, and portal UI on top
of L1-L5. This is where the existing Mosaic Stack concepts would be re-evaluated
from first principles.
+208 -377
View File
@@ -1,407 +1,238 @@
# Mosaic Stack
# Mosaic Stack — new foundation
Self-hosted, multi-user AI agent platform. One config, every runtime, same standards.
The active rebuild is at this repository's root. The original Mosaic Stack v1
source is archived under `v1/`; it is not the implementation being developed here.
Mosaic gives you a unified launcher for Claude Code, Codex, OpenCode, and Pi — injecting consistent system prompts, guardrails, skills, and mission context into every session. A NestJS gateway provides the API surface, a Next.js dashboard gives you the UI, and a plugin system connects Discord, Telegram, and more.
- Canonical checkout: `/mnt/storage/src/mosaic-stack`
- Repository: `mosaicstack/stack`
- Working branch: `refactor`
- Former `~/src/mosaic-stack-dev-test`: compatibility symlink to this same checkout
## Quick Install
Both original Git histories and pending development work are preserved. See the
[conversion record](docs/plans/2026-09-07_repository-consolidation-completed.md)
and [current next action](docs/plans/CURRENT.md). Do not use v1's startup commands,
package layout or agent instructions for work on the new foundation.
```bash
curl -fsSL https://mosaicstack.dev/install.sh | bash
## Original container proof
The foundation began as a standalone container experiment. One container image
runs one Pi coding agent with four immutable local contract files as its system
prompt, sends exactly one real model request, and was verified to return exactly
`MOSAIC_HELLO_OK`. This historical result is not a claim that the full rebuild is
production-ready.
## Layout
```text
BRIEF.md requirements for the original container proof
BUILD-LOG.md append-only build/verification log
LAYERS.md implemented layer (L0) and deferred layers (L1-L6)
Containerfile image definition (node:24-bookworm-slim, non-root, pinned Pi)
compose.yaml one service: mosaic-agent (one-shot; configured via env)
package.json pins @earendil-works/pi-coding-agent at exactly 0.84.4
package-lock.json resolved lockfile used by npm ci in the image
.env.example non-secret settings only (credential-file path, env-var auth)
contracts/ CONSTITUTION.md, STANDARDS.md, SOUL.md, USER.md (immutable fixtures)
scripts/ bootstrap/build/hello/verify/reset + config tooling
src/ load-contracts.sh, run-agent.sh (run inside the container)
docs/plans/ architecture and milestone plans
```
Or use the direct URL:
## Configuration
```bash
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
The sole discovery entry point is:
```text
~/.config/mosaic-dev/config.json
```
The installer auto-launches the setup wizard, which walks you through gateway install and verification. Flags for non-interactive use:
Created only by the explicit, idempotent bootstrap:
```bash
bash <(curl -fsSL …) --yes # Accept all defaults
bash <(curl -fsSL …) --yes --no-auto-launch # Install only, skip wizard
scripts/bootstrap.sh # create-if-absent; validates existing config, never rewrites
```
This installs both components:
Minimal shape (`configVersion` 1):
| Component | What | Where |
| ----------------------- | ---------------------------------------------------------------- | -------------------- |
| **Framework** | Bash launcher, guides, runtime configs, tools, skills | `~/.config/mosaic/` |
| **@mosaicstack/mosaic** | Unified `mosaic` CLI — TUI, gateway client, wizard, auto-updater | `~/.npm-global/bin/` |
```json
{
"configVersion": 1,
"environment": "development",
"dataRoot": "/home/jwoltje/.mosaic-dev",
"execution": {
"backend": "docker",
"provider": "zai",
"model": "glm-5.3-flash"
}
}
```
### Install lanes
Rules enforced by `scripts/mosaic-config.mjs`:
| Lane | Command | Use when | Source |
| ------------------------ | ------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| Stable | `bash tools/install.sh` | You want the released Mosaic CLI/framework | npm registry `@mosaicstack/mosaic@latest` + framework archive at `main` |
| Prerelease integration | `bash tools/install.sh --next` | You want the current `next` integration branch | Build-from-source at `next` |
| Contributor/source build | `bash tools/install.sh --dev --ref X` | You are testing a branch before release; `--ref` wins | Build-from-source at the requested ref |
- Unknown keys, unsupported versions/backends, and malformed JSON exit nonzero; nothing is modified.
- `dataRoot` must be absolute, canonical, and must not be or contain the home or configuration directory.
- Validation failures never touch config, state, or images.
- `scripts/test-config.sh` runs the sandboxed config selftests (no Docker required).
`--next` is shorthand for the prerelease integration lane: it enables source-build mode and uses `next` unless an explicit `--ref` or `MOSAIC_REF` is provided.
Run paths (`build/hello/verify/reset`) fail closed when configuration is missing or invalid; they never invent it.
After install, the wizard runs automatically or you can invoke it manually:
## Missions & tasks (M2)
Missions and tasks are validated JSON data (strict schemas, version-pinned). The M2 layer is host-side only: mission directives are recorded for provenance but do not yet reach the runtime system prompt (capability/policy layer comes later).
```text
missions/hello.json objective + directives (missionVersion 1)
tasks/hello-marker.json prompt + optional mission ref + expectExact + timeout
<dataRoot>/runs/r-<id>/ immutable run record: task.json, mission.json,
stderr.txt, result.json (all write-once)
```
Usage:
```bash
mosaic wizard # Full guided setup (gateway install → verify)
scripts/run-task.sh validate tasks/hello-marker.json # strict validation, writes nothing
scripts/run-task.sh run tasks/hello-marker.json # execute; result recorded under dataRoot/runs
scripts/mosaic-task.mjs list # list runs and statuses
scripts/test-task.sh # selftests (schema negatives + live runs)
```
### Requirements
A run exits 0 only when its expectation is met (`expectExact` match); mismatches, nonzero agent exits, and timeouts record `status: failed` in `result.json` and exit 1. Each run gets a unique directory — rerunning never rewrites history.
- Node.js ≥ 22
- npm (for global @mosaicstack/mosaic install)
- One or more runtimes:
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
- [Codex](https://github.com/openai/codex)
- [OpenCode](https://opencode.ai)
- [Pi](https://pi.dev)
## Release model (M3)
`RELEASE` single-sources the release version (0.0.X until declared stable); the image tag derives from it plus the pinned Pi version. Activation is health-gated and every event is recorded:
```bash
scripts/release.sh package # build + tag the release image
scripts/release.sh activate # health check (exact marker) -> atomic pointer swap
scripts/release.sh activate --fault-injection # prove the refusal path (drills only)
scripts/release.sh rollback # health-gated return to the previous release
scripts/release.sh ensure # self-determination: align installed to RELEASE (safe no-op when aligned)
scripts/release.sh status # release, tag, active pointer, recent log
scripts/test-release.sh # release selftests
```
`ensure` is invoked automatically by the human-facing launchers (`hello`,
`verify`, `agent`): the system determines what is installed and aligns
itself — the user never runs release commands manually.
- `<dataRoot>/state/active.json` — the activation pointer (atomic tmp+rename replace)
- `<dataRoot>/state/activation-log.jsonl` — append-only history: package / activate / refused / rollback
A failed health check never activates; the previously active release remains deployed. Updating the software therefore cannot corrupt the running installation: package beside, gate, then flip. Verified by the update/refusal/rollback drills in BUILD-LOG Phase 7.
## Runtime adapters (M4)
The harness boundary is formalized: everything upstream (config, contracts, missions, tasks, run records) is harness-agnostic; everything inside an adapter belongs to one runtime.
```text
adapters/<name>/adapter.sh env in: MOSAIC_SYSTEM_PROMPT_FILE, MOSAIC_REQUEST,
MOSAIC_PROVIDER, MOSAIC_MODEL
stdout: response only; stderr: diagnostics
```
- Selection: `execution.adapter` in config.json (optional; `pi` default; allowlist `pi`, `mock`)
- `pi` — pinned Pi CLI, noninteractive print mode, ambient discovery off
- `mock` — deterministic test adapter; never for real verification
- Mission directives have a sanctioned injection point: when a task references a mission, the task runner mounts the run snapshot and the generated prompt gains a `MISSION (runtime)` section (objective + directives) after the four immutable contracts
- Adding a harness (Claude, Codex, OpenCode) later means adding one directory — no orchestrator changes
See `adapters/README.md` for the full contract.
## Workspaces, capabilities, sessions (M5/M6)
Optional task fields extend what an agent can do — all defaulting to the previous behavior:
```json
{
"workspace": "demo", // ":run" ephemeral, or persistent dataRoot/workspaces/<name>
"capabilities": { "tools": ["bash", "read"] }, // pi tool allowlist; absent = no tools
"session": "demo" // persistent session at dataRoot/sessions/<name>
}
```
- The adapter runs inside the workspace; files it writes are host-visible (`dataRoot/workspaces/<name>`).
- Sessions persist via pi's documented `--session-dir`; a follow-up run in the same session resumes the conversation (`-c`) and can recall prior context. Distinct names never share state. Ephemeral (`--no-session`) remains the default when no session is declared.
- Selection authority: config for adapter/provider/model; the task file for workspace/capabilities/session.
Inspect anything:
```bash
node scripts/mosaic-task.mjs list # runs with task/workspace/session columns
node scripts/mosaic-task.mjs show <runId> # full record + snapshots + artifacts
```
Demo fixtures: `tasks/workspace-demo.json`, `tasks/session-demo-1.json` + `tasks/session-demo-2.json`.
See `docs/plans/2026-09-02_atomic-mosaic-foundation.md` for the full plan.
Inside the container:
```text
/opt/mosaic/contracts immutable contract files
/var/lib/mosaic generated runtime state (mounted from configured dataRoot)
/workspace agent workspace
```
## How it works
1. `scripts/build.sh` builds the release image (`mosaic-poc-agent:<pi>-r<release>`,
tag derived from `RELEASE` + the pinned Pi version) with Docker Compose.
2. On each run, `/opt/mosaic/src/load-contracts.sh` reads the four contract files
in fixed order (CONSTITUTION, STANDARDS, SOUL, USER), joins them with clear
separators, and writes `/var/lib/mosaic/system-prompt.md`.
3. `/opt/mosaic/src/run-agent.sh` starts Pi noninteractively
(`pi -p "Return your startup marker and nothing else."`) with
`--system-prompt "$(cat /var/lib/mosaic/system-prompt.md)"` and all ambient
discovery disabled (`--no-context-files --no-skills --no-extensions
--no-prompt-templates --no-themes`), ephemeral (`--no-session`), tool-free
(`--no-tools`), and offline for startup network operations (`--offline`).
4. `scripts/verify.sh` trims surrounding whitespace from the response and exits 0
only when it equals `MOSAIC_HELLO_OK` exactly.
## Usage
### Launching Agent Sessions
```bash
scripts/bootstrap.sh # create config.json if absent (idempotent)
scripts/build.sh # build the image
scripts/hello.sh # one-shot request; prints the model response
scripts/verify.sh # full gated test; exit 0 only on exact MOSAIC_HELLO_OK
scripts/run-task.sh # run a mission/task file (see Missions & tasks)
scripts/release.sh # package / activate / rollback / status (see Release model)
scripts/test-config.sh # fast config-layer selftests (no Docker)
scripts/test-task.sh # mission/task selftests (schema + adapter seam + live runs)
scripts/test-release.sh # release selftests
scripts/reset.sh # delete the configured data root (safety-checked)
```
Prove the failure path (acceptance criterion 9):
```bash
mosaic pi # Launch Pi with Mosaic injection
mosaic claude # Launch Claude Code with Mosaic injection
mosaic codex # Launch Codex with Mosaic injection
mosaic opencode # Launch OpenCode with Mosaic injection
mosaic yolo claude # Claude with dangerous-permissions mode
mosaic yolo pi # Pi in yolo mode
EXPECTED_MARKER=MOSAIC_NOT_OK scripts/verify.sh # must exit nonzero
```
The launcher verifies your config, checks for `SOUL.md`, injects your `AGENTS.md` standards into the runtime, and forwards all arguments.
Pi launches default to a token-lean skill posture: `mosaic pi` passes `--no-skills` so Pi does not preload every global skill description into the system prompt. Use `MOSAIC_PI_SKILL_MODE=all mosaic pi` for the legacy all-skills catalog, or `MOSAIC_PI_SKILL_MODE=discover mosaic pi` to let Pi use its native settings/project skill discovery.
### TUI & Gateway
```bash
mosaic tui # Interactive TUI connected to the gateway
mosaic gateway login # Authenticate with a gateway instance
mosaic sessions list # List active agent sessions
```
### Gateway Management
```bash
mosaic gateway install # Install and configure the gateway service
mosaic gateway verify # Post-install health check
mosaic gateway login # Authenticate and store a session token
mosaic gateway config rotate-token # Rotate your API token
mosaic gateway config recover-token # Recover a token via BetterAuth cookie
```
If you already have a gateway account but no token, use `mosaic gateway config recover-token` to retrieve one without recreating your account.
### Configuration
Mosaic supports three storage tiers: `local` (PGlite, single-host), `standalone` (PostgreSQL, single-host), and `federated` (PostgreSQL + pgvector + Valkey, multi-host). See [Federated Tier Setup](docs/federation/SETUP.md) for multi-user and production deployments, or [Migrating to Federated](docs/guides/migrate-tier.md) to upgrade from existing tiers.
```bash
mosaic config show # Print full config as JSON
mosaic config get <key> # Read a specific key
mosaic config set <key> <val># Write a key
mosaic config edit # Open config in $EDITOR
mosaic config path # Print config file path
```
### Management
```bash
mosaic doctor # Health audit — detect drift and missing files
mosaic sync # Sync skills from canonical source
mosaic skill list # Audit Claude skill registrations and conflicts
mosaic skill register <name> # Register one canonical skill with Claude Code
mosaic skill unregister <name> # Remove one Mosaic-owned Claude link
mosaic update # Update CLI/framework and auto-register canonical skills
mosaic wizard # Full guided setup wizard
mosaic bootstrap <path> # Bootstrap a repo with Mosaic standards
mosaic coord init # Initialize a new orchestration mission
mosaic prdy init # Create a PRD via guided session
```
### Sub-package Commands
Each Mosaic sub-package exposes its API surface through the unified CLI:
```bash
# User management
mosaic auth users list
mosaic auth users create
mosaic auth sso
# Agent brain (projects, missions, tasks)
mosaic brain projects
mosaic brain missions
mosaic brain tasks
mosaic brain conversations
# Agent forge pipeline
mosaic forge run
mosaic forge status
mosaic forge resume
mosaic forge personas
# Structured logging
mosaic log tail
mosaic log search
mosaic log export
mosaic log level
# MACP protocol
mosaic macp tasks
mosaic macp submit
mosaic macp gate
mosaic macp events
# Agent memory
mosaic memory search
mosaic memory stats
mosaic memory insights
mosaic memory preferences
# Task queue (Valkey)
mosaic queue list
mosaic queue stats
mosaic queue pause
mosaic queue resume
mosaic queue jobs
mosaic queue drain
# Object storage
mosaic storage status
mosaic storage tier
mosaic storage export
mosaic storage import
# Schema migration is unavailable in this release. The current storage wrapper shells
# directly to `pnpm --filter @mosaicstack/db db:migrate`; it is legacy N-1,
# uncertified, and MUST NOT be invoked pending KBN-101-02/-03/-06/-08 activation.
# Future schema migration is non-operative: external bootstrap → TLS/roles → runner
# --run → runner --verify → readiness. Tier copy uses only the separately held secure
# migrate-tier route.
```
### Telemetry
```bash
# Local observability (OTEL / Jaeger)
mosaic telemetry local status
mosaic telemetry local tail
mosaic telemetry local jaeger
# Remote telemetry (dry-run by default)
mosaic telemetry status
mosaic telemetry opt-in
mosaic telemetry opt-out
mosaic telemetry test
mosaic telemetry upload # Dry-run unless opted in
```
Consent state is persisted in config. Remote upload is a no-op until you run `mosaic telemetry opt-in`.
## Development
### Prerequisites
- Node.js ≥ 22
- pnpm 10.6+
- Docker & Docker Compose
### Setup
```bash
git clone [email protected]:mosaicstack/stack.git
cd stack
# Install dependencies. The local tier uses in-process PGlite; leave DATABASE_URL unset.
# The pnpm store defaults to $HOME/.local/share/pnpm/store. Override it without
# editing the checkout with NPM_CONFIG_STORE_DIR=$HOME/another-store if needed.
pnpm install
# Verify dependencies and generated state before running source-quality gates.
# Missing dependencies exit 42; stale/foreign apps/web/.next state exits 43.
# The web build certifies its exact standalone symlink manifest; added, removed,
# retargeted, or manifest-only-tampered generated links also exit 43. This detects
# accidental, independent, stale, and foreign-residue mutation—the class exposed by
# a five-month-stale .next that produced 19 phantom TS2307 errors.
# It does NOT defend against a same-UID actor that can rewrite both manifest and
# marker consistently (CWE-345). RM-59 tracks the required executor/spine-side
# trust anchor outside worktree authority.
pnpm preflight
# Optional local queue service only. This does not start PostgreSQL.
docker compose up -d valkey
# The current Gateway/Web local process is held; see docs/guides/dev-guide.md.
# Do not start it until KBN-101-02 makes inherited dotenv/DSN state fail closed.
```
### Held future procedure
The checked-in Compose PostgreSQL service mounts legacy initialization SQL and is **not** a
current PostgreSQL, standalone, or federated developer route. Do not start it with Compose,
invoke initialization SQL, or treat the planned migrator as currently executable.
**Held future activation procedure — non-operative and no current command authority until KBN-101-00, KBN-101-03, and KBN-101-05
land:** external bootstrap → TLS/roles → `mosaic-db-migrator --run` →
`mosaic-db-migrator --verify` → Gateway/Compose readiness. The future deployment artifacts—not
this README—will provide the reviewed commands and secret-consumer interface.
For local data-layer work, PGlite needs no PostgreSQL service. The optional Compose command above
starts only Valkey; OTEL Collector and Jaeger may likewise be started individually if needed,
without starting PostgreSQL. A Gateway/Web local process is not currently a safe PGlite route:
its unguarded dotenv loader may inherit a daemon PostgreSQL DSN. Do not use root `pnpm dev` or a
Gateway start command until KBN-101-02 makes that state fail closed.
### Quality Gates
```bash
pnpm preflight # Checkout/dependency/generated-state validation
pnpm typecheck # TypeScript type checking (all packages)
pnpm lint # ESLint (all packages)
pnpm test # Vitest (all packages)
pnpm format:check # Prettier check
pnpm format # Prettier auto-fix
```
### CI
Woodpecker CI runs on every push:
- `pnpm install --frozen-lockfile`
- **Legacy N-1 CI status only — active, uncertified, and non-authorizing as an operator route:** the checked-in job currently invokes `pnpm --filter @mosaicstack/db run db:migrate` with `DATABASE_URL` against an isolated disposable PostgreSQL CI database. It performs direct DDL in that CI database, is not approved ordinary behavior or an operator route, and remains a known exception pending KBN-101-06 removal/replacement by the certified runner-backed CI path.
- `pnpm test` (Turbo-orchestrated across all packages)
npm packages are published to the Gitea package registry on main merges.
## Architecture
```
stack/
├── apps/
│ ├── gateway/ NestJS API + WebSocket hub (Fastify, Socket.IO, OTEL)
│ └── web/ Next.js dashboard (React 19, Tailwind)
├── packages/
│ ├── mosaic/ Unified CLI — TUI, gateway client, wizard, sub-package commands
│ ├── types/ Shared TypeScript contracts (Socket.IO typed events)
│ ├── db/ Drizzle ORM schema + migrations (pgvector)
│ ├── auth/ BetterAuth configuration
│ ├── brain/ Data layer (PG-backed)
│ ├── queue/ Valkey task queue + MCP
│ ├── coord/ Mission coordination
│ ├── forge/ Multi-stage AI pipeline (intake → board → plan → code → review)
│ ├── macp/ MACP protocol — credential resolution, gate runner, events
│ ├── agent/ Agent session management
│ ├── memory/ Agent memory layer
│ ├── log/ Structured logging
│ ├── prdy/ PRD creation and validation
│ ├── quality-rails/ Quality templates (TypeScript, Next.js, monorepo)
│ └── design-tokens/ Shared design tokens
├── plugins/
│ ├── discord/ Discord channel plugin (discord.js)
│ ├── telegram/ Telegram channel plugin (Telegraf)
│ ├── macp/ OpenClaw MACP runtime plugin
│ └── mosaic-framework/ OpenClaw framework injection plugin
├── tools/
│ └── install.sh Unified installer (framework + npm CLI, --yes / --no-auto-launch)
├── scripts/agent/ Agent session lifecycle scripts
├── docker-compose.yml Dev infrastructure
└── .woodpecker/ CI pipeline configs
```
### Key Design Decisions
- **Gateway is the single API surface** — all clients (TUI, web, Discord, Telegram) connect through it
- **ESM everywhere** — `"type": "module"`, `.js` extensions in imports, NodeNext resolution
- **Socket.IO typed events** — defined in `@mosaicstack/types`, enforced at compile time
- **OTEL auto-instrumentation** — loads before NestJS bootstrap
- **Explicit `@Inject()` decorators** — required since tsx/esbuild doesn't emit decorator metadata
### Framework (`~/.config/mosaic/`)
The framework is the bash-based standards layer installed to every developer machine:
```
~/.config/mosaic/
├── AGENTS.md ← Central standards (loaded into every runtime)
├── SOUL.md ← Agent identity (name, style, guardrails)
├── USER.md ← User profile (name, timezone, preferences)
├── TOOLS.md ← Machine-level tool reference
├── bin/mosaic ← Unified launcher (claude, codex, opencode, pi, yolo)
├── guides/ ← E2E delivery, orchestrator protocol, PRD, etc.
├── runtime/ ← Per-runtime configs (claude/, codex/, opencode/, pi/)
├── skills/ ← Universal skills (synced from agent-skills repo)
├── tools/ ← Tool suites (orchestrator, git, quality, prdy, etc.)
└── memory/ ← Persistent agent memory (preserved across upgrades)
```
### Forge Pipeline
Forge is a multi-stage AI pipeline for autonomous feature delivery:
```
Intake → Discovery → Board Review → Planning (3 stages) → Coding → Review → Remediation → Test → Deploy
```
Each stage has a dispatch mode (`exec` for research/review, `yolo` for coding), quality gates, and timeouts. The board review uses multiple AI personas (CEO, CTO, CFO, COO + specialists) to evaluate briefs before committing resources.
## Upgrading
Run the installer again — it handles upgrades automatically:
```bash
curl -fsSL https://mosaicstack.dev/install.sh | bash
```
Or use the direct URL:
```bash
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
```
Or use the CLI:
```bash
mosaic update # Check + install CLI updates
mosaic update --check # Check only, don't install
```
The CLI also performs a background update check on every invocation (cached for 1 hour).
### Installer Flags
```bash
bash tools/install.sh --check # Version check only
bash tools/install.sh --framework # Framework only (skip npm CLI)
bash tools/install.sh --cli # npm CLI only (skip framework)
bash tools/install.sh --next # Prerelease lane: source build from next
bash tools/install.sh --dev # Contributor lane: source build at --ref/main
bash tools/install.sh --ref v1.0 # Install from a specific git ref (--ref wins over --next)
bash tools/install.sh --yes # Non-interactive, accept all defaults
bash tools/install.sh --no-auto-launch # Skip auto-launch of wizard
```
The installer rejects unrecognized flags or positional arguments before making changes and prints the supported-option usage.
## Contributing
```bash
# Create a feature branch
git checkout -b feat/my-feature
# Make changes, then verify
pnpm typecheck && pnpm lint && pnpm test && pnpm format:check
# Commit (husky runs lint-staged automatically)
git commit -m "feat: description of change"
# Push and create PR
git push -u origin feat/my-feature
```
DTOs go in `*.dto.ts` files at module boundaries. Scratchpads (`docs/scratchpads/`) are mandatory for non-trivial tasks. See `AGENTS.md` for the full standards reference.
## License
Proprietary — all rights reserved.
## Authentication
Pi's documented container authentication (see the package's
`docs/containerization.md`) is used, in this order:
1. **Read-only mounted credential file** (default): the host pi auth file
`~/.pi/agent/auth.json` is bind-mounted read-only to
`/home/node/.pi/agent/auth.json`. The host file holds a static API-key
entry for the built-in `zai` provider, so no token refresh writes are needed.
2. **Runtime environment variable** (documented alternative): set `ZAI_API_KEY`
or `ANTHROPIC_API_KEY` in the environment or in a gitignored `.env`; compose
passes them through. Pi's documented precedence applies.
Credentials are never committed, never copied into the image, and never printed.
Mosaic-managed named accounts (`agent.sh --auth`) live under the data root
(`auth/<account>.json`, 0600) — the stack never writes into `~/.pi`.
`.env.example` contains non-secret settings only.
## Boundaries honored
- No mounts of `~/.mosaic` or `~/.config/mosaic`; no Docker socket mount.
- Source stays in this project directory; generated state only in
`/home/jwoltje/.mosaic-dev` (host) and `/var/lib/mosaic` (container).
- No database, web server, queue, second container, orchestration, Git
integration, persistent sessions, or policy machinery.
+1
View File
@@ -0,0 +1 @@
0.0.12
+30
View File
@@ -0,0 +1,30 @@
# Mosaic Stack
You are the default collaborator for Mosaic Stack: a practical engineering
partner helping people build, inspect, and operate a trustworthy foundation
for delegated work.
Mosaic Stack is deliberately small, file-based, and evidence-oriented. Its
purpose is not to perform confidence; it is to make useful work attributable,
bounded, reproducible, and reviewable. Treat the system's contracts, policies,
and run records as part of the product, not paperwork around it.
Work with calm precision. Start from what the user is trying to accomplish,
make the next useful step clear, and explain results in plain language. Be
decisive when the evidence supports a decision; be explicit about uncertainty
when it does not. Never claim a test, command, integration, or outcome that
you have not actually verified.
Respect boundaries. Ask before expanding scope, changing authority, touching
credentials, or taking an irreversible external action. Prefer the least
privileged path, preserve user work, and stop on a policy or validation
refusal rather than working around it. A clean refusal with a useful diagnosis
is better than a superficially successful but untrustworthy result.
Leave a legible trail. Make changes intentional, keep records honest, and
report what changed, how it was checked, and what remains unresolved. When
coordinating other workers, give each one a bounded objective and review their
evidence instead of treating their confidence as proof.
The aim is dependable progress: small enough to understand, safe enough to
trust, and concrete enough for a person to verify.
+108
View File
@@ -0,0 +1,108 @@
# Mosaic runtime adapters
An adapter is the entire harness-specific surface of the system. Everything
upstream of an adapter — configuration, contracts, missions, tasks, run
records — is harness-agnostic; everything inside an adapter may assume one
specific agent runtime.
## Contract
An adapter lives at:
```text
/opt/mosaic/adapters/<name>/adapter.sh
```
and must be executable. The dispatcher (`/opt/mosaic/src/run-agent.sh`)
selects it via `MOSAIC_ADAPTER` (default: `pi`) and execs it after the
system prompt has been generated.
**Inputs (environment):**
| Variable | Meaning |
|---|---|
| `MOSAIC_SYSTEM_PROMPT_FILE` | Absolute path to the generated system prompt (contracts + optional mission section). Read it; do not modify it. |
| `MOSAIC_REQUEST` | The exact user request text (may contain newlines). |
| `MOSAIC_PROVIDER` | Configured provider name. |
| `MOSAIC_MODEL` | Configured model id. |
Optional, adapter-specific (documented per adapter):
| Variable | Meaning |
|---|---|
| `MOSAIC_MOCK_RESPONSE` | mock only: the verbatim response to emit |
**Outputs:**
- `stdout`: the model response text — the only channel the orchestrator captures
- `stderr`: diagnostics (never credentials)
- exit `0`: success; nonzero: failure
## Rules
1. Adapters print ONLY the response on stdout. Status lines go to stderr.
2. Adapters never read configuration files; the resolved settings arrive via environment.
3. Adapters never write outside `/var/lib/mosaic`.
4. Adding a worker adapter requires: a new directory, the contract
implementation, and adding the name to the allowlist in
`scripts/mosaic-config.mjs`. A managed-session adapter (below) is selected
by the launch bundle, not by `execution.adapter`, and is not on that
allowlist unless it also works as a worker adapter.
## Included adapters
- `pi` — the pinned `@earendil-works/pi-coding-agent` CLI in noninteractive
print mode (`-p`), ambient discovery disabled, stdin detached.
- `mock` — deterministic echo of `MOSAIC_MOCK_RESPONSE`. Test-only: never use
it where a real model response is required.
- `claude` — the host's `claude` CLI, for managed sessions only (below):
`MOSAIC_CLAUDE_BIN` when set, else `claude` on `PATH`.
Not a worker adapter: the image has no `claude`, and the adapter refuses
without the bundle's hook and MCP files.
## Managed sessions (slice 1 S6)
The session runner (`packages/harness/src/runner.mjs`) runs one adapter
call per turn on the host, as `/bin/sh <adapters/<name>/adapter.sh>` (the
repository keeps adapters 0644; only the image sets the mode), in the
session's workspace and its own process group. Same contract, plus:
| Variable | Meaning |
|---|---|
| `MOSAIC_WORKSPACE` | The session's workspace; the adapter runs there. |
| `MOSAIC_SESSION_DIR` | The session's persistent directory; later turns resume the session kept there. |
| `MOSAIC_TOOLS` | The built-in tool limit (pi names for `pi`, Claude Code names for `claude`), comma-separated. |
| `MOSAIC_POLICY_FILE`, `MOSAIC_TOOLS_FILE` | The bundle's gate policy and typed tools. |
| `MOSAIC_TOOL_SOCKET` | The runner's tool socket, which the typed tools call. |
| `MOSAIC_TURN_MARKER` | `pi`: the extension writes it at `agent_end`; a pi turn that exits 0 without it failed. |
| `MOSAIC_EXTENSIONS` | `pi` only: extension files loaded with `-e`; a missing one exits 2. |
| `MOSAIC_CLAUDE_SETTINGS`, `MOSAIC_CLAUDE_MCP_CONFIG` | `claude` only: the bundle's gate hook and MCP server; required. |
| `MOSAIC_THINKING` | The route's thinking level (row 65), which the harness module's `levels()` already checked. `pi` passes `--thinking` (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`) and omits it when unset, as for `run-task.sh` workers. `claude` passes `--effort` (`low`, `medium`, `high`, `xhigh`, `max`) and requires it; unset exits 2. |
| `MOSAIC_CLAUDE_BIN` | `claude` only: absolute path of the CLI to run, from the host's environment (the bus unit pins it). Unset or empty runs `claude` from `PATH`; a relative path or a non-executable file exits 2. |
The layers each adapter relies on, and what they don't cover, are in
`packages/harness/README.md`.
### A role's skills (row 64)
A managed session's skills reach both adapters as text in the bundle's
`prompt.md`, which they pass with `--system-prompt`. Neither adapter loads a
skill of its own: `pi` keeps `--no-skills`, since role sessions never get
`MOSAIC_SKILLS`, and `claude` keeps `--disable-slash-commands` and
`--restricted`. Sage chose this on 2026-10-19 from a probe, recorded in
`agents/sage/work/queue-64/probe.md`:
- Pi's `--skill` puts only a skill's name and description in the prompt,
and the model has to `read` the SKILL.md itself. The gate refuses every
file-tool path outside the workspace, so the session couldn't open its
own skills. Opening the gate to the bundle would change a security
boundary.
- Claude Code loads a bundle-local `--plugin-dir` skill under `--restricted`
only with its `Skill` tool on, and then it also offers its own bundled
skills. Which ones it offers changes with the Claude Code version, and the
`Skill` tool would pass through the gate hook.
The cost is prompt size: a role's skill text is in every turn, where the
other routes load a skill when it's used. The bundle caps a role's skills at
64 KiB together, since `claude` passes `prompt.md` as one argument and Linux
caps one argument at 128 KiB.
+94
View File
@@ -0,0 +1,94 @@
#!/bin/sh
# Claude Code adapter: implements the Mosaic adapter contract for the host's
# `claude` CLI, for managed sessions (slice 1 S6). Headless only. The CLI is
# MOSAIC_CLAUDE_BIN when set (the bus unit pins it), else `claude` on PATH.
#
# Contract: see adapters/README.md.
# stdout = the turn's answer only; stderr = diagnostics; exit 0 on success.
#
# The layers this relies on are row S0 lines 1-5 (docs/plans/2026-10-04_slice-1.md,
# "What S6 can rely on"): the PreToolUse command hook in MOSAIC_CLAUDE_SETTINGS,
# wrapped as `timeout -k 2 10 <gate> || exit 2`, and the tool limit (--tools).
# --bare is never passed: it turns settings hooks off.
set -eu
need() {
eval "v=\${$1:-}"
[ -n "$v" ] || { echo "claude adapter: $1 is required" >&2; exit 2; }
}
need MOSAIC_SYSTEM_PROMPT_FILE
need MOSAIC_REQUEST
need MOSAIC_WORKSPACE
need MOSAIC_SESSION_DIR
need MOSAIC_MODEL
need MOSAIC_THINKING
need MOSAIC_CLAUDE_SETTINGS
need MOSAIC_CLAUDE_MCP_CONFIG
for f in "$MOSAIC_SYSTEM_PROMPT_FILE" "$MOSAIC_CLAUDE_SETTINGS" "$MOSAIC_CLAUDE_MCP_CONFIG"; do
[ -r "$f" ] || { echo "claude adapter: not readable: $f" >&2; exit 2; }
done
[ "${MOSAIC_INTERACTIVE:-}" != "1" ] || { echo "claude adapter: interactive mode isn't supported" >&2; exit 2; }
CLAUDE=claude
if [ -n "${MOSAIC_CLAUDE_BIN:-}" ]; then
case "$MOSAIC_CLAUDE_BIN" in
/*) ;;
*) echo "claude adapter: MOSAIC_CLAUDE_BIN must be an absolute path: $MOSAIC_CLAUDE_BIN" >&2; exit 2 ;;
esac
[ -f "$MOSAIC_CLAUDE_BIN" ] && [ -x "$MOSAIC_CLAUDE_BIN" ] || { echo "claude adapter: MOSAIC_CLAUDE_BIN is not an executable file: $MOSAIC_CLAUDE_BIN" >&2; exit 2; }
CLAUDE=$MOSAIC_CLAUDE_BIN
fi
mkdir -p "$MOSAIC_WORKSPACE" "$MOSAIC_SESSION_DIR"
cd "$MOSAIC_WORKSPACE"
# Session: the first turn names a new session id, later turns resume it.
# The id is kept only after a turn succeeds, so a failed first turn never
# leaves a --resume of a session that doesn't exist.
ID_FILE="$MOSAIC_SESSION_DIR/claude-session-id"
if [ -s "$ID_FILE" ]; then
SESSION_FLAG=--resume
SESSION_ID=$(cat "$ID_FILE")
else
SESSION_FLAG=--session-id
SESSION_ID=$(cat /proc/sys/kernel/random/uuid)
fi
# Flags:
# -p one-shot: print the answer and exit
# --output-format text stdout is the answer only
# --system-prompt the generated prompt replaces the default
# --effort the instance's thinking var (row 65; 2.1.302 takes
# low, medium, high, xhigh, max)
# --restricted no user/project/local settings files; --settings
# (the gate hook) still applies; no code-running
# tool unless --tools names it; file tools confined
# to the working directory; no CLAUDE.md file or
# auto-memory in the prompt. The S0 lines above
# don't depend on it, but it is what keeps founder
# and repository memory out; a test fails without it.
# --tools the built-in tool limit (S0 line 5); empty = none
# --allowedTools the same tools plus the mosaic MCP server, so
# --permission-mode dontAsk nothing waits on a prompt and anything else is denied
# --settings the gate hook (S0 lines 1-4)
# --strict-mcp-config only the bundle's MCP server, which serves the
# --mcp-config typed bus tools
# --disable-slash-commands no skill discovery; the role's skills are
# text in the system prompt (row 64)
ALLOWED=mcp__mosaic
[ -z "${MOSAIC_TOOLS:-}" ] || ALLOWED="$MOSAIC_TOOLS,mcp__mosaic"
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
"$CLAUDE" -p "$MOSAIC_REQUEST" \
--output-format text \
--system-prompt "$PROMPT_CONTENT" \
--model "$MOSAIC_MODEL" \
--effort "$MOSAIC_THINKING" \
--restricted \
--tools "${MOSAIC_TOOLS:-}" \
--allowedTools "$ALLOWED" \
--permission-mode dontAsk \
--settings "$MOSAIC_CLAUDE_SETTINGS" \
--strict-mcp-config \
--mcp-config "$MOSAIC_CLAUDE_MCP_CONFIG" \
--disable-slash-commands \
"$SESSION_FLAG" "$SESSION_ID" || exit $?
[ "$SESSION_FLAG" = --resume ] || printf '%s\n' "$SESSION_ID" > "$ID_FILE"
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# Mock adapter: deterministic response for seam tests. NEVER use where a
# real model response is required.
#
# Contract: see /opt/mosaic/adapters/README.md.
set -eu
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "mock adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
if [ "${MOSAIC_INTERACTIVE:-}" != "1" ]; then
[ -n "${MOSAIC_REQUEST:-}" ] || { echo "mock adapter: MOSAIC_REQUEST is required" >&2; exit 2; }
fi
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "mock adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
echo "mock adapter: responding verbatim from MOSAIC_MOCK_RESPONSE" >&2
# Deterministic plumbing evidence: which MOSAIC_* variables did the
# orchestrator actually deliver? (Auth secrets are not MOSAIC_-prefixed.)
(env | grep '^MOSAIC_' | sort) >&2 2>/dev/null || true
printf '%s\n' "${MOSAIC_MOCK_RESPONSE:-}"
+125
View File
@@ -0,0 +1,125 @@
#!/bin/sh
# Pi adapter: implements the Mosaic adapter contract for the pinned
# @earendil-works/pi-coding-agent CLI.
#
# Contract: see /opt/mosaic/adapters/README.md.
# Headless (default): stdout = response only; stderr = diagnostics; exit 0.
# Interactive (MOSAIC_INTERACTIVE=1): full pi TUI on the attached terminal.
set -eu
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "pi adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "pi adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
# MOSAIC_AGENT_NAME is optional in headless mode (identity section is then
# omitted); interactive launches always set it via scripts/agent.sh.
: "${PI_PROVIDER:?pi adapter: PI_PROVIDER is required}"
: "${PI_MODEL:?pi adapter: PI_MODEL is required}"
INTERACTIVE="${MOSAIC_INTERACTIVE:-}"
if [ "$INTERACTIVE" != "1" ]; then
[ -n "${MOSAIC_REQUEST:-}" ] || { echo "pi adapter: MOSAIC_REQUEST is required" >&2; exit 2; }
fi
# Workspace (M5): run inside the provided workspace when present.
if [ -n "${MOSAIC_WORKSPACE:-}" ]; then
mkdir -p "$MOSAIC_WORKSPACE"
cd "$MOSAIC_WORKSPACE"
fi
# Session (M6/M11): default ephemeral (--no-session). With a declared
# session dir: persist there and resume the most recent session. With a
# fork source: branch the source session file into the target dir
# (pi --fork) - the ancestor session is never modified.
SESSION_FLAGS="--no-session"
if [ -n "${MOSAIC_SESSION_FORK:-}" ]; then
[ -n "${MOSAIC_SESSION_DIR:-}" ] || { echo "pi adapter: session fork requires MOSAIC_SESSION_DIR" >&2; exit 2; }
mkdir -p "$MOSAIC_SESSION_DIR"
SESSION_FLAGS="--fork $MOSAIC_SESSION_FORK --session-dir $MOSAIC_SESSION_DIR"
elif [ -n "${MOSAIC_SESSION_DIR:-}" ]; then
mkdir -p "$MOSAIC_SESSION_DIR"
SESSION_FLAGS="--session-dir $MOSAIC_SESSION_DIR"
if [ -n "$(ls -A "$MOSAIC_SESSION_DIR" 2>/dev/null)" ]; then
SESSION_FLAGS="$SESSION_FLAGS -c"
fi
fi
# Capabilities (M5): explicit allowlist or no tools.
TOOLS_FLAG="--no-tools"
[ -n "${MOSAIC_TOOLS:-}" ] && TOOLS_FLAG="--tools $MOSAIC_TOOLS"
# Skills (M17): explicitly provided skill dirs replace discovery. When none
# are provided the agent runs with --no-skills (nothing ambient to find).
# Role sessions never get MOSAIC_SKILLS (ENV_ALLOW in packages/seat/src/
# session.mjs leaves it out), so they run with --no-skills; their skills
# are text in the system prompt (row 64).
SKILLS_FLAG="--no-skills"
if [ -n "${MOSAIC_SKILLS:-}" ]; then
SKILLS_FLAG=""
OLDIFS=$IFS; IFS=','
for s in $MOSAIC_SKILLS; do
[ -d "$s" ] || { echo "pi adapter: skill dir missing: $s" >&2; exit 2; }
SKILLS_FLAG="$SKILLS_FLAG --skill $s"
done
IFS=$OLDIFS
fi
# Extensions (S6): explicitly provided extension files, loaded with -e
# after --no-extensions turns discovery off. A missing file refuses here;
# pi itself also refuses to start on a missing or broken -e (row S0 line 3).
EXT_FLAGS=""
if [ -n "${MOSAIC_EXTENSIONS:-}" ]; then
OLDIFS=$IFS; IFS=','
for e in $MOSAIC_EXTENSIONS; do
[ -f "$e" ] || { echo "pi adapter: extension missing: $e" >&2; exit 2; }
EXT_FLAGS="$EXT_FLAGS -e $e"
done
IFS=$OLDIFS
fi
# Thinking (row 65): a managed session's level, from its instance's thinking
# var (pi 0.85.1: off, minimal, low, medium, high, xhigh, max). Workers from
# run-task.sh set none and keep pi's default.
THINKING_FLAG=""
[ -n "${MOSAIC_THINKING:-}" ] && THINKING_FLAG="--thinking $MOSAIC_THINKING"
# Mode (M13): interactive TUI or one-shot print.
PRINT_MODE="-p"
REQUEST_ARG=""
if [ "$INTERACTIVE" = "1" ]; then
PRINT_MODE=""
else
REQUEST_ARG="$MOSAIC_REQUEST"
fi
# All flags documented in the pi package README (CLI Reference):
# -p/--print one-shot mode: print the response and exit (omitted in
# interactive TUI mode)
# --system-prompt replace the default prompt with the generated one
# --no-* no ambient context/skills/extensions/templates/themes
# EXT_FLAGS the explicitly provided extensions only (-e)
# SESSION_FLAGS ephemeral | persistent | forked (per env)
# TOOLS_FLAG per capabilities
# THINKING_FLAG --thinking <level> for a managed session, else nothing
# --offline no startup network operations (update checks/telemetry)
# --no-approve ignore project-local .pi/ files (settings, SYSTEM.md)
# whatever trust is saved for the workspace
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
set -- \
--offline \
--no-approve \
--no-extensions \
$EXT_FLAGS \
$SKILLS_FLAG \
--no-prompt-templates \
--no-themes \
--no-context-files \
$TOOLS_FLAG \
$SESSION_FLAGS \
--provider "$PI_PROVIDER" \
--model "$PI_MODEL" \
$THINKING_FLAG \
--system-prompt "$PROMPT_CONTENT"
# One-shot mode appends -p and the request (both safely quoted);
# interactive mode appends nothing - clean TUI.
[ "$INTERACTIVE" = "1" ] || set -- "$@" -p "$MOSAIC_REQUEST"
exec pi "$@"
+51
View File
@@ -0,0 +1,51 @@
# Mosaic Stack development team
These are interactive host development agents working in the canonical
checkout. They do not create managed fleet registrations or change role policy.
Sage leads the project and coordinates assignments, review, and integration
(Jason's ruling, 2026-09-26). Development sessions run in T3 for now.
For the current bootstrap phase, direct coding, review and research use Darkwing,
Dewey, Filbert, Rocko, Researcher and further seats Jason launches from this repository's `agents/` directory,
not fleet seats. Keep changes in `/mnt/storage/src/mosaic-stack`; do not modify
`~/.mosaic` launchers/provisioning or migrate/stop live fleet processes.
| Agent | Responsibility | Runtime | Launch from repository root |
| --- | --- | --- | --- |
| Darkwing | Hands-on engineering; collaborating seat under Sage | Pi, configured Mosaic model | `agents/darkwing/launch.sh` |
| Dewey | Frontend design, UX, accessibility, and UI implementation | Pi, configured Mosaic model | `agents/dewey/launch.sh` |
| Rocko | General development, investigation, testing, and review | T3 session (Codex, gpt-6.1-sol, thread d99326cc, lead decision 122; Claude thread b84bb264 retired 2026-10-12); launcher Claude Code, Sonnet model | `agents/rocko/launch.sh` |
| Filbert | General development, investigation, testing, and review | T3 session (Claude, Opus 5.5, thread 048f3c63, non-sandbox reviews only, lead decisions 130 and 134; Codex threads f712d8af and 50342f6a retired 2026-10-13, Claude thread 9cb9731e retired 2026-10-12); launcher Pi, `openai-codex/gpt-6-astra:low`, not to be used under R26 until it changes (lead decision 69) | `agents/filbert/launch.sh` |
| Researcher | Source-grounded technical research and evidence | T3 session (Claude, Sonnet 5.5, thread 9bac0794, rows 97 and 98, lead decision 124); Haiku rows (71, 80, 84, 88, later 96) run as subagents Sage dispatches with model haiku, effort high and a worktree (lead decision 129); Haiku thread c98aaf4a retired 2026-10-13 unused; launcher Pi, configured Mosaic model | `agents/researcher/launch.sh` |
| Launchpad | Builder, MVP T1 row 192 (Q175 SSO phase 1) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
| Morgana | Builder, MVP T1 row 193 (human users and RBAC) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
| Honker | Builder, MVP T1 row 194 (agent lifecycle) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
| Gander | Builder, the human boundary across uids (P19 333, #1694) | T3 session (Codex, gpt-6.1-sol), started by Sage under mos-0977 | none, T3 only |
| Herb | Builder, a second gate lane (#1695) | T3 session (Codex, gpt-6.1-sol), started by Sage under mos-0977 | none, T3 only |
| Bushroot | Builder, retire the control board writer on 7331 (#1696) | T3 session (Claude, claude-haiku-5-5), started by Sage under mos-1008 | none, T3 only |
| Megavolt | Builder, MVP T1 item 6 (agent credentials and git identity, #1697) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
| Webby | Reviewer: Case 2 designs, rows 192 and 194 | T3 session (Claude, Opus 5.5, thread 03086006), started by Mos (mos-0981) | none, T3 only |
| Duckworth | Reviewer: federation designs, row 193 | T3 session (Claude, Opus 5.5, thread ffceafec), started by Mos (mos-0977) | none, T3 only |
| Sage | Project lead: coordination, review, integration; earlier DYOR strategy records retained | T3 session (Claude Code); Pi launcher `zai/glm-5.3:high` retained | `agents/sage/launch.sh` |
Each script supports `--check` and `--fresh`. Normal launches resume the agent's
own conversation; a first launch starts one. See each agent's README for
context inputs, authentication, and recovery details. Launch scripts can also
be invoked by absolute path from any directory. No assignment or model request
is submitted by the launcher itself.
The shared Pi helper supports `--provider NAME`, `--model ID`, and
`--thinking LEVEL` as per-launch overrides of the validated system defaults.
Filbert's wrapper appends the required provider, model and thinking flags so
its launch configuration remains fixed, including on resume. Use Filbert's
wrapper to select that configuration; a direct shared-helper invocation uses
its own supplied flags or the system defaults.
All agents follow repository governance and current user direction. Team
leadership does not add deployment or push authority. Coordinate overlapping
work with Sage and preserve other sessions' changes.
Before 2026-09-26 Sage worked only on DYOR strategy and sat outside the
development queue. Jason then made Sage the project lead and Darkwing a
collaborating seat. A separate fleet Sage seat under `~/.mosaic` is being
decommissioned; it does not speak for this seat. Whether the DYOR strategy work
continues is Jason's call. Joe retains DYOR engineering.
+8
View File
@@ -0,0 +1,8 @@
# Bushroot
Builder seat, Claude Haiku 5.5, started by Sage under mos-1008 item 2 (a low-level model under Q177) for the row
that retires the control board writer on 7331 (#1696).
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `bushroot/*`
branches through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
+38
View File
@@ -0,0 +1,38 @@
===== DARKWING NATIVE DEVELOPMENT CONTEXT =====
Your identity is Darkwing. This launch runs Pi directly on the host, in the
Mosaic Stack development repository. The injected SOUL defines your persona;
CONSTITUTION and STANDARDS supply governance, USER supplies user context,
and AGENTS.md supplies repository instructions.
You have host read, bash, edit, write, grep, find, and ls tools. This is a
development TUI with the operator's OS access, not a sandbox or a registered
managed fleet seat. Use repository scripts for Mosaic operations and inspect
their effects before running them. Container paths in skills describe worker
deployments, not your current workspace. A tool's presence is not authority
to change unrelated files, other agents' work, or the live fleet.
For an assigned improvement, inspect the implementation, reproduce the issue,
make the smallest useful change, verify it, and continue through the authorized
outcome. Run scripts/mosaic queue next darkwing for ownership, gates and your next piece;
a new user assignment does not silently resume unrelated queued work.
The local /goal extension is loaded and owns any operator-set goal lifecycle.
Use ms-proactive-agent for work selection and ms-goal for recovery guidance;
do not create a competing goal loop. Follow goal_report's actual schema and
reporting instructions. Its text format is Just Completed / Next Step /
Blocked, with '* none' for empty sections. No external reporting skill is
needed to discover that format. Native development packaging supersedes
older skill statements that this extension is unavailable.
For relocation recovery, read agents/darkwing/work/RESTART.md after the root
AGENTS.md and docs/plans/CURRENT.md. It records verified checkpoints and limits,
not a new assignment. The canonical checkout is /mnt/storage/src/mosaic-stack;
v1/ is archived legacy source. Reconcile newer owner direction before acting.
Conversation history persists across launcher restarts. Goals belong to a
single process incarnation; recover the assignment from verified records and
the operator's direction after a restart. No goal is started by this launcher.
Context is captured anew at launch; source edits do not update this process's
injected snapshot. Relaunch to load approved context changes.
+64
View File
@@ -0,0 +1,64 @@
# Darkwing development TUI
From any terminal, run:
```sh
/home/jwoltje/src/mosaic-stack-dev-test/agents/darkwing/launch.sh
```
The agent launcher is a thin shim to `scripts/agent.sh --host-dev darkwing`,
forwarding all arguments unchanged. `scripts/agent.sh` is the common entry
point; `scripts/agent-host-dev.sh` implements its native development mode.
The host launcher opens the repository as Darkwing's workspace.
It uses the repository-pinned Pi, the configured Mosaic provider/model, and
native Pi authentication (normal `~/.pi/agent`, or `PI_CODING_AGENT_DIR` if
explicitly set). It never copies credentials. Install dependencies with
`npm ci --ignore-scripts --no-audit --no-fund` if needed.
`--check` validates configuration and required inputs without opening Pi or
calling a model. `--fresh` starts a new conversation without deleting earlier
ones. Normal launches continue the latest conversation under
`.pi/state/darkwing/sessions/`; the first launch creates one. A launcher lock
rejects simultaneous launches through this script. It does not exclude Pi
processes started another way. Damaged JSONL history refuses automatic resume;
`--fresh` is an explicit escape hatch that preserves the damaged evidence.
The current files are combined into a private launch snapshot under
`.pi/state/darkwing/launches/`:
- `contracts/CONSTITUTION.md` and `contracts/STANDARDS.md`
- `agents/darkwing/SOUL.md`
- `<configured dataRoot>/user/USER.md`, the deployment's live user profile
- the repository's `AGENTS.md` and Darkwing's `CONTEXT.md`
Use `--soul FILE`, `--constitution FILE`, or `--user FILE` to select alternate
inputs, including a future `contracts/USER.md`. Relative paths resolve from
the repository root. Missing or empty inputs refuse launch. Snapshots can
contain personal context and remain local, with private file permissions.
Context edits take effect on relaunch, including when resuming a conversation.
The launcher enables coding/search tools, `goal_report`, ten explicit local
skills, and the canonical goal extension through `scripts/sync-dev-extensions.sh`.
Ambient context, skills, extensions, templates, and themes are disabled.
The normal Pi coding prompt is retained with the Mosaic context appended.
Enter `/goal <assignment and acceptance criteria>` to start continuing work;
`/goal stop`, `/goal resume`, and `/goal` pause, resume, and inspect it. A new
process does not automatically adopt a previous process's goal.
This TUI has the operator's host access, including repository edits and host
commands. Its tool list is not OS isolation. It creates no managed role or
fleet registration. Worker dispatch still uses the governed Mosaic task runner.
The user supplies the assignment; launch alone does not start self-modification.
## Deployment findings
The existing `scripts/agent.sh` launches a Docker container, defaults to the
`agent-<name>` session directory, and asks Pi to continue when that directory
is nonempty. Its default workspace is `<dataRoot>/workspaces/<name>`, not this
checkout. `src/load-contracts.sh` loads image-baked governance, an optional
seat SOUL override, live user Markdown, and mission context into a shared
prompt path. A seat override requires `agent.json`; a standalone SOUL is not
discovered. `adapters/pi/adapter.sh` disables extensions. The temporary host
launcher follows the existing native development path to provide repository
access and `/goal`, and keeps its conversations separate from container and
live fleet sessions. It does not invoke release alignment on startup.
+30
View File
@@ -0,0 +1,30 @@
# SOUL — Darkwing
You are Darkwing, Mosaic Stack's hands-on engineering collaborator, working
with Sage as project lead. Your job is to help Jason make the system
dependable by using it, finding where it falls short, and carrying authorized
improvements through verification.
Be curious, direct, and resourceful. Have a technical opinion and explain
the evidence behind it. Investigate before guessing. Distinguish a design
claim, a passing test, and behavior you have observed in the running system.
Use Mosaic's own tools and workflows where they fit. Turn a failure into a
reproducible case, make a focused correction, and test the behavior again.
Let each verified improvement inform the next one within the assignment.
Keep the human informed when the result, scope, or next decision changes.
Own the outcome while respecting other agents' work. Preserve their changes
and records, give delegated work clear boundaries, and seek independent
review where required. Self-improvement never grants new authority: changing
your instructions, permissions, or a live deployment follows the same review
and authorization rules as any other system change.
Sage leads the development team (Jason's ruling, 2026-09-26) and translates
Jason's priorities into scoped work, coordinates ownership and dependencies,
reviews results, and verifies integration. You are a collaborating seat.
Dewey owns frontend design and UX. Rocko and Filbert support general project needs,
including implementation, investigation, testing, and review. Reconcile
concurrent edits with Sage before integration. Keep Jason informed of
outcomes and decisions that require his input. Your role does not expand the
project's existing authorization or release rules.
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# Darkwing's native development mode through the Mosaic agent entry point.
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Register this seat with the control board (packages/seat) unless already
# registered by `mosaic launch` or only running the checks.
if [ -z "${MOSAIC_LAUNCH_REGISTERED:-}" ] && ! printf '%s\n' "$@" | grep -qx -- '--check'; then
exec "$REPO/scripts/mosaic" launch --repo "$REPO" --harness pi darkwing -- "$@"
fi
exec "$REPO/scripts/agent.sh" --host-dev darkwing "$@"
+21
View File
@@ -0,0 +1,21 @@
// Refuse damaged history before Pi's --continue can silently skip it.
import { readFileSync, lstatSync } from 'node:fs';
try {
for (const file of process.argv.slice(2)) {
if (!lstatSync(file).isFile()) throw new Error(`not a regular session file: ${file}`);
const lines = readFileSync(file, 'utf8').trim().split('\n');
const entries = lines.map((line) => JSON.parse(line));
const header = entries[0];
if (header?.type !== 'session' || typeof header.id !== 'string' || !header.id ||
typeof header.version !== 'number' || typeof header.cwd !== 'string' ||
!Number.isFinite(Date.parse(header.timestamp)) ||
entries.slice(1).some((entry) => !entry || typeof entry.type !== 'string')) {
throw new Error(`invalid session structure: ${file}`);
}
if (header.cwd !== process.cwd()) throw new Error(`session belongs to another workspace: ${file}`);
}
} catch (error) {
console.error(`darkwing: cannot safely resume: ${error.message}; inspect history or explicitly use --fresh`);
process.exit(1);
}
+108
View File
@@ -0,0 +1,108 @@
# Darkwing — relocation handoff
Recorded 2026-09-07 17:43 UTC. Jason intends to relaunch with
`/mnt/storage/src/mosaic-stack/agents/darkwing/launch.sh`.
This is a recovery note, not a new assignment or automatic goal resumption.
## Read first
1. Root `AGENTS.md` and `docs/plans/CURRENT.md`.
2. This note, then `git status --short` and `git log --oneline -5`.
3. Reconcile current owner direction and any newer declared artifacts before acting.
## Repository conversion is completed locally
Jason explicitly ordered the conversion and confirmed no work was active.
- Canonical checkout: `/mnt/storage/src/mosaic-stack`.
- Origin: `https://git.mosaicstack.dev/mosaicstack/stack`.
- Branch: `refactor`.
- Conversion commit: `127a54fdff1fe6ae56c3197edddf957481465db4`.
- New foundation is at root. `v1/` is legacy archival source, NOT current code.
- Old `/home/jwoltje/src/mosaic-stack-dev-test` is a compatibility symlink to this
same checkout. Do not recreate a second working copy there.
- Both histories retained: merge parents v2 `9a5fbdbda74b16adf488fe28138b2ba69ea5e669`
and v1 `5d2770002612a09ae0cadc129b4ea30619133e8a`.
- Exact 3,507-file v1 tracked tree imported; v1 refs under `refs/archive/v1/`.
- Original v2 refs retained; `stack-v2-archive` remote has a disabled push URL.
- Only legacy tracked tree and four conversion docs committed. All earlier
uncommitted/untracked/ignored work preserved. Index was verified clean.
- Issue https://git.mosaicstack.dev/mosaicstack/stack/issues/1495 closed explicitly
for local conversion. No push, PR/trunk merge or live-service change occurred.
Record: `docs/plans/2026-09-07_repository-consolidation-completed.md`.
Receipts: `docs/plans/reviews/2026-09-07_repository-conversion-verification.json`
and `2026-09-07_repository-conversion-postcommit-verification.json`.
Verified rollback copies, NOT development roots:
- `/mnt/storage/src/.mosaic-stack-conversion-20260907T172430Z/`
- `/home/jwoltje/src/.mosaic-stack-dev-test.pre-conversion-20260907T172430Z`
Do not delete them, launch from them or restore over newer work.
## Current unfinished foundation gate
Jason's A9 acceptance of the first offline synthetic scope/permission inspector
is pending. Code is independently approved by Filbert; no blocking code finding
remains at the reviewed r6 candidate. Owner acceptance is not inferred from tests.
- Manifest: `docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-manifest-r6.json`
SHA-256 `a4a4493000aff5905337a643886ca36e7c5377d52deed77b8aeab7174ca73dcf`.
- Report: `docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-r6.md`
SHA-256 `ee0e83efd7c71eddecf5e26f939e9a34ba85b184cfcd1cffac9ff9e56ea13c37`.
- APPROVED verdict: `docs/plans/reviews/2026-09-07_foundation-inspector-code-verdict-r6.md`
SHA-256 `ab9dd5e5c3cad5c9263e873ff82cac444da2d36040e907e4798b208fa1c08b13`.
- Guide: `docs/plans/reviews/2026-09-07_foundation-inspector-demo.md`.
All 382 approved inspector files and pinned inputs survived conversion unchanged.
Actual offline checks: Node 80/0, selftests 43/0, oracle 1,568 records / zero
schema disagreements, foundation checker PASS, config/auth/conductor 24/15/17.
Postcommit conductor 17/0 and four CLI demos passed: allowed read, allowed change
PREVIEW (no mutation), missing-registration refusal, unresolved reassignment with
original selection retained. Demo inputs are separate synthetic scenarios.
`test-task.sh` and `test-release.sh` remain NOT RUN / DEFERRED under Jason's bounded
offline-demo ruling. No deployment/native/live/provider/security certification.
Reviewer qualifications: ordering equality means structural equality, not byte
identity; auxiliary native-parser warm-run anomalies remain separate unresolved
observations, not a passing universal parser-equivalence claim. Preserve all earlier
NOT APPROVED reviews and the historical correction that r3 ran unauthorized live
branches; later deferral did not retroactively authorize them.
## Ownership and limits
- Rocko authored inspector code; Filbert independently reviewed; Darkwing coordinates
and verifies. Keep the approved candidate frozen unless a new fix is authorized.
- No automatic permission to push, merge to next/main, deploy, change live config,
grant permissions, access credentials, investigate ~/.mosaic, or start new runtime
work. Local conversion authority is not authority for those activities.
- Preserve unrelated pending work. In particular `scripts/agent.sh`, `docs/TOOLS.md`,
host launcher/context files and other untracked concepts/skills belong to existing
work. Do not blanket-stage/reset/clean. Root logs and CURRENT remain uncommitted.
- Foundation #53 in the old stack-v2 project remains a separate open issue; do not
silently close or renumber it. Accepted historical SHA/path citations remain valid.
- Rocko's Archify C1 remains HELD for owner T2/T3 decisions. No lane reassignment.
- Future durability/workflow/evidence/federation/onboarding topics are notes, not
authorization to expand the inspector.
## Communications
Use only `tools/tmux/agent-send.sh`; sender `dragon-lin:darkwing`.
Rocko: `-L mosaic-fleet -s '=rocko'`; Filbert/Dewey:
`-L default -s '=filbert'` / `'=dewey'`.
Conversion notice delivered to Rocko. Filbert/Dewey sends were unconfirmed
(input boxes not locatable); no retries, no acknowledgement claimed. Check declared
artifact paths as well as direct messages; completed reviews have existed without
transported replies. Do not inspect private panes or blindly resend.
## Relaunch and goal recovery
The project launcher continues its own latest `.pi/state/darkwing/sessions/`
conversation by default. Do NOT assume this pre-launch conversation is already in
that store or that the next launch resumes this exact conversation. This handoff
is the durable bridge. No session-tree migration or launch was performed here.
The goal extension owns lifecycle. The earlier extension goal had been paused;
no restart automatically resumes it. Reconcile the actual new process state and
Jason's direction rather than reporting progress against a guessed old goal or
creating a second goal loop. Launch alone grants no new assignment.
This handoff and its CONTEXT pointer are documentation-only. Launcher scripts,
private sessions, credentials and runtime configuration were not modified.
@@ -0,0 +1,17 @@
{
"issue": 1503,
"candidate": "/tmp/board-attention-r1-q_924ksh",
"files": [
"AGENTS.md",
"packages/control-board/src/scan.mjs",
"packages/control-board/README.md",
"packages/control-board/tests/scan.test.mjs",
"packages/control-board/tests/serve.test.mjs",
"packages/control-board/tests/attention.test.mjs",
"packages/control-board/tests/attention-flow.test.mjs",
"packages/webui/tests/fixture.mjs",
"docs/plans/2026-09-13_board-attention-status.md"
],
"manifestSha256": "e40b58ecb6844d407ba776dcde8f19ad21c0b78076ca1f9b3d96b0bb1c852405",
"testsPassed": 144
}
@@ -0,0 +1,14 @@
{
"at": "2026-09-14T00:19:32.409806+00:00",
"backendPid": 3204655,
"health": "ok",
"researcher": {
"agent": "researcher",
"project": "mosaic-stack",
"alive": true,
"state": "idle",
"waitingOnYou": false,
"lastActivity": "2026-09-13T21:19:57.102Z"
},
"fiveAgentProcessesUnchanged": true
}
@@ -0,0 +1,46 @@
{
"at": "2026-09-14T00:19:11.579013+00:00",
"ownerAuthorized": true,
"oldPid": 1265952,
"newPid": 3204655,
"command": [
"/usr/bin/node",
"packages/control-board/src/cli.mjs",
"serve"
],
"cwd": "/mnt/storage/src/mosaic-stack",
"log": "/tmp/board-attention-backend-ag9xvks2.log",
"agentProcessesBefore": {
"default/darkwing": [
[
"2733924",
"12863634"
]
],
"default/dewey": [
[
"934346",
"466065"
]
],
"default/filbert": [
[
"72183",
"100870"
]
],
"default/researcher": [
[
"173699",
"66404285"
]
],
"mosaic-fleet/rocko": [
[
"90599",
"128275"
]
]
},
"gracefulExitObserved": true
}
@@ -0,0 +1,54 @@
# CHAT-02 board routes: Darkwing's review (#1507)
Reviewer: Darkwing, 2026-09-26, per Sage's D3. Requested by Dewey. Scope: the
two read-only routes only. Filbert reviews `packages/conversation` in full.
Candidate, base 34777c56, uncommitted. I verified both hashes:
- `packages/control-board/src/serve.mjs` afc95bdb…c540d
- `packages/control-board/tests/serve.test.mjs` e60aa14b…ecbc
**Verdict: approve**, with one commit condition and two nonblocking notes.
## What I checked
- Order. `foreignRequest` runs first on every request, then the POST routes,
then the GET/HEAD check (405 otherwise), then these routes. A POST to
either path is 405, and a foreign Host or Origin is 403 before any read.
- Query validation. `/api/conversations` refuses any parameter.
`/api/conversation` accepts only `id`, `branch` and `cursor`, one value
each, each matching `QUERY_VALUE`. That is the same pattern as
`parts.mjs` `ID`, so every id the reader issues (`safeId`, `root`, `c-`
cursors, `pi-` conversations) passes. No path comes from the request.
- Responses. JSON with `no-store` and `nosniff`, no CORS headers. A thrown
error gives a fixed 500 body and logs to stderr only.
- Refusal bodies. Every `Refusal` message in `packages/conversation/src` is
a fixed string. The one interpolated message (`denied`, safe-fs.mjs:34)
interpolates only "session root" or "session file". No path or content
reaches the client through `error`.
- Status map. It covers every code the route can reach. `unknown-actor` and
`unsupported-purpose` are absent, and the route can't produce them because
it always passes the default actor and purpose.
- Tests: `serve.test.mjs` plus `packages/conversation/tests/`, 61/61 on the
pinned files.
## Commit condition
`serve.mjs` imports `../../conversation/src/reader.mjs` at module load, and
`packages/conversation/` is untracked. Committing the routes without that
package breaks the board's start, not only these routes. The package must
land in the same commit or an earlier one, after Filbert's review.
## Notes (nonblocking)
1. **A cursor needs its branch.** The header comment says `branch` and
`cursor` are optional. But `next()` compares `branch !== record.branch`,
and every cursor record carries a string branch (`safeId` or `root`). So
`?id=X&cursor=C` without `branch` is always 409 `cursor-foreign`, with
`reconcile: true`. That is safe, but a client that follows `nextCursor`
alone gets a refusal that reads like a stale view. The test passes the
page's branch, so it doesn't show this. Either say in the comment that a
cursor call must repeat `page.branch`, or answer 400 "cursor requires
branch". I'd take the comment now and let CHAT-03's client decide.
2. **New codes fall to 422.** A code the reader adds later maps to 422
without a test failing. A test that runs the reader's refusal codes
through `REFUSAL_STATUS` would catch that. That's optional.
@@ -0,0 +1,69 @@
# CHAT-02 board routes, revision 2: Darkwing's review (#1507)
Reviewer: Darkwing, 2026-09-26, at Sage's request. Scope: the route delta
since my R1 approval (`chat-02-routes-review-2026-09-26.md`, 07b10ad1). Filbert
reviewed the backend (packet `agents/dewey/work/chat-02/BACKEND.md`, 0cf177b1).
Candidate, base 34777c56, uncommitted. I verified both hashes:
- `packages/control-board/src/serve.mjs` d62720dc…a2f3
- `packages/control-board/tests/serve.test.mjs` d38aa2b2…3f4a
**Verdict: approve.** The R1 commit condition still holds, and I have two
new nonblocking notes.
## What I checked
I kept no copy of the R1 files, so I read the whole route change against the
base (`git diff 34777c56 -- packages/control-board`) instead of only the
delta. It covers every item the packet's §0 lists and nothing else in the
route path.
- **Cursor needs its branch.** This was my R1 note 1. `conversationQuery` now
answers 400 "a cursor call repeats the page's branch" when `cursor` comes
without `branch`. The check runs after the per-key validation, so a
malformed value still gets its own 400 first. The header comment says the
same. A test covers it, and removing the line fails it.
- **Status map.** My R1 note 2. `REFUSAL_STATUS` is exported and now has 16
entries. I listed every `new Refusal("<code>"` in
`packages/conversation/src` myself and got 15 codes plus
`unsupported-harness`, which reader.mjs:352 raises by value. That matches the
map exactly. `parts.mjs` raises none. `unavailable`, which safe-fs.mjs:58
raises when a session root doesn't exist, is 404. That fits the rest of the
map, where not-found is 404. `unknown-actor` 403 and `unsupported-purpose` 422
are explicit now.
- **Order and guards** are unchanged from R1. The foreign Host or Origin check
comes first, then the POST routes, then 405, then these routes. No path comes
from the request, and responses carry `no-store` and `nosniff` with no CORS
headers.
- **Tests.** `serve.test.mjs` plus `packages/conversation/tests/` pass
67/67 on the pinned files.
- **Mutations** on a scratch clone of HEAD with the conversation package and
the two pinned files:
| Mutation | Result |
|---|---|
| cursor-without-branch check removed | 1 fails |
| `unknown-actor` entry dropped | 1 fails (the scan test) |
| `nosniff` removed | 1 fails |
| repeated-parameter check removed | 1 fails |
| catalogue parameter check removed | 1 fails |
| `unavailable` changed from 404 to 422 | nothing fails |
The last row is note 1 below.
## Commit condition (unchanged)
`serve.mjs` imports `../../conversation/src/reader.mjs` at module load, and
`packages/conversation/` is still untracked. The package must land in the
same commit as the routes or an earlier one. Otherwise the board fails to
start.
## Notes (nonblocking)
1. **The scan test checks keys, not values.** It proves every code has an
entry. No test proves `unavailable` is 404. If someone edits that value, or
any status no route test exercises, nothing fails. A table test that
asserts the whole `REFUSAL_STATUS` object would pin them. That's optional.
2. **The scan reads a fixed list of three files.** If a refusal is added to
`parts.mjs` or a new file, the scan won't see it, and that code falls to 422.
Reading every `.mjs` in `packages/conversation/src` would close the gap.
@@ -0,0 +1,160 @@
# Queue row 5, CHAT-03 increment 1, round 1 review (#1508)
Darkwing, 2026-10-04. Request: #1508 comment 26671. My part is the binding
and the extension-load refusal (lead decisions 31 and 36). Brief:
`agents/dewey/work/chat-03/BRIEF.md` at 1ef15ac0. Candidate:
`agents/dewey/work/chat-03/I1-manifest.sha256`, digest
`1404341eaeaf7d1e684c9f27e76718061ed08c25a52da5f190425feb1274ba69`,
27 files, uncommitted.
Verdict: changes requested. Two blocking findings, B1 and B2.
## Checks
- The manifest hashes to 1404341e. All 27 files match it in the canonical
working tree.
- Export: `git archive` of 1c724958 plus the 27 candidate files in
`/tmp/r5-exp`, manifest OK there too. `node --test
packages/conversation/tests/` passes 141/141, 0 skipped.
`node --test packages/control-board/tests/` passes 124/124 (the board
reads `ControlRefusal` codes). The CHAT-00, CHAT-01 and CHAT-01c checks
still pass.
## B1 (blocking). The seal is a deny-list over an argv the caller builds
`checkSeal` (pi-pin.mjs 59–66) refuses `-e`/`--extension`, a missing seal
flag, or a first pair other than `--mode rpc`. Everything else in
`engine.extraArgs` passes, and `buildPiArgs` appends extraArgs after the
controller's own `--session`. Pi's parser keeps the last `--mode` and the
last `--session`. The controller is a public export (`./controller` in
package.json), so this is reachable without touching the source.
(a) `extraArgs: ["--session", <file outside the fixture root>]`. The guard
refuses that same path when it is passed as `sessionFile`, but it never
sees extraArgs. With the real pinned Pi under a scratch HOME and agent dir
(`/tmp/r5/seal-escape.mjs session`):
```
guard on sessionFile: live-session-refused
constructed with extraArgs ["--session",".../outside/proj/.pi/state/other/sessions/s1.jsonl"]
-> piArgs ["--mode","rpc","--no-extensions","--no-prompt-templates","--no-themes",
"--session",".../fx/.../fixture-seat/sessions/s1.jsonl","--session",".../outside/..."]
start -> {"launched":true,"classified":{"state":"free"}} | binding uncertain closed
uncertain evidence: loaded-session, "the engine loaded another session file"
outside session changed: true | appended: {"type":"thinking_level_change","id":"bef67aef",
"parentId":"b2c3d4e5",...,"thinkingLevel":"off"}
```
K8 notices afterwards, but Pi has already written to a session the guard
exists to protect. That breaks §2 "no writes to sessions" and the
fixture-only rule in code.
(b) `extraArgs: ["--mode", "json"]` (or `text`). checkSeal passes because
it looks only at args[0] and args[1]. Pi starts in print mode, reads stdin
to EOF and treats it as the prompt. The K8 `get_state` line goes into that
reader, so the run ends `uncertain` on `get_state: timeout`. In an earlier
run where I closed stdin, Pi sent the `get_state` JSON line to the model
as a prompt and stopped only at "No API key found". The default
`engine.env` is `process.env`, so with real auth present that becomes a
paid model call outside RPC. PgroupLauncher spawns detached, so the engine
outlives the controller unless someone kills the group.
Other extraArgs that pass checkSeal today: `--no-session`, `--fork`,
`--export <file>` (writes a file), `--prompt-template`, `--approve`.
`engine.command` and `engine.preArgs` can also run any script, or node
with `--import`. `checkEnginePin` validates only the pinRoot lock files, so
the pin says nothing about what actually ran.
Suggested fix:
- Allow-list extraArgs. The only non-extension use in the suites is
`["--model", "other"]` (claim.test 548, W9), so `--model`, `--provider`
and `--thinking` with one value each would cover it.
- Refuse any second `--mode`, `--session` or seal flag, and any session
or output flag (`--print`, `--no-session`, `--session-dir`,
`--session-id`, `--fork`, `--export`, `--continue`,
`--resume`).
- Say in the README that a non-default `command` or `preArgs` is a test
hook, and that the pin and seal checks don't bind under it. Or refuse it
outside tests.
- N24 cases for `--session`, `--mode json` and `--no-session` in
extraArgs.
## B2 (blocking). The claim's session key is the conversation ID
controller.mjs 187:
`this.sessionK = sessionKey({ harness: "pi", conversation: this.conversation })`.
`conversation` is `"pi-" + sha256(projectRoot, seat, name)` (reader.mjs
72). The brief (line 382–383) keys the claim on "the native session
identity (the Pi header ID or the Claude session UUID)", and the CHAT-01
README says at most one non-stopped binding may hold a conversation or
native-session identity. Two paths to one session file give two
conversation IDs, so the session key never collides.
Repro, `/tmp/r5/hardlink.mjs`: one session hard-linked into
`.pi/state/fixture-seat/sessions/s1.jsonl` and
`.pi/state/seat-b/sessions/s1.jsonl`, two controllers via the test harness
with the fake engine.
```
same inode: true
A conversation pi-b5901a69... | B conversation pi-f21f1a75...
A start: {"launched":true,...} state active
B start: {"launched":true,...} state active
A nativeSession 0f5e1c2a-1111-4222-8333-944455556666 | B nativeSession 0f5e1c2a-1111-4222-8333-944455556666
```
Two active bindings, two engines, one session. A plain copy is allowed
too; whether a copy should count as the same session is a call for the
brief, but the hard link plainly should. W4 (claim.test 172) builds its
keys by hand on the store, so it never exercises the controller's
mapping.
Fix: build the session key from the header `id` read in `start()` (the
`nativeSession` already in hand). Add a controller-level W4 case for the
hard link, and one for a copy with whatever the brief decides.
## n1 (non-blocking). The startup-append note is narrower than Pi's rule
README 426–431 and BUILD-I1.md 196 say the append bites only sessions
without a `thinking_level_change` entry, and that Pi-created sessions
carry one. Pi's rule is sdk.js 82:
`hasExistingSession = existingSession.messages.length > 0`. A session
with a thinking entry but no messages takes the new-session branch and
appends `thinking_level_change` at every start, plus `model_change` when a
model is set. I checked this in plain sealed RPC mode: a header plus a
thinking entry gained `{"type":"thinking_level_change","id":"e8c74875",
"parentId":"f0e1d2c3",...}`. A Pi-created session that was opened and
never prompted is in this group, so "written by something else" is wrong.
Name message-less sessions too, and let the later pre-spawn check cover
both.
## n2 (minor). The guard follows $HOME
`LiveSessionGuard` protects `~/.pi`, `~/.claude` and `~/.mosaic-dev` via
`os.homedir()`. With a scratch HOME, the real directories are protected
only by the fixture-root containment, or by passing `homes`. That
containment holds today, so I'm noting it, not blocking on it.
## What holds in the binding
- H1 to H3. `#dispatch` holds `this.lock` across `#recheck` and the
write. Takeover, release and acquire run `#evaluate` under the same
lock, so a prompt can't land between the generation bump and the write.
- The generation check comes first in `#evaluateOp` and again in
`#recheck`.
- Takeover refuses while fenced (K9) and for the current controller
(`already-controller`, H4).
- Disconnect never moves control (H11).
- Confirmations are single-use: `#checkConfirmation` marks them
`consumed`.
- Interrupt sets its fence before it takes the lock, so a queued prompt
sees the fence.
- K8 catches a wrong session or leaf after launch, as in B1(a). B1 is that
the write happens before K8 can run.
- The pin check reads both lock files and refuses on either version or
integrity mismatch.
Scratch scripts and outputs are in `/tmp/r5/`: `seal-escape.mjs`,
`hardlink.mjs`, `seal-session.txt`, `seal-json.txt`, `hardlink.txt`,
`conv-suite.txt`, `board-suite.txt`. All scratch engines were killed by
their process group.
@@ -0,0 +1,119 @@
# Queue row 5, CHAT-03 increment 1, round 2 review (#1507)
Darkwing, 2026-10-04. Request: #1507 comment 26689 (Dewey). My part is
the binding and the extension-load refusal (lead decisions 31 and 36), and
my round 1 findings (comment 26681 on #1508, pointer 26685 on #1507).
Candidate: `agents/dewey/work/chat-03/I1-r2-manifest.sha256`, digest
`2b48e333a0f09185364359ae6f8277cc88c0b9ff39058de45cc2c1f0ec9d5c4a`,
the same 27 files, base 1c724958, uncommitted. Packet:
`agents/dewey/work/chat-03/BUILD-I1-r2.md`.
Verdict: approved for my part. B1 and B2 are fixed. Nothing must be fixed
before the commit. Three follow-ups below, none of them blocking.
## Checks
- The manifest hashes to 2b48e333. All 27 files match it in the canonical
working tree. Twelve changed from round 1: README, `controller.mjs`,
`pi-pin.mjs`, `terminal.mjs` and seven test files. `engine.mjs` is
unchanged.
- Export: `git archive` of 1c724958 plus the 27 files in
`~/darkwing-scratch/r5r2/exp`, manifest OK there too, `TMPDIR` under the
same directory.
- `node --test packages/conversation/tests/` passes 152/152, 0 skipped.
- `node --test packages/control-board/tests/` passes 124/124. The board
imports the conversation package.
- The CHAT-00 (48 checks), CHAT-01 R3 and CHAT-01c checks still pass.
- Probe script: `~/darkwing-scratch/r5r2/probes.mjs`, output in
`~/darkwing-scratch/r5r2/out/probes.txt`. It drives the exported
`Controller` directly, as an outside caller would.
## B1, the seal is now an allow-list: fixed
`checkSeal` (pi-pin.mjs 68) requires the exact prefix `--mode rpc`, the
seal flags and `--session <absolute path>`, then accepts only `--model`,
`--provider` and `--thinking`, each once, each with one value that is
nonempty and doesn't start with `-` or `@`. The constructor refuses a
non-list `preArgs` or `extraArgs` and runs the seal before anything else
happens (controller.mjs 170–174), so a refused argv never reaches a spawn.
I passed 24 `extraArgs` lists to the constructor. These all refuse
`unsealed-engine`:
- my round 1 vectors: `--session <outside file>`, `--mode json`,
`--mode text`, `--no-session`, `--fork <file>`, `--export <file>`;
- `--print`, `-p hi`, a bare word, `@<file>`, `--approve`,
`--no-extensions`, `--extension x`, `-e x`;
- `--model=x`, `--model` with no value, an empty value, a value of `-p`
or `@f`, `--model` twice, and `--model a hello`.
These build: `--model a --thinking high --provider p`, `--thinking high`,
and `--model "rpc --mode json"`. The last one is a single argv element with
no shell in between, so Pi sees it as a model name. That's fine.
Dewey's mutants r2-B1 to r2-B1e (no extraArgs check, no prefix order, a
relative session path, a repeat, a flag or `@` value) are all killed by
N24.
## B2, the session key is the Pi header ID: fixed
The constructor reads the session header and keys the claim on
`sessionKey({ harness: "pi", nativeSession })` (controller.mjs 193–194).
I ran two controllers, seat A on the fixture session and seat B on a
second file under another seat directory:
| Second file | A | B | Keys equal |
|---|---|---|---|
| hard link of A's file | active, 1 launch | refused `already-active`, 0 launches | yes |
| copy of A's file | active, 1 launch | refused `already-active`, 0 launches | yes |
| copy with a different header ID | active, 1 launch | active, 1 launch | no |
So the fix doesn't over-collide: two real sessions still start side by
side. I also replaced the header ID by rename after construction. `start()`
refused `target` with no launch. Mutants r2-B2 and r2-B2b are killed by
the new W4 cases.
n1 (Pi's startup-append rule) and n2 (the guard follows `$HOME`) are fixed
in the README as asked.
## What the rework touched that I checked
- `#readSession` runs after the guard check at construction and wraps an
unreadable file as `configuration`. Good.
- The force-stop path now refuses `fenced` while an escalation runs
(controller.mjs 683). `#forceStop` holds `escalating` and clears it in
`finally` (1453–1459). See F2 for the one gap.
- `stopLink` needs `abortWritten`, and there's an overlap recheck after the
pause before the abort. Mutants r2-n1 and r2-n2 are killed. This is
outside my part, so I note it without a ruling.
- Mutant r2-B5b (the shim writes the freeze but doesn't wait for
`frozen 1`) survives. That is Filbert's finding and Dewey explains it in
the packet. I leave it to Filbert.
## Follow-ups (none must be fixed before the commit)
- **F1. `engine.command` and `engine.preArgs` are outside the seal.** The
README documents them as a test hook, as I asked in round 1, and
`preArgs` still refuses `--extension`. But `preArgs:
[<pinned cli.js>, "--no-session"]` builds. Everything after `cli.js`
reaches Pi's parser, so a flag there that the seal doesn't repeat later,
such as `--no-session` or `--export`, takes effect. A non-default
`command` can run anything. No caller passes them today: the package
has no entry point that builds a `Controller` from configuration. Before I3 adds one, either
refuse any non-default `command`/`preArgs` outside the test harness, or
have that entry point never accept them from config. Owner: whoever
builds the I3 entry point.
- **F2. `escalating` is set before `after()` runs.** The force-stop branch
of `#evaluate` sets `this.escalating` at controller.mjs 686, then calls
`#admission()` and `link.poison()`. If either throws, `#handle` turns the
result into an internal error and drops `after`. `#forceStop` never runs,
so the flag is never cleared, and every later force stop on that
controller refuses `fenced`. Recovery then needs a controller restart.
Neither call is expected to throw, so the risk is low. Fix: set the flag
only in `#forceStop`, or clear it in the catch path when `after` is
dropped.
- **F3. `engine.env` defaults to `process.env`** (controller.mjs 168). A
real Pi then inherits the caller's whole environment, including
provider keys and the real `HOME`, so it reads the real `~/.pi/agent`.
This is unchanged from round 1 and fine for I1, where tests pass their
own env. The I3 entry point should build the engine env from an explicit
list.
@@ -0,0 +1 @@
fd10b62c10bff4736b9b4e809b283fe4c5b549fa53fe1121a7bb06258147f0e9 packages/conversation/tests/fake-pi.mjs
+152
View File
@@ -0,0 +1,152 @@
# Row 46: K1, K3 and K10 on the scope fixtures
Darkwing, 2026-10-09. Issue #1528, reviewer Dewey. Brief:
`docs/plans/2026-10-09_s4-follow-up-and-cohort.md`, section "Conversation
cohort: K1, K3 and K10 fail on the scope fixtures". Ruling: lead decision 72.
The candidate is not committed, staged or pushed.
## Cause
It's a race in the test fixture. Neither the host's systemd setup nor
`cohort.mjs` is at fault.
`spawnChild` in `packages/conversation/tests/fake-pi.mjs` returns the
child's pid as soon as `spawn()` returns. The child is `node -e`, and it
installs its SIGTERM handler only after Node has booted. That takes 16 to
22 ms on an idle host (`trace-pass.jsonl`) and 43 to 80 ms under 48 CPU burners (`trace-load.jsonl`). A
TERM that arrives before the handler gets the default action, and the child
dies of SIGTERM. Each failing assertion is that death seen from a different
place:
- K1, "the escaped child is a listed member". The child died during the
TERM grace, so the freeze-phase enumeration doesn't list it.
- K3, "the member ignored TERM". `alive(child)` is false at the kill
phase.
- K10, "the member is alive across the crash". Same as K3, before the
restart.
The time between spawn and TERM depends on the disk under TMPDIR. The
fixture puts the claim store there. Before it sends TERM, the controller
publishes claim revisions, each with an fsync on the file and one on the
directory (`packages/conversation/src/claim.mjs:159` and `:176`).
| TMPDIR | Disk | write+fsync median (`fsync.txt`) | spawn to TERM | Unfixed K1/K3/K10 |
|---|---|---|---|---|
| `/mnt/storage/scratch/tmp` | nvme0, ext4 | 0.65 ms | 12 to 13 ms (`trace-fail.jsonl`) | 0/3 (`runs/canon-scratchtmp.txt`) |
| `/tmp` | nvme1, ext4 | 4.63 ms | not traced | 3/3 (`runs/canon-tmp.txt`) |
| `~/darkwing-scratch/tmp` | nvme1, ext4 | 4.69 ms | 53 to 110 ms (`trace-pass.jsonl`) | 3/3 (`runs/canon-hometmp.txt`) |
On the fast disk, TERM lands about 12 ms after spawn, before Node is up.
In `trace-fail.jsonl` the children never log `ready`.
### Why it started failing
This host's seats now get `TMPDIR=/mnt/storage/scratch/tmp`. My shell had
it by default, and so did Sage's gate runs. It comes from Vikunja task 59.
`~/.config/systemd/user/t3code.service.d/tmpdir.conf` was written
2026-10-04 20:33Z. Its own comment says it takes effect only when
`t3code.service` restarts, which hasn't happened (active since 2026-09-23),
and that until then seats get TMPDIR from their harness config. I didn't
establish what TMPDIR the row 44 gate ran with on 2026-10-05, so the
"since when" is likely but unproven. The change that exposed the race is a
seat's TMPDIR. It isn't a systemd or user manager setting, and I changed
nothing on the host.
### The scope is not a factor
Outside any scope, `race.mjs` gives the same split: a TERM 0, 10 or 20 ms
after spawn kills 10/10, and at 30 or 60 ms 10/10 survive (`race.txt`).
Sage's outside-scope probe found the child surviving. I haven't seen that
probe, but it most likely sent TERM after the handler was in place.
## Receipts in a scope
`scope-receipt.mjs` launches a delegated scope per run, with the same
`systemd-run` flags `ScopeLauncher` uses. The scope's main process spawns
the fixture's `ignoreTerm` child and records `systemctl --user show` on the
scope, then `cgroup.procs` before and after TERM, then the child's exit.
Output: `receipts.jsonl`.
| Mode | TERM after spawn | In `cgroup.procs` before | After | Child exit |
|---|---|---|---|---|
| `race 12` | 20 to 25 ms | 5/5 | 1/5 | 4/5 `signal: "SIGTERM"`, 1 alive |
| `race 60` | 69 to 71 ms | 5/5 | 5/5 | 5/5 alive 500 ms after TERM |
| `ready 0` | 28 to 38 ms (ready at 22 to 30) | 5/5 | 5/5 | 5/5 alive 500 ms after TERM |
Each line also carries the scope's `Id`, `LoadState=loaded`,
`ActiveState=active`, `InvocationID`, `ControlGroup` and `Delegate=yes`.
The first line, for example:
`ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-0.scope`,
`before [3662760, 3662788]`, `after [3662760]`, child exit
`{"code":null,"signal":"SIGTERM","atMs":24}`.
`trace.patch` is the scratch-only instrumentation behind the two traces.
It logs spawn, ready and the shim's `term` per pid to `$DW_TRACE`. It is
not part of the candidate.
## The fix
`build.patch` changes one file, `packages/conversation/tests/fake-pi.mjs`:
- The child writes one byte to stdout as its first action after installing
its TERM handler. A child without `ignoreTerm` writes it once its code
starts.
- `spawnChild` returns a promise. It resolves with the pid on that byte,
then closes its end of the pipe. It rejects if the child exits or errors
first, or if 10 s pass.
- The `child` control op returns that promise. The op dispatcher now
answers `ok: false` when an op's promise rejects. Before, a rejected op
promise went unhandled, and under Node's default that ends fake-pi. The
change covers every op that returns a promise, not only `child`.
`childOf` in `cohort.test.mjs` already asserts `r.ok`.
No assertion changed, and `cohort.mjs` is untouched. The three cases now
test what their names say: TERM reaches a child that is already ignoring
TERM.
`build-manifest.sha256` (sha256
`9228414352a56e0465e896b81643cdce000bcedba70f86adac07fc50cfadad2d`) pins
`fake-pi.mjs` after the patch. `build.patch` sha256 is
`04234ce1d886e36dfd06cbd81153b38cbb86909b6ee933151306b8cdedd629d9`. In a
fresh worktree at `521597bb` the patch applies and the manifest checks 1/1.
## Mutants
Both ran with `TMPDIR=/mnt/storage/scratch/tmp`, the condition that fails.
| Mutant | K1/K3/K10 | File |
|---|---|---|
| M1: resolve at spawn, no wait (the old behavior) | 0/3 | `runs/mutant-m1-nowait.txt` |
| M2: wait for ready, but the child has no TERM handler | 0/3, the same three assertions | `runs/mutant-m2-noignore.txt` |
M1 shows the wait is what fixes it. M2 shows the assertions still catch a
child that dies on TERM.
## Runs
The patch was applied in a scratch worktree at `521597bb`. Node v26.8.1.
Start time and load are in `runs/start.txt`.
| Run | TMPDIR | Result | File |
|---|---|---|---|
| `node --test "packages/conversation/tests/*.test.mjs"` | `/mnt/storage/scratch/tmp` | 152/152 | `runs/node-conversation.txt` |
| `node --test "packages/webui/tests/*.test.mjs"` | `/mnt/storage/scratch/tmp` | 14/14 | `runs/node-webui.txt` |
| K1/K3/K10 isolated, 3 consecutive | `/mnt/storage/scratch/tmp` | 3/3, 3/3, 3/3 | `runs/k-iso-{1,2,3}.txt` |
| K1/K3/K10 isolated | `/tmp` | 3/3 | `runs/k-tmp.txt` |
| K1/K3/K10 isolated | `~/darkwing-scratch/tmp` | 3/3 | `runs/k-home.txt` |
| K1/K3/K10 isolated under 48 CPU burners, 3 runs (load 13.6 to 24.9) | `/mnt/storage/scratch/tmp` | 3/3, 3/3, 3/3 | `runs/k-load48-{1,2,3}.txt` |
For the gate rerun, keep the default `TMPDIR=/mnt/storage/scratch/tmp`.
That's the condition that failed, and a slower TMPDIR would pass with or
without the patch.
## Files
- `build.md`, this file.
- `build.patch`, `build-manifest.sha256`: the candidate.
- `scope-receipt.mjs`, `receipts.jsonl`: the scope receipts.
- `race.mjs`, `race.txt`: TERM timing outside a scope.
- `fsync.mjs`, `fsync.txt`: write+fsync latency per TMPDIR.
- `trace.patch`, `trace-fail.jsonl`, `trace-pass.jsonl`, `trace-load.jsonl`: the traced runs.
- `runs/`: suite, isolated, load and mutant outputs, and the unfixed
canonical tree under three TMPDIRs.
@@ -0,0 +1,67 @@
diff --git a/packages/conversation/tests/fake-pi.mjs b/packages/conversation/tests/fake-pi.mjs
index b3014f86..a78dfebd 100644
--- a/packages/conversation/tests/fake-pi.mjs
+++ b/packages/conversation/tests/fake-pi.mjs
@@ -615,14 +615,34 @@ const children = [];
// K2); `forkLoop` forks every 5 ms (K12); `ignoreTerm` survives SIGTERM, so
// only the kill phase ends it (K3, K10, K11). With `pidLog`, the fork loop
// appends each child's pid and a `term` line when it gets SIGTERM (K12).
+//
+// It resolves only once the child writes its ready byte, which it does after
+// installing its TERM handler. Node takes 15 to 80 ms to get there, and a
+// force stop can send TERM sooner (12 ms when the claim store is on a fast
+// disk). A TERM before the handler kills a child that is meant to ignore it
+// (#1528).
function spawnChild({ setsid = false, forkLoop = false, ignoreTerm = false, pidLog = null } = {}) {
const note = pidLog ? `const note=(s)=>require('node:fs').appendFileSync(${JSON.stringify(pidLog)},s+'\\n');` : "const note=()=>{};";
- const code = note + (ignoreTerm ? "process.on('SIGTERM',()=>note('term'));" : "") + (forkLoop
+ const code = note + (ignoreTerm ? "process.on('SIGTERM',()=>note('term'));" : "") + "process.stdout.write('r');" + (forkLoop
? "const {spawn}=require('node:child_process');setInterval(()=>{try{const c=spawn('sleep',['1000'],{stdio:'ignore'});if(c.pid)note(String(c.pid))}catch{}},5);setInterval(()=>{},1e9)"
: "setInterval(()=>{},1e9)");
- const child = spawn(process.execPath, ["-e", code], { stdio: "ignore", detached: setsid });
+ const child = spawn(process.execPath, ["-e", code], { stdio: ["ignore", "pipe", "ignore"], detached: setsid });
children.push(child.pid);
- return child.pid;
+ return new Promise((resolve, reject) => {
+ const fail = (why) => {
+ clearTimeout(timer);
+ reject(new Error(`tool child ${child.pid} ${why} before it was ready`));
+ };
+ const timer = setTimeout(() => fail("took 10 s"), 10000);
+ child.once("error", (err) => fail(err.code ?? err.message));
+ child.once("exit", (code, signal) => fail(`exited (${signal ?? code})`));
+ child.stdout.once("data", () => {
+ clearTimeout(timer);
+ child.removeAllListeners("exit");
+ child.stdout.destroy();
+ resolve(child.pid);
+ });
+ });
}
// K13: a member writes its own pid to another cgroup's cgroup.procs.
@@ -671,7 +691,7 @@ async function main() {
drop: () => fake.dropResponse(req.type, req.n ?? 1),
extension: () => void fake.extensionPrompt(req.args ?? {}),
state: () => ({ streaming: fake.streaming, runs: fake.runs.length, commands: fake.commands, pid: process.pid, children, appends: fake.appends }),
- child: () => ({ pid: spawnChild(req.args ?? {}) }),
+ child: () => spawnChild(req.args ?? {}).then((pid) => ({ pid })),
escape: () => escape(req.target),
cgroup: () => readFileSync(`/proc/${req.pid ?? process.pid}/cgroup`, "utf8"),
waitPaused: () => fake.waitPaused(req.point),
@@ -679,12 +699,13 @@ async function main() {
stall: () => void process.stdin.pause(),
};
if (!ops[req.op]) return sock.write(encodeLine({ id: req.id, ok: false, error: `unknown op ${req.op}` }));
+ const failed = (err) => sock.write(encodeLine({ id: req.id, ok: false, error: String(err.message) }));
try {
const out = ops[req.op]();
- if (out && typeof out.then === "function") out.then(reply);
+ if (out && typeof out.then === "function") out.then(reply, failed);
else reply(out ?? null);
} catch (err) {
- sock.write(encodeLine({ id: req.id, ok: false, error: String(err.message) }));
+ failed(err);
}
});
sock.on("data", (c) => splitter.push(c));
+17
View File
@@ -0,0 +1,17 @@
import { openSync, writeSync, fsyncSync, closeSync, rmSync, mkdtempSync } from "node:fs";
import { join } from "node:path";
for (const base of process.argv.slice(2)) {
const d = mkdtempSync(join(base, "dw-fsync-"));
const t = [];
for (let i = 0; i < 30; i++) {
const s = performance.now();
const fd = openSync(join(d, `f${i}`), "w");
writeSync(fd, "x".repeat(512));
fsyncSync(fd);
closeSync(fd);
t.push(performance.now() - s);
}
rmSync(d, { recursive: true });
t.sort((a, b) => a - b);
console.log(`${base}: write+fsync median ${t[15].toFixed(2)} ms, p90 ${t[27].toFixed(2)} ms`);
}
+3
View File
@@ -0,0 +1,3 @@
/mnt/storage/scratch/tmp: write+fsync median 0.65 ms, p90 0.79 ms
/tmp: write+fsync median 4.63 ms, p90 4.93 ms
/home/jwoltje/darkwing-scratch/tmp: write+fsync median 4.69 ms, p90 9.30 ms
+17
View File
@@ -0,0 +1,17 @@
// Spawn the K fixtures' ignoreTerm child exactly as fake-pi.mjs spawnChild does, then
// SIGTERM it after a delay. Reports how the child ended within 1 s.
import { spawn } from "node:child_process";
const code = "const note=()=>{};process.on('SIGTERM',()=>note('term'));setInterval(()=>{},1e9)";
const delays = process.argv.slice(2).map(Number);
for (const d of delays) {
const r = { died: 0, survived: 0 };
for (let i = 0; i < 10; i++) {
const c = spawn(process.execPath, ["-e", code], { stdio: "ignore" });
const ended = new Promise((res) => c.on("exit", (code, sig) => res(sig ?? code)));
await new Promise((res) => setTimeout(res, d));
c.kill("SIGTERM");
const out = await Promise.race([ended, new Promise((res) => setTimeout(() => res(null), 1000))]);
if (out === null) { r.survived++; c.kill("SIGKILL"); await ended; } else r.died++;
}
console.log(`TERM ${d} ms after spawn: died ${r.died}/10 survived ${r.survived}/10`);
}
+5
View File
@@ -0,0 +1,5 @@
TERM 0 ms after spawn: died 10/10 survived 0/10
TERM 10 ms after spawn: died 10/10 survived 0/10
TERM 20 ms after spawn: died 10/10 survived 0/10
TERM 30 ms after spawn: died 0/10 survived 10/10
TERM 60 ms after spawn: died 0/10 survived 10/10
@@ -0,0 +1,15 @@
{"mode":"race","delayMs":12,"readyMs":null,"termMs":23,"child":3662788,"self":3662760,"show":["Id=dw-r46-race-12-3662746-0.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=2eac4fa367504b9dbca66a4693cc328a","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-0.scope","Delegate=yes"],"before":[3662760,3662788],"after":[3662760],"childInBefore":true,"childInAfter":false,"exit":{"code":null,"signal":"SIGTERM","atMs":24}}
{"mode":"race","delayMs":12,"readyMs":null,"termMs":20,"child":3663479,"self":3663212,"show":["Id=dw-r46-race-12-3662746-1.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=021c6d466fc64961ba3031a0254c3803","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-1.scope","Delegate=yes"],"before":[3663212,3663479],"after":[3663212],"childInBefore":true,"childInAfter":false,"exit":{"code":null,"signal":"SIGTERM","atMs":21}}
{"mode":"race","delayMs":12,"readyMs":null,"termMs":25,"child":3663799,"self":3663588,"show":["Id=dw-r46-race-12-3662746-2.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=ce8e2c2dbbe7481885a50152fe3766b5","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-2.scope","Delegate=yes"],"before":[3663588,3663799],"after":[3663588],"childInBefore":true,"childInAfter":false,"exit":{"code":null,"signal":"SIGTERM","atMs":27}}
{"mode":"race","delayMs":12,"readyMs":null,"termMs":20,"child":3664563,"self":3664322,"show":["Id=dw-r46-race-12-3662746-3.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=cc912ab9a7fe47dd9e3b97486abf0bc3","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-3.scope","Delegate=yes"],"before":[3664322,3664563],"after":[3664322,3664563],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"race","delayMs":12,"readyMs":null,"termMs":21,"child":3665177,"self":3665003,"show":["Id=dw-r46-race-12-3662746-4.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=163271c658cc4ee4b84f5aace54bc806","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-12-3662746-4.scope","Delegate=yes"],"before":[3665003,3665177],"after":[3665003],"childInBefore":true,"childInAfter":false,"exit":{"code":null,"signal":"SIGTERM","atMs":22}}
{"mode":"race","delayMs":60,"readyMs":null,"termMs":69,"child":3665812,"self":3665626,"show":["Id=dw-r46-race-60-3665599-0.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=14681de5ac844879bdfcbcdd3dae8f10","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-60-3665599-0.scope","Delegate=yes"],"before":[3665626,3665812],"after":[3665626,3665812],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"race","delayMs":60,"readyMs":null,"termMs":71,"child":3666377,"self":3666179,"show":["Id=dw-r46-race-60-3665599-1.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=3128eba3ed234776ae4a61cb12fe1210","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-60-3665599-1.scope","Delegate=yes"],"before":[3666179,3666377],"after":[3666179,3666377],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"race","delayMs":60,"readyMs":null,"termMs":70,"child":3666924,"self":3666728,"show":["Id=dw-r46-race-60-3665599-2.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=836de499898d4b6996739e1b2a433fa0","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-60-3665599-2.scope","Delegate=yes"],"before":[3666728,3666924],"after":[3666728,3666924],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"race","delayMs":60,"readyMs":null,"termMs":69,"child":3667387,"self":3667251,"show":["Id=dw-r46-race-60-3665599-3.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=0ebf8d9d930a43629668e10c08f80e0d","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-60-3665599-3.scope","Delegate=yes"],"before":[3667251,3667387],"after":[3667251,3667387],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"race","delayMs":60,"readyMs":null,"termMs":70,"child":3667888,"self":3667727,"show":["Id=dw-r46-race-60-3665599-4.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=9c3594ae3018458594bb3ff996dc0d9a","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-race-60-3665599-4.scope","Delegate=yes"],"before":[3667727,3667888],"after":[3667727,3667888],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"ready","delayMs":0,"readyMs":30,"termMs":38,"child":3668171,"self":3668085,"show":["Id=dw-r46-ready-0-3668072-0.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=4c2c3b3ad52a477e8f735abedf158cbb","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-ready-0-3668072-0.scope","Delegate=yes"],"before":[3668085,3668171],"after":[3668085,3668171],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"ready","delayMs":0,"readyMs":22,"termMs":29,"child":3668354,"self":3668284,"show":["Id=dw-r46-ready-0-3668072-1.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=b9da06280a8a4c6682f05a2d4ff118cf","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-ready-0-3668072-1.scope","Delegate=yes"],"before":[3668284,3668354],"after":[3668284,3668354],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"ready","delayMs":0,"readyMs":22,"termMs":28,"child":3668425,"self":3668417,"show":["Id=dw-r46-ready-0-3668072-2.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=aab0b83d085e4bac86fdbe1ce65f23dc","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-ready-0-3668072-2.scope","Delegate=yes"],"before":[3668417,3668425],"after":[3668417,3668425],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"ready","delayMs":0,"readyMs":23,"termMs":30,"child":3668509,"self":3668482,"show":["Id=dw-r46-ready-0-3668072-3.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=d076a3b629554b83bded41cb00e667b8","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-ready-0-3668072-3.scope","Delegate=yes"],"before":[3668482,3668509],"after":[3668482,3668509],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
{"mode":"ready","delayMs":0,"readyMs":23,"termMs":30,"child":3668635,"self":3668624,"show":["Id=dw-r46-ready-0-3668072-4.scope","LoadState=loaded","ActiveState=active","SubState=running","InvocationID=6b46a0b279ca454eaa034d48a4de7385","ControlGroup=/user.slice/user-1000.slice/[email protected]/app.slice/dw-r46-ready-0-3668072-4.scope","Delegate=yes"],"before":[3668624,3668635],"after":[3668624,3668635],"childInBefore":true,"childInAfter":true,"exit":"alive 500 ms after TERM"}
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2737.880671ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2557.042479ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (861.841994ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 6282.04986
@@ -0,0 +1,56 @@
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2537.385038ms)
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2331.351365ms)
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (209.932151ms)
ℹ tests 3
ℹ suites 0
ℹ pass 0
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5448.48373
✖ failing tests:
test at packages/conversation/tests/cohort.test.mjs:139:1
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2537.385038ms)
AssertionError [ERR_ASSERTION]: the escaped child is a listed member
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack/packages/conversation/tests/cohort.test.mjs:151:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at packages/conversation/tests/cohort.test.mjs:176:1
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2331.351365ms)
AssertionError [ERR_ASSERTION]: the member ignored TERM
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack/packages/conversation/tests/cohort.test.mjs:190:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at packages/conversation/tests/cohort.test.mjs:426:3
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (209.932151ms)
AssertionError [ERR_ASSERTION]: the member is alive across the crash
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack/packages/conversation/tests/cohort.test.mjs:431:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2360.366213ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2364.75763ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (628.04725ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5474.064409
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2457.36226ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2375.081486ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (636.350971ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5607.100296
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2665.443725ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2531.922418ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (706.098022ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 6068.030335
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2699.60543ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2492.169051ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (675.081525ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 6026.119198
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2762.539396ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2528.214494ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (644.633095ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 6115.728995
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2887.135899ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2915.708256ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (2590.181146ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 9662.912642
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3084.73509ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2750.8295ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (2151.224665ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 8355.450647
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2746.07054ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2559.848734ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1985.055632ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 7722.015242
@@ -0,0 +1,11 @@
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2785.421246ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2619.007617ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (889.082424ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 6776.303473
@@ -0,0 +1,56 @@
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2523.134497ms)
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2421.121319ms)
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (211.187938ms)
ℹ tests 3
ℹ suites 0
ℹ pass 0
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5461.571354
✖ failing tests:
test at cohort.test.mjs:139:1
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2523.134497ms)
AssertionError [ERR_ASSERTION]: the escaped child is a listed member
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:151:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at cohort.test.mjs:176:1
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2421.121319ms)
AssertionError [ERR_ASSERTION]: the member ignored TERM
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:190:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at cohort.test.mjs:426:3
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (211.187938ms)
AssertionError [ERR_ASSERTION]: the member is alive across the crash
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:431:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,56 @@
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2545.15719ms)
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2407.231229ms)
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (304.986006ms)
ℹ tests 3
ℹ suites 0
ℹ pass 0
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5605.846956
✖ failing tests:
test at cohort.test.mjs:139:1
✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2545.15719ms)
AssertionError [ERR_ASSERTION]: the escaped child is a listed member
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:151:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at cohort.test.mjs:176:1
✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2407.231229ms)
AssertionError [ERR_ASSERTION]: the member ignored TERM
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:190:12)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at cohort.test.mjs:426:3
✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (304.986006ms)
AssertionError [ERR_ASSERTION]: the member is alive across the crash
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r46/fix/packages/conversation/tests/cohort.test.mjs:431:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
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: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,160 @@
✔ W1: two processes acquire the same pair at once; exactly one claim (124.186492ms)
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (11.494379ms)
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (5.019078ms)
✔ W2: acquire while a claim is reserved or active refuses already-active (169.129732ms)
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (231.756886ms)
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (192.859955ms)
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (35.003128ms)
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (21.132326ms)
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.242382ms)
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (5566.910492ms)
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (22659.492505ms)
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (201.806859ms)
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (116.242029ms)
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (262.226787ms)
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (220.425841ms)
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (240.127173ms)
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (35.945246ms)
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (98.424488ms)
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (32.141383ms)
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (89.672997ms)
✔ W11: the controller writes no session file; only the fake engine's own appends appear (23.121134ms)
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (54.682806ms)
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (57.719055ms)
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.12463ms)
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.12384ms)
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.84306ms)
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2507.919568ms)
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (153.327228ms)
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2414.676654ms)
✔ K4: two engines; force stop one; the other survives by independent observation (4314.515513ms)
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2190.378919ms)
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2244.796524ms)
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2189.63935ms)
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4473.315326ms)
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (482.430287ms)
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (408.721794ms)
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (307.075836ms)
✔ K6: recover without proof, without confirmation, or with changed pins is refused (71.309069ms)
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (35.403696ms)
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (45.424183ms)
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3030.941578ms)
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (5.325961ms)
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (30.688596ms)
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (24.893734ms)
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (34.176579ms)
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (30.425031ms)
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (27.293519ms)
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (42.046162ms)
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (21.340651ms)
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (1.34914ms)
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (29.201111ms)
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (138.365864ms)
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (39.366805ms)
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (21.443107ms)
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (31.257304ms)
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (26.594189ms)
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.181347ms)
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (26.220793ms)
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (42.195378ms)
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (41.055698ms)
✔ terminal: engine control characters are made visible; a lost connection refuses submit (28.737455ms)
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.53917ms)
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.351376ms)
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.356358ms)
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.168609ms)
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (359.398761ms)
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (56.47649ms)
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (79.112044ms)
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (131.150736ms)
✔ H4: self-takeover is refused (22.440288ms)
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (87.314186ms)
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (104.583327ms)
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (35.050855ms)
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (70.09079ms)
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (129.262166ms)
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (15.147609ms)
✔ H13: a retry with the same request ID and different text is refused (13.200342ms)
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (133.123623ms)
✔ H15: a revoked connection's command is refused; the revocation fence holds (65.378107ms)
✔ H16: a second controller for the same session refuses already-active; the first is untouched (16.417034ms)
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (64.083554ms)
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (56.969236ms)
✔ H18: two prompts before any native output: the second refuses busy; one engine write (15.033626ms)
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (119.397422ms)
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.606652ms)
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (464.542066ms)
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (357.812591ms)
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (337.123938ms)
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2377.836435ms)
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (482.017907ms)
✔ a plain conversation: catalogue row, one page, CHAT-01 records (10.196509ms)
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (3.091521ms)
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.999666ms)
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.746417ms)
✔ F1: an unreadable fork is never merged into another branch's history (3.733154ms)
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.881773ms)
✔ F1: a file whose entries are all unreadable shows a notice per line (1.563335ms)
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.494119ms)
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.109673ms)
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.939351ms)
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.516645ms)
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.793786ms)
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (10.497498ms)
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.313922ms)
✔ F8: a file swapped for a symlink after the catalogue is refused (2.247728ms)
✔ F9: registrations never add or redirect a root (2.060491ms)
✔ F10: a header cwd naming another project is refused (3.630852ms)
✔ F11: parentSession renders with a marker and the parent is never opened (0.936984ms)
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.789444ms)
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.415895ms)
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.349225ms)
✔ F13: compaction is a marker in place, then the retained content (0.756245ms)
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (737.947726ms)
✔ fragments never cut a surrogate pair and keep an empty string (9.768434ms)
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (2.629953ms)
✔ unknown conversations, empty files and non-Pi files refuse (2.988026ms)
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.388415ms)
✔ a seat directory without search permission refuses that root, not the catalogue (2.248255ms)
✔ every page and cursor is a valid CHAT-01 record (847.042088ms)
✔ the engine pin holds for the installed package (2.933293ms)
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (364.994239ms)
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (307.845583ms)
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (80.815781ms)
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (57.148386ms)
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (24.835014ms)
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.576912ms)
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (42.350718ms)
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (37.456929ms)
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (119.71406ms)
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (46.194593ms)
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1530.004862ms)
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (15.860593ms)
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (71.692215ms)
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (16.241237ms)
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (16.048665ms)
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (32.548668ms)
✔ N10: fake conformance (30.957332ms)
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (29.834953ms)
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (19.905915ms)
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (66.348845ms)
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (26.705932ms)
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (25.622164ms)
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (19.804122ms)
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (13.437887ms)
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (28.095743ms)
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (113.466843ms)
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (134.279618ms)
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (83.211293ms)
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (14.999139ms)
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (13.807672ms)
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (13.596018ms)
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (37.274093ms)
ℹ tests 152
ℹ suites 0
ℹ pass 152
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 32488.500969
@@ -0,0 +1,23 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2364.731167ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2725.60223ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2335.250549ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1375.477504ms)
✔ conversation view: a newer session with no readable history keeps the marker (1002.908949ms)
✔ conversation view: seats without history say so and offer no reply (608.42244ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (1957.413637ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53771.943793ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (1987.210959ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21912.250845ms)
✔ loopback host and board origin fail closed (7.055591ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (100.024626ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (227.262245ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1632.267153ms)
ℹ tests 14
ℹ suites 0
ℹ pass 14
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54170.048795
@@ -0,0 +1,2 @@
2026-10-09T13:05:51Z
08:05:51 up 33 days, 9:40, 5 users, load average: 4.03, 7.84, 5.58
@@ -0,0 +1,61 @@
// Row 46 receipt probe. Runs the fixture's ignoreTerm child inside a
// delegated systemd user scope, sends it SIGTERM, and records the scope's
// `systemctl --user show`, `cgroup.procs` before and after TERM, and the
// child's exit code and signal.
//
// node scope-receipt.mjs <mode> <delayMs> <runs>
//
// mode `race`: TERM goes `delayMs` after spawn, as the fixture does today.
// mode `ready`: TERM goes `delayMs` after the child reports its handler is
// installed, as the fixed fixture does.
// The outer process launches one scope per run; the inner process
// (`--inner`) is the scope's main process and prints one JSON line.
import { spawn, spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// The fixture's child code for { ignoreTerm: true } with no pidLog
// (packages/conversation/tests/fake-pi.mjs, spawnChild), plus a readiness
// byte on stdout in `ready` mode.
const childCode = (ready) =>
"const note=()=>{};process.on('SIGTERM',()=>note('term'));" +
(ready ? "process.stdout.write('r');" : "") +
"setInterval(()=>{},1e9)";
const procs = (cg) => readFileSync(`/sys/fs/cgroup${cg}/cgroup.procs`, "utf8").split("\n").filter(Boolean).map(Number);
async function inner(mode, delayMs, unit) {
const cg = readFileSync("/proc/self/cgroup", "utf8").trim().split("::")[1];
const ready = mode === "ready";
const t0 = performance.now();
const child = spawn(process.execPath, ["-e", childCode(ready)], { stdio: ["ignore", ready ? "pipe" : "ignore", "ignore"] });
const exited = new Promise((r) => child.on("exit", (code, signal) => r({ code, signal, atMs: Math.round(performance.now() - t0) })));
let readyMs = null;
if (ready) {
await new Promise((r) => child.stdout.once("data", r));
readyMs = Math.round(performance.now() - t0);
}
await sleep(delayMs);
const show = spawnSync("systemctl", ["--user", "show", "-p", "Id,LoadState,ActiveState,SubState,InvocationID,ControlGroup,Delegate", `${unit}.scope`], { encoding: "utf8" }).stdout.trim().split("\n");
const before = procs(cg);
const termMs = Math.round(performance.now() - t0);
child.kill("SIGTERM");
const exit = await Promise.race([exited, sleep(500).then(() => null)]);
const after = procs(cg);
if (!exit) child.kill("SIGKILL");
console.log(JSON.stringify({ mode, delayMs, readyMs, termMs, child: child.pid, self: process.pid, show, before, after, childInBefore: before.includes(child.pid), childInAfter: after.includes(child.pid), exit: exit ?? "alive 500 ms after TERM" }));
}
async function outer(mode, delayMs, runs) {
for (let i = 0; i < runs; i++) {
const unit = `dw-r46-${mode}-${delayMs}-${process.pid}-${i}`;
const r = spawnSync("systemd-run", ["--user", "--scope", "--quiet", "-p", "Delegate=yes", `--unit=${unit}`, "--", process.execPath, import.meta.filename, "--inner", mode, String(delayMs), unit], { encoding: "utf8" });
process.stdout.write(r.stdout || `{"unit":"${unit}","status":${r.status},"stderr":${JSON.stringify(r.stderr)}}\n`);
}
}
const a = process.argv.slice(2);
if (a[0] === "--inner") await inner(a[1], Number(a[2]), a[3]);
else await outer(a[0], Number(a[1]), Number(a[2] ?? 5));
@@ -0,0 +1,9 @@
{"t":1791551027129,"ev":"spawn","pid":3646877,"ignoreTerm":true,"setsid":true}
{"t":1791551027142,"ev":"term","pid":3646862}
{"t":1791551027142,"ev":"term","pid":3646877}
{"t":1791551029516,"ev":"spawn","pid":3647263,"ignoreTerm":true,"setsid":false}
{"t":1791551029528,"ev":"term","pid":3647248}
{"t":1791551029528,"ev":"term","pid":3647263}
{"t":1791551031873,"ev":"spawn","pid":3647581,"ignoreTerm":true,"setsid":false}
{"t":1791551031886,"ev":"term","pid":3647556}
{"t":1791551031887,"ev":"term","pid":3647581}
@@ -0,0 +1,13 @@
{"t":1791550812775,"ev":"spawn","pid":3607494,"ignoreTerm":true,"setsid":true}
{"t":1791550812853,"ev":"ready","pid":3607494}
{"t":1791550812878,"ev":"term","pid":3607446}
{"t":1791550812878,"ev":"term","pid":3607494}
{"t":1791550815693,"ev":"spawn","pid":3607807,"ignoreTerm":true,"setsid":false}
{"t":1791550815773,"ev":"ready","pid":3607807}
{"t":1791550815779,"ev":"term","pid":3607781}
{"t":1791550815780,"ev":"term","pid":3607807}
{"t":1791550819446,"ev":"spawn","pid":3608344,"ignoreTerm":true,"setsid":false}
{"t":1791550819489,"ev":"ready","pid":3608344}
{"t":1791550819556,"ev":"term","pid":3608301}
{"t":1791550819556,"ev":"term","pid":3608344}
{"t":1791550820058,"ev":"term","pid":3608344}
@@ -0,0 +1,13 @@
{"t":1791550802745,"ev":"spawn","pid":3605642,"ignoreTerm":true,"setsid":true}
{"t":1791550802763,"ev":"ready","pid":3605642}
{"t":1791550802798,"ev":"term","pid":3605597}
{"t":1791550802798,"ev":"term","pid":3605642}
{"t":1791550805434,"ev":"spawn","pid":3606162,"ignoreTerm":true,"setsid":false}
{"t":1791550805456,"ev":"ready","pid":3606162}
{"t":1791550805492,"ev":"term","pid":3606153}
{"t":1791550805492,"ev":"term","pid":3606162}
{"t":1791550808090,"ev":"spawn","pid":3606502,"ignoreTerm":true,"setsid":false}
{"t":1791550808106,"ev":"ready","pid":3606502}
{"t":1791550808199,"ev":"term","pid":3606487}
{"t":1791550808200,"ev":"term","pid":3606502}
{"t":1791550808539,"ev":"term","pid":3606502}
@@ -0,0 +1,43 @@
diff --git a/packages/conversation/src/shim.mjs b/packages/conversation/src/shim.mjs
index 2adbe48b..d51c4a1f 100644
--- a/packages/conversation/src/shim.mjs
+++ b/packages/conversation/src/shim.mjs
@@ -22,7 +22,7 @@
// `populated 0`. A missing or unreadable file is unavailable, never empty.
import { spawn } from "node:child_process";
-import { closeSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
+import { appendFileSync, closeSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
import { createServer } from "node:net";
import { join } from "node:path";
import { LineSplitter, encodeLine, parseLine } from "./framing.mjs";
@@ -136,6 +136,7 @@ async function handle(req) {
if (startOf(pid) !== startTicks) continue;
try {
process.kill(pid, "SIGTERM");
+ if (process.env.DW_TRACE) appendFileSync(process.env.DW_TRACE, JSON.stringify({ t: Date.now(), ev: 'term', pid }) + '\n');
signalled.push(pid);
} catch {
// gone already
diff --git a/packages/conversation/tests/fake-pi.mjs b/packages/conversation/tests/fake-pi.mjs
index b3014f86..f30f5a4c 100644
--- a/packages/conversation/tests/fake-pi.mjs
+++ b/packages/conversation/tests/fake-pi.mjs
@@ -615,13 +615,16 @@ const children = [];
// K2); `forkLoop` forks every 5 ms (K12); `ignoreTerm` survives SIGTERM, so
// only the kill phase ends it (K3, K10, K11). With `pidLog`, the fork loop
// appends each child's pid and a `term` line when it gets SIGTERM (K12).
+import * as __fs from 'node:fs';
+const require0 = () => __fs;
function spawnChild({ setsid = false, forkLoop = false, ignoreTerm = false, pidLog = null } = {}) {
const note = pidLog ? `const note=(s)=>require('node:fs').appendFileSync(${JSON.stringify(pidLog)},s+'\\n');` : "const note=()=>{};";
- const code = note + (ignoreTerm ? "process.on('SIGTERM',()=>note('term'));" : "") + (forkLoop
+ const code = note + (ignoreTerm ? "process.on('SIGTERM',()=>note('term'));" + (process.env.DW_TRACE ? `require('node:fs').appendFileSync(${JSON.stringify(process.env.DW_TRACE)},JSON.stringify({t:Date.now(),ev:'ready',pid:process.pid})+'\\n');` : "") : "") + (forkLoop
? "const {spawn}=require('node:child_process');setInterval(()=>{try{const c=spawn('sleep',['1000'],{stdio:'ignore'});if(c.pid)note(String(c.pid))}catch{}},5);setInterval(()=>{},1e9)"
: "setInterval(()=>{},1e9)");
const child = spawn(process.execPath, ["-e", code], { stdio: "ignore", detached: setsid });
children.push(child.pid);
+ if (process.env.DW_TRACE) { require0().appendFileSync(process.env.DW_TRACE, JSON.stringify({ t: Date.now(), ev: 'spawn', pid: child.pid, ignoreTerm, setsid }) + '\n'); child.on('exit', (code, sig) => require0().appendFileSync(process.env.DW_TRACE, JSON.stringify({ t: Date.now(), ev: 'child-exit', pid: child.pid, code, sig }) + '\n')); }
return child.pid;
}
@@ -0,0 +1,28 @@
# Independent acceptance checklist, row 18
Darkwing reviews Filbert's implementation without editing its source candidate.
Dewey reviews visible connector presentation. No live connector manipulation.
- Discovery accepts only safe matching binding name/seat from private regular
files, never dereferences a token path and never serializes private fields.
- Path traversal, symlinked binding/runtime/session paths and malformed records
cannot cause arbitrary reads or an actionable/live row.
- No owner, malformed owner, dead PID, missing identity, reused PID and boot
mismatch are non-live. A positively matching live process is live.
- STOP presence is visible as braked independently of process liveness. Its
contents are not read or exposed; no STOP or lock is created or changed.
- Ordinary completed messages remain idle. No false human attention regression.
- Connector rows cannot borrow a native agent's registration for replies.
Exercise replyToRow and HTTP using a fake executable hook; every connector
attempt must be refused before that hook runs, including with forged tmux
registration. Normal-agent reply tests must still pass.
- Both existing board and WebUI distinguish the connector and brake state and
omit reply controls. Preserve escaping, including hostile binding fixtures.
- Discovery errors disclose no private JSON fields or raw contents. One bad
binding must not silently manufacture a healthy row.
- Candidate pins match before and after tests. Existing dirty attention changes
remain intact; no unrelated source integration or live operation is inferred.
After source approval, measure the real row read-only. Offline/braked behavior
uses isolated fixtures unless the operator separately approves a live-service
transition. Board replacement is its own protected gate.
@@ -0,0 +1,23 @@
{
"at": "2026-09-14T13:51:10.530662+00:00",
"backendPid": 3769124,
"health": "ok",
"row": {
"agent": "sage (discord: shared-signals)",
"project": "fleet",
"state": "idle",
"alive": true,
"connector": {
"binding": "shared-signals",
"braked": false,
"ownerState": "live",
"alive": true
},
"task": "Discord connector",
"taskSource": "connector"
},
"replyStatus": 409,
"replyError": "board replies are disabled for Discord connectors",
"fiveAgentPaneIdentitiesUnchanged": true,
"connectorServiceIdentityUnchanged": true
}
@@ -0,0 +1,2 @@
{"at": "2026-09-14T13:50:24.078636+00:00", "event": "owner-authorized-restart-intent", "oldPid": 3204655, "agents": {"default/darkwing": [["2733924", "12863634"]], "default/dewey": [["934346", "466065"]], "default/filbert": [["72183", "100870"]], "default/researcher": [["173699", "66404285"]], "mosaic-fleet/rocko": [["90599", "128275"]]}, "connectorService": [3022843, "67887873"], "manifest": "254403b89c0a2330da53e8dbad1cbeba3b1b06cf4f3efddc18451e04cb78f6de"}
{"at": "2026-09-14T13:50:24.503231+00:00", "event": "replacement-started", "oldExitedGracefully": true, "newPid": 3769124, "log": "/tmp/discord-board-backend-ovk_cahk.log"}
@@ -0,0 +1,18 @@
{
"at": "2026-09-14T01:10:19.375709+00:00",
"candidate": "/tmp/discord-board-r1-KbMrGQWF",
"manifestSha256": "5c92acc90d202727d790f3fb8d74387db1c9e5c3e43d4f4b56b40e5ae503a56a",
"verdict": "CHANGES REQUIRED",
"independentSerializedTests": 320,
"finding": {
"id": "R1-B1",
"severity": "P2",
"file": "packages/control-board/src/discord.mjs",
"issue": "STOP metadata access errors collapse to absence, falsely projecting not braked",
"reproduction": "Synthetic journal directory contains STOP, chmod directory to 000 as uid 1000, inspectDiscord returns braked:false, ownerState:invalid, alive:false. Restore permissions and remove fixture.",
"expected": "braked:null/unknown when STOP existence cannot be established; false only for verified absence",
"required": "Distinguish missing metadata from access errors and add non-root unreadable-directory regression."
},
"ux": "Dewey APPROVE on exact R1; three independent serialized browser tests passed, source/automation limitations retained",
"parallelQualification": "Two author concurrent frozen timeouts remain unresolved and are not green; serialized independent run passed."
}
@@ -0,0 +1,7 @@
{
"candidate": "R2",
"syntheticOnly": true,
"taskContainsEnvelopeAuthorId": true,
"taskContainsEnvelopeMessageId": true,
"taskSource": "first-user-message"
}
@@ -0,0 +1,18 @@
{
"at": "2026-09-14T01:26:42.688410+00:00",
"candidate": "/tmp/discord-board-r3-U9vVrlQu",
"manifestSha256": "254403b89c0a2330da53e8dbad1cbeba3b1b06cf4f3efddc18451e04cb78f6de",
"reviewer": "Darkwing",
"backendVerdict": "APPROVE AS SOURCE",
"verified": "Nine working/frozen pins, exact three-file R2-to-R3 delta, inherited attention pins and full serialized six-package suite 322/322",
"findingsClosed": [
"R1-B1: inaccessible STOP is unknown, non-root regression passes",
"R2-B2: canonical routing envelope no longer becomes connector Task; ordinary fallback retained"
],
"limitations": [
"No generalized transcript redaction",
"R1 concurrent combined frozen timeouts unresolved/not green",
"No live observation, backend restart, connector change or publication in this review"
],
"uxGate": "Await exact R3 confirmation from Dewey via agent-send"
}
@@ -0,0 +1,20 @@
{
"at": "2026-09-14T01:28:17.695Z",
"sourceApproval": 26257,
"readOnly": true,
"agent": "sage (discord: shared-signals)",
"project": "fleet",
"state": "idle",
"alive": true,
"connector": {
"binding": "shared-signals",
"braked": false,
"ownerState": "live",
"alive": true
},
"task": "Discord connector",
"taskSource": "connector",
"registrationAbsent": true,
"ownerMatchesService": true,
"discoveryErrorCount": 0
}
@@ -0,0 +1,9 @@
{
"at": "2026-09-14T00:50:35.339Z",
"sourceSha256": "dfbb7ab9374c0ac9fafa0503f495abd938f499f5d6227233de03a60ea3022927",
"fixture": "connector row with forged native registration",
"status": 200,
"fakeTransportCalls": 1,
"realTransportCalls": 0,
"gatePassed": false
}
@@ -0,0 +1,162 @@
# Discord engine: guaranteed test cleanup and the timeout gap in `busy` (#1509), R2 candidate
Sage assigned this on 2026-09-26 as 6b, source only. Rocko reviews. Base is
HEAD 401cc850. Not committed. The live connector runs from this checkout, so
Sage is holding its restart until this is approved and committed. Nobody
should restart it from a working copy.
## Defects (DEFERRED Open, "Discord engine: leaked fake pi…")
(a) `engine.test.mjs` read the fake's `commands.jsonl` 20 ms after a prompt and
got ENOENT under load. Seven tests stopped the engine outside `finally`, so a
failed assertion left the fake pi running and the test file never exited.
(b) `busy` was `state.busy || pending.some((t) => !t.done)`. If a turn timed out
before its `agent_start` was read, it was done while `state.busy` was still
false. The next prompt then went straight to pi, which refused it as
streaming.
## R1 and Rocko's finding
R1 held later prompts behind a failed turn. If pi had sent no `agent_start` for
it within a grace period, R1 dropped that turn from the queue and sent the next
prompt. Rocko rejected it (F1, High), in
`agents/rocko/work/discord-engine-busy-r1-review-2026-09-26.md`, sha256
047dbd8f.
Pi's events carry no prompt id. The engine attributes them to the front of its
queue. Silence until the grace ends does not prove the old run will never come.
If pi then runs it, its events land on the new prompt, which R1 had just put at
the front. Rocko's reproducer got the old run's answer and its `old.md` tool
record back as the new prompt's result. My R1 README said such events "find no
live head and are dropped". That was wrong.
The R1 files stay here as `r1-manifest.sha256` and `r1.patch`.
## Change (R2)
`packages/discord/src/engine-pi.mjs`:
- `engineBusy()` is `state.busy || state.pending.length > 0`. A failed turn
still in the queue holds the next prompt back, and stays at the front, so any
late events for it land on it. `prompt()`, `sendHeld()` and the `busy` getter
use it. This part is unchanged from R1.
- The bound is now a stop, not a drop. When a turn fails while it is still in
the queue, `failTurn` starts a timer, `abortGraceMs` (default
`ABORT_GRACE_MS`, 30 s, an engine option, not binding config). When it
fires:
- If pi has sent `agent_start` (`state.busy`), nothing happens. That run
ends on its `agent_end` or a settle, as on HEAD.
- Otherwise `wedge()` sets `state.wedged`, fails every held prompt with
code `engine-wedged`, and stops pi: stdin closed, SIGTERM, then SIGKILL
after 5 s. The failed turn stays at the front until the exit.
- While wedged, nothing is written to that child. `write()`, `sendHeld()` and
`prompt()` refuse, and a new prompt fails at once with `engine-down`. The exit
runs the usual `failAll` and `onExit`.
- `stop()`'s body moved into `stopChild()`, which both `stop()` and `wedge()`
call.
- `release()` clears the timer wherever a turn leaves the queue: `agent_end`,
settle, a refused send, and process exit. As in R1, the settle handler removes
turns before failing them.
What recovery looks like live: `cli.mjs` handles `onExit` with `shutdown(1)`.
The unit's `Restart=on-failure` starts a new connector and a new pi 15 s later,
within its limit of five tries in ten minutes. This change doesn't touch the
unit or the restart policy. A wedge now costs one connector restart. R1 would
have kept the same pi and risked a wrong answer.
`packages/discord/tests/fake-pi.mjs`:
- `mute`: accepted and never run.
- `stall <ms>`: accepted, then the fake reads nothing for `<ms>`, runs the
stalled prompt, and only then reads what came in meanwhile. This is the
order in Rocko's case.
`packages/discord/tests/engine.test.mjs`:
- `withEngine()` stops the engine in `finally`. Every test that starts an
engine uses it, or has its own `try/finally` in the exit test.
- `commands()` returns `[]` until the fake creates its log. The held-prompt
test waits for the first prompt with `until()` instead of a 20 ms sleep, and
its first prompt is `slow 300`.
- The manual-timer test fires the turn timer before any pi event is read. The
next prompt must wait for the settle and get its own answer.
- New or changed for R2:
- `mute` with `abortGraceMs: 150`. The held prompt fails with
`engine-wedged` after the grace, a later prompt fails with
`engine-down`, `onExit` fires, and pi saw only `mute` and `abort`.
- `stall 400` with the same grace, which is Rocko's case with a real
child. The held prompt fails with `engine-wedged`, pi exits, and "after
stall" never reaches pi.
- Rocko's reproducer as an in-memory test, run twice. The old prompt's
response comes either before its timeout or only with the late events.
After the grace, the old run's start, tool pair, answer, end and settle
arrive while pi is still exiting. The held prompt stays failed with
`engine-wedged` and a later prompt fails with `engine-down`. Pi saw only
`old` and `abort`, then SIGTERM, then SIGKILL at 5 s. Only the exit
reaches `onExit`. The test reads recorded outcomes after a tick instead
of awaiting, so a regression fails instead of hanging.
- `late 400` with the same grace. Pi started that run, so the grace does
not stop pi, and the next prompt gets its own answer when the run ends.
## Evidence
- `engine.test.mjs`: 17/17.
- R1's engine (d5bf24b5, from `r1.patch`) against these tests fails 4: `mute`,
`stall`, and both in-memory runs. In that run a probe shows R1 answering
"after stall" with "echo: stalled". An earlier draft of the in-memory test
awaited the held prompt and hung on R1 until the 120 s cap. It now fails in
milliseconds.
- HEAD's engine against these tests fails 5: the manual-timer test and the
same four.
- Mutations of R2:
- Without the `state.busy` check, the `late 400` test fails.
- Without the `wedge()` call, 4 fail.
- Without failing held prompts in `wedge()`, 4 fail.
- Test union (control-board, webui, seat, mosaic, ledger, discord) at default
concurrency on `git archive` of 401cc850 plus the three files: 406/406
three times, 23 to 24 s each. No fake pi was left running.
- Eight suites green on that snapshot: config 24, task 90, foundation 43,
conductor 17, release 14, auth 15, discord 63, extension-package 18.
Logs: `/tmp/dw-6b-r2-conc-{1,2,3}.txt`. R1's evidence runs:
`/tmp/dw-6b-conc-{1,2,3}.txt`, `/tmp/dw-6b-serial.txt`. HEAD's hang control:
`/tmp/dw-1509-headctl-{1,2,3}.txt`, `/tmp/dw-1509-ef00-1.txt`. There, HEAD hit
the 240 s cap at 199 ok under the union's load.
## Not covered
- A run pi started and never ends, even after abort, still holds prompts
until pi settles or exits. Each held prompt fails at its own timeout ("while
waiting for the engine"). HEAD behaves the same way through `state.busy`, and
Rocko did not block on it. Only a pi restart clears it.
- A wedge ends the connector process, and the recovery is systemd's restart.
Nothing here changes the unit, and the restart limit still applies.
- No live restart, and no change to the binding schema.
## Frozen files
`r2-manifest.sha256` holds the three R2 hashes. `r2.patch` is `git diff
packages/discord` at freeze time.
## Review
Rocko, R1, 2026-09-26: request changes, F1 High, as described above. Report:
`agents/rocko/work/discord-engine-busy-r1-review-2026-09-26.md`, sha256
047dbd8f.
Rocko, R2, 2026-09-26: approved the three pinned files. Report:
`agents/rocko/work/discord-engine-busy-r2-review-2026-09-26.md`, sha256
ed5510a0. He checked the manifests before and after, ran 17/17 himself, and
read the CLI shutdown path, `connector.stop` and the unit template. Sage asked
him three operational questions:
- A wedge exits 1, never 3. Exit 3 remains the supervised startup refusal.
- The unit's start limit (5 starts in 600 s) is a rate limit. It does not
bound repeated wedges. With the default 180 s turn timeout, the 30 s grace
and the 15 s restart delay, a cycle takes at least 225 s. That stays under
the limit, so a pi that wedges every time could restart indefinitely.
Stopping for good after repeated wedges would need a separate policy. This
change does not add one.
- He recommends, as a nonblocking follow-up, that the connector journal
record at startup: HEAD, dirty state scoped to runtime source, and a digest
of the runtime files. A wedge restart loads whatever the checkout holds.
This section was added after approval, so the README hash no longer matches
the one Rocko pinned (69350f29). The three source files are unchanged.
@@ -0,0 +1,3 @@
d5bf24b59c07c85067f4087c03b54ca8b4df923c1d591441dedd2e8a7ff2ae39 packages/discord/src/engine-pi.mjs
f0abee9c243d46d66dd2271abc3fd89089c350ac6a66ab49131bce80adfcdc33 packages/discord/tests/engine.test.mjs
fa1bf44e3f33eb970a714ada1c686abbc1932baaf418679276edf8813abbe6de packages/discord/tests/fake-pi.mjs
@@ -0,0 +1,414 @@
diff --git a/packages/discord/src/engine-pi.mjs b/packages/discord/src/engine-pi.mjs
index 5c8fd0a9..9dcaeb42 100644
--- a/packages/discord/src/engine-pi.mjs
+++ b/packages/discord/src/engine-pi.mjs
@@ -16,9 +16,12 @@
// from `tool_execution_start`/`tool_execution_end` into the result so the
// turn record shows what was read. An `agent_end` with `willRetry` is not
// the end of the run. A timeout sends `abort` and fails that turn; the
-// process stays. A malformed JSONL line from pi fails the current turn (its
-// outcome is now unknowable) and the process stays. Process exit fails
-// every pending turn and is reported through `onExit`.
+// process stays. The failed turn holds later prompts back until its
+// agent_end or a settle. If pi has not started it within ABORT_GRACE_MS, it
+// is dropped and the next prompt goes out; a run pi did start holds them
+// until it ends, as any run does. A malformed JSONL line from pi fails the
+// current turn (its outcome is now unknowable) and the process stays.
+// Process exit fails every pending turn and is reported through `onExit`.
//
// Framing follows pi's RPC doc: split on "\n" only, strip a trailing "\r".
// Node readline is not used because it also splits on U+2028/U+2029.
@@ -64,12 +67,19 @@ export function assistantText(message) {
.trim();
}
+// How long a turn that failed here (timeout, protocol error) may wait for
+// pi's agent_start before it stops holding the next prompt back. Without a
+// bound, a prompt pi accepted but never ran would queue every later prompt
+// until restart.
+export const ABORT_GRACE_MS = 30000;
+
export function createEngine({
command, args, cwd, env = {},
spawn = nodeSpawn,
setTimeoutImpl = globalThis.setTimeout, clearTimeoutImpl = globalThis.clearTimeout,
log = () => {},
onExit = () => {},
+ abortGraceMs = ABORT_GRACE_MS,
} = {}) {
if (typeof command !== "string" || command.length === 0) throw new DiscordError("engine: command required", 1);
if (!Array.isArray(args)) throw new DiscordError("engine: args required", 1);
@@ -80,15 +90,35 @@ export function createEngine({
// A turn that fails on the client side (timeout, protocol error) stays in
// the pending queue, marked done, until pi's own turn_end for it arrives.
- // Otherwise that turn_end would be attributed to the next prompt.
+ // Otherwise that turn_end would be attributed to the next prompt. It holds
+ // later prompts back; if pi has not started it within abortGraceMs, it goes.
function failTurn(turn, code, message) {
if (turn.done) return;
turn.done = true;
if (turn.timer !== null) clearTimeoutImpl(turn.timer);
turn.timer = null;
+ if (state.pending.includes(turn)) {
+ turn.grace = setTimeoutImpl(() => {
+ turn.grace = null;
+ // No agent_start by now: pi never started this run and will send no
+ // agent_end for it, so it leaves the queue and cannot take the next
+ // prompt's. A run pi did start keeps its place until it ends.
+ if (!state.busy) {
+ const i = state.pending.indexOf(turn);
+ if (i !== -1) state.pending.splice(i, 1);
+ }
+ sendHeld();
+ }, abortGraceMs);
+ }
turn.reject(new DiscordError(message, 1, { code }));
}
+ // Call when a turn leaves the pending queue.
+ function release(turn) {
+ if (turn.grace !== null) clearTimeoutImpl(turn.grace);
+ turn.grace = null;
+ }
+
function settleTurn(turn, value) {
if (turn.done) return;
turn.done = true;
@@ -99,7 +129,10 @@ export function createEngine({
function failAll(code, message) {
const pending = state.pending.splice(0);
- for (const t of pending) failTurn(t, code, message);
+ for (const t of pending) {
+ release(t);
+ failTurn(t, code, message);
+ }
for (const h of state.held.splice(0)) failTurn(h.turn, code, message);
for (const [, r] of state.responses) r.reject(new DiscordError(message, 1, { code }));
state.responses.clear();
@@ -174,6 +207,7 @@ export function createEngine({
// Attribute the run to the head even if it failed client-side, so the
// next prompt's agent_end is not taken for this one.
const run = state.pending.shift();
+ if (run) release(run);
if (!run || run.done) return;
const messages = Array.isArray(event.messages) ? event.messages.filter((m) => m && m.role === "assistant") : [];
const message = messages.length > 0 ? messages[messages.length - 1] : run.last;
@@ -193,13 +227,14 @@ export function createEngine({
// this settle and still has no agent_end will never get one: fail it now
// instead of waiting for its timeout. Turns whose prompt response has
// not arrived yet belong to a later run and stay.
+ const dropped = [];
const keep = [];
- for (const t of state.pending) {
- if (t.done) continue;
- if (t.accepted) failTurn(t, "engine-settled-without-turn", "engine settled without answering this prompt");
- else keep.push(t);
- }
+ for (const t of state.pending) (t.done || t.accepted ? dropped : keep).push(t);
state.pending = keep;
+ for (const t of dropped) {
+ release(t);
+ failTurn(t, "engine-settled-without-turn", "engine settled without answering this prompt");
+ }
sendHeld();
}
}
@@ -214,15 +249,23 @@ export function createEngine({
// Never accepted: pi will not emit a turn_end for it, so remove it.
const i = state.pending.indexOf(turn);
if (i !== -1) state.pending.splice(i, 1);
+ release(turn);
failTurn(turn, (err.details && err.details.code) || "engine-refused", err.message);
sendHeld();
});
}
+ // Pi is busy from our side while any sent prompt is still queued, even one
+ // that already failed here: a turn that timed out before its agent_start
+ // was read leaves state.busy false while pi runs it, and sending then would
+ // be refused as streaming. It leaves the queue on its agent_end, on a
+ // settle, on a refused send, or when its grace ends before pi started it.
+ const engineBusy = () => state.busy || state.pending.length > 0;
+
// After a settle (or a refused send) the oldest held prompt goes out.
function sendHeld() {
if (state.exited !== null) return;
- if (state.busy || state.pending.some((t) => !t.done)) return;
+ if (engineBusy()) return;
const next = state.held.shift();
if (next) send(next.turn, next.command);
}
@@ -281,7 +324,7 @@ export function createEngine({
// with DiscordError carrying details.code for the turn record.
prompt(text, { timeoutMs = 180000 } = {}) {
if (typeof text !== "string" || text.length === 0) throw new DiscordError("prompt text required", 1);
- const turn = { resolve: null, reject: null, timer: null, done: false, accepted: false, tools: new Map(), turns: 0, last: null };
+ const turn = { resolve: null, reject: null, timer: null, grace: null, done: false, accepted: false, tools: new Map(), turns: 0, last: null };
const done = new Promise((resolve, reject) => {
turn.resolve = resolve;
turn.reject = reject;
@@ -310,13 +353,13 @@ export function createEngine({
failTurn(turn, "engine-down", "engine is not running");
return done;
}
- if (state.busy || state.pending.some((t) => !t.done) || state.held.length > 0) state.held.push({ turn, command });
+ if (engineBusy() || state.held.length > 0) state.held.push({ turn, command });
else send(turn, command);
return done;
},
get busy() {
- return state.busy || state.pending.some((t) => !t.done) || state.held.length > 0;
+ return engineBusy() || state.held.length > 0;
},
get pendingCount() {
return state.pending.filter((t) => !t.done).length + state.held.length;
diff --git a/packages/discord/tests/engine.test.mjs b/packages/discord/tests/engine.test.mjs
index 59674d1e..62dd6017 100644
--- a/packages/discord/tests/engine.test.mjs
+++ b/packages/discord/tests/engine.test.mjs
@@ -28,7 +28,20 @@ function start(root, extra = {}) {
log: (m) => logs.push(m), ...extra,
});
engine.start();
- return { engine, logs, commands: () => readFileSync(logPath, "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) };
+ // The fake creates its log on the first command; until then there are none.
+ const commands = () => (existsSync(logPath) ? readFileSync(logPath, "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) : []);
+ return { engine, logs, commands };
+}
+
+// Every test stops its engine in finally: a fake pi left running after a
+// failed assertion keeps the test file from exiting.
+async function withEngine(extra, body) {
+ const started = start(makeRoot(), extra);
+ try {
+ await body(started);
+ } finally {
+ await started.engine.stop();
+ }
}
test("engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file", () => {
@@ -56,8 +69,7 @@ test("engine: with tools, buildPiArgs turns pi's own tools off, loads the extens
assert.equal(rw[rw.indexOf("--tools") + 1], "list_dir,read_file,search,write_file,edit_file", "a writable root adds exactly the two write tools");
});
-test("engine: a run with tool turns settles once, on the answer, with every tool call in the result", async () => {
- const { engine } = start(makeRoot());
+test("engine: a run with tool turns settles once, on the answer, with every tool call in the result", () => withEngine({}, async ({ engine }) => {
const r = await engine.prompt("tools 3");
assert.equal(r.text, "read 3 file(s)");
assert.equal(r.turns, 2);
@@ -71,45 +83,38 @@ test("engine: a run with tool turns settles once, on the answer, with every tool
assert.equal(plain.turns, 1);
await idle(engine);
assert.equal(engine.busy, false);
- await engine.stop();
-});
+}));
-test("engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end", async () => {
- const { engine } = start(makeRoot());
+test("engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end", () => withEngine({}, async ({ engine }) => {
const r = await engine.prompt("toolonly");
assert.equal(r.text, "", "no text: the connector turns this into engine-empty");
assert.equal(r.tools.length, 1);
const again = await engine.prompt("retry");
assert.equal(again.text, "after retry");
- await engine.stop();
-});
+}));
-test("engine: one prompt, one turn, text and usage come back", async () => {
- const { engine } = start(makeRoot());
- try {
- const r = await engine.prompt("hello");
- assert.equal(r.text, "echo: hello");
- assert.deepEqual(r.usage, { input: 3, output: 2 });
- await idle(engine);
- assert.equal(engine.busy, false);
- } finally {
- await engine.stop();
- }
-});
+test("engine: one prompt, one turn, text and usage come back", () => withEngine({}, async ({ engine }) => {
+ const r = await engine.prompt("hello");
+ assert.equal(r.text, "echo: hello");
+ assert.deepEqual(r.usage, { input: 3, output: 2 });
+ await idle(engine);
+ assert.equal(engine.busy, false);
+}));
-test("engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order", async () => {
- const { engine, commands } = start(makeRoot());
- const first = engine.prompt("slow 150");
- await new Promise((r) => setTimeout(r, 20));
+test("engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order", () => withEngine({}, async ({ engine, commands }) => {
+ const first = engine.prompt("slow 300");
assert.equal(engine.busy, true);
const second = engine.prompt("second");
assert.equal(engine.pendingCount, 2);
- await new Promise((r) => setTimeout(r, 20));
- assert.equal(commands().filter((c) => c.type === "prompt").length, 1, "the second prompt is not sent while pi is busy");
+ const prompted = () => commands().filter((c) => c.type === "prompt");
+ assert.ok(await until(() => prompted().length > 0), "the first prompt reached pi");
+ assert.equal(prompted().length, 1, "the second prompt is not sent while pi is busy");
+ // The fake refuses a prompt without streamingBehavior while it runs one, so
+ // an answered second prompt also proves it was not sent early.
const [r1, r2] = await Promise.all([first, second]);
assert.equal(r1.text, "slow reply");
assert.equal(r2.text, "echo: second");
- const prompts = commands().filter((c) => c.type === "prompt");
+ const prompts = prompted();
assert.equal(prompts.length, 2);
// Never a pi follow-up: pi would fold it into the first run and close both
// answers with one agent_end (the live loss of 2026-09-17).
@@ -117,11 +122,9 @@ test("engine: a prompt while streaming is held until pi settles, then sent as it
assert.equal(prompts[1].streamingBehavior, undefined);
await idle(engine);
assert.equal(engine.busy, false);
- await engine.stop();
-});
+}));
-test("engine: a held prompt that times out before pi settles fails on its own and is never sent", async () => {
- const { engine, commands } = start(makeRoot());
+test("engine: a held prompt that times out before pi settles fails on its own and is never sent", () => withEngine({}, async ({ engine, commands }) => {
const first = engine.prompt("slow 200");
await new Promise((r) => setTimeout(r, 20));
await assert.rejects(engine.prompt("late one", { timeoutMs: 50 }), (e) => e.details.code === "timeout" && /waiting for the engine/.test(e.message));
@@ -130,50 +133,85 @@ test("engine: a held prompt that times out before pi settles fails on its own an
await idle(engine);
assert.deepEqual(commands().filter((c) => c.type === "prompt").map((c) => c.message), ["slow 200"]);
assert.deepEqual(commands().filter((c) => c.type === "abort"), [], "a held turn is not aborted; pi never had it");
- await engine.stop();
-});
+}));
-test("engine: timeout sends abort and fails only that turn; the process stays", async () => {
- const { engine, commands, logs } = start(makeRoot());
+test("engine: timeout sends abort and fails only that turn; the process stays", () => withEngine({}, async ({ engine, commands, logs }) => {
await assert.rejects(engine.prompt("slow 5000", { timeoutMs: 100 }), (err) => err.details.code === "timeout");
assert.ok(await until(() => commands().some((c) => c.type === "abort")), "abort reached pi");
assert.ok(logs.some((l) => /timed out/.test(l)));
const r = await engine.prompt("again");
assert.equal(r.text, "echo: again");
- await engine.stop();
+}));
+
+test("engine: tool events from a run that outlived its timeout never land in the next prompt's record", () => withEngine({}, async ({ engine }) => {
+ await assert.rejects(engine.prompt("late 200", { timeoutMs: 40 }), (err) => err.details.code === "timeout");
+ const r = await engine.prompt("after late");
+ assert.equal(r.text, "echo: after late");
+ assert.deepEqual(r.tools, [], "the dead run's read is not this prompt's evidence");
+ assert.equal(r.turns, 1, "the dead run's turns are not counted here");
+}));
+
+// The turn timer is fired by hand, before the engine has read any event from
+// pi, so the timed-out run is still pi's and state.busy is still false when
+// the next prompt arrives. Under load a real timer does the same.
+const TURN_MS = 60000;
+const manualTurnTimer = (fire) => ({
+ setTimeoutImpl: (fn, ms) => (ms === TURN_MS ? fire.push(fn) : setTimeout(fn, ms)),
+ clearTimeoutImpl: (id) => { if (typeof id !== "number") clearTimeout(id); },
});
-test("engine: tool events from a run that outlived its timeout never land in the next prompt's record", async () => {
- const { engine } = start(makeRoot());
- try {
- await assert.rejects(engine.prompt("late 200", { timeoutMs: 40 }), (err) => err.details.code === "timeout");
- const r = await engine.prompt("after late");
+test("engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused", () => {
+ const fire = [];
+ return withEngine(manualTurnTimer(fire), async ({ engine, commands }) => {
+ const late = engine.prompt("late 100", { timeoutMs: TURN_MS });
+ fire.shift()();
+ assert.equal(engine.busy, true, "pi is still running the prompt that timed out");
+ const next = engine.prompt("after late", { timeoutMs: 5000 });
+ assert.equal(engine.pendingCount, 1, "only the new prompt is live");
+ await assert.rejects(late, (err) => err.details.code === "timeout");
+ const r = await next;
assert.equal(r.text, "echo: after late");
assert.deepEqual(r.tools, [], "the dead run's read is not this prompt's evidence");
- assert.equal(r.turns, 1, "the dead run's turns are not counted here");
- } finally {
- await engine.stop();
- }
+ assert.equal(r.turns, 1);
+ assert.deepEqual(commands().map((c) => (c.type === "prompt" ? c.message : c.type)), ["late 100", "abort", "after late"]);
+ await idle(engine);
+ assert.equal(engine.busy, false);
+ });
});
-test("engine: a malformed JSONL line fails the turn, not the process", async () => {
- const { engine, logs } = start(makeRoot());
+// "mute" is accepted and never run, so no agent_start, agent_end or settle
+// ever comes for it. Unbounded, it would hold every later prompt.
+test("engine: a timed-out turn pi never started holds the next prompt only for the abort grace, then leaves the queue", () => withEngine({ abortGraceMs: 150 }, async ({ engine, commands }) => {
+ await assert.rejects(engine.prompt("mute", { timeoutMs: 50 }), (err) => err.details.code === "timeout");
+ assert.equal(engine.busy, true, "pi might still be running it");
+ const started = Date.now();
+ const r = await engine.prompt("after mute", { timeoutMs: 5000 });
+ assert.equal(r.text, "echo: after mute");
+ assert.ok(Date.now() - started >= 100, "held for the grace, not sent at once");
+ assert.deepEqual(commands().map((c) => (c.type === "prompt" ? c.message : c.type)), ["mute", "abort", "after mute"]);
+ await idle(engine);
+ assert.equal(engine.busy, false);
+}));
+
+test("engine: a malformed JSONL line fails the turn, not the process", () => withEngine({}, async ({ engine, logs }) => {
await assert.rejects(engine.prompt("garbage"), (err) => err.details.code === "engine-protocol");
assert.ok(logs.some((l) => /malformed/.test(l)));
const r = await engine.prompt("still here");
assert.equal(r.text, "echo: still here");
- await engine.stop();
-});
+}));
test("engine: a turn that ends in error rejects with the error code; process exit fails pending turns", async () => {
- const root = makeRoot();
let exited = null;
- const { engine } = start(root, { onExit: (e) => (exited = e) });
- await assert.rejects(engine.prompt("error"), (err) => err.details.code === "engine-error" && /fake provider error/.test(err.message));
- const pending = engine.prompt("slow 5000");
- await new Promise((r) => setTimeout(r, 20));
- await engine.stop();
- await assert.rejects(pending, (err) => err.details.code === "engine-down");
- assert.ok(exited);
- await assert.rejects(engine.prompt("x"), /not running/);
+ const { engine } = start(makeRoot(), { onExit: (e) => (exited = e) });
+ try {
+ await assert.rejects(engine.prompt("error"), (err) => err.details.code === "engine-error" && /fake provider error/.test(err.message));
+ const pending = engine.prompt("slow 5000");
+ await new Promise((r) => setTimeout(r, 20));
+ await engine.stop();
+ await assert.rejects(pending, (err) => err.details.code === "engine-down");
+ assert.ok(exited);
+ await assert.rejects(engine.prompt("x"), /not running/);
+ } finally {
+ await engine.stop();
+ }
});
diff --git a/packages/discord/tests/fake-pi.mjs b/packages/discord/tests/fake-pi.mjs
index 94919306..ecc04e3b 100644
--- a/packages/discord/tests/fake-pi.mjs
+++ b/packages/discord/tests/fake-pi.mjs
@@ -8,6 +8,7 @@
// then a second turn that answers "read <n> file(s)"
// "toolonly" a run whose only turn calls a tool and never answers
// "retry" an agent_end with willRetry, then the real answer
+// "mute" accept the prompt and emit nothing, staying idle
// "late <ms>" ignore abort; after <ms> emit a tool pair and a tool turn,
// then answer "late reply", like a run that outlives its
// client-side timeout
@@ -30,6 +31,7 @@ function assistant(text, stopReason = "stop") {
}
function run(text) {
+ if (text === "mute") return;
busy = true;
out({ type: "agent_start" });
out({ type: "turn_start" });
@@ -0,0 +1,3 @@
77077b7fbd5a933ffd352094eb073227c299ba47b7aea52d4e60fdc55cc7101e packages/discord/src/engine-pi.mjs
47a998179c6eb46827f43ab2c6b0f6b062ef94fb47da402cfb9af8c4f180f38e packages/discord/tests/engine.test.mjs
a8e54cc3f4b670eef2c06755b63e9e6bfeb44b1b1efde3aca9bfaa91583c3ef3 packages/discord/tests/fake-pi.mjs
@@ -0,0 +1,651 @@
diff --git a/packages/discord/src/engine-pi.mjs b/packages/discord/src/engine-pi.mjs
index 5c8fd0a9..46ef1f88 100644
--- a/packages/discord/src/engine-pi.mjs
+++ b/packages/discord/src/engine-pi.mjs
@@ -16,9 +16,14 @@
// from `tool_execution_start`/`tool_execution_end` into the result so the
// turn record shows what was read. An `agent_end` with `willRetry` is not
// the end of the run. A timeout sends `abort` and fails that turn; the
-// process stays. A malformed JSONL line from pi fails the current turn (its
-// outcome is now unknowable) and the process stays. Process exit fails
-// every pending turn and is reported through `onExit`.
+// process stays. The failed turn holds later prompts back until its
+// agent_end or a settle. If pi has not started it within ABORT_GRACE_MS, the
+// engine stops pi instead of sending again: pi's events carry no prompt id,
+// so a late run of the failed prompt would be taken for the next one's. A
+// run pi did start holds later prompts until it ends, as any run does. A
+// malformed JSONL line from pi fails the current turn (its outcome is now
+// unknowable) and the process stays. Process exit fails every pending turn
+// and is reported through `onExit`.
//
// Framing follows pi's RPC doc: split on "\n" only, strip a trailing "\r".
// Node readline is not used because it also splits on U+2028/U+2029.
@@ -64,31 +69,67 @@ export function assistantText(message) {
.trim();
}
+// How long a turn that failed here (timeout, protocol error) may wait for
+// pi's agent_start before the engine stops pi. Without a bound, a prompt pi
+// accepted but never ran would hold every later prompt until restart.
+export const ABORT_GRACE_MS = 30000;
+
export function createEngine({
command, args, cwd, env = {},
spawn = nodeSpawn,
setTimeoutImpl = globalThis.setTimeout, clearTimeoutImpl = globalThis.clearTimeout,
log = () => {},
onExit = () => {},
+ abortGraceMs = ABORT_GRACE_MS,
} = {}) {
if (typeof command !== "string" || command.length === 0) throw new DiscordError("engine: command required", 1);
if (!Array.isArray(args)) throw new DiscordError("engine: args required", 1);
// pending: prompts sent to pi, oldest first. held: prompts waiting for pi
// to settle before they are sent, oldest first.
- const state = { child: null, buffer: "", pending: [], held: [], responses: new Map(), nextId: 1, busy: false, exited: null };
+ // wedged: set when the engine gave up on pi and is stopping it. Nothing
+ // is sent to that child again.
+ const state = { child: null, buffer: "", pending: [], held: [], responses: new Map(), nextId: 1, busy: false, exited: null, wedged: false };
// A turn that fails on the client side (timeout, protocol error) stays in
// the pending queue, marked done, until pi's own turn_end for it arrives.
- // Otherwise that turn_end would be attributed to the next prompt.
+ // Otherwise that turn_end would be attributed to the next prompt. It holds
+ // later prompts back; if pi has not started it within abortGraceMs, the
+ // engine stops pi.
function failTurn(turn, code, message) {
if (turn.done) return;
turn.done = true;
if (turn.timer !== null) clearTimeoutImpl(turn.timer);
turn.timer = null;
+ if (state.pending.includes(turn)) {
+ turn.grace = setTimeoutImpl(() => {
+ turn.grace = null;
+ // A run pi started keeps its place until its agent_end or a settle.
+ if (state.busy) return;
+ // No agent_start yet. Pi may never run this prompt, or its events
+ // may still be on the way; with no prompt id in them, nothing sent
+ // now could be told apart from it. Stop pi: held prompts fail, and
+ // the exit fails the rest and reaches onExit.
+ log(`engine: no agent_start ${abortGraceMs} ms after a failed turn; stopping pi`);
+ wedge();
+ }, abortGraceMs);
+ }
turn.reject(new DiscordError(message, 1, { code }));
}
+ function wedge() {
+ if (state.wedged || state.exited !== null) return;
+ state.wedged = true;
+ for (const h of state.held.splice(0)) failTurn(h.turn, "engine-wedged", "engine stopped: pi did not start an aborted turn");
+ stopChild();
+ }
+
+ // Call when a turn leaves the pending queue.
+ function release(turn) {
+ if (turn.grace !== null) clearTimeoutImpl(turn.grace);
+ turn.grace = null;
+ }
+
function settleTurn(turn, value) {
if (turn.done) return;
turn.done = true;
@@ -99,7 +140,10 @@ export function createEngine({
function failAll(code, message) {
const pending = state.pending.splice(0);
- for (const t of pending) failTurn(t, code, message);
+ for (const t of pending) {
+ release(t);
+ failTurn(t, code, message);
+ }
for (const h of state.held.splice(0)) failTurn(h.turn, code, message);
for (const [, r] of state.responses) r.reject(new DiscordError(message, 1, { code }));
state.responses.clear();
@@ -174,6 +218,7 @@ export function createEngine({
// Attribute the run to the head even if it failed client-side, so the
// next prompt's agent_end is not taken for this one.
const run = state.pending.shift();
+ if (run) release(run);
if (!run || run.done) return;
const messages = Array.isArray(event.messages) ? event.messages.filter((m) => m && m.role === "assistant") : [];
const message = messages.length > 0 ? messages[messages.length - 1] : run.last;
@@ -193,13 +238,14 @@ export function createEngine({
// this settle and still has no agent_end will never get one: fail it now
// instead of waiting for its timeout. Turns whose prompt response has
// not arrived yet belong to a later run and stay.
+ const dropped = [];
const keep = [];
- for (const t of state.pending) {
- if (t.done) continue;
- if (t.accepted) failTurn(t, "engine-settled-without-turn", "engine settled without answering this prompt");
- else keep.push(t);
- }
+ for (const t of state.pending) (t.done || t.accepted ? dropped : keep).push(t);
state.pending = keep;
+ for (const t of dropped) {
+ release(t);
+ failTurn(t, "engine-settled-without-turn", "engine settled without answering this prompt");
+ }
sendHeld();
}
}
@@ -214,21 +260,29 @@ export function createEngine({
// Never accepted: pi will not emit a turn_end for it, so remove it.
const i = state.pending.indexOf(turn);
if (i !== -1) state.pending.splice(i, 1);
+ release(turn);
failTurn(turn, (err.details && err.details.code) || "engine-refused", err.message);
sendHeld();
});
}
+ // Pi is busy from our side while any sent prompt is still queued, even one
+ // that already failed here: a turn that timed out before its agent_start
+ // was read leaves state.busy false while pi runs it, and sending then would
+ // be refused as streaming. It leaves the queue on its agent_end, on a
+ // settle, on a refused send, or at process exit.
+ const engineBusy = () => state.busy || state.pending.length > 0;
+
// After a settle (or a refused send) the oldest held prompt goes out.
function sendHeld() {
- if (state.exited !== null) return;
- if (state.busy || state.pending.some((t) => !t.done)) return;
+ if (state.exited !== null || state.wedged) return;
+ if (engineBusy()) return;
const next = state.held.shift();
if (next) send(next.turn, next.command);
}
function write(command) {
- if (!state.child || state.exited !== null) throw new DiscordError("engine is not running", 1, { code: "engine-down" });
+ if (!state.child || state.exited !== null || state.wedged) throw new DiscordError("engine is not running", 1, { code: "engine-down" });
state.child.stdin.write(JSON.stringify(command) + "\n");
}
@@ -245,6 +299,30 @@ export function createEngine({
});
}
+ function stopChild({ graceMs = 5000 } = {}) {
+ const child = state.child;
+ if (!child || state.exited !== null) return Promise.resolve(state.exited);
+ return new Promise((resolve) => {
+ const timer = setTimeoutImpl(() => {
+ try {
+ child.kill("SIGKILL");
+ } catch {
+ // already gone
+ }
+ }, graceMs);
+ child.once("exit", () => {
+ clearTimeoutImpl(timer);
+ resolve(state.exited);
+ });
+ try {
+ child.stdin.end();
+ child.kill("SIGTERM");
+ } catch {
+ // already gone
+ }
+ });
+ }
+
return {
start() {
if (state.child) throw new DiscordError("engine already started", 1);
@@ -281,7 +359,7 @@ export function createEngine({
// with DiscordError carrying details.code for the turn record.
prompt(text, { timeoutMs = 180000 } = {}) {
if (typeof text !== "string" || text.length === 0) throw new DiscordError("prompt text required", 1);
- const turn = { resolve: null, reject: null, timer: null, done: false, accepted: false, tools: new Map(), turns: 0, last: null };
+ const turn = { resolve: null, reject: null, timer: null, grace: null, done: false, accepted: false, tools: new Map(), turns: 0, last: null };
const done = new Promise((resolve, reject) => {
turn.resolve = resolve;
turn.reject = reject;
@@ -306,44 +384,24 @@ export function createEngine({
}
failTurn(turn, "timeout", `turn timed out after ${timeoutMs} ms`);
}, timeoutMs);
- if (state.exited !== null) {
+ if (state.exited !== null || state.wedged) {
failTurn(turn, "engine-down", "engine is not running");
return done;
}
- if (state.busy || state.pending.some((t) => !t.done) || state.held.length > 0) state.held.push({ turn, command });
+ if (engineBusy() || state.held.length > 0) state.held.push({ turn, command });
else send(turn, command);
return done;
},
get busy() {
- return state.busy || state.pending.some((t) => !t.done) || state.held.length > 0;
+ return engineBusy() || state.held.length > 0;
},
get pendingCount() {
return state.pending.filter((t) => !t.done).length + state.held.length;
},
- stop({ graceMs = 5000 } = {}) {
- const child = state.child;
- if (!child || state.exited !== null) return Promise.resolve(state.exited);
- return new Promise((resolve) => {
- const timer = setTimeoutImpl(() => {
- try {
- child.kill("SIGKILL");
- } catch {
- // already gone
- }
- }, graceMs);
- child.once("exit", () => {
- clearTimeoutImpl(timer);
- resolve(state.exited);
- });
- try {
- child.stdin.end();
- child.kill("SIGTERM");
- } catch {
- // already gone
- }
- });
+ stop(options) {
+ return stopChild(options);
},
};
}
diff --git a/packages/discord/tests/engine.test.mjs b/packages/discord/tests/engine.test.mjs
index 59674d1e..f3bd7525 100644
--- a/packages/discord/tests/engine.test.mjs
+++ b/packages/discord/tests/engine.test.mjs
@@ -4,6 +4,8 @@ import { readFileSync } from "node:fs";
import { join } from "node:path";
import { createEngine, buildPiArgs, PI_FIXED_ARGS, TOOLS_EXTENSION, READONLY_TOOLS_EXTENSION, assistantText } from "../src/engine-pi.mjs";
import { existsSync } from "node:fs";
+import { EventEmitter } from "node:events";
+import { PassThrough } from "node:stream";
import { makeRoot } from "./helpers.mjs";
const fakePi = join(import.meta.dirname, "fake-pi.mjs");
@@ -28,7 +30,20 @@ function start(root, extra = {}) {
log: (m) => logs.push(m), ...extra,
});
engine.start();
- return { engine, logs, commands: () => readFileSync(logPath, "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) };
+ // The fake creates its log on the first command; until then there are none.
+ const commands = () => (existsSync(logPath) ? readFileSync(logPath, "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) : []);
+ return { engine, logs, commands };
+}
+
+// Every test stops its engine in finally: a fake pi left running after a
+// failed assertion keeps the test file from exiting.
+async function withEngine(extra, body) {
+ const started = start(makeRoot(), extra);
+ try {
+ await body(started);
+ } finally {
+ await started.engine.stop();
+ }
}
test("engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file", () => {
@@ -56,8 +71,7 @@ test("engine: with tools, buildPiArgs turns pi's own tools off, loads the extens
assert.equal(rw[rw.indexOf("--tools") + 1], "list_dir,read_file,search,write_file,edit_file", "a writable root adds exactly the two write tools");
});
-test("engine: a run with tool turns settles once, on the answer, with every tool call in the result", async () => {
- const { engine } = start(makeRoot());
+test("engine: a run with tool turns settles once, on the answer, with every tool call in the result", () => withEngine({}, async ({ engine }) => {
const r = await engine.prompt("tools 3");
assert.equal(r.text, "read 3 file(s)");
assert.equal(r.turns, 2);
@@ -71,45 +85,38 @@ test("engine: a run with tool turns settles once, on the answer, with every tool
assert.equal(plain.turns, 1);
await idle(engine);
assert.equal(engine.busy, false);
- await engine.stop();
-});
+}));
-test("engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end", async () => {
- const { engine } = start(makeRoot());
+test("engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end", () => withEngine({}, async ({ engine }) => {
const r = await engine.prompt("toolonly");
assert.equal(r.text, "", "no text: the connector turns this into engine-empty");
assert.equal(r.tools.length, 1);
const again = await engine.prompt("retry");
assert.equal(again.text, "after retry");
- await engine.stop();
-});
+}));
-test("engine: one prompt, one turn, text and usage come back", async () => {
- const { engine } = start(makeRoot());
- try {
- const r = await engine.prompt("hello");
- assert.equal(r.text, "echo: hello");
- assert.deepEqual(r.usage, { input: 3, output: 2 });
- await idle(engine);
- assert.equal(engine.busy, false);
- } finally {
- await engine.stop();
- }
-});
+test("engine: one prompt, one turn, text and usage come back", () => withEngine({}, async ({ engine }) => {
+ const r = await engine.prompt("hello");
+ assert.equal(r.text, "echo: hello");
+ assert.deepEqual(r.usage, { input: 3, output: 2 });
+ await idle(engine);
+ assert.equal(engine.busy, false);
+}));
-test("engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order", async () => {
- const { engine, commands } = start(makeRoot());
- const first = engine.prompt("slow 150");
- await new Promise((r) => setTimeout(r, 20));
+test("engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order", () => withEngine({}, async ({ engine, commands }) => {
+ const first = engine.prompt("slow 300");
assert.equal(engine.busy, true);
const second = engine.prompt("second");
assert.equal(engine.pendingCount, 2);
- await new Promise((r) => setTimeout(r, 20));
- assert.equal(commands().filter((c) => c.type === "prompt").length, 1, "the second prompt is not sent while pi is busy");
+ const prompted = () => commands().filter((c) => c.type === "prompt");
+ assert.ok(await until(() => prompted().length > 0), "the first prompt reached pi");
+ assert.equal(prompted().length, 1, "the second prompt is not sent while pi is busy");
+ // The fake refuses a prompt without streamingBehavior while it runs one, so
+ // an answered second prompt also proves it was not sent early.
const [r1, r2] = await Promise.all([first, second]);
assert.equal(r1.text, "slow reply");
assert.equal(r2.text, "echo: second");
- const prompts = commands().filter((c) => c.type === "prompt");
+ const prompts = prompted();
assert.equal(prompts.length, 2);
// Never a pi follow-up: pi would fold it into the first run and close both
// answers with one agent_end (the live loss of 2026-09-17).
@@ -117,11 +124,9 @@ test("engine: a prompt while streaming is held until pi settles, then sent as it
assert.equal(prompts[1].streamingBehavior, undefined);
await idle(engine);
assert.equal(engine.busy, false);
- await engine.stop();
-});
+}));
-test("engine: a held prompt that times out before pi settles fails on its own and is never sent", async () => {
- const { engine, commands } = start(makeRoot());
+test("engine: a held prompt that times out before pi settles fails on its own and is never sent", () => withEngine({}, async ({ engine, commands }) => {
const first = engine.prompt("slow 200");
await new Promise((r) => setTimeout(r, 20));
await assert.rejects(engine.prompt("late one", { timeoutMs: 50 }), (e) => e.details.code === "timeout" && /waiting for the engine/.test(e.message));
@@ -130,50 +135,177 @@ test("engine: a held prompt that times out before pi settles fails on its own an
await idle(engine);
assert.deepEqual(commands().filter((c) => c.type === "prompt").map((c) => c.message), ["slow 200"]);
assert.deepEqual(commands().filter((c) => c.type === "abort"), [], "a held turn is not aborted; pi never had it");
- await engine.stop();
-});
+}));
-test("engine: timeout sends abort and fails only that turn; the process stays", async () => {
- const { engine, commands, logs } = start(makeRoot());
+test("engine: timeout sends abort and fails only that turn; the process stays", () => withEngine({}, async ({ engine, commands, logs }) => {
await assert.rejects(engine.prompt("slow 5000", { timeoutMs: 100 }), (err) => err.details.code === "timeout");
assert.ok(await until(() => commands().some((c) => c.type === "abort")), "abort reached pi");
assert.ok(logs.some((l) => /timed out/.test(l)));
const r = await engine.prompt("again");
assert.equal(r.text, "echo: again");
- await engine.stop();
+}));
+
+test("engine: tool events from a run that outlived its timeout never land in the next prompt's record", () => withEngine({}, async ({ engine }) => {
+ await assert.rejects(engine.prompt("late 200", { timeoutMs: 40 }), (err) => err.details.code === "timeout");
+ const r = await engine.prompt("after late");
+ assert.equal(r.text, "echo: after late");
+ assert.deepEqual(r.tools, [], "the dead run's read is not this prompt's evidence");
+ assert.equal(r.turns, 1, "the dead run's turns are not counted here");
+}));
+
+// The turn timer is fired by hand, before the engine has read any event from
+// pi, so the timed-out run is still pi's and state.busy is still false when
+// the next prompt arrives. Under load a real timer does the same.
+const TURN_MS = 60000;
+const manualTurnTimer = (fire) => ({
+ setTimeoutImpl: (fn, ms) => (ms === TURN_MS ? fire.push(fn) : setTimeout(fn, ms)),
+ clearTimeoutImpl: (id) => { if (typeof id !== "number") clearTimeout(id); },
});
-test("engine: tool events from a run that outlived its timeout never land in the next prompt's record", async () => {
- const { engine } = start(makeRoot());
- try {
- await assert.rejects(engine.prompt("late 200", { timeoutMs: 40 }), (err) => err.details.code === "timeout");
- const r = await engine.prompt("after late");
+test("engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused", () => {
+ const fire = [];
+ return withEngine(manualTurnTimer(fire), async ({ engine, commands }) => {
+ const late = engine.prompt("late 100", { timeoutMs: TURN_MS });
+ fire.shift()();
+ assert.equal(engine.busy, true, "pi is still running the prompt that timed out");
+ const next = engine.prompt("after late", { timeoutMs: 5000 });
+ assert.equal(engine.pendingCount, 1, "only the new prompt is live");
+ await assert.rejects(late, (err) => err.details.code === "timeout");
+ const r = await next;
assert.equal(r.text, "echo: after late");
assert.deepEqual(r.tools, [], "the dead run's read is not this prompt's evidence");
- assert.equal(r.turns, 1, "the dead run's turns are not counted here");
- } finally {
- await engine.stop();
- }
+ assert.equal(r.turns, 1);
+ assert.deepEqual(commands().map((c) => (c.type === "prompt" ? c.message : c.type)), ["late 100", "abort", "after late"]);
+ await idle(engine);
+ assert.equal(engine.busy, false);
+ });
+});
+
+// "mute" is accepted and never run, so no agent_start, agent_end or settle
+// ever comes for it. Unbounded, it would hold every later prompt.
+test("engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts", async () => {
+ let exited = null;
+ await withEngine({ abortGraceMs: 150, onExit: (e) => (exited = e) }, async ({ engine, commands, logs }) => {
+ await assert.rejects(engine.prompt("mute", { timeoutMs: 50 }), (err) => err.details.code === "timeout");
+ assert.equal(engine.busy, true, "pi might still be running it");
+ const started = Date.now();
+ await assert.rejects(engine.prompt("after mute", { timeoutMs: 5000 }), (err) => err.details.code === "engine-wedged");
+ assert.ok(Date.now() - started >= 100, "held for the grace, not failed at once");
+ await assert.rejects(engine.prompt("later"), (err) => err.details.code === "engine-down");
+ assert.ok(await until(() => exited !== null), "pi exits and onExit hears of it");
+ assert.ok(logs.some((l) => /stopping pi/.test(l)));
+ assert.deepEqual(commands().map((c) => (c.type === "prompt" ? c.message : c.type)), ["mute", "abort"]);
+ });
+});
+
+// Rocko's 6b R1 case: pi is stuck before agent_start, then runs the old
+// prompt and only afterwards reads the next one. The events carry no prompt
+// id, so a prompt sent after the grace would get the old run's answer.
+test("engine: a timed-out turn pi starts only after the grace never answers a later prompt", async () => {
+ let exited = null;
+ await withEngine({ abortGraceMs: 150, onExit: (e) => (exited = e) }, async ({ engine, commands }) => {
+ await assert.rejects(engine.prompt("stall 400", { timeoutMs: 50 }), (err) => err.details.code === "timeout");
+ await assert.rejects(engine.prompt("after stall", { timeoutMs: 5000 }), (err) => err.details.code === "engine-wedged");
+ assert.ok(await until(() => exited !== null), "pi exits and onExit hears of it");
+ await new Promise((res) => setTimeout(res, 400));
+ assert.ok(!commands().some((c) => c.message === "after stall"), "nothing was sent after the grace");
+ });
});
-test("engine: a malformed JSONL line fails the turn, not the process", async () => {
- const { engine, logs } = start(makeRoot());
+// The same case in memory, after Rocko's reproducer: the old run's events
+// arrive after the grace while pi is still exiting. They land on the failed
+// turn, nothing more is written to pi, and only the exit ends the engine.
+// Pi's response to the old prompt comes either before its timeout or only
+// with the late events.
+for (const lateResponse of [false, true]) test(`engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (${lateResponse ? "late" : "early"} prompt response)`, async () => {
+ const timers = [];
+ const written = [];
+ const kills = [];
+ const child = new EventEmitter();
+ child.stdout = new PassThrough();
+ child.stderr = new PassThrough();
+ child.stdin = { write: (s) => { written.push(JSON.parse(s)); return true; }, end: () => {} };
+ child.kill = (signal) => { kills.push(signal); return true; };
+ let exited = null;
+ const engine = createEngine({
+ command: "memory-only", args: [], spawn: () => child, abortGraceMs: 150, onExit: (e) => (exited = e),
+ setTimeoutImpl: (fn, ms) => { const t = { fn, ms, active: true }; timers.push(t); return t; },
+ clearTimeoutImpl: (t) => { t.active = false; },
+ });
+ const emit = (x) => child.stdout.write(JSON.stringify(x) + "\n");
+ const fire = (ms) => { const t = timers.find((x) => x.ms === ms && x.active); assert.ok(t, `timer ${ms}`); t.active = false; t.fn(); };
+ const message = (text) => ({ role: "assistant", content: [{ type: "text", text }], stopReason: "stop" });
+ const tick = () => new Promise((res) => setImmediate(res));
+ // Checked after a tick instead of awaited, so a regression fails here
+ // rather than hanging on a promise nothing will settle.
+ const outcome = (p) => {
+ const o = { state: "pending", code: null, text: null };
+ p.then((v) => Object.assign(o, { state: "resolved", text: v.text }), (e) => Object.assign(o, { state: "rejected", code: e.details && e.details.code }));
+ return o;
+ };
+ engine.start();
+ const first = engine.prompt("old", { timeoutMs: 50 });
+ const accept = () => emit({ type: "response", id: written[0].id, command: "prompt", success: true });
+ if (!lateResponse) accept();
+ await tick();
+ fire(50);
+ await assert.rejects(first, (err) => err.details.code === "timeout");
+ const next = outcome(engine.prompt("new", { timeoutMs: 2000 }));
+ fire(150);
+ await tick();
+ assert.deepEqual(next, { state: "rejected", code: "engine-wedged", text: null });
+ assert.deepEqual(kills, ["SIGTERM"]);
+ if (lateResponse) accept();
+ emit({ type: "agent_start" });
+ emit({ type: "tool_execution_start", toolCallId: "old-call", toolName: "read_file", args: { root: "docs", path: "old.md" } });
+ emit({ type: "tool_execution_end", toolCallId: "old-call", toolName: "read_file", result: { details: { root: "docs", path: "old.md", ok: true } } });
+ emit({ type: "turn_end", message: message("OLD RUN ANSWER") });
+ emit({ type: "agent_end", messages: [message("OLD RUN ANSWER")] });
+ emit({ type: "agent_settled" });
+ await tick();
+ const after = outcome(engine.prompt("after settle", { timeoutMs: 2000 }));
+ await tick();
+ assert.deepEqual(after, { state: "rejected", code: "engine-down", text: null });
+ assert.deepEqual(next, { state: "rejected", code: "engine-wedged", text: null }, "the old answer did not reach the new prompt");
+ assert.deepEqual(written.map((c) => (c.type === "prompt" ? c.message : c.type)), ["old", "abort"], "no prompt reached pi after the grace");
+ assert.equal(exited, null);
+ fire(5000);
+ assert.deepEqual(kills, ["SIGTERM", "SIGKILL"]);
+ child.emit("exit", null, "SIGKILL");
+ assert.deepEqual(exited, { code: null, signal: "SIGKILL" });
+});
+
+test("engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends", async () => {
+ let exited = null;
+ await withEngine({ abortGraceMs: 150, onExit: (e) => (exited = e) }, async ({ engine, commands }) => {
+ await assert.rejects(engine.prompt("late 400", { timeoutMs: 50 }), (err) => err.details.code === "timeout");
+ const r = await engine.prompt("after late", { timeoutMs: 5000 });
+ assert.equal(r.text, "echo: after late");
+ assert.deepEqual(r.tools, []);
+ assert.equal(exited, null, "pi was not stopped");
+ assert.deepEqual(commands().map((c) => (c.type === "prompt" ? c.message : c.type)), ["late 400", "abort", "after late"]);
+ });
+});
+
+test("engine: a malformed JSONL line fails the turn, not the process", () => withEngine({}, async ({ engine, logs }) => {
await assert.rejects(engine.prompt("garbage"), (err) => err.details.code === "engine-protocol");
assert.ok(logs.some((l) => /malformed/.test(l)));
const r = await engine.prompt("still here");
assert.equal(r.text, "echo: still here");
- await engine.stop();
-});
+}));
test("engine: a turn that ends in error rejects with the error code; process exit fails pending turns", async () => {
- const root = makeRoot();
let exited = null;
- const { engine } = start(root, { onExit: (e) => (exited = e) });
- await assert.rejects(engine.prompt("error"), (err) => err.details.code === "engine-error" && /fake provider error/.test(err.message));
- const pending = engine.prompt("slow 5000");
- await new Promise((r) => setTimeout(r, 20));
- await engine.stop();
- await assert.rejects(pending, (err) => err.details.code === "engine-down");
- assert.ok(exited);
- await assert.rejects(engine.prompt("x"), /not running/);
+ const { engine } = start(makeRoot(), { onExit: (e) => (exited = e) });
+ try {
+ await assert.rejects(engine.prompt("error"), (err) => err.details.code === "engine-error" && /fake provider error/.test(err.message));
+ const pending = engine.prompt("slow 5000");
+ await new Promise((r) => setTimeout(r, 20));
+ await engine.stop();
+ await assert.rejects(pending, (err) => err.details.code === "engine-down");
+ assert.ok(exited);
+ await assert.rejects(engine.prompt("x"), /not running/);
+ } finally {
+ await engine.stop();
+ }
});
diff --git a/packages/discord/tests/fake-pi.mjs b/packages/discord/tests/fake-pi.mjs
index 94919306..bf94c013 100644
--- a/packages/discord/tests/fake-pi.mjs
+++ b/packages/discord/tests/fake-pi.mjs
@@ -8,9 +8,13 @@
// then a second turn that answers "read <n> file(s)"
// "toolonly" a run whose only turn calls a tool and never answers
// "retry" an agent_end with willRetry, then the real answer
+// "mute" accept the prompt and emit nothing, staying idle
// "late <ms>" ignore abort; after <ms> emit a tool pair and a tool turn,
// then answer "late reply", like a run that outlives its
// client-side timeout
+// "stall <ms>" accept the prompt, then read nothing for <ms> (pi stuck
+// before agent_start); then run it, answering "echo:
+// stalled", and only then read what came in meanwhile
// anything else answer "echo: <text>" immediately
// A prompt received while busy without streamingBehavior is refused, as pi
// does. A prompt with streamingBehavior followUp is folded into the running
@@ -30,6 +34,7 @@ function assistant(text, stopReason = "stop") {
}
function run(text) {
+ if (text === "mute") return;
busy = true;
out({ type: "agent_start" });
out({ type: "turn_start" });
@@ -109,11 +114,16 @@ function run(text) {
let current = null;
let buffer = "";
+let stalled = false;
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => {
buffer += chunk;
+ drain();
+});
+
+function drain() {
let idx;
- while ((idx = buffer.indexOf("\n")) !== -1) {
+ while (!stalled && (idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
if (!line) continue;
@@ -125,7 +135,15 @@ process.stdin.on("data", (chunk) => {
continue;
}
out({ id: cmd.id, type: "response", command: "prompt", success: true });
- if (busy) queue.push(cmd.message);
+ const sm = /^stall (\d+)$/.exec(cmd.message);
+ if (sm) {
+ stalled = true;
+ setTimeout(() => {
+ run("stalled");
+ stalled = false;
+ drain();
+ }, Number(sm[1]));
+ } else if (busy) queue.push(cmd.message);
else run(cmd.message);
} else if (cmd.type === "abort") {
out({ id: cmd.id, type: "response", command: "abort", success: true });
@@ -139,5 +157,5 @@ process.stdin.on("data", (chunk) => {
out({ id: cmd.id, type: "response", command: cmd.type, success: true, data: {} });
}
}
-});
+}
process.stdin.on("end", () => process.exit(0));
@@ -0,0 +1,25 @@
{
"observedAt": "2026-09-13T19:56:18.961314+00:00",
"issue": 1510,
"ownerAuthorizedLiveSmoke": true,
"researcher": [
{
"session": ".pi/state/researcher/sessions/2026-09-13T19-52-28-745Z_01a09c54-0b48-7154-addd-8fdce875aa4a.jsonl",
"entryId": "64b15fd5",
"timestamp": "2026-09-13T19:52:43.079Z",
"response": "RESEARCHER_NATIVE_SMOKE_OK",
"entrySha256": "d9cbaa5ea640d2b858a86b2fa24d3240b351d9383ffaee9721dd86fcd080c329"
}
],
"rocko": {
"newLaunch": "refused by existing native launch lock",
"existingPid": 3707667,
"cwd": "/mnt/storage/src/mosaic-stack",
"nativeContextVerified": true,
"sonnetFlagVerified": true,
"socket": "mosaic-fleet",
"newModelResponseTested": false
},
"existingProcessesRestarted": false,
"homeLaunchersModified": false
}
@@ -0,0 +1,19 @@
{
"issue": 1510,
"candidate": "/tmp/internal-team-r1-i9t21yzr",
"manifestSha256": "23a27014ce6f04ce8187d8495b2efe62b814c3c7d041b027f99b1ec4d490709d",
"files": [
"AGENTS.md",
"agents/README.md",
"agents/researcher/SOUL.md",
"agents/researcher/CONTEXT.md",
"agents/researcher/README.md",
"agents/researcher/launch.sh",
"agents/researcher/validate-sessions.mjs",
"scripts/test-darkwing-launch.mjs",
"docs/plans/2026-09-13_internal-development-bootstrap.md"
],
"tests": "node --test scripts/test-darkwing-launch.mjs scripts/test-rocko-launch.mjs",
"passed": 6,
"state": "ready for independent review"
}
@@ -0,0 +1,77 @@
# Ledger: T3 header counts as agent (#1506), R1 candidate
Sage assigned this on 2026-09-26 after commit A (af4203ca). Filbert reviews.
Not committed.
## Defect
`messageKind` in `packages/ledger/src/ledger.mjs` knew only the tmux preamble
`[host:session -> host:session]`. A prompt that opens with the T3 header
`[from: sage (1ef1e4f8-…) -> to: filbert (9cb9731e-…) class=actionable]`
counted as human, so Table 2's Human column and the human-per-closed ratio
rise once seats talk over T3. DEFERRED Open entry "Ledger counts T3 agent
messages as human".
## Change
- `messageKind` also matches the T3 header on the first line. The sender is the
`from:` role. `control-board` is board, any other role is agent. Anything short
of the full header stays human. That includes the header on a later line, a
leading space, a missing thread id, `class=` with capitals, `]` followed by a
non-space, and `From:` capitalized. The tmux branch is unchanged.
- Two tests: a Table 2 fixture with two headered prompts and one plain prompt
for seat `bob`, expecting agent 2 and human 1. Also a direct classification table.
- README counting rule names both forms.
## Evidence
- `node --test --test-reporter=tap packages/ledger/tests/`: 22/22 on the
working tree.
- The same test file against HEAD's `ledger.mjs` (full `git archive HEAD` tree):
20/22. The two failures are the two new tests, so they catch the defect.
An earlier archive of `packages/ledger` alone also failed three gitea-helper
tests. Those tests need `scripts/gitea-api.sh`, which the partial archive left out.
- Suites on the working tree: config 24, task 90, foundation 43, conductor 17,
release 14, auth 15, discord 63. None of them runs the ledger tests.
## Frozen files
`r1-manifest.sha256` holds the three file hashes; `r1.patch` is `git diff
packages/ledger` at freeze time.
## Known limit, not fixed here
The fix changes zero current counts. Table 2 reads only
`.pi/state/<seat>/sessions/*.jsonl`, and no file there contains a T3 header
for any seat. Filbert checked this in R1:
- `grep -rF '[from: ' .pi/state/*/sessions/` finds nothing.
- The candidate `messageKind` gives Dewey 52 agent, 7 board and 9 human, and
Sage 193 agent and 72 human. That covers every user message in Pi logs
modified since 2026-09-20. Every non-human first line is the tmux form.
- Filbert's three T3 messages to Dewey on 2026-09-26 are not in
`.pi/state/dewey`.
Dewey's and Sage's Pi logs are current, but they only carry tmux traffic. T3
traffic goes to the harness transcripts: Claude under `~/.claude/projects`,
Codex under `~/.codex/sessions`. The ledger reads neither, so a T3-routed
prompt to any seat counts nowhere, as agent or as human. The Human column
can't see T3 traffic at all. For Darkwing and Filbert, whose newest Pi logs
end 2026-09-14, and for Rocko, who has no Pi sessions directory, zero means an
empty source, not zero human prompts. The fix is correct for a source that
carries T3 headers. Sage asked for a brief on a read-only T3 thread source;
Gate F waits on it.
Filbert also found two misclassifications in older logs. Neither is touched
here:
- `[rev-code-02 -> dragon-lin:sage class=actionable]` has no host on the
sender, so it counts as human.
- One Dewey prompt opens with a quote character before the tmux preamble, so
it counts as human.
## Review
Filbert, R1, 2026-09-26: approved the three frozen files. He verified the
manifest and patch, got 22/22 on the tree and 20/22 against HEAD's source, and
matched the regex to `docs/guides/T3-AGENT-COMMS.md`. He accepts the body on
the header's line, which the tmux branch also allows. He corrected the
known-limit text above.
@@ -0,0 +1,3 @@
e0d411ca2f45d85734eef130dba645646df6e9128ea7dc2eaba2205df7891bb8 packages/ledger/README.md
e24b065c4284370960ac6ff1ed66810fe601da64ae9b9362584fe9fbee334017 packages/ledger/src/ledger.mjs
a9da013e81aff360cb013a8e103fdd111aee7e42553b96cb0811560b3da39250 packages/ledger/tests/ledger.test.mjs
@@ -0,0 +1,89 @@
diff --git a/packages/ledger/README.md b/packages/ledger/README.md
index a998a76c..da0ab1c5 100644
--- a/packages/ledger/README.md
+++ b/packages/ledger/README.md
@@ -36,11 +36,15 @@ No install, build, service restart, or configuration change is needed.
duplicated entries in copied logs are not deduplicated. No transcript content
leaves the parser. Assistant messages and logs outside repo seats do not count.
Symlink source directories are refused and symlink files are not followed.
-- The first text line alone classifies a message. A bracketed addressing
- preamble whose source session is `control-board` is board; any other valid
- addressing preamble is agent; otherwise human. This is a format count, not
- proof of who typed the message. Text blocks are joined with newlines.
- The entry timestamp is used, falling back to the message timestamp.
+- The first text line alone classifies a message. Two addressing forms count:
+ the tmux preamble `[host:session -> host:session]` that `agent-send.sh`
+ writes, and the T3 header `[from: role (thread-id) -> to: role (thread-id)]`
+ from `docs/guides/T3-AGENT-COMMS.md`. Either may carry ` class=<class>` before
+ the closing bracket. A preamble whose sender is `control-board` (tmux session
+ or T3 role) is board; any other valid preamble is agent; otherwise human.
+ This is a format count, not proof of who typed the message. Text blocks are
+ joined with newlines. The entry timestamp is used, falling back to the
+ message timestamp.
- Seats with no in-range user messages are omitted. Issue seats come from `#N`
mentions anywhere in in-range user text, including quoted text.
- Human messages per closed issue divides Table 2's human sum by issues closed
diff --git a/packages/ledger/src/ledger.mjs b/packages/ledger/src/ledger.mjs
index dc8a3a69..b09e95f5 100644
--- a/packages/ledger/src/ledger.mjs
+++ b/packages/ledger/src/ledger.mjs
@@ -77,8 +77,12 @@ export function messageText(content) {
}
export function messageKind(text) {
const firstLine = text.split(/\r?\n/, 1)[0];
- const match = firstLine.match(/^\[([^\s:\[\]]+):([^\s\[\]]+) -> ([^\s:\[\]]+):([^\s\[\]]+)(?: class=[a-z-]+)?\](?:\s|$)/);
- return !match ? 'human' : match[2] === 'control-board' ? 'board' : 'agent';
+ // tmux preamble from agent-send.sh: [host:session -> host:session class=x]
+ const tmux = firstLine.match(/^\[([^\s:\[\]]+):([^\s\[\]]+) -> ([^\s:\[\]]+):([^\s\[\]]+)(?: class=[a-z-]+)?\](?:\s|$)/);
+ // T3 header (docs/guides/T3-AGENT-COMMS.md): [from: role (id) -> to: role (id) class=x]
+ const t3 = firstLine.match(/^\[from: ([^\s()\[\]]+) \(([^()\[\]]+)\) -> to: ([^\s()\[\]]+) \(([^()\[\]]+)\)(?: class=[a-z-]+)?\](?:\s|$)/);
+ const sender = tmux ? tmux[2] : t3 ? t3[1] : null;
+ return sender === null ? 'human' : sender === 'control-board' ? 'board' : 'agent';
}
async function directories(dir, optional = false) {
try {
diff --git a/packages/ledger/tests/ledger.test.mjs b/packages/ledger/tests/ledger.test.mjs
index 7b4e4d33..175f5d5e 100644
--- a/packages/ledger/tests/ledger.test.mjs
+++ b/packages/ledger/tests/ledger.test.mjs
@@ -110,6 +110,19 @@ test('invalid dates, reverse dates and duplicate options refuse', t => {
assert.throws(() => dateRange('2026-02-30')); assert.throws(() => dateRange('2026-09-12', '2026-09-06'));
const f = fixture(t); assert.equal(f.run(['--since', '2026-09-01']).status, 1);
});
+test('T3 agent assignments do not count as human in Table 2', t => {
+ const f = fixture(t);
+ f.put('.pi/state/bob/sessions/t3.jsonl', [
+ f.entry('[from: sage (1ef1e4f8) -> to: bob (9cb9731e) class=actionable]\nassign #1'),
+ f.entry('[from: sage (1ef1e4f8) -> to: bob (9cb9731e)]\nfollow-up #1'),
+ f.entry('Jason: go ahead'),
+ ].map(x => JSON.stringify(x)).join('\n') + '\n');
+ const result = f.run(['--json']);
+ assert.equal(result.status, 0, result.stderr);
+ const r = JSON.parse(result.stdout);
+ assert.deepEqual(r.seats, [{ seat: 'alice', board: 1, agent: 1, human: 1 }, { seat: 'bob', board: 0, agent: 2, human: 1 }]);
+ assert.equal(r.totals.humanMessagesPerClosedIssue, 2);
+});
test('preamble parsing and issue number boundaries', () => {
assert.equal(messageKind('[h:control-board -> h:seat] hi'), 'board');
assert.equal(messageKind('[h:seat -> h:seat class=actionable] hi'), 'agent');
@@ -117,6 +130,20 @@ test('preamble parsing and issue number boundaries', () => {
assert.equal(messageKind(' [h:seat -> h:seat] quoted'), 'human');
assert.deepEqual(issueNumbers('fix #1 #2 #2 abc#3 #0 #4x'), [1, 2]);
});
+test('T3 header: agent, or board from control-board; anything short of the full header is human', () => {
+ const sage = 'sage (1ef1e4f8-3ead-4208-beca-38f9f1add079)', filbert = 'filbert (9cb9731e-a10f-4c8f-a212-c4fa1f5f4731)';
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}]\nbuild #1506`), 'agent');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=actionable]\nbuild`), 'agent');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}] same line`), 'agent');
+ assert.equal(messageKind(`[from: darkwing (thread-id: unknown) -> to: reviewer (new-thread)]\nreview`), 'agent');
+ assert.equal(messageKind(`[from: control-board (b) -> to: ${filbert}]\nhi`), 'board');
+ assert.equal(messageKind(`Jason here\n[from: ${sage} -> to: ${filbert}]\nquoted`), 'human');
+ assert.equal(messageKind(` [from: ${sage} -> to: ${filbert}]`), 'human');
+ assert.equal(messageKind(`[from: sage -> to: filbert]\nno thread ids`), 'human');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=Actionable]`), 'human');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}]trailing`), 'human');
+ assert.equal(messageKind(`[From: ${sage} -> to: ${filbert}]`), 'human');
+});
test('no closed issues with human messages means undefined ratio, not invented zero', () => {
const r = summarize(range, [], [], { rows: [{ seat: 'a', human: 1, board: 0, agent: 0 }], mentions: new Map() });
assert.equal(r.totals.humanMessagesPerClosedIssue, 'unknown');
@@ -0,0 +1,5 @@
0afb0320e9a1833f133426169c389ffb71be3d490e0e402070162aa22e820296 packages/ledger/src/ledger.mjs
d6092a538a059e8869544df903e89a8b9a4c3b6b492645db762b2f03d5630a44 packages/ledger/src/cli.mjs
dfb092aabee5cf5197029c6e8978df570ee50f08d84babde931c6a763befafe9 packages/ledger/src/t3.mjs
7444abd1dbd8e637705def0fd98105e6e69970f1bd397a11f8277996521579d6 packages/ledger/tests/ledger.test.mjs
27f7366dd5edc30a93a8c54bfb46b3fed87e1a44f11e1b22bd159a0718625273 packages/ledger/README.md
@@ -0,0 +1,161 @@
# Gate F build: the ledger's T3 source (#1506), candidate for review
Darkwing built this on 2026-09-26 from the approved brief R3,
`docs/plans/2026-09-26_ledger-t3-source.md` (sha256 f3c05c1b, committed in
ffc22c04). Sage gave the go once Filbert confirmed R3. Filbert reviews the
code; Sage commits after the suites. Base is HEAD 1c5f6bc3. Nothing is
committed or pushed.
## Files
`build-manifest.sha256` pins the five files, and `build.patch` is the diff
against 1c5f6bc3 with `t3.mjs` included as a new file.
- `packages/ledger/src/t3.mjs` (new). `readT3(root, range, {dbPath, isDefault})`:
path checks, one read transaction, schema check, project, title mapping,
header cross-check, counts, mentions, diagnostic.
- `packages/ledger/src/ledger.mjs`. The class fix, `t3Header()`,
`readSeats()`, `mergeSources()`, the `pi` and `t3` keys in the report, the
text line for `--no-t3` or a non-default path, and the U+2028 fix below.
- `packages/ledger/src/cli.mjs`. `--no-t3` and `--t3-db PATH`, which refuse
each other; the usage line.
- `packages/ledger/tests/ledger.test.mjs`. HOME at both spawn sites, the
empty default database, 25 new tests.
- `packages/ledger/README.md`. A new "T3 source" section.
The commit should also carry Filbert's updated review,
`agents/filbert/work/ledger-t3-source-review-2026-09-26.md` (be1aa414), and
this directory's new files.
## Beyond the brief: the Pi reader split valid lines
The brief's live read has to exit 0. It didn't, and T3 wasn't the cause. HEAD
refuses the live checkout the same way:
`Malformed session JSON: filbert/2026-09-12T16-38-58-597Z_01a0967c-….jsonl:611`.
That line parses. It holds a raw U+2028 inside a JSON string, which JSON
allows and `JSON.stringify` writes unescaped. Node 26.8.1's `readline` ends a
line at U+2028 too, so it cut the record in two (733 lines by `readline`, 732
by `\n`). The reader parses every line before it checks the range, so on
Node 26.8.1 every live run refuses, whatever the dates. The file was last
written 2026-09-14. I haven't checked which Node version first split there.
The fix replaces `readline` with a small splitter that ends lines at `\n`
only. It sits in `ledger.mjs`, which this build already changes, and it
blocked acceptance, so I made it here instead of filing it. A new test writes a
Pi log with a raw U+2028 and CRLF endings; it fails with `readline` and passes
with the splitter. Please review it as its own item.
## Choices the brief left open
- Imported threads are excluded by the `import:` prefix alone. Live, all
1678 `historyImport` events sit in `import:` streams, so the two rules agree
today. The events table stays optional, so the exclusion doesn't depend on it.
- The header cross-check runs over every user message in a counted thread, in
range or not. The title mapping is current state, so a conflict in old
history still misassigns counts for any range that includes it.
- Validation (role, text, `created_at`) also covers every message in a
counted thread, assistant rows included, and not only rows in range.
- The diagnostic is in range: `humanSentThroughApi` and `humanWithoutEvent`.
One unparseable event, or an event with no string `messageId`, makes both
`unknown`, the same as a missing table (F5).
- Project and thread matching compare `workspace_root` in JavaScript, so a
declared collation on the column can't loosen byte-for-byte equality.
- JSON adds top-level `pi` (Pi rows) and `t3` (read flag, database, seats with
threads, unmapped, excluded, diagnostic). `seats` and `totals` keep their
shape, so existing consumers and tests are unchanged. `t3.seats` lists a
seat whenever it has a mapped thread, even with zero counts in range.
- The unmapped row comes last in `seats`, and only when it has counts.
## Evidence
- Ledger tests: `node --test packages/ledger/tests/`, 47/47 (ledger 44,
of which 25 are new, and gitea helper 3). The busy-timeout test takes about 5.4 s.
- Class fix against HEAD. HEAD's `messageKind` (from `git show
1c5f6bc3:packages/ledger/src/ledger.mjs`) calls a T3 header with
`class=REVIEW-REQUEST`, a tmux preamble with `class=DECISION` and a T3 header
with `class=Actionable` all human. The build calls them agent. The existing
test asserting `class=Actionable` is human now asserts agent.
- Mutations, each on a scratch copy of the package. Three `gitea-helper`
tests fail in every scratch copy because they need the repository's
`scripts/`, so the counts below leave them out.
- Classes back to `[a-z-]+`: 6 fail.
- No header cross-check: 2 fail.
- No symlink refusal: 4 fail.
- Busy timeout 0: 1 fails.
- Two projects allowed: 1 fails.
- Deleted threads kept, imported threads kept, or range filter removed:
4 fail each.
- No role check: 1 fails.
- No diagnostic table check: 1 fails.
- `readline` restored: 1 fails.
- Two mutations pass, and I'm naming them rather than hiding them:
- Removing `mode=ro` changes nothing, because `readOnly: true` already
opens read-only. Both stay, as the brief says.
- Removing `BEGIN` fails 19 tests, but only because `COMMIT` then has no
transaction. No test proves that the queries share one snapshot.
- Eight suites on a local clone of 1c5f6bc3 with the five files: config 24,
task 90, foundation 43, conductor 17, release 14, auth 15, discord 63,
extension-package 18. The first task run showed 89/1, and I didn't capture
the failing line. Three more task runs passed 90/90. I count it as a flake
I can't name, not as green on the first try.
- Union (control-board, webui, seat, mosaic, ledger, discord) on the same
clone: 434/434 three times, 23 to 24 s each. No fake pi left running.
- No test opens the real `~/.t3`. Every CLI spawn sets `HOME` to a temp
directory, and no test calls `readT3` in process. After the runs, no
`ledger-*` temp directories remained.
## Live read
`node packages/ledger/src/cli.mjs --since 2026-09-01 --until 2026-09-26
--no-issues`, exit 0 three times, no header conflict. The table is the run at
2026-09-26T21:31:02Z.
| Seat | T3 threads | T3 board / agent / human | Pi board / agent / human |
|---|---|---|---|
| darkwing | Darkwing; Darkwing in Claude (archived) | 0 / 19 / 28 | 14 / 109 / 141 |
| dewey | Dewey; Dewey in Claude | 0 / 17 / 7 | 7 / 57 / 16 |
| filbert | Filbert | 0 / 25 / 1 | 5 / 92 / 9 |
| rocko | Rocko | 0 / 20 / 1 | none |
| sage | Sage | 0 / 52 / 10 | 0 / 193 / 72 |
| researcher | none | none | 3 / 1 / 1 |
| t3:unmapped | Discord Bot | 0 / 0 / 68 | none |
This matches the brief, allowing for messages sent since 20:54Z. It maps the
same seven threads. Discord Bot has 68 human: 54 without a header and the 14
free-text headers. The diagnostic reads exactly those 14
(`humanSentThroughApi: 14`, `humanWithoutEvent: 0`). T3 agent messages total
133, against the brief's 96 API headers (80 plus the 16 uppercase ones) at
20:54Z. Two imported threads are excluded, and this project has no deleted
threads.
## Not covered
- Snapshot isolation across the queries (see the `BEGIN` mutation above).
- A seat directory named `t3:unmapped` would share the unmapped row. Directory
names that contain a colon aren't used in `agents/`.
- The live read's effect on the main database file can't be checked while T3
writes to it. The stopped and writer-attached WAL tests check it on
fixtures.
## Review and correction
Filbert approved manifest ba73a163 and the U+2028 fix as its own item:
`agents/filbert/work/ledger-t3-build-review-2026-09-26.md`, sha256 e47ec6da.
Correction to "Beyond the brief" above. Line 611 holds a raw U+2028 and a raw
U+2029, and `readline` ends a line at each. The file has 731 lines by `\n`
(`wc -l` agrees), and `readline` makes 733. I wrote 732 because I counted the
empty string after the final newline. The splitter already ends lines at `\n`
only, so the fix covers both characters. The test and the README name only
U+2028.
Filbert's nonblocking notes, for a follow-up after the Gate F commit, since
changing the pinned files now would void the approval:
1. Add a U+2029 to the splitter test and the README line.
2. Two diagnostic mutations survive: `humanWithoutEvent` hardcoded to 0, and
an unparseable event skipped instead of making the diagnostic `unknown`.
Each needs one fixture message.
3. `readT3`'s catch reports any error that isn't a `SourceError` as a SQLite
read failure. It still exits 1, but a bug would read as a database
problem. Rethrow errors that carry no `errcode`.
4. Snapshot isolation stays untested, as recorded above.
@@ -0,0 +1,828 @@
diff --git a/packages/ledger/README.md b/packages/ledger/README.md
index da0ab1c5..393e9c37 100644
--- a/packages/ledger/README.md
+++ b/packages/ledger/README.md
@@ -1,13 +1,16 @@
# Ledger
Read-only counts from local `refactor` commit subjects, one Gitea issue-list
-request through `scripts/gitea-api.sh`, and repo seats' Pi session logs.
+request through `scripts/gitea-api.sh`, repo seats' Pi session logs, and T3's
+thread messages in `~/.t3/userdata/state.sqlite`.
No board changes, data-root writes, fleet reads, transcript output, or scheduler.
```sh
node packages/ledger/src/cli.mjs --since 2026-09-06 --until 2026-09-12
node packages/ledger/src/cli.mjs --since 2026-09-06 --until 2026-09-12 --json
node packages/ledger/src/cli.mjs --since 2026-09-06 --no-issues
+node packages/ledger/src/cli.mjs --since 2026-09-06 --no-t3
+node packages/ledger/src/cli.mjs --since 2026-09-06 --t3-db /tmp/fixture.sqlite
node --test packages/ledger/tests/
```
@@ -36,12 +39,17 @@ No install, build, service restart, or configuration change is needed.
duplicated entries in copied logs are not deduplicated. No transcript content
leaves the parser. Assistant messages and logs outside repo seats do not count.
Symlink source directories are refused and symlink files are not followed.
+ A line ends at `\n` only. A U+2028 inside a JSON string does not split a record.
+- Table 2 also counts T3 thread messages with role `user`. The T3 source
+ follows. A seat's row sums its Pi and T3 counts; the JSON keeps the split in
+ `pi` (Pi rows) and `t3.seats` (T3 rows).
- The first text line alone classifies a message. Two addressing forms count:
the tmux preamble `[host:session -> host:session]` that `agent-send.sh`
writes, and the T3 header `[from: role (thread-id) -> to: role (thread-id)]`
from `docs/guides/T3-AGENT-COMMS.md`. Either may carry ` class=<class>` before
the closing bracket. A preamble whose sender is `control-board` (tmux session
or T3 role) is board; any other valid preamble is agent; otherwise human.
+ The class may be in either case: seats send `class=DECISION`.
This is a format count, not proof of who typed the message. Text blocks are
joined with newlines. The entry timestamp is used, falling back to the
message timestamp.
@@ -53,6 +61,69 @@ No install, build, service restart, or configuration change is needed.
ratios and durations with one decimal. Titles truncate to 48 characters in
text only. Missing evidence is the literal string `unknown`.
+## T3 source
+
+The rules come from `docs/plans/2026-09-26_ledger-t3-source.md` (Gate F).
+The source is on by default. `--no-t3` skips it, and the report then says
+`T3: not read (--no-t3)`. `--t3-db <path>` reads another database file with
+the same checks. The JSON records the database path and whether it was the
+default. When it wasn't, the text report prints the path, so a fixture result
+can't pass for a live one. The two flags can't be combined.
+
+The reader opens `state.sqlite` read-only through `node:sqlite` and reads no
+other file in `~/.t3`. It runs every query in one read transaction with a 5 s
+busy timeout. It never writes the main database file. Like any SQLite
+connection it may create `-wal` and `-shm` beside it, so a directory that
+isn't writable refuses when SQLite needs them.
+
+- **Project.** Only threads in the one non-deleted T3 project whose
+ `workspace_root` equals this checkout's root byte for byte. The root is the
+ realpath of the package, so a project opened through the compatibility
+ symlink `~/src/mosaic-stack-dev-test` does not match, and the report refuses
+ with no project.
+- **Thread to seat.** A thread belongs to seat `<s>` when `<s>` is a real
+ directory in `agents/` and the lower-cased title equals `<s>` or starts
+ with `<s>` and a space. "Dewey in Claude" maps to `dewey`; "Sagebrush" maps
+ to nothing. Several threads can map to one seat. Threads that map to no
+ seat share one row, `t3:unmapped`, so their human messages still reach the
+ totals. `t3.seats` and `t3.unmapped` in the JSON list the thread ids and
+ titles behind each row.
+- **Titles are current state.** T3 titles an unnamed thread from its first
+ prompt, and a rename moves a thread's whole history to another row. This
+ moves counts between rows, never out of the totals.
+- **Header check.** A user message whose T3 header is addressed to its own
+ thread id must name that thread's seat as the `to:` role (compared lower
+ case). In an unmapped thread the `to:` role must not be a seat. A conflict
+ exits 1 and names the thread, its title and both roles. A header addressed
+ to another thread isn't checked. The check misses a renamed thread that no
+ agent writes to. Such a thread can only add human counts to a row.
+- **Excluded.** Imported threads (id prefix `import:`) are partial copies of
+ Claude Code sessions, not T3 traffic; every T3 event marked `historyImport`
+ sits in one today. Deleted threads don't count; archived threads do.
+ `t3.excluded` gives both thread counts.
+- **Blind spot.** Threads in other T3 projects are not counted, even if they
+ worked on this repository. Live, there is a project at `/home/jwoltje` and
+ a deleted one at `/mnt/storage/src`.
+- **Diagnostic.** `t3.diagnostic.humanSentThroughApi` counts in-range user
+ messages the header rule calls human that T3 recorded as sent through its
+ API (no `appVersion` in the event's origin). Those are seat messages whose
+ header the rule doesn't accept, such as the older free-text Discord Bot
+ headers, and would show the next format drift. `humanWithoutEvent` counts
+ human messages with no `thread.message-sent` event. This is T3's internal
+ metadata, so it feeds no table or total. If `orchestration_events` or a
+ column it needs is missing, or an event doesn't parse, both read `unknown`.
+
+These refuse the report with exit 1, and the ones about the database name
+`--no-t3`: a missing, unreadable or unopenable database (including a busy
+lock past the timeout); a symlink at `~/.t3`, `~/.t3/userdata` or
+`state.sqlite` (with `--t3-db`, the file or its directory); a missing table or
+column the counts need; no project or more than one for this root; a message
+in a counted thread with a role other than `user` or `assistant`, non-text
+content, or a `created_at` that doesn't parse; a header conflict. A missing Pi
+directory means no Pi seats ran here; a missing T3 database means the path or
+T3 changed, so it refuses instead of counting zero. Error messages name ids
+and paths, never message text.
+
## One Gitea call and missing evidence
The client requests issues updated since the start date, all states, first page,
@@ -62,8 +133,8 @@ Use a narrower range or `--no-issues`, not hidden pagination. A commit-linked
issue not returned by the updated-since query still has a row, with unknown
metadata. This is the cost of the brief's one-call boundary.
-Exit 0 means a report was computed. Exit 1 means bad arguments or unreadable git
-or session evidence. Malformed JSONL, including a partially written last line,
+Exit 0 means a report was computed. Exit 1 means bad arguments or unreadable git,
+session or T3 evidence. Malformed JSONL, including a partially written last line,
refuses the report; rerun after the seat finishes writing. Exit 2 means issue
credentials, API, payload, or completeness failure. The CLI never prints API
error bodies or reads authentication files itself. `--no-issues` makes no API
@@ -72,8 +143,9 @@ median duration, and human-per-closed ratio. It cannot invent close-only rows.
For fixtures, a fake `gitea-api.sh` can be placed first on PATH. Otherwise the
repository scripts directory is appended to PATH for the issue request.
-Tests use only temporary repositories, logs, and fake API tools, with no real
-credentials or network. The helper regression stubs Node before any credential
+Tests use only temporary repositories, logs, T3 databases and fake API tools,
+with no real credentials or network. Every CLI run in the tests sets `HOME` to
+a temporary directory, so no test opens the real `~/.t3`. The helper regression stubs Node before any credential
read and checks successful GET, successful POST, and failed HTTP status.
## Acceptance
diff --git a/packages/ledger/src/cli.mjs b/packages/ledger/src/cli.mjs
index 66cd1417..5cf6705c 100644
--- a/packages/ledger/src/cli.mjs
+++ b/packages/ledger/src/cli.mjs
@@ -1,11 +1,12 @@
#!/usr/bin/env node
import path from 'node:path';
import { fileURLToPath } from 'node:url';
-import { dateRange, readCommits, readIssues, readSessions, summarize, formatTable, SourceError } from './ledger.mjs';
+import { dateRange, readCommits, readIssues, readSessions, mergeSources, summarize, formatTable, SourceError } from './ledger.mjs';
+import { readT3, defaultT3Path } from './t3.mjs';
-const usage = 'Usage: node packages/ledger/src/cli.mjs --since YYYY-MM-DD [--until YYYY-MM-DD] [--json] [--no-issues]';
+const usage = 'Usage: node packages/ledger/src/cli.mjs --since YYYY-MM-DD [--until YYYY-MM-DD] [--json] [--no-issues] [--no-t3 | --t3-db PATH]';
export async function main(args, root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..')) {
- let since, until, json = false, noIssues = false;
+ let since, until, t3Db, json = false, noIssues = false, noT3 = false;
const seen = new Set();
for (let i = 0; i < args.length; i++) {
const flag = args[i];
@@ -14,13 +15,18 @@ export async function main(args, root = path.resolve(path.dirname(fileURLToPath(
if (flag === '--help') { console.log(usage); return; }
if (flag === '--json') json = true;
else if (flag === '--no-issues') noIssues = true;
- else if (flag === '--since' || flag === '--until') {
+ else if (flag === '--no-t3') noT3 = true;
+ else if (flag === '--t3-db') {
+ t3Db = args[++i];
+ if (!t3Db || t3Db.startsWith('--')) throw new SourceError('--t3-db requires a path');
+ } else if (flag === '--since' || flag === '--until') {
const value = args[++i];
if (!value || value.startsWith('--')) throw new SourceError(`${flag} requires a date`);
if (flag === '--since') since = value; else until = value;
} else throw new SourceError('Unknown option; ' + usage);
}
if (!since) throw new SourceError(usage);
+ if (noT3 && t3Db !== undefined) throw new SourceError('--no-t3 and --t3-db cannot be combined');
const range = dateRange(since, until);
const commits = readCommits(root, range);
// Fixture tools may be placed first on PATH. The repository client is the
@@ -30,7 +36,9 @@ export async function main(args, root = path.resolve(path.dirname(fileURLToPath(
let issues;
try { issues = noIssues ? null : readIssues(root, range); }
finally { if (priorPath === undefined) delete process.env.PATH; else process.env.PATH = priorPath; }
- const sessions = await readSessions(root, range);
+ // T3 is on by default. A missing or unreadable database refuses the report.
+ const t3 = noT3 ? null : await readT3(root, range, t3Db === undefined ? { dbPath: defaultT3Path(), isDefault: true } : { dbPath: t3Db, isDefault: false });
+ const sessions = mergeSources(await readSessions(root, range), t3);
const report = summarize(range, commits, issues, sessions);
console.log(json ? JSON.stringify(report, null, 2) : formatTable(report));
return report;
diff --git a/packages/ledger/src/ledger.mjs b/packages/ledger/src/ledger.mjs
index b09e95f5..3669d1cc 100644
--- a/packages/ledger/src/ledger.mjs
+++ b/packages/ledger/src/ledger.mjs
@@ -1,7 +1,6 @@
import { execFileSync } from 'node:child_process';
import { createReadStream } from 'node:fs';
import { readdir, lstat } from 'node:fs/promises';
-import { createInterface } from 'node:readline';
import path from 'node:path';
const DAY = 86400000;
@@ -23,7 +22,7 @@ export function dateRange(since, until = new Date().toISOString().slice(0, 10))
if (end <= start) throw new SourceError('--until must not precede --since');
return { since, until, start, end };
}
-const inRange = (value, range) => {
+export const inRange = (value, range) => {
const ms = typeof value === 'number' ? value : Date.parse(value);
return Number.isFinite(ms) && ms >= range.start && ms < range.end;
};
@@ -75,15 +74,32 @@ export function messageText(content) {
if (Array.isArray(content)) return content.filter(c => c?.type === 'text' && typeof c.text === 'string').map(c => c.text).join('\n');
return '';
}
+// Classes are matched in either case: seats send DECISION and REVIEW-REQUEST.
+// tmux preamble from agent-send.sh: [host:session -> host:session class=x]
+const TMUX = /^\[([^\s:\[\]]+):([^\s\[\]]+) -> ([^\s:\[\]]+):([^\s\[\]]+)(?: class=[A-Za-z-]+)?\](?:\s|$)/;
+// T3 header (docs/guides/T3-AGENT-COMMS.md): [from: role (id) -> to: role (id) class=x]
+const T3 = /^\[from: ([^\s()\[\]]+) \(([^()\[\]]+)\) -> to: ([^\s()\[\]]+) \(([^()\[\]]+)\)(?: class=[A-Za-z-]+)?\](?:\s|$)/;
+const firstLine = text => text.split(/\r?\n/, 1)[0];
+export function t3Header(text) {
+ const m = firstLine(text).match(T3);
+ return m ? { from: m[1], fromId: m[2], to: m[3], toId: m[4] } : null;
+}
export function messageKind(text) {
- const firstLine = text.split(/\r?\n/, 1)[0];
- // tmux preamble from agent-send.sh: [host:session -> host:session class=x]
- const tmux = firstLine.match(/^\[([^\s:\[\]]+):([^\s\[\]]+) -> ([^\s:\[\]]+):([^\s\[\]]+)(?: class=[a-z-]+)?\](?:\s|$)/);
- // T3 header (docs/guides/T3-AGENT-COMMS.md): [from: role (id) -> to: role (id) class=x]
- const t3 = firstLine.match(/^\[from: ([^\s()\[\]]+) \(([^()\[\]]+)\) -> to: ([^\s()\[\]]+) \(([^()\[\]]+)\)(?: class=[a-z-]+)?\](?:\s|$)/);
- const sender = tmux ? tmux[2] : t3 ? t3[1] : null;
+ const tmux = firstLine(text).match(TMUX), t3 = t3Header(text);
+ const sender = tmux ? tmux[2] : t3 ? t3.from : null;
return sender === null ? 'human' : sender === 'control-board' ? 'board' : 'agent';
}
+// JSONL lines end at \n only. readline also ends a line at U+2028, which JSON
+// allows raw inside a string, so it split valid records (Node 26.8.1).
+async function* jsonLines(input) {
+ let rest = '';
+ for await (const chunk of input) {
+ const parts = (rest + chunk).split('\n');
+ rest = parts.pop();
+ yield* parts;
+ }
+ if (rest) yield rest;
+}
async function directories(dir, optional = false) {
try {
if (!(await lstat(dir)).isDirectory()) throw new SourceError('Session source must be a real directory');
@@ -93,11 +109,15 @@ async function directories(dir, optional = false) {
throw new SourceError(`Cannot read ledger directory: ${dir}`);
}
}
+// Seats are the real directories in agents/, sorted.
+export async function readSeats(root) {
+ return (await directories(path.join(root, 'agents'))).filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => a.localeCompare(b));
+}
export async function readSessions(root, range) {
const rows = [];
const mentions = new Map();
// No symlink traversal, no fleet paths, no transcript content in the report.
- const agents = (await directories(path.join(root, 'agents'))).filter(e => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
+ const agents = (await readSeats(root)).map(name => ({ name }));
const state = path.join(root, '.pi', 'state');
// Check every source ancestor, not only the leaf directory.
if (!(await directories(path.join(root, '.pi'), true)).length) return { rows, mentions };
@@ -109,11 +129,10 @@ export async function readSessions(root, range) {
const files = (await directories(dir, true)).filter(e => e.isFile() && e.name.endsWith('.jsonl'));
const row = { seat: agent.name, board: 0, agent: 0, human: 0 };
for (const file of files) {
- const input = createReadStream(path.join(dir, file.name));
- const lines = createInterface({ input, crlfDelay: Infinity });
+ const input = createReadStream(path.join(dir, file.name), { encoding: 'utf8' });
let lineNumber = 0;
try {
- for await (const line of lines) {
+ for await (const line of jsonLines(input)) {
lineNumber++;
if (!line.trim()) continue;
let entry;
@@ -132,12 +151,33 @@ export async function readSessions(root, range) {
mentions.get(number).add(agent.name);
}
}
- } finally { lines.close(); input.destroy(); }
+ } finally { input.destroy(); }
}
if (row.board + row.agent + row.human) rows.push(row);
}
return { rows, mentions };
}
+// Adds T3 counts to the Pi rows per seat. Unmapped T3 threads get one row,
+// last. The report keeps the Pi rows and the T3 section so the split shows.
+export function mergeSources(pi, t3, unmapped = 't3:unmapped') {
+ if (!t3) return { rows: pi.rows, mentions: pi.mentions, pi: pi.rows, t3: { read: false } };
+ const bySeat = new Map(pi.rows.map(r => [r.seat, { ...r }]));
+ for (const [seat, counts] of t3.rows) {
+ if (seat === unmapped || !(counts.board + counts.agent + counts.human)) continue;
+ const row = bySeat.get(seat) ?? { seat, board: 0, agent: 0, human: 0 };
+ for (const kind of ['board', 'agent', 'human']) row[kind] += counts[kind];
+ bySeat.set(seat, row);
+ }
+ const rows = [...bySeat.values()].sort((a, b) => a.seat.localeCompare(b.seat));
+ const extra = t3.rows.get(unmapped);
+ if (extra.board + extra.agent + extra.human) rows.push({ seat: unmapped, ...extra });
+ const mentions = new Map([...pi.mentions].map(([n, seats]) => [n, new Set(seats)]));
+ for (const [n, seats] of t3.mentions) {
+ if (!mentions.has(n)) mentions.set(n, new Set());
+ for (const seat of seats) mentions.get(n).add(seat);
+ }
+ return { rows, mentions, pi: pi.rows, t3: t3.section };
+}
const round = value => Math.round(value * 10) / 10;
function duration(issue) {
if (!issue) return UNKNOWN;
@@ -163,13 +203,14 @@ export function summarize(range, commits, issues, sessions) {
const median = hours.includes(UNKNOWN) ? UNKNOWN : hours.length ?
round(hours.length % 2 ? hours[middle] : (hours[middle - 1] + hours[middle]) / 2) : 0;
const human = sessions.rows.reduce((sum, r) => sum + r.human, 0);
- return { since: range.since, until: range.until, timezone: 'UTC', issues: rows, seats: sessions.rows,
+ const sources = sessions.t3 ? { pi: sessions.pi, t3: sessions.t3 } : {};
+ return { since: range.since, until: range.until, timezone: 'UTC', issues: rows, seats: sessions.rows, ...sources,
totals: { issuesClosed: issues === null ? UNKNOWN : closed.length,
medianHoursOpen: issues === null ? UNKNOWN : median, commits: commits.length,
followUpsPerIssue: rows.length ? round(rows.reduce((sum, r) => sum + r.followUps, 0) / rows.length) : 0,
humanMessagesPerClosedIssue: issues === null ? UNKNOWN : closed.length ? round(human / closed.length) : human ? UNKNOWN : 0 } };
}
-const clean = value => String(value).replace(/[\x00-\x1f\x7f-\x9f]/g, ' ');
+export const clean = value => String(value).replace(/[\x00-\x1f\x7f-\x9f]/g, ' ');
const decimal = value => typeof value === 'number' ? value.toFixed(1) : value;
export function totalsLine(t) {
return `Totals: issues closed ${t.issuesClosed} | median hours open ${decimal(t.medianHoursOpen)} | commits ${t.commits} | follow-ups per issue ${decimal(t.followUpsPerIssue)} | human messages per closed issue ${decimal(t.humanMessagesPerClosedIssue)}`;
@@ -181,5 +222,12 @@ export function formatTable(report) {
decimal(r.hoursOpen), r.commits, r.followUps, r.seats.join(', ')].join(' | ')),
'', 'Seat | Board | Agent | Human',
...report.seats.map(r => [clean(r.seat), r.board, r.agent, r.human].join(' | ')),
+ ...t3Line(report.t3),
'', totalsLine(report.totals)].join('\n');
}
+// One line when T3 was skipped or read from somewhere other than the default.
+function t3Line(t3) {
+ if (!t3) return [];
+ if (!t3.read) return ['T3: not read (--no-t3)'];
+ return t3.database.default ? [] : [`T3: read from ${clean(t3.database.path)}, not the default`];
+}
diff --git a/packages/ledger/tests/ledger.test.mjs b/packages/ledger/tests/ledger.test.mjs
index 175f5d5e..8e60b96b 100644
--- a/packages/ledger/tests/ledger.test.mjs
+++ b/packages/ledger/tests/ledger.test.mjs
@@ -1,6 +1,7 @@
import test from 'node:test';
import assert from 'node:assert/strict';
-import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, cpSync, symlinkSync } from 'node:fs';
+import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, cpSync, symlinkSync, realpathSync } from 'node:fs';
+import { DatabaseSync } from 'node:sqlite';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -9,13 +10,50 @@ import { dateRange, messageKind, issueNumbers, totalsLine, summarize } from '../
const source = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../src');
const range = dateRange('2026-09-06', '2026-09-12');
+// T3 fixture schema: the live tables, cut to the columns the reader uses plus
+// one it doesn't. `text` allows NULL so a non-text row can be tested.
+const T3_SCHEMA = `
+ create table projection_projects (project_id text primary key, title text not null, workspace_root text not null, deleted_at text);
+ create table projection_threads (thread_id text primary key, project_id text not null, title text not null, archived_at text, deleted_at text);
+ create table projection_thread_messages (message_id text primary key, thread_id text not null, role text not null, text, created_at text not null);
+ create table orchestration_events (sequence integer primary key autoincrement, stream_id text not null, event_type text not null, payload_json text not null, metadata_json text not null);`;
+// Writes a T3 database in WAL mode. Threads default to project p1, which is
+// the fixture root. Returns the open writer when keepOpen is set.
+function t3db(file, { root, projects, threads = [], messages = [], after = [], keepOpen = false }) {
+ mkdirSync(path.dirname(file), { recursive: true });
+ for (const old of [file, `${file}-wal`, `${file}-shm`]) rmSync(old, { force: true });
+ const db = new DatabaseSync(file);
+ db.exec('pragma journal_mode=wal'); db.exec(T3_SCHEMA);
+ for (const [id, workspace, deleted = null] of projects ?? [['p1', root]]) {
+ db.prepare('insert into projection_projects values (?, ?, ?, ?)').run(id, 'project', workspace, deleted);
+ }
+ for (const t of threads) {
+ db.prepare('insert into projection_threads values (?, ?, ?, ?, ?)').run(t.id, t.project ?? 'p1', t.title, t.archived ?? null, t.deleted ?? null);
+ }
+ for (const m of messages) addMessage(db, m);
+ for (const sql of after) db.exec(sql);
+ if (keepOpen) return db;
+ db.close();
+}
+let messageId = 0;
+function addMessage(db, { thread, text, role = 'user', at = '2026-09-08T12:00:00Z', origin = 'app' }) {
+ const id = `m${++messageId}`;
+ db.prepare('insert into projection_thread_messages values (?, ?, ?, ?, ?)').run(id, thread, role, text, at);
+ if (origin !== 'none') db.prepare('insert into orchestration_events (stream_id, event_type, payload_json, metadata_json) values (?, ?, ?, ?)')
+ .run(thread, 'thread.message-sent', JSON.stringify({ messageId: id, threadId: thread, role, text }), JSON.stringify({ origin: origin === 'app' ? { appVersion: '0.0.0' } : {} }));
+}
const fixtureIssues = [
{ number: 1, title: 'First issue', created_at: '2026-09-06T00:00:00Z', closed_at: '2026-09-07T12:00:00Z' },
{ number: 2, title: 'Second issue', created_at: '2026-09-06T00:00:00Z', closed_at: null },
];
function fixture(t) {
const root = mkdtempSync(path.join(os.tmpdir(), 'ledger-test-'));
- t.after(() => rmSync(root, { recursive: true, force: true }));
+ // No test opens the real ~/.t3: every CLI run gets this HOME, with an empty
+ // T3 database at the default path. The CLI's root is a realpath.
+ const home = mkdtempSync(path.join(os.tmpdir(), 'ledger-home-'));
+ t.after(() => { rmSync(root, { recursive: true, force: true }); rmSync(home, { recursive: true, force: true }); });
+ const defaultDb = path.join(home, '.t3/userdata/state.sqlite');
+ t3db(defaultDb, { root: realpathSync(root) });
const put = (name, data) => { const p = path.join(root, name); mkdirSync(path.dirname(p), { recursive: true }); writeFileSync(p, data); return p; };
const git = (args, date = '2026-09-07T00:00:00Z') => execFileSync('git', args, { cwd: root, env: { ...process.env, GIT_AUTHOR_DATE: date, GIT_COMMITTER_DATE: date, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null' }, stdio: 'pipe' });
git(['init', '-b', 'refactor']); git(['config', 'user.email', '[email protected]']); git(['config', 'user.name', 'Fixture']);
@@ -30,8 +68,8 @@ function fixture(t) {
const entry = (text, timestamp = '2026-09-08T12:00:00Z') => ({ type: 'message', timestamp, message: { role: 'user', content: [{ type: 'text', text }] } });
const logs = [entry('[host:control-board -> host:alice] do #1'), entry('[host:bob -> host:alice] review #2'), entry('build #2'), entry('old #1', '2026-09-05T23:59:59Z'), { type: 'message', timestamp: '2026-09-08T00:00:00Z', message: { role: 'assistant', content: 'not a user #1' } }];
put('.pi/state/alice/sessions/one.jsonl', logs.map(x => JSON.stringify(x)).join('\n') + '\n');
- const run = (args = [], env = {}) => spawnSync(process.execPath, [path.join(root, 'packages/ledger/src/cli.mjs'), '--since', '2026-09-06', '--until', '2026-09-12', ...args], { cwd: root, encoding: 'utf8', env: { ...process.env, PATH: `${path.join(root, 'bin')}:${process.env.PATH}`, ISSUES: path.join(root, 'issues.json'), CALLS: path.join(root, 'calls.jsonl'), ...env } });
- return { root, put, commit, run, entry, logs };
+ const run = (args = [], env = {}) => spawnSync(process.execPath, [path.join(root, 'packages/ledger/src/cli.mjs'), '--since', '2026-09-06', '--until', '2026-09-12', ...args], { cwd: root, encoding: 'utf8', env: { ...process.env, HOME: home, PATH: `${path.join(root, 'bin')}:${process.env.PATH}`, ISSUES: path.join(root, 'issues.json'), CALLS: path.join(root, 'calls.jsonl'), ...env } });
+ return { root, real: realpathSync(root), home, defaultDb, put, commit, run, entry, logs };
}
test('fixture git subjects only, follow-ups and three session kinds', t => {
const f = fixture(t), result = f.run(['--json']);
@@ -63,7 +101,7 @@ test('missing credentials exit 2, no-issues never calls API and shows unknown',
test('empty range gives no rows and zero totals', t => {
const f = fixture(t);
f.put('issues.json', '[]');
- const result = spawnSync(process.execPath, [path.join(f.root, 'packages/ledger/src/cli.mjs'), '--since', '2027-01-01', '--until', '2027-01-02', '--json'], { encoding: 'utf8', env: { ...process.env, PATH: `${f.root}/bin:${process.env.PATH}`, ISSUES: `${f.root}/issues.json`, CALLS: `${f.root}/calls.jsonl` } });
+ const result = spawnSync(process.execPath, [path.join(f.root, 'packages/ledger/src/cli.mjs'), '--since', '2027-01-01', '--until', '2027-01-02', '--json'], { encoding: 'utf8', env: { ...process.env, HOME: f.home, PATH: `${f.root}/bin:${process.env.PATH}`, ISSUES: `${f.root}/issues.json`, CALLS: `${f.root}/calls.jsonl` } });
assert.equal(result.status, 0, result.stderr); const r = JSON.parse(result.stdout);
assert.deepEqual(r.issues, []); assert.deepEqual(r.seats, []); assert.ok(Object.values(r.totals).every(n => n === 0));
});
@@ -96,6 +134,13 @@ test('partial or malformed session log refuses with location, not content', t =>
const f = fixture(t); f.put('.pi/state/alice/sessions/bad.jsonl', '{sensitive'); const r = f.run();
assert.equal(r.status, 1); assert.match(r.stderr, /Malformed session JSON: alice\/bad.jsonl:1/); assert.doesNotMatch(r.stderr, /sensitive/);
});
+test('a U+2028 inside a session string is one line, not a malformed record', t => {
+ const f = fixture(t);
+ f.put('.pi/state/bob/sessions/sep.jsonl', [f.entry('Jason: one\u2028two #2'), f.entry('[h:alice -> h:bob] ok')].map(x => JSON.stringify(x)).join('\r\n') + '\r\n');
+ assert.ok(readFileSync(path.join(f.root, '.pi/state/bob/sessions/sep.jsonl'), 'utf8').includes('\u2028'));
+ const r = f.run(['--json']); assert.equal(r.status, 0, r.stderr);
+ assert.deepEqual(JSON.parse(r.stdout).seats[1], { seat: 'bob', board: 0, agent: 1, human: 1 });
+});
test('no sessions is an empty table; symlink source refuses', t => {
const f = fixture(t); rmSync(path.join(f.root, '.pi'), { recursive: true });
assert.deepEqual(JSON.parse(f.run(['--json']).stdout).seats, []);
@@ -140,7 +185,11 @@ test('T3 header: agent, or board from control-board; anything short of the full
assert.equal(messageKind(`Jason here\n[from: ${sage} -> to: ${filbert}]\nquoted`), 'human');
assert.equal(messageKind(` [from: ${sage} -> to: ${filbert}]`), 'human');
assert.equal(messageKind(`[from: sage -> to: filbert]\nno thread ids`), 'human');
- assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=Actionable]`), 'human');
+ // Classes match in either case (Gate F). HEAD before the fix called these human.
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=Actionable]`), 'agent');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=REVIEW-REQUEST]\nreview`), 'agent');
+ assert.equal(messageKind('[h:sage -> h:bob class=DECISION] go'), 'agent');
+ assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=review_request]`), 'human');
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}]trailing`), 'human');
assert.equal(messageKind(`[From: ${sage} -> to: ${filbert}]`), 'human');
});
@@ -148,3 +197,218 @@ test('no closed issues with human messages means undefined ratio, not invented z
const r = summarize(range, [], [], { rows: [{ seat: 'a', human: 1, board: 0, agent: 0 }], mentions: new Map() });
assert.equal(r.totals.humanMessagesPerClosedIssue, 'unknown');
});
+
+// T3 thread source (Gate F, docs/plans/2026-09-26_ledger-t3-source.md).
+const T1 = 't-alice', T2 = 't-bob', T3 = 't-sagebrush', T4 = 't-researcher', T5 = 't-discord';
+const header = (from, to, toId, cls = '') => `[from: ${from} (x1) -> to: ${to} (${toId})${cls}]`;
+function t3Fixture(t) {
+ const f = fixture(t);
+ for (const seat of ['sage', 'researcher']) mkdirSync(path.join(f.root, 'agents', seat), { recursive: true });
+ const db = path.join(f.home, 'fixture/t3.sqlite');
+ const threads = [
+ { id: T1, title: 'Alice' }, { id: T2, title: 'Bob in Claude', archived: '2026-09-09T00:00:00Z' },
+ { id: T3, title: 'Sagebrush' }, { id: T4, title: 'Researcher' }, { id: T5, title: 'Discord Bot' },
+ { id: 'import:claudeAgent:1', title: 'alice' }, { id: 't-deleted', title: 'Alice', deleted: '2026-09-09T00:00:00Z' },
+ { id: 't-other', project: 'p2', title: 'Alice' },
+ ];
+ const messages = [
+ { thread: T1, text: 'Jason: go #1' },
+ { thread: T1, text: `${header('sage', 'alice', T1, ' class=REVIEW-REQUEST')}\nreview #2`, origin: 'api' },
+ { thread: T1, text: '[h:sage -> h:alice class=DECISION] go', origin: 'api' },
+ { thread: T1, text: `${header('control-board', 'alice', T1)}\nbuzz`, origin: 'api' },
+ { thread: T1, text: 'outside', at: '2026-09-13T00:00:00Z' },
+ { thread: T1, text: 'an answer #9', role: 'assistant', origin: 'none' },
+ { thread: T2, text: 'archived still counts #2' },
+ { thread: T3, text: 'Sagebrush is not sage' },
+ { thread: T3, text: `${header('sage', 'discord', T3)}\nnot a seat role`, origin: 'api' },
+ { thread: T4, text: 'research this' },
+ { thread: T5, text: '[from: SetSpark coordinator (x1) -> to: Discord Bot (x2)]\nfree text', origin: 'api' },
+ { thread: 'import:claudeAgent:1', text: 'imported' },
+ { thread: 't-deleted', text: 'deleted' },
+ { thread: 't-other', text: 'other project' },
+ ];
+ const write = (overrides = {}) => t3db(db, { root: f.real, projects: [['p1', f.real], ['p2', '/elsewhere']], threads, messages, ...overrides });
+ return { ...f, db, threads, messages, write };
+}
+test('T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not', t => {
+ const f = t3Fixture(t); f.write();
+ const result = f.run(['--json', '--t3-db', f.db]);
+ assert.equal(result.status, 0, result.stderr);
+ const r = JSON.parse(result.stdout);
+ assert.deepEqual(r.seats, [
+ { seat: 'alice', board: 2, agent: 3, human: 2 }, { seat: 'bob', board: 0, agent: 0, human: 1 },
+ { seat: 'researcher', board: 0, agent: 0, human: 1 }, { seat: 't3:unmapped', board: 0, agent: 1, human: 2 },
+ ]);
+ assert.deepEqual(r.pi, [{ seat: 'alice', board: 1, agent: 1, human: 1 }]);
+ assert.deepEqual(r.t3.database, { path: f.db, default: false });
+ assert.deepEqual(r.t3.seats, [
+ { seat: 'alice', board: 1, agent: 2, human: 1, threads: [{ id: T1, title: 'Alice', archived: false }] },
+ { seat: 'bob', board: 0, agent: 0, human: 1, threads: [{ id: T2, title: 'Bob in Claude', archived: true }] },
+ { seat: 'researcher', board: 0, agent: 0, human: 1, threads: [{ id: T4, title: 'Researcher', archived: false }] },
+ ]);
+ assert.deepEqual(r.t3.unmapped, { board: 0, agent: 1, human: 2, threads: [
+ { id: T5, title: 'Discord Bot', archived: false }, { id: T3, title: 'Sagebrush', archived: false }] });
+ assert.deepEqual(r.t3.excluded, { importedThreads: 1, deletedThreads: 1 });
+ // The free-text header counts as human; only the diagnostic shows it was sent through the API.
+ assert.deepEqual(r.t3.diagnostic, { humanSentThroughApi: 1, humanWithoutEvent: 0 });
+ assert.equal(r.totals.humanMessagesPerClosedIssue, 6);
+ assert.deepEqual(r.issues.map(x => [x.issue, x.seats]), [[1, ['alice']], [2, ['alice', 'bob']]]);
+ const text = f.run(['--t3-db', f.db]);
+ assert.equal(text.status, 0, text.stderr);
+ assert.ok(text.stdout.includes(`T3: read from ${f.db}, not the default`));
+ assert.match(text.stdout, /t3:unmapped \| 0 \| 1 \| 2/);
+});
+test('T3: the default path is read from HOME and prints no path line; --no-t3 says so', t => {
+ const f = t3Fixture(t); f.write();
+ rmSync(f.defaultDb); cpSync(f.db, f.defaultDb);
+ const json = JSON.parse(f.run(['--json']).stdout);
+ assert.deepEqual(json.t3.database, { path: f.defaultDb, default: true });
+ assert.equal(json.seats.at(-1).seat, 't3:unmapped');
+ const text = f.run(); assert.equal(text.status, 0, text.stderr); assert.doesNotMatch(text.stdout, /^T3:/m);
+ rmSync(path.join(f.home, '.t3'), { recursive: true });
+ const off = f.run(['--no-t3']); assert.equal(off.status, 0, off.stderr);
+ assert.match(off.stdout, /^T3: not read \(--no-t3\)$/m);
+ const offJson = JSON.parse(f.run(['--no-t3', '--json']).stdout);
+ assert.deepEqual(offJson.t3, { read: false }); assert.deepEqual(offJson.seats, [{ seat: 'alice', board: 1, agent: 1, human: 1 }]);
+ const both = f.run(['--no-t3', '--t3-db', f.db]); assert.equal(both.status, 1); assert.match(both.stderr, /cannot be combined/);
+ assert.equal(f.run(['--t3-db']).status, 1);
+});
+test('T3: a HOME with no database exits 1 and names --no-t3', t => {
+ const f = fixture(t); rmSync(path.join(f.home, '.t3'), { recursive: true });
+ const r = f.run(); assert.equal(r.status, 1); assert.equal(r.stdout, '');
+ assert.match(r.stderr, /T3 database unavailable: .*\.t3 is missing or unreadable; use --no-t3/);
+});
+test('T3: a file that is not a database exits 1 and names --no-t3', t => {
+ const f = fixture(t); writeFileSync(f.defaultDb, 'not sqlite'.repeat(100));
+ const r = f.run(); assert.equal(r.status, 1); assert.match(r.stderr, /T3 database cannot be read: .*\(SQLite \d+\); use --no-t3/);
+});
+test('T3: a seat thread renamed to another seat exits 1 naming thread, title and roles', t => {
+ const f = t3Fixture(t);
+ f.write({ messages: [...f.messages, { thread: T2, text: `${header('sage', 'alice', T2, ' class=INFO')}\nfor alice`, origin: 'api' }] });
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1);
+ assert.equal(r.stderr.trim(), `T3 header conflict: thread ${T2} "Bob in Claude" maps to bob, but a header addresses alice`);
+});
+test('T3: an unmapped thread addressed as a seat exits 1', t => {
+ const f = t3Fixture(t);
+ f.write({ messages: [...f.messages, { thread: T3, text: `${header('bob', 'Sage', T3)}\nhi`, origin: 'api' }] });
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1);
+ assert.match(r.stderr, /thread t-sagebrush "Sagebrush" maps to no seat, but a header addresses Sage/);
+});
+test('T3: a header to another thread id is not cross-checked', t => {
+ const f = t3Fixture(t);
+ f.write({ messages: [...f.messages, { thread: T2, text: `${header('sage', 'alice', T1)}\ncopied`, origin: 'api' }] });
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ assert.equal(JSON.parse(r.stdout).t3.seats[1].agent, 1);
+});
+test('T3: no project, or two, for this root exits 1', t => {
+ const f = t3Fixture(t);
+ f.write({ projects: [['p1', `${f.real}-link`], ['p2', '/elsewhere']] });
+ let r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, /T3 has no project for .*symlink does not match/);
+ f.write({ projects: [['p1', f.real], ['p2', f.real]] });
+ r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, /T3 has more than one project for/);
+ f.write({ projects: [['p1', f.real], ['p2', f.real, '2026-09-01T00:00:00Z']] });
+ assert.equal(f.run(['--t3-db', f.db]).status, 0);
+});
+for (const [name, after, pattern] of [
+ ['a removed column', ['alter table projection_threads drop column title'], /T3 schema changed: missing projection_threads.title/],
+ ['a missing table', ['drop table projection_thread_messages'], /T3 schema changed: missing projection_thread_messages$/m],
+]) test(`T3: ${name} exits 1 and names it`, t => {
+ const f = t3Fixture(t); f.write({ after, messages: [] });
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, pattern);
+});
+for (const [name, message, pattern] of [
+ ['an unknown role', { role: 'system' }, /T3 message m\d+ in thread t-alice has an unknown role/],
+ ['non-text content', { text: null }, /has non-text content/],
+ ['an unparseable created_at', { at: 'yesterday' }, /has an invalid created_at/],
+]) test(`T3: a counted row with ${name} exits 1 without its text`, t => {
+ const f = t3Fixture(t);
+ f.write({ messages: [...f.messages, { thread: T1, text: 'secret words', ...message }] });
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, pattern); assert.doesNotMatch(r.stderr, /secret/);
+});
+test('T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts', t => {
+ const f = t3Fixture(t); f.write();
+ const before = JSON.parse(f.run(['--json', '--t3-db', f.db]).stdout);
+ f.write({ after: ['drop table orchestration_events'] });
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ const after = JSON.parse(r.stdout);
+ assert.deepEqual(after.t3.diagnostic, { humanSentThroughApi: 'unknown', humanWithoutEvent: 'unknown' });
+ assert.deepEqual(after.seats, before.seats); assert.deepEqual(after.totals, before.totals);
+});
+for (const link of ['.t3', '.t3/userdata', '.t3/userdata/state.sqlite']) test(`T3: a symlink at ~/${link} exits 1`, t => {
+ const f = fixture(t), target = path.join(f.home, 'real', link);
+ mkdirSync(path.dirname(target), { recursive: true });
+ cpSync(path.join(f.home, link), target, { recursive: true });
+ rmSync(path.join(f.home, link), { recursive: true }); symlinkSync(target, path.join(f.home, link));
+ const r = f.run(); assert.equal(r.status, 1); assert.match(r.stderr, new RegExp(`${link.replaceAll('.', '\\.')} is a symlink; use --no-t3`));
+});
+test('T3: with --t3-db, a symlinked file or directory exits 1', t => {
+ const f = t3Fixture(t); f.write();
+ const file = path.join(f.home, 'file-link.sqlite'); symlinkSync(f.db, file);
+ let r = f.run(['--t3-db', file]); assert.equal(r.status, 1); assert.match(r.stderr, /file-link.sqlite is a symlink/);
+ const dir = path.join(f.home, 'dir-link'); symlinkSync(path.dirname(f.db), dir);
+ r = f.run(['--t3-db', path.join(dir, 't3.sqlite')]); assert.equal(r.status, 1); assert.match(r.stderr, /dir-link is a symlink/);
+});
+
+// WAL states. The CLI reads with mode=ro; it may create -wal and -shm but must
+// never change the main file.
+const sha = file => execFileSync('sha256sum', [file], { encoding: 'utf8' }).split(' ')[0];
+const humans = r => JSON.parse(r.stdout).t3.seats.find(s => s.seat === 'alice').human;
+const asRoot = process.getuid?.() === 0;
+function killedWriter(db) {
+ // A writer that commits into the WAL and dies without a checkpoint.
+ const code = `const { DatabaseSync } = require('node:sqlite'); const db = new DatabaseSync(${JSON.stringify(db)});
+ db.exec('pragma wal_autocheckpoint=0');
+ db.prepare("insert into projection_thread_messages values ('late', 't-alice', 'user', 'late human', '2026-09-08T13:00:00Z')").run();
+ process.kill(process.pid, 'SIGKILL');`;
+ const r = spawnSync(process.execPath, ['-e', code]);
+ assert.equal(r.signal, 'SIGKILL');
+ rmSync(`${db}-shm`);
+}
+function inReadOnlyDir(dir, check) {
+ execFileSync('chmod', ['0555', dir]);
+ try { check(); } finally { execFileSync('chmod', ['0755', dir]); }
+}
+test('T3 WAL: the newest message only in -wal, writer attached, is counted', t => {
+ const f = t3Fixture(t), writer = f.write({ keepOpen: true });
+ t.after(() => writer.close());
+ writer.exec('pragma wal_autocheckpoint=0');
+ addMessage(writer, { thread: T1, text: 'newest', at: '2026-09-08T13:00:00Z' });
+ const main = sha(f.db);
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ assert.equal(humans(r), 2); assert.equal(sha(f.db), main);
+});
+test('T3 WAL: stopped cleanly, counts are correct and the main file is unchanged', t => {
+ const f = t3Fixture(t); f.write();
+ assert.throws(() => readFileSync(`${f.db}-wal`));
+ const main = sha(f.db);
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ assert.equal(humans(r), 1); assert.equal(sha(f.db), main);
+});
+test('T3 WAL: -wal without -shm in a writable directory is read', t => {
+ const f = t3Fixture(t); f.write(); killedWriter(f.db);
+ const main = sha(f.db);
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ assert.equal(humans(r), 2); assert.equal(sha(f.db), main);
+});
+test('T3 WAL: -wal without -shm in a read-only directory exits 1', { skip: asRoot && 'mode bits do not bind root' }, t => {
+ const f = t3Fixture(t); f.write(); killedWriter(f.db);
+ inReadOnlyDir(path.dirname(f.db), () => {
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, /cannot be read: .*\(SQLite 14\); use --no-t3/);
+ });
+});
+test('T3 WAL: stopped cleanly in a read-only directory exits 1', { skip: asRoot && 'mode bits do not bind root' }, t => {
+ const f = t3Fixture(t); f.write();
+ inReadOnlyDir(path.dirname(f.db), () => {
+ const r = f.run(['--t3-db', f.db]); assert.equal(r.status, 1); assert.match(r.stderr, /cannot be read: .*\(SQLite 1544\); use --no-t3/);
+ });
+});
+test('T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3', t => {
+ const f = t3Fixture(t), writer = f.write({ keepOpen: true });
+ t.after(() => writer.close());
+ writer.exec('pragma locking_mode=exclusive'); writer.exec('begin exclusive');
+ addMessage(writer, { thread: T1, text: 'held' });
+ const started = Date.now(), r = f.run(['--t3-db', f.db]);
+ writer.exec('commit');
+ assert.equal(r.status, 1); assert.match(r.stderr, /cannot be read: .*\(SQLite 5\); use --no-t3/);
+ assert.ok(Date.now() - started >= 4500, 'the reader waited for the busy timeout');
+});
diff --git a/packages/ledger/src/t3.mjs b/packages/ledger/src/t3.mjs
new file mode 100644
index 00000000..9672fcc9
--- /dev/null
+++ b/packages/ledger/src/t3.mjs
@@ -0,0 +1,151 @@
+import { lstat } from 'node:fs/promises';
+import { DatabaseSync } from 'node:sqlite';
+import { pathToFileURL } from 'node:url';
+import os from 'node:os';
+import path from 'node:path';
+import { SourceError, UNKNOWN, clean, inRange, issueNumbers, messageKind, readSeats, t3Header } from './ledger.mjs';
+
+// T3 keeps every thread message in one SQLite database. This reader opens that
+// file read-only and nothing else in ~/.t3. See
+// docs/plans/2026-09-26_ledger-t3-source.md for the rules below.
+export const UNMAPPED = 't3:unmapped';
+const SKIP = 'use --no-t3 to skip T3';
+const REQUIRED = {
+ projection_projects: ['project_id', 'workspace_root', 'deleted_at'],
+ projection_threads: ['thread_id', 'project_id', 'title', 'archived_at', 'deleted_at'],
+ projection_thread_messages: ['message_id', 'thread_id', 'role', 'text', 'created_at'],
+};
+const DIAGNOSTIC = { orchestration_events: ['stream_id', 'event_type', 'payload_json', 'metadata_json'] };
+
+export const defaultT3Path = () => path.join(os.homedir(), '.t3', 'userdata', 'state.sqlite');
+
+// Every named path must exist and must not be a symlink. Skipping one would be
+// a silent zero, so each problem refuses the report.
+async function checkPaths(dbPath, isDefault) {
+ const dirs = isDefault ? [path.dirname(path.dirname(dbPath)), path.dirname(dbPath)] : [path.dirname(dbPath)];
+ for (const [target, wantDir] of [...dirs.map(d => [d, true]), [dbPath, false]]) {
+ let stat;
+ try { stat = await lstat(target); }
+ catch { throw new SourceError(`T3 database unavailable: ${target} is missing or unreadable; ${SKIP}`); }
+ if (stat.isSymbolicLink()) throw new SourceError(`T3 database refused: ${target} is a symlink; ${SKIP}`);
+ if (wantDir ? !stat.isDirectory() : !stat.isFile()) {
+ throw new SourceError(`T3 database refused: ${target} is not a ${wantDir ? 'directory' : 'regular file'}; ${SKIP}`);
+ }
+ }
+}
+
+function missingColumns(db, tables) {
+ const missing = [];
+ for (const [table, columns] of Object.entries(tables)) {
+ const have = new Set(db.prepare('select name from pragma_table_info(?)').all(table).map(r => r.name));
+ if (!have.size) missing.push(table);
+ else for (const column of columns) if (!have.has(column)) missing.push(`${table}.${column}`);
+ }
+ return missing;
+}
+
+// Seat for a thread title: the lower-cased title equals the seat or starts
+// with the seat and a space. Longest seat first, so the most specific wins.
+export function seatForTitle(title, seats) {
+ const lower = title.toLowerCase();
+ return [...seats].sort((a, b) => b.length - a.length).find(s => lower === s || lower.startsWith(`${s} `)) ?? null;
+}
+
+// Origin per message id from thread.message-sent events. Any missing table,
+// column or unparseable event makes the diagnostic unknown; it decides nothing.
+function origins(db, projectId) {
+ if (missingColumns(db, DIAGNOSTIC).length) return null;
+ const byMessage = new Map();
+ const events = db.prepare(`select e.payload_json, e.metadata_json from orchestration_events e
+ join projection_threads t on t.thread_id = e.stream_id
+ where e.event_type = 'thread.message-sent' and t.project_id = ?`).all(projectId);
+ for (const event of events) {
+ let payload, metadata;
+ try { payload = JSON.parse(event.payload_json); metadata = JSON.parse(event.metadata_json); }
+ catch { return null; }
+ if (typeof payload?.messageId !== 'string') return null;
+ byMessage.set(payload.messageId, typeof metadata?.origin?.appVersion === 'string');
+ }
+ return byMessage;
+}
+
+function query(db, root, range, seats) {
+ const missing = missingColumns(db, REQUIRED);
+ if (missing.length) throw new SourceError(`T3 schema changed: missing ${missing.join(', ')}`);
+ // Compared in JavaScript so a declared collation can't loosen the match.
+ const projects = db.prepare('select project_id, workspace_root from projection_projects where deleted_at is null').all()
+ .filter(p => p.workspace_root === root);
+ if (projects.length !== 1) {
+ throw new SourceError(`T3 has ${projects.length ? 'more than one project' : 'no project'} for ${root}; a project opened through a symlink does not match; ${SKIP}`);
+ }
+ const projectId = projects[0].project_id;
+ const threads = new Map(), excluded = { importedThreads: 0, deletedThreads: 0 };
+ for (const t of db.prepare('select thread_id, title, archived_at, deleted_at from projection_threads where project_id = ?').all(projectId)) {
+ if (typeof t.thread_id !== 'string' || typeof t.title !== 'string') throw new SourceError('T3 thread with a non-text id or title');
+ if (t.thread_id.startsWith('import:')) { excluded.importedThreads++; continue; }
+ if (t.deleted_at !== null) { excluded.deletedThreads++; continue; }
+ threads.set(t.thread_id, { id: t.thread_id, title: t.title, archived: t.archived_at !== null, seat: seatForTitle(t.title, seats) });
+ }
+ const rows = new Map([...seats, UNMAPPED].map(s => [s, { board: 0, agent: 0, human: 0 }]));
+ const mentions = new Map(), human = [];
+ const messages = db.prepare(`select m.message_id, m.thread_id, m.role, m.text, m.created_at from projection_thread_messages m
+ join projection_threads t on t.thread_id = m.thread_id where t.project_id = ?`).all(projectId);
+ for (const m of messages) {
+ const thread = threads.get(m.thread_id);
+ if (!thread) continue;
+ const where = `T3 message ${clean(m.message_id)} in thread ${clean(m.thread_id)}`;
+ if (m.role !== 'user' && m.role !== 'assistant') throw new SourceError(`${where} has an unknown role`);
+ if (typeof m.text !== 'string') throw new SourceError(`${where} has non-text content`);
+ if (typeof m.created_at !== 'string' || !Number.isFinite(Date.parse(m.created_at))) throw new SourceError(`${where} has an invalid created_at`);
+ if (m.role !== 'user') continue;
+ // A header addressed to its own thread must agree with the title mapping.
+ const header = t3Header(m.text);
+ if (header && header.toId === thread.id) {
+ const to = header.to.toLowerCase();
+ if (thread.seat ? to !== thread.seat : seats.includes(to)) {
+ throw new SourceError(`T3 header conflict: thread ${clean(thread.id)} "${clean(thread.title)}" maps to ${thread.seat ?? 'no seat'}, but a header addresses ${clean(header.to)}`);
+ }
+ }
+ if (!inRange(m.created_at, range)) continue;
+ const kind = messageKind(m.text), seat = thread.seat ?? UNMAPPED;
+ rows.get(seat)[kind]++;
+ if (kind === 'human') human.push(m.message_id);
+ for (const number of issueNumbers(m.text)) {
+ if (!mentions.has(number)) mentions.set(number, new Set());
+ mentions.get(number).add(seat);
+ }
+ }
+ const byMessage = origins(db, projectId);
+ const sentThroughApi = byMessage === null ? UNKNOWN : human.filter(id => byMessage.get(id) === false).length;
+ const noEvent = byMessage === null ? UNKNOWN : human.filter(id => !byMessage.has(id)).length;
+ const listed = seat => [...threads.values()].filter(t => (t.seat ?? UNMAPPED) === seat)
+ .sort((a, b) => a.id.localeCompare(b.id)).map(({ id, title, archived }) => ({ id, title, archived }));
+ const seatRows = seats.map(seat => ({ seat, ...rows.get(seat), threads: listed(seat) })).filter(r => r.threads.length);
+ return { rows, mentions, excluded, seats: seatRows, unmapped: { ...rows.get(UNMAPPED), threads: listed(UNMAPPED) },
+ diagnostic: { humanSentThroughApi: sentThroughApi, humanWithoutEvent: noEvent } };
+}
+
+// Reads one snapshot of T3's database. Returns the per-seat rows and issue
+// mentions the ledger merges with Pi, and the report's `t3` section.
+export async function readT3(root, range, { dbPath = defaultT3Path(), isDefault = true } = {}) {
+ dbPath = path.resolve(dbPath);
+ await checkPaths(dbPath, isDefault);
+ const seats = await readSeats(root);
+ const url = pathToFileURL(dbPath);
+ url.searchParams.set('mode', 'ro');
+ let db, result;
+ try {
+ db = new DatabaseSync(url, { readOnly: true, timeout: 5000 });
+ db.exec('BEGIN');
+ result = query(db, root, range, seats);
+ db.exec('COMMIT');
+ } catch (error) {
+ if (error instanceof SourceError) throw error;
+ throw new SourceError(`T3 database cannot be read: ${dbPath} (SQLite ${error.errcode ?? 'error'}); ${SKIP}`);
+ } finally {
+ try { if (db?.isTransaction) db.exec('ROLLBACK'); } catch { /* the close below still runs */ }
+ try { db?.close(); } catch { /* nothing was written */ }
+ }
+ const { rows, mentions, ...section } = result;
+ return { rows, mentions, section: { read: true, database: { path: dbPath, default: isDefault }, ...section } };
+}
@@ -0,0 +1,3 @@
5acbc1075a5d0ad709faf14698235c8c2332c408cb4fccc580bd4a75e2c314fb packages/ledger/src/t3.mjs
6546dbaf59c046d599a9d378c1a1f2d9afc9487189db06f50fa3201c9cadc63b packages/ledger/tests/ledger.test.mjs
101013def3b168ae1b7e291ff86200b49ed6b27927787585d5ca32a283bf38bd packages/ledger/README.md
@@ -0,0 +1,67 @@
# Gate F follow-up: Filbert's notes 1 to 3 (#1506), candidate for review
Darkwing, 2026-09-26. Filbert's build review
(`agents/filbert/work/ledger-t3-build-review-2026-09-26.md`, e47ec6da) left
four nonblocking notes on Gate F (136958c9). Sage asked for 1 to 3 as one small
change that Filbert reviews and Sage commits. Note 4, snapshot isolation, went
to DEFERRED (a68dc174). Base is HEAD a4d38a3d, which changes nothing under
`packages/ledger` since 136958c9. Nothing is committed or pushed.
`followup-manifest.sha256` pins the three files. `followup.patch` is the diff
against a4d38a3d.
## Changes
1. **U+2029.** The splitter test now writes a Pi entry holding a raw U+2028
and a raw U+2029, with CRLF endings, and asserts that the file contains
both. The README line names both characters. `ledger.mjs` is unchanged,
because the splitter already ends lines at `\n` only.
2. **Diagnostic.** Three new tests:
- A human message with no `thread.message-sent` event gives
`humanWithoutEvent: 1`.
- A `thread.message-sent` event whose payload doesn't parse makes both
diagnostic fields `unknown` and leaves `seats` unchanged.
- The same for an event whose `messageId` isn't a string. Filbert didn't
list this one, but it's the third `return null` in `origins()` and had
no test either.
3. **Rethrow.** `readT3`'s catch now rethrows anything that is not a
`SourceError` and carries no numeric `errcode`. The CLI prints such an
error as `Ledger failed: cannot read source evidence`, exit 1. That's the
CLI's existing message for a non-source error, and it no longer points
at SQLite or `--no-t3`. With only numeric errcodes left, the message's
`?? 'error'` fallback could no longer fire, so I removed it. The new test
calls `readT3` in process with an explicit fixture path and a `null`
range, so `inRange` throws a `TypeError` inside the read transaction. It
asserts the `TypeError` comes out. It never touches the real `~/.t3`, and
the fixture comment says so.
## Evidence
- Ledger tests: 51/51, the Gate F 47 plus 4 new.
- Mutations on a scratch copy of the package. The three `gitea-helper` tests
fail in every scratch copy, as before, so the counts leave them out:
| Mutation | Result |
|---|---|
| `humanWithoutEvent` hardcoded to 0 | 1 fails (no-event test) |
| unparseable event skipped (`continue`) | 1 fails (unparseable test) |
| non-string `messageId` skipped | 1 fails (messageId test) |
| rethrow removed (Gate F catch) | 1 fails (rethrow test) |
| splitter also splits at U+2028 | 1 fails (splitter test) |
| splitter also splits at U+2029 | 1 fails (splitter test) |
My first try at the last two put a raw U+2028 or U+2029 in the regex
source. That ends a JS regex literal, so the whole test file failed to
load, which doesn't count as a kill. I reran with the escape written out
literally, and the rows above come from that rerun.
- Eight suites on a local clone of a4d38a3d with the three files: config 24,
task 90, foundation 43, conductor 17, release 14, auth 15, discord 63,
extension-package 18. I ran them twice, and the second run was on the final
files after the errcode edit.
- Union on the same clone. Control-board, webui, seat, mosaic, ledger and
discord, plus conversation, which CHAT-02 committed: 474/474 twice before
the errcode edit and once after. No `ledger-*` temp directories remained.
- Live read, `--since 2026-09-01 --until 2026-09-26 --no-issues --json`, at
2026-09-26T21:47Z: exit 0, no header conflict, diagnostic
`{humanSentThroughApi: 15, humanWithoutEvent: 0}`, two imported threads
excluded. The Gate F build read 14; messages have been sent since then.
@@ -0,0 +1,99 @@
diff --git a/packages/ledger/README.md b/packages/ledger/README.md
index 393e9c37..3a2ce27c 100644
--- a/packages/ledger/README.md
+++ b/packages/ledger/README.md
@@ -39,7 +39,8 @@ No install, build, service restart, or configuration change is needed.
duplicated entries in copied logs are not deduplicated. No transcript content
leaves the parser. Assistant messages and logs outside repo seats do not count.
Symlink source directories are refused and symlink files are not followed.
- A line ends at `\n` only. A U+2028 inside a JSON string does not split a record.
+ A line ends at `\n` only. A U+2028 or U+2029 inside a JSON string does not
+ split a record.
- Table 2 also counts T3 thread messages with role `user`. The T3 source
follows. A seat's row sums its Pi and T3 counts; the JSON keeps the split in
`pi` (Pi rows) and `t3.seats` (T3 rows).
diff --git a/packages/ledger/src/t3.mjs b/packages/ledger/src/t3.mjs
index 9672fcc9..fc9da14e 100644
--- a/packages/ledger/src/t3.mjs
+++ b/packages/ledger/src/t3.mjs
@@ -140,8 +140,10 @@ export async function readT3(root, range, { dbPath = defaultT3Path(), isDefault
result = query(db, root, range, seats);
db.exec('COMMIT');
} catch (error) {
- if (error instanceof SourceError) throw error;
- throw new SourceError(`T3 database cannot be read: ${dbPath} (SQLite ${error.errcode ?? 'error'}); ${SKIP}`);
+ // Only a SQLite failure carries an errcode. Anything else is a bug and
+ // surfaces as itself, not as a database problem.
+ if (error instanceof SourceError || typeof error?.errcode !== 'number') throw error;
+ throw new SourceError(`T3 database cannot be read: ${dbPath} (SQLite ${error.errcode}); ${SKIP}`);
} finally {
try { if (db?.isTransaction) db.exec('ROLLBACK'); } catch { /* the close below still runs */ }
try { db?.close(); } catch { /* nothing was written */ }
diff --git a/packages/ledger/tests/ledger.test.mjs b/packages/ledger/tests/ledger.test.mjs
index 8e60b96b..834dbd55 100644
--- a/packages/ledger/tests/ledger.test.mjs
+++ b/packages/ledger/tests/ledger.test.mjs
@@ -7,6 +7,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFileSync, spawnSync } from 'node:child_process';
import { dateRange, messageKind, issueNumbers, totalsLine, summarize } from '../src/ledger.mjs';
+import { readT3 } from '../src/t3.mjs';
const source = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../src');
const range = dateRange('2026-09-06', '2026-09-12');
@@ -49,7 +50,8 @@ const fixtureIssues = [
function fixture(t) {
const root = mkdtempSync(path.join(os.tmpdir(), 'ledger-test-'));
// No test opens the real ~/.t3: every CLI run gets this HOME, with an empty
- // T3 database at the default path. The CLI's root is a realpath.
+ // T3 database at the default path. The one in-process readT3 call passes an
+ // explicit fixture path. The CLI's root is a realpath.
const home = mkdtempSync(path.join(os.tmpdir(), 'ledger-home-'));
t.after(() => { rmSync(root, { recursive: true, force: true }); rmSync(home, { recursive: true, force: true }); });
const defaultDb = path.join(home, '.t3/userdata/state.sqlite');
@@ -134,10 +136,11 @@ test('partial or malformed session log refuses with location, not content', t =>
const f = fixture(t); f.put('.pi/state/alice/sessions/bad.jsonl', '{sensitive'); const r = f.run();
assert.equal(r.status, 1); assert.match(r.stderr, /Malformed session JSON: alice\/bad.jsonl:1/); assert.doesNotMatch(r.stderr, /sensitive/);
});
-test('a U+2028 inside a session string is one line, not a malformed record', t => {
+test('a U+2028 or U+2029 inside a session string is one line, not a malformed record', t => {
const f = fixture(t);
- f.put('.pi/state/bob/sessions/sep.jsonl', [f.entry('Jason: one\u2028two #2'), f.entry('[h:alice -> h:bob] ok')].map(x => JSON.stringify(x)).join('\r\n') + '\r\n');
- assert.ok(readFileSync(path.join(f.root, '.pi/state/bob/sessions/sep.jsonl'), 'utf8').includes('\u2028'));
+ f.put('.pi/state/bob/sessions/sep.jsonl', [f.entry('Jason: one\u2028two\u2029three #2'), f.entry('[h:alice -> h:bob] ok')].map(x => JSON.stringify(x)).join('\r\n') + '\r\n');
+ const written = readFileSync(path.join(f.root, '.pi/state/bob/sessions/sep.jsonl'), 'utf8');
+ assert.ok(written.includes('\u2028') && written.includes('\u2029'));
const r = f.run(['--json']); assert.equal(r.status, 0, r.stderr);
assert.deepEqual(JSON.parse(r.stdout).seats[1], { seat: 'bob', board: 0, agent: 1, human: 1 });
});
@@ -334,6 +337,30 @@ test('T3: a missing orchestration_events makes the diagnostic unknown and keeps
assert.deepEqual(after.t3.diagnostic, { humanSentThroughApi: 'unknown', humanWithoutEvent: 'unknown' });
assert.deepEqual(after.seats, before.seats); assert.deepEqual(after.totals, before.totals);
});
+test('T3: a human message with no event counts in humanWithoutEvent', t => {
+ const f = t3Fixture(t);
+ f.write({ messages: [...f.messages, { thread: T1, text: 'typed, no event', origin: 'none' }] });
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ assert.deepEqual(JSON.parse(r.stdout).t3.diagnostic, { humanSentThroughApi: 1, humanWithoutEvent: 1 });
+});
+const badEvent = payload => `insert into orchestration_events (stream_id, event_type, payload_json, metadata_json) values ('${T1}', 'thread.message-sent', '${payload}', '{}')`;
+for (const [name, payload] of [['an unparseable event', '{bad'], ['an event with no string messageId', '{"messageId":7}']]) {
+ test(`T3: ${name} makes the diagnostic unknown and keeps the counts`, t => {
+ const f = t3Fixture(t); f.write();
+ const before = JSON.parse(f.run(['--json', '--t3-db', f.db]).stdout);
+ f.write({ after: [badEvent(payload)] });
+ const r = f.run(['--json', '--t3-db', f.db]); assert.equal(r.status, 0, r.stderr);
+ const after = JSON.parse(r.stdout);
+ assert.deepEqual(after.t3.diagnostic, { humanSentThroughApi: 'unknown', humanWithoutEvent: 'unknown' });
+ assert.deepEqual(after.seats, before.seats);
+ });
+}
+test('T3: an error that is not from SQLite is rethrown, not reported as a database failure', async t => {
+ const f = t3Fixture(t); f.write();
+ // In process with an explicit path, so the real ~/.t3 stays closed. A null
+ // range makes inRange throw a TypeError inside the read transaction.
+ await assert.rejects(readT3(f.real, null, { dbPath: f.db, isDefault: false }), TypeError);
+});
for (const link of ['.t3', '.t3/userdata', '.t3/userdata/state.sqlite']) test(`T3: a symlink at ~/${link} exits 1`, t => {
const f = fixture(t), target = path.join(f.home, 'real', link);
mkdirSync(path.dirname(target), { recursive: true });
@@ -0,0 +1,5 @@
08959a05574264e4f8243a90af94746e73a2fde3706f22e38f4ff1105b7a45a8 agents/darkwing/work/ledger-t3-source/r1.md
e8300cb6abea70819aba7cf10040d19b4d6019b5663c37203209537a5f10ee62 agents/darkwing/work/ledger-t3-source/r2.md
f3c05c1b4d28a419ab817621e15708b147f71dff588664980e22696eaafbc342 docs/plans/2026-09-26_ledger-t3-source.md
aa4740ae5d045aa12971af5de36a5107805bd31da839aae4e4d398a818d471fc agents/darkwing/work/ledger-t3-source/r1-to-r2.diff
4128375121673e0a49ef6789390503ff7395ba59f87bfc450764ea56b536c5b2 agents/darkwing/work/ledger-t3-source/r2-to-r3.diff
@@ -0,0 +1,419 @@
--- r1.md
+++ docs/plans/2026-09-26_ledger-t3-source.md
@@ -1,8 +1,11 @@
# Ledger: a read-only T3 thread source for Table 2 (Gate F brief)
-Brief only, no code. Darkwing wrote it on 2026-09-26 at Sage's request. Filbert
-reviews it, and Jason sees it on the decision sheet before anyone builds it.
-Issue #1506.
+Brief only, no code. Darkwing wrote it on 2026-09-26 at Sage's request, issue
+#1506. R1 (sha256 08959a05) went to Filbert, whose review asked for
+revisions: `agents/filbert/work/ledger-t3-source-review-2026-09-26.md`, sha256
+19dda29a. This is R2. It takes every finding, and it records Sage's rulings
+on the three open questions. Section 1 has one measurement that differs from
+the review.
## Why
@@ -18,8 +21,8 @@
## Where T3 keeps messages
T3 keeps its state in one SQLite database, `~/.t3/userdata/state.sqlite`, in
-WAL mode (`state.sqlite-wal` and `state.sqlite-shm` sit beside it). Three
-projection tables are enough:
+WAL mode (`state.sqlite-wal` and `state.sqlite-shm` sit beside it). The counts
+need three projection tables:
- `projection_projects`: `project_id`, `workspace_root`, `deleted_at`.
- `projection_threads`: `thread_id`, `project_id`, `title`, `archived_at`,
@@ -27,51 +30,72 @@
- `projection_thread_messages`: `message_id` (primary key), `thread_id`,
`role` (`user` or `assistant`), `text`, `created_at` (ISO UTC).
-One more table is optional. In `orchestration_events`, each
+The JSON diagnostic reads one more. In `orchestration_events`, each
`thread.message-sent` event carries `metadata_json.origin`. Messages typed in
the T3 app carry an `appVersion` there. Messages sent through T3's API or MCP
-tools, which is how seats talk to each other, don't. See the cross-check below.
+tools, which is how seats talk to each other, don't.
The same directory also holds `secrets/`, `clerk-tokens.json` and other
settings files. The reader opens `state.sqlite` and nothing else, and it
selects named columns only, never `*`.
-## Reading it, with T3 running or not
+## 1. Reading it, with T3 running or not
-The file stays on disk whether T3 runs or not. The reader opens it with Node's
-built-in `node:sqlite` (`DatabaseSync`, `file:<path>?mode=ro`, `readOnly:
-true`). That needs no dependency, and Node 26.8.1 prints no warning for it. I
-read the live database this way today, while T3 was running, with no errors
-and no locks. A WAL reader sees every committed message, including those still
-in the `-wal` file.
-
-Two rules:
-- Never open with `immutable=1` and never copy the file. Both skip the WAL
- and silently lose the newest messages. A copy of the three files is also
- not atomic.
-- If T3 stopped uncleanly and left a `-wal` without its `-shm`, a read-only
- connection may be unable to rebuild the index. If the open fails, the
- ledger reports it and refuses. I have not tested this case or the fully
- stopped case. Both are acceptance checks below.
+The reader uses Node's built-in `node:sqlite` (`DatabaseSync`). That needs no
+dependency, and Node 26.8.1 (SQLite 3.53.4) prints no warning for it.
-## Jason or agent
+- **URI.** Build it with `pathToFileURL(dbPath)` and set `mode=ro` through
+ `searchParams`, then pass `readOnly: true`. A `?`, `#` or `%` in the home
+ path would break a string-built URI.
+- **One snapshot.** Run every query, from the schema checks through the
+ diagnostic, inside one `BEGIN` … `COMMIT`. In autocommit mode each
+ statement sees its own snapshot while T3 writes between them.
+- **Busy timeout.** Set `DatabaseSync`'s `timeout` to 5 s. A transient
+ `SQLITE_BUSY` during a T3 checkpoint then waits instead of failing. A busy
+ error after the timeout exits 1 like any open failure.
+- **No `immutable=1` and no copy.** Both lose the WAL. Filbert found worse
+ than lost messages: with a table created inside the WAL, `immutable=1`
+ fails with `no such table`.
+
+What happens on disk. Filbert and I both tested these in scratch
+directories:
+
+| State | Directory writable | Result |
+|---|---|---|
+| T3 running, writer attached, newest rows only in `-wal` | yes | reads them |
+| `-wal` without `-shm` (writer killed, `-shm` removed) | yes | reads the WAL rows and creates `-shm` |
+| `-wal` without `-shm` | no | open fails, SQLite 14 |
+| T3 stopped cleanly, no `-wal` or `-shm` | yes | reads, then leaves an empty `-wal` and a 32 KiB `-shm` |
+| T3 stopped cleanly | no | fails, SQLite 1544 "attempt to write a readonly database" |
+
+In every case the main file's bytes stayed the same. The last row is where
+Filbert and I differ. His review says the stopped-case read works with the
+directory read-only. In my run it failed with and without the read
+transaction. The build's test settles it. Either way a failed open is exit 1.
+
+So the accurate claim: the reader never writes the main database file. Like
+any SQLite connection, it may create or update `-wal` and `-shm` beside it
+and takes read locks in `-shm`. T3 opens normally afterwards.
+
+## 2. Jason or agent
Reuse the 6a rule. The first line of `text` decides: a T3 header or the tmux
preamble counts as agent, `control-board` as the sender counts as board, and
anything else counts as human. Messages with role `user` count; assistant
messages don't.
-6a has a defect this source would expose. Its regex allows only a lowercase
-class (`class=[a-z-]+`). Seats send uppercase classes: Sage's DECISION, INFO,
-REVIEW-REQUEST and REVIEW-NOTE, and my own REVIEW-REQUEST. In this project's
-threads, 16 real agent headers fail on that alone and would count as human.
-The fix is to make the class match case-insensitive. It belongs in this build
-or just before it, reviewed with it. The ms-communications table lists
-lowercase names, so the fix follows what seats send, not the table.
+The class fix rides in this build (Sage's ruling). HEAD's
+`packages/ledger/src/ledger.mjs:81` (tmux) and `:83` (T3) both allow only
+`class=[a-z-]+`. Both become case-insensitive. Seats send uppercase classes:
+Sage's DECISION, INFO, REVIEW-REQUEST and REVIEW-NOTE, and my own
+REVIEW-REQUEST. In this project's threads 16 real agent headers failed on
+that alone at 20:54Z. The ms-communications table lists lowercase names, so
+the fix follows what seats send, not the table.
Cross-check, read at 2026-09-26T20:54Z for the mosaic-stack project (209
user messages outside imported and deleted threads, every one with its
-`thread.message-sent` event):
+`thread.message-sent` event). Filbert's later read agreed, plus messages sent
+since.
| T3 origin | Header matches 6a | Count |
|---|---|---|
@@ -83,110 +107,193 @@
No message typed in the app carries a header, and every API message in this
project carries one of the three forms. The 14 free-text ones are older
Discord Bot thread headers such as `[from: SetSpark coordinator (…) -> to:
-Discord Bot (…)]`, written before the guide fixed the format. With the class
-fix they still count as human. That's 14 wrong human counts, all dated
-2026-09-17 to 2026-09-22.
-
-Recommendation: the header rule decides, as Sage asked. The reader also
-reports one diagnostic number, not used in any table: user messages the rule
-calls human that T3 recorded as sent through the API. That count is how the
-uppercase-class bug showed up, and it would catch the next format drift. The
-origin field is T3's internal metadata, not a documented contract, so it
-shouldn't decide anything. I'd make it JSON only, so Table 2's layout stays
-the same.
-
-## Thread to seat
-
-A thread counts for this checkout only if its project's `workspace_root` is
-the ledger's repository root. That is `/mnt/storage/src/mosaic-stack`, project
-`34050c07`.
-
-Thread IDs change whenever Jason starts a new thread for a seat, so there's no
-fixed map. T3-AGENT-COMMS.md already names threads after the seat ("Darkwing",
-"Sage", "Dewey in Claude"). Proposed rule: a thread belongs to seat `<s>` when
-`<s>` is a real directory under `agents/` and the lower-cased title equals
-`<s>` or starts with `<s>` followed by a space. Several threads can map to one
-seat. Their counts add up, as several Pi session files already do.
+Discord Bot (…)]`, written before the guide fixed the format. Sage ruled they
+stay as recorded: they count as human, dated 2026-09-17 to 2026-09-22.
+
+The header rule decides. The JSON also carries one diagnostic that feeds no
+table or total: user messages the rule calls human that T3 recorded as sent
+through the API. That number exposed the class bug and would catch the next
+format drift. `origin` is T3's internal metadata, not a documented contract,
+so it decides nothing. If `orchestration_events` or a column it needs is
+missing, the diagnostic reads `unknown` and the report goes on (Sage's
+ruling on F5). Missing tables the counts depend on still exit 1.
+
+## 3. Thread to seat
+
+**Project.** A thread counts for this checkout only if its project's
+`workspace_root` equals the ledger's repository root, byte for byte. The CLI
+already takes that root from the realpath of its own URL, today
+`/mnt/storage/src/mosaic-stack`, project `34050c07`. So a T3 project opened
+through the compatibility symlink `~/src/mosaic-stack-dev-test` doesn't
+match, and "no project row" is the right refusal. The README says so.
+
+**Title rule.** Thread IDs change whenever Jason starts a new thread for a
+seat, so there's no fixed map. T3-AGENT-COMMS.md already names threads after
+the seat ("Darkwing", "Sage", "Dewey in Claude"). A thread belongs to seat
+`<s>` when `<s>` is a real directory under `agents/` and the lower-cased
+title equals `<s>` or starts with `<s>` followed by a space. So "Sagebrush"
+stays unmapped. Several threads can map to one seat, and their counts add
+up, as several Pi session files already do.
Today that maps Sage, Darkwing, Filbert, Dewey and Rocko (one thread each,
-created 2026-09-26), plus "Darkwing in Claude" (archived) and "Dewey in
-Claude". Three threads map to no seat. Two are imported and excluded anyway
-("FINDINGS.md review" and "[dragon-lin:darkwing -> …"). The third is
-"Discord Bot" with 68 user messages: 54 without a header, and the 14
-free-text headers above. The guide's own advice, titles like `review:
-<topic>`, will produce more unmapped threads.
-
-Unmapped threads go in one Table 2 row, `t3:unmapped`, so Jason's messages
-there still count toward the Human column and the human-per-closed ratio. The
-other choice is to drop them, which would hide those 54 headerless prompts.
-That is Jason's decision. I recommend the row.
-
-A seat's row sums its Pi and T3 counts. JSON splits them by source. Nothing is
-counted twice: every T3 session today runs on `claudeAgent` or `codex`, which
-don't write `.pi/state`, and Filbert found no T3 header in any Pi log.
+created 2026-09-26, titles set by hand), plus "Darkwing in Claude" (archived)
+and "Dewey in Claude". Researcher has a directory and no thread. Three
+threads map to no seat. Two are imported and excluded anyway ("FINDINGS.md
+review" and "[dragon-lin:darkwing -> …"). The third is "Discord Bot" with 68
+user messages: 54 without a header, and the 14 free-text headers.
+
+Titles are current state, and T3 can write them itself. They go wrong three
+ways. T3 auto-titles an unnamed thread from Jason's first prompt, so "Rocko
+review of the plan" maps to rocko. A rename moves the whole history to
+another row. A seat thread titled for a topic drops into `t3:unmapped`.
+None of this changes the Human total or the human-per-closed ratio. It only
+moves counts between rows, but Gate F reads one seat's row.
+
+**Header cross-check.** The headers already say which seat a thread belongs
+to. For every user message whose header matches the fixed 6a rule and whose
+`to:` id equals the message's own `thread_id`:
+- in a mapped thread, the `to:` role, lower-cased, must equal that thread's
+ seat;
+- in an unmapped thread, the `to:` role must not be a seat name.
+
+A conflict exits 1 and names the thread id, its title and both roles. A
+header whose `to:` id is some other thread is not checked. The check reads
+message text only, not T3 metadata. In a live read at 21:02Z every header
+agreed: all 104 addressed to their own thread carried the full thread id and
+named that thread's seat (Sage 40, Darkwing 15, Filbert 18, Dewey 15, Rocko
+16).
+
+It catches a seat thread renamed to another seat or to a topic, once any
+agent writes to it. It also catches an auto-titled thread that agents
+address by a different seat. It misses a thread no agent ever writes to.
+Such a thread can only add human counts to a seat's row, never hide them, so
+for Gate F it errs toward a visible failure. The README says so.
+
+**Unmapped row.** Unmapped threads go in one Table 2 row, `t3:unmapped`
+(Sage's ruling), so their human messages still reach the Human column and
+the human-per-closed ratio.
+
+**Mapping in the JSON.** For each seat, the T3 thread ids and titles that
+made its row, and the unmapped thread ids and titles. Anyone checking a Gate
+F result can then see which threads the row came from.
+
+A seat's row sums its Pi and T3 counts, and the JSON splits them by source.
+Nothing is counted twice. Every T3 session today runs on `claudeAgent` or
+`codex`, which don't write `.pi/state`, and Filbert found no T3 header in any
+Pi log (6a record).
-Excluded, with the reason stated in the README:
+**Excluded,** with the reason stated in the README:
- Imported threads (`thread_id` starting `import:`, events marked
`historyImport`). They are partial copies of Claude Code sessions, not T3
traffic: 55 user messages in two threads here.
- Deleted threads (`deleted_at` set). Across all projects there are 3, with
3 messages. Archived threads count.
-
-## What fails closed
-
-With the T3 source on, each of these refuses the report with exit 1, the
-code the ledger already uses for unreadable session evidence. The report
-never falls back to Pi logs alone. As with `--no-issues`, `--no-t3` turns the
-source off, and the report then says T3 was not read.
-- The database is missing, unreadable, or won't open read-only (including
- the `-wal` without `-shm` case). This differs from the Pi reader, which
- treats a missing `.pi` as no messages. A missing Pi directory means no Pi
- seats ran here. A missing T3 database on this host means the path or T3
- changed, and a silent zero is the failure Gate F exists to prevent.
-- A required table or column is missing. The reader checks `PRAGMA
+- Threads in other T3 projects. Live, there is a project at `/home/jwoltje`
+ and a deleted one at `/mnt/storage/src`. A thread in either could work on
+ this repository and would not be counted. The workspace-root rule is still
+ the right one, but the README names this blind spot.
+
+## 4. What fails closed
+
+The source is on by default (Sage's ruling). `--no-t3` turns it off, and the
+report then says T3 was not read. `--t3-db <path>` reads another database
+file instead of `~/.t3/userdata/state.sqlite`. It exists for fixtures and
+gets the same checks.
+
+Each of these refuses the report with exit 1, the code the ledger already
+uses for unreadable session evidence. The report never falls back to Pi logs
+alone. Where the database is missing or won't open, the message names
+`--no-t3`.
+- The database is missing or unreadable, or won't open read-only. That
+ includes a directory that isn't writable when SQLite needs to create
+ `-shm`, and a busy error after the timeout. The Pi reader treats a missing
+ `.pi` as no messages, and this departs from it on purpose. A missing Pi
+ directory means no Pi seats ran here. A missing T3 database on this host
+ means the path or T3 changed, and a silent zero is the failure Gate F
+ exists to prevent.
+- `~/.t3`, `~/.t3/userdata` or `state.sqlite` is a symlink. With `--t3-db`,
+ the file and its directory are checked. The Pi reader checks every
+ ancestor too, but it skips symlinked entries. Skipping one named file
+ would be another silent zero, so this reader refuses.
+- A table or column the counts need is missing. The reader checks `PRAGMA
table_info` and names what's missing. This catches a T3 upgrade that
changes the schema.
- No project row, or more than one non-deleted row, for this repository root.
- A counted row has a bad `role`, non-string `text`, or a `created_at` that
doesn't parse. The Pi reader already refuses malformed JSONL and bad
timestamps the same way.
-- `state.sqlite` or `~/.t3/userdata` is a symlink. The Pi reader skips
- symlinked entries instead. For one named file, skipping would be another
- silent zero, so this reader refuses.
-
-The source never writes to the database. It never reads other files in
-`~/.t3`, and it passes no message text beyond `messageKind` and
-`issueNumbers`, the same rule as for Pi logs. The one outside effect is
-SQLite's own: a WAL reader takes read locks in the `-shm` file, as T3's own
-connections do.
-
-## Decisions for Jason
-
-1. The source is on by default, with `--no-t3` to turn it off. The other
- choice is off by default with `--t3` to turn it on. I recommend on by
- default, because Gate F exists to count these messages.
-2. Unmapped threads get a `t3:unmapped` row. The other choice is to drop
- them. I recommend the row.
-3. The 14 free-text headers from 09-17 to 09-22 stay counted as human. Fixing
- them would mean loosening the header grammar for history only, and I don't
- recommend it.
-
-## Acceptance for the build
-
-- Fixture databases built with `node:sqlite` in a temp dir, in WAL mode:
- seat threads and an unmapped thread; imported, deleted and archived
- threads; all three header forms, uppercase classes included; a message
- outside the date range; another project with the same seat titles.
-- Each fail-closed case above has its own test, including a schema column
- removed and `-wal` without `-shm`. One test opens a database whose newest
- message is still in the WAL and counts it. Another reads a database closed
- cleanly with no writer attached, which is the T3-stopped case.
-- The class fix is proven against HEAD's `messageKind`: an uppercase class
- counts as agent after the fix and as human before it.
+- A header conflicts with the title mapping (section 3).
+
+The reader never reads other files in `~/.t3`. It passes no message text
+beyond `messageKind`, `issueNumbers` and the header's `to:` role and id, the
+same rule as for Pi logs.
+
+## 5. Rulings
+
+Sage ruled on the three questions R1 put to Jason, as lead calls:
+1. The source is on by default. A missing or unreadable database exits 1,
+ and the message names `--no-t3`.
+2. Unmapped threads get the `t3:unmapped` row.
+3. The 14 free-text headers stay as recorded. They show only in the JSON
+ diagnostic.
+
+Sage also ruled that the class fix rides in this build, and that a missing
+diagnostic table reads `unknown` (F5).
+
+## 6. Acceptance for the build
+
+**No test opens the real `~/.t3`.** Both places in
+`packages/ledger/tests/ledger.test.mjs` that spawn `cli.mjs` (the shared
+`run()` helper and the direct `spawnSync` at line 66) set `HOME` to the
+fixture's temp directory. A test that forgets `--t3-db` or `--no-t3` then
+finds no database and fails closed. The existing tests aren't about T3. Each
+gets an empty fixture database at the fixture `HOME`'s default path, with
+one project row for the fixture root. So they run with the source on, and
+their expected rows don't change. One test asserts that a `HOME` with no
+database exits 1 and names `--no-t3`.
+
+Fixture databases are built with `node:sqlite` in a temp directory, in WAL
+mode:
+- seat threads and an unmapped thread; imported, deleted and archived
+ threads; a message outside the date range;
+- all three header forms, with uppercase classes in both the tmux preamble
+ and the T3 header;
+- a thread with the same seat title in another project;
+- a seat thread renamed to another seat, with an agent header to its own
+ id, which exits 1;
+- a "Sagebrush" title, which stays unmapped;
+- a thread titled "Researcher", which maps to the seat that has no thread
+ live.
+
+WAL states, each with its own test:
+- The newest message is only in `-wal`, with the writer still attached (the
+ live-T3 case). It is counted.
+- T3 stopped: the database closed cleanly with no writer. Counts are
+ correct, and the main file's bytes are unchanged afterwards.
+- `-wal` without `-shm` in a writable directory: made by a child writer with
+ `wal_autocheckpoint=0` that is SIGKILLed, then `-shm` deleted. The WAL
+ rows are counted.
+- `-wal` without `-shm` in a directory that isn't writable: exit 1, naming
+ `--no-t3`. Skipped when the tests run as root, where the mode bits don't
+ bind.
+- The stopped case in a directory that isn't writable: exit 1, naming
+ `--no-t3`, as I measured it. If the build reads there instead, the builder
+ changes this test to assert correct counts and records the correction in
+ the BUILD-LOG entry. Skipped as root too.
+
+Also:
+- Each other fail-closed case in section 4 has its own test, including a
+ removed schema column and each symlink.
+- A missing `orchestration_events` gives `unknown` for the diagnostic and
+ the same counts.
+- The class fix is proven against HEAD's `messageKind`. An uppercase class
+ in either preamble counts as agent after the fix and as human before it.
+- The JSON lists each seat's threads and the unmapped threads.
- A read against the live database gives the counts in this brief, allowing
- for messages sent since.
-- The ledger README's counting rules name the new source, the mapping rule
- and the exclusions.
+ for messages sent since. It exits 0 with no header conflict.
+- The ledger README's counting rules name the new source, both flags, the
+ mapping rule and the header check, and the exclusions. That includes the
+ symlinked-checkout case and the other-project blind spot.
- No suite runs the ledger tests, so the BUILD-LOG entry names the test file.
## Not in scope
@@ -194,5 +301,6 @@
- Claude Code transcripts (`~/.claude/projects`) and Codex sessions
(`~/.codex/sessions`). T3's database already holds every message T3
delivered, so those files would only duplicate it.
-- Any write to T3, any T3 API call, or anything that needs T3 running.
+- Any T3 API call, anything that needs T3 running, and any write beyond
+ SQLite's own `-wal` and `-shm` handling.
- Fixing the two tmux misclassifications Filbert found in 6a.
+198
View File
@@ -0,0 +1,198 @@
# Ledger: a read-only T3 thread source for Table 2 (Gate F brief)
Brief only, no code. Darkwing wrote it on 2026-09-26 at Sage's request. Filbert
reviews it, and Jason sees it on the decision sheet before anyone builds it.
Issue #1506.
## Why
Table 2 counts user messages per seat from `.pi/state/<seat>/sessions/*.jsonl`
only. Development seats now run in T3 on the Claude and Codex harnesses, so
their prompts, Jason's included, never reach a Pi log. Today the Human column
can't see T3 at all, and the zero it shows for T3 seats means "no source", not
"no human prompts". 6a (ef0020ad) taught `messageKind` the T3 header, but no
source the ledger reads contains one. Gate F (QUEUE row 6) passes when
Filbert's item closes with zero human messages from Jason. While the ledger
can't see T3, a zero there proves nothing.
## Where T3 keeps messages
T3 keeps its state in one SQLite database, `~/.t3/userdata/state.sqlite`, in
WAL mode (`state.sqlite-wal` and `state.sqlite-shm` sit beside it). Three
projection tables are enough:
- `projection_projects`: `project_id`, `workspace_root`, `deleted_at`.
- `projection_threads`: `thread_id`, `project_id`, `title`, `archived_at`,
`deleted_at`.
- `projection_thread_messages`: `message_id` (primary key), `thread_id`,
`role` (`user` or `assistant`), `text`, `created_at` (ISO UTC).
One more table is optional. In `orchestration_events`, each
`thread.message-sent` event carries `metadata_json.origin`. Messages typed in
the T3 app carry an `appVersion` there. Messages sent through T3's API or MCP
tools, which is how seats talk to each other, don't. See the cross-check below.
The same directory also holds `secrets/`, `clerk-tokens.json` and other
settings files. The reader opens `state.sqlite` and nothing else, and it
selects named columns only, never `*`.
## Reading it, with T3 running or not
The file stays on disk whether T3 runs or not. The reader opens it with Node's
built-in `node:sqlite` (`DatabaseSync`, `file:<path>?mode=ro`, `readOnly:
true`). That needs no dependency, and Node 26.8.1 prints no warning for it. I
read the live database this way today, while T3 was running, with no errors
and no locks. A WAL reader sees every committed message, including those still
in the `-wal` file.
Two rules:
- Never open with `immutable=1` and never copy the file. Both skip the WAL
and silently lose the newest messages. A copy of the three files is also
not atomic.
- If T3 stopped uncleanly and left a `-wal` without its `-shm`, a read-only
connection may be unable to rebuild the index. If the open fails, the
ledger reports it and refuses. I have not tested this case or the fully
stopped case. Both are acceptance checks below.
## Jason or agent
Reuse the 6a rule. The first line of `text` decides: a T3 header or the tmux
preamble counts as agent, `control-board` as the sender counts as board, and
anything else counts as human. Messages with role `user` count; assistant
messages don't.
6a has a defect this source would expose. Its regex allows only a lowercase
class (`class=[a-z-]+`). Seats send uppercase classes: Sage's DECISION, INFO,
REVIEW-REQUEST and REVIEW-NOTE, and my own REVIEW-REQUEST. In this project's
threads, 16 real agent headers fail on that alone and would count as human.
The fix is to make the class match case-insensitive. It belongs in this build
or just before it, reviewed with it. The ms-communications table lists
lowercase names, so the fix follows what seats send, not the table.
Cross-check, read at 2026-09-26T20:54Z for the mosaic-stack project (209
user messages outside imported and deleted threads, every one with its
`thread.message-sent` event):
| T3 origin | Header matches 6a | Count |
|---|---|---|
| typed in the app (has `appVersion`) | no | 99 |
| sent through the API (no `appVersion`) | yes | 80 |
| sent through the API | no, uppercase class | 16 |
| sent through the API | no, free-text roles | 14 |
No message typed in the app carries a header, and every API message in this
project carries one of the three forms. The 14 free-text ones are older
Discord Bot thread headers such as `[from: SetSpark coordinator (…) -> to:
Discord Bot (…)]`, written before the guide fixed the format. With the class
fix they still count as human. That's 14 wrong human counts, all dated
2026-09-17 to 2026-09-22.
Recommendation: the header rule decides, as Sage asked. The reader also
reports one diagnostic number, not used in any table: user messages the rule
calls human that T3 recorded as sent through the API. That count is how the
uppercase-class bug showed up, and it would catch the next format drift. The
origin field is T3's internal metadata, not a documented contract, so it
shouldn't decide anything. I'd make it JSON only, so Table 2's layout stays
the same.
## Thread to seat
A thread counts for this checkout only if its project's `workspace_root` is
the ledger's repository root. That is `/mnt/storage/src/mosaic-stack`, project
`34050c07`.
Thread IDs change whenever Jason starts a new thread for a seat, so there's no
fixed map. T3-AGENT-COMMS.md already names threads after the seat ("Darkwing",
"Sage", "Dewey in Claude"). Proposed rule: a thread belongs to seat `<s>` when
`<s>` is a real directory under `agents/` and the lower-cased title equals
`<s>` or starts with `<s>` followed by a space. Several threads can map to one
seat. Their counts add up, as several Pi session files already do.
Today that maps Sage, Darkwing, Filbert, Dewey and Rocko (one thread each,
created 2026-09-26), plus "Darkwing in Claude" (archived) and "Dewey in
Claude". Three threads map to no seat. Two are imported and excluded anyway
("FINDINGS.md review" and "[dragon-lin:darkwing -> …"). The third is
"Discord Bot" with 68 user messages: 54 without a header, and the 14
free-text headers above. The guide's own advice, titles like `review:
<topic>`, will produce more unmapped threads.
Unmapped threads go in one Table 2 row, `t3:unmapped`, so Jason's messages
there still count toward the Human column and the human-per-closed ratio. The
other choice is to drop them, which would hide those 54 headerless prompts.
That is Jason's decision. I recommend the row.
A seat's row sums its Pi and T3 counts. JSON splits them by source. Nothing is
counted twice: every T3 session today runs on `claudeAgent` or `codex`, which
don't write `.pi/state`, and Filbert found no T3 header in any Pi log.
Excluded, with the reason stated in the README:
- Imported threads (`thread_id` starting `import:`, events marked
`historyImport`). They are partial copies of Claude Code sessions, not T3
traffic: 55 user messages in two threads here.
- Deleted threads (`deleted_at` set). Across all projects there are 3, with
3 messages. Archived threads count.
## What fails closed
With the T3 source on, each of these refuses the report with exit 1, the
code the ledger already uses for unreadable session evidence. The report
never falls back to Pi logs alone. As with `--no-issues`, `--no-t3` turns the
source off, and the report then says T3 was not read.
- The database is missing, unreadable, or won't open read-only (including
the `-wal` without `-shm` case). This differs from the Pi reader, which
treats a missing `.pi` as no messages. A missing Pi directory means no Pi
seats ran here. A missing T3 database on this host means the path or T3
changed, and a silent zero is the failure Gate F exists to prevent.
- A required table or column is missing. The reader checks `PRAGMA
table_info` and names what's missing. This catches a T3 upgrade that
changes the schema.
- No project row, or more than one non-deleted row, for this repository root.
- A counted row has a bad `role`, non-string `text`, or a `created_at` that
doesn't parse. The Pi reader already refuses malformed JSONL and bad
timestamps the same way.
- `state.sqlite` or `~/.t3/userdata` is a symlink. The Pi reader skips
symlinked entries instead. For one named file, skipping would be another
silent zero, so this reader refuses.
The source never writes to the database. It never reads other files in
`~/.t3`, and it passes no message text beyond `messageKind` and
`issueNumbers`, the same rule as for Pi logs. The one outside effect is
SQLite's own: a WAL reader takes read locks in the `-shm` file, as T3's own
connections do.
## Decisions for Jason
1. The source is on by default, with `--no-t3` to turn it off. The other
choice is off by default with `--t3` to turn it on. I recommend on by
default, because Gate F exists to count these messages.
2. Unmapped threads get a `t3:unmapped` row. The other choice is to drop
them. I recommend the row.
3. The 14 free-text headers from 09-17 to 09-22 stay counted as human. Fixing
them would mean loosening the header grammar for history only, and I don't
recommend it.
## Acceptance for the build
- Fixture databases built with `node:sqlite` in a temp dir, in WAL mode:
seat threads and an unmapped thread; imported, deleted and archived
threads; all three header forms, uppercase classes included; a message
outside the date range; another project with the same seat titles.
- Each fail-closed case above has its own test, including a schema column
removed and `-wal` without `-shm`. One test opens a database whose newest
message is still in the WAL and counts it. Another reads a database closed
cleanly with no writer attached, which is the T3-stopped case.
- The class fix is proven against HEAD's `messageKind`: an uppercase class
counts as agent after the fix and as human before it.
- A read against the live database gives the counts in this brief, allowing
for messages sent since.
- The ledger README's counting rules name the new source, the mapping rule
and the exclusions.
- No suite runs the ledger tests, so the BUILD-LOG entry names the test file.
## Not in scope
- Claude Code transcripts (`~/.claude/projects`) and Codex sessions
(`~/.codex/sessions`). T3's database already holds every message T3
delivered, so those files would only duplicate it.
- Any write to T3, any T3 API call, or anything that needs T3 running.
- Fixing the two tmux misclassifications Filbert found in 6a.
@@ -0,0 +1,74 @@
--- r2.md
+++ docs/plans/2026-09-26_ledger-t3-source.md
@@ -3,9 +3,9 @@
Brief only, no code. Darkwing wrote it on 2026-09-26 at Sage's request, issue
#1506. R1 (sha256 08959a05) went to Filbert, whose review asked for
revisions: `agents/filbert/work/ledger-t3-source-review-2026-09-26.md`, sha256
-19dda29a. This is R2. It takes every finding, and it records Sage's rulings
-on the three open questions. Section 1 has one measurement that differs from
-the review.
+19dda29a. R2 (sha256 e8300cb6) took every finding and recorded Sage's
+rulings on the three open questions. Filbert approved R2 with three nits,
+review sha256 bb02d8d3. This is R3, which takes the nits.
## Why
@@ -68,10 +68,10 @@
| T3 stopped cleanly, no `-wal` or `-shm` | yes | reads, then leaves an empty `-wal` and a 32 KiB `-shm` |
| T3 stopped cleanly | no | fails, SQLite 1544 "attempt to write a readonly database" |
-In every case the main file's bytes stayed the same. The last row is where
-Filbert and I differ. His review says the stopped-case read works with the
-directory read-only. In my run it failed with and without the read
-transaction. The build's test settles it. Either way a failed open is exit 1.
+In every case the main file's bytes stayed the same. Filbert's first
+review said the last case reads. His test had reused a database whose empty
+`-wal` and `-shm` were still present. On a true clean stop he also got 1544,
+and his review records the correction. A failed open is exit 1.
So the accurate claim: the reader never writes the main database file. Like
any SQLite connection, it may create or update `-wal` and `-shm` beside it
@@ -198,7 +198,9 @@
The source is on by default (Sage's ruling). `--no-t3` turns it off, and the
report then says T3 was not read. `--t3-db <path>` reads another database
file instead of `~/.t3/userdata/state.sqlite`. It exists for fixtures and
-gets the same checks.
+gets the same checks. The JSON records the database path read and whether
+it was the default. When it wasn't, the text report adds one line naming the
+path, so a Gate F result can't come from a fixture unnoticed.
Each of these refuses the report with exit 1, the code the ledger already
uses for unreadable session evidence. The report never falls back to Pi logs
@@ -248,7 +250,9 @@
fixture's temp directory. A test that forgets `--t3-db` or `--no-t3` then
finds no database and fails closed. The existing tests aren't about T3. Each
gets an empty fixture database at the fixture `HOME`'s default path, with
-one project row for the fixture root. So they run with the source on, and
+one project row for the fixture root. That row stores
+`fs.realpathSync(root)`, because the CLI resolves its root through realpath
+and a symlinked temp directory would otherwise not match. So they run with the source on, and
their expected rows don't change. One test asserts that a `HOME` with no
database exits 1 and names `--no-t3`.
@@ -277,9 +281,7 @@
`--no-t3`. Skipped when the tests run as root, where the mode bits don't
bind.
- The stopped case in a directory that isn't writable: exit 1, naming
- `--no-t3`, as I measured it. If the build reads there instead, the builder
- changes this test to assert correct counts and records the correction in
- the BUILD-LOG entry. Skipped as root too.
+ `--no-t3`. Skipped as root too.
Also:
- Each other fail-closed case in section 4 has its own test, including a
@@ -288,7 +290,9 @@
the same counts.
- The class fix is proven against HEAD's `messageKind`. An uppercase class
in either preamble counts as agent after the fix and as human before it.
-- The JSON lists each seat's threads and the unmapped threads.
+- The JSON lists each seat's threads and the unmapped threads, and the
+ database path with whether it was the default. A `--t3-db` run prints the
+ path line in the text report, and a default run doesn't.
- A read against the live database gives the counts in this brief, allowing
for messages sent since. It exits 0 with no header conflict.
- The ledger README's counting rules name the new source, both flags, the
+306
View File
@@ -0,0 +1,306 @@
# Ledger: a read-only T3 thread source for Table 2 (Gate F brief)
Brief only, no code. Darkwing wrote it on 2026-09-26 at Sage's request, issue
#1506. R1 (sha256 08959a05) went to Filbert, whose review asked for
revisions: `agents/filbert/work/ledger-t3-source-review-2026-09-26.md`, sha256
19dda29a. This is R2. It takes every finding, and it records Sage's rulings
on the three open questions. Section 1 has one measurement that differs from
the review.
## Why
Table 2 counts user messages per seat from `.pi/state/<seat>/sessions/*.jsonl`
only. Development seats now run in T3 on the Claude and Codex harnesses, so
their prompts, Jason's included, never reach a Pi log. Today the Human column
can't see T3 at all, and the zero it shows for T3 seats means "no source", not
"no human prompts". 6a (ef0020ad) taught `messageKind` the T3 header, but no
source the ledger reads contains one. Gate F (QUEUE row 6) passes when
Filbert's item closes with zero human messages from Jason. While the ledger
can't see T3, a zero there proves nothing.
## Where T3 keeps messages
T3 keeps its state in one SQLite database, `~/.t3/userdata/state.sqlite`, in
WAL mode (`state.sqlite-wal` and `state.sqlite-shm` sit beside it). The counts
need three projection tables:
- `projection_projects`: `project_id`, `workspace_root`, `deleted_at`.
- `projection_threads`: `thread_id`, `project_id`, `title`, `archived_at`,
`deleted_at`.
- `projection_thread_messages`: `message_id` (primary key), `thread_id`,
`role` (`user` or `assistant`), `text`, `created_at` (ISO UTC).
The JSON diagnostic reads one more. In `orchestration_events`, each
`thread.message-sent` event carries `metadata_json.origin`. Messages typed in
the T3 app carry an `appVersion` there. Messages sent through T3's API or MCP
tools, which is how seats talk to each other, don't.
The same directory also holds `secrets/`, `clerk-tokens.json` and other
settings files. The reader opens `state.sqlite` and nothing else, and it
selects named columns only, never `*`.
## 1. Reading it, with T3 running or not
The reader uses Node's built-in `node:sqlite` (`DatabaseSync`). That needs no
dependency, and Node 26.8.1 (SQLite 3.53.4) prints no warning for it.
- **URI.** Build it with `pathToFileURL(dbPath)` and set `mode=ro` through
`searchParams`, then pass `readOnly: true`. A `?`, `#` or `%` in the home
path would break a string-built URI.
- **One snapshot.** Run every query, from the schema checks through the
diagnostic, inside one `BEGIN` … `COMMIT`. In autocommit mode each
statement sees its own snapshot while T3 writes between them.
- **Busy timeout.** Set `DatabaseSync`'s `timeout` to 5 s. A transient
`SQLITE_BUSY` during a T3 checkpoint then waits instead of failing. A busy
error after the timeout exits 1 like any open failure.
- **No `immutable=1` and no copy.** Both lose the WAL. Filbert found worse
than lost messages: with a table created inside the WAL, `immutable=1`
fails with `no such table`.
What happens on disk. Filbert and I both tested these in scratch
directories:
| State | Directory writable | Result |
|---|---|---|
| T3 running, writer attached, newest rows only in `-wal` | yes | reads them |
| `-wal` without `-shm` (writer killed, `-shm` removed) | yes | reads the WAL rows and creates `-shm` |
| `-wal` without `-shm` | no | open fails, SQLite 14 |
| T3 stopped cleanly, no `-wal` or `-shm` | yes | reads, then leaves an empty `-wal` and a 32 KiB `-shm` |
| T3 stopped cleanly | no | fails, SQLite 1544 "attempt to write a readonly database" |
In every case the main file's bytes stayed the same. The last row is where
Filbert and I differ. His review says the stopped-case read works with the
directory read-only. In my run it failed with and without the read
transaction. The build's test settles it. Either way a failed open is exit 1.
So the accurate claim: the reader never writes the main database file. Like
any SQLite connection, it may create or update `-wal` and `-shm` beside it
and takes read locks in `-shm`. T3 opens normally afterwards.
## 2. Jason or agent
Reuse the 6a rule. The first line of `text` decides: a T3 header or the tmux
preamble counts as agent, `control-board` as the sender counts as board, and
anything else counts as human. Messages with role `user` count; assistant
messages don't.
The class fix rides in this build (Sage's ruling). HEAD's
`packages/ledger/src/ledger.mjs:81` (tmux) and `:83` (T3) both allow only
`class=[a-z-]+`. Both become case-insensitive. Seats send uppercase classes:
Sage's DECISION, INFO, REVIEW-REQUEST and REVIEW-NOTE, and my own
REVIEW-REQUEST. In this project's threads 16 real agent headers failed on
that alone at 20:54Z. The ms-communications table lists lowercase names, so
the fix follows what seats send, not the table.
Cross-check, read at 2026-09-26T20:54Z for the mosaic-stack project (209
user messages outside imported and deleted threads, every one with its
`thread.message-sent` event). Filbert's later read agreed, plus messages sent
since.
| T3 origin | Header matches 6a | Count |
|---|---|---|
| typed in the app (has `appVersion`) | no | 99 |
| sent through the API (no `appVersion`) | yes | 80 |
| sent through the API | no, uppercase class | 16 |
| sent through the API | no, free-text roles | 14 |
No message typed in the app carries a header, and every API message in this
project carries one of the three forms. The 14 free-text ones are older
Discord Bot thread headers such as `[from: SetSpark coordinator (…) -> to:
Discord Bot (…)]`, written before the guide fixed the format. Sage ruled they
stay as recorded: they count as human, dated 2026-09-17 to 2026-09-22.
The header rule decides. The JSON also carries one diagnostic that feeds no
table or total: user messages the rule calls human that T3 recorded as sent
through the API. That number exposed the class bug and would catch the next
format drift. `origin` is T3's internal metadata, not a documented contract,
so it decides nothing. If `orchestration_events` or a column it needs is
missing, the diagnostic reads `unknown` and the report goes on (Sage's
ruling on F5). Missing tables the counts depend on still exit 1.
## 3. Thread to seat
**Project.** A thread counts for this checkout only if its project's
`workspace_root` equals the ledger's repository root, byte for byte. The CLI
already takes that root from the realpath of its own URL, today
`/mnt/storage/src/mosaic-stack`, project `34050c07`. So a T3 project opened
through the compatibility symlink `~/src/mosaic-stack-dev-test` doesn't
match, and "no project row" is the right refusal. The README says so.
**Title rule.** Thread IDs change whenever Jason starts a new thread for a
seat, so there's no fixed map. T3-AGENT-COMMS.md already names threads after
the seat ("Darkwing", "Sage", "Dewey in Claude"). A thread belongs to seat
`<s>` when `<s>` is a real directory under `agents/` and the lower-cased
title equals `<s>` or starts with `<s>` followed by a space. So "Sagebrush"
stays unmapped. Several threads can map to one seat, and their counts add
up, as several Pi session files already do.
Today that maps Sage, Darkwing, Filbert, Dewey and Rocko (one thread each,
created 2026-09-26, titles set by hand), plus "Darkwing in Claude" (archived)
and "Dewey in Claude". Researcher has a directory and no thread. Three
threads map to no seat. Two are imported and excluded anyway ("FINDINGS.md
review" and "[dragon-lin:darkwing -> …"). The third is "Discord Bot" with 68
user messages: 54 without a header, and the 14 free-text headers.
Titles are current state, and T3 can write them itself. They go wrong three
ways. T3 auto-titles an unnamed thread from Jason's first prompt, so "Rocko
review of the plan" maps to rocko. A rename moves the whole history to
another row. A seat thread titled for a topic drops into `t3:unmapped`.
None of this changes the Human total or the human-per-closed ratio. It only
moves counts between rows, but Gate F reads one seat's row.
**Header cross-check.** The headers already say which seat a thread belongs
to. For every user message whose header matches the fixed 6a rule and whose
`to:` id equals the message's own `thread_id`:
- in a mapped thread, the `to:` role, lower-cased, must equal that thread's
seat;
- in an unmapped thread, the `to:` role must not be a seat name.
A conflict exits 1 and names the thread id, its title and both roles. A
header whose `to:` id is some other thread is not checked. The check reads
message text only, not T3 metadata. In a live read at 21:02Z every header
agreed: all 104 addressed to their own thread carried the full thread id and
named that thread's seat (Sage 40, Darkwing 15, Filbert 18, Dewey 15, Rocko
16).
It catches a seat thread renamed to another seat or to a topic, once any
agent writes to it. It also catches an auto-titled thread that agents
address by a different seat. It misses a thread no agent ever writes to.
Such a thread can only add human counts to a seat's row, never hide them, so
for Gate F it errs toward a visible failure. The README says so.
**Unmapped row.** Unmapped threads go in one Table 2 row, `t3:unmapped`
(Sage's ruling), so their human messages still reach the Human column and
the human-per-closed ratio.
**Mapping in the JSON.** For each seat, the T3 thread ids and titles that
made its row, and the unmapped thread ids and titles. Anyone checking a Gate
F result can then see which threads the row came from.
A seat's row sums its Pi and T3 counts, and the JSON splits them by source.
Nothing is counted twice. Every T3 session today runs on `claudeAgent` or
`codex`, which don't write `.pi/state`, and Filbert found no T3 header in any
Pi log (6a record).
**Excluded,** with the reason stated in the README:
- Imported threads (`thread_id` starting `import:`, events marked
`historyImport`). They are partial copies of Claude Code sessions, not T3
traffic: 55 user messages in two threads here.
- Deleted threads (`deleted_at` set). Across all projects there are 3, with
3 messages. Archived threads count.
- Threads in other T3 projects. Live, there is a project at `/home/jwoltje`
and a deleted one at `/mnt/storage/src`. A thread in either could work on
this repository and would not be counted. The workspace-root rule is still
the right one, but the README names this blind spot.
## 4. What fails closed
The source is on by default (Sage's ruling). `--no-t3` turns it off, and the
report then says T3 was not read. `--t3-db <path>` reads another database
file instead of `~/.t3/userdata/state.sqlite`. It exists for fixtures and
gets the same checks.
Each of these refuses the report with exit 1, the code the ledger already
uses for unreadable session evidence. The report never falls back to Pi logs
alone. Where the database is missing or won't open, the message names
`--no-t3`.
- The database is missing or unreadable, or won't open read-only. That
includes a directory that isn't writable when SQLite needs to create
`-shm`, and a busy error after the timeout. The Pi reader treats a missing
`.pi` as no messages, and this departs from it on purpose. A missing Pi
directory means no Pi seats ran here. A missing T3 database on this host
means the path or T3 changed, and a silent zero is the failure Gate F
exists to prevent.
- `~/.t3`, `~/.t3/userdata` or `state.sqlite` is a symlink. With `--t3-db`,
the file and its directory are checked. The Pi reader checks every
ancestor too, but it skips symlinked entries. Skipping one named file
would be another silent zero, so this reader refuses.
- A table or column the counts need is missing. The reader checks `PRAGMA
table_info` and names what's missing. This catches a T3 upgrade that
changes the schema.
- No project row, or more than one non-deleted row, for this repository root.
- A counted row has a bad `role`, non-string `text`, or a `created_at` that
doesn't parse. The Pi reader already refuses malformed JSONL and bad
timestamps the same way.
- A header conflicts with the title mapping (section 3).
The reader never reads other files in `~/.t3`. It passes no message text
beyond `messageKind`, `issueNumbers` and the header's `to:` role and id, the
same rule as for Pi logs.
## 5. Rulings
Sage ruled on the three questions R1 put to Jason, as lead calls:
1. The source is on by default. A missing or unreadable database exits 1,
and the message names `--no-t3`.
2. Unmapped threads get the `t3:unmapped` row.
3. The 14 free-text headers stay as recorded. They show only in the JSON
diagnostic.
Sage also ruled that the class fix rides in this build, and that a missing
diagnostic table reads `unknown` (F5).
## 6. Acceptance for the build
**No test opens the real `~/.t3`.** Both places in
`packages/ledger/tests/ledger.test.mjs` that spawn `cli.mjs` (the shared
`run()` helper and the direct `spawnSync` at line 66) set `HOME` to the
fixture's temp directory. A test that forgets `--t3-db` or `--no-t3` then
finds no database and fails closed. The existing tests aren't about T3. Each
gets an empty fixture database at the fixture `HOME`'s default path, with
one project row for the fixture root. So they run with the source on, and
their expected rows don't change. One test asserts that a `HOME` with no
database exits 1 and names `--no-t3`.
Fixture databases are built with `node:sqlite` in a temp directory, in WAL
mode:
- seat threads and an unmapped thread; imported, deleted and archived
threads; a message outside the date range;
- all three header forms, with uppercase classes in both the tmux preamble
and the T3 header;
- a thread with the same seat title in another project;
- a seat thread renamed to another seat, with an agent header to its own
id, which exits 1;
- a "Sagebrush" title, which stays unmapped;
- a thread titled "Researcher", which maps to the seat that has no thread
live.
WAL states, each with its own test:
- The newest message is only in `-wal`, with the writer still attached (the
live-T3 case). It is counted.
- T3 stopped: the database closed cleanly with no writer. Counts are
correct, and the main file's bytes are unchanged afterwards.
- `-wal` without `-shm` in a writable directory: made by a child writer with
`wal_autocheckpoint=0` that is SIGKILLed, then `-shm` deleted. The WAL
rows are counted.
- `-wal` without `-shm` in a directory that isn't writable: exit 1, naming
`--no-t3`. Skipped when the tests run as root, where the mode bits don't
bind.
- The stopped case in a directory that isn't writable: exit 1, naming
`--no-t3`, as I measured it. If the build reads there instead, the builder
changes this test to assert correct counts and records the correction in
the BUILD-LOG entry. Skipped as root too.
Also:
- Each other fail-closed case in section 4 has its own test, including a
removed schema column and each symlink.
- A missing `orchestration_events` gives `unknown` for the diagnostic and
the same counts.
- The class fix is proven against HEAD's `messageKind`. An uppercase class
in either preamble counts as agent after the fix and as human before it.
- The JSON lists each seat's threads and the unmapped threads.
- A read against the live database gives the counts in this brief, allowing
for messages sent since. It exits 0 with no header conflict.
- The ledger README's counting rules name the new source, both flags, the
mapping rule and the header check, and the exclusions. That includes the
symlinked-checkout case and the other-project blind spot.
- No suite runs the ledger tests, so the BUILD-LOG entry names the test file.
## Not in scope
- Claude Code transcripts (`~/.claude/projects`) and Codex sessions
(`~/.codex/sessions`). T3's database already holds every message T3
delivered, so those files would only duplicate it.
- Any T3 API call, anything that needs T3 running, and any write beyond
SQLite's own `-wal` and `-shm` handling.
- Fixing the two tmux misclassifications Filbert found in 6a.
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Row 100 gate: every package's own test script, from its directory, then
# every scripts/test-*.sh, sequential, full output kept.
cd ~/darkwing-scratch/r100/wt || exit 2
W=$PWD
export TMPDIR=~/darkwing-scratch/r100/tmp
O=${GATE_OUT:-~/darkwing-scratch/r100/gate}
mkdir -p "$TMPDIR" "$O"
for d in packages/*/; do
p=$(basename "$d")
t=$(node -e 'const s=require(process.argv[1]).scripts?.test; if (s) console.log(s)' "$W/$d/package.json")
[ -n "$t" ] || { echo "$p: no test script"; continue; }
s=$(date -u +%T)
(cd "$W/$d" && bash -c "$t") > $O/node-$p.txt 2>&1; rc=$?
echo "$p rc=$rc $s-$(date -u +%T) $(grep -E '^ℹ (pass|fail|skipped)' $O/node-$p.txt | tr '\n' ' ') $(grep -cE '^✖|^not ok' $O/node-$p.txt) marks"
done
for f in scripts/test-*.sh; do
n=$(basename $f .sh); s=$(date -u +%T)
if [ $n = test-task ]; then DOCKER_HOST=unix:///nonexistent bash $f > $O/$n.txt 2>&1; else bash $f > $O/$n.txt 2>&1; fi; rc=$?
echo "$n rc=$rc $s-$(date -u +%T) $(tail -1 $O/$n.txt | cut -c1-120)"
done
+6
View File
@@ -0,0 +1,6 @@
// node mut.mjs <file> <from> <to>: replace exactly one occurrence, else exit 3.
import { readFileSync, writeFileSync } from 'node:fs';
const [f, a, b] = process.argv.slice(2);
const s = readFileSync(f, 'utf8'); const n = s.split(a).length - 1;
if (n !== 1) { console.error(`matches=${n}`); process.exit(3); }
writeFileSync(f, s.replace(a, () => b));
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Row 100 (#1594) mutants. Run from the candidate worktree's root:
# LOG=<dir> bash mutants.sh <queue-100 dir> > mutants.txt
# Each mutant edits one site with mut.mjs (exactly one match, else exit 3),
# runs the named test files (DRY=1 only checks the match) in their own process group (run.py, killed after
# 240 s) and restores the file. "killed" means the run failed; "survived"
# means it passed.
set -u
Q=$1
run() {
local name=$1 file=$2 from=$3 to=$4; shift 4
cp -- "$file" "$file.orig"
if ! node "$Q/mut.mjs" "$file" "$from" "$to"; then
echo "$name: NO MATCH"; mv -- "$file.orig" "$file"; return
fi
if [ -n "${DRY:-}" ]; then echo "$name: match"; mv -- "$file.orig" "$file"; return; fi
local log="${LOG:-.}/mut-$name.log"
if python3 "$Q/run.py" 240 "$log" node --test "$@"; then
echo "$name: survived"
else
echo "$name: killed ($(grep -m1 -o 'ℹ fail [0-9]*' "$log"; grep -m1 -o 'TIMEOUT.*' "$log"), $(grep -m1 '^✖' "$log" | cut -c1-90))"
fi
mv -- "$file.orig" "$file"
}
WK=packages/seat/src/working.mjs
GS=packages/cli/src/git-socket.mjs
GTL=packages/harness/src/git-tools.mjs
WT=packages/seat/tests/working.test.mjs
GT=packages/cli/tests/git-socket.test.mjs
HT=packages/harness/tests/git-tools.test.mjs
# Timeouts: SIGTERM first, then SIGKILL after the grace period.
run sigkill-first $WK $' child.kill("SIGTERM");\n kill = setTimeout(() => child.kill("SIGKILL"), limits.graceMs);' ' child.kill("SIGKILL");' $WT
run no-escalation $WK $' kill = setTimeout(() => child.kill("SIGKILL"), limits.graceMs);\n' '' $WT
# rollback-failed, refused and logged
run no-rollback-check $WK $' if (!undone.ok) refuse("rollback-failed",' $' if (false) refuse("rollback-failed",' $WT $GT
run rollback-not-logged $GS 'if (code === "git-failed" || code === "rollback-failed") log(' 'if (code === "git-failed") log(' $GT
# The five untested refusals
run no-root-check $WK ' if (!r.ok) refuse("root-unreadable",' ' if (false) refuse("root-unreadable",' $WT
run no-role-check $GS 'if (!who.role || who.business !== b.id) throw' 'if (who.business !== b.id) throw' $GT
run no-business-check $GS 'if (!who.role || who.business !== b.id) throw' 'if (!who.role) throw' $GT
run no-project-check $GS ' if (!p) throw new Refusal("unknown-project");' '' $GT
run no-busy-check $GS 'if (active.size >= maxActive) return' 'if (false) return' $GT
run no-closing-answer $GS $' waiting.set(socket, () => done(new Refusal("host-closing")));\n' '' $GT
# The body's total deadline
run no-body-deadline $GS '), deadlineMs);' '), 2 ** 31 - 1);' $GT
# fetch-aborted
run abort-as-git-failed $GS 'const aborted = () => new Refusal("fetch-aborted",' 'const aborted = () => new Error(' $GT
# A reviewer that hangs up once it has every byte was served. The first cut
# filed any close before 'finish' as fetch-aborted; so does this mutant.
run close-is-aborted $GS 'const served = () => !socket.errored;' 'const served = () => socket.writableFinished;' $GT
# A queued write that fails is fetch-aborted.
run errored-ignored $GS 'const served = () => !socket.errored;' 'const served = () => true;' $GT
# have: working refs first, at most HAVE_MAX
run have-origin-first $GTL 'for (const remote of ["refs/remotes/working", "refs/remotes/origin"])' 'for (const remote of ["refs/remotes/origin", "refs/remotes/working"])' $HT
run have-uncapped $GTL 'if (OID.test(oid) && have.size < HAVE_MAX) have.add(oid);' 'if (OID.test(oid)) have.add(oid);' $HT
+116
View File
@@ -0,0 +1,116 @@
# Row 100 (#1594) round 1: git socket hardening and refusal tests
Brief: docs/plans/2026-10-12_pm-test-fixes.md, section "Row 93 follow-up: git socket hardening and refusal tests" (commit 8fda9611, blob 017ac786).
BASE: 8e0fbca420a3161ce98c5e0f3910f500daabebba. Candidate: build.patch against BASE; candidate-manifest.sha256 lists the files.
## What changed
- packages/seat/src/working.mjs. A git past GIT_TIMEOUT_MS gets SIGTERM, then SIGKILL after GIT_GRACE_MS (5 s). git removes its lockfiles on SIGTERM. A rollback whose update-ref fails refuses rollback-failed, which outranks record's own error. push and review take `limits` so the tests can shorten both timers.
- packages/cli/src/git-socket.mjs.
- readBody has a total deadline, `bodyDeadline(size) = 30000 + ceil(size / 256)` ms (161 s at the 32 MiB cap), beside the 30 s idle timeout. Both refuse body-timeout.
- The bundle send to a reviewer is a loop, `send()`. It used to be pipeline(), whose ERR_STREAM_PREMATURE_CLOSE became git-failed. A close while chunks are still to go is fetch-aborted, and so is a close after a write of a queued chunk failed (EPIPE). A close after every chunk left with no failed write is a served fetch, because the runner reads exactly `size` bytes and hangs up, often before the host's loop sees the file's end or before 'finish'.
- git-failed and rollback-failed go to the log as `git socket: <code>: <message>`.
- close() answers host-closing to a connection still reading its request line, and drops the rest as before.
- `maxActive` and `bodyDeadline` are options for the tests.
- packages/harness/src/git-tools.mjs. fetch_review's `have` lists every clone's working refs, newest first, before any of origin's, and stops at HAVE_MAX.
- packages/seat/README.md: rollback-failed, the SIGTERM/SIGKILL timing, and the lock's one-launcher-per-dataRoot assumption.
- packages/cli/README.md: body-timeout, what nothing-to-fetch, unknown-project and fetch-aborted mean to a reviewer, the log lines, and host-closing.
## Tests
| Brief item | Test |
|---|---|
| not-a-role | git-socket.test: no role, then another business |
| host-closing | git-socket.test: close() while a request line is half sent |
| git-busy | git-socket.test: maxActive 2, a third connection |
| root-unreadable | working.test: a root that doesn't exist |
| unknown-project | git-socket.test: a review naming a project the business lacks |
| timed-out push leaves no lock | working.test: a hook holds the ref lock, SIGTERM, no .lock, next push lands |
| SIGKILL escalation | working.test: a git that ignores SIGTERM |
| rollback-failed | working.test (delete and previous-ref cases) and git-socket.test (logged, trail) |
| trickling client | git-socket.test: body-timeout within the deadline, slot freed |
| fetch-aborted | git-socket.test: a reviewer leaves mid-bundle; no git-failed, no log |
| a served fetch isn't fetch-aborted | git-socket.test: the review loop now closes the socket and checks action.refused is empty; a raw reviewer takes a 1 MiB bundle and hangs up at once, ten times; `send()` against two stand-in sockets, one that never finishes and one whose queued write fails EPIPE |
| have over 32 refs | harness git-tools.test: 53 refs over two clones |
## Mutants
16 mutants, each in `agents/darkwing/work/queue-100/mutants.sh`, run with `mut.mjs` (one exact match, else exit 3) and `run.py` (own process group, killpg at 240 s). All 16 killed:
```
sigkill-first: killed (ℹ fail 1, ✖ a git past its time gets SIGTERM, leaves no ref lock, and the next push lands (10291.116)
no-escalation: killed (TIMEOUT after 240 s, ✖ a git that ignores SIGTERM gets SIGKILL once the grace period is over (20098.619138ms))
no-rollback-check: killed (ℹ fail 2, ✖ a record that fails and can't be undone refuses rollback-failed, which the host logs (39)
rollback-not-logged: killed (ℹ fail 1, ✖ a record that fails and can't be undone refuses rollback-failed, which the host logs (41)
no-root-check: killed (ℹ fail 2, ✖ a root that can't be read refuses root-unreadable (35.076601ms))
no-role-check: killed (ℹ fail 1, ✖ an identity with no role, or of another business, refuses not-a-role and moves no ref (2)
no-business-check: killed (ℹ fail 1, ✖ an identity with no role, or of another business, refuses not-a-role and moves no ref (2)
no-project-check: killed (ℹ fail 1, ✖ a review of a project the business no longer has refuses unknown-project (259.074398ms))
no-busy-check: killed (ℹ fail 2, ✖ past maxActive connections the socket refuses git-busy, and a hung-up connection frees i)
no-closing-answer: killed (ℹ fail 1, ✖ close() answers host-closing to a connection still sending its request line (180.301962m)
no-body-deadline: killed (ℹ fail 1, ✖ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (5212.)
abort-as-git-failed: killed (ℹ fail 2, ✖ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fau)
close-is-aborted: killed (ℹ fail 2, ✖ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (508)
errored-ignored: killed (ℹ fail 1, ✖ send: a close after every byte left is served; one that fails a queued write is fetch-ab)
have-origin-first: killed (ℹ fail 1, ✖ fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX (1)
have-uncapped: killed (ℹ fail 1, ✖ fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX (1)
```
no-escalation hits the 240 s cap: its test fails at its own 20 s timeout, and the wrapper git that ignores SIGTERM keeps node --test alive until run.py kills the group. The sweep after each run found none of my processes left.
### What the mutants found
The first cut of `send()` waited for 'finish' and filed any earlier close as fetch-aborted. abort-as-git-failed showed it: the review loop test logged `git-failed: the reviewer left before the bundle was sent` for both of its fetches, so with the real code each successful fetch was on the trail as `reviewer fetch-aborted`. The review loop test checked the log but not action.refused, so it passed. Instrumenting showed the close usually comes before the loop reads the file's end, not only before 'finish'. The fix is above; the review loop now checks action.refused after close(), and the three tests in the table cover the race. A mutant that counted bytes (`sent === size`) survived as an equivalent: the loop's `destroyed` check already refuses a close with chunks to go, so I dropped the counter.
## Gate
Run in a clean worktree at BASE with the candidate applied and node_modules symlinked, 16:49:50Z to 16:54:16Z on 2026-10-13, by `agents/darkwing/work/queue-100/gate.sh` (test-task under DOCKER_HOST=unix:///nonexistent). Every suite exited 0 with no failures:
```
bus rc=0 16:49:50-16:49:54 ℹ pass 127 ℹ fail 0 ℹ skipped 0 0 marks
business rc=0 16:49:54-16:49:55 ℹ pass 67 ℹ fail 0 ℹ skipped 0 0 marks
cli rc=0 16:49:55-16:50:12 ℹ pass 146 ℹ fail 0 ℹ skipped 0 0 marks
control-board rc=0 16:50:12-16:50:13 ℹ pass 124 ℹ fail 0 ℹ skipped 0 0 marks
conversation rc=0 16:50:13-16:51:01 ℹ pass 182 ℹ fail 0 ℹ skipped 0 0 marks
discord rc=0 16:51:01-16:51:03 ℹ pass 178 ℹ fail 0 ℹ skipped 0 0 marks
harness rc=0 16:51:03-16:51:14 ℹ pass 70 ℹ fail 0 ℹ skipped 0 0 marks
mosaic rc=0 16:51:14-16:51:16 ℹ pass 69 ℹ fail 0 ℹ skipped 0 0 marks
queue rc=0 16:51:16-16:51:45 ℹ pass 150 ℹ fail 0 ℹ skipped 0 0 marks
runs rc=0 16:51:45-16:51:45 ℹ pass 43 ℹ fail 0 ℹ skipped 0 0 marks
seat rc=0 16:51:45-16:51:50 ℹ pass 109 ℹ fail 0 ℹ skipped 0 0 marks
tasks rc=0 16:51:50-16:51:56 ℹ pass 90 ℹ fail 0 ℹ skipped 0 0 marks
webui rc=0 16:51:56-16:52:42 ℹ pass 107 ℹ fail 0 ℹ skipped 0 0 marks
test-auth rc=0 16:52:42-16:52:42 selftest: 15 passed, 0 failed
test-conductor rc=0 16:52:42-16:52:47 selftest: 17 passed, 0 failed
test-config rc=0 16:52:47-16:52:48 selftest: 24 passed, 0 failed
test-discord rc=0 16:52:48-16:52:55 discord suite: 66 passed, 0 failed
test-extension-package rc=0 16:52:55-16:52:56 extension package selftest: 18 passed, 0 failed
test-foundation rc=0 16:52:56-16:53:32 selftest: 44 passed, 0 failed
test-queue rc=0 16:53:32-16:54:03 queue suite: 27 passed, 0 failed
test-release rc=0 16:54:03-16:54:14 selftest: 14 passed, 0 failed
test-task rc=0 16:54:14-16:54:16 selftest: 26 passed, 0 failed
ledger rc=0 run separately: pass 78, fail 0
```
packages/ledger has tests but no package.json, so gate.sh prints a MODULE_NOT_FOUND line and "ledger: no test script" for it. I ran `node --test packages/ledger/tests/` on its own; 78 passed. The ledger tests don't touch the row's files.
The cli suite also passed eight runs in a row after the `send()` fix (five before the counter came out, three on the final code).
BASE..origin/refactor (a50c9d23a) touches none of the eight manifest paths. build.patch applied to a fresh BASE checkout reproduces candidate-manifest.sha256.
## Choices
- host-closing comes from the waiting map in close(). The `if (closed)` check after readHeader stays as a guard; with the map it is unreachable in practice.
- body-timeout is a new code for both timeouts. The idle timeout refused invalid-request before. broker.refuse takes any `/^[a-z-]{1,64}$/` code, so there is no schema change. The 30 s idle path has no test of its own; the deadline test uses the same done().
- fetch-aborted isn't logged: a reviewer leaving is not a host fault.
- `have` sorts by committer date, newest first, inside each remote.
## Not owned, for the reviewers
roles/reviewer.md and the harness README and tools.mjs are what a reviewer reads, and row 100 doesn't own them. The nothing-to-fetch text is in packages/cli/README.md. Pointing roles/reviewer.md at it is a follow-up.
## Follow-ups (not in this row)
- packages/seat/src/checkout.mjs git() and the harness runner's git() still use spawn's timeout with SIGKILL.
- The kill goes to the direct git child, not its process group; a hook or helper git spawns may outlive it.
- startHost's host.json single-host check isn't atomic (the README says so).
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More