Compare commits
151
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2da63d5e30 | ||
|
|
a14fdc4436 | ||
|
|
ec86f2c847 | ||
|
|
4ec279dc43 | ||
|
|
3d2569ce25 | ||
|
|
3a20f29d66 | ||
|
|
c5f96836d5 | ||
|
|
99930362e4 | ||
|
|
579720e84a | ||
|
|
c25799928d | ||
|
|
6a0f0f4f80 | ||
|
|
571376c30e | ||
|
|
6002698c32 | ||
|
|
dd6cb5872b | ||
|
|
64ea540838 | ||
|
|
854b783cda | ||
|
|
dbdfcfd52c | ||
|
|
7e59ba8b3d | ||
|
|
3569bf54f5 | ||
|
|
3fd5307e1e | ||
|
|
5283d3745f | ||
|
|
467b05fe2b | ||
|
|
dca5831e7e | ||
|
|
021422e921 | ||
|
|
920b691263 | ||
|
|
8d40ed20a8 | ||
|
|
860afc2b66 | ||
|
|
cd366fc685 | ||
|
|
535b05adbf | ||
|
|
e3fb4e6c90 | ||
|
|
156e5c6040 | ||
|
|
8b1a2b0fed | ||
|
|
833ec1cc8d | ||
|
|
a147042103 | ||
|
|
062599252a | ||
|
|
aabaf97b60 | ||
|
|
e0cb1db2c8 | ||
|
|
0cae2dad55 | ||
|
|
4764bc9eb3 | ||
|
|
712a084124 | ||
|
|
cb3a456989 | ||
|
|
546366f4af | ||
|
|
604523b200 | ||
|
|
094124b4e8 | ||
|
|
5f4e578cf4 | ||
|
|
fe2752954a | ||
|
|
84530f6c5e | ||
|
|
86d8dbc035 | ||
|
|
ae3acc3bda | ||
|
|
40501ab3e1 | ||
|
|
ea4eaf388f | ||
|
|
f9202a7701 | ||
|
|
689a0a3c91 | ||
|
|
45e3ca7bdb | ||
|
|
582b86935a | ||
|
|
fbe57a05b1 | ||
|
|
b64e681edf | ||
|
|
2cc076fec5 | ||
|
|
8e2885d265 | ||
|
|
63b9c64431 | ||
|
|
6b8743c857 | ||
|
|
fc858800bf | ||
|
|
303216ae25 | ||
|
|
ccfb8308bc | ||
|
|
04f3dc4f5f | ||
|
|
4310555874 | ||
|
|
45788f92c1 | ||
|
|
93694c5185 | ||
|
|
8d2856ee3c | ||
|
|
252b29bd8c | ||
|
|
54d9e07bb7 | ||
|
|
de6419f30f | ||
|
|
9ed0f3374a | ||
|
|
efcd3f0fc4 | ||
|
|
b358427677 | ||
|
|
675813594f | ||
|
|
3f1aa76b03 | ||
|
|
bcd1f34c2d | ||
|
|
c87ddc6041 | ||
|
|
ce4040542f | ||
|
|
f2f76fd2c3 | ||
|
|
59226f878f | ||
|
|
f67e5c15c5 | ||
|
|
e2b8733904 | ||
|
|
fd3d2a99fe | ||
|
|
661890e909 | ||
|
|
3046fd60a3 | ||
|
|
b97f9df1ee | ||
|
|
8352da88c5 | ||
|
|
45dca69f6e | ||
|
|
293b42fee9 | ||
|
|
369607e92d | ||
|
|
59f013247c | ||
|
|
e8bb13df84 | ||
|
|
dba2ebdb85 | ||
|
|
45ea3fa0d2 | ||
|
|
f69c962009 | ||
|
|
5b6c878143 | ||
|
|
8c2c5303cf | ||
|
|
23e3d09fa9 | ||
|
|
795a645e65 | ||
|
|
076ca8d6e4 | ||
|
|
68be863248 | ||
|
|
264fad2e26 | ||
|
|
24672fd9d6 | ||
|
|
30e6bb6803 | ||
|
|
7defb58e97 | ||
|
|
9c8877bdf3 | ||
|
|
2d0318a44a | ||
|
|
bd3fa2a484 | ||
|
|
ab575ee553 | ||
|
|
1676ad0de8 | ||
|
|
4874d1e83a | ||
|
|
e909ef51a2 | ||
|
|
4607e15b83 | ||
|
|
8b42010551 | ||
|
|
4863e2caf4 | ||
|
|
7b25a60633 | ||
|
|
e86df37d76 | ||
|
|
fe4ee8c7b5 | ||
|
|
0621c439e0 | ||
|
|
d243f6b25f | ||
|
|
869670d1b1 | ||
|
|
dbbda25373 | ||
|
|
a9da046f83 | ||
|
|
4a71b56abc | ||
|
|
5208894196 | ||
|
|
8dc132bec8 | ||
|
|
08b8831bce | ||
|
|
d339892301 | ||
|
|
0ea3db297b | ||
|
|
f859d06d20 | ||
|
|
70651f45d8 | ||
|
|
490f45f024 | ||
|
|
1e629c892d | ||
|
|
84de3fbca3 | ||
|
|
1e4c1e6e94 | ||
|
|
5b846b048a | ||
|
|
59f0e44f47 | ||
|
|
bb031ec840 | ||
|
|
07315e6514 | ||
|
|
fb04cbc431 | ||
|
|
9d929911c8 | ||
|
|
bcf3c1dacf | ||
|
|
d52d9f1f2f | ||
|
|
830f3d8bff | ||
|
|
e48d602165 | ||
|
|
cabcb81d49 | ||
|
|
353d65f577 | ||
|
|
2c289f6f0d | ||
|
|
a863015a51 |
No files matched your search
@@ -5389,3 +5389,81 @@ Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault r
|
||||
|
||||
|
||||
2026-10-23T11:03:46Z | Gyro Vikunja571 mos-1444 PRD d8 follow-on | Branch gyro/editor-571-d8 from origin/refactor c16e943b9. Both designs now keep library.convert outside ACTIONS/ROUTED and broker processing, retain REQ-WAIT-1/hard-wait test unchanged, constrain unmarked story eligibility to mapped book/playground scope, and add whole-host pre-import restore recovery from remote import manifests plus the Gitea archive commit. Lookups refuse until one current version is proven; VM1012 restore needs Jason present under Q185. W7 no longer owns hard-waits.test.mjs. Canonical queue next gyro returned nothing; direct Mos assignment governs. Static diff, exact replacement and append-only checks pass. No code, tests, build, browser, private-file or live action. Fenton re-check requested after authorized branch push.
|
||||
|
||||
2026-10-22 | Huey | Row 272 / 264e (#1777) preparation on huey/row272, base 2d0d407ba. Before: bus session metadata and launcher isolation exist, CLI/Console expose only the saved thread default. Draft: session CLI, Console chooser with isolated drafts/cursors/receipts, metadata counts and safe-clear operation receipts against Gizmoduck's 0260 contracts; narrow deferred B6 reply fence. Tests authored, not run: Gander row 253 holds Sage's slot; no Node, claim, HTTPS git, push or live changes. This entry records preparation after the first draft, not a green acceptance result.
|
||||
|
||||
2026-10-22 | Huey | Row 272 draft, 264d additive contract gizmoduck-0272 (78e14352f). Added confirmed cancellation through the human channel, cancelled receipt retirement distinction, named refusals and routed-message cancellation output. Before: cancelled was not recognized as terminal; after: draft CLI stops polling and Console shows the before/after retirement outcome. Acceptance remains unverified: no Node/tests/claim/commit/push under Sage's gate. No runtime/lifecycle authority expanded.
|
||||
|
||||
2026-10-23 | Huey | Row 272 / 264e sage-3141 local rebase. Before: uncommitted draft on 2d0d407ba. After: same draft on new huey/row272-b23783f39 at b23783f39, with 270/271 landed (7328da8ca) and schema 3n. Product changes auto-merged; both append-only logs retain the full new base and original Huey additions. Checked landed session.list and clear/cancel/operation envelopes against the wired draft: no incompatible contract change. Historical unavailable-dependency cases stay as stand-ins; actual base supplies counters and lifecycle state. No tests/Node/claim/commit/push while Gander holds row 574 run 2. Existing-test fixture/assertion delta is named in the packet for Mos's ruling via Sage before execution.
|
||||
|
||||
2026-10-23 | Huey | Row 272 sage-3145 preservation correction. refs/stash is shared, so it was unsafe as sole draft storage. Pinned exact stash commit 07dc9d2f7944b4781b5e80dfa18425f4749f7a45 to local huey/row272-stash-keep and left the entry untouched. Sage explicitly directed committing the rebased draft before testing to preserve it: an untested local checkpoint, no acceptance claim. No Node/test/queue/network/live action. Separate claims and test CLEARs remain pending; old-test delta is with Mos via sage-3144.
|
||||
|
||||
2026-10-23 | Huey | Row 272 early static review follow-up, scrooge-272-early-read-huey-1. Before: a bad-session 404 erased sibling draft/recovery caches, malformed clear/cancel write receipts omitted the audit line, and another session's op key produced an ambiguous error. Draft after: purge only on named thread-level refusals, write one redacted malformed-receipt audit line, and identify key reuse without a write. New browser/HTTP/CLI tests authored, unrun under test hold; approved old-test hunks unchanged. Published 850b22680/round-1 manifest unchanged; follow-up is uncommitted, no acceptance claim.
|
||||
|
||||
2026-10-23 | Huey | Row 272 round 2 on row 219 landing 5fdfd7275, after 295. Replayed round 1 and B1/N1/N2; seven conflicts resolved retaining both log histories, both test sets, 219 instance chooser and 295 owner-settings-only transport. Static comparison confirms old tests differ only by mos-1388 approved hunks. Sage3357/Mos1442 authorize bounded granted-user legacy fallback: only session.list access-refused and omitted-session route; no new session grants or owner routing. RED first under3358 on new file only: 1 expected legacy assertion failure, 3 guards pass, 3.504s, TMPDIR mktemp/no underscore, no compose network; slot returned as huey0035. Then implemented fallback and added timeout/bus/malformed guards plus isolated-copy mutant runner, all unrun afterward. Candidate commit/push precedes Sage's full gate by3354; no green claim.
|
||||
|
||||
2026-10-23 | Huey | Row 272 round 3 per mos-1455 / sage-3435. Gate run 1 on b44332a93 RED: bus 891/0, business 231/0, CLI 940/1, plugins 110/1, webui 372/20; no mutants. Fixed only new CLI fixture role authority and three null-old render guards. Catching browser tests unchanged. manifest.r3 freezes the same 17 paths. Authorized pre-gate follow-up commit/push; no local tests, Node or queue writes; separate compactions dependency and Sage gate remain pending.
|
||||
|
||||
2026-10-23 | Sage (1ef1e4f8) | Row 272 landing (#1777, 264e) | Gate r4 on bea0c8e1c over 1e629c892: bus 891/0, cli 941/0, webui 392/0, business 231/0; plugins 110/1, the single red being row 65 harness.test.mjs:192 naming compactions.mjs, which row 307 owned and fixed. M1-M6 and F1-F4 killed, restore clean. Landed d33989230, queue commit 8dc132bec.
|
||||
|
||||
2026-10-23 | Sage (1ef1e4f8) | Row 307 landing (#1814, Vikunja 598) | Six first-parent commits replayed onto 8dc132bec with matching patch-ids, 16 paths equal to 6fdfe3790, manifest digest 17c9f8d9 rebuilt from git. Gate green 14:37:21Z: bus 891/0, cli 941/0, plugins 119/0, webui 392/0, business 231/0, every other package 0 failed 0 skipped, every scripts/test-*.sh passed (skips: test-queue canonical root, test-task live provider x2). M1-M4 killed, restore clean. Landed d243f6b25, DEFERRED 307 N1 0621c439e, queue commit fe4ee8c7b. Correction: my first note said Vikunja 598 done when I had only commented (951); Mos closed it (952).
|
||||
|
||||
2026-10-23T17:29:46Z | Gyro Vikunja571 sage-3542 Connections amendment | Branch gyro/editor-connections-design cut exactly from landed PRD1.17 30e6bb680; read REQ-LIB-5 in that PRD. Replaced stale J-b waits in parent and W4. Proposed W8 owns current authorized Markdown/DOCX links/backlinks list, labels, versioned refresh, leakage refusals and fixture tests. Build order under mos-1470/comment961 is W7 closed routing candidate, W8 landing, then W7 human first-real-import opening. No REQ-LIB-8 condition or acceptance change. Q234/mos-1363/S0 M1-M14 and post-10-27/MVP priority fixed. Static diff, twelve brief sections and append-only checks pass. No repo Node, tests, build, browser, queue mutation or live action. Fenton reviews after authorized docs branch push; Sage lands on approval.
|
||||
|
||||
2026-10-23T17:32:37Z | Gyro sage-3544 fenton-0016 N1/N2 delta | Before: W7 Gate/final real-conversion list omit the W8 build-order cross-reference and W8 human-view filtering cites agent settings. After: both locations explicitly put W8 landing before human opening as mos-1470 build order, not acceptance; comment/tracked-deletion links are not current story links and never appear in the human Connections view regardless of agent settings. New commit atop approved 5af060fb5, no amend. Static diff and append-only checks pass. Docs only, no repo Node/tests/live action. Fenton checks only this delta.
|
||||
|
||||
2026-10-23 | Gladstone | Gitea #1816 / Vikunja 614 hostcheck item 5 and bundle verification | Before: dpkg's correct 750 form falsely fails item 5, and two run-sheet bundle verifies require a missing repository. Draft after: exact root/mosaic/path with octal 0750 equivalence; stat/output unchanged; both verifies use cleaned-up bare scratch repos. Three additive-only test blocks, two planned mutants. Untested under Sage-3738; Sage-3740 authorizes untested branch push for static review. Queue row pending; Sage owns gate and landing.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 scope amendment mos-1534, sage-3741 | F1 narrowed to exact 750/0750 and single spaces; F3 allows @ in absolute unit paths with unsafe-character refusals retained; F4 moves copy_tree to / after path resolution. AS_USER audit found git -C and stack-release's existing checkout cd cover the other steps. Added new unit and unreadable-cwd copy tests only. Untested under Sage's no-Node hold; no live action.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 F5 draft (mos-1536) | Before: the run sheet omits the explicit notify decision and precheck misses a bus-start refusal. After: section 9a creates a private null-binding decision as mosaic; precheck directly reuses readNotifyConfig and private_dir, and new tests cover accepted and refused decisions. Existing fixture setup additions await Mos approval through Sage; installer unchanged. Untested under no-Node hold.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 fixture ruling mos-1537 | Added only three setup lines each to host(t)/world(t) for the explicit null notifier decision. Existing assertions/fakes/lines intact. New install test checks decision bytes/mode preserved and missing decision remains absent. F5 missing-file fault removes the setup file. Untested under Sage's hold; separate follow-up atop 33950cf9b.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 F6 and notify parent (mos-1537) | Before: the run sheet's copy check lacks its required CLI wrapper; mkdir -p -m does not guarantee the notify parent mode. After: a separately Mos-gated wrapper --create step follows install and precedes check, as mosaic from cwd /; section 9a explicitly creates both notify directories at 0700. Docs only, no live action; Node/tests still held.
|
||||
|
||||
2026-10-23 | Gladstone | Row320 / #1816 first review CHANGES (morgana-0217) | Reviewer run: hostcheck19/19, unit47/47, install24/24, copy17/18; seven of eight mutants killed, M4 survived because both base/mutant failed only cleanup. Fixed this row's F4 test to enter cwd then chmod000, assert no read/search, pass an absolute outside bundle, and restore0700 in finally. Existing tests/setup and production scripts unchanged. Correction untested locally pending Morgana's focused rerun; no acceptance claim.
|
||||
|
||||
2026-10-23 | Boyd | Row 298 (#1804, Q228), before implementation completion | Took over Megavolt 7a141269c on boyd/row298 at 45dca69f6 under mos-1496/1502. Approved D1-D6 govern human-only grants.scope.set, empty fallback, in-scope close/evidence and spend recheck. All inherited code UNTESTED; no Node/tests until Sage CLEAR. Plan and static gaps recorded in agents/boyd/work/queue-298/BUILD.md; no live grant or credential access.
|
||||
|
||||
2026-10-23 | Boyd | Row 298 (#1804, Q228), targeted verification | On sage-3642 T1 lane B, five whole files serial: business scope 7/0, bus scope 14/0, scope runtime 1/0, CLI scope 4/0, project runtime 3/0 (29/0, no skips). Initial bus fixture typo recorded and corrected. M1-M10 killed by named assertions, source restored and final files green. Dewey dewey-0933 positive control and notes addressed; manifest and evidence in agents/boyd/work/queue-298/BUILD.md. Full package/script gate remains Sage's. No live scope set.
|
||||
|
||||
2026-10-23T22:41:07Z | Boyd | Row 298 (#1804), rebase after row 239 | Rebased onto 833ec1cc8d7c11842efb82e57c1a0b256c8d22d7 on boyd/row298-r2 without altering published boyd/row298. Only append-log conflicts; upstream bytes preserved and Boyd suffixes appended. Product merges automatic; row 239 writer-lock revoke and post-revoke error handling preserved, organization.mjs unchanged from new base. Static checks only; UNTESTED on new base pending Sage CLEAR. Details in Boyd BUILD.md.
|
||||
|
||||
2026-10-23 | Goldie | Row 322 / #1818 F8/F9 before | The library installer runs the clone importer, whose yaml dependency is absent on fresh clones, and tail -n 1 hides uncaught errors behind Node's version banner. Plan: run the same-commit copy importer, include it in COPY_PATHS, keep the first error line with last-line fallback, append isolated regression coverage. Mos-1550 approves setup-only world(t) additions and the one-path copy-file pin addition. No tests run yet; Sage slot required.
|
||||
|
||||
2026-10-23 | Goldie | Row 322 / #1818 F8/F9 after | Importer now runs from the same-commit dependency-equipped copy; COPY_PATHS includes it. One-line refusal selects an actual Error/ERR_ line, avoiding Node's source excerpt/banner, with last-line fallback. Mos-1550-approved fixture setup and one-file pin changes; two append-only regression tests. Under sage-3808 CLEAR, serialized timeout -k 10 runs in /mnt/storage/scratch/tmp/goldieuQ2664: install26/26, copy18/18, pinned-copy10/10; new tests2/2 first run and restored; N installer expected-red2/2, F8 and F9 mutants killed, installer SHA256 restored each time. No unexpected red. Evidence and exact old-test changes in agents/goldie/work/queue-322/BUILD.md; full gate and Morgana review belong to Sage.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 before (Q247, mos-1555) | New branch goldie/row324 at row298 approved860afc2b6. Design/readers audit in agents/goldie/work/queue-324/BUILD.md: normalize omitted tracker to null, preserve present validation, guard sync readers/adapter config; runtime/instance-settings/broker checks already optional. Existing business.test.mjs98 explicitly requires tracker, so implementation held for Mos approval of removing that one list entry; no old test or product edit, no tests run.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 draft built (sage-3836, mos-1556) | Claim rev3759. Omitted tracker normalizes null; present including null/malformed stays validated. Loader, business validate, bootConfig trackerFor and Console sync reader handle absence. Row298's guarded runtime/instance-settings/broker paths preserved. Two new test files (9 planned tests); sole old-test edit drops tracker from required-key list under mos-1556. node --check6/6 and diff check pass. No tests run: untested branch push expressly ordered before CLEAR; Duckworth review and Sage gate pending.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 rebased verification (sage-3850) | Rebased onto3569bf54f as7e59ba8b3; only append-log conflicts, eight manifest files byte-equal. Serialized timeout-k10 scratch TMPDIR goldieOdgIjr: new business5/5, new CLI4/4 (includes Console absent read), existing business21/21; presence mutant killed, bytes restored, new files rerun5/5 and4/4. No unexpected red. Published rebased branch goldie/row324-r2 preserves original remote draft without force-push. Evidence/mutant diff/reader list in BUILD.md; review/full gate pending.
|
||||
|
||||
2026-10-23 | Goldie | Row 323 / #1819 before | Case 1 lacks a committed business file. Brief requires actual .53 values and refuses guessing. New branch goldie/row323 from local refactor da537e7d1; source review shows project root, literal human id, tracker sync identity/reference metadata and launch model values absent from DESIGN, plus nonempty launch.instances requires another role. Asked Sage (goldie-0008); no config or test written, no test run. Details agents/goldie/work/queue-323/BUILD.md.
|
||||
|
||||
2026-10-23 | Goldie | Row 323 / #1819 static blocker (sage-3823) | Mos-1554 requires no bot and an absent, never-created tracker token path. Static tracing confirms this refuses install validation (business cli84-97, credentials78-83) and bus boot (runtime131/157, bus credentials225-228), even without tracker.baseUrl; unset env also refuses at bus credentials278. Reported Sage goldie-0009; no JSON/test/credential written, no tests. Accepted source values/role and refusal-test rulings are recorded in BUILD.md; waiting for product-boundary resolution.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 resumed draft (sage-3859) | Rebasing prior notes onto Q247 landing dd6cb5872 resolved only append logs. Added trackerless .53 factory JSON: jason, approved project root, chief+assistant for required nonempty launch.instances, claude-code/Opus5.5/high, tester on. No credentials or token placeholder. Seven new tests prepare exact-doc loading plus scratch real validate/install/boot and three refusal paths; one restoring tester-switch mutant prepared. No old test edits; no tests before Sage CLEAR. Source choices/rulings in BUILD.md.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 pre-test commit (sage-3861 CLEAR) | Sage explicitly requests commit before runs; JSON/new7-case test/restoring mutant and field provenance committed untested before slot work. Exact sources distinguish mos-1554 human/no-bot intent, sage-3820 roles, sage-3822 project/model, and Q247 tracker omission. No old test edits. Next runs: new file, business whole, CLI whole, mutant/restored file, serialized scratch TMPDIR timeout-k10.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 first slot STOP (sage-3861) | On committed571376c30: new file7/7 and business261/261 green; whole CLI hit unchanged revoke-window.test.mjs170 (test579, row306 R4), no RESULT after20.1s at ask167/Promise.all194. Stopped suite on observing red, cleaned only positively identified own scratch fixture. Partial631 pass/1 fail before termination, not full-suite green. No mutant/restored run, no old-test edit/rerun. Manifest unchanged; Sage notified goldie-0016 and slot returned. Evidence local under queue-323, awaits ruling.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 second red STOP (mos-1560, sage-3864) | Isolated revoke-window whole-file run1 on unchanged571376c30 failed0/1 after20.126s, same no RESULT at ask167/test194. Start00:24:22Z, load1.46/1.68/2.70, busyhelperCLEAR, fresh scratch goldieBObDQ9. No run2/3/CLI retry/mutant/restored run; no source/old-test edit. Sage goldie-0018 has log/load/error; slot returned, awaits base-dd6cb5872 root cause. Remote remains unverified571.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 accepted evidence (mos-1561, sage-3869) | Sage traced revoke-window reds to37-byte TMPDIR making109-byte tools.sock >107 limit (session.mjs278), not Codex. Fresh31-byte /scratch/tmp/XXXXXX confirmation passes1/1 in0.84s. On unchanged571376c30 serialized timeout-k10: business261/261, new7/7, tester-switch mutant killed/restored SHA85e6b9ac, restored new7/7. Every log starts UTC/load/helperCLEAR; all positive runs0 fail/skip/cancel. CLI whole acceptance comes from Sage gate under mos-1561; no local CLI rerun. Evidence-only notes/log commit follows, no old test/source changes.
|
||||
|
||||
2026-10-24 | Goldie | Row325 / #1821 before (mos-1563/1564) | Real .mosaic/project.json idstack disagrees with Case1 project's mosaic-stack key; row323 scratch fixture omitted project file and only warned. New goldie/row325 at99930362e changes only that JSON key and expressly authorized old assertion key; new real-byte project-copy validation tests require valid and reject old key. Mutant prepared; no tests before Sage CLEAR, no queue writes/host actions.
|
||||
|
||||
2026-10-24 | Goldie | Row325 / #1821 after (sage-3882 CLEAR) | Only Case1 projects key becomesstack; only old test70 expected key updated under mos-1564. New3-case test copies real .mosaic/project.json bytes, requires CLI project valid, rejects old key. Serialized shortTMP timeout-k10: new3/3, business264/264, old-key mutant killed/restored SHA9e43bfef, restored new3/3; all positive0 fail/skip/cancel. Manifest4/4 verifies including unchanged project document. Logs/load lines and mutant diff in BUILD.md; no queue writes/host action, Gladstone review/Sage gate pending.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 before (sage-3906, Q246, mos-1566) | Recovered seat/brief/design at ec86f2c84 on bentina/row157. Canonical queue next flags stale brief pin. Outbound audit finds broker gitea-grant node:https and web DNS-pinned node:https paths, triggering I2 explicit stop rule. Sage notified bentina-0001; BUILD.md records shifted cited sites. No implementation/test/Docker/credential/live-state action; awaiting lead re-pin and transport ruling.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 first slot STOP (sage-3910) | New config/helper31/31 and proxy11/11 green. test-config exit1:55 pass/1 old output regression; new install20/20 green. Validator added absent install key to JSON. Stopped before Docker/install suite; slot returned Sage bentina-0004. sage-3916 authorizes preserving absent output shape/defaulting env to local: fixed own loader/new test only, no rerun before next CLEAR. test-config diff +4/-0. No commit/push; seat BUILD and01/02/03 logs.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 after (sage-3919, sage-3923) | Absent install JSON shape preserved; helper defaults local. Existing config56/56, helper/config31/31, final proxy12/12, final test-install exit0 with three release0.0.12 rootless builds/pins, internal TLS/deny/DNS checks and real Vikunja publicGET200/notifier REST dummyGET401 through services proxy. Upstream-reset correction verified. Slot returned bentina-0007; no broader suites by Sage direction. Boyd then Darkwing review, Mos provider-host measurement and Sage landing gate pending. Evidence in seat BUILD and04-08 logs; only bentina/row157 candidate push authorized.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 round2 before (boyd-0023, sage-3933) | Read reproduced B1 half-open refusal socket retention and B2 same-PID exec inherited cache bypass. Added two new regression files; no existing assertion edits. Proxy drains then destroys refused sockets with1s fallback; helper clears inherited cache/export attributes at source. No tests before CLEAR; Goldie holds slot. Source/causes recorded in seat BUILD.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 round2 after (sage-3936) | B1 refused sockets now drain/destroy with1s bound; B2 source clears inherited cache/export attributes. Archived a14+new regressions red as intended (5controls pass/7defects fail), fixed12/12, helper/config33/33, egress22/22, rootless install exit0 incl image pins/network/services dummyGET. Logs09-13 in seat work. No existing test/assertion edit for round2. Slot returned bentina-0014; followup own-branch commit/manifest for Boyd re-review; Mos and Sage landing gates pending.
|
||||
@@ -76,6 +76,16 @@ Rules enforced by `scripts/mosaic-config.mjs`:
|
||||
|
||||
Run paths (`build/hello/verify/reset`) fail closed when configuration is missing or invalid; they never invent it.
|
||||
|
||||
The optional `install` key selects the containerized install explicitly:
|
||||
`"install": {"mode":"containerized","endpoint":"unix:///run/user/1000/docker.sock"}`.
|
||||
An absent key defaults to local mode and remains absent in validation output.
|
||||
Local mode forbids `endpoint`;
|
||||
containerized mode requires a Unix socket endpoint without whitespace
|
||||
(the actual pattern is `^unix:///[^\s]+\.sock$`). Unknown install keys or
|
||||
modes refuse with exit 2 without changing the config. Validation contacts no
|
||||
daemon; the endpoint helper checks rootless status before engine use. Keep
|
||||
existing live configs unchanged until the install rollout is authorized.
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -47,6 +47,14 @@ 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.
|
||||
|
||||
Any run that includes the webui suite, whether a seat's slot run or a landing
|
||||
gate, sets `MOSAIC_REQUIRE_EDITOR_BUILD=1`, so a missing editor toolchain fails
|
||||
the run instead of skipping the rebuild test. A worktree that links the
|
||||
canonical `node_modules` also runs `npm ci --ignore-scripts --no-audit --no-fund`
|
||||
in its own `packages/webui` first. The root install carries esbuild 0.28.1
|
||||
through pi-coding-agent, and the rebuild needs the 0.28.2 that
|
||||
`packages/webui/package-lock.json` pins (Vikunja 597, mos-1453).
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Beakley
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 217 (C2-06, handed over from Launchpad).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 88b97f65) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `beakley/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,10 @@
|
||||
# Bentina
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Sage under Q246 and mos-1566. Owns row 157, I2: the install images, the
|
||||
install config key, the endpoint helper and the egress proxy (docs/plans/2026-10-18_containerized-install-rows.md),
|
||||
reassigned from Researcher so the coding runs on Codex.
|
||||
|
||||
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; it pushes only its own `bentina/*` branches through the repository credential helper (mos-0981), never `refactor`,
|
||||
`main` or another seat's branch. Claude seats orchestrate and review the install path (mos-1542 rule 4). Registered by
|
||||
Sage on 2026-10-24 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,43 @@
|
||||
2026-10-24T04:05:53Z
|
||||
23:05:53 up 10 days, 14:25, 2 users, load average: 2.29, 2.31, 1.89
|
||||
CLEAR sage-3910 TMPDIR=/mnt/storage/scratch/tmp/lEvgx1
|
||||
✔ install: absent (28.959485ms)
|
||||
✔ install: local (24.660305ms)
|
||||
✔ install: local endpoint (26.210523ms)
|
||||
✔ install: containerized missing endpoint (25.495263ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (28.62092ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (32.126399ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (26.69995ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (33.36326ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (30.579866ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (40.844622ms)
|
||||
✔ install: bad endpoint "unix:///a" (36.371215ms)
|
||||
✔ install: bad endpoint "" (31.603044ms)
|
||||
✔ install: bad endpoint null (29.645166ms)
|
||||
✔ install: bad endpoint 4 (30.638453ms)
|
||||
✔ install: valid endpoint (28.088952ms)
|
||||
✔ install: unknown mode (28.182929ms)
|
||||
✔ install: missing mode (26.297287ms)
|
||||
✔ install: unknown key (27.205488ms)
|
||||
✔ install: null (29.189475ms)
|
||||
✔ install: array (30.347371ms)
|
||||
✔ helper refuses rootful (76.082505ms)
|
||||
✔ helper refuses missing (58.354463ms)
|
||||
✔ helper refuses substring (86.830586ms)
|
||||
✔ helper refuses object (108.0825ms)
|
||||
✔ helper refuses invalid JSON (85.203357ms)
|
||||
✔ helper refuses null success (85.225204ms)
|
||||
✔ helper refuses rootless output but failed (67.840402ms)
|
||||
✔ helper checks once and explicitly names host for both calls (119.763305ms)
|
||||
✔ local mode refuses before Docker null (35.42747ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (30.531767ms)
|
||||
✔ invalid config refuses before Docker (30.092363ms)
|
||||
ℹ tests 31
|
||||
ℹ suites 0
|
||||
ℹ pass 31
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 833.790665
|
||||
EXIT=0
|
||||
@@ -0,0 +1,23 @@
|
||||
2026-10-24T04:06:01Z
|
||||
23:06:01 up 10 days, 14:25, 2 users, load average: 2.27, 2.30, 1.89
|
||||
CLEAR sage-3910 TMPDIR=/mnt/storage/scratch/tmp/lfgV9z
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (21.273322ms)
|
||||
✔ unlisted host (1.324735ms)
|
||||
✔ unlisted port (1.081626ms)
|
||||
✔ GET (0.948442ms)
|
||||
✔ invalid port (0.95243ms)
|
||||
✔ userinfo (0.874803ms)
|
||||
✔ bad header (0.782898ms)
|
||||
✔ slow client has a total header deadline (81.829907ms)
|
||||
✔ oversized header refuses (1.267008ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.491274ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.716299ms)
|
||||
ℹ tests 11
|
||||
ℹ suites 0
|
||||
ℹ pass 11
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 156.342618
|
||||
EXIT=0
|
||||
@@ -0,0 +1,90 @@
|
||||
2026-10-24T04:06:08Z
|
||||
23:06:08 up 10 days, 14:25, 2 users, load average: 2.53, 2.36, 1.91
|
||||
CLEAR sage-3910 TMPDIR=/mnt/storage/scratch/tmp/6eJjJz
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK valid console.sso loads and is in the output
|
||||
OK console.sso HS256 alg exits 2 naming the path (exit 2)
|
||||
OK console.sso alg none exits 2 naming the path (exit 2)
|
||||
OK console.sso missing slot field exits 2 naming the path (exit 2)
|
||||
OK console.sso slot name outside the pattern exits 2 naming the path (exit 2)
|
||||
OK unknown key under console exits 2 naming the path (exit 2)
|
||||
OK valid federation key loads and is in the output
|
||||
OK federation unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer origin over http exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a path exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with userinfo exits 2 naming the key (exit 2)
|
||||
OK federation peer id equal to self exits 2 naming the key (exit 2)
|
||||
OK federation peer label empty exits 2 naming the key (exit 2)
|
||||
OK federation not an object exits 2 naming the key (exit 2)
|
||||
OK federation self pattern exits 2 naming the key (exit 2)
|
||||
OK federation peers not an array exits 2 naming the key (exit 2)
|
||||
OK federation peer not an object exits 2 naming the key (exit 2)
|
||||
OK federation peer id pattern exits 2 naming the key (exit 2)
|
||||
OK federation peer label over 80 characters exits 2 naming the key (exit 2)
|
||||
OK federation duplicate peer id exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a trailing slash exits 2 naming the key (exit 2)
|
||||
OK federation self missing exits 2 naming the key (exit 2)
|
||||
OK federation peer id missing exits 2 naming the key (exit 2)
|
||||
OK federation peer label missing exits 2 naming the key (exit 2)
|
||||
OK config without federation loads and has no federation output
|
||||
FAIL config without console loads with unchanged output
|
||||
OK compactionReadBytes 8388608 loads and is in the output as written
|
||||
OK compactionReadBytes "x" loads and is in the output as written
|
||||
OK compactionStreams 2048 loads and is in the output as written
|
||||
OK compactionStreams "x" loads and is in the output as written
|
||||
OK failed validation modified nothing
|
||||
|
||||
✔ install: absent (28.022711ms)
|
||||
✔ install: local (26.314077ms)
|
||||
✔ install: local endpoint (27.030768ms)
|
||||
✔ install: containerized missing endpoint (26.036164ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (29.311242ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (28.012086ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (26.941261ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (26.90999ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (23.646272ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (26.383344ms)
|
||||
✔ install: bad endpoint "unix:///a" (24.914913ms)
|
||||
✔ install: bad endpoint "" (24.320663ms)
|
||||
✔ install: bad endpoint null (25.688262ms)
|
||||
✔ install: bad endpoint 4 (26.934437ms)
|
||||
✔ install: valid endpoint (27.504294ms)
|
||||
✔ install: unknown mode (27.111833ms)
|
||||
✔ install: missing mode (26.365767ms)
|
||||
✔ install: unknown key (26.974983ms)
|
||||
✔ install: null (25.910901ms)
|
||||
✔ install: array (25.507423ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 584.03015
|
||||
OK install configuration cases
|
||||
selftest: 55 passed, 1 failed
|
||||
EXIT=1
|
||||
@@ -0,0 +1,43 @@
|
||||
2026-10-24T04:08:52Z
|
||||
23:08:52 up 10 days, 14:28, 2 users, load average: 2.16, 2.49, 2.04
|
||||
CLEAR sage-3919 TMPDIR=/mnt/storage/scratch/tmp/o2n7Db
|
||||
✔ install: absent (31.8592ms)
|
||||
✔ install: local (29.807282ms)
|
||||
✔ install: local endpoint (24.009896ms)
|
||||
✔ install: containerized missing endpoint (26.296453ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (26.404525ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (27.486365ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (24.280876ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (24.363468ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (24.920898ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (25.784031ms)
|
||||
✔ install: bad endpoint "unix:///a" (27.89803ms)
|
||||
✔ install: bad endpoint "" (28.939674ms)
|
||||
✔ install: bad endpoint null (26.129602ms)
|
||||
✔ install: bad endpoint 4 (27.601233ms)
|
||||
✔ install: valid endpoint (29.310223ms)
|
||||
✔ install: unknown mode (30.307014ms)
|
||||
✔ install: missing mode (26.432375ms)
|
||||
✔ install: unknown key (29.208115ms)
|
||||
✔ install: null (29.793ms)
|
||||
✔ install: array (28.956899ms)
|
||||
✔ helper refuses rootful (88.440017ms)
|
||||
✔ helper refuses missing (49.868469ms)
|
||||
✔ helper refuses substring (76.03165ms)
|
||||
✔ helper refuses object (77.494513ms)
|
||||
✔ helper refuses invalid JSON (78.525159ms)
|
||||
✔ helper refuses null success (79.166999ms)
|
||||
✔ helper refuses rootless output but failed (52.195589ms)
|
||||
✔ helper checks once and explicitly names host for both calls (124.380922ms)
|
||||
✔ local mode refuses before Docker null (28.199092ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (29.618143ms)
|
||||
✔ invalid config refuses before Docker (27.543884ms)
|
||||
ℹ tests 31
|
||||
ℹ suites 0
|
||||
ℹ pass 31
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 762.528877
|
||||
EXIT=0
|
||||
@@ -0,0 +1,90 @@
|
||||
2026-10-24T04:09:01Z
|
||||
23:09:01 up 10 days, 14:28, 2 users, load average: 2.28, 2.50, 2.05
|
||||
CLEAR sage-3919 TMPDIR=/mnt/storage/scratch/tmp/hYypHU
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK valid console.sso loads and is in the output
|
||||
OK console.sso HS256 alg exits 2 naming the path (exit 2)
|
||||
OK console.sso alg none exits 2 naming the path (exit 2)
|
||||
OK console.sso missing slot field exits 2 naming the path (exit 2)
|
||||
OK console.sso slot name outside the pattern exits 2 naming the path (exit 2)
|
||||
OK unknown key under console exits 2 naming the path (exit 2)
|
||||
OK valid federation key loads and is in the output
|
||||
OK federation unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer origin over http exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a path exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with userinfo exits 2 naming the key (exit 2)
|
||||
OK federation peer id equal to self exits 2 naming the key (exit 2)
|
||||
OK federation peer label empty exits 2 naming the key (exit 2)
|
||||
OK federation not an object exits 2 naming the key (exit 2)
|
||||
OK federation self pattern exits 2 naming the key (exit 2)
|
||||
OK federation peers not an array exits 2 naming the key (exit 2)
|
||||
OK federation peer not an object exits 2 naming the key (exit 2)
|
||||
OK federation peer id pattern exits 2 naming the key (exit 2)
|
||||
OK federation peer label over 80 characters exits 2 naming the key (exit 2)
|
||||
OK federation duplicate peer id exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a trailing slash exits 2 naming the key (exit 2)
|
||||
OK federation self missing exits 2 naming the key (exit 2)
|
||||
OK federation peer id missing exits 2 naming the key (exit 2)
|
||||
OK federation peer label missing exits 2 naming the key (exit 2)
|
||||
OK config without federation loads and has no federation output
|
||||
OK config without console loads with unchanged output
|
||||
OK compactionReadBytes 8388608 loads and is in the output as written
|
||||
OK compactionReadBytes "x" loads and is in the output as written
|
||||
OK compactionStreams 2048 loads and is in the output as written
|
||||
OK compactionStreams "x" loads and is in the output as written
|
||||
OK failed validation modified nothing
|
||||
|
||||
✔ install: absent (33.602997ms)
|
||||
✔ install: local (27.76628ms)
|
||||
✔ install: local endpoint (28.360207ms)
|
||||
✔ install: containerized missing endpoint (26.872508ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (27.729466ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (29.663768ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (28.875798ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (27.427064ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (30.77649ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (34.03786ms)
|
||||
✔ install: bad endpoint "unix:///a" (31.459666ms)
|
||||
✔ install: bad endpoint "" (26.221924ms)
|
||||
✔ install: bad endpoint null (31.982463ms)
|
||||
✔ install: bad endpoint 4 (34.408275ms)
|
||||
✔ install: valid endpoint (30.184118ms)
|
||||
✔ install: unknown mode (26.58596ms)
|
||||
✔ install: missing mode (26.563043ms)
|
||||
✔ install: unknown key (28.663535ms)
|
||||
✔ install: null (26.567284ms)
|
||||
✔ install: array (24.756494ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 640.201326
|
||||
OK install configuration cases
|
||||
selftest: 56 passed, 0 failed
|
||||
EXIT=0
|
||||
@@ -0,0 +1,245 @@
|
||||
2026-10-24T04:09:14Z
|
||||
23:09:14 up 10 days, 14:29, 2 users, load average: 2.58, 2.56, 2.08
|
||||
CLEAR sage-3919 TMPDIR=/mnt/storage/scratch/tmp/7xUNh5
|
||||
✔ install: absent (28.837858ms)
|
||||
✔ install: local (28.328967ms)
|
||||
✔ install: local endpoint (26.178892ms)
|
||||
✔ install: containerized missing endpoint (25.578947ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (24.988885ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (25.850327ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (26.499708ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (42.276491ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (32.161423ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (26.834457ms)
|
||||
✔ install: bad endpoint "unix:///a" (30.552354ms)
|
||||
✔ install: bad endpoint "" (28.938768ms)
|
||||
✔ install: bad endpoint null (27.00598ms)
|
||||
✔ install: bad endpoint 4 (29.63046ms)
|
||||
✔ install: valid endpoint (28.707851ms)
|
||||
✔ install: unknown mode (25.507911ms)
|
||||
✔ install: missing mode (27.101142ms)
|
||||
✔ install: unknown key (32.316453ms)
|
||||
✔ install: null (27.273355ms)
|
||||
✔ install: array (25.503395ms)
|
||||
✔ helper refuses rootful (81.447175ms)
|
||||
✔ helper refuses missing (49.32616ms)
|
||||
✔ helper refuses substring (80.299191ms)
|
||||
✔ helper refuses object (96.465808ms)
|
||||
✔ helper refuses invalid JSON (89.854499ms)
|
||||
✔ helper refuses null success (79.934412ms)
|
||||
✔ helper refuses rootless output but failed (61.053445ms)
|
||||
✔ helper checks once and explicitly names host for both calls (120.469053ms)
|
||||
✔ local mode refuses before Docker null (28.252291ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (32.631557ms)
|
||||
✔ invalid config refuses before Docker (28.529643ms)
|
||||
ℹ tests 31
|
||||
ℹ suites 0
|
||||
ℹ pass 31
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 798.423992
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (18.932222ms)
|
||||
✔ unlisted host (1.309946ms)
|
||||
✔ unlisted port (1.067401ms)
|
||||
✔ GET (0.988233ms)
|
||||
✔ invalid port (0.919738ms)
|
||||
✔ userinfo (0.832286ms)
|
||||
✔ bad header (0.761085ms)
|
||||
✔ slow client has a total header deadline (80.345593ms)
|
||||
✔ oversized header refuses (1.044536ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.486134ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.562662ms)
|
||||
ℹ tests 11
|
||||
ℹ suites 0
|
||||
ℹ pass 11
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 149.849801
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 454B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 1.6s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 ...
|
||||
|
||||
#5 [internal] load build context
|
||||
#5 transferring context: 31.36MB 0.1s done
|
||||
#5 DONE 0.2s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 sha256:08489011619dfda982191cb9cc0418dc48ea3b1341588aeef534e4db0ab4d3d0 447B / 447B 0.1s done
|
||||
#4 sha256:1a0cf4b1ffc8dd659bb8c6e90471bc8fec24620e4fdb68bb2b6faa56154ae146 0B / 1.71MB 0.2s
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 0B / 50.86MB 0.2s
|
||||
#4 sha256:517d640e32835287653c4b15eb5d9f6bcf48e103baea4f7e29bac937002f649b 3.31kB / 3.31kB 0.2s done
|
||||
#4 sha256:1a0cf4b1ffc8dd659bb8c6e90471bc8fec24620e4fdb68bb2b6faa56154ae146 1.71MB / 1.71MB 0.3s done
|
||||
#4 sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39 3.15MB / 28.25MB 0.3s
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 6.29MB / 50.86MB 0.5s
|
||||
#4 sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39 11.53MB / 28.25MB 0.5s
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 14.68MB / 50.86MB 0.6s
|
||||
#4 sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39 19.92MB / 28.25MB 0.6s
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 24.12MB / 50.86MB 0.8s
|
||||
#4 sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39 28.25MB / 28.25MB 0.8s done
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 49.28MB / 50.86MB 1.1s
|
||||
#4 extracting sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39
|
||||
#4 sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 50.86MB / 50.86MB 1.1s done
|
||||
#4 extracting sha256:7fd1ebe418b5079d4aa36f416af9b143d127bc75b0155d71cb5fd559b5e04d39 0.7s done
|
||||
#4 DONE 1.6s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 extracting sha256:517d640e32835287653c4b15eb5d9f6bcf48e103baea4f7e29bac937002f649b 0.0s done
|
||||
#4 extracting sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a
|
||||
#4 extracting sha256:11ffcabe0f772f0f1a9c80b4b290b5ec0de54f11c671f0fa10e5844f9b52d17a 1.0s done
|
||||
#4 DONE 2.6s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 extracting sha256:1a0cf4b1ffc8dd659bb8c6e90471bc8fec24620e4fdb68bb2b6faa56154ae146 0.0s done
|
||||
#4 extracting sha256:08489011619dfda982191cb9cc0418dc48ea3b1341588aeef534e4db0ab4d3d0 done
|
||||
#4 DONE 2.7s
|
||||
|
||||
#6 [2/9] WORKDIR /opt/mosaic
|
||||
#6 DONE 0.0s
|
||||
|
||||
#7 [3/9] COPY package.json package-lock.json ./
|
||||
#7 DONE 0.0s
|
||||
|
||||
#8 [4/9] RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund && npm cache clean --force
|
||||
#8 1.008 npm warn deprecated [email protected]: Use your platform's native DOMException instead
|
||||
#8 2.464
|
||||
#8 2.464 added 128 packages in 2s
|
||||
#8 2.465 npm notice
|
||||
#8 2.465 npm notice New major version of npm available! 11.19.0 -> 12.2.1
|
||||
#8 2.465 npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.1
|
||||
#8 2.465 npm notice To update run: npm install -g [email protected]
|
||||
#8 2.465 npm notice
|
||||
#8 2.562 npm warn using --force Recommended protections disabled.
|
||||
#8 DONE 2.7s
|
||||
|
||||
#9 [5/9] COPY packages/ packages/
|
||||
#9 DONE 0.1s
|
||||
|
||||
#10 [6/9] COPY scripts/ scripts/
|
||||
#10 DONE 0.0s
|
||||
|
||||
#11 [7/9] COPY contracts/ contracts/
|
||||
#11 DONE 0.0s
|
||||
|
||||
#12 [8/9] COPY roles/ roles/
|
||||
#12 DONE 0.0s
|
||||
|
||||
#13 [9/9] COPY RELEASE ./
|
||||
#13 DONE 0.0s
|
||||
|
||||
#14 exporting to image
|
||||
#14 exporting layers
|
||||
#14 exporting layers 3.3s done
|
||||
#14 exporting manifest sha256:b288643bbe89b43f6ffe7890598fe70801933c9e1e5d64efbfc6d87bbc502389 done
|
||||
#14 exporting config sha256:c8e137b79bfd807d4d9fc37d5ac211476decc3cc3c8db1e39b2e5dedf5471191 done
|
||||
#14 exporting attestation manifest sha256:5f2e757dd0fc2a424c78eb4bdd2ddd3039067afb54bc76bc03d6a6f9f1e2ac75 done
|
||||
#14 exporting manifest list sha256:7b4fa741d785225148156d1962a1770b40ab84b55ce8ffde5a3485b316f93b3a done
|
||||
#14 naming to docker.io/library/mosaic-core:0.0.12 done
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12 1.6s done
|
||||
#14 DONE 4.9s
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 424B done
|
||||
#1 WARN: InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/mosaic-core:0.0.12
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/2] FROM docker.io/library/mosaic-core:0.0.12@sha256:7b4fa741d785225148156d1962a1770b40ab84b55ce8ffde5a3485b316f93b3a
|
||||
#4 resolve docker.io/library/mosaic-core:0.0.12@sha256:7b4fa741d785225148156d1962a1770b40ab84b55ce8ffde5a3485b316f93b3a done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [2/2] RUN npm install -g --no-audit --no-fund @anthropic-ai/[email protected] @earendil-works/[email protected] && npm cache clean --force && rm -rf /root/.npm /tmp/*
|
||||
#5 1.716 npm warn deprecated [email protected]: Use your platform's native DOMException instead
|
||||
#5 6.031
|
||||
#5 6.031 added 167 packages in 6s
|
||||
#5 6.032 npm warn install-scripts 4 packages have install scripts not yet covered by allowScripts:
|
||||
#5 6.032 npm warn install-scripts @anthropic-ai/[email protected] (postinstall: node install.cjs)
|
||||
#5 6.032 npm warn install-scripts @google/[email protected] (preinstall: echo 'preinstall: no-op')
|
||||
#5 6.032 npm warn install-scripts [email protected] (postinstall: node install.js)
|
||||
#5 6.032 npm warn install-scripts [email protected] (postinstall: node scripts/postinstall)
|
||||
#5 6.032 npm warn install-scripts
|
||||
#5 6.032 npm warn install-scripts Run `npm install -g --allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs --location=user` to allow them for all global installs.
|
||||
#5 6.119 npm warn using --force Recommended protections disabled.
|
||||
#5 DONE 6.3s
|
||||
|
||||
#6 exporting to image
|
||||
#6 exporting layers
|
||||
#6 exporting layers 18.7s done
|
||||
#6 exporting manifest sha256:16af90e8e3291626f37fb5b2e78904d87126d875e7bcc240dd2ba453ca62bc10 done
|
||||
#6 exporting config sha256:ad269f0a84c0b1c5833b787793a45df782c1aae86e37f6216782a12a01f4557b done
|
||||
#6 exporting attestation manifest sha256:436490e0d139cb0db83f260c42ac3364377971160b2418cd03ee07c18873a5f6 done
|
||||
#6 exporting manifest list sha256:bc95495952e58a005c275a9d00842242c17965d0d9c2a83c476048cbfc110946 done
|
||||
#6 naming to docker.io/library/mosaic-session:0.0.12 done
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12 5.3s done
|
||||
#6 DONE 24.0s
|
||||
|
||||
[33m1 warning found (use docker --debug to expand):
|
||||
[0m - InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 299B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/4] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [2/4] WORKDIR /opt/mosaic
|
||||
#5 CACHED
|
||||
|
||||
#6 [internal] load build context
|
||||
#6 transferring context: 459B 0.0s done
|
||||
#6 DONE 0.0s
|
||||
|
||||
#7 [3/4] COPY packages/egress/ packages/egress/
|
||||
#7 DONE 0.0s
|
||||
|
||||
#8 [4/4] COPY contracts/egress.json contracts/egress.json
|
||||
#8 DONE 0.0s
|
||||
|
||||
#9 exporting to image
|
||||
#9 exporting layers 0.0s done
|
||||
#9 exporting manifest sha256:f3f318ddc871b5a5f953aa0709cf69194260e633e26b15daafefe40572a36f27 done
|
||||
#9 exporting config sha256:c315f86a3be49d29aa7cad287a1653424206adb43683a34b91633b9bb8a14c6b done
|
||||
#9 exporting attestation manifest sha256:d41ee39a55170821cf46236fbf6069519e52c7386e27259ee17113777fa348e1 done
|
||||
#9 exporting manifest list sha256:af29f552efea177d706779ed9afb43c89763b58de913da0a570208cdda619d50 done
|
||||
#9 naming to docker.io/library/mosaic-egress:0.0.12 done
|
||||
#9 unpacking to docker.io/library/mosaic-egress:0.0.12 0.0s done
|
||||
#9 DONE 0.1s
|
||||
internal network: listed TLS reached, unlisted host/port refused, external DNS refused
|
||||
Vikunja client public GET 200; notifier REST dummy-token GET 401
|
||||
test-install: PASS (provider-host measurement remains Mos gate)
|
||||
EXIT=0
|
||||
@@ -0,0 +1,24 @@
|
||||
2026-10-24T04:13:52Z
|
||||
23:13:52 up 10 days, 14:33, 2 users, load average: 2.19, 2.69, 2.26
|
||||
CLEAR sage-3923 TMPDIR=/mnt/storage/scratch/tmp/5Tw4Jn
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (17.065009ms)
|
||||
✔ unlisted host (1.179696ms)
|
||||
✔ unlisted port (4.182798ms)
|
||||
✔ GET (0.916554ms)
|
||||
✔ invalid port (0.897474ms)
|
||||
✔ userinfo (0.772938ms)
|
||||
✔ bad header (0.737254ms)
|
||||
✔ slow client has a total header deadline (81.64527ms)
|
||||
✔ oversized header refuses (1.073337ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.464571ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.555082ms)
|
||||
✔ upstream reset after CONNECT closes the tunnel without a second HTTP response (21.757224ms)
|
||||
ℹ tests 12
|
||||
ℹ suites 0
|
||||
ℹ pass 12
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 173.90579
|
||||
EXIT=0
|
||||
@@ -0,0 +1,207 @@
|
||||
2026-10-24T04:13:56Z
|
||||
23:13:56 up 10 days, 14:33, 2 users, load average: 2.18, 2.67, 2.26
|
||||
CLEAR sage-3923 TMPDIR=/mnt/storage/scratch/tmp/Yb2lkZ
|
||||
✔ install: absent (26.874578ms)
|
||||
✔ install: local (25.86789ms)
|
||||
✔ install: local endpoint (24.545469ms)
|
||||
✔ install: containerized missing endpoint (26.458263ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (24.315713ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (27.113397ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (24.399221ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (26.259544ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (24.051655ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (23.493287ms)
|
||||
✔ install: bad endpoint "unix:///a" (25.527734ms)
|
||||
✔ install: bad endpoint "" (24.647712ms)
|
||||
✔ install: bad endpoint null (24.029319ms)
|
||||
✔ install: bad endpoint 4 (22.545369ms)
|
||||
✔ install: valid endpoint (24.217907ms)
|
||||
✔ install: unknown mode (22.578207ms)
|
||||
✔ install: missing mode (26.668685ms)
|
||||
✔ install: unknown key (25.239503ms)
|
||||
✔ install: null (26.507504ms)
|
||||
✔ install: array (23.325961ms)
|
||||
✔ helper refuses rootful (83.142099ms)
|
||||
✔ helper refuses missing (48.938896ms)
|
||||
✔ helper refuses substring (74.574145ms)
|
||||
✔ helper refuses object (69.21506ms)
|
||||
✔ helper refuses invalid JSON (71.320245ms)
|
||||
✔ helper refuses null success (70.397988ms)
|
||||
✔ helper refuses rootless output but failed (50.077162ms)
|
||||
✔ helper checks once and explicitly names host for both calls (116.70882ms)
|
||||
✔ local mode refuses before Docker null (26.523468ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (26.276186ms)
|
||||
✔ invalid config refuses before Docker (26.092328ms)
|
||||
ℹ tests 31
|
||||
ℹ suites 0
|
||||
ℹ pass 31
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 707.332707
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (15.694598ms)
|
||||
✔ unlisted host (1.162772ms)
|
||||
✔ unlisted port (1.047448ms)
|
||||
✔ GET (0.913924ms)
|
||||
✔ invalid port (0.930799ms)
|
||||
✔ userinfo (0.750312ms)
|
||||
✔ bad header (0.739214ms)
|
||||
✔ slow client has a total header deadline (81.348784ms)
|
||||
✔ oversized header refuses (0.992296ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.441271ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.4408ms)
|
||||
✔ upstream reset after CONNECT closes the tunnel without a second HTTP response (21.481904ms)
|
||||
ℹ tests 12
|
||||
ℹ suites 0
|
||||
ℹ pass 12
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 169.285852
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 454B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [internal] load build context
|
||||
#5 transferring context: 31.37MB 0.1s done
|
||||
#5 DONE 0.1s
|
||||
|
||||
#6 [3/9] COPY package.json package-lock.json ./
|
||||
#6 CACHED
|
||||
|
||||
#7 [2/9] WORKDIR /opt/mosaic
|
||||
#7 CACHED
|
||||
|
||||
#8 [4/9] RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund && npm cache clean --force
|
||||
#8 CACHED
|
||||
|
||||
#9 [5/9] COPY packages/ packages/
|
||||
#9 DONE 0.1s
|
||||
|
||||
#10 [6/9] COPY scripts/ scripts/
|
||||
#10 DONE 0.0s
|
||||
|
||||
#11 [7/9] COPY contracts/ contracts/
|
||||
#11 DONE 0.0s
|
||||
|
||||
#12 [8/9] COPY roles/ roles/
|
||||
#12 DONE 0.0s
|
||||
|
||||
#13 [9/9] COPY RELEASE ./
|
||||
#13 DONE 0.0s
|
||||
|
||||
#14 exporting to image
|
||||
#14 exporting layers
|
||||
#14 exporting layers 0.4s done
|
||||
#14 exporting manifest sha256:065dd55d59585806e540929e2ea66df397d47292cc84160bb7be491ecd2b9d59 done
|
||||
#14 exporting config sha256:3d97362dc6b6242fc540f08f538068b91ec28b1d3c9a34d7bc2bebff137a7a40 done
|
||||
#14 exporting attestation manifest sha256:f66bf47e4ec5b67db64f44d8554192f646111116e165ec3bc017b5a2b011204b done
|
||||
#14 exporting manifest list sha256:d840b0ec16ce6548549d18fb8bfc6b13c76ee77e9131a317582f919c61dcaf7f done
|
||||
#14 naming to docker.io/library/mosaic-core:0.0.12 done
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12 0.2s done
|
||||
#14 DONE 0.6s
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 424B done
|
||||
#1 WARN: InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/mosaic-core:0.0.12
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/2] FROM docker.io/library/mosaic-core:0.0.12@sha256:d840b0ec16ce6548549d18fb8bfc6b13c76ee77e9131a317582f919c61dcaf7f
|
||||
#4 resolve docker.io/library/mosaic-core:0.0.12@sha256:d840b0ec16ce6548549d18fb8bfc6b13c76ee77e9131a317582f919c61dcaf7f done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [2/2] RUN npm install -g --no-audit --no-fund @anthropic-ai/[email protected] @earendil-works/[email protected] && npm cache clean --force && rm -rf /root/.npm /tmp/*
|
||||
#5 1.700 npm warn deprecated [email protected]: Use your platform's native DOMException instead
|
||||
#5 5.518
|
||||
#5 5.518 added 167 packages in 5s
|
||||
#5 5.519 npm warn install-scripts 4 packages have install scripts not yet covered by allowScripts:
|
||||
#5 5.519 npm warn install-scripts @anthropic-ai/[email protected] (postinstall: node install.cjs)
|
||||
#5 5.519 npm warn install-scripts @google/[email protected] (preinstall: echo 'preinstall: no-op')
|
||||
#5 5.519 npm warn install-scripts [email protected] (postinstall: node install.js)
|
||||
#5 5.519 npm warn install-scripts [email protected] (postinstall: node scripts/postinstall)
|
||||
#5 5.519 npm warn install-scripts
|
||||
#5 5.519 npm warn install-scripts Run `npm install -g --allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs --location=user` to allow them for all global installs.
|
||||
#5 5.590 npm warn using --force Recommended protections disabled.
|
||||
#5 DONE 5.7s
|
||||
|
||||
#6 exporting to image
|
||||
#6 exporting layers
|
||||
#6 exporting layers 16.9s done
|
||||
#6 exporting manifest sha256:d4c83e350da6f18bfa7f3dbcef9ab078d72ef7f1f957a2926726e17973969e6a done
|
||||
#6 exporting config sha256:8afa310f48e06614963dcbcdc9c3ac497d9e4bad4ddd0524062ba8a52bc3cda2 done
|
||||
#6 exporting attestation manifest sha256:b7f082a8cd17d2c90eb27a81359aea66186b80d56a6d0c5e89d29187a2f78ed4 done
|
||||
#6 exporting manifest list sha256:2b6299e5a329adf85ea36038cb0407f2328e72e848dbfe95a17bd1fefbf9cf92 done
|
||||
#6 naming to docker.io/library/mosaic-session:0.0.12 done
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12 4.7s done
|
||||
#6 DONE 21.7s
|
||||
|
||||
[33m1 warning found (use docker --debug to expand):
|
||||
[0m - InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 299B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/4] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [internal] load build context
|
||||
#5 transferring context: 459B 0.0s done
|
||||
#5 DONE 0.0s
|
||||
|
||||
#6 [2/4] WORKDIR /opt/mosaic
|
||||
#6 CACHED
|
||||
|
||||
#7 [3/4] COPY packages/egress/ packages/egress/
|
||||
#7 DONE 0.0s
|
||||
|
||||
#8 [4/4] COPY contracts/egress.json contracts/egress.json
|
||||
#8 DONE 0.0s
|
||||
|
||||
#9 exporting to image
|
||||
#9 exporting layers 0.0s done
|
||||
#9 exporting manifest sha256:e47c86064470f291d2ea468a1002f1e5f9b2e682e42940d06bd33d4e39945a6e done
|
||||
#9 exporting config sha256:ba520eadeb66ccfa2dbd3170744fca08affb4dac377d6cf2d26ec27d98d4ee25 done
|
||||
#9 exporting attestation manifest sha256:93cbd2f8a7fb1afb858b7b1a35a3020af8e9623e3a98be71d04e9d746c25d148 done
|
||||
#9 exporting manifest list sha256:a55e9d5ec6da74933bbefac6816efd0ea5246e825f61eaa4297e7baf7b09501c done
|
||||
#9 naming to docker.io/library/mosaic-egress:0.0.12 done
|
||||
#9 unpacking to docker.io/library/mosaic-egress:0.0.12 0.0s done
|
||||
#9 DONE 0.1s
|
||||
internal network: listed TLS reached, unlisted host/port refused, external DNS refused
|
||||
Vikunja client public GET 200; notifier REST dummy-token GET 401
|
||||
test-install: PASS (provider-host measurement remains Mos gate)
|
||||
EXIT=0
|
||||
@@ -0,0 +1,165 @@
|
||||
2026-10-24T04:29:25Z
|
||||
23:29:25 up 10 days, 14:49, 2 users, load average: 2.63, 2.97, 2.72
|
||||
CLEAR sage-3936 TMPDIR=/mnt/storage/scratch/tmp/jcNy2F archived=a14fdc443 expected RED
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=false) (62.262337ms)
|
||||
✖ refusal 408 releases server socket (allowHalfOpen=true) (552.049114ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=false) (12.304169ms)
|
||||
✖ refusal 400 releases server socket (allowHalfOpen=true) (509.122366ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=false) (11.566877ms)
|
||||
✖ refusal 403 releases server socket (allowHalfOpen=true) (508.134074ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=false) (11.339612ms)
|
||||
✖ refusal 431 releases server socket (allowHalfOpen=true) (507.396552ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=false) (11.812225ms)
|
||||
✖ refusal 502 releases server socket (allowHalfOpen=true) (509.521901ms)
|
||||
✖ inherited cache is discarded; private cache stays unexported (rootless=false) (49.005306ms)
|
||||
✖ inherited cache is discarded; private cache stays unexported (rootless=true) (42.961333ms)
|
||||
ℹ tests 12
|
||||
ℹ suites 0
|
||||
ℹ pass 5
|
||||
ℹ fail 7
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2739.328587
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:11:79
|
||||
✖ refusal 408 releases server socket (allowHalfOpen=true) (552.049114ms)
|
||||
AssertionError [ERR_ASSERTION]: refused peer must not retain a server socket
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:30:10)
|
||||
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: 1,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:11:79
|
||||
✖ refusal 400 releases server socket (allowHalfOpen=true) (509.122366ms)
|
||||
AssertionError [ERR_ASSERTION]: refused peer must not retain a server socket
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:30:10)
|
||||
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: 1,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:11:79
|
||||
✖ refusal 403 releases server socket (allowHalfOpen=true) (508.134074ms)
|
||||
AssertionError [ERR_ASSERTION]: refused peer must not retain a server socket
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:30:10)
|
||||
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: 1,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:11:79
|
||||
✖ refusal 431 releases server socket (allowHalfOpen=true) (507.396552ms)
|
||||
AssertionError [ERR_ASSERTION]: refused peer must not retain a server socket
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:30:10)
|
||||
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: 1,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:11:79
|
||||
✖ refusal 502 releases server socket (allowHalfOpen=true) (509.521901ms)
|
||||
AssertionError [ERR_ASSERTION]: refused peer must not retain a server socket
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/packages/egress/tests/refusal.test.mjs:30:10)
|
||||
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: 1,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/scripts/install-tests/review-cache.test.mjs:10:36
|
||||
✖ inherited cache is discarded; private cache stays unexported (rootless=false) (49.005306ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/scripts/install-tests/review-cache.test.mjs:19:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 2,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at ../../../../../../mnt/storage/scratch/tmp/jcNy2F/legacy/scripts/install-tests/review-cache.test.mjs:10:36
|
||||
✖ inherited cache is discarded; private cache stays unexported (rootless=true) (42.961333ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'--host',
|
||||
+ 'unix:///inherited.sock',
|
||||
+ 'ps'
|
||||
- 'unix:///configured.sock',
|
||||
- 'info',
|
||||
- '--format',
|
||||
- '{{json .SecurityOptions}}'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/jcNy2F/legacy/scripts/install-tests/review-cache.test.mjs:20:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ '--host', 'unix:///inherited.sock', 'ps' ],
|
||||
expected: [ '--host', 'unix:///configured.sock', 'info', '--format', '{{json .SecurityOptions}}' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
EXIT=1 (expected 1 for reproduced defects)
|
||||
@@ -0,0 +1,24 @@
|
||||
2026-10-24T04:29:39Z
|
||||
23:29:39 up 10 days, 14:49, 2 users, load average: 2.20, 2.85, 2.69
|
||||
CLEAR sage-3936 TMPDIR=/mnt/storage/scratch/tmp/0gZQZl
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=false) (53.837502ms)
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=true) (42.030239ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=false) (1.261482ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=true) (0.904817ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=false) (0.917847ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=true) (0.804008ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=false) (0.718814ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=true) (0.668466ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=false) (1.344911ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=true) (0.979004ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=false) (82.339935ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=true) (112.830463ms)
|
||||
ℹ tests 12
|
||||
ℹ suites 0
|
||||
ℹ pass 12
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 245.761199
|
||||
EXIT=0
|
||||
@@ -0,0 +1,45 @@
|
||||
2026-10-24T04:29:49Z
|
||||
23:29:49 up 10 days, 14:49, 2 users, load average: 2.09, 2.81, 2.68
|
||||
CLEAR sage-3936 TMPDIR=/mnt/storage/scratch/tmp/tUdE4l
|
||||
✔ install: absent (26.366604ms)
|
||||
✔ install: local (22.63247ms)
|
||||
✔ install: local endpoint (22.799393ms)
|
||||
✔ install: containerized missing endpoint (25.600693ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (23.03486ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (23.131519ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (24.109097ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (23.350143ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (25.171936ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (23.993281ms)
|
||||
✔ install: bad endpoint "unix:///a" (23.480143ms)
|
||||
✔ install: bad endpoint "" (24.103262ms)
|
||||
✔ install: bad endpoint null (22.669641ms)
|
||||
✔ install: bad endpoint 4 (24.019575ms)
|
||||
✔ install: valid endpoint (24.603314ms)
|
||||
✔ install: unknown mode (24.602796ms)
|
||||
✔ install: missing mode (23.797819ms)
|
||||
✔ install: unknown key (22.998491ms)
|
||||
✔ install: null (25.129192ms)
|
||||
✔ install: array (24.482115ms)
|
||||
✔ helper refuses rootful (69.872106ms)
|
||||
✔ helper refuses missing (48.024972ms)
|
||||
✔ helper refuses substring (75.261764ms)
|
||||
✔ helper refuses object (69.618809ms)
|
||||
✔ helper refuses invalid JSON (69.071543ms)
|
||||
✔ helper refuses null success (72.882659ms)
|
||||
✔ helper refuses rootless output but failed (53.345431ms)
|
||||
✔ helper checks once and explicitly names host for both calls (102.552473ms)
|
||||
✔ local mode refuses before Docker null (26.011087ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (28.185416ms)
|
||||
✔ invalid config refuses before Docker (27.514422ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=false) (75.055951ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=true) (109.227515ms)
|
||||
ℹ tests 33
|
||||
ℹ suites 0
|
||||
ℹ pass 33
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 690.153576
|
||||
EXIT=0
|
||||
@@ -0,0 +1,34 @@
|
||||
2026-10-24T04:29:59Z
|
||||
23:29:59 up 10 days, 14:49, 2 users, load average: 1.93, 2.75, 2.66
|
||||
CLEAR sage-3936 TMPDIR=/mnt/storage/scratch/tmp/TviERP
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (23.927026ms)
|
||||
✔ unlisted host (1.732173ms)
|
||||
✔ unlisted port (1.604846ms)
|
||||
✔ GET (1.184716ms)
|
||||
✔ invalid port (1.343703ms)
|
||||
✔ userinfo (1.188622ms)
|
||||
✔ bad header (1.150055ms)
|
||||
✔ slow client has a total header deadline (81.793054ms)
|
||||
✔ oversized header refuses (1.244104ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.57805ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.746501ms)
|
||||
✔ upstream reset after CONNECT closes the tunnel without a second HTTP response (22.237884ms)
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=false) (55.691211ms)
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=true) (43.646383ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=false) (1.568816ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=true) (1.105107ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=false) (1.202959ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=true) (1.016559ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=false) (0.914639ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=true) (0.889516ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=false) (1.698346ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=true) (1.234793ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 183.862745
|
||||
EXIT=0
|
||||
@@ -0,0 +1,219 @@
|
||||
2026-10-24T04:30:08Z
|
||||
23:30:08 up 10 days, 14:49, 2 users, load average: 1.79, 2.69, 2.64
|
||||
CLEAR sage-3936 TMPDIR=/mnt/storage/scratch/tmp/qmqk7B
|
||||
✔ install: absent (25.729884ms)
|
||||
✔ install: local (27.889152ms)
|
||||
✔ install: local endpoint (24.35751ms)
|
||||
✔ install: containerized missing endpoint (32.226985ms)
|
||||
✔ install: bad endpoint "tcp://localhost:2375" (36.932763ms)
|
||||
✔ install: bad endpoint "ssh://host/run/docker.sock" (30.311354ms)
|
||||
✔ install: bad endpoint "run/docker.sock" (25.198808ms)
|
||||
✔ install: bad endpoint "unix://relative.sock" (26.143708ms)
|
||||
✔ install: bad endpoint "unix:///a b.sock" (26.182728ms)
|
||||
✔ install: bad endpoint "unix:///a.sock\n" (24.78053ms)
|
||||
✔ install: bad endpoint "unix:///a" (25.183076ms)
|
||||
✔ install: bad endpoint "" (26.038913ms)
|
||||
✔ install: bad endpoint null (28.122111ms)
|
||||
✔ install: bad endpoint 4 (25.273681ms)
|
||||
✔ install: valid endpoint (24.978162ms)
|
||||
✔ install: unknown mode (25.362867ms)
|
||||
✔ install: missing mode (30.700943ms)
|
||||
✔ install: unknown key (25.157537ms)
|
||||
✔ install: null (25.311198ms)
|
||||
✔ install: array (22.791684ms)
|
||||
✔ helper refuses rootful (73.335956ms)
|
||||
✔ helper refuses missing (58.142601ms)
|
||||
✔ helper refuses substring (76.536037ms)
|
||||
✔ helper refuses object (71.066459ms)
|
||||
✔ helper refuses invalid JSON (75.615304ms)
|
||||
✔ helper refuses null success (74.72487ms)
|
||||
✔ helper refuses rootless output but failed (56.316538ms)
|
||||
✔ helper checks once and explicitly names host for both calls (116.375519ms)
|
||||
✔ local mode refuses before Docker null (33.189488ms)
|
||||
✔ local mode refuses before Docker {"mode":"local"} (33.416869ms)
|
||||
✔ invalid config refuses before Docker (33.260841ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=false) (77.918846ms)
|
||||
✔ inherited cache is discarded; private cache stays unexported (rootless=true) (125.24254ms)
|
||||
ℹ tests 33
|
||||
ℹ suites 0
|
||||
ℹ pass 33
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 751.025142
|
||||
✔ allows exact host and port, fragmented headers, and preserves tunnel head (21.619769ms)
|
||||
✔ unlisted host (1.449654ms)
|
||||
✔ unlisted port (1.146495ms)
|
||||
✔ GET (1.037357ms)
|
||||
✔ invalid port (0.994228ms)
|
||||
✔ userinfo (0.84581ms)
|
||||
✔ bad header (0.834734ms)
|
||||
✔ slow client has a total header deadline (81.37968ms)
|
||||
✔ oversized header refuses (1.096351ms)
|
||||
✔ missing, invalid and unknown lists fail closed (0.499339ms)
|
||||
✔ refused upstream returns 502 while retaining one allowed decision (1.730862ms)
|
||||
✔ upstream reset after CONNECT closes the tunnel without a second HTTP response (21.825124ms)
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=false) (55.028256ms)
|
||||
✔ refusal 408 releases server socket (allowHalfOpen=true) (42.613145ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=false) (1.348936ms)
|
||||
✔ refusal 400 releases server socket (allowHalfOpen=true) (1.104981ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=false) (1.097498ms)
|
||||
✔ refusal 403 releases server socket (allowHalfOpen=true) (0.965577ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=false) (0.806917ms)
|
||||
✔ refusal 431 releases server socket (allowHalfOpen=true) (0.752832ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=false) (1.582846ms)
|
||||
✔ refusal 502 releases server socket (allowHalfOpen=true) (1.14509ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 181.752441
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 454B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 0.7s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/9] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [internal] load build context
|
||||
#5 transferring context: 31.38MB 0.1s done
|
||||
#5 DONE 0.1s
|
||||
|
||||
#6 [2/9] WORKDIR /opt/mosaic
|
||||
#6 CACHED
|
||||
|
||||
#7 [3/9] COPY package.json package-lock.json ./
|
||||
#7 CACHED
|
||||
|
||||
#8 [4/9] RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund && npm cache clean --force
|
||||
#8 CACHED
|
||||
|
||||
#9 [5/9] COPY packages/ packages/
|
||||
#9 DONE 0.1s
|
||||
|
||||
#10 [6/9] COPY scripts/ scripts/
|
||||
#10 DONE 0.0s
|
||||
|
||||
#11 [7/9] COPY contracts/ contracts/
|
||||
#11 DONE 0.0s
|
||||
|
||||
#12 [8/9] COPY roles/ roles/
|
||||
#12 DONE 0.0s
|
||||
|
||||
#13 [9/9] COPY RELEASE ./
|
||||
#13 DONE 0.0s
|
||||
|
||||
#14 exporting to image
|
||||
#14 exporting layers
|
||||
#14 exporting layers 0.4s done
|
||||
#14 exporting manifest sha256:cfd93bac400adc7580134f6177efd9d748e4b4ad65b0ff22515ca3b3ffafa530 done
|
||||
#14 exporting config sha256:bda6c7420c2df9510f081d28f50d205083867d0ce907c12290fff226c020fe38 done
|
||||
#14 exporting attestation manifest sha256:79c61e7daf7794a3db88121ce6260fafab69f182ef89773914c41ca75a44574c done
|
||||
#14 exporting manifest list sha256:82fc7a3df060c3e58799cca4f89dbd57dc007c5534fdedce54d3de637cc83b67 done
|
||||
#14 naming to docker.io/library/mosaic-core:0.0.12 done
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12
|
||||
#14 unpacking to docker.io/library/mosaic-core:0.0.12 0.2s done
|
||||
#14 DONE 0.7s
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 424B done
|
||||
#1 WARN: InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/mosaic-core:0.0.12
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/2] FROM docker.io/library/mosaic-core:0.0.12@sha256:82fc7a3df060c3e58799cca4f89dbd57dc007c5534fdedce54d3de637cc83b67
|
||||
#4 resolve docker.io/library/mosaic-core:0.0.12@sha256:82fc7a3df060c3e58799cca4f89dbd57dc007c5534fdedce54d3de637cc83b67 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [2/2] RUN npm install -g --no-audit --no-fund @anthropic-ai/[email protected] @earendil-works/[email protected] && npm cache clean --force && rm -rf /root/.npm /tmp/*
|
||||
#5 1.920 npm warn deprecated [email protected]: Use your platform's native DOMException instead
|
||||
#5 6.054
|
||||
#5 6.054 added 167 packages in 6s
|
||||
#5 6.055 npm warn install-scripts 4 packages have install scripts not yet covered by allowScripts:
|
||||
#5 6.055 npm warn install-scripts @anthropic-ai/[email protected] (postinstall: node install.cjs)
|
||||
#5 6.055 npm warn install-scripts @google/[email protected] (preinstall: echo 'preinstall: no-op')
|
||||
#5 6.055 npm warn install-scripts [email protected] (postinstall: node install.js)
|
||||
#5 6.055 npm warn install-scripts [email protected] (postinstall: node scripts/postinstall)
|
||||
#5 6.055 npm warn install-scripts
|
||||
#5 6.055 npm warn install-scripts Run `npm install -g --allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs` to allow these scripts once, or `npm config set allow-scripts=@anthropic-ai/claude-code,@google/genai,esbuild,protobufjs --location=user` to allow them for all global installs.
|
||||
#5 6.127 npm warn using --force Recommended protections disabled.
|
||||
#5 DONE 6.3s
|
||||
|
||||
#6 exporting to image
|
||||
#6 exporting layers
|
||||
#6 exporting layers 19.5s done
|
||||
#6 exporting manifest sha256:8b6b7de22cf08845f8fc02961c9677afb9faf0ddc4efe04ab4073a822a60518b done
|
||||
#6 exporting config sha256:94013fda37e404bc26b7b15281138cd60afbfea672cae2d2187acc0a7c3a0d7e done
|
||||
#6 exporting attestation manifest sha256:127fcf7ecd51a985d22f5a33d5c441d8fa1e246f88dc6e990c01197b46a61427 done
|
||||
#6 exporting manifest list sha256:9a272acf49ab376fa9739f66453979487ea8ab259bc43e56f6553e2da7b20835 done
|
||||
#6 naming to docker.io/library/mosaic-session:0.0.12 done
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12
|
||||
#6 unpacking to docker.io/library/mosaic-session:0.0.12 5.2s done
|
||||
#6 DONE 24.8s
|
||||
|
||||
[33m1 warning found (use docker --debug to expand):
|
||||
[0m - InvalidDefaultArgInFrom: Default value for ARG ${CORE_IMAGE} results in empty or invalid base image name (line 2)
|
||||
#0 building with "default" instance using docker driver
|
||||
|
||||
#1 [internal] load build definition from Dockerfile
|
||||
#1 transferring dockerfile: 299B done
|
||||
#1 DONE 0.0s
|
||||
|
||||
#2 [internal] load metadata for docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#2 DONE 0.0s
|
||||
|
||||
#3 [internal] load .dockerignore
|
||||
#3 transferring context: 2B done
|
||||
#3 DONE 0.0s
|
||||
|
||||
#4 [1/4] FROM docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
|
||||
#4 resolve docker.io/library/node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20 done
|
||||
#4 DONE 0.0s
|
||||
|
||||
#5 [internal] load build context
|
||||
#5 transferring context: 517B 0.0s done
|
||||
#5 DONE 0.0s
|
||||
|
||||
#6 [2/4] WORKDIR /opt/mosaic
|
||||
#6 CACHED
|
||||
|
||||
#7 [3/4] COPY packages/egress/ packages/egress/
|
||||
#7 DONE 0.0s
|
||||
|
||||
#8 [4/4] COPY contracts/egress.json contracts/egress.json
|
||||
#8 DONE 0.0s
|
||||
|
||||
#9 exporting to image
|
||||
#9 exporting layers 0.0s done
|
||||
#9 exporting manifest sha256:db3e8991a9330374257298a4a5d4ff36779e8c8d0b00c8b8ead47fc82e32fada done
|
||||
#9 exporting config sha256:e7d66f4f785e49a6529b3ed4b708e3b985f4e737d03a66a2c861666288d435d9 done
|
||||
#9 exporting attestation manifest sha256:8d2362ebe73a0b71b175ea4d9571ab841c6f5b0913e8fa265d1f4544307987d2 done
|
||||
#9 exporting manifest list sha256:36d0bbeb3abc92fc099d95e1fac416df5cc29edf147fcfc4d8743cbe1d60e592 done
|
||||
#9 naming to docker.io/library/mosaic-egress:0.0.12 done
|
||||
#9 unpacking to docker.io/library/mosaic-egress:0.0.12 0.0s done
|
||||
#9 DONE 0.1s
|
||||
internal network: listed TLS reached, unlisted host/port refused, external DNS refused
|
||||
Vikunja client public GET 200; notifier REST dummy-token GET 401
|
||||
test-install: PASS (provider-host measurement remains Mos gate)
|
||||
EXIT=0
|
||||
@@ -0,0 +1,363 @@
|
||||
# Row 157 / I2 / #1652 — Bentina build record
|
||||
|
||||
## 2026-10-24 — recovery and pre-build hold
|
||||
|
||||
Authority: Sage sage-3906 under Q246 / mos-1566. Seat thread:
|
||||
`d1ae468e-51ba-40c4-9513-b39ebbb40450`. Branch `bentina/row157`,
|
||||
base `ec86f2c84` (required registration commit), initially clean.
|
||||
Read root AGENTS.md, agents/bentina/README.md, the I2 brief and its shared
|
||||
rules, and the approved containerized-install design. CURRENT.md is a
|
||||
historical narrative; queue and Sage's assignment select this row. Goal
|
||||
order recovered from 2026-09-27_goals-review.md. Boyd first review, then
|
||||
Darkwing install-path review, per Sage's newer assignment.
|
||||
|
||||
### Queue boundary
|
||||
|
||||
The initial worktree `scripts/mosaic queue next bentina` refused because
|
||||
queue commands require the canonical checkout. Retried the same read using
|
||||
`/mnt/storage/src/mosaic-stack/scripts/mosaic` in that checkout (the documented
|
||||
queue location, without changing its configuration):
|
||||
|
||||
```
|
||||
start row 157: I2 install images, the install config key, the endpoint helper and the egress proxy; brief docs/plans/2026-10-18_containerized-install-rows.md § I2 install images, the install config key, the endpoint helper and the egress proxy; brief differs from pinned blob; ask the lead to re-pin
|
||||
```
|
||||
|
||||
No claimant move until Sage resolves the stale brief pin. No other queue
|
||||
write authorized. Sent Sage bentina-0001 with this refusal and the outbound
|
||||
path findings below. Implementation has not started.
|
||||
|
||||
### Re-read of the I2 brief's cited files at ec86f2c84
|
||||
|
||||
| Cited file / old site | Current observation |
|
||||
|---|---|
|
||||
| scripts/hello.sh:24 | Calls processing-control/src/container-cli.mjs:25; Docker moved behind processing control. |
|
||||
| scripts/build.sh:13 | Still ambient `docker compose build`:13. |
|
||||
| scripts/release.sh:39,122 | Still ambient image inspect:39 and compose build:122. |
|
||||
| scripts/agent.sh:208 | Now execs processing-control/src/container-cli.mjs --tty:209. |
|
||||
| scripts/verify.sh:36,39,45 | Ambient image inspect:37 and compose build:40; request now uses processing-control container CLI:46. |
|
||||
| scripts/land-work.sh:87-88 | Ambient network inspect/remove unchanged at 87-88. |
|
||||
| scripts/test-release.sh:58 | Docker probes now at 78,129; cleanup network rm:25; network checks:131,133. |
|
||||
| scripts/test-task.sh:139,395,442,517 | Docker probes now at 166,422,470,513,557,579; cleanup network rm:21 and network checks:581,583. |
|
||||
| scripts/mosaic-task.mjs:396 | Now calls runContainer:402, imported from processing control, rather than directly spawning Docker. |
|
||||
| packages/cli/src/git-socket.mjs:243 | dropNetwork now spawns ambient docker network rm:266; I6 retains ownership. |
|
||||
| scripts/mosaic-config.mjs:109-160 / :115 | Loader expanded for instances, Console, federation and compaction settings. validate starts:293; allowed-key expression:304 retains a prefix used by stack-release-fixture.mjs. No install key yet. |
|
||||
|
||||
The design says to read via scripts/load_config; this is a function, not a
|
||||
file. Actual entry is scripts/common.sh:16 `load_config`, invoking
|
||||
mosaic-config.mjs env, then exporting four existing variables. No install
|
||||
mode/endpoint exports yet. No modification made.
|
||||
|
||||
Design sections 3/4/7 re-read: exact base digest
|
||||
`node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20`;
|
||||
three release-tagged images; optional install with local default; exact
|
||||
rootless SecurityOptions array element check; baked agents/services CONNECT
|
||||
lists; JSON decision log. Spike proxy source re-read: one-chunk request
|
||||
parsing, no slow-client bound, environment allowlist; it needs the reviewed
|
||||
implementation required by I2, not a direct copy. package.json pins pi
|
||||
0.85.1; spike Dockerfile.clis pins Claude Code 2.1.296 (current session-code
|
||||
pin still needs checking before implementation).
|
||||
|
||||
### Outbound stop condition (brief What ships; design section 7)
|
||||
|
||||
The brief explicitly says: “If one doesn't, stop and tell Sage before
|
||||
adding a dependency.” Not all broker outbound paths use fetch at this base:
|
||||
|
||||
- packages/bus/src/gitea-grant.mjs:11 imports node:https request;
|
||||
send():91-140 calls httpsRequest:115 with `agent: false`, auth header,
|
||||
optional CA, timeout and bounded response. It is not the measured fetch
|
||||
path. runtime.mjs imports and wires the grant executor.
|
||||
- packages/bus/src/web.mjs delegates to packages/discord/src/web.mjs.
|
||||
That implementation imports node:https/node:http:23-24, resolves public
|
||||
addresses and pins the connection to a vetted address (:161 onward).
|
||||
Changing it to fetch without preserving that fence would be a boundary
|
||||
change. Its arbitrary public destinations also need reconciliation with
|
||||
the design's fixed services host list; do not widen the list speculatively.
|
||||
- packages/tasks/src/vikunja.mjs:30,41 uses injected/default global fetch,
|
||||
redirect:error, and processing cancellation/deadline.
|
||||
- packages/cli/src/notifier-process.mjs:41-46 supplies global fetch to
|
||||
openDirect. packages/discord/src/notify.mjs:17 builds REST only; it
|
||||
explicitly does not run the gateway. packages/discord/src/rest.mjs:45
|
||||
calls injected fetch. The separate Discord connector gateway uses native
|
||||
WebSocket (gateway.mjs:193); do not claim the notifier needs that gateway.
|
||||
- No NODE_USE_ENV_PROXY or HTTPS_PROXY setup found in host.mjs,
|
||||
notifier-process.mjs or bus/process.mjs. I6 owns container startup;
|
||||
I2's eventual image/process verification must demonstrate the actual path
|
||||
with dummy credentials, after the transport ruling and a CLEAR slot.
|
||||
|
||||
Recommendation sent to Sage: re-pin the approved brief and rule on the
|
||||
bounded handling/ownership of non-fetch paths before implementation. No new
|
||||
dependency, host-list expansion, or replacement of DNS pinning authorized.
|
||||
|
||||
### Environment and evidence limits
|
||||
|
||||
`/run/user/1000/docker.sock` exists as a Unix socket owned by jwoltje.
|
||||
Existence is not a rootless attestation. No Docker command or endpoint probe
|
||||
has been run. All test execution requires Sage CLEAR; none requested yet
|
||||
because there is no implementation candidate. No test, image build,
|
||||
credential read, live config/data access, daemon/volume/unit change, commit
|
||||
or push. Once unblocked, implement within the owned file list, add required
|
||||
acceptance cases, then request a concrete CLEAR with paths and suites;
|
||||
use fresh /mnt/storage/scratch/tmp/XXXXXX TMPDIR and timeout -k 10 for each
|
||||
scripts/test-*.sh. Old-test changes require Sage authorization before edits.
|
||||
Provider-host measurement ships with expected output for Mos; no real token
|
||||
will be used here.
|
||||
|
||||
## sage-3907 — stop resolved; implementation draft
|
||||
|
||||
Sage confirms only I6 changed since the brief pin, and will re-pin row 157.
|
||||
I2 must not change non-fetch security transports or add a dependency. Record
|
||||
all gaps, retain each named service host and code path in the contract with
|
||||
an explicit note, and build the rest. Sage owns the transport ruling before
|
||||
broker rollout in later rows. This supersedes the pre-build transport hold;
|
||||
the queue claim still awaits a current pin. No queue move attempted.
|
||||
|
||||
Additional non-fetch broker paths found:
|
||||
|
||||
- runtime.mjs:511 calls seat/src/working.mjs:461 publishRemote, which runs
|
||||
git ls-remote/push at :479/:490 with credential helper input on fd 3.
|
||||
checkout.mjs:80 gitEnv emits a fixed environment without HTTPS_PROXY.
|
||||
This is Git HTTPS, not fetch; core's no-git design also prevents it from
|
||||
working today. The Gitea host entry retains this caveat.
|
||||
- runtime.mjs:292 wires hostGrant; host-grant.mjs:174 spawns ssh-keyscan
|
||||
to read public host keys at the registered address/port. It uses fixed
|
||||
PATH-only env, not fetch or HTTP CONNECT. runtime.mjs:435 wires
|
||||
hostExecutor; host-run.mjs:30-45 sshArgs explicitly sets
|
||||
ProxyCommand=none and ProxyJump=none, then ssh to run authorized commands
|
||||
using a pinned host key and private agent. Neither routes through an env
|
||||
HTTP proxy. Hosts/ports are per registry and may be SSH, so no fabricated
|
||||
:443 allowlist entry or network expansion was added.
|
||||
- Search-source plug-ins delegate to discord/src/web.mjs. searxng-url
|
||||
sends search GETs to the operator's configured host; searxng-bundled points
|
||||
at 127.0.0.1:8888. Both are non-fetch and depend on the existing DNS checks.
|
||||
No fixed host can be truthfully named for arbitrary web.fetch or operator
|
||||
search; no wildcard entry was added. These are documented gaps, not
|
||||
claims of proxy coverage.
|
||||
- Separate Discord connector gateway uses native WebSocket; retained
|
||||
gateway.discord.gg:443 with a NOT PROXIED note, as Sage directs. Notifier
|
||||
is REST-only; no gateway test or live connector run added.
|
||||
|
||||
Choices: dependency-free net proxy; exact list lookup; 8192-byte/5s total
|
||||
header bounds, 10s upstream connect/120s tunnel idle bounds. JSON allowed
|
||||
means the contract permitted the destination, not that upstream succeeded.
|
||||
Images use exact I1 base, core has NODE_USE_ENV_PROXY=1, session globally
|
||||
installs Claude 2.1.296 and package.json pi pin. Source context includes only
|
||||
tracked runtime paths plus explicit new I2 runtime paths, never agent work
|
||||
or host node_modules/.git. Tags derive from RELEASE. Core receives no git.
|
||||
The zai host candidate api.z.ai is confirmed by the pinned dependency's
|
||||
providers/zai.js:9 in canonical node_modules; real-token measurement remains
|
||||
Mos's gate. OpenAI candidate is from S6 and is only measured when configured.
|
||||
No live model-table config was read.
|
||||
|
||||
New tests cover config exit/immutability; helper rootful, null/nonzero,
|
||||
substring, wrong type/JSON, exact rootless and hostile DOCKER_HOST; proxy
|
||||
fragmentation/pipelined data, deny/port/malformed/slow/oversize and JSON logs.
|
||||
test-install additionally builds, inspects pins and exercises an internal
|
||||
network plus actual Vikunja/public GET and notifier REST/dummy GET with
|
||||
proxy log evidence. The generic S5 fetch test is not repeated.
|
||||
|
||||
Old-suite change is prepared as test-config.patch (adds new config cases
|
||||
before the existing summary). It has NOT been applied: Sage's old-test
|
||||
change rule requires approval first. No old tests changed. No tests run.
|
||||
|
||||
## sage-3910 — first CLEAR
|
||||
|
||||
Authorized four serialized runs exactly as requested, plus applying the
|
||||
addition-only test-config.patch. No broader suites; Sage supplies the
|
||||
landing gate. Return slot on run 4 completion or an actual stop.
|
||||
|
||||
Additional paths retained under Sage's explicit ruling:
|
||||
- scripts/install-tests/config.test.mjs: table of install schema/exit/byte
|
||||
preservation cases, reused by test-config.sh and test-install.sh.
|
||||
- scripts/install-tests/helper.test.mjs: fake Docker executable to assert
|
||||
exact argv and rootless refusal before the requested call, without a daemon.
|
||||
- scripts/install-tests/network-probe.mjs: actual internal-network tunnel,
|
||||
TLS, unlisted host/port and external DNS refusal in a core image.
|
||||
- scripts/install-tests/services-probe.mjs: actual Vikunja and notifier REST
|
||||
client functions, public GET plus invalid dummy token, with proxy log proof.
|
||||
All are new files; no old assertion changes.
|
||||
|
||||
## First CLEAR result and sage-3916 correction
|
||||
|
||||
Run 1: exit 0, config/helper 31/31, zero fail/skip/cancel,
|
||||
01-install-unit.log. Run 2: exit 0, proxy 11/11, zero fail/skip/cancel,
|
||||
02-egress.log. Run 3: exit 1, config selftest 55 pass / 1 fail; new install
|
||||
cases 20/20, 03-config.log. Each log records UTC/load/CLEAR/fresh scratch TMPDIR.
|
||||
Run 4 NOT RUN: no Docker command, probe or build. Slot returned to Sage in
|
||||
bentina-0004 before any rerun.
|
||||
|
||||
Old red: config without console loads with unchanged output
|
||||
(test-config.sh:266; log:53). Cause: this implementation unconditionally
|
||||
added install:{mode:local} to resolved JSON. Sage-3916 approves the source-only
|
||||
fix: omit output key when input omits it (optional-key convention), default
|
||||
env export to local. Applied this fix to mosaic-config.mjs and adjusted only
|
||||
the new absent-key case. No old assertion changed. No verification after fix:
|
||||
Poe has the current slot, Bentina runs next only on a new CLEAR. Run order
|
||||
will be 1, 3, 4; no reason to rerun unchanged proxy unit tests separately.
|
||||
|
||||
The approved test-config patch is addition-only: git diff --numstat reports
|
||||
4 added, 0 removed lines for scripts/test-config.sh. No existing line or
|
||||
assertion changed. git diff --stat before first run:
|
||||
|
||||
```
|
||||
BUILD-LOG.md | 2 ++
|
||||
README.md | 9 +++++++++
|
||||
docs/SESSIONS.md | 2 ++
|
||||
docs/TOOLS.md | 38 ++++++++++++++++++++++++++++++++++++++
|
||||
scripts/mosaic-config.mjs | 20 +++++++++++++++++++-
|
||||
scripts/test-config.sh | 4 ++++
|
||||
6 files changed, 74 insertions(+), 1 deletion(-)
|
||||
```
|
||||
|
||||
New untracked paths are not included by git diff --stat; see explicit paths
|
||||
above. Claim bentina-0003 initially refused queue-lock-held; same op retried.
|
||||
Sage reports re-pin rev 3808 (sage-3913).
|
||||
|
||||
## sage-3919 — corrected acceptance green, slot returned
|
||||
|
||||
Run 1 exit 0, helper/config 31/31 (04-install-unit-fixed.log); run 3 exit 0,
|
||||
config selftest 56/56 plus new install 20/20 (05-config-fixed.log); run 4
|
||||
exit 0 (06-install.log), helper/config 31/31 and proxy 11/11, images built
|
||||
at RELEASE 0.0.12, rootless endpoint explicitly attested, image version
|
||||
checks passed, internal-network TLS to listed host reached, unlisted host
|
||||
and port refused, external DNS refused. Actual Vikunja public GET returned
|
||||
200 and notifier REST dummy-token GET returned 401; services proxy logs
|
||||
show both host decisions. No real token or broker started. Test-owned
|
||||
networks/containers cleaned by suite. Slot returned bentina-0005. No broader
|
||||
suites: Sage's landing gate supplies them. Claim bentina-0003 acknowledged
|
||||
rev 3812 briefed -> in-progress. No queue files changed in this worktree.
|
||||
|
||||
Post-pass source review found one small correctness defect before handoff:
|
||||
upstream errors after a successful CONNECT would append a plaintext 502 to
|
||||
an established TLS tunnel. Corrected to close an established tunnel and
|
||||
reserve 502 for establishment errors. Added a local reset-after-CONNECT
|
||||
regression alongside the existing establishment-error test. This creates
|
||||
a new verification need: request a new CLEAR for egress units and install
|
||||
rebuild/probe before committing. Existing green logs above predate this fix;
|
||||
do not claim them for the final proxy bytes.
|
||||
|
||||
## sage-3923 — final proxy and install evidence
|
||||
|
||||
07-egress-final.log: exit 0, 12/12, zero fail/skip/cancel, including upstream
|
||||
reset after a CONNECT200 closes the tunnel without a second HTTP response.
|
||||
08-install-final.log: exit 0, 31/31 config/helper and 12/12 proxy,
|
||||
zero fail/skip/cancel, three images rebuilt, version checks passed,
|
||||
internal-network listed TLS200/unlisted host403/unlisted port403/external
|
||||
DNS refused, Vikunja public GET200 and notifier REST dummy-token GET401
|
||||
through services proxy; JSON log decisions verified. Explicit endpoint
|
||||
unix:///run/user/1000/docker.sock passed rootless check. Suite removed only
|
||||
its named i2 containers/networks. Slot returned to Sage in bentina-0007.
|
||||
|
||||
No runtime source change after these final reruns. Mos-only measurement
|
||||
runner was statically hardened to report a fixed message on malformed
|
||||
credential JSON, avoiding JSON.parse's error snippet echoing private input.
|
||||
The runner and provider script have not been executed here: Mos owns the
|
||||
real-token/provider-model-table check and its output must land on #1652.
|
||||
No claim of completed provider-host measurement or final host-list approval.
|
||||
|
||||
Acceptance accounting:
|
||||
- Install validator cases and unchanged config bytes: new 20 cases green;
|
||||
entire existing config suite 56/56 (05-config-fixed.log); absent output
|
||||
shape preserved; env/helper default local proven by helper's absent test.
|
||||
- Rootless guard: exact argv/array membership/status/type/refusal fake
|
||||
cases plus real rootless check in 08-install-final.log. No ambient Docker
|
||||
context used by new code. Other scripts' migration remains I6.
|
||||
- Images: exact base digest in Dockerfiles, release 0.0.12 tags, core node
|
||||
24.21.0/no-git/RELEASE assertion; session Claude2.1.296/pi0.85.1 assertions;
|
||||
all built through the helper on the explicit rootless endpoint.
|
||||
- Proxy: allow/deny/port/malformed/slow/fragmented/header-bound/upstream-error
|
||||
and structured logs verified in final unit suite; actual TLS tunnel and
|
||||
internal-network denials plus services client fetch paths verified by
|
||||
install suite. The generic S5 fetch probe was not repeated.
|
||||
- Non-fetch broker/gateway/SSH/Git/web paths remain documented gaps under
|
||||
sage-3907, not modified or silently claimed to be covered. All fixed
|
||||
service hosts retain entries and code-path notes. No dependency added.
|
||||
- Full suites: not run here per sage-3910; Sage's landing gate supplies
|
||||
broader coverage. Reviewer and Mos gates still pending.
|
||||
|
||||
Static source diff whitespace check passed (excluding captured .log files
|
||||
and unified .patch context). Raw build logs retain Docker/npm output
|
||||
including whitespace; no evidence was normalized or rewritten. Old-suite
|
||||
change remains +4/-0 and every prior assertion byte-identical. No queue
|
||||
file changed here. No secret read, token test, live state/unit operation,
|
||||
daemon/volume/restart action, protected-branch push, or integration.
|
||||
|
||||
Candidate handoff: base ec86f2c84; commit only this I2 work and receipts on
|
||||
bentina/row157, push through configured repository helper, then send Sage
|
||||
and Boyd the tip/base/manifest for first review. Darkwing follows Boyd.
|
||||
The manifest lists each changed path except its own self-reference.
|
||||
|
||||
## Round 2 — Boyd B1/B2 (boyd-0023; Sage sage-3933)
|
||||
|
||||
First candidate a14fdc443, manifest b0ed8395, CHANGES on #1652 comment31494.
|
||||
Read Boyd's exact scratch harnesses and logs under /mnt/storage/scratch/tmp/IyNqMs.
|
||||
B1 reproduction: normal peer control releases its socket, allowHalfOpen peer
|
||||
receives408 but getConnections still reports1 after100ms (assertion1 != 0).
|
||||
Root cause: refusal end sends FIN but clears the only deadline and leaves the
|
||||
read side open. B2 reproduction: same-PID exec inherits exported checked_pid
|
||||
and endpoint; fake rootful Docker sees ps on inherited.sock, no info; rc0 vs
|
||||
expected2. Root cause: source initializes neither cache validity nor endpoint.
|
||||
These findings agree with source inspection and require no design departure.
|
||||
No local tests were run while Goldie holds the slot.
|
||||
|
||||
Added tests before fixes, adapted from the reviewer with explicit cleanup:
|
||||
- packages/egress/tests/refusal.test.mjs: real loopback sockets, normal and
|
||||
half-open controls for every400/403/408/431 refusal; asserts server socket
|
||||
count reaches0, one denial log, expected response. Removing destruction
|
||||
after flush should fail half-open cases while normal controls still pass.
|
||||
- scripts/install-tests/review-cache.test.mjs: same-PID exec with inherited
|
||||
exported cache, fake rootful/rootless Docker, actual loader/helper; asserts
|
||||
first call is info at configured.sock, refusal or exact once-per-process
|
||||
cache behavior, and no cache keys in Docker's child environment. Removing
|
||||
source-time reset should fail; no real engine, config or credentials used.
|
||||
No existing test file or assertion edited.
|
||||
|
||||
Minimal fixes: proxy uses endAndClose for all refusal responses, including
|
||||
upstream establishment502: destroy after the small response flushes, plus a
|
||||
fixed1s drain deadline cleared on close. Established-tunnel reset behavior
|
||||
stays unchanged. Helper unsets checked_pid/endpoint/mode on every source,
|
||||
clearing inherited export attributes; ordinary function calls still reuse
|
||||
only an attestation obtained in the current process. No dependency, contract,
|
||||
host-list or authority change. Request CLEAR for new regression red/green,
|
||||
full targeted units and test-install rebuild before commit. Old-test red
|
||||
still stops and returns slot. Sage's broader landing gate remains separate.
|
||||
|
||||
Sage-3935 approves the five-run plan including archived red-first proof;
|
||||
Goldie holds the slot and Bentina is next. Still no node/docker test run.
|
||||
The new refusal test also includes normal/half-open502 establishment failures
|
||||
on a closed loopback port, exercising the shared termination helper on that
|
||||
path (allowed log stays true for an approved destination whose upstream fails).
|
||||
This stays within the named new regression file and approved commands.
|
||||
|
||||
## Round 2 verified — sage-3936 slot returned
|
||||
|
||||
Claimant move bentina-0012 acknowledged rev3825 in-review -> in-progress,
|
||||
before the correction commit. Five serialized runs, each fresh short scratch
|
||||
TMPDIR/UTC/load/CLEAR and timeout -k10:
|
||||
|
||||
| Run | Log (this directory) | Exit | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 old archived runtime + new tests | 09-r2-archived-red.log | 1 expected | 12 tests:5 normal-peer controls pass;7 intended failures (five half-open400/403/408/431/502 cases, two inherited-cache cases);0skip/cancel |
|
||||
| 2 fixed regressions | 10-r2-regressions-green.log | 0 | 12/12,0fail/skip/cancel |
|
||||
| 3 install unit suite | 11-r2-install-units.log | 0 | 33/33,0fail/skip/cancel |
|
||||
| 4 egress suite | 12-r2-egress-suite.log | 0 | 22/22,0fail/skip/cancel |
|
||||
| 5 install images/network/services | 13-r2-install.log | 0 | 33+22 unit tests green;three images rebuilt/version pins checked;explicit rootless guard;listed TLS200,unlisted host/port403,external DNS refused;Vikunja publicGET200/notifier REST dummyGET401;JSON decisions verified |
|
||||
|
||||
Run1 unpacked git archive of a14fdc443 into its own scratch directory and
|
||||
overlaid only the two new tests; it did not revert or modify the builder
|
||||
worktree. Failures were exact behavioral assertions, not infrastructure or
|
||||
syntax errors. All test sockets cleaned in t.after; the install suite
|
||||
removed only its i2-prefixed containers/networks. Slot returned to Sage in
|
||||
bentina-0014. No existing test file/assertion changed for round2. No code
|
||||
change after verification. Earlier config56/56 evidence remains in05 log;
|
||||
loader/config unchanged for this review. No broader suites authorized here.
|
||||
|
||||
The round2 manifest MANIFEST-r2.sha256 covers the full candidate delta from
|
||||
original base ec86f2c84, including the unchanged historical round1 manifest;
|
||||
it excludes only its own self-reference (its hash is supplied in the review
|
||||
message). Historical round1 manifest still describes a14fdc443 and is not
|
||||
rewritten to describe later source. New manifest, candidate tip/base and
|
||||
receipts go to Sage and Boyd, then Darkwing follows Boyd's approve. The
|
||||
owner's automated review-request credential boundary still belongs to Sage;
|
||||
no token read or identity substitution is authorized. Mos provider evidence
|
||||
and Sage's broader landing gate remain required. No protected-branch push,
|
||||
real-token test, live config/state/unit action or daemon/volume operation.
|
||||
@@ -0,0 +1,44 @@
|
||||
58e473e1783a9f64f6b1a696f747925b9a444d85dbe72e48e7a0dcbd62a2d4e5 BUILD-LOG.md
|
||||
67bd811b3030ba0355af2316d02e8b2931b4b2c5a74c5be904b5311de6ef81dd README.md
|
||||
6161cc0505bf0be33ba0e7c7f0f07382cca0ec2b5ace65ac54e16c4d1e3424fc agents/bentina/work/queue-157/01-install-unit.log
|
||||
f13213941dab22955841108d85e62f67ede2ddbc95c25a7b351f30070b035a85 agents/bentina/work/queue-157/02-egress.log
|
||||
b46c3fb5f252100f3e2de5b97bd0efc0c1fbbef26230252945f7bb309d359f5e agents/bentina/work/queue-157/03-config.log
|
||||
fc76e5f9b19a31b494ebf4a128ef5f171f1f0712dc3928bebb6556815577015c agents/bentina/work/queue-157/04-install-unit-fixed.log
|
||||
c9a46d969c5d28814addccd7cafe8e734d15c02a407ee80434d4e3e70a3fa4b7 agents/bentina/work/queue-157/05-config-fixed.log
|
||||
1d5505a6aba0078e7cf9936fb683e06ac900af12e9e8faf224099a5e2d825fc7 agents/bentina/work/queue-157/06-install.log
|
||||
c684dfdd8a460a915e15291fc0f92c9ba30f38d3d3dfa5fa190e7835fbcd448f agents/bentina/work/queue-157/07-egress-final.log
|
||||
5c3efacdbcda5628882045644c32169b7020a6e49d6b89b273aba20ae042898b agents/bentina/work/queue-157/08-install-final.log
|
||||
d2f7bd37b5ac8cc781fae1308a5b047e0ed0d1b63f8d5e22d56e0efa328c4883 agents/bentina/work/queue-157/09-r2-archived-red.log
|
||||
4230f9fcc91d1670df0d975e9c5794edb7aa9b21b24c4f415d1594373e769da4 agents/bentina/work/queue-157/10-r2-regressions-green.log
|
||||
3e5cd8d154b01ede9a9533f4ff40ac29b4e8193131fd63ba04707df51217d0bb agents/bentina/work/queue-157/11-r2-install-units.log
|
||||
4d23ae4a6d3247a498bd446960c075235f632474fba9e8db374a32aaf4760219 agents/bentina/work/queue-157/12-r2-egress-suite.log
|
||||
a4652f0eb6dfcc177c24c5d85d7ec08096e9f49304085f817828d7ef451acd64 agents/bentina/work/queue-157/13-r2-install.log
|
||||
5b595798fa8184b15412276d070a0891fdf5cb629eabb8b7bb5f6b3d2b69b3a5 agents/bentina/work/queue-157/BUILD.md
|
||||
b0ed8395108ad398f5c9c08fbac8a1b5ca02f579a5d7b709d19d3aaf0860f9ae agents/bentina/work/queue-157/MANIFEST.sha256
|
||||
21f9783dcfe2cac00a49d0d7930a57134bc59ec19dd7bf2e96950f79fae5b1b1 agents/bentina/work/queue-157/PROVIDER-EXPECTED.md
|
||||
6518c574291db5816057fbbc438435d0571e088c8f26d1a47ec03ff3047a5efa agents/bentina/work/queue-157/provider-hosts.sh
|
||||
6dbef8d20ede98a9311ba47137b587c07cbc03e921b2a8429652fe362aeb4b85 agents/bentina/work/queue-157/provider-runner.mjs
|
||||
e7cb4d0c67eadec557a25385b4ea9803970818354a9e8b0780ac4de07cf34911 agents/bentina/work/queue-157/test-config.patch
|
||||
cb2b251342f0020764b7b757b22567d52380053fc15f0e1c7e665a5ec1838f24 contracts/egress.json
|
||||
58107cfa274b7551ae163ea77c56d08df530e7f08e06539ddd2999c22252d749 docs/SESSIONS.md
|
||||
edcaa526bf0a518716d4e87895d49920b8912295688809a25102ce6b9d6ebdc3 docs/TOOLS.md
|
||||
6bd1dc7c244cfb4134d813e0b149ef1182a810eb99f01a95d548f0885b216437 images/README.md
|
||||
9c6c3d5736f1ed7eb6dcdb893761b937b7108e29730daf28fe8dd0064df73740 images/core/Dockerfile
|
||||
0179d84a7b9321832efb1d90c0290c8f2f6de03db3b2b6d8379672aca598c2a0 images/egress/Dockerfile
|
||||
e246e3e3da1065f3c8cf24cc1f7ae15290f3e4c6508f0c6f17a4889a77f2d591 images/session/Dockerfile
|
||||
3930764cf5848571507eec3e5b3eda2ebb8aa2d9642f83615b462bf5035e0e75 packages/egress/README.md
|
||||
2c45bfed1f19f8a42e05552f8a10584feed8d25ccc506252d55a9a3b6c90a21c packages/egress/package.json
|
||||
257058faee55a429d98955b6253d6b8a0bb058c50446764566586b06a8f76b61 packages/egress/src/cli.mjs
|
||||
ce3adb0515ca7e9041f36d7a82df355a3fc97e2a96fc6811ea3b9b47e464787b packages/egress/src/proxy.mjs
|
||||
5c4e778ec9ec5b93ab681d615a2240cd342d09adfe4df1634f3d6abbf16a405b packages/egress/tests/proxy.test.mjs
|
||||
258e954ad7e441a7dd7d25ca9208691ea167e9cb7f34d7d689ed11005adeb4b4 packages/egress/tests/refusal.test.mjs
|
||||
86e0152120b04fcfe2a4081b7686f179a139c5370027828c79572033f3cf81c3 scripts/images.sh
|
||||
3647b06df02b4d9c0a74b62c774ecdff9272e68a84c06cd85861d7bf18655d5a scripts/install-tests/config.test.mjs
|
||||
21e05bc3e0639427f5a9d4cf166d2991964134845272df76c712ad00ea8033b6 scripts/install-tests/helper.test.mjs
|
||||
aa5328566d3e5b46fee587657dac146712735dca0aca90d3d0ad84bc20e313e1 scripts/install-tests/network-probe.mjs
|
||||
45274f6745cf931f3002e6c36dcfc46a96c8f09e01e6243c18659749673752fd scripts/install-tests/review-cache.test.mjs
|
||||
72ae66696ad2faaf258c832fdfdf680efc8fca7099359e58da1ee01a88541d8a scripts/install-tests/services-probe.mjs
|
||||
79a8ecfc5ab279111031d5a44906450b732b517fe98511992cafd873ab7a84d3 scripts/lib/docker.sh
|
||||
7398bb289d2f28ed15a2984bdeee7e30a704890a33b5e2a5c9f7554115aca8f9 scripts/mosaic-config.mjs
|
||||
dcf1799238141689c9d80adc6680694a609e5fb9deef7b78d6b3f4765163e3fe scripts/test-config.sh
|
||||
08be25f67d8fc6676218b1014d34a2ce36a939231f58b657972d856ee26169b2 scripts/test-install.sh
|
||||
@@ -0,0 +1,36 @@
|
||||
6995aa3bcdf50e2441142cc9b6fc9ec84853e353e7a408bbc877ee7e188e73c4 BUILD-LOG.md
|
||||
67bd811b3030ba0355af2316d02e8b2931b4b2c5a74c5be904b5311de6ef81dd README.md
|
||||
6161cc0505bf0be33ba0e7c7f0f07382cca0ec2b5ace65ac54e16c4d1e3424fc agents/bentina/work/queue-157/01-install-unit.log
|
||||
f13213941dab22955841108d85e62f67ede2ddbc95c25a7b351f30070b035a85 agents/bentina/work/queue-157/02-egress.log
|
||||
b46c3fb5f252100f3e2de5b97bd0efc0c1fbbef26230252945f7bb309d359f5e agents/bentina/work/queue-157/03-config.log
|
||||
fc76e5f9b19a31b494ebf4a128ef5f171f1f0712dc3928bebb6556815577015c agents/bentina/work/queue-157/04-install-unit-fixed.log
|
||||
c9a46d969c5d28814addccd7cafe8e734d15c02a407ee80434d4e3e70a3fa4b7 agents/bentina/work/queue-157/05-config-fixed.log
|
||||
1d5505a6aba0078e7cf9936fb683e06ac900af12e9e8faf224099a5e2d825fc7 agents/bentina/work/queue-157/06-install.log
|
||||
c684dfdd8a460a915e15291fc0f92c9ba30f38d3d3dfa5fa190e7835fbcd448f agents/bentina/work/queue-157/07-egress-final.log
|
||||
5c3efacdbcda5628882045644c32169b7020a6e49d6b89b273aba20ae042898b agents/bentina/work/queue-157/08-install-final.log
|
||||
f4755f24d626dc69b8159c6934e4213a907659dbb4d46d29d82efc0405b50152 agents/bentina/work/queue-157/BUILD.md
|
||||
21f9783dcfe2cac00a49d0d7930a57134bc59ec19dd7bf2e96950f79fae5b1b1 agents/bentina/work/queue-157/PROVIDER-EXPECTED.md
|
||||
6518c574291db5816057fbbc438435d0571e088c8f26d1a47ec03ff3047a5efa agents/bentina/work/queue-157/provider-hosts.sh
|
||||
6dbef8d20ede98a9311ba47137b587c07cbc03e921b2a8429652fe362aeb4b85 agents/bentina/work/queue-157/provider-runner.mjs
|
||||
e7cb4d0c67eadec557a25385b4ea9803970818354a9e8b0780ac4de07cf34911 agents/bentina/work/queue-157/test-config.patch
|
||||
cb2b251342f0020764b7b757b22567d52380053fc15f0e1c7e665a5ec1838f24 contracts/egress.json
|
||||
d6239336e48b6a78b80680c88e83ba4b67f8cbed99bf2ee3a697b1b0611e54e9 docs/SESSIONS.md
|
||||
edcaa526bf0a518716d4e87895d49920b8912295688809a25102ce6b9d6ebdc3 docs/TOOLS.md
|
||||
6bd1dc7c244cfb4134d813e0b149ef1182a810eb99f01a95d548f0885b216437 images/README.md
|
||||
9c6c3d5736f1ed7eb6dcdb893761b937b7108e29730daf28fe8dd0064df73740 images/core/Dockerfile
|
||||
0179d84a7b9321832efb1d90c0290c8f2f6de03db3b2b6d8379672aca598c2a0 images/egress/Dockerfile
|
||||
e246e3e3da1065f3c8cf24cc1f7ae15290f3e4c6508f0c6f17a4889a77f2d591 images/session/Dockerfile
|
||||
3930764cf5848571507eec3e5b3eda2ebb8aa2d9642f83615b462bf5035e0e75 packages/egress/README.md
|
||||
2c45bfed1f19f8a42e05552f8a10584feed8d25ccc506252d55a9a3b6c90a21c packages/egress/package.json
|
||||
257058faee55a429d98955b6253d6b8a0bb058c50446764566586b06a8f76b61 packages/egress/src/cli.mjs
|
||||
2ec533e8a2c9ace2d2dc17ac9efe75a9930ae50e93f6700e3de8aa0475b42295 packages/egress/src/proxy.mjs
|
||||
5c4e778ec9ec5b93ab681d615a2240cd342d09adfe4df1634f3d6abbf16a405b packages/egress/tests/proxy.test.mjs
|
||||
86e0152120b04fcfe2a4081b7686f179a139c5370027828c79572033f3cf81c3 scripts/images.sh
|
||||
3647b06df02b4d9c0a74b62c774ecdff9272e68a84c06cd85861d7bf18655d5a scripts/install-tests/config.test.mjs
|
||||
21e05bc3e0639427f5a9d4cf166d2991964134845272df76c712ad00ea8033b6 scripts/install-tests/helper.test.mjs
|
||||
aa5328566d3e5b46fee587657dac146712735dca0aca90d3d0ad84bc20e313e1 scripts/install-tests/network-probe.mjs
|
||||
72ae66696ad2faaf258c832fdfdf680efc8fca7099359e58da1ee01a88541d8a scripts/install-tests/services-probe.mjs
|
||||
e524a285dcebe2a6350e7690650c2796e54661225ce00a67b4559e519f9e3fea scripts/lib/docker.sh
|
||||
7398bb289d2f28ed15a2984bdeee7e30a704890a33b5e2a5c9f7554115aca8f9 scripts/mosaic-config.mjs
|
||||
dcf1799238141689c9d80adc6680694a609e5fb9deef7b78d6b3f4765163e3fe scripts/test-config.sh
|
||||
08be25f67d8fc6676218b1014d34a2ce36a939231f58b657972d856ee26169b2 scripts/test-install.sh
|
||||
@@ -0,0 +1,37 @@
|
||||
# Mos provider-host gate (not run by Bentina)
|
||||
|
||||
Run only after candidate images build and Mos authorizes the real-token calls.
|
||||
Use a scratch config with install.mode containerized and the rootless endpoint;
|
||||
do not change the live config. Provide one JSON object on stdin with
|
||||
`claudeOAuth` and `zai`, plus optional `anthropic` / `openai` keys if those
|
||||
providers are in the actual model table. The script opens no host token file.
|
||||
Never paste the input or raw CLI output on the issue. It prints success flags
|
||||
and the proxy's host decisions only. Wrap in `timeout -k 10 600`.
|
||||
|
||||
From the checkout:
|
||||
|
||||
```
|
||||
Mos-private-json-producer | MOSAIC_CONFIG=/path/to/scratch-config.json agents/bentina/work/queue-157/provider-hosts.sh
|
||||
```
|
||||
|
||||
Expected required output (with just Claude Code and pi/zai):
|
||||
|
||||
```
|
||||
{"case":"claude","ok":true}
|
||||
{"case":"pi-zai","ok":true}
|
||||
{"hosts":["api.anthropic.com:443","api.z.ai:443"],"denied":[]}
|
||||
```
|
||||
|
||||
Optional pi/anthropic adds `{"case":"pi-anthropic","ok":true}` and no
|
||||
host. Optional pi/openai adds `{"case":"pi-openai","ok":true}` and
|
||||
`api.openai.com:443` to sorted hosts. All cases must return ok true, exit 0,
|
||||
and denied empty. Model names are explicit candidates; Mos must reconcile
|
||||
them to the actual model table without broadening a host list on failure.
|
||||
A new host, refusal, failure, or different model requirement returns to Sage;
|
||||
there is no wildcard or automatic discovery/update. The baked agents list
|
||||
remains provisional until Mos posts matching output on #1652.
|
||||
|
||||
Each resource name begins i2-provider; cleanup removes only this run's two
|
||||
networks and two containers. No volumes, daemon or restart operations. The
|
||||
measurement uses container root as the design requires; I5 still owns the
|
||||
full root filesystem re-measurement and final session limits.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Mos runs this explicitly with a caller-supplied scratch MOSAIC_CONFIG and
|
||||
# credential JSON on stdin. This script reads no host credential file.
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
|
||||
cd "$REPO"
|
||||
source scripts/lib/docker.sh
|
||||
mosaic_docker version >/dev/null
|
||||
release="$(tr -d '[:space:]' < RELEASE)"
|
||||
prefix="i2-provider-$(date -u +%s)-$$"
|
||||
nets=("$prefix-internal" "$prefix-egress")
|
||||
cleanup(){
|
||||
rc=$?; trap - EXIT
|
||||
mosaic_docker container rm -f "$prefix-session" "$prefix-proxy" >/dev/null 2>&1 || true
|
||||
for net in "${nets[@]}"; do mosaic_docker network rm "$net" >/dev/null 2>&1 || true; done
|
||||
exit "$rc"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
mosaic_docker network create --internal "${nets[0]}" >/dev/null
|
||||
mosaic_docker network create "${nets[1]}" >/dev/null
|
||||
mosaic_docker create --name "$prefix-proxy" --network "${nets[1]}" --read-only --cap-drop ALL --security-opt no-new-privileges --pids-limit 64 --memory 128m --memory-swap 128m --cpus 0.5 "mosaic-egress:$release" agents >/dev/null
|
||||
mosaic_docker network connect --alias proxy "${nets[0]}" "$prefix-proxy"
|
||||
mosaic_docker start "$prefix-proxy" >/dev/null
|
||||
ready=0
|
||||
for attempt in {1..50}; do
|
||||
if mosaic_docker exec "$prefix-proxy" node -e 'const s=require("node:net").connect(3128,"127.0.0.1");s.on("connect",()=>{s.destroy();process.exit(0)});s.on("error",()=>process.exit(1))'; then ready=1; break; fi
|
||||
sleep 0.1
|
||||
done
|
||||
[ "$ready" = 1 ] || { echo proxy-not-ready >&2; exit 1; }
|
||||
rc=0
|
||||
# Token travels only in stdin and the CLI child's env; never an engine env or log.
|
||||
mosaic_docker run --rm --init -i --name "$prefix-session" --network "${nets[0]}" --log-driver none --read-only --tmpfs /root:rw,nosuid --tmpfs /tmp:rw,noexec,nosuid --cap-drop ALL --security-opt no-new-privileges --pids-limit 512 --memory 4g --memory-swap 4g --cpus 2 \
|
||||
--mount "type=bind,src=$REPO/agents/bentina/work/queue-157/provider-runner.mjs,dst=/opt/mosaic/provider-runner.mjs,readonly" \
|
||||
"mosaic-session:$release" node provider-runner.mjs || rc=$?
|
||||
mosaic_docker logs "$prefix-proxy" | node -e 'let s="";process.stdin.on("data",c=>s+=c).on("end",()=>{const rows=s.trim().split("\n").filter(Boolean).map(JSON.parse);const deny=rows.filter(r=>r.host&&!r.allowed);console.log(JSON.stringify({hosts:[...new Set(rows.filter(r=>r.allowed).map(r=>r.host+":"+r.port))].sort(),denied:[...new Set(deny.map(r=>r.host+":"+r.port))].sort()}));if(deny.length)process.exitCode=1})' || rc=$?
|
||||
exit "$rc"
|
||||
@@ -0,0 +1,17 @@
|
||||
// Mos-only real-token measurement. Reads one JSON object on stdin. Never logs CLI output.
|
||||
import { execFile } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
const exec = promisify(execFile);
|
||||
let input='';for await(const chunk of process.stdin){input+=chunk;if(input.length>65536)throw new Error('measurement-input-too-large');}
|
||||
let keys;try{keys=JSON.parse(input);}catch{input='';process.stderr.write('measurement-input-invalid\n');process.exit(2);}input='';
|
||||
if(!keys.claudeOAuth||!keys.zai)throw new Error('measurement-needs-claudeOAuth-and-zai');
|
||||
const prompt='Reply with exactly I2_PROVIDER_OK and no other text.';
|
||||
const env={PATH:process.env.PATH,HOME:'/root',HTTPS_PROXY:'http://proxy:3128',HTTP_PROXY:'http://proxy:3128',NODE_USE_ENV_PROXY:'1',CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:'1'};
|
||||
const pi=['--no-session','--no-tools','--no-approve','--no-extensions','--no-skills','--no-prompt-templates','--no-themes','--no-context-files'];
|
||||
const cases=[['claude','claude',['-p',prompt,'--tools',''],{CLAUDE_CODE_OAUTH_TOKEN:keys.claudeOAuth}],['pi-zai','pi',[...pi,'--provider','zai','--model','glm-5.3-flash','-p',prompt],{ZAI_API_KEY:keys.zai}]];
|
||||
if(keys.anthropic)cases.push(['pi-anthropic','pi',[...pi,'--provider','anthropic','--model','claude-sonnet-5-5','-p',prompt],{ANTHROPIC_API_KEY:keys.anthropic}]);
|
||||
if(keys.openai)cases.push(['pi-openai','pi',[...pi,'--provider','openai','--model','gpt-5.4','-p',prompt],{OPENAI_API_KEY:keys.openai}]);
|
||||
for(const [name,bin,args,secrets] of cases){
|
||||
let ok=false;try{const {stdout}=await exec(bin,args,{env:{...env,...secrets},timeout:120000,killSignal:'SIGKILL',maxBuffer:1024*1024});ok=stdout.includes('I2_PROVIDER_OK');}catch{}
|
||||
console.log(JSON.stringify({case:name,ok}));if(!ok)process.exitCode=1;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/scripts/test-config.sh
|
||||
+++ b/scripts/test-config.sh
|
||||
@@ -291,5 +291,9 @@
|
||||
fi
|
||||
|
||||
echo
|
||||
+# I2: each install shape asserts its exit code and config-byte preservation.
|
||||
+node --test scripts/install-tests/config.test.mjs
|
||||
+check "install configuration cases" $?
|
||||
+
|
||||
echo "selftest: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
@@ -0,0 +1,8 @@
|
||||
# Boyd
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 298 (the chief-of-staff grant scope, handed over from Megavolt).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 48a3ae38) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `boyd/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,272 @@
|
||||
# Row 298 (#1804, Q228): chief-of-staff grant scope
|
||||
|
||||
Owner Boyd (48a3ae38), reviewer Dewey. Branch `boyd/row298`, base `45dca69f6`.
|
||||
Assignment: mos-1496/Q245. Handoff: Megavolt megavolt-0203, local `7a141269c`;
|
||||
cherry-picked as `4de3cec27` (design) and `7cea739de` (UNTESTED code).
|
||||
Brief: `docs/plans/2026-10-23_mvp-readiness-rows.md`, chief-of-staff grant scope.
|
||||
Megavolt's original design remains in `agents/megavolt/work/queue-298/BUILD.md`.
|
||||
|
||||
## Authority and accepted design
|
||||
|
||||
Sage sage-3594, restated in sage-3612: launch.grantScope, lenient read with empty
|
||||
fallback, strict human-only write. No general organization setter may write it.
|
||||
Per-method agents/projects or agents/hosts/commands; host-enroll never in scope.
|
||||
Host command arrays match whole tokens and exact lengths. `grants.scope.set`
|
||||
from the human CLI is Approve; no Console page or chief-raised scope change.
|
||||
Mos mos-1496/1502 requires the same human-shell guard as `mosaic access`.
|
||||
The broker closes an in-scope request via scope and writes grant.scope.used;
|
||||
missing evidence must refuse execution. Narrowing before spend fails
|
||||
scope-changed. Empty scope ships; only Jason sets the live value (Q188).
|
||||
|
||||
No credential files read, no live grants, no deployment, no queue writes. Push
|
||||
only boyd/*, additive commits, no amend/force-push or attribution trailers.
|
||||
Sage owns queue, shared test slot and landing. No Node/tests before CLEAR.
|
||||
|
||||
## Implementation plan and ledger
|
||||
|
||||
Using writing-plans and executing-plans inline; the approved brief and D1-D6
|
||||
are the spec. This packet is the requested plan/tracking location.
|
||||
|
||||
- [x] Read brief, roster, implementation and handoff; register session.
|
||||
- [x] Bring Megavolt's two commits onto Boyd's branch without changing history.
|
||||
- [x] Add independent CLI tests in `packages/cli/tests/grants-scope.test.mjs`:
|
||||
scope read/set requests; agent-marker rejection before stdin/transport;
|
||||
--tester rejection; missing revision, malformed/empty/oversize input.
|
||||
- [x] Strengthen new business/bus grant-scope tests for exact commands,
|
||||
scope evidence binding, host-enroll exclusion and spent scope-changed.
|
||||
- [ ] On Sage CLEAR, observe targeted failures before fixes; fix inherited
|
||||
implementation using the existing human proof and grant executors.
|
||||
- [ ] Update bus/CLI docs, append BUILD-LOG before/after evidence, manifest.
|
||||
- [ ] On Sage CLEAR run touched suites; record exact results and mutants.
|
||||
- [ ] Commit UNTESTED until verified; push boyd/row298 only when slot permits;
|
||||
report to Sage and Dewey, cc Mos for pushes/blockers. Dewey reviews manifest.
|
||||
|
||||
Pre-flight interfaces: business checkGrantScope/grantScopeOf/grantInScope feed
|
||||
the instance writer and broker. Runtime supplies a fresh scope from the org
|
||||
file and routes the human setter to the same organization writer. CLI callFor
|
||||
supplies refuseInsideAgent and humanTransport. The grant executors consume the
|
||||
broker's immutable close/evidence. Tests use scratch files/fake Gitea only.
|
||||
|
||||
Review focus: exact argv length, stale scope before spend, forged/missing scope
|
||||
evidence, malformed fallback, and CLI agent ancestry. Tests must cover each.
|
||||
|
||||
Static findings before any Boyd Node/test run:
|
||||
|
||||
- Handoff introduced commandPrefixes beyond the accepted D2 shape, allowing
|
||||
trailing args. Remove it; only exact command arrays are authorized.
|
||||
- scope.used carries a digest without validating it, and no org revision.
|
||||
Bind evidence to the canonical scope snapshot and grant/close sequence;
|
||||
preserve historical grants when scope is later changed, recheck at spend.
|
||||
- Gitea recheck throws inside the spend transaction and rolls back spend on
|
||||
scope-changed. Preserve spend and log failure for that case (D6).
|
||||
- The optional giteaStart?. assertion always passes when the API is absent;
|
||||
use the real runGiteaGrant entry point. Fake Gitea tokens() does exist.
|
||||
|
||||
## Existing test changes (mos-1272)
|
||||
|
||||
Approved by Mos mos-1510 via Sage sage-3624; edited exactly as approved:
|
||||
|
||||
| File | Actual numstat | Reason |
|
||||
| --- | --- | --- |
|
||||
| packages/bus/tests/project-runtime.test.mjs | +1 / -1 | Vocabulary import pin adds grantScopeOf and grantInScope. |
|
||||
|
||||
Other test work is confined to row 298's new files. No pre-existing CLI tests
|
||||
will change. Record final numstat here before review.
|
||||
|
||||
## Human proof and organization ownership
|
||||
|
||||
CLI: `packages/cli/src/cli.mjs` callFor runs refuseInsideAgent before constructing
|
||||
humanTransport, as `accessCommand` does. The setter takes no --tester flag.
|
||||
`packages/bus/src/human.mjs` verifyHuman checks the nonce, CLI path and every
|
||||
ancestor's markers/argv; `bindHuman` and #human in broker.mjs require the
|
||||
business human, via cli, outsideAgent true. Tests will pin this chain's caller
|
||||
refusal. The scope receipt records by/via/outsideAgent and old/new sha256.
|
||||
|
||||
Org file: `packages/business/src/organization.mjs` regular() requires a regular,
|
||||
non-symlink file owned by the process uid and not writable by group/other;
|
||||
write() uses revision checks, locked durable receipts and atomic rename.
|
||||
The OS owner uid can edit it directly; the agent sandbox does not mount the
|
||||
user's config (`packages/seat/src/sandbox.mjs`). This is the existing cooperative
|
||||
human/process boundary, not a hostile same-uid isolation claim.
|
||||
|
||||
Chief enrollment: broker instances.set calls #human and #mayHuman before
|
||||
add/change; credential issue/delete use #requestCredential's human gate.
|
||||
launch.by is an organization-file setting, excluded from general setters;
|
||||
only that chief may auto-approve within scope. Additional chief instances
|
||||
remain on the per-grant human approval path.
|
||||
|
||||
## Validation
|
||||
|
||||
UNTESTED. No Boyd Node/test run yet; waiting for Sage CLEAR.
|
||||
M1: bypass in-scope check. M2: bypass invalid-value fallback. M3: remove human
|
||||
setter guard. M4: drop grant.scope.used. All need named failing tests on gate.
|
||||
|
||||
## Session correction
|
||||
|
||||
The initial SESSIONS entry labels startup 19:13Z; startup was approximately
|
||||
19:08Z. Message header timestamps in boyd-0001 through 0004 were estimates;
|
||||
use T3's receipt timestamps as authoritative. Subsequent reports use date -u.
|
||||
|
||||
## Departures from Megavolt 7a141269c (Sage sage-3624)
|
||||
|
||||
1. Removed commandPrefixes; host scope commands match exact token arrays only.
|
||||
2. grantScope callback now supplies one organization read `{value,revision}`.
|
||||
grant.scope.used includes that revision, canonical scope snapshot and digest;
|
||||
#approvedGrant validates the digest, shape, method, agent and close sequence.
|
||||
It uses the immutable snapshot so later narrowing doesn't revoke issued grants.
|
||||
3. Gitea scope-changed is caught inside the spend transaction and logs a terminal
|
||||
failure without rolling back its spend. Other recheck failures keep their
|
||||
existing behavior. Host already catches recheck failures after spending.
|
||||
|
||||
Additional handoff corrections: replaced optional nonexistent giteaStart with
|
||||
runGiteaGrant; added successful host executor/revoke and enrollment-exclusion
|
||||
coverage; added real runtime writer coverage and independent CLI guard tests.
|
||||
|
||||
Sage scheduled push at 19:41Z with MOSAIC_GITEA_CREDENTIAL_FILE unset, then T1
|
||||
lane B around 19:47Z (after explicit CLEAR), four new files whole and serial,
|
||||
hard stop 20:00Z. Fethry lane A is disjoint. No test was run early. Production
|
||||
corrections are marked UNTESTED under that slot mandate; baseline regression
|
||||
checks against the inherited implementation will run only on CLEAR.
|
||||
|
||||
## Candidate preparation (before T1)
|
||||
|
||||
The three static corrections, CLI/runtime coverage and docs are prepared but
|
||||
UNTESTED. `git diff --check` is clean. The approved import-pin edit is exactly
|
||||
1 addition / 1 deletion. M1-M4 are encoded in mutants.py (not run). No Node,
|
||||
https git, full suite or webui build has run in Boyd's worktree.
|
||||
|
||||
Review package: product/test/documentation files in MANIFEST.sha256, excluding
|
||||
append-only project logs and the preserved predecessor packet. The manifest
|
||||
identifies exact bytes for Dewey; each later product change regenerates it and
|
||||
requires a review delta. Existing tests changed: only project-runtime, 1/1.
|
||||
|
||||
## Dewey static review dewey-0933
|
||||
|
||||
N1: Added a positive control to the malformed-evidence fixture. A valid event
|
||||
must pass scope authorization and reach decision-mismatch on the deliberately
|
||||
absent definition binding; invalid events remain not-authorized. No spend.
|
||||
N2: Additional named mutants cover spend recheck, exact argv length, Gitea spend
|
||||
rollback, launch.by eligibility and accepting any evidence row (results below).
|
||||
N3: At spend only grant scope membership is rechecked, not whether the raising
|
||||
chief is still launch.by. This implements sage-3594 Q5; changing the launcher
|
||||
later does not by itself invalidate a pending scope-approved request.
|
||||
N4: The scope shortcut returns the closed decision view to the chief without
|
||||
sending a separate close message; documented in the bus verb table.
|
||||
N5: grant.scope is logged after the organization file commit in a separate bus
|
||||
transaction. A crash between them leaves durable changes.jsonl as the receipt,
|
||||
as with other organization writers. The setter does not claim cross-store
|
||||
atomicity.
|
||||
|
||||
## T1 lane B results (sage-3642, 2026-10-23 19:23Z–19:29Z)
|
||||
|
||||
TMPDIR: `/mnt/storage/scratch/tmp/boyd298-4bzr6fn7`. Each whole file ran serially
|
||||
as `timeout -k 10 180 node --test --test-concurrency=1 FILE`, TMPDIR set to that
|
||||
scratch directory. Final restored candidate results:
|
||||
|
||||
| Whole file | Pass | Fail | Skip |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| business/tests/grant-scope.test.mjs | 7 | 0 | 0 |
|
||||
| bus/tests/grant-scope.test.mjs | 14 | 0 | 0 |
|
||||
| bus/tests/grant-scope-runtime.test.mjs | 1 | 0 | 0 |
|
||||
| cli/tests/grants-scope.test.mjs | 4 | 0 | 0 |
|
||||
| bus/tests/project-runtime.test.mjs | 3 | 0 | 0 |
|
||||
|
||||
29/0/0 total. `final-1.log` through `final-5.log` hold the final output. The
|
||||
runtime's reload-needs-restart notice is expected: the fixture's boot document
|
||||
has fewer credential references than its organization document, as in the
|
||||
existing instances runtime tests. No credential contents are loaded from the
|
||||
real user config.
|
||||
|
||||
Initial run: business 7/0; bus 13/1 due to Boyd's test typo, an out-of-scope
|
||||
`opts` reference in the missing-scope assertion. Corrected only that new-test
|
||||
line, then bus 14/0 and the remaining files green. Dewey N1 positive control
|
||||
was added and the whole bus file passed again; final rerun above followed all
|
||||
mutants. No production code changed during this test window.
|
||||
|
||||
Mutants: all whole files, serial, with timeout -k 10 and the same TMPDIR.
|
||||
`mutants.py --sage-clear sage-3642 --logs SCRATCH/review-mutants --deadline
|
||||
2026-10-23T19:38:00+00:00` restores exact original source after each run.
|
||||
M1/M2/M3/M4 are brief/Sage mutants; M5–M10 cover review focus and dewey-0933.
|
||||
Each exited 1 with named assertion failures, no loader/syntax kill.
|
||||
|
||||
| Mutant | Change | Pass/fail | Named killer |
|
||||
| --- | --- | --- | --- |
|
||||
| M1 | skip raise scope check | 11/3 | bus out-of-scope Gitea waits; empty/bad scope waits; host argv exclusion |
|
||||
| M2 | skip invalid-value fallback | 5/2 | business missing/bad scope fallback; bad file scope fallback |
|
||||
| M3 | remove #human from scope verb | 13/1 | bus Q3 caller refusals |
|
||||
| M4 | drop grant.scope.used write | 9/5 | bus Q4 protected evidence assertion; four executor waits also fail |
|
||||
| M5 | skip scope digest check | 13/1 | bus Q4 bad evidence: decision-mismatch instead of not-authorized |
|
||||
| M6 | rethrow narrowing, rolling spend back | 13/1 | bus Q5 Gitea spent assertion |
|
||||
| M7 | same without length check | 6/1 | business Q2 shorter command prefix exclusion |
|
||||
| M8 | no-op #scopeRecheck | 12/2 | bus Q5 Gitea and host scope-changed outcomes |
|
||||
| M9 | remove launch.by check | 13/1 | bus Q6 second chief stays open |
|
||||
| M10 | accept any matched scope evidence row | 13/1 | bus Q4 bad evidence, with positive control |
|
||||
|
||||
Mutation log sha256 (under scratch/review-mutants):
|
||||
|
||||
```text
|
||||
55e9973af5fca0088963e2ca11fd81b2fb46dfead813a577c0e0aad2d6269b68 M1.log
|
||||
abd1b784eff5ae1333a7b47085d3e199cad6d27a00e2584edf0b62417ad4c888 M2.log
|
||||
1a56085e548d1e244f0f88fe12a45f77c162375880794f9ef88b241ed3a1704d M3.log
|
||||
9c120f3f7128488edaf8bfd6d41bae712e69e76da4195a0d4dc0e8336b02038f M4.log
|
||||
1bba88a3752823454acdaa51abde6aad66fdd5488801b4aceda02a3b42c8123d M5.log
|
||||
a4ce3cacca9f7ea56b8cdfd503c76173a8e4ff1d8a7ec07032c93f0b51aa60d8 M6.log
|
||||
acecd5afc93d68d96d713f8a9a2dc08351831a0b7360a13b4e30a572ad4167bf M7.log
|
||||
fce7fa505ccf4f2d4190bd03c209fd19f1cf2770d3ae1246a3bd03697594019e M8.log
|
||||
a830a7964890dd04a2dcc7a932a1502f1dea06419a1eae6b91a85f7878dc5681 M9.log
|
||||
38ce3e7f69f6c6ffe10133397465d52538a930a830f76256a8b9280f8df78e4b M10.log
|
||||
```
|
||||
|
||||
Full touched-package suites and every scripts/test-*.sh remain for Sage's full gate; this CLEAR authorized only the five whole files. Candidate remains
|
||||
marked Untested for the full gate. No queue row has been changed by Boyd.
|
||||
|
||||
## Dewey final delta review (dewey-0935, 2026-10-23 19:31:55Z)
|
||||
|
||||
Dewey reviewed 574ece13c..bcd93dc08 with local git only, no Node: CLEAN,
|
||||
nothing blocks. Verified all 13 manifest entries, exact package-file coverage,
|
||||
project-runtime 1/1, M5-M10 mutation sites, all ten mutation log hashes and
|
||||
counts, named assertion kills without loader/syntax errors, final five logs
|
||||
29/0/0, and N1-N5 coverage. No production code delta in this review range.
|
||||
|
||||
Formal Gitea APPROVE on #1804 and queue record wait on the row 291 hold release
|
||||
and Sage's go. Sage's touched-package and every scripts/test-*.sh gate remains
|
||||
required. Boyd has released lane B and has no running test processes.
|
||||
|
||||
## Rebase after row 239 (sage-3794)
|
||||
|
||||
Current branch: `boyd/row298-r2`, rebased from published row 298 tip
|
||||
`2fc27bce6bfac2debe9703782bf308132c26b393` onto
|
||||
`833ec1cc8d7c11842efb82e57c1a0b256c8d22d7`, which includes row 239 through
|
||||
`e0cb1db2c`. Original `boyd/row298` remains intact locally and remotely. A new
|
||||
Boyd branch carries the rebased candidate so no published history is amended
|
||||
or force-pushed. This section supersedes the original base/branch header for
|
||||
current candidate work.
|
||||
|
||||
Conflicts: only the first Boyd log append in `BUILD-LOG.md` and
|
||||
`docs/SESSIONS.md` conflicted while replaying 574ece13c. For each, verified the
|
||||
old commit's log started with the merge base bytes, then retained the new
|
||||
base's complete log bytes and appended only Boyd's suffix (434 and 281 bytes
|
||||
respectively). Later Boyd appends replayed automatically. No prior entries
|
||||
were removed or rewritten. Five commits replayed; the third/fourth range-diff
|
||||
entries differ only in append-log context; package-only range-diff is all `=`.
|
||||
|
||||
The overlapping product files merged automatically: broker.mjs, runtime.mjs
|
||||
and instance-settings.mjs had no textual conflicts. Inspected their diff
|
||||
against the new base: only the existing row 298 scope additions remain.
|
||||
Preserved row 239's injected writer, fire/delete beforeWrite callback, and
|
||||
commit() passing beforeWrite into writer.write. There is no pre-lock revoke
|
||||
call in commit(). `packages/business/src/organization.mjs` is byte-identical
|
||||
to the new base: beforeWrite still runs inside withLock, after revision and
|
||||
validation checks; post-revoke errors still carry revoked true. Broker's
|
||||
row-239 error handling and runtime's lock/revoke wiring remain intact.
|
||||
|
||||
Static checks only: package patch range-diff all equal, organization writer
|
||||
unchanged, diff whitespace clean, refreshed 13-file package manifest verifies.
|
||||
The only pre-existing row-298 test change remains project-runtime.test.mjs
|
||||
exactly 1/1 under mos-1510. No test files were changed for this rebase.
|
||||
UNTESTED on the new base: no Node, tests, mutants, builds or queue writes run.
|
||||
Earlier 29/0 and M1-M10 results apply to the old base only. Await Sage CLEAR
|
||||
for whole touched-file reruns; Dewey reviews the new candidate.
|
||||
|
||||
Row 321 (#1817) review assignment acknowledged; waiting for Sage's request
|
||||
and Gladstone's pushed docs-only F7/F10 run-sheet candidate.
|
||||
@@ -0,0 +1,13 @@
|
||||
612488b705853e75e6f8952e41552526f4d07dd297d7619d608d434ed75121d6 packages/bus/README.md
|
||||
63fdf6c048f0994af3551510119eeb294c71f8116702772844214930b827cc56 packages/bus/src/broker.mjs
|
||||
65425ead353017c99e04f1e0bcc3f85c4ba60054da293badaa0360e681e418dc packages/bus/src/runtime.mjs
|
||||
e7065db949cacfd4679a1031eebf5cde09ed196ce85612e8400ae563f02a0f18 packages/bus/tests/grant-scope-runtime.test.mjs
|
||||
4030cf584481a1db12d9e6f744015c48fdebd468335d065ead3e90180858d366 packages/bus/tests/grant-scope.test.mjs
|
||||
bc432d8d430433b9d84c00883584dc53d4b27afb5e93f739fe3ca28e80f01efa packages/bus/tests/project-runtime.test.mjs
|
||||
84d6dd15d5ca2926d8f7a34548c70c3e7a3c58e692f0b009414f1af35cb096cc packages/business/src/business.mjs
|
||||
e3b425f9ad2a0d4e6e0b55bd48827e144015662d95ace4163fe6474e5f98da81 packages/business/src/instance-settings.mjs
|
||||
fed4177325f65a2b9823a40a9c35c01d60c0520e3ff40b4574c3cf2c29cc096b packages/business/src/vocabulary.mjs
|
||||
b7f96e45b9ffca2a7ebc18dc446f0adcaac72d3900f098c60157cf6cbf1bfc13 packages/business/tests/grant-scope.test.mjs
|
||||
60fc267b7f8f9e83eeb0d0207c8abdef68d554f32b08d508e98dbf59b442a366 packages/cli/README.md
|
||||
b586907ca8f351f60dc19ec2683df75d2d70ddb12955d09fb14560c9d3bed3dc packages/cli/src/grants.mjs
|
||||
fb7c30b31facd887b03ff02dd6734d48a815978481b33bb21ec48604a51c5ff5 packages/cli/tests/grants-scope.test.mjs
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Row 298 mutants. Run only with Sage's explicit CLEAR in Boyd's slot.
|
||||
|
||||
Each mutation is applied to this worktree, runs one whole new test file,
|
||||
then restores the exact original bytes, including after an interruption.
|
||||
Logs go to a caller-supplied scratch directory, never the repository root.
|
||||
"""
|
||||
import argparse
|
||||
import datetime
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[4]
|
||||
BROKER = ROOT / 'packages/bus/src/broker.mjs'
|
||||
VOCABULARY = ROOT / 'packages/business/src/vocabulary.mjs'
|
||||
BUS_TEST = 'packages/bus/tests/grant-scope.test.mjs'
|
||||
BUSINESS_TEST = 'packages/business/tests/grant-scope.test.mjs'
|
||||
|
||||
|
||||
def replace_once(source, old, new):
|
||||
if source.count(old) != 1:
|
||||
raise RuntimeError(f'expected exactly one mutant site, found {source.count(old)}')
|
||||
return source.replace(old, new, 1)
|
||||
|
||||
|
||||
def mutate(name, source):
|
||||
if name == 'M1':
|
||||
return replace_once(source,
|
||||
'if (!raise?.grant || !grantInScope(scope, raise.grant)) return view;',
|
||||
'if (!raise?.grant) return view;')
|
||||
if name == 'M2':
|
||||
return replace_once(source,
|
||||
'return scope === null ? { scope: GRANT_SCOPE_EMPTY, fallback: true } : { scope, fallback: false };',
|
||||
'return { scope: scope ?? value, fallback: false };')
|
||||
if name == 'M3':
|
||||
return replace_once(source,
|
||||
' #grantScopeVerb(s, verb, a) {\n this.#human(s);',
|
||||
' #grantScopeVerb(s, verb, a) {')
|
||||
if name == 'M4':
|
||||
old = """ this.#grantEvent(s.business, {
|
||||
action: 'grant.scope.used', grantDecision: d.id, decisionSeq: seq, method: raise.grant.method,
|
||||
agent: raise.grant.agent, scope, revision, scopeSha256: sha256(JSON.stringify(scope)),
|
||||
}, d.id);"""
|
||||
return replace_once(source, old, ' // MUTANT: scope evidence omitted.')
|
||||
if name == 'M5':
|
||||
return replace_once(source,
|
||||
"evidence.agent === grant.agent && evidence.scopeSha256 === sha256(JSON.stringify(checked.scope)) &&",
|
||||
"evidence.agent === grant.agent &&")
|
||||
if name == 'M6':
|
||||
start = source.index(" checked = this.#store.transaction(() => {\n const consumed = this.#consumeGrant")
|
||||
end = source.index('\n } catch (e) {', start)
|
||||
return source[:start] + " checked = this.#store.transaction(() => this.#giteaRecheck(business, decision, this.#consumeGrant(business, decision, 'gitea')));" + source[end:]
|
||||
if name == 'M7':
|
||||
return replace_once(source,
|
||||
'const same = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);',
|
||||
'const same = (a, b) => a.every((x, i) => x === b[i]);')
|
||||
if name == 'M8':
|
||||
return replace_once(source,
|
||||
" if (this.#closed(decision)?.via === 'scope' && !grantInScope(this.#scopeNow(business).scope, g)) fail('scope-changed');",
|
||||
" // MUTANT: no spend-time scope recheck.")
|
||||
if name == 'M9':
|
||||
return replace_once(source,
|
||||
' if (s.role !== this.#business(s.business).launch?.by) return view;',
|
||||
' // MUTANT: any chief instance can use scope.')
|
||||
if name == 'M10':
|
||||
start = source.index(' const evidence = JSON.parse(row.body);', source.index(' #scopeUsed('))
|
||||
end = source.index('\n }', start)
|
||||
return source[:start] + ' return true;' + source[end:]
|
||||
raise ValueError(name)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--sage-clear', required=True, help='CLEAR message key; required before any Node run')
|
||||
parser.add_argument('--logs', type=pathlib.Path, required=True)
|
||||
parser.add_argument('--mutant', choices=['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'all'], default='all')
|
||||
parser.add_argument('--deadline', default='2026-10-23T19:38:00+00:00')
|
||||
args = parser.parse_args()
|
||||
deadline = datetime.datetime.fromisoformat(args.deadline)
|
||||
args.logs.mkdir(parents=True, exist_ok=True)
|
||||
names = ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10'] if args.mutant == 'all' else [args.mutant]
|
||||
for name in names:
|
||||
remaining = (deadline - datetime.datetime.now(datetime.timezone.utc)).total_seconds()
|
||||
if remaining <= 15:
|
||||
raise RuntimeError('Sage slot deadline reached; no new test run')
|
||||
path, test = (VOCABULARY, BUSINESS_TEST) if name in ['M2', 'M7'] else (BROKER, BUS_TEST)
|
||||
original = path.read_bytes()
|
||||
mutated = mutate(name, original.decode())
|
||||
log = args.logs / f'{name}.log'
|
||||
try:
|
||||
path.write_text(mutated)
|
||||
with log.open('w') as output:
|
||||
result = subprocess.run(['timeout', '-k', '10', str(int(min(180, remaining - 13))), 'node', '--test', '--test-concurrency=1', test],
|
||||
cwd=ROOT, stdout=output, stderr=subprocess.STDOUT,
|
||||
timeout=min(195, remaining - 1), check=False)
|
||||
print(f'{name}: exit={result.returncode} log={log}', flush=True)
|
||||
finally:
|
||||
path.write_bytes(original)
|
||||
# Exit 1 needs human inspection of the failing test names; errors are not kills.
|
||||
if result.returncode == 0:
|
||||
raise RuntimeError(f'{name} survived')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,8 @@
|
||||
# Bubba
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 288 (agent write notices and library revert, handed over from Honker).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 3d6672b5) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `bubba/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,30 @@
|
||||
# Row 239 round 1 verdict: honker/row239-r4 e0cb1db2c (duckworth-0295)
|
||||
|
||||
Request: honker-0206 (in-review round 1, rev 3719). Candidate: e0cb1db2c on refactor 546366f4a.
|
||||
Method: git reads of local objects only. No node, no tests, no fetch, no queue reads.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- **The replay is exact.** `git range-diff 93694c518..5b8492b0f 546366f4a..0cae2dad5` gives four `=`
|
||||
lines. None of the 16 manifest paths changed between 93694c518 and 546366f4a, and
|
||||
`git diff 5b8492b0f 0cae2dad5` over those 16 paths is empty. My r3 read (REBASE-r3-5b8492b0f.md,
|
||||
duckworth-0292) applies unchanged, and so do the 0165 design approval and the 0168 code approval with
|
||||
C1 and C2 met.
|
||||
- **e0cb1db2c is doc only.** It appends 26 lines to agents/honker/work/queue-239/BUILD.md. No test reads
|
||||
that file.
|
||||
- **Pins at e0cb1db2c.** All four 64-hex pins in channel-proof.test.mjs match the files' sha256:
|
||||
human.mjs, human-cli.mjs, transport.mjs and webui bus.mjs (e3f52172…). Upstream changed library
|
||||
src/index.mjs and business src/index.mjs, and no test or script pins them. The only hit in my grep
|
||||
is a basename collision (`cli.mjs` matched `human-cli.mjs`).
|
||||
- **The run, as Honker reported it (sage-3773 CLEAR, on 0cae2dad5. I didn't rerun it):**
|
||||
- The six touched test files ran whole: 65/65, 0 skipped, no first-run red.
|
||||
- Mutants M1-M3g: 9/9 killed as assertion failures in their named tests.
|
||||
- M3f killed T6, which matches my static trace in 0292.
|
||||
- Every restore matched by sha256, and the tree was clean afterwards.
|
||||
Running on the replay rather than r3's base was right, because bus runtime.mjs imports library
|
||||
src/index.mjs and the cli imports business src/index.mjs, and both changed upstream.
|
||||
- **What remains is Sage's.** The full gate runs on the replay at landing (sage-3730). Sage's call.
|
||||
|
||||
## Notes
|
||||
|
||||
- None new. 0168 N1 (endsWith access-revoked) stands as recorded.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Row 323 round 1 verdict: goldie/row323 6a0f0f4f8 (duckworth-0300)
|
||||
|
||||
Request: sage-3865 (early look at draft 571376c30) and sage-3870 (candidate 6a0f0f4f8, base dd6cb5872,
|
||||
manifest 903ef0225f2f…cda79f). Brief: docs/plans/2026-10-23_case1-business-file-brief.md, section "Brief:
|
||||
the Case 1 factory business file".
|
||||
Method: static. Git reads of local objects only. No node, no tests, no fetch, no queue reads.
|
||||
|
||||
## Verdict: APPROVE (static), with one condition
|
||||
|
||||
- **The manifest matches.** MANIFEST.sha256 hashes to 903ef0225f2f…cda79f, and both of its blobs match at
|
||||
6a0f0f4f8: the JSON 85e6b9ac… and the test aecd1c0f…. Neither file changed between 571376c30 and
|
||||
6a0f0f4f8. The rest of that range is BUILD.md, logs, BUILD-LOG and SESSIONS. dd6cb5872 is an ancestor.
|
||||
No existing test or source file changed.
|
||||
- **Fields against the brief and the cited rulings:**
|
||||
- id `mosaic-stack` (DESIGN section 7). `vars.bus.tester` true. There is no `library` key and no
|
||||
library project.
|
||||
- human `jason`. I can't read mos-1554 myself, so this rests on Sage's relay in sage-3823. BUILD.md is
|
||||
explicit that this is the only literal value that comes from mos-1554.
|
||||
- The project root /srv/mosaic/src/mosaic-stack is under the DESIGN home /srv/mosaic, and the value
|
||||
comes from sage-3822. It passes the normalized-absolute check at business.mjs:274. A missing project
|
||||
file there only gives a warning in validate (cli.mjs:73-77), so the path doesn't need to exist at
|
||||
install.
|
||||
- Roles are chief (chief-of-staff) and assistant. The assistant is needed because checkLaunch requires
|
||||
`launch.instances` to be nonempty and to exclude the launcher (business.mjs:186-190). BUILD.md names
|
||||
that check, as the brief asks. Both arbiters are chief, under sage-3820. launch.by is chief, which
|
||||
F7 (case1-accept.sh:545-559) reads.
|
||||
- Claude only. `plugins.harness` is [claude-code] and both roles use harness claude-code. There is no
|
||||
pi.
|
||||
- No tracker, no credentials, no library, and no credential value or reference anywhere in the file.
|
||||
Q247 covers the tracker by omission.
|
||||
- **The model, provider and effort have sources.**
|
||||
- The model claude-opus-5-5 is Sage's ruling in sage-3822.
|
||||
- The provider mapping anthropic/claude-opus-5-5 matches packages/business/README.md:185.
|
||||
- `thinking: high` is one of the claude-code LEVELS (index.mjs:28) and matches the chief convention at
|
||||
session-launch.test.mjs:83.
|
||||
- With no `launch.limits`, there is no ceiling: limitHit (launch.mjs:137-155) checks only the limits
|
||||
that are defined. That is a valid choice, and N1 records it.
|
||||
- **The tests read the shipped bytes.**
|
||||
- DOCUMENT is `REPO/docs/guides/case1-business.mosaic-stack.json`. Case 1 writes those exact bytes,
|
||||
loads them through loadBusiness, checks the bytes didn't change, and then asserts every field.
|
||||
- The validate, install, boot and refusal cases copy the document and change only the project root,
|
||||
under sage-3820. Since an absent root only warns, the copy differs from the shipped file only in a
|
||||
way validate doesn't refuse.
|
||||
- The brief's three tests are all there:
|
||||
- (1) The loader case.
|
||||
- (2) The real case2-install.sh factory profile with `--business-file`, with only stack-copy stubbed.
|
||||
- (3) `bus-tester-off`, the literal library key through the final validate (sage-3820), and the
|
||||
library project through `profile-library-business`.
|
||||
- The boot case also covers the 324 boundary: bootConfig gives no trackers, credentials.status() is
|
||||
[], and the reader's credentials are [].
|
||||
- **The tester-switch mutant is a real kill target.**
|
||||
- mutant.py asserts that the needle `"bus.tester": true` occurs exactly once, flips only that value,
|
||||
and restores in a finally block with a byte-equality check.
|
||||
- The value is asserted at test :66. mutant.log shows `not ok 1`, `false !== true`, `# fail 1`, and the
|
||||
restored sha256 85e6b9ac… equals the manifest.
|
||||
- The flip would also make the install case at :95 fail, because the install refuses with
|
||||
bus-tester-off.
|
||||
- **The run, as Goldie reported it (sage-3869 CLEAR, 31-byte TMPDIRs, on 571376c30). I didn't rerun it.**
|
||||
- The new file passed 7/7, the business suite 261/261, and the restored file 7/7 again.
|
||||
- The mutant was killed.
|
||||
- The earlier revoke-window red is the tools.sock length limit (session.mjs:278, dda2d07b4). It fails
|
||||
the same way on the bare base dd6cb5872, so it isn't this row's.
|
||||
|
||||
## Condition
|
||||
|
||||
- C1. Under mos-1561, the cli suite counts only from Sage's full gate on 6a0f0f4f8. This approval
|
||||
depends on that gate passing with no new failures.
|
||||
|
||||
## Notes
|
||||
|
||||
- N1 (doc). `launch` has no `limits`, so the business has no session ceiling. If Mos wants a cap on .53,
|
||||
it's a later launch-settings edit and doesn't need a new file.
|
||||
- N2 (doc). An install on .53 with no project file at /srv/mosaic/src/mosaic-stack still passes. It
|
||||
warns "no project variables" (cli.mjs:76), which the run sheet could mention.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Row 324 round 1 verdict: goldie/row324-r2 dbdfcfd52 (duckworth-0298)
|
||||
|
||||
Request: sage-3853 (#1820, comment 31469, round 1, rev 3768/3769). Candidate: dbdfcfd52 on refactor
|
||||
3569bf54f, code commit 7e59ba8b3. Brief: docs/plans/2026-10-23_optional-tracker-brief.md.
|
||||
Method: static. Git reads of local objects only. No node, no tests, no fetch, no queue reads.
|
||||
|
||||
## Verdict: APPROVE (static)
|
||||
|
||||
- **The manifest matches.** agents/goldie/work/queue-324/MANIFEST.sha256 hashes to 52874356…2b473d,
|
||||
the digest Sage gave. All 8 listed blobs match the files at dbdfcfd52.
|
||||
- **The loader change is narrow.**
|
||||
- business.mjs:239 drops "tracker" from the required-key loop.
|
||||
- business.mjs:296 is `Object.hasOwn(document, "tracker") ? checkTracker(document.tracker, file) : null`.
|
||||
Omission gives `tracker: null`. A present key always goes through checkTracker.
|
||||
- business.mjs:298 guards the role tracker comparison on `tracker &&`. An absent business tracker
|
||||
doesn't waive the role pm tracker check, and the new test covers that.
|
||||
- **A present null still refuses.** checkTracker (business.mjs:85) calls requireObject first, so a
|
||||
present null, false, [] or a non-object refuses. {} and {sync:{}} refuse further in. The new
|
||||
refusal test covers each of them.
|
||||
- **Every business.tracker reader handles absence.** I read each hit in Goldie's reader-audit.txt and
|
||||
the two outside it in scripts:
|
||||
- bus runtime.mjs:131 registers `<business>/@sync` only under `b.tracker?.sync?.credentials`. An
|
||||
absent tracker registers nothing.
|
||||
- bus runtime.mjs:247 hashes `b.tracker?.sync ?? null`.
|
||||
- broker.mjs:6242 reads `b.tracker?.sync?.bot`. Broker 3969 and 4497 read the role-level tracker
|
||||
only.
|
||||
- cli.mjs:85 and webui reads.mjs:166 use `tracker?.sync?.credentials ?? {}`. The
|
||||
`b.tracker.sync.bot` inside the reads.mjs loop runs only when there are entries.
|
||||
- cli config.mjs:58 trackerFor returns null on an absent tracker before the vars check. bootConfig
|
||||
(config.mjs:138-139) sets config.trackers only for a non-null result.
|
||||
- tasks/adapter.mjs loads only for configured trackers (process.mjs:136), and its settings refuse
|
||||
with tracker-config at :25 before the :35 and :81 reads.
|
||||
- instance-settings.mjs:127 reads `old.tracker?.sync`.
|
||||
- stack-release.sh:2074 and :2120 use `b.tracker?.sync?.credentials`.
|
||||
- launch-settings, resolve, instances.mjs and public/bus.js:785 read role-level trackers only.
|
||||
conversation/controller exec.tracker is a different object.
|
||||
- **Writers don't serialize the loader's null.** The setters build `next` from the raw file bytes
|
||||
(organization.mjs:88-93), not from the loaded object, and validate before writing
|
||||
(organization.mjs:201, :208). A business file without a tracker stays without one.
|
||||
- **The brief's tests are there.**
|
||||
- business/tests/optional-tracker.test.mjs has 5 tests: omission loads null, CLI validate with the
|
||||
sync file gone, a present tracker preserved and frozen, the refusal set, and the role pm check.
|
||||
- cli/tests/optional-tracker.test.mjs has 4 tests: no trackers from bootConfig with tracker vars
|
||||
set, startBroker with no @sync credential, businessView with no credentials, and a present
|
||||
tracker with a missing token refusing credential-file.
|
||||
- The existing-tracker case runs through business.test.mjs unchanged apart from :98.
|
||||
- **The one old-test edit is in bounds.** business.test.mjs:98 drops "tracker" from the
|
||||
required-key loop, as mos-1556 allows.
|
||||
- **The run, as Goldie reported it (sage-3850 CLEAR, I didn't rerun it):** 5/5, 4/4 and
|
||||
business.test 21/21. The presence mutant at business.mjs:296 is killed, and the restore matched
|
||||
sha256 e76db421.
|
||||
|
||||
## Notes
|
||||
|
||||
- N1 (doc). The 'undefined' refusal case only exists in memory. A JSON file can't carry undefined,
|
||||
so on disk it is the same as omission. The test is still correct.
|
||||
@@ -0,0 +1,8 @@
|
||||
# Fethry
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 225 (C2-10b, handed over from Morgana).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 14f80e3c) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `fethry/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,82 @@
|
||||
# Row 305 (#1812, Vikunja 591): shell suites remove the compose network they create
|
||||
|
||||
Owner Gizmoduck. Reviewer Filbert. Gate owner Sage. Brief:
|
||||
docs/plans/2026-10-23_gate-env-rows.md, "Brief: shell suites remove the compose network they create
|
||||
(Vikunja 591)", at c16e943b9. Base c16e943b9. Mos approved the change in mos-1442 point 2. Mos approved
|
||||
the two new checks in mos-1445 under mos-1272, adding the presence half.
|
||||
|
||||
## The leak, as the code has it at c16e943b9
|
||||
|
||||
- `packages/processing-control/src/containers.mjs:16-19`. `containerArgs` builds
|
||||
`docker compose run --rm ...`. `--rm` removes the container. The project's `<project>_default`
|
||||
network stays.
|
||||
- `compose.yaml` has no top-level `name:`, so compose names the project after the checkout
|
||||
directory. Every gate worktree left one network. By 10-23 they had used up Docker's address pools.
|
||||
|
||||
## Does COMPOSE_PROJECT_NAME reach compose? Yes, on every path the two suites take
|
||||
|
||||
- Task path. `scripts/run-task.sh:29` execs `node scripts/mosaic-task.mjs`. Nothing scrubs the env
|
||||
before that: `scripts/common.sh` has no `env -i`, no `unset` and no COMPOSE. `mosaic-task.mjs:326`
|
||||
sets `spawnEnv = { ...process.env }`, `:405` passes `env: spawnEnv` to `runContainer`, and
|
||||
`containers.mjs:25` passes those options to `spawnSync('docker', ...)`.
|
||||
- test-release. `release.sh activate` calls `health_check`, and `health_check` calls `run-task.sh`
|
||||
at `:54`, which is the task path above. `env MOSAIC_MOCK_RESPONSE=...` adds to the env and keeps
|
||||
the rest. The suite calls only `status`, `activate` and `rollback`, so `release.sh:122`
|
||||
`compose build` is off its path.
|
||||
- test-task seat cases (`:240`, `:315-351`). `agent.sh:209` execs
|
||||
`packages/processing-control/src/container-cli.mjs`, and `container-cli.mjs:16` calls
|
||||
`runContainer` with `env: process.env`.
|
||||
- `build.sh:13`, `release.sh:122` and `verify.sh:40` also run compose. Neither suite calls them.
|
||||
|
||||
## Change (scripts/test-release.sh, scripts/test-task.sh only)
|
||||
|
||||
- After `SANDBOX="$(mktemp -d)"`, export
|
||||
`COMPOSE_PROJECT_NAME=mosaic-test-<release|task>-<id>`. `<id>` is the sandbox name after its last
|
||||
dot, lowercased, alphanumeric only.
|
||||
- `remove_compose_network` runs `docker network rm "${COMPOSE_PROJECT_NAME}_default"`:
|
||||
- If there's no docker binary, it returns 0.
|
||||
- If the error says `not found` or `No such network`, it returns 0.
|
||||
- On any other failure it prints one stderr line naming the network, then returns 0. That
|
||||
includes a daemon that is down. Sage kept that line in sage-3367 (a).
|
||||
- Each existing EXIT trap keeps everything it did and gains `; remove_compose_network` at the end.
|
||||
The trap's own exit status doesn't change the script's.
|
||||
- One new check per suite, just before the summary. That is after the last Docker step whether or
|
||||
not the live cases ran. Both halves sit inside a `docker info` branch:
|
||||
- the network is present under the run's project name before the explicit
|
||||
`remove_compose_network` (this proves the export reached compose);
|
||||
- after the remove, it is absent.
|
||||
|
||||
A failing `docker network ls` fails the check. Without the daemon, the check prints a skip line.
|
||||
- No `docker compose down`, and no other network is touched. No product file changes.
|
||||
|
||||
Commits: 541dee2b7 (draft), 3bce852d5 (presence half, mos-1445).
|
||||
|
||||
## Gate (Sage's slot, in a gap)
|
||||
|
||||
- Before the first run, run `docker network rm` on a name that was never created and record the
|
||||
daemon's exact wording (sage-3367 b). If the wording matches neither pattern, stop and report to
|
||||
Sage.
|
||||
- Each suite runs whole from a gate worktree, under `timeout -k 10 900`. That is far more than
|
||||
3x the row 302 gate's walls: test-release 3 s, test-task 16 s (mos-1275).
|
||||
- TMPDIR comes from `mktemp -d /mnt/storage/scratch/tmp/gXXX`.
|
||||
- Before and after each run, record `docker network ls` filtered to `mosaic-test-`. The run's
|
||||
network must be absent after.
|
||||
- Grep `^skip` and name every skip.
|
||||
- A run killed by the timeout's SIGKILL skips the EXIT trap and leaks its one network. The name is
|
||||
per-run under `mosaic-test-`, so the gate's after-listing finds it and removes it by its exact name
|
||||
(Filbert N1; no code change).
|
||||
- If `docker info` works but there is no network to find, that's a first-run red. It goes to Sage
|
||||
for Mos, unfixed (mos-1445).
|
||||
|
||||
## Mutants (mos-1445)
|
||||
|
||||
- (a) Drop the export. The new check must go red, because compose uses the directory's project and
|
||||
nothing exists under the run's name.
|
||||
- (b) Make the function's `docker network rm` a no-op. The new check must go red, because the
|
||||
network is still present after the remove.
|
||||
- (c) Drop `remove_compose_network` from the trap. This one is a known survivor: the explicit call
|
||||
covers the normal path, and the trap covers early exits.
|
||||
|
||||
Each mutant runs on an isolated copy of the file. Restores are checked by sha256. After each mutant, the network it
|
||||
leaves is removed by its exact name (mos-1438). Mutant (a) leaves the worktree directory's
|
||||
`<dir>_default`, and mutant (b) leaves the run's `${COMPOSE_PROJECT_NAME}_default`.
|
||||
@@ -0,0 +1,88 @@
|
||||
start 2026-10-23T11:14:01Z HEAD 8ddba89b7 refactor b99ca86b9 docker 29.7.2
|
||||
== wording
|
||||
rc=1 stderr: Error response from daemon: network mosaic-test-absent-46829331_default not found
|
||||
exit status 1
|
||||
== green
|
||||
TMPDIR /mnt/storage/scratch/tmp/gIj4
|
||||
nets[before test-task]:
|
||||
test-task rc=0 wall 16 s :: selftest: 92 passed, 0 failed
|
||||
OK compose network made under the run's project and removed after the run
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
nets[after test-task]:
|
||||
TMPDIR /mnt/storage/scratch/tmp/gDYF
|
||||
nets[before test-release]:
|
||||
test-release rc=0 wall 4 s :: selftest: 15 passed, 0 failed
|
||||
OK compose network made under the run's project and removed after the run
|
||||
nets[after test-release]:
|
||||
== mutants
|
||||
Ma scripts/test-task.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/g6SN
|
||||
nets[before Ma-task]:
|
||||
Ma-task rc=1 wall 16 s :: selftest: 91 passed, 1 failed
|
||||
FAIL compose network made under the run's project and removed after the run
|
||||
red: FAIL compose network made under the run's project and removed after the run
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
nets[after Ma-task]: gizmo-305_default
|
||||
restore ok scripts/test-task.sh
|
||||
Ma-task KILLED
|
||||
cleaned gizmo-305_default
|
||||
nets[after cleanup Ma-task]:
|
||||
Ma scripts/test-release.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/g48Q
|
||||
nets[before Ma-release]:
|
||||
Ma-release rc=1 wall 3 s :: selftest: 14 passed, 1 failed
|
||||
FAIL compose network made under the run's project and removed after the run
|
||||
red: FAIL compose network made under the run's project and removed after the run
|
||||
nets[after Ma-release]: gizmo-305_default
|
||||
restore ok scripts/test-release.sh
|
||||
Ma-release KILLED
|
||||
cleaned gizmo-305_default
|
||||
nets[after cleanup Ma-release]:
|
||||
Mb scripts/test-task.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/gcm1
|
||||
nets[before Mb-task]:
|
||||
Mb-task rc=1 wall 15 s :: selftest: 91 passed, 1 failed
|
||||
FAIL compose network made under the run's project and removed after the run
|
||||
red: FAIL compose network made under the run's project and removed after the run
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
nets[after Mb-task]: mosaic-test-task-pf33rz8tic_default
|
||||
restore ok scripts/test-task.sh
|
||||
Mb-task KILLED
|
||||
cleaned mosaic-test-task-pf33rz8tic_default
|
||||
nets[after cleanup Mb-task]:
|
||||
Mb scripts/test-release.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/gSxg
|
||||
nets[before Mb-release]:
|
||||
Mb-release rc=1 wall 3 s :: selftest: 14 passed, 1 failed
|
||||
FAIL compose network made under the run's project and removed after the run
|
||||
red: FAIL compose network made under the run's project and removed after the run
|
||||
nets[after Mb-release]: mosaic-test-release-db4p70csnf_default
|
||||
restore ok scripts/test-release.sh
|
||||
Mb-release KILLED
|
||||
cleaned mosaic-test-release-db4p70csnf_default
|
||||
nets[after cleanup Mb-release]:
|
||||
Mc scripts/test-task.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/g6WJ
|
||||
nets[before Mc-task]:
|
||||
Mc-task rc=0 wall 16 s :: selftest: 92 passed, 0 failed
|
||||
OK compose network made under the run's project and removed after the run
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
skip: skip live provider cases (row 244: no provider call in the gate)
|
||||
nets[after Mc-task]:
|
||||
restore ok scripts/test-task.sh
|
||||
Mc-task survived (known)
|
||||
nets[after cleanup Mc-task]:
|
||||
Mc scripts/test-release.sh | 2 +-
|
||||
TMPDIR /mnt/storage/scratch/tmp/g2jz
|
||||
nets[before Mc-release]:
|
||||
Mc-release rc=0 wall 3 s :: selftest: 15 passed, 0 failed
|
||||
OK compose network made under the run's project and removed after the run
|
||||
nets[after Mc-release]:
|
||||
restore ok scripts/test-release.sh
|
||||
Mc-release survived (known)
|
||||
nets[after cleanup Mc-release]:
|
||||
tree clean after mutants
|
||||
end 2026-10-23T11:15:17Z
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 305 gate (sage-3374): wording, both suites whole, mutants a/b/c with exact-name cleanup.
|
||||
set -uo pipefail
|
||||
WT=/mnt/storage/scratch/gizmo-305
|
||||
OUT=/mnt/storage/scratch/gizmo-305-out/gate
|
||||
mkdir -p "$OUT"
|
||||
cd "$WT" || exit 9
|
||||
[ "$(git rev-parse HEAD)" = 8ddba89b7442f09fc71cf24d7f250e40a7f60fdf ] || { echo "HEAD drift"; exit 9; }
|
||||
[ -z "$(git status --porcelain)" ] || { echo "dirty tree"; exit 9; }
|
||||
[ -L node_modules ] || ln -s /mnt/storage/src/mosaic-stack/node_modules node_modules
|
||||
[ -L node_modules ] || { echo "no node_modules link"; exit 8; }
|
||||
DIRNET=gizmo-305_default
|
||||
nets() { echo " nets[$1]: $(docker network ls --format '{{.Name}}' | grep -E '^(mosaic-test-|gizmo-305_)' | tr '\n' ' ')"; }
|
||||
newtmp() { export TMPDIR="$(mktemp -d /mnt/storage/scratch/tmp/gXXX)"; echo " TMPDIR $TMPDIR"; }
|
||||
run() { # name script
|
||||
local name=$1 s=$2; newtmp; nets "before $name"
|
||||
local t0=$SECONDS
|
||||
timeout -k 10 900 "$s" > "$OUT/$name.log" 2>&1; local rc=$?
|
||||
echo "$name rc=$rc wall $((SECONDS-t0)) s :: $(grep -E '^selftest:' "$OUT/$name.log")"
|
||||
[ $rc -eq 124 ] || [ $rc -eq 137 ] && echo "SHELL-PHASE TIMEOUT (mos-1275): to Mos unplaced"
|
||||
grep -E 'compose network' "$OUT/$name.log" | sed 's/^/ /'
|
||||
grep -E '^FAIL|could not remove' "$OUT/$name.log" | sed 's/^/ red: /'
|
||||
grep -E '^skip' "$OUT/$name.log" | sed 's/^/ skip: /'
|
||||
nets "after $name"
|
||||
LASTRC=$rc
|
||||
}
|
||||
echo "start $(date -u +%FT%TZ) HEAD $(git rev-parse --short HEAD) refactor $(git rev-parse --short origin/refactor) docker $(docker version --format '{{.Server.Version}}')"
|
||||
echo "== wording"
|
||||
ABS="mosaic-test-absent-$RANDOM$RANDOM"_default
|
||||
docker network rm "$ABS" >/dev/null 2>"$OUT/absent.txt"; echo " rc=$? stderr: $(cat "$OUT/absent.txt")"
|
||||
grep -qE 'not found|No such network' "$OUT/absent.txt" || { echo "STOP: wording matches neither pattern"; exit 3; }
|
||||
echo "== green"
|
||||
for s in task release; do run "test-$s" "scripts/test-$s.sh"; [ $LASTRC -eq 0 ] || { echo "STOP: red on green run"; exit 4; }; done
|
||||
nets "after green" | grep -qE 'mosaic-test-|gizmo-305_' && { echo "STOP: network left after green"; exit 5; }
|
||||
echo "== mutants"
|
||||
mut() { # tag python-edit expected(red|green)
|
||||
local tag=$1 edit=$2 want=$3
|
||||
for s in task release; do
|
||||
local f="scripts/test-$s.sh" sum; sum=$(sha256sum "$f" | cut -d' ' -f1)
|
||||
cp -p "$f" "$OUT/$s.orig"
|
||||
python3 -B -c "$edit" "$f" || { echo "mutant $tag edit failed"; exit 6; }
|
||||
git diff --stat -- "$f" | head -1 | sed "s/^/ M$tag /"
|
||||
run "M$tag-$s" "$f"
|
||||
cp -p "$OUT/$s.orig" "$f"
|
||||
[ "$(sha256sum "$f" | cut -d' ' -f1)" = "$sum" ] && echo " restore ok $f" || { echo "RESTORE MISMATCH $f"; exit 7; }
|
||||
if [ "$want" = red ]; then grep -q '^FAIL.*compose network' "$OUT/M$tag-$s.log" && echo " M$tag-$s KILLED" || echo " M$tag-$s SURVIVED (unexpected)"
|
||||
else grep -q '^OK.*compose network' "$OUT/M$tag-$s.log" && echo " M$tag-$s survived (known)" || echo " M$tag-$s unexpected red"; fi
|
||||
# exact-name cleanup (mos-1438)
|
||||
for n in $(docker network ls --format '{{.Name}}' | grep -E '^(mosaic-test-(task|release)-[a-z0-9]+_default|gizmo-305_default)$'); do
|
||||
docker network rm "$n" >/dev/null && echo " cleaned $n"
|
||||
done
|
||||
nets "after cleanup M$tag-$s"
|
||||
done
|
||||
}
|
||||
E='import sys;p=sys.argv[1];s=open(p).read();o=%s;n=%s;assert s.count(o)==1;open(p,"w").write(s.replace(o,n))'
|
||||
mut a "$(printf "$E" "'export COMPOSE_PROJECT_NAME='" "'COMPOSE_PROJECT_NAME='")" red
|
||||
mut b "$(printf "$E" "'err=\"\$(docker network rm \"\$net\" 2>&1 >/dev/null)\" && return 0'" "'err=\"\" && return 0'")" red
|
||||
mut c "$(printf "$E" "'; remove_compose_network'\"'\"' EXIT'" "''\"'\"' EXIT'")" green
|
||||
[ -z "$(git status --porcelain | grep -v '^?? node_modules$')" ] && echo "tree clean after mutants" || { echo "TREE DIRTY"; git status --porcelain; }
|
||||
echo "end $(date -u +%FT%TZ)"
|
||||
@@ -0,0 +1,180 @@
|
||||
# Row 307 (#1814, Vikunja 598): move row 270's pi compaction reading behind the harness kind
|
||||
|
||||
Owner Gizmoduck. Reviewer Duckworth. Gate owner Sage. Brief:
|
||||
docs/plans/2026-10-23_harness-kind-compactions.md. Base 1e629c892. Design: DESIGN.md beside this file,
|
||||
approved (static) by Duckworth on 67daf6403 (duckworth-0280), with C1, C2, N1 and N2 folded in at
|
||||
cb048b93c. Mos ruled go on the four old-test edits in mos-1459, with the bounds below.
|
||||
|
||||
## Commits
|
||||
|
||||
1. 1f6db1589, red first. The two new test files and capture.mjs. Its product files are base's:
|
||||
`git diff --stat 1e629c892 1f6db1589` lists only this packet, capture.mjs and the two tests.
|
||||
2. 44104adb1, the build. The product files and the four old-test edits.
|
||||
|
||||
## What the build does
|
||||
|
||||
- `packages/plugins/src/kinds.mjs:52-57`: the harness kind's exports end in `"compactions"`, and the
|
||||
comment gains its contract line. `interfaceVersion` stays 1 (DESIGN.md "interfaceVersion stays 1").
|
||||
- New `packages/plugins/harness/pi/compactions.mjs`: base's `scanPi`, `readRange`, `digest`, `STREAM`,
|
||||
`TAIL`, the chunk size, `PI_VERSION` and `claudeHistory`, unchanged in behaviour, plus the export.
|
||||
`scanPi` is no longer exported and takes no defaults: its one caller passes all four arguments.
|
||||
- `pi/index.mjs` re-exports it. `claude-code/index.mjs` answers harness-unsupported with version null.
|
||||
- `packages/seat/src/compactions.mjs`: keeps the ledger. `readLedger` sets `unsupported` on
|
||||
`r.reason === "harness-unsupported"` only (:113). `checkAnswer` (:147-164) refuses an answer out of
|
||||
contract. `collectCompactions(store, { name, compactions }, opts)` (:174-205) calls the export, checks
|
||||
it, and writes the same lines in the same order as base.
|
||||
- `packages/cli/src/launcher.mjs`: the `compactions` closure takes the loaded module and calls the
|
||||
export directly, with the N1 comment on why it skips `call()`. Both call sites pass `harness`.
|
||||
- `packages/plugins/README.md`: the Harness section gains the `compactions` bullet.
|
||||
|
||||
## Old-test edits (mos-1459 bounds)
|
||||
|
||||
| File | Edit | Bound |
|
||||
| --- | --- | --- |
|
||||
| plugins/tests/kinds.test.mjs | `"compactions"` appended to the pinned list | :17-21, append only |
|
||||
| seat/tests/compactions.test.mjs | two module imports; `collect` passes `{ name, compactions }` | :11/:21, helper only |
|
||||
| seat/tests/compactions-budget.test.mjs | pi import; `collect` passes `{ name, compactions }` | :13/:24-25, helper only |
|
||||
| cli/tests/compactions-launch.test.mjs | pi import; base :344's call (now :345) passes `{ name: "pi", compactions: pi }` | call only, `.count === 1` kept |
|
||||
|
||||
No call or expected value in those files changed. harness.test.mjs, cli helpers.mjs:133-151,
|
||||
launch-routes.test.mjs:17-23 and launcher.test.mjs:694-697 are unedited.
|
||||
|
||||
## New tests
|
||||
|
||||
- `packages/seat/tests/compactions-ledger.test.mjs`:
|
||||
- FIXTURES: seven ledgers row 270's code writes (known, store-incomplete, store-unreadable,
|
||||
not-collected, harness-unsupported from claude-code, harness-unsupported from Claude history, and
|
||||
torn), each with its streams, projects and steps. Per fixture, test A reads `compactionCount` and
|
||||
test B runs one collection through `{ name: "pi", compactions: pi.compactions }`.
|
||||
- Test B runs over the fixture's own streams, not an empty session as DESIGN.md:189 says. Over an
|
||||
empty session every fixture with a cursor would go store-incomplete (the stream went away), so
|
||||
"the others go known" could not hold. The expected answers: known stays known (1), store-unreadable
|
||||
goes known with 0 (the cursor is at the end), not-collected goes known with 1, and
|
||||
store-incomplete, torn and both harness-unsupported fixtures stay unknown.
|
||||
- The malformed-answer test (17 answers, plus a module that throws, over a fresh store and over the
|
||||
known fixture) and the argument test (exact keys; core passes a copy of the cursors).
|
||||
- `packages/plugins/tests/harness-compactions.test.mjs`: CASES plain, budget (100 bytes), cap (1
|
||||
stream of 2), shrank and header against pi; pi's `first` with and without Claude history;
|
||||
claude-code; and a module re-exporting every pi export but `compactions`, refused `missing-export`.
|
||||
- capture.mjs reads both capture blocks from the test files. It replays each fixture with base's
|
||||
`collectCompactions(store, "<name>", ...)` and compares the ledger byte for byte, and it runs base's
|
||||
`scanPi` per case and compares the answer under base's reason order (:301 at base) and
|
||||
`PI_VERSION`. It prints base's `compactionCount` per fixture: that is the before half.
|
||||
|
||||
## Expected red on 1f6db1589
|
||||
|
||||
- seat compactions-ledger: the seven test A pass (the ledger rules are base's). Test B fails for known,
|
||||
store-unreadable and not-collected: base takes the object as a harness that isn't pi and writes
|
||||
harness-unsupported. Test B passes on base for the other four, whose expected answer is unknown with
|
||||
the same reason. The malformed-answer and argument tests fail: base never calls the module.
|
||||
Correction after the run: test B fails on base for claude-history too, so the reds are known,
|
||||
store-unreadable, not-collected and claude-history. On base the object harness takes :304, which
|
||||
writes `collected` without a scan, so the count stays null. Pi on the tip scans on a later
|
||||
collection, finds c1 and answers 1, still unknown with harness-unsupported. The test is right and
|
||||
this list was wrong (Duckworth agreed, duckworth-0287).
|
||||
- plugins harness-compactions: the five CASES, the `first` test and the claude-code test fail with a
|
||||
TypeError (`compactions` isn't exported on base). The missing-export test fails: base's loader loads
|
||||
the module.
|
||||
- harness.test.mjs:192 is already red on base. That's the brief's starting point.
|
||||
|
||||
## Run plan (mos-1275, `timeout -k 10`)
|
||||
|
||||
All on a CLEAR from Sage. TMPDIR comes from `mktemp -d /mnt/storage/scratch/tmp/gXXX`. Gate worktrees
|
||||
under /mnt/storage/scratch, node_modules linked right after `git worktree add`, pipefail and tee.
|
||||
|
||||
| Run | Longest recorded | LIMIT |
|
||||
| --- | --- | --- |
|
||||
| capture.mjs on a 1f6db1589 worktree (base and candidate both it) | none | 120 s |
|
||||
| red first: the two new files on 1f6db1589, each whole | none (seat suite 7 s) | 300 s |
|
||||
| the touched test files whole on 44104adb1, one at a time | compactions-launch alone: none; other files 15 s | 600 s |
|
||||
| bus suite | 25 s (270 r5) | 900 s |
|
||||
| cli suite | 682 s (270 r5) | 2100 s |
|
||||
| plugins suite | none recorded | 900 s |
|
||||
| webui suite | 58.2 s (294) | 600 s |
|
||||
| business suite | none recorded | 900 s |
|
||||
| seat suite whole, `node --test tests/` (Duckworth C1) | 7 s (270 r5) | 600 s |
|
||||
| each scripts/test-*.sh | 42 s (test-foundation) | 900 s |
|
||||
| each mutant, its kill files whole | 1 s (264a probes) | 300 s |
|
||||
|
||||
- In the cli run, compactions-launch's T3 tests (base :206, and :305 with the :344 edit; :207, :306
|
||||
and :345 after the import line) must show as run, not skipped on `sandboxProblem()` (base :36, now
|
||||
:37). The report names every skip (Duckworth C2).
|
||||
- Plugins must have zero failures.
|
||||
- A timeout with no failed test is a hang and goes to Sage. A timeout in the shell phase goes to Mos
|
||||
unplaced. A first-run red in a new 307 test is placed and reported after (mos-1272). A red in an old
|
||||
test goes to Sage, unfixed.
|
||||
|
||||
## Mutants
|
||||
|
||||
Each on an isolated copy of the file, restore checked by sha256, only on a green base.
|
||||
|
||||
| # | Change | Kill files |
|
||||
| --- | --- | --- |
|
||||
| M1 | `readLedger`: `if (r.harness !== "pi" \|\| r.reason === "harness-unsupported")` restored | plugins harness.test.mjs (:192) |
|
||||
| M2 | pi `compactions` drops the `first && claudeHistory(store)` branch | seat compactions-budget.test.mjs (base :86, now :87), plugins harness-compactions.test.mjs |
|
||||
| M3 | `readLedger` never sets `unsupported` | seat compactions-ledger.test.mjs, seat compactions.test.mjs (base :197, now :199) |
|
||||
| M4 | `collectCompactions` skips `checkAnswer` | seat compactions-ledger.test.mjs |
|
||||
|
||||
## Runs
|
||||
|
||||
Sage's CLEARs: sage-3455 for steps 1-3, sage-3465 for steps 4-6. Gate worktrees at
|
||||
/mnt/storage/scratch/g307-base (1f6db1589), g307-tip and g307-mut (1f00d4f91), with node_modules
|
||||
linked. TMPDIR was /mnt/storage/scratch/tmp/gTIK. Outputs went to /mnt/storage/scratch/gizmo-307-out/s1 and s2.
|
||||
|
||||
### Steps 1-3 (13:31:21Z-13:31:36Z, then the capture rerun at 13:32Z)
|
||||
|
||||
- capture.mjs crashed with ENOENT on its first run. Base `sessionStore` (session.mjs:78
|
||||
`privateDirs`, called from :117) makes its directories one level at a time, and :44 passed a
|
||||
per-fixture root that didn't exist yet. The fix is one line before :44,
|
||||
`` mkdirSync(join(root, `f-${name}`)) ``. A copy with that line, run on g307-base as both base and
|
||||
candidate, exited rc 0 and printed `all match`: 7 ledgers byte for byte, 7 base counts and 5 scans.
|
||||
Base counts: known 1 known; store-incomplete, store-unreadable and not-collected null, unknown
|
||||
with their own reason; claude-code and claude-history null, harness-unsupported; torn 1,
|
||||
store-incomplete.
|
||||
- Red first on 1f6db1589, both rc 1. Seat compactions-ledger: 9 pass, 7 fail (the corrected list
|
||||
above, plus the malformed-answer and argument tests). Plugins harness-compactions: 0 pass, 8 fail.
|
||||
Seven fail with `TypeError: pi.compactions is not a function` or `claude.compactions is not a
|
||||
function`. The missing-export test fails because base loads the module.
|
||||
- Touched files whole on 1f00d4f91, all rc 0: compactions-ledger 16/16, harness-compactions 8/8,
|
||||
kinds 5/5, harness 9/9 (:192 green), compactions 20/20, compactions-budget 10/10,
|
||||
compactions-launch 5/5 with 0 skipped.
|
||||
|
||||
### Steps 4-6 (14:00:10Z-14:14:00Z, on 1f00d4f91)
|
||||
|
||||
| Run | rc | Time | Result |
|
||||
| --- | --- | --- | --- |
|
||||
| bus | 0 | 24 s | 890 pass, 0 fail, 0 skipped |
|
||||
| cli | 0 | 631 s | 927 pass, 0 fail, 0 skipped; T3 :207, :306, :345 ran |
|
||||
| plugins | 0 | 0 s | 119 pass, 0 fail, 0 skipped |
|
||||
| webui | 0 | 57 s | 374 pass, 0 fail, 1 skipped (below) |
|
||||
| business | 0 | 2 s | 231 pass, 0 fail, 0 skipped |
|
||||
| seat | 0 | 7 s | 218 pass, 0 fail, 0 skipped |
|
||||
| test-auth | 0 | 0 s | 15 passed, 0 failed |
|
||||
| test-conductor | 0 | 7 s | |
|
||||
| test-config | 0 | 3 s | |
|
||||
| test-discord | 0 | 7 s | |
|
||||
| test-extension-package | 0 | 2 s | |
|
||||
| test-foundation | 0 | 39 s | |
|
||||
| test-land-work | 0 | 12 s | |
|
||||
| test-queue | 0 | 20 s | one skip (below) |
|
||||
| test-release | 0 | 3 s | 15 passed, 0 failed |
|
||||
| test-task | 0 | 14 s | 92 passed, 0 failed, two skips (below) |
|
||||
|
||||
Skips:
|
||||
- webui: "rebuilding from the source gives the same bytes" needs the pinned esbuild. 307 doesn't
|
||||
touch the editor.
|
||||
- test-queue: "queue verify and render --check", because the gate worktree isn't the queue's
|
||||
canonical root.
|
||||
- test-task: "live provider cases" twice (row 244, no provider call in the gate).
|
||||
|
||||
Mutants ran in g307-mut. Each restore matched its sha256, and the tree was clean after M4.
|
||||
|
||||
| # | Killed by |
|
||||
| --- | --- |
|
||||
| M1 | plugins harness.test.mjs:192 |
|
||||
| M2 | seat compactions-budget.test.mjs:87 and :104; plugins harness-compactions.test.mjs:47 |
|
||||
| M3 | seat compactions-ledger.test.mjs:123 (test B, claude-code and claude-history); seat compactions.test.mjs:199 |
|
||||
| M4 | seat compactions-ledger.test.mjs:151 (the malformed-answer test) |
|
||||
|
||||
There were no reds, so nothing was placed under mos-1272. The sweep found no node or chromium left in
|
||||
my cwds.
|
||||
@@ -0,0 +1,241 @@
|
||||
# Row 307 (#1814, Vikunja 598): row 270's pi compaction reading moves behind the harness kind
|
||||
|
||||
Owner Gizmoduck. Reviewer Duckworth. Gate owner Sage. Brief:
|
||||
`docs/plans/2026-10-23_harness-kind-compactions.md` at 1e629c892, which is the base. Mos ruled the
|
||||
move in mos-1455 item 3 and declined an EXCEPT entry.
|
||||
|
||||
## What is red, as the code has it at 1e629c892
|
||||
|
||||
`packages/plugins/tests/harness.test.mjs:192` greps core sources for a quoted harness name. It finds
|
||||
three lines in `packages/seat/src/compactions.mjs`:
|
||||
|
||||
- `:232` in `readLedger`: `if (r.harness !== "pi" || r.reason === "harness-unsupported") unsupported = true;`
|
||||
- `:283` and `:287` in `collectCompactions`: the `harness === "pi"` branches.
|
||||
|
||||
Everything else row 270 put in that file is either harness-free (the ledger, the budget and stream
|
||||
settings, `storeDir`, `compactionCount`) or pi-only (`scanPi`, `PI_VERSION`, `claudeHistory`, and
|
||||
the helpers only `scanPi` uses: `readRange`, `digest`, `STREAM`, `TAIL`).
|
||||
|
||||
## The new export: `compactions`
|
||||
|
||||
`packages/plugins/src/kinds.mjs:54` appends `"compactions"` to the harness kind's exports, so the
|
||||
loader refuses a module without it (`loader.mjs:61-66`, code `missing-export`).
|
||||
|
||||
```
|
||||
compactions({ store, cursors, budget, streams, first })
|
||||
-> { found, cursors, known, reason, version }
|
||||
```
|
||||
|
||||
Arguments, all from core:
|
||||
|
||||
- `store` is `{ session, projects }`, the two native store directories `sessionStore` makes. The
|
||||
module never gets `store.dir`, the directory that holds the ledger.
|
||||
- `cursors` is a copy of the ledger's cursor map, `{ [stream]: { bytes, tail } }`.
|
||||
- `budget` and `streams` are `readBudget(...)` and `readStreams(...)`, as the launcher passes them
|
||||
now (`launcher.mjs:299`, `:305`). Core keeps both readers and their defaults.
|
||||
- `first` is true when the store has no ledger yet (today's `!prior`).
|
||||
|
||||
The answer:
|
||||
|
||||
- `found`: `[{ entry, completedAt, stream }]`, the compactions read past the cursors. `entry` is a
|
||||
non-empty string, `completedAt` a string or null, `stream` a non-empty string.
|
||||
- `cursors`: `{ [stream]: { bytes, tail } }` for each stream the module read. `bytes` is a safe
|
||||
integer of 0 or more and `tail` 64 lowercase hex.
|
||||
- `reason`: null, `store-incomplete`, `store-unreadable`, `not-collected` or `harness-unsupported`.
|
||||
- `known`: a boolean, true exactly when `reason` is null.
|
||||
- `version`: the store format the module read against as a string, or null when it read nothing.
|
||||
|
||||
Core checks the answer before it writes. Anything else throws `harness <name> compactions: bad
|
||||
answer: <what>` and nothing is appended. A throw from the module itself is the same case. Both reach
|
||||
the launcher's existing catch (`launcher.mjs:313-316`), which logs the message and records the run's
|
||||
`compactions` event as unknown with `store-unreadable`, as a scan that throws today does.
|
||||
|
||||
### interfaceVersion stays 1
|
||||
|
||||
- Every harness module lives in this tree, and the loader loads nothing else (decision 143,
|
||||
`loader.mjs:1-5`). Both shipped modules gain the export in the same commit.
|
||||
- A module without the export already refuses at load, as `missing-export` naming `compactions`.
|
||||
That is the brief's third test. A bump would add a second refusal for the same fact.
|
||||
- No existing export changes its arguments or answer. Row 125 moved limit-check to 2 because
|
||||
`mayLaunch`, `mustStop` and `report` changed shape. Nothing like that happens here.
|
||||
- `harness.test.mjs:36` pins `interfaceVersion` 1, and the brief keeps that file unchanged.
|
||||
|
||||
The tradeoff: a module someone wrote against version 1 without `compactions` reports
|
||||
`missing-export`, not `interface-version`. The message names the export, which is the more useful of
|
||||
the two.
|
||||
|
||||
## What moves where
|
||||
|
||||
New file `packages/plugins/harness/pi/compactions.mjs`, imported by `pi/index.mjs`. The loader
|
||||
checks only `index.mjs` itself (`loader.mjs:42-54`), and claude-code's module already imports files
|
||||
outside its directory (`claude-code/index.mjs:11-12`). It takes, unchanged in behaviour:
|
||||
|
||||
- `scanPi`, with `readRange`, `digest`, `STREAM`, `TAIL` and the chunk size it reads by;
|
||||
- `PI_VERSION` ("0.85.1");
|
||||
- `claudeHistory(store)`;
|
||||
- the reason order `["store-incomplete", "store-unreadable", "not-collected"]` from `:301`;
|
||||
- the header comment's Pi paragraphs (`compactions.mjs:12-19`, `:21-24`).
|
||||
|
||||
pi's `compactions`:
|
||||
|
||||
- `first` and `claudeHistory(store)`: answers `{ found: [], cursors: {}, known: false, reason:
|
||||
"harness-unsupported", version: "0.85.1" }`. That is today's `:283-286`.
|
||||
- otherwise: `scanPi(store.session, cursors, budget, streams)`, and answers its `found` and
|
||||
`cursors` with the first reason in the order above, `known` when that's null, and version
|
||||
"0.85.1". That is today's `:287-302` before the dedupe.
|
||||
|
||||
claude-code's `compactions` reads nothing and answers `{ found: [], cursors: {}, known: false,
|
||||
reason: "harness-unsupported", version: null }`. That is today's `:303-304`. Vikunja 466 still has
|
||||
no pinned Claude Code format.
|
||||
|
||||
## Core keeps the ledger
|
||||
|
||||
`packages/seat/src/compactions.mjs` keeps `LEDGER`, `READ_BYTES`, `STREAMS`, `readBudget`,
|
||||
`readStreams`, `storeDir`, `readLedger`, `compactionCount`, the key and the single O_APPEND write.
|
||||
|
||||
`collectCompactions(store, harness, { now, budget, streams })` takes `harness = { name,
|
||||
compactions }`, the name the route used and the loaded module's export. It:
|
||||
|
||||
1. reads the ledger (`prior`), as now;
|
||||
2. calls `harness.compactions({ store: { session: store.session, projects: store.projects },
|
||||
cursors: { ...ledger.cursors }, budget, streams, first: prior === null })` and checks the answer;
|
||||
3. appends, in today's order: one `compaction` line per key the ledger doesn't hold yet, one
|
||||
`cursor` line per stream whose cursor moved, then `{ v: 1, kind: "collected", at, known, reason,
|
||||
harness: name, version }`. These are the same lines and field order as today's `:288-302` and
|
||||
`:304`. `name` is a variable, never a literal;
|
||||
4. returns `compactionCount(store.dir)`.
|
||||
|
||||
`readLedger`'s unsupported test becomes `r.reason === "harness-unsupported"`, a field every answer
|
||||
carries. No core line names a harness. The core comments drop the quoted names too, because the
|
||||
grep reads comments.
|
||||
|
||||
`packages/cli/src/launcher.mjs:309-318`: the `compactions` closure takes the loaded module too and
|
||||
calls `collectCompactions(store, { name: model.harness, compactions: harness.compactions }, {
|
||||
budget, streams })`. Both call sites, `:509` and `:650`, have `harness` in scope (it's
|
||||
`startLocked`'s `let harness` from `:503`; the `exited.then` callback at `:632` closes over it).
|
||||
The closure calls the export directly, not through `call()` (`launcher.mjs:196`), because `call()`
|
||||
turns a throw into a run refusal. The closure gets a comment saying so (Duckworth N1).
|
||||
`bus/src/runtime.mjs:282` and `broker.mjs:885-889` keep calling `compactionCount` and don't change.
|
||||
|
||||
`packages/plugins/README.md`, Harness section, and the comment at `kinds.mjs:35-51` each gain the
|
||||
`compactions` line with the contract above.
|
||||
|
||||
## A ledger row 270 wrote reads the same count
|
||||
|
||||
Row 270's code writes a `collected` line in exactly three places:
|
||||
|
||||
| Line | harness | reason | version | old rule (`:232`) | new rule |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `:286`, Claude history on the first pi collection | `"pi"` | `harness-unsupported` | "0.85.1" | unsupported | unsupported |
|
||||
| `:302`, a pi scan | `"pi"` | null, `store-incomplete`, `store-unreadable` or `not-collected` | "0.85.1" | supported | supported |
|
||||
| `:304`, any other harness name (`claude-code`, or `claude` in the tests) | that name | `harness-unsupported` | null | unsupported | unsupported |
|
||||
|
||||
The two rules agree on every line row 270 can write. The rest of `readLedger` (the `bad`, `torn`,
|
||||
`incomplete` and `events` handling) and all of `compactionCount` don't change. So every ledger row
|
||||
270 wrote reads the same `{ count, known, reason, at }` after the move. Nothing rewrites a ledger;
|
||||
the next collection only appends.
|
||||
|
||||
The rules would differ only on a `collected` line naming a harness other than `"pi"` with a reason
|
||||
other than `harness-unsupported`. Row 270 never writes one, and the launcher is the ledger's only
|
||||
writer (`compactions.mjs:7-10`).
|
||||
|
||||
New collections after the move append the same bytes as before for the same store state. pi's
|
||||
answer is today's scan result, core keeps the dedupe and the line format, and `harness` in the line
|
||||
is still the route's name.
|
||||
|
||||
## Existing tests that change (to Mos through Sage, mos-1272)
|
||||
|
||||
Grepped for `interfaceVersion`, the harness exports, `write("harness"`, `fakeHarnesses` and
|
||||
`collectCompactions(` in `packages/*/tests/*` and `scripts/`.
|
||||
|
||||
Four existing files change:
|
||||
|
||||
1. `packages/plugins/tests/kinds.test.mjs:17-21`: the pinned harness export list gains
|
||||
`"compactions"` at the end.
|
||||
2. `packages/seat/tests/compactions.test.mjs:11` and `:21`: import the two shipped modules. `setup`'s
|
||||
`collect(harness = "pi")` passes `{ name: harness, compactions: MODULES[harness].compactions }`,
|
||||
with `pi` mapped to the pi module and `claude` to claude-code's. The 27 `f.collect()` calls and
|
||||
the two named ones at `:199` and `:201` stay as written.
|
||||
3. `packages/seat/tests/compactions-budget.test.mjs:13` and `:24-25`: the same change in its
|
||||
`collect`. Its calls at `:67-69` and `:129-134` stay as written.
|
||||
4. `packages/cli/tests/compactions-launch.test.mjs:344`: the one direct
|
||||
`collectCompactions(legacy, "pi")` becomes `collectCompactions(legacy, { name: "pi",
|
||||
compactions: pi.compactions })`, with an import of the pi module.
|
||||
|
||||
Fixtures that build a harness module and need no edit, because they re-export a real module and
|
||||
inherit `compactions`:
|
||||
|
||||
- `packages/cli/tests/helpers.mjs:133-151` `fakeHarnesses`, used by `compactions-launch`,
|
||||
`idle-stop-race`, `launcher`, `lifecycle`, `session-clear`, `session-launch`, `thread-launch` and
|
||||
`thread-revoke`;
|
||||
- `packages/cli/tests/launch-routes.test.mjs:17-23`: `down`, `sparse`, `metered` and `throws`
|
||||
re-export pi. `broken` lacks every export but `adapter` on purpose. The loader still refuses it at
|
||||
`levels` first, so `:114` and `:138` (`missing-export`) hold;
|
||||
- `packages/cli/tests/launcher.test.mjs:694-697`: `down` and `fixture` re-export pi.
|
||||
|
||||
`packages/plugins/tests/harness.test.mjs` doesn't change. `:33-37` loops over the kind's exports,
|
||||
which both modules then have, and `:192` goes green.
|
||||
|
||||
No `scripts/test-*.sh` builds a harness module. `test-config.sh:266-280` checks only the two
|
||||
config keys.
|
||||
|
||||
## New tests, red first (mos-1272)
|
||||
|
||||
1. `packages/seat/tests/compactions-ledger.test.mjs`: one fixture ledger per reason row 270 writes:
|
||||
known, `store-incomplete`, `store-unreadable`, `not-collected`, `harness-unsupported` from
|
||||
claude-code, `harness-unsupported` from Claude history, and a torn ledger.
|
||||
- Each is literal JSONL in the test. Before the build, in the slot, a capture script runs base
|
||||
1e629c892's `collectCompactions` with a fixed `now` over the same stores. The literals must
|
||||
equal its output byte for byte. That proves row 270's code wrote them.
|
||||
- `compactionCount` reads each one. I run this half on the base and on the candidate, and record
|
||||
both outputs in BUILD.md as the before and after.
|
||||
- Then one more collection through `{ name, compactions }` over an empty session gives each
|
||||
fixture's expected next answer: unsupported and `store-incomplete` stay, the others go known.
|
||||
This half is red on the base, where core treats the object as a harness that isn't pi.
|
||||
2. `packages/plugins/tests/harness-compactions.test.mjs`:
|
||||
- pi's `compactions` over fixture streams answers what base `scanPi` answers, captured by the same
|
||||
script: a plain count, a budget that stops short (cursor at the last whole line,
|
||||
`not-collected`), more streams than the cap (no cursor for unopened streams, `not-collected`),
|
||||
a stream that shrank under its cursor (`store-incomplete`), a non-session header
|
||||
(`store-unreadable`). Each answer has version "0.85.1".
|
||||
- pi with `first` and a non-empty `projects` answers `harness-unsupported` without scanning.
|
||||
Without `first`, the same store scans.
|
||||
- claude-code answers `harness-unsupported` with version null, over directories that don't exist.
|
||||
- A harness module that re-exports every other pi export by name, without `compactions`, refuses
|
||||
at load: `missing-export`, "doesn't export compactions".
|
||||
3. In the same seat file: core refuses each malformed answer and appends nothing. The cases are
|
||||
`found` not an array, an empty `entry`, a cursor whose `bytes` is negative or `tail` isn't 64
|
||||
hex, `known` true with a reason, an unknown reason, a numeric `version`, and a module that
|
||||
throws. The module gets exactly `{ store: { session, projects }, cursors, budget, streams,
|
||||
first }`.
|
||||
|
||||
## Mutants
|
||||
|
||||
- M1, the brief's first: core branches on a harness name again (`readLedger`'s `r.harness !== "pi"
|
||||
||` restored). Killed by `harness.test.mjs:192`.
|
||||
- M2, the brief's second: pi's module drops the Claude-history check. Killed by
|
||||
`compactions-budget.test.mjs:86` and new test 2's `first` case.
|
||||
- M3, the brief's third: `readLedger` never sets `unsupported`. Killed by new test 1's two
|
||||
`harness-unsupported` fixtures and `compactions.test.mjs:197`.
|
||||
- M4, mine: core skips the answer check. Killed by new test 3.
|
||||
|
||||
## Gate
|
||||
|
||||
Duckworth approves this design, then the manifest. On Sage's CLEAR:
|
||||
|
||||
- the capture script on base 1e629c892 first, then the new tests red on the base;
|
||||
- the bus, cli, plugins (zero failures), webui and business suites, and every `scripts/test-*.sh`;
|
||||
- the seat suite whole (`packages/seat`, `node --test tests/`), where two old-test edits, new
|
||||
tests 1 and 3, and the M2 and M3 kills live (Duckworth C1);
|
||||
- in the cli run, `compactions-launch`'s T3 tests (`:206`, and `:305` with the `:344` edit) must
|
||||
run, not skip on `sandboxProblem()` (`:36`). BUILD.md names every skip (Duckworth C2);
|
||||
- the mutants, each on an isolated copy with its restore checked by sha256.
|
||||
|
||||
Each suite runs under `timeout -k 10` at 3x or more of its longest recorded wall (mos-1275). I'll
|
||||
put the numbers in BUILD.md before the first run. TMPDIR comes from
|
||||
`mktemp -d /mnt/storage/scratch/tmp/gXXX`.
|
||||
|
||||
## Round 1
|
||||
|
||||
Duckworth approved round 1 on 67daf6403 (duckworth-0280). This revision takes C1 and C2 into
|
||||
the gate, adds the N1 comment to the launcher plan, and corrects the N2 line refs (`:302`, `:301`).
|
||||
@@ -0,0 +1,79 @@
|
||||
// Row 307 (#1814): checks the new tests' literals against row 270's code at base 1e629c892.
|
||||
//
|
||||
// node capture.mjs <base checkout at 1e629c892> <candidate checkout>
|
||||
//
|
||||
// Reads the FIXTURES and CASES blocks (between `// capture:begin` and `// capture:end`) from the
|
||||
// candidate's two new test files. Replays each fixture's steps with the base's collectCompactions,
|
||||
// harness named by string as row 270 takes it, with the clock at 12:00:00 and one second a step,
|
||||
// and compares the ledger byte for byte. Prints the base's compactionCount for each. Runs the
|
||||
// base's scanPi over each case and compares the answer the base's reason order gives (:301) with
|
||||
// version PI_VERSION. Exit 0 only when every one matches. Writes only under TMPDIR.
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { appendFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const [base, cand] = process.argv.slice(2).map((p) => p && resolve(p));
|
||||
if (!base || !cand) {
|
||||
console.error("usage: node capture.mjs <base checkout> <candidate checkout>");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
function block(file) {
|
||||
const text = readFileSync(join(cand, file), "utf8");
|
||||
const m = /\n\/\/ capture:begin\nconst [A-Z]+ = (\{[\s\S]*?\});\n\/\/ capture:end\n/.exec(text);
|
||||
if (!m) throw new Error(`${file}: no capture block`);
|
||||
return JSON.parse(m[1]);
|
||||
}
|
||||
|
||||
const FIXTURES = block("packages/seat/tests/compactions-ledger.test.mjs");
|
||||
const CASES = block("packages/plugins/tests/harness-compactions.test.mjs");
|
||||
const seat = await import(pathToFileURL(join(base, "packages/seat/src/compactions.mjs")).href);
|
||||
const { sessionStore } = await import(pathToFileURL(join(base, "packages/seat/src/session.mjs")).href);
|
||||
|
||||
const root = mkdtempSync(join(tmpdir(), "g307-capture-"));
|
||||
let failed = 0;
|
||||
const report = (what, same, detail) => {
|
||||
console.log(`${same ? "MATCH" : "DIFF "} ${what}${detail ? ` ${detail}` : ""}`);
|
||||
if (!same) failed++;
|
||||
};
|
||||
try {
|
||||
for (const [name, fx] of Object.entries(FIXTURES)) {
|
||||
mkdirSync(join(root, `f-${name}`));
|
||||
const store = sessionStore(join(root, `f-${name}`), "acme", "th1", "s1");
|
||||
for (const [file, text] of Object.entries(fx.streams)) writeFileSync(join(store.session, file), text);
|
||||
for (const [file, text] of Object.entries(fx.projects ?? {})) writeFileSync(join(store.projects, file), text);
|
||||
let tick = 0;
|
||||
const now = () => new Date(Date.UTC(2026, 9, 22, 12, 0, tick++)).toISOString();
|
||||
for (const step of fx.steps) {
|
||||
if (step.tear !== undefined) appendFileSync(join(store.dir, seat.LEDGER), step.tear);
|
||||
else seat.collectCompactions(store, step.collect, { now, ...(step.budget === undefined ? {} : { budget: step.budget }) });
|
||||
}
|
||||
const got = readFileSync(join(store.dir, seat.LEDGER), "utf8");
|
||||
const want = fx.ledger.join("\n") + "\n";
|
||||
report(`ledger ${name}`, got === want, got === want ? `(${Buffer.byteLength(got)} bytes)` : `\n--- base wrote\n${got}--- test has\n${want}`);
|
||||
const count = seat.compactionCount(store.dir);
|
||||
report(`base count ${name}`, JSON.stringify(count) === JSON.stringify(fx.count), JSON.stringify(count));
|
||||
}
|
||||
for (const [name, c] of Object.entries(CASES)) {
|
||||
const dir = join(root, `c-${name}`);
|
||||
mkdirSync(dir);
|
||||
for (const [file, text] of Object.entries(c.streams)) writeFileSync(join(dir, file), text);
|
||||
const scan = seat.scanPi(dir, c.cursors, c.budget, c.cap);
|
||||
const reason = ["store-incomplete", "store-unreadable", "not-collected"].find((p) => scan.problems.includes(p)) ?? null;
|
||||
const answer = { found: scan.found, cursors: scan.cursors, known: reason === null, reason, version: seat.PI_VERSION };
|
||||
let same = true;
|
||||
try {
|
||||
assert.deepStrictEqual(answer, c.answer);
|
||||
} catch {
|
||||
same = false;
|
||||
}
|
||||
report(`scan ${name}`, same, JSON.stringify(scan));
|
||||
}
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
console.log(failed ? `${failed} differ` : "all match");
|
||||
process.exit(failed ? 1 : 0);
|
||||
@@ -0,0 +1,9 @@
|
||||
# Gladstone
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos (mos-1531, mos-1532). Owns the Case 1/2 host fixes for the 433 P1 rerun,
|
||||
F1 to F6 (#1816, Vikunja 614).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 433058fc) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `gladstone/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,304 @@
|
||||
# Hostcheck item 5 and bundle verification (Gitea #1816, Vikunja 614)
|
||||
|
||||
Queue row pending from Sage. Owner Gladstone, reviewer Morgana, gate/landing Sage.
|
||||
Base: fetched origin/refactor 93694c5185d0a7f989d2fe78fb620e44599b744e;
|
||||
branch gladstone/hostcheck-item5. Scope: mos-1531 / ops-0256, sage-3738 and sage-3740.
|
||||
|
||||
## Before and after
|
||||
|
||||
Ubuntu 24.04 dpkg 1.22.6ubuntu6.6 prints `root mosaic 750 /usr/bin/bwrap`.
|
||||
Item 5 compared the literal `0750`, falsely refusing a correctly configured host;
|
||||
--enforce would unload the profile, close bwrap and record DRIFT.
|
||||
The candidate accepts exactly four fields, root, mosaic, octal 0750, and $BWRAP.
|
||||
`0*750` represents all octal spellings numerically equal to 0750 without arithmetic
|
||||
conversion/overflow. Horizontal whitespace separates fields; extra fields and
|
||||
embedded newlines refuse. Owner, group and path remain exact. The stat comparison
|
||||
and existing output strings remain unchanged. Only the item 5 comment also changes.
|
||||
|
||||
The two release-transport bundle checks previously ran outside any repository.
|
||||
Each now creates an empty bare scratch repository, verifies with git -C, and removes
|
||||
the scratch directory using an EXIT trap. A failure exits the shell before cloning
|
||||
or fetching. No other run-sheet text changes.
|
||||
|
||||
## Tests and mutants
|
||||
|
||||
Only packages/cli/tests/case1-hostcheck.test.mjs changes among tests, by appending
|
||||
three new test() blocks; the existing passing 0750 fake and failing fake are intact.
|
||||
The pass test exercises 750, 0750 and 000750. The enforce test checks no unload,
|
||||
chown, chmod, DRIFT or hostcheck log for 750. Refusals independently cover 755,
|
||||
0755, 4750, 75, 7500, 075, non-octal strings, other owner/group/path, missing path,
|
||||
extra fields, multiple lines, blank/empty output and a missing fixture file.
|
||||
|
||||
| Mutant | Change | Killer | Result |
|
||||
|---|---|---|---|
|
||||
| M1 | Restore the old literal root mosaic 0750 comparison (drop 750) | new check pass test | Unrun |
|
||||
| M2 | Accept any mode while preserving owner/group/path | new refusal test | Unrun |
|
||||
|
||||
Mutant procedure: preserve script bytes, assert one replacement, run the named
|
||||
killer alone, require failure for its intended assertion, restore in finally and
|
||||
check the original bytes/hash. Run the original targeted file again after restore.
|
||||
|
||||
## Verification status
|
||||
|
||||
Untested. Sage-3738 prohibits all Node until CLEAR because the joint full gate
|
||||
runs alone. Sage-3740 explicitly directs pushing this untested build for Morgana's
|
||||
static review before CLEAR. This supersedes test-first execution for this draft;
|
||||
no Node, tests, node --check, full suites or live-host operations have run.
|
||||
Queue next refused linked-worktree execution; Sage owns queue creation/movement.
|
||||
Push only gladstone/*, with MOSAIC_GITEA_CREDENTIAL_FILE unset; no amend/force.
|
||||
|
||||
Static draft receipt: `git diff --check` passed. Test-file numstat against the
|
||||
base is `46 0 packages/cli/tests/case1-hostcheck.test.mjs` (added/deleted).
|
||||
|
||||
## Scope and implementation amendment (mos-1534, sage-3741)
|
||||
|
||||
F1 correction: Sage requires precisely `750` or `0750` and exactly single spaces,
|
||||
so the candidate now uses `0?750` and literal spaces. It refuses 00750/000750,
|
||||
a doubled space and a tab. This supersedes the initial numeric-equivalence design
|
||||
above. Only this row's own appended tests change; no base test line changes.
|
||||
|
||||
F3: case2-unit.sh plain() adds @. abs_plain is its only caller and requires an
|
||||
absolute normalized path, so @ cannot become an ExecStart prefix. In sed @ is
|
||||
ordinary replacement text (the delimiter is |); &, backslash and | still refuse.
|
||||
Environment=MOSAIC_CLAUDE_BIN carries @ literally; the parser compares the complete
|
||||
value, with no shell evaluation. @anthropic-ai also cannot match the residual
|
||||
@[A-Z_]+@ token check. Tests render/reinstall with a regular executable under
|
||||
@anthropic-ai, checking Environment and the new copy's WorkingDirectory. Since a
|
||||
nonroot test cannot create root ownership, a stat fake models root:0755 only for
|
||||
that file and its ancestor chain. The real file type/executable checks, render,
|
||||
installed-unit parser, and all other stat calls remain active. A separate block
|
||||
refuses space, %, $, both quote characters, backslash, & and |.
|
||||
|
||||
F4: copy_tree changes to / before archive/npm/find, after all its path arguments
|
||||
are absolute. copy_bundle retains its existing relative-bundle resolution before
|
||||
calling copy_tree. Checkout copies have absolute REPO and COPIES too. Source-only
|
||||
use from stack-release.sh changes no cwd because copy_tree is not called there.
|
||||
Audit of every AS_USER command in stack-upgrade.sh: fetch and merge use git -C;
|
||||
plan, gate, revert, confirm-rollback, stage (activate/rollback), confirm and rehearse
|
||||
invoke stack-release.sh, which changes to its checkout at line 34. No other
|
||||
AS_USER command needs a fix. The new test enters a directory mode 0111, verifies
|
||||
that ls cannot read it, then runs both a relative-bundle copy and checkout copy,
|
||||
asserting success, no find restore error, and an immutable completed copy.
|
||||
|
||||
Additional mutants (unrun under the same no-Node hold):
|
||||
|
||||
| Mutant | Change | Killer | Result |
|
||||
|---|---|---|---|
|
||||
| M3 | Drop @ from plain() | new factory scoped Claude-path test | Unrun |
|
||||
| M4 | Remove copy_tree's cd / | new unreadable-cwd copy test | Unrun |
|
||||
|
||||
All four fixes remain untested. Follow-up is an additive commit on the pushed
|
||||
branch, per sage-3741, with no amend or force. Morgana's static F1/F2 read of
|
||||
f30c74b12 was CLEAN (morgana-0205); F3/F4 and execution remain open.
|
||||
|
||||
Morgana N1: the outer bundle-verification failure now prints
|
||||
`STOP: bundle verify failed; do not continue` instead of exiting the interactive
|
||||
root shell. The subshell still returns failure and cleans up. This follows the
|
||||
sheet's human read-and-stop convention and avoids sending remaining pasted lines
|
||||
to a parent shell after exiting sudo -i. Inner mktemp failure still exits only the
|
||||
subshell. This supersedes the initial outer-shell failure behavior noted above.
|
||||
|
||||
## File numstat against 93694c518 (added / deleted)
|
||||
|
||||
| File | Added | Deleted |
|
||||
|---|---:|---:|
|
||||
| `BUILD-LOG.md` | 4 | 0 |
|
||||
| `agents/gladstone/work/hostcheck-item5/BUILD.md` | 114 | 0 |
|
||||
| `docs/SESSIONS.md` | 1 | 0 |
|
||||
| `docs/guides/host-run-sheet.md` | 12 | 2 |
|
||||
| `packages/cli/tests/case1-hostcheck.test.mjs` | 48 | 0 |
|
||||
| `packages/cli/tests/case2-unit.test.mjs` | 29 | 0 |
|
||||
| `packages/cli/tests/stack-copy.test.mjs` | 16 | 0 |
|
||||
| `scripts/case1-hostcheck.sh` | 2 | 2 |
|
||||
| `scripts/case2-unit.sh` | 1 | 1 |
|
||||
| `scripts/stack-copy.sh` | 3 | 0 |
|
||||
|
||||
## F5 (mos-1536 / ops-0260): explicit notifier decision
|
||||
|
||||
Before: no run-sheet step creates the required notifier decision, and precheck
|
||||
accepts a host whose bus will refuse missing notify.json. After: section 9a
|
||||
requires the mosaic user to create its business notify directory at 0700 and an
|
||||
explicit {"notifyVersion": 1, "binding": null} file at 0600 before section 10.
|
||||
The shell runs with cwd / and umask 077; noclobber refuses an existing decision.
|
||||
Named binding is explicitly a later per-host choice requiring Mos's go.
|
||||
case2-install.sh is unchanged and makes no silent default.
|
||||
|
||||
case2-precheck.sh's existing facts helper dynamically imports readNotifyConfig
|
||||
from cli.mjs and calls it directly. File type, uid, exact 0600 mode, JSON keys,
|
||||
version and binding-name validation therefore stay identical to the bus. The
|
||||
shell checks the business notify directory with existing private_dir (same uid,
|
||||
not a symlink, exactly 0700) and prints the new notifier config line. No notifier
|
||||
is started and no configuration is written. Tests append pass cases for null and
|
||||
a valid named binding, and refusal cases for missing file, 0644, 0755 directory,
|
||||
extra key, malformed JSON, invalid binding/version and a symlinked file.
|
||||
|
||||
M5 (unrun): remove the notifier shell check, including its refusal branch; the
|
||||
new notifier refusal test must fail. Also include Morgana's F3 negative mutant
|
||||
M3b (allow $ in plain), killed by the new unsafe-character test.
|
||||
|
||||
Existing positive precheck tests need an explicit notify fixture. Approval
|
||||
requested via Sage in gladstone-0006/-0007 for minimal setup-only additions to
|
||||
case2-unit.test.mjs host(t) and case2-install.test.mjs world(t); assertions remain
|
||||
unchanged. Those additions are not yet applied. Correction to gladstone-0007:
|
||||
case1-accept.test.mjs also names case2-precheck.sh, but uses a stand-in; it does
|
||||
not execute the changed script and needs no fixture amendment.
|
||||
|
||||
Execution remains held by Sage: no Node/tests run. Morgana-0207 static F1-F4
|
||||
verdict on a75532db1 is CLEAN; execution and F5 review are still open.
|
||||
|
||||
F5 acceptance from mos-1536 (for review):
|
||||
1. Before section 10, as mosaic, explicitly create <dataRoot>/notify/<business>
|
||||
at 0700 and notify.json at 0600 with default binding null; a later named
|
||||
binding needs Mos's go. Do not make case2-install.sh write it.
|
||||
2. Precheck must refuse missing file, non-0600 file, directory looser than 0700
|
||||
and a document outside the CLI's exact shape. Reuse/match cli.mjs validation,
|
||||
and name the new line in this run-sheet step's Precheck entry.
|
||||
3. Append-only test blocks cover pass, missing, 0644, 0755 dir, extra key; record
|
||||
a skip-check mutant, the touched test file and its numstat.
|
||||
4. F3/F4 precede F5, each follow-up commit stays on gladstone/hostcheck-item5;
|
||||
send pushes to Sage/Morgana with Mos cc. No Node until Sage CLEAR.
|
||||
|
||||
Static F5 draft receipt: git diff --check passed. Existing fixture approvals
|
||||
remain pending; F5's new pass test creates its own explicit decisions. No claim
|
||||
that the old positive precheck tests pass without the pending setup changes.
|
||||
|
||||
## F5 candidate file numstat against 93694c518 (added / deleted)
|
||||
|
||||
| File | Added | Deleted |
|
||||
|---|---:|---:|
|
||||
| `BUILD-LOG.md` | 6 | 0 |
|
||||
| `agents/gladstone/work/hostcheck-item5/BUILD.md` | 179 | 0 |
|
||||
| `docs/SESSIONS.md` | 1 | 0 |
|
||||
| `docs/guides/host-run-sheet.md` | 34 | 2 |
|
||||
| `packages/cli/tests/case1-hostcheck.test.mjs` | 48 | 0 |
|
||||
| `packages/cli/tests/case2-unit.test.mjs` | 65 | 0 |
|
||||
| `packages/cli/tests/stack-copy.test.mjs` | 16 | 0 |
|
||||
| `scripts/case1-hostcheck.sh` | 2 | 2 |
|
||||
| `scripts/case2-precheck.sh` | 19 | 0 |
|
||||
| `scripts/case2-unit.sh` | 1 | 1 |
|
||||
| `scripts/stack-copy.sh` | 3 | 0 |
|
||||
|
||||
## Fixture approval and separate follow-up (mos-1537)
|
||||
|
||||
Mos approved exactly setup-only additions to case2-unit.test.mjs host(t) and
|
||||
case2-install.test.mjs world(t): each gets three added lines for a 0700
|
||||
notify/acme directory and 0600 notify.json with {"notifyVersion":1,"binding":null}.
|
||||
No existing line, fake or assertion changes or is deleted. F5's missing-file
|
||||
negative case removes the file after setup, so the new default cannot mask it.
|
||||
The newly appended install test checks preserved bytes and mode after success,
|
||||
and separately removes the decision before install and checks it stays absent.
|
||||
case1-accept.test.mjs is unchanged. This follow-up is committed separately on
|
||||
33950cf9b as directed, with no amend. No Node/tests run; suite claims remain open.
|
||||
Morgana-0211 found F5 source/run sheet CLEAN statically; the setup prerequisite B1
|
||||
is addressed by this approved fixture patch, pending execution.
|
||||
|
||||
Additional planned mutants from Morgana-0211 (unrun): M5b drops private_dir,
|
||||
killed by the directory fault; M5c prints NOTIFY ok unconditionally, killed by
|
||||
the document faults. Existing test preservation is checked by git diff showing
|
||||
only additions (numstat below).
|
||||
|
||||
Fixture follow-up numstat: case2-unit.test.mjs +3/-0,
|
||||
case2-install.test.mjs +18/-0. Against the original base: case1-hostcheck.test.mjs
|
||||
+48/-0, case2-unit.test.mjs +68/-0, stack-copy.test.mjs +16/-0,
|
||||
case2-install.test.mjs +18/-0. git diff --check passed.
|
||||
|
||||
## F6 and notify parent correction (mos-1537 / ops-0261)
|
||||
|
||||
Before: install creates a copy but no human CLI wrapper; the sheet's
|
||||
stack-copy.sh check --units external therefore fails even with correct units.
|
||||
After: the sheet explicitly places a wrapper step after successful install and
|
||||
before that check. On Mos's separate live-step go, mosaic changes cwd to /,
|
||||
creates $HOME/.npm-global/bin if absent, then invokes its clone's stack-copy.sh
|
||||
wrapper <sha> --create. Existing wrappers refuse and require review; install is
|
||||
unchanged. This is documentation of a gated host action, not execution of it.
|
||||
There are no tests pinning these sheet steps to adjust.
|
||||
|
||||
Section 9a now explicitly creates/chmods <dataRoot>/notify and then its business
|
||||
leaf, each 0700. This addresses ops-0261's observation that mkdir -p -m only
|
||||
controls the leaf. The existing umask 077 remains. All exits are inside the
|
||||
unprivileged shell, so no failure exits the operator's interactive root shell.
|
||||
|
||||
Static receipt: git diff --check passed. No Node, tests or live action. F6 and
|
||||
this parent-directory documentation correction are a separate commit after the
|
||||
approved fixture-only 5fd2328b6 follow-up. Reviewer should retain the earlier
|
||||
F1-F5 source verdicts and inspect this delta. Queue row still pending from Sage.
|
||||
|
||||
## F6 candidate file numstat against 93694c518 (added / deleted)
|
||||
|
||||
| File | Added | Deleted |
|
||||
|---|---:|---:|
|
||||
| `BUILD-LOG.md` | 10 | 0 |
|
||||
| `agents/gladstone/work/hostcheck-item5/BUILD.md` | 241 | 0 |
|
||||
| `docs/SESSIONS.md` | 1 | 0 |
|
||||
| `docs/guides/host-run-sheet.md` | 51 | 2 |
|
||||
| `packages/cli/tests/case1-hostcheck.test.mjs` | 48 | 0 |
|
||||
| `packages/cli/tests/case2-install.test.mjs` | 18 | 0 |
|
||||
| `packages/cli/tests/case2-unit.test.mjs` | 68 | 0 |
|
||||
| `packages/cli/tests/stack-copy.test.mjs` | 16 | 0 |
|
||||
| `scripts/case1-hostcheck.sh` | 2 | 2 |
|
||||
| `scripts/case2-precheck.sh` | 19 | 0 |
|
||||
| `scripts/case2-unit.sh` | 1 | 1 |
|
||||
| `scripts/stack-copy.sh` | 3 | 0 |
|
||||
|
||||
## F6 gate citation (sage-3747)
|
||||
|
||||
Added scripts/stack-copy.sh:3 to the F6 wrapper step's explicit Mos-go requirement.
|
||||
This is doc-only; commands and tests are unchanged. Sage ruled the delete-after-
|
||||
setup missing-file negative meets mos-1537, so no fixture change is needed.
|
||||
Morgana-0213 found the fixture/F6 deltas CLEAN statically. No Node/tests run;
|
||||
Morgana's eventual run waits for Sage's row and CLEAR after the 220+216 landing.
|
||||
Numstat for this follow-up: host-run-sheet.md +1/-1; BUILD.md +9/-0.
|
||||
Static receipt: git diff --check passed.
|
||||
|
||||
Correction: 0f3d874d4 added 10 BUILD.md lines, not 9; the git numstat is authoritative.
|
||||
|
||||
## First review run and F4 test correction (morgana-0217)
|
||||
|
||||
Morgana ran af201b4a9 (6fa47e07d only relocates this file) under her authorized
|
||||
slot with timeout -k 10 and TMPDIR under /mnt/storage/scratch/tmp. Receipts:
|
||||
/mnt/storage/scratch/morgana-g320-out/base-summary.txt and mutants-summary.txt.
|
||||
|
||||
| Targeted file | Passed | Failed |
|
||||
|---|---:|---:|
|
||||
| case1-hostcheck.test.mjs | 19 | 0 |
|
||||
| case2-unit.test.mjs | 47 | 0 |
|
||||
| case2-install.test.mjs | 24 | 0 |
|
||||
| stack-copy.test.mjs | 17 | 1 |
|
||||
|
||||
Seven of eight mutants were killed: M1 literal0750, M2 anymode, M3a drop@,
|
||||
M3b allow$, M5 drop shell block, M5b drop private_dir, M5c always NOTIFY ok.
|
||||
M4 drop cd / SURVIVED: base and mutant both failed only setup's cleanup hook.
|
||||
This is a test defect, not a green acceptance result. The 0111 cwd retained
|
||||
search permission and did not reproduce the inaccessible inherited cwd. It
|
||||
also blocked setup's chmod -R cleanup because the directory remained unreadable.
|
||||
|
||||
B1 correction touches only this row's appended F4 test: the child shell enters
|
||||
an initially searchable directory, chmods it 000, checks both ! -r and ! -x, then
|
||||
execs stack-copy. The parent asserts all read/search bits remain cleared. The
|
||||
bundle now lives outside cwd and is passed absolutely, matching the real flow;
|
||||
existing row232 coverage still tests relative bundles from an accessible cwd.
|
||||
Both bundle and checkout paths remain covered.
|
||||
B2 correction: each child run/assertion sequence has a finally that restores the
|
||||
cwd to0700 before setup's existing cleanup. No old setup/test lines change.
|
||||
Production scripts are unchanged; the cd / fix remains correct by static review.
|
||||
|
||||
Correction is untested locally: no builder Node/test CLEAR was given. Morgana
|
||||
will rerun stack-copy whole, M4 and residue cleanup on the pushed correction;
|
||||
other files need no rerun unless changed. git diff --check passed.
|
||||
|
||||
## F4 corrected candidate numstat against 93694c518 (added / deleted)
|
||||
|
||||
| File | Added | Deleted |
|
||||
|---|---:|---:|
|
||||
| `BUILD-LOG.md` | 12 | 0 |
|
||||
| `agents/gladstone/work/queue-320/BUILD.md` | 304 | 0 |
|
||||
| `docs/SESSIONS.md` | 1 | 0 |
|
||||
| `docs/guides/host-run-sheet.md` | 51 | 2 |
|
||||
| `packages/cli/tests/case1-hostcheck.test.mjs` | 48 | 0 |
|
||||
| `packages/cli/tests/case2-install.test.mjs` | 18 | 0 |
|
||||
| `packages/cli/tests/case2-unit.test.mjs` | 68 | 0 |
|
||||
| `packages/cli/tests/stack-copy.test.mjs` | 22 | 0 |
|
||||
| `scripts/case1-hostcheck.sh` | 2 | 2 |
|
||||
| `scripts/case2-precheck.sh` | 19 | 0 |
|
||||
| `scripts/case2-unit.sh` | 1 | 1 |
|
||||
| `scripts/stack-copy.sh` | 3 | 0 |
|
||||
@@ -0,0 +1,171 @@
|
||||
# Row 321: F7 wrapper environment and F10 first adoption (#1817)
|
||||
|
||||
Owner Gladstone; reviewer Boyd (separate Codex thread); queue/review/landing Sage.
|
||||
Authorized by sage-3791. Branch gladstone/row321 starts at fetched origin/refactor
|
||||
833ec1cc8d7c11842efb82e57c1a0b256c8d22d7. The row320 branch is unchanged.
|
||||
Brief: docs/plans/2026-10-23_host-fixes-1816-brief.md, section
|
||||
"Brief: F7, the wrapper step's PATH and umask under sudo", including F10;
|
||||
verified blob 7333f94ba9cfd58f87c5a3580d428b84c65845b6 (requested pin 7333f94ba9cf).
|
||||
Only the run sheet and this BUILD.md change. No scripts, tests, config, credentials,
|
||||
queue state, deployment or live host operation changed. No tests or Node commands run.
|
||||
|
||||
## Before and after
|
||||
|
||||
F7: sudo secure_path omits the pinned Node directory, so wrapper creation reaches
|
||||
instance.sh's bare node and exits127. An inherited umask can leave the bin dir0775.
|
||||
The MOSAIC_WRAPPER heredoc now sets umask077 and the sheet's existing pinned Node
|
||||
PATH before mkdir. One sentence names secure_path and why the step sets both.
|
||||
The existing Mos go remains explicit. No wrapper command is executed here.
|
||||
|
||||
F10: first install omits adoption, so the first activate refuses no-pointer.
|
||||
The release-transport section now places explicit adoption after both units are
|
||||
running (section10) and before any activate/upgrade, run as mosaic on Mos's go by
|
||||
Jason or Mos, citing stack-release.sh:14. It uses the configured dev instance and
|
||||
external units, with the same pinned Node PATH under sudo.
|
||||
Adjacent to the upgrade command, the sheet now says only the first upgrade after
|
||||
adopt takes --no-rehearsal: adopt writes no backup; missing flag refuses no-backup;
|
||||
later upgrades omit it, because a backup with that flag refuses rehearsal-required.
|
||||
Source checks: stack-release.sh pointer_for_business refuses no-pointer and directs
|
||||
adopt; stack-upgrade.sh rehearse handles rc3/no-backup and rc0/rehearsal-required.
|
||||
This records existing behavior and authority; it adds no requirement or live go.
|
||||
|
||||
## Test-reference grep (negative and positive control)
|
||||
|
||||
The required negative grep covers both F7 and F10 because neither is read by any
|
||||
matched test. The positive control locates the precheck path that the unit/install
|
||||
tests actually execute, showing the pathspecs reach their test files.
|
||||
|
||||
Negative grep:
|
||||
|
||||
```sh
|
||||
git grep -n 'host-run-sheet' -- 'packages/*/tests/*' 'scripts/test-*.sh'
|
||||
# exit 1
|
||||
# no matches
|
||||
```
|
||||
|
||||
Positive control:
|
||||
|
||||
```sh
|
||||
git grep -n 'case2-precheck.sh' -- 'packages/*/tests/*' 'scripts/test-*.sh'
|
||||
# exit 0
|
||||
packages/cli/tests/case1-accept.test.mjs:26:const PRECHECK = `${HOME}/.local/share/mosaic-stack/copies/${COPY}/scripts/case2-precheck.sh`;
|
||||
packages/cli/tests/case2-install.test.mjs:2:// scripts/case2-precheck.sh's library checks and --library-status mode. Brief:
|
||||
packages/cli/tests/case2-install.test.mjs:23:const PRECHECK = join(REPO, "scripts", "case2-precheck.sh");
|
||||
packages/cli/tests/case2-install.test.mjs:271:// ---- case2-precheck.sh, the library profile (D6a) ----
|
||||
packages/cli/tests/case2-install.test.mjs:334:// ---- case2-precheck.sh --library-status (D6b) ----
|
||||
packages/cli/tests/case2-unit.test.mjs:2:// and scripts/case2-precheck.sh checks the host before they start. Brief: docs/plans/2026-10-20_case2-rows.md
|
||||
packages/cli/tests/case2-unit.test.mjs:20:const PRECHECK = join(REPO, "scripts", "case2-precheck.sh");
|
||||
packages/cli/tests/case2-unit.test.mjs:588:// ---- case2-precheck.sh ----
|
||||
```
|
||||
|
||||
## Run-sheet diff against the pinned base (git diff --unified=0)
|
||||
|
||||
```diff
|
||||
diff --git a/docs/guides/host-run-sheet.md b/docs/guides/host-run-sheet.md
|
||||
index d2ba28d7c..96fd4f363 100644
|
||||
--- a/docs/guides/host-run-sheet.md
|
||||
+++ b/docs/guides/host-run-sheet.md
|
||||
@@ -209,0 +210,9 @@ sudo -u mosaic -H git clone -q -b refactor /srv/mosaic/incoming/<sha>.bundle /sr
|
||||
+After a first install, once both units are running (section 10), adopt the installed copy before
|
||||
+any activate or upgrade. This step runs only on Mos's go, with Jason or Mos running it
|
||||
+(`scripts/stack-release.sh:14`):
|
||||
+
|
||||
+```sh
|
||||
+sudo -u mosaic -H env PATH=/opt/mosaic-stack/node-<v>/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
+ /srv/mosaic/src/mosaic-stack/scripts/stack-release.sh adopt <business> --instance dev --units external
|
||||
+```
|
||||
+
|
||||
@@ -211,0 +221,4 @@ Each upgrade, before `stack-upgrade.sh upgrade`:
|
||||
+Only the first upgrade after adopt takes `--no-rehearsal`: append it to the upgrade command below.
|
||||
+Adopt writes no backup, so omitting the flag then refuses `no-backup`. Subsequent upgrades omit it;
|
||||
+when a backup exists, supplying it refuses `rehearsal-required`.
|
||||
+
|
||||
@@ -246,0 +260 @@ step requiring Mos's go (`scripts/stack-copy.sh:3`); the install does not create
|
||||
+The step runs under sudo's `secure_path`, so it sets its own PATH and umask.
|
||||
@@ -250,0 +265,2 @@ cd / || exit 1
|
||||
+umask 077
|
||||
+PATH=/opt/mosaic-stack/node-<v>/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
No matching test reads the changed lines, so the stop condition did not apply.
|
||||
No test run or slot is required under sage-3791. Verification is static: the
|
||||
required grep and positive control above, exact brief/base pins, scoped diff,
|
||||
and git diff --check. Reviewer Boyd and Sage own review/landing; no approval
|
||||
or landing is claimed here.
|
||||
|
||||
Receipt correction: the first commit embedded ordinary diff context lines that
|
||||
contain a single space. git diff --check flagged those as trailing whitespace.
|
||||
This follow-up uses the exact zero-context diff instead. The run sheet is
|
||||
unchanged, no tests or Node commands run, and no amend or force-push is used.
|
||||
|
||||
## F11 addition (sage-3805, mos-1549, ops-0275)
|
||||
|
||||
The brief was extended after F7/F10 drafting and re-pinned at revision3733:
|
||||
canonical local commit a9bae9af2, same F7 section anchor, full verified blob
|
||||
0e684ef2103447dcc2013b6bf81b1c4ca5bbc0bd (requested pin0e684ef21034).
|
||||
Read using git show from the canonical checkout; no fetch, rebase or queue write
|
||||
is required. This new pin supersedes the earlier brief pin for this follow-up.
|
||||
|
||||
Defect: a precheck one second after unit start found no bus.sqlite yet.
|
||||
Section10 now requires loopback healthz success after a unit start before any
|
||||
case2-precheck.sh, including section9's --library-status. It uses the same
|
||||
127.0.0.1:<port>/healthz curl form as scripts/case1-accept.sh:458. The subshell
|
||||
tries at most30 times, each curl capped at2 seconds, with1 second between failed
|
||||
attempts (at most89 seconds plus scheduling overhead). Exhaustion prints STOP
|
||||
and exits only the subshell. The instruction says to stop before precheck/adopt;
|
||||
it does not exit the interactive operator shell. The section9 library-gitea row
|
||||
now explicitly requires this bounded wait before --library-status. <port> is the
|
||||
installed Console unit's --port, not an assumed port or business name.
|
||||
No host action, test, Node command or credential access occurred.
|
||||
|
||||
Required grep repeated for the widened scope:
|
||||
|
||||
```sh
|
||||
git grep -n 'host-run-sheet' -- 'packages/*/tests/*' 'scripts/test-*.sh'
|
||||
# exit1, no matches
|
||||
git grep -n 'case2-precheck.sh' -- 'packages/*/tests/*' 'scripts/test-*.sh'
|
||||
# exit0, positive control; output follows
|
||||
packages/cli/tests/case1-accept.test.mjs:26:const PRECHECK = `${HOME}/.local/share/mosaic-stack/copies/${COPY}/scripts/case2-precheck.sh`;
|
||||
packages/cli/tests/case2-install.test.mjs:2:// scripts/case2-precheck.sh's library checks and --library-status mode. Brief:
|
||||
packages/cli/tests/case2-install.test.mjs:23:const PRECHECK = join(REPO, "scripts", "case2-precheck.sh");
|
||||
packages/cli/tests/case2-install.test.mjs:271:// ---- case2-precheck.sh, the library profile (D6a) ----
|
||||
packages/cli/tests/case2-install.test.mjs:334:// ---- case2-precheck.sh --library-status (D6b) ----
|
||||
packages/cli/tests/case2-unit.test.mjs:2:// and scripts/case2-precheck.sh checks the host before they start. Brief: docs/plans/2026-10-20_case2-rows.md
|
||||
packages/cli/tests/case2-unit.test.mjs:20:const PRECHECK = join(REPO, "scripts", "case2-precheck.sh");
|
||||
packages/cli/tests/case2-unit.test.mjs:588:// ---- case2-precheck.sh ----
|
||||
```
|
||||
|
||||
No test reads the changed run-sheet lines; the stop condition does not apply.
|
||||
No test run is needed under the docs-only brief. Static verification checks both
|
||||
locations, retry limit/timeout, the loopback URL, unchanged F7/F10 and scoped diff.
|
||||
|
||||
F11 delta against d9400f0d3 (git diff --unified=0):
|
||||
|
||||
```diff
|
||||
diff --git a/docs/guides/host-run-sheet.md b/docs/guides/host-run-sheet.md
|
||||
index 96fd4f363..e9fa87f24 100644
|
||||
--- a/docs/guides/host-run-sheet.md
|
||||
+++ b/docs/guides/host-run-sheet.md
|
||||
@@ -322 +322 @@ file with `stat -c '%U %a %n' PATH`, never `cat`. `<business>` is the host's bus
|
||||
-| `/srv/mosaic/.config/mosaic-dev/secrets/<business>/` | `library-gitea` | 0600 | mosaic | library | `case2-precheck.sh --business <business> --library-status`, after the bus is up: `library-last-error library-credential-missing` until it's placed, then `library <project> last fetched ...` |
|
||||
+| `/srv/mosaic/.config/mosaic-dev/secrets/<business>/` | `library-gitea` | 0600 | mosaic | library | After a unit start, wait for loopback `/healthz` with the bounded retry in section 10 before `case2-precheck.sh --business <business> --library-status`: `library-last-error library-credential-missing` until it's placed, then `library <project> last fetched ...` |
|
||||
@@ -357,0 +358,16 @@ None by hand. I2's `case2-unit.sh` enables both units, starts the bus, then the
|
||||
+After a unit start, wait for the Console's loopback `/healthz` to succeed before running
|
||||
+`case2-precheck.sh`, including `--library-status` in section 9. Replace `<port>` with the installed
|
||||
+Console unit's `--port`. This retry is bounded to 30 attempts, each with a 2-second timeout, with
|
||||
+1 second between failed attempts. If it prints STOP, stop here; do not run the precheck or adopt.
|
||||
+
|
||||
+```sh
|
||||
+(
|
||||
+ attempts=0
|
||||
+ until curl -fsS --max-time 2 -o /dev/null 'http://127.0.0.1:<port>/healthz'; do
|
||||
+ attempts=$((attempts + 1))
|
||||
+ [ "$attempts" -lt 30 ] || { echo "STOP: loopback healthz did not become ready; do not continue" >&2; exit 1; }
|
||||
+ sleep 1
|
||||
+ done
|
||||
+)
|
||||
+```
|
||||
+
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
# Goldie
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Sage under mos-1542 and mos-1546. Owns F8 and F9, the library persona
|
||||
import in `scripts/case2-install.sh` (#1818), for the 433 Run B rerun.
|
||||
|
||||
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; it pushes only its own `goldie/*` branches through the repository credential helper (mos-0981), never `refactor`,
|
||||
`main` or another seat's branch. Claude seats orchestrate and review the install path (mos-1542 rule 4). Registered by
|
||||
Sage on 2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -0,0 +1,69 @@
|
||||
# Row 322 / #1818 — F8 and F9
|
||||
|
||||
Goldie, 2026-10-23. Branch goldie/row322, base 833ec1cc8d7c11842efb82e57c1a0b256c8d22d7.
|
||||
Brief: docs/plans/2026-10-23_host-fixes-1816-brief.md, F8/F9 section at 7333f94ba9cf.
|
||||
Issue #1818 is supplied by Sage; no new issue is needed. Reviewer Morgana; Sage owns landing/full gate.
|
||||
|
||||
## Implementation and authority
|
||||
|
||||
F8 runs the importer from `$COPIES/$COMMIT/scripts/case2-persona-import.mjs`. This uses Node's normal ESM resolution, finding yaml beside the copy and the relative harness module in that same commit. It avoids a custom loader or ambient resolution setting. The copy closure already includes harness/src and business/src, but omitted the importer itself; COPY_PATHS now includes that one script. No new dependency or dependency version change.
|
||||
|
||||
F9 selects the first line beginning with an Error type (including TypeError etc.) or ERR_, falling back to the last line for the importer's handled refusal. awk exits after the first matching line and emits exactly one line, keeping the stack and version banner out of the install refusal. Anchoring avoids selecting Node's earlier `throw new ERR_MODULE_NOT_FOUND(...)` source excerpt instead of the actual Error line.
|
||||
|
||||
Mos-1550 authorizes the copy-path addition, setup-only additions to world(t), and adding the single new filename to any existing copy-file pin. Existing test edits outside the appended row-322 block:
|
||||
|
||||
- case2-install.test.mjs: the fake stack-copy setup gains a loop copying the importer, harness bundle.mjs/tools.mjs, and business vocabulary.mjs; it copies yaml into the fake copy's node_modules. The existing last template-string line is extended to add those setup commands. No existing assertion changes.
|
||||
- packages/webui/tests/pinned-copy.test.mjs:76: the scripts file-list assertion gains only `case2-persona-import.mjs`, in sorted order. No other change in this test.
|
||||
|
||||
The new append-only block builds a scratch clone outside the repository with no node_modules, real installer and importer, stubbed bootstrap/business validation, and a copy with the real importer, relative module closure and yaml. F8 checks installed persona bytes/mode; F9 removes yaml from that copy to produce a real uncaught Node missing-package error, then checks one refusal line carries the cause and no stack/banner. Existing tests missed F8 because the real importer ran from the development checkout, which has yaml installed; the fake copy held only scripts/mosaic. The old fixtures thus never exercised the dependency boundary or an uncaught importer load error.
|
||||
|
||||
## Class check and copy-test audit
|
||||
|
||||
Before change:
|
||||
|
||||
```
|
||||
grep -n 'node "$REPO/' scripts/case2-install.sh scripts/case2-precheck.sh scripts/stack-copy.sh
|
||||
scripts/case2-install.sh:185: out="$(node "$REPO/scripts/case2-persona-import.mjs" ...
|
||||
```
|
||||
|
||||
After change: same grep has no hits (exit 1). Direct clone-side .mjs entry points in the three scripts with a bare package import: **scripts/case2-persona-import.mjs → yaml**, moved to the copy by F8. No other direct entry point imports a bare package. Precheck's inline Node programs dynamically import cli/src/config.mjs, business/src/index.mjs, library/src/index.mjs and cli/src/cli.mjs; those entry files import builtins/relative modules only. Library reader.mjs uses its own front-matter parser and has no yaml import. stack-copy sources instance.sh, whose mosaic-config.mjs imports builtins only. Installer bootstrap likewise runs mosaic-config.mjs, and business validation runs business/src/cli.mjs, also no direct bare imports. No other product fix made.
|
||||
|
||||
Audit commands (read-only):
|
||||
|
||||
```
|
||||
rg -n 'COPY_PATHS|copy carries|copy.*(hash|sha256|files|file set)|sha256.*copy' packages/*/tests/* scripts/test-*.sh
|
||||
rg -n 'COPY_PATHS|stack-copy\.sh|readdirSync|sha256|deepEqual' packages/webui/tests/pinned-copy.test.mjs packages/cli/tests/stack-copy.test.mjs
|
||||
```
|
||||
|
||||
Relevant hits: cli/tests/stack-copy.test.mjs:35-47 checks harness file closure (positive control: COPY_PATHS at :36), :52 asserts a required-file subset, :248-264 computes hashes/modes and compares bundle/check-out copy trees dynamically, :274 compares another bundle tree. None freezes a complete path list/hash. webui/tests/pinned-copy.test.mjs:75 pins top-level entries (unchanged), :76 pins scripts' exact file set (approved one-path update). Other readdir hits in those files check cleanup/existing-target behavior, not COPY_PATHS identity. Broad initial grep also hits business/tests/helpers.mjs:33, cli/tests/stack-upgrade.test.mjs:295, webui/tests/views.test.mjs:172, cli/tests/stack-release.test.mjs:1751 and :1987; inspected contexts are unrelated comments/UI copy or different fixture copies. No scripts/test-*.sh hit for these path/hash patterns.
|
||||
|
||||
## Verification plan
|
||||
|
||||
Pending Sage CLEAR before any test: run new tests on fixed build first, then whole case2-install.test.mjs, whole stack-copy.test.mjs and webui pinned-copy.test.mjs. Demonstrate expected failure at 094124b4e with new block applied (baseline importer path), and kill the `$REPO` reversion mutant. Record all outcomes. First unexpected new-test red is recorded/reported under mos-1272; existing-test edits outside mos-1550 or a second unexpected red stop for Sage.
|
||||
|
||||
No tests run yet. No commit/push yet. No host changes, live state, secrets, deploy or release actions.
|
||||
|
||||
## Verified results — sage-3808 CLEAR, 2026-10-23T22:47Z
|
||||
|
||||
All runs were serialized with `TMPDIR=/mnt/storage/scratch/tmp/goldieuQ2664` (created by `mktemp -d /mnt/storage/scratch/tmp/goldieXXXXXX`), using `timeout -k 10 90` for focused tests and `timeout -k 10 180` for whole files/negative-proof driver. Node v26.8.1. Dependencies were read through the ignored, uncommitted node_modules symlink approved by sage-3804; each scratch clone had no node_modules. No /tmp fixture was used.
|
||||
|
||||
| Run | Result | Evidence |
|
||||
| --- | --- | --- |
|
||||
| New tests, first run | 2/2 pass, exit 0 | test-new.log |
|
||||
| Whole case2-install.test.mjs | 26/26 pass, exit 0 | test-install.log |
|
||||
| Whole stack-copy.test.mjs | 18/18 pass, exit 0 | test-copy.log |
|
||||
| Whole webui pinned-copy.test.mjs | 10/10 pass, exit 0 | test-pinned-copy.log |
|
||||
| Installer from 094124b4e, new tests | 0/2 pass, exit 1, expected red | baseline-094124b4e.log |
|
||||
| F8 mutant: restore `$REPO` importer path only | 0/1 pass, exit 1, killed | mutant-F8.log |
|
||||
| F9 mutant: restore tail-only refusal only | 0/1 pass, exit 1, killed | mutant-F9.log |
|
||||
| Fixed installer restored, new tests rerun | 2/2 pass, exit 0 | test-restored.log |
|
||||
|
||||
The baseline proof temporarily installs the exact installer bytes from `git show 094124b4e:scripts/case2-install.sh`, retaining the newly appended tests and approved fixture setup. Relevant installer/importer/copy/test source paths have no changes between N and the branch base 833ec1cc8 (`git diff 094124b4e 833ec1cc8 --` those paths is empty). It is a targeted baseline proof, not a full checkout/suite run at N. Baseline failures show `persona-import: Node.js v26.8.1`; the F8-only mutant exposes the underlying `Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'yaml'` from the scratch clone. F9-only mutant again prints the banner. All expected failures are deliberate acceptance checks; no unexpected first-run red occurred.
|
||||
|
||||
negative-proof.py records each run, checks the expected failure count/exit, restores in a finally block, and verifies fixed installer bytes after each run. Restored SHA-256 each time: `c23a08324528cc29895014093c661948a5c8eb40316c7b2e21c6f833cf5d6462`. Final focused green confirms restoration. git diff --check passed.
|
||||
|
||||
`git diff --numstat -- packages/cli/tests/case2-install.test.mjs`: **57 additions, 1 deletion**. Outside the new appended block, original line 70's template-string close moves after four new setup lines: the existing mkdir/empty mosaic/echo command is unchanged, followed by the `for file ...` line, mkdir/cp loop body, `done`, and mkdir/cp yaml line. All pre-existing assertions remain byte-for-byte unchanged. The only pre-existing assertion edit anywhere is the expressly approved one-filename addition at packages/webui/tests/pinned-copy.test.mjs:76.
|
||||
|
||||
Sage's full package/script gate and Morgana's review remain pending. This builder ran only the approved three whole test files and targeted acceptance checks. Sage retains queue transition/review posting and integration authority; no PR was created by Goldie.
|
||||
|
||||
Evidence log lines have trailing whitespace removed for git diff --check; test content and outcomes are unchanged.
|
||||
@@ -0,0 +1,4 @@
|
||||
c23a08324528cc29895014093c661948a5c8eb40316c7b2e21c6f833cf5d6462 scripts/case2-install.sh
|
||||
cb1a4fef997b9696406e324878d7a0e5181892622162c9ce77a8fd625b5cd59a scripts/stack-copy.sh
|
||||
a7b02af4b5df92e6d727fe8c67565ea0483d6d57531765dee68f54fd97e7c23f packages/cli/tests/case2-install.test.mjs
|
||||
2574b414459f4cfb187de64b9a2fbe34ab4b4d64f582caae8baf6a1985e8cd3c packages/webui/tests/pinned-copy.test.mjs
|
||||
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: row 322 F8: library personas install from the copy with no clone node_modules
|
||||
not ok 1 - row 322 F8: library personas install from the copy with no clone node_modules
|
||||
---
|
||||
duration_ms: 90.885888
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:451:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-rQM4ct/home/.config/mosaic-dev/config.json
|
||||
case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-rQM4ct/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc
|
||||
case2-install: refused: persona-import: Node.js v26.8.1
|
||||
|
||||
|
||||
1 !== 0
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: 0
|
||||
actual: 1
|
||||
operator: 'strictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:454:10)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
# Subtest: row 322 F9: importer refusal keeps the error line rather than the Node banner
|
||||
not ok 2 - row 322 F9: importer refusal keeps the error line rather than the Node banner
|
||||
---
|
||||
duration_ms: 91.239243
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:462:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
The input did not match the regular expression /persona-import: Error \[ERR_MODULE_NOT_FOUND\]: Cannot find package 'yaml'/. Input:
|
||||
|
||||
'case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-O6VWs2/home/.config/mosaic-dev/config.json\n' +
|
||||
'case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-O6VWs2/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc\n' +
|
||||
'case2-install: refused: persona-import: Node.js v26.8.1\n'
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
actual: |-
|
||||
case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-O6VWs2/home/.config/mosaic-dev/config.json
|
||||
case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-O6VWs2/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc
|
||||
case2-install: refused: persona-import: Node.js v26.8.1
|
||||
|
||||
operator: 'match'
|
||||
stack: |-
|
||||
refused (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:137:10)
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:469:3)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
Test.run (node:internal/test_runner/test:1467:12)
|
||||
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
...
|
||||
1..2
|
||||
# tests 2
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 2
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 287.462723
|
||||
@@ -0,0 +1,37 @@
|
||||
TAP version 13
|
||||
# Subtest: row 322 F8: library personas install from the copy with no clone node_modules
|
||||
not ok 1 - row 322 F8: library personas install from the copy with no clone node_modules
|
||||
---
|
||||
duration_ms: 90.468116
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:451:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-bzVkal/home/.config/mosaic-dev/config.json
|
||||
case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-bzVkal/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc
|
||||
case2-install: refused: persona-import: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'yaml' imported from /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-bzVkal/clone/scripts/case2-persona-import.mjs
|
||||
|
||||
|
||||
1 !== 0
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: 0
|
||||
actual: 1
|
||||
operator: 'strictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:454:10)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 194.231229
|
||||
@@ -0,0 +1,41 @@
|
||||
TAP version 13
|
||||
# Subtest: row 322 F9: importer refusal keeps the error line rather than the Node banner
|
||||
not ok 1 - row 322 F9: importer refusal keeps the error line rather than the Node banner
|
||||
---
|
||||
duration_ms: 92.814376
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:462:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
The input did not match the regular expression /persona-import: Error \[ERR_MODULE_NOT_FOUND\]: Cannot find package 'yaml'/. Input:
|
||||
|
||||
'case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-6HcR0m/home/.config/mosaic-dev/config.json\n' +
|
||||
'case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-6HcR0m/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc\n' +
|
||||
'case2-install: refused: persona-import: Node.js v26.8.1\n'
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
actual: |-
|
||||
case2-install: config /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-6HcR0m/home/.config/mosaic-dev/config.json
|
||||
case2-install: copy /mnt/storage/scratch/tmp/goldieuQ2664/case2-install-6HcR0m/home/.local/share/mosaic-stack/copies/cccccccccccccccccccccccccccccccccccccccc
|
||||
case2-install: refused: persona-import: Node.js v26.8.1
|
||||
|
||||
operator: 'match'
|
||||
stack: |-
|
||||
refused (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:137:10)
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row322/packages/cli/tests/case2-install.test.mjs:469:3)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 200.516406
|
||||
@@ -0,0 +1,6 @@
|
||||
baseline-094124b4e: exit 1, expected failures 2
|
||||
restored sha256 c23a08324528cc29895014093c661948a5c8eb40316c7b2e21c6f833cf5d6462
|
||||
mutant-F8: exit 1, expected failures 1
|
||||
restored sha256 c23a08324528cc29895014093c661948a5c8eb40316c7b2e21c6f833cf5d6462
|
||||
mutant-F9: exit 1, expected failures 1
|
||||
restored sha256 c23a08324528cc29895014093c661948a5c8eb40316c7b2e21c6f833cf5d6462
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Run row-322 expected-red proofs, always restoring installer bytes."""
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[4]
|
||||
installer = root / 'scripts/case2-install.sh'
|
||||
logs = Path(__file__).resolve().parent
|
||||
fixed = installer.read_bytes()
|
||||
baseline = subprocess.check_output(['git', 'show', '094124b4e:scripts/case2-install.sh'], cwd=root)
|
||||
f8 = fixed.replace(b'$COPIES/$COMMIT/scripts/case2-persona-import.mjs', b'$REPO/scripts/case2-persona-import.mjs')
|
||||
assert f8 != fixed
|
||||
old_error = baseline.splitlines()[185]
|
||||
new_error = fixed.splitlines()[185]
|
||||
f9 = fixed.replace(new_error, old_error)
|
||||
assert f9 != fixed
|
||||
try:
|
||||
for label, source, pattern, failures in [
|
||||
('baseline-094124b4e', baseline, 'row 322', 2),
|
||||
('mutant-F8', f8, 'row 322 F8', 1),
|
||||
('mutant-F9', f9, 'row 322 F9', 1),
|
||||
]:
|
||||
installer.write_bytes(source)
|
||||
result = subprocess.run([
|
||||
'timeout', '-k', '10', '90', 'node', '--test', '--test-reporter=tap',
|
||||
f'--test-name-pattern={pattern}', 'packages/cli/tests/case2-install.test.mjs',
|
||||
], cwd=root, env=os.environ, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
(logs / f'{label}.log').write_text(result.stdout)
|
||||
print(f'{label}: exit {result.returncode}, expected failures {failures}', flush=True)
|
||||
assert result.returncode == 1, result.stdout
|
||||
assert f'# fail {failures}\n' in result.stdout, result.stdout
|
||||
assert 'ERR_MODULE_NOT_FOUND' in result.stdout, result.stdout
|
||||
installer.write_bytes(fixed)
|
||||
assert installer.read_bytes() == fixed
|
||||
print('restored sha256 ' + hashlib.sha256(fixed).hexdigest(), flush=True)
|
||||
finally:
|
||||
installer.write_bytes(fixed)
|
||||
assert installer.read_bytes() == fixed
|
||||
@@ -0,0 +1,26 @@
|
||||
✔ a copy carries every file the harness modules read from it (891.310838ms)
|
||||
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (537.82282ms)
|
||||
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (713.621566ms)
|
||||
✔ row 190 N6: check --instance passes only when the wrapper exports the instance's MOSAIC_CONFIG (657.082198ms)
|
||||
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (799.380065ms)
|
||||
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (499.872009ms)
|
||||
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (3824.368055ms)
|
||||
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (668.500662ms)
|
||||
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (399.490955ms)
|
||||
✔ install never shows a template name on systemd 261; template drop-ins still refuse (318.110324ms)
|
||||
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (347.788225ms)
|
||||
✔ inherited mosaic-.service.d override refuses without changing the unit (403.142763ms)
|
||||
✔ inherited service.d override refuses without changing the unit (384.337811ms)
|
||||
✔ inherited drop-in unreadable directory or file refuses before writing (370.630432ms)
|
||||
✔ row 232: copy --bundle copies a SHA the bundle's refactor reaches, byte-identical to copy SHA, and refuses the rest (2257.394077ms)
|
||||
✔ row 232: wrapper --create writes the first wrapper only, the same bytes wrapper ID writes, and refuses over any file (779.276348ms)
|
||||
✔ row 232: check --units external reads the system units, the default reads the user units (641.792634ms)
|
||||
✔ Vikunja 614: bundle and checkout copy leave an unreadable caller directory before find (1047.143772ms)
|
||||
ℹ tests 18
|
||||
ℹ suites 0
|
||||
ℹ pass 18
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 15638.417631
|
||||
@@ -0,0 +1,34 @@
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (258.707115ms)
|
||||
✔ a library re-run passes and leaves every file as it was (391.278216ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (274.686097ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (274.084555ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (175.791904ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (72.301025ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (76.469642ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (57.89739ms)
|
||||
✔ the library install needs --personas (5.495139ms)
|
||||
✔ a validate refusal removes the business file this run wrote (230.311754ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (397.123924ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (404.408689ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (399.17119ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (402.47159ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (390.108413ms)
|
||||
✔ the library precheck refuses a business file at 0644 (402.829663ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (573.861228ms)
|
||||
✔ the library precheck refuses a Discord binding (386.405794ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (309.213305ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (330.922116ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (335.576032ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (379.69927ms)
|
||||
✔ --library-status takes no --profile (5.544173ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (465.015156ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (139.662489ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (95.452368ms)
|
||||
ℹ tests 26
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 7340.739711
|
||||
@@ -0,0 +1,10 @@
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (178.794273ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (101.739988ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 394.59041
|
||||
@@ -0,0 +1,18 @@
|
||||
✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (872.104795ms)
|
||||
✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (1245.376299ms)
|
||||
✔ copy writes nothing when an archived path is missing at the commit (409.033887ms)
|
||||
✔ a copy that loses the rename race leaves no read-only temp directory behind (548.743561ms)
|
||||
✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (534.468073ms)
|
||||
✔ a copy that meets a target holding files says exists: and moves nothing into it (541.303916ms)
|
||||
✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (547.966283ms)
|
||||
✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (856.865742ms)
|
||||
✔ serve refuses a --checkout that is not an absolute directory path (274.552284ms)
|
||||
✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (49.311324ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 6003.825092
|
||||
@@ -0,0 +1,10 @@
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (148.410784ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (94.205865ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 344.990842
|
||||
@@ -0,0 +1,141 @@
|
||||
# Row 323 / #1819 — Case 1 factory business file
|
||||
|
||||
Goldie, 2026-10-23. New isolated worktree `/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323`, branch `goldie/row323`, base local refactor `da537e7d159be0bf39a36eaaa65cd16c20373b72` (descends from brief commit 68f3bded5). Row322 worktree remains unchanged.
|
||||
|
||||
## Sources and input boundary
|
||||
|
||||
Read brief docs/plans/2026-10-23_case1-business-file-brief.md, row195 DESIGN.md (host values sections1/2/6/7), installer factory HELPER, precheck profile checks, case1-accept F3/F7, business loader and credentials/launch checks, role definitions and business fixture helpers. No host access or credential read.
|
||||
|
||||
DESIGN fixes business `mosaic-stack`, OS user mosaic, home `/srv/mosaic`, config `/srv/mosaic/.config/mosaic-dev`, data `/srv/mosaic/.mosaic-dev`, Claude only, tester on. Human id is explicitly left to the go (:82/:300), no project root is named. Business loader requires at least one project/root (:261-275), tracker.sync bot/botId/Vikunja credential reference (:85-94), launch.models and nonempty launch.instances excluding launcher (:186-193). These host/business values are not supplied by DESIGN. Per sage-3815 and brief, stop configuration rather than guess. Asked Sage in goldie-0008 for exact values.
|
||||
|
||||
Recommended minimal role set: chief-of-staff instance chief with claude-code harness plus credential-free assistant solely because launch.instances must contain a declared non-launcher. Both arbiters may reference chief; no additional PM/CTO role credential required. This remains a recommendation pending input, not a written host config.
|
||||
|
||||
Literal top-level `library` is ignored by the installer plan and rejected later by business validation as unsupported key; a library project is refused by the factory plan as profile-library-business. Asked Sage which intended refusal assertion to use; recommend covering both through their real paths without a product change.
|
||||
|
||||
## Authority and status
|
||||
|
||||
Sage-3815 explicitly authorizes claimant queue moves despite brief's generic no-queue-writes boundary. Claim op goldie-0323-start-1 retries every20s on lock contention per sage-3814; no lock touched. Tests and mutant require new Sage CLEAR; none run. No business JSON or test written yet. No commit/push yet. BUILD/SESSIONS/BUILD-LOG notes record investigation only.
|
||||
|
||||
## Rulings and static blocker, 23:02Z
|
||||
|
||||
Sage-3820 accepts chief + credential-free assistant because launch.instances must be nonempty and exclude chief, both arbiters chief. It accepts literal library-key rejection through final real business validation and library-project rejection through factory planning, no production change. Sage-3822 supplies project mosaic-stack at /srv/mosaic/src/mosaic-stack and model claude-opus-5-5 (provider/thinking to be sourced). Sage-3823 relays Mos-1554: human jason, no real tracker bot, token path must be absent and never created; stop immediately if this refuses boot/launch.
|
||||
|
||||
Static trace proves refusal, reported to Sage in goldie-0009:
|
||||
|
||||
- cli/src/config.mjs:127 loads business. trackerFor :60 returns null without tracker.baseUrl; :137-138 suppresses only config.trackers, not business tracker.sync.
|
||||
- bus/src/runtime.mjs:131 registers business tracker.sync credentials under business/@sync; :157 constructs Credentials regardless of config.trackers.
|
||||
- bus/src/credentials.mjs:134-139 eagerly reads every reference. Ordinary ref.file lstat/realpath failure at :225-228 throws credential-file during boot, before chief can launch.
|
||||
- The business credential check warns for unset env, but bus/src/credentials.mjs:268 sends undefined to #format; :278 rejects credential-format. An env reference is not a bypass and is outside the brief's file requirement.
|
||||
- Install refuses even earlier: business/src/cli.mjs:84-88 checks tracker.sync references, business/src/credentials.mjs:78-83 records a missing ordinary file as a problem, CLI :95-97 exits2. case2-install.sh:217-220 wraps validate refusal and removes the business file it created.
|
||||
- business/src/business.mjs:41 requires tracker bot names to begin bot-, :42 requires positive botId. Mos's illustrative no-bot-mosaic-stack-sync would fail that shape; a bot-no-bot marker still cannot solve the absent-file refusal.
|
||||
|
||||
Status: real fail-closed product boundary. No business JSON, test, placeholder credential, host action or tests written/run. Await Sage/Mos resolution; do not bypass by inventing a token or expanding product scope. Only investigation notes exist in this worktree. Claim attempts continue separately under the queue window's explicit retry instruction.
|
||||
|
||||
## Resumed after Q247 landing — sage-3859
|
||||
|
||||
Current base is pushed `dd6cb5872b972bcabb99b82589b2905e4e47dd3e`. Preserved earlier investigation as a notes-only commit, then rebased onto that tip; only append-only BUILD-LOG/SESSIONS conflicts, resolved retaining the entire landed files and appending this row's original suffix. Earlier equivalent local refactor doc commits were skipped by git as already applied. No other row323 source existed before this resume.
|
||||
|
||||
Q247 removes the static blocker: this file now omits tracker entirely, with no dummy bot/token/reference. No tracker credential or token file is required or created. The separate Claude session login file remains the operator's run-sheet step, as described in DESIGN section3; this document contains no credential value or reference, and this work reads no credential.
|
||||
|
||||
Approved values and minimal role set:
|
||||
|
||||
- business mosaic-stack, factory bus.tester true, no library key/project (DESIGN sections1/2/7).
|
||||
- human jason (Mos-1554, sage-3823); project mosaic-stack at /srv/mosaic/src/mosaic-stack (sage-3822).
|
||||
- chief-of-staff chief and credential-free assistant, both arbiters chief, launch.by chief, launch.instances [assistant] (sage-3820). The loader requires nonempty launch.instances excluding the launcher (business.mjs:186-191), so a chief-only document cannot load. No other role is needed.
|
||||
- Both roles use claude-code; plugins.harness enables only claude-code. No pi route or fallback.
|
||||
- chief model claude-opus-5-5 is Sage's explicit choice (sage-3822). Provider anthropic/model claude-opus-5-5 is the repository model mapping at packages/business/README.md:185. There is no separate per-model default thinking catalog in the implementation: choose high from the Claude harness's supported LEVELS at packages/plugins/harness/claude-code/index.mjs:28, following the existing chief route convention at packages/cli/tests/session-launch.test.mjs:83. Both roles use that one declared model/effort; no extra model or capacity policy is introduced.
|
||||
|
||||
One new file packages/business/tests/case1-business-file.test.mjs has seven cases. It first loads the shipped JSON bytes unchanged from a temporary business-file location (loader checks shape, no host path access). Its runtime/CLI/install cases copy the document into a scratch input and replace only the project root with a scratch fixture root, per sage-3820's instruction to use test fixture paths. The real factory installer takes that input via --business-file and uses its real bootstrap/business validation, with only stack-copy stubbed (no clone/install/network). The boot case uses bootConfig + actual startBroker and asserts no tracker config or credential status. Negative cases cover bus.tester false (bus-tester-off), literal library (final validate unsupported key, per sage-3820), and a library project (factory plan profile-library-business). No pre-existing test changed.
|
||||
|
||||
The new tester-switch mutant flips only bus.tester in the shipped JSON, targets the new exact-document assertion, then restores/checks bytes in finally. Prepared but not run. Tests and mutant remain held until Sage CLEAR; only syntax/diff/static JSON checks have run. Row323 claimant is still in-progress from rev3749.
|
||||
|
||||
## Field provenance and pre-test commit — sage-3861
|
||||
|
||||
Sage-3861 CLEAR explicitly requires committing the draft before any run, so test counts attach to a commit. This is a specific exception to the usual green-before-commit rule. Until recorded below, this commit is untested. No old test is modified.
|
||||
|
||||
| Document field | Value and source |
|
||||
| --- | --- |
|
||||
| businessVersion | 1, business loader's version requirement (business/src/business.mjs:233). Schema value, not a mos-1554 host choice. |
|
||||
| id | mosaic-stack, row195 DESIGN section7 (sage-1813), restated by row323 brief. |
|
||||
| human | jason, mos-1554 relayed by sage-3823. |
|
||||
| arbiters.delivery / arbiters.technical | chief / chief, sage-3820's explicit minimal-role ruling; both reference the chief-of-staff definition. |
|
||||
| projects.mosaic-stack.root | /srv/mosaic/src/mosaic-stack, sage-3822's explicit project-id/root decision. DESIGN supplied /srv/mosaic as home but did not supply this root. |
|
||||
| vars.bus.tester | true, DESIGN section2 factory profile and installer bus-tester-off requirement. |
|
||||
| plugins.harness | [claude-code], brief's Claude-only/P19 mos-0865 boundary and DESIGN section3. |
|
||||
| roles.chief.definition | chief-of-staff, brief/roles/chief-of-staff.json; no credential or tracker bot. |
|
||||
| roles.assistant.definition | assistant, sage-3820 acceptance of this one extra credential-free role, required because launch.instances must be nonempty and exclude chief. |
|
||||
| roles.*.vars.harness | claude-code for both; same Claude-only rule, no fallback route. |
|
||||
| roles.*.vars.model | claude-opus-5-5 for both, chief model explicitly chosen in sage-3822; reuse the single declared model for assistant to avoid adding another model decision. |
|
||||
| roles.*.vars.thinking | high, supported effort in packages/plugins/harness/claude-code/index.mjs:28; existing chief convention packages/cli/tests/session-launch.test.mjs:83. The repository has no per-model default-effort catalog; this is an explicit supported-convention choice, not an invented catalog default. |
|
||||
| launch.by | chief, brief and F7's requirement to name the chief launch; sage-3820. |
|
||||
| launch.instances | [assistant], sage-3820 and business.mjs:186-191's nonempty/non-launcher rule. |
|
||||
| launch.models.claude-opus-5-5.provider / model | anthropic / claude-opus-5-5, repository model mapping packages/business/README.md:185 and sage-3822. The model id is also the map key so no extra alias is introduced. |
|
||||
| Omitted tracker / credentials / library | Q247/mos-1555 and sage-3859 supersede mos-1554's proposed absent-token placeholder; no bot, token path, credential value or library project/key is present. |
|
||||
|
||||
Only human=jason is a literal field value supplied by mos-1554. The no-bot intent also comes from mos-1554; Q247 implements it by omission. Other fields deliberately cite the actual design/schema/Sage source rather than attributing them to mos-1554.
|
||||
|
||||
Planned runs under sage-3861: new file whole; business suite whole; CLI suite whole; tester-switch mutant; restored new file whole. Fresh scratch TMPDIR and timeout-k10 for each, one process at a time. Existing-test red or any additional old-test edit stops work; first-run new red is recorded and reported. Push remains own branch goldie/row323 after the test sequence/report evidence is concrete.
|
||||
|
||||
## First slot result — stopped on existing CLI test red
|
||||
|
||||
Committed draft before any test: `571376c30e77f46bbe41069633390cc001f57ac8` (base dd6cb5872b972bcabb99b82589b2905e4e47dd3e), per sage-3861. Fresh TMPDIR `/mnt/storage/scratch/tmp/goldielKvBJv`, allocated by mktemp under the mandated scratch parent. All runs serialized with timeout -k 10. Source and test bytes remained unchanged throughout.
|
||||
|
||||
| Run | Command after TMPDIR/timeout | Outcome |
|
||||
| --- | --- | --- |
|
||||
| New whole file | timeout -k 10 180 node --test --test-reporter=tap packages/business/tests/case1-business-file.test.mjs | 7/7 pass, 0 fail/skip/cancel; test-new.log |
|
||||
| Whole business suite | timeout -k 10 600 node --test --test-reporter=tap --test-concurrency=1 packages/business/tests/ | 261/261 pass, 0 fail/skip/cancel; test-business-suite.log |
|
||||
| Whole CLI suite | timeout -k 10 1200 node --test --test-reporter=tap --test-concurrency=1 packages/cli/tests/ | Interrupted on existing-test failure; 631 top-level passes/1 failure recorded before termination, no final full-suite totals; test-cli-suite.log |
|
||||
| Tester-switch mutant | Not run | Stop rule reached first |
|
||||
| Restored new file | Not run | Mutant not run; source already matches manifest |
|
||||
|
||||
Existing failure: CLI test579, packages/cli/tests/revoke-window.test.mjs:170: `row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22`. After 20111.683685ms it failed `no RESULT on 61c275d4-42f9-4a92-8a43-1ae94c25b730`, ask assertion at167, Promise.all index1, test at194. This file has no changes from base. No cause or flakiness claim is established.
|
||||
|
||||
On observing the red in the suite log, sent SIGTERM to this run's verified timeout process; the suite ended exit1 with `Interrupted while running: packages/cli/tests/stack-release-external.test.mjs` (it had progressed while output was being inspected). One remaining process was positively identified by an argument under this exact scratch TMPDIR and cleaned with SIGTERM; no live host process/state touched. No further tests or reruns, no old test changes.
|
||||
|
||||
Reported immediately to Sage as goldie-0016, returned slot, requested the bounded investigation/retry ruling. JSON/new-test manifest still verifies. Candidate is not verified/ready for review and has not been pushed as such. These failure notes/logs remain local evidence until Sage's ruling; no mutant acceptance claim.
|
||||
|
||||
## Mos-1560 isolated rerun — second red, stopped
|
||||
|
||||
Sage-3864 CLEAR authorized three whole-file revoke-window runs on unchanged571376c30, followed only on three passes by CLI/mutant/restored runs. Every run had to begin with date -u, uptime and the last line of sage-busy-now.sh. Only run1 occurred; it failed, reaching the second-red stop condition.
|
||||
|
||||
Log: revoke-window-run-1.log. Exact start/load lines:
|
||||
|
||||
```
|
||||
Sat Oct 24 00:24:22 UTC 2026
|
||||
19:24:22 up 10 days, 10:44, 2 users, load average: 1.46, 1.68, 2.70
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/goldieBObDQ9
|
||||
```
|
||||
|
||||
Command: `TMPDIR=<fresh mktemp above> timeout -k 10 180 node --test --test-reporter=tap packages/cli/tests/revoke-window.test.mjs`.
|
||||
|
||||
Whole file result: 0/1 pass, 1 failure, exit1, no skips/cancellations. Same unchanged row306 R4 test at revoke-window.test.mjs:170 failed after20126.967034ms: `no RESULT on dc4ccc94-4d08-4da1-bc66-2f4fcfc098dc`, ask167, Promise.all index1, test194. No edits, no run2/3, no full CLI rerun, no mutant/restored run. No remaining process with this exact run's scratch path was found after the file finished. Manifest still matches. This proves repeat failure under the recorded CLEAR/load conditions, not a root cause.
|
||||
|
||||
Reported to Sage as goldie-0018 with full log path, time/load/busy lines, test and error. Slot returned; holding for root-cause assignment/ruling on base dd6cb5872. Remote goldie/row323 stays the unverified draft571376c30; post-run failure evidence remains local. All row323 product/test sources are still exactly those committed before the first slot.
|
||||
|
||||
## Root cause correction and accepted evidence — mos-1561, sage-3869
|
||||
|
||||
The repeated revoke-window red was caused by TMPDIR length, not Codex. Sage identified and reproduced it (sage-3869; recorded in their local dda2d07b4): newRun rejects a tools.sock path exceeding107 bytes at packages/seat/src/session.mjs:278. The 37-byte `/mnt/storage/scratch/tmp/goldieXXXXXX` combined with revoke-window's fixture root produces109 bytes, while Sage's35-byte prefix fits107. The test drops the launcher refusal log, so the symptom was a20s no-RESULT wait. The detached base dd6cb5872 also failed0/1 after20106.869486ms with long TMPDIR (log copied as revoke-window-detached-base.log). Prior messages attributing the difference to an unspecified environment are superseded by this diagnosis; no product/old-test fix is needed for row323.
|
||||
|
||||
From now on every suite uses `export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)` (31bytes). Under sage-3869 CLEAR, all source/test bytes remained exactly571376c30; each run got its own fresh31-byte TMPDIR and began with date-u, uptime, and busy-helper's last line:
|
||||
|
||||
| Run / log | UTC start | Load averages | Busy | Result |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| revoke-window-short-tmp.log | 2026-10-24 00:36:41 | 1.01,1.34,1.91 | CLEAR | Whole file1/1 pass; test703.937594ms, file838.51754ms; TMPDIR lCsnmg |
|
||||
| test-business-short-tmp.log | 2026-10-24 00:36:50 | 1.08,1.35,1.91 | CLEAR | Whole suite261/261 pass, 6415.261266ms; TMPDIR flQGnB |
|
||||
| test-new-short-tmp.log | 2026-10-24 00:37:03 | 1.37,1.40,1.92 | CLEAR | Whole new file7/7 pass, 611.516605ms; TMPDIR HNhoti |
|
||||
| mutant-driver.log + mutant.log | 2026-10-24 00:37:12 | 1.38,1.41,1.92 | CLEAR | Mutant killed: targeted exact-document test0/1, expected exit1; driver exit0; TMPDIR XAmmxp |
|
||||
| test-new-restored.log | 2026-10-24 00:37:25 | 1.61,1.46,1.93 | CLEAR | Restored new file7/7 pass, 616.699665ms; TMPDIR IVLOcQ |
|
||||
|
||||
All TMPDIRs above are under /mnt/storage/scratch/tmp/. Every run used timeout-k10 (180s limit, business suite600s), one run at a time. Positive runs have0 failures/skips/cancellations. These are fresh accepted row323 results on committed571376c30. No full CLI rerun was authorized or performed: under mos-1561, Sage's landing gate supplies the CLI suite acceptance/counts, while Goldie's evidence is the business suite, new file and mutant. The earlier partial CLI log remains diagnostic evidence, not a passing-suite claim.
|
||||
|
||||
Required tester-switch mutant:
|
||||
|
||||
```diff
|
||||
- "bus.tester": true
|
||||
+ "bus.tester": false
|
||||
```
|
||||
|
||||
It fails the new committed-document assertion at case1-business-file.test.mjs:66 (`false !== true`). mutant.py restores exact original bytes in finally; restored JSON SHA256 `85e6b9ac62785734fb9d854ea70ea550e171b5060b5cad0417e2f42f0b60c59e`. Both manifest entries verify after restoration, and the whole new file passes again. No pre-existing test or production source changed. git diff --check passes.
|
||||
|
||||
Committing these notes and preserved logs is evidence-only on top of the tested571376c30. The product manifest remains the same two paths/digests. Duckworth's static review and Sage's integration/full gate remain their authority. Logs have trailing whitespace removed for git diff --check; substantive output/results are unchanged. Earlier stopped runs remain recorded honestly above and in their logs.
|
||||
@@ -0,0 +1,2 @@
|
||||
85e6b9ac62785734fb9d854ea70ea550e171b5060b5cad0417e2f42f0b60c59e docs/guides/case1-business.mosaic-stack.json
|
||||
aecd1c0f0fe1eb46800a7dea6f1f2f6a965f1daa33a39f9837bb1e2c19e0abfc packages/business/tests/case1-business-file.test.mjs
|
||||
@@ -0,0 +1,6 @@
|
||||
Sat Oct 24 00:37:12 UTC 2026
|
||||
19:37:12 up 10 days, 10:57, 2 users, load average: 1.38, 1.41, 1.92
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/XAmmxp bytes=31
|
||||
bus.tester mutant: exit 1
|
||||
restored sha256 85e6b9ac62785734fb9d854ea70ea550e171b5060b5cad0417e2f42f0b60c59e
|
||||
@@ -0,0 +1,34 @@
|
||||
TAP version 13
|
||||
# Subtest: row 323: committed Case 1 document loads unchanged through the business loader
|
||||
not ok 1 - row 323: committed Case 1 document loads unchanged through the business loader
|
||||
---
|
||||
duration_ms: 4.020549
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323/packages/business/tests/case1-business-file.test.mjs:57:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly equal:
|
||||
|
||||
false !== true
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: true
|
||||
actual: false
|
||||
operator: 'strictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323/packages/business/tests/case1-business-file.test.mjs:66:10)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 113.96636
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Row 323 tester-switch mutant; run only after Sage CLEAR."""
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[4]
|
||||
source = root / 'docs/guides/case1-business.mosaic-stack.json'
|
||||
fixed = source.read_bytes()
|
||||
needle = b'"bus.tester": true'
|
||||
assert fixed.count(needle) == 1
|
||||
try:
|
||||
source.write_bytes(fixed.replace(needle, b'"bus.tester": false'))
|
||||
result = subprocess.run([
|
||||
'timeout', '-k', '10', '90', 'node', '--test', '--test-reporter=tap',
|
||||
'--test-name-pattern=row 323: committed Case 1 document loads',
|
||||
'packages/business/tests/case1-business-file.test.mjs',
|
||||
], cwd=root, env=os.environ, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
Path(__file__).with_suffix('.log').write_text(result.stdout)
|
||||
print(f'bus.tester mutant: exit {result.returncode}', flush=True)
|
||||
assert result.returncode == 1, result.stdout
|
||||
assert '# fail 1\n' in result.stdout and 'false !== true' in result.stdout, result.stdout
|
||||
finally:
|
||||
source.write_bytes(fixed)
|
||||
assert source.read_bytes() == fixed
|
||||
print('restored sha256 ' + hashlib.sha256(fixed).hexdigest(), flush=True)
|
||||
@@ -0,0 +1,39 @@
|
||||
Sat Oct 24 00:29:57 UTC 2026
|
||||
19:29:57 up 10 days, 10:49, 2 users, load average: 1.29, 1.30, 2.21
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/goldieccUVab
|
||||
dd6cb5872b972bcabb99b82589b2905e4e47dd3e
|
||||
v26.8.1
|
||||
/usr/bin/node
|
||||
TAP version 13
|
||||
# Subtest: row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
not ok 1 - row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
---
|
||||
duration_ms: 20106.869486
|
||||
type: 'test'
|
||||
location: '/mnt/storage/scratch/tmp/goldie-base2qHbGq/worktree/packages/cli/tests/revoke-window.test.mjs:170:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: 'no RESULT on 94ddbce3-5e46-49a7-8f63-60af8995c10d'
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: true
|
||||
operator: '=='
|
||||
stack: |-
|
||||
ask (file:///mnt/storage/scratch/tmp/goldie-base2qHbGq/worktree/packages/cli/tests/revoke-window.test.mjs:167:10)
|
||||
runNextTicks (node:internal/process/task_queues:65:5)
|
||||
listOnTimeout (node:internal/timers:644:9)
|
||||
process.processTimers (node:internal/timers:618:7)
|
||||
async Promise.all (index 1)
|
||||
async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/goldie-base2qHbGq/worktree/packages/cli/tests/revoke-window.test.mjs:194:3)
|
||||
async Test.run (node:internal/test_runner/test:1409:7)
|
||||
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 20235.90436
|
||||
@@ -0,0 +1,33 @@
|
||||
Sat Oct 24 00:24:22 UTC 2026
|
||||
19:24:22 up 10 days, 10:44, 2 users, load average: 1.46, 1.68, 2.70
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/goldieBObDQ9
|
||||
TAP version 13
|
||||
# Subtest: row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
not ok 1 - row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
---
|
||||
duration_ms: 20126.967034
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323/packages/cli/tests/revoke-window.test.mjs:170:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: 'no RESULT on dc4ccc94-4d08-4da1-bc66-2f4fcfc098dc'
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: true
|
||||
operator: '=='
|
||||
stack: |-
|
||||
ask (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323/packages/cli/tests/revoke-window.test.mjs:167:10)
|
||||
async Promise.all (index 1)
|
||||
async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row323/packages/cli/tests/revoke-window.test.mjs:194:3)
|
||||
async Test.run (node:internal/test_runner/test:1409:7)
|
||||
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 20266.295192
|
||||
@@ -0,0 +1,22 @@
|
||||
Sat Oct 24 00:36:41 UTC 2026
|
||||
19:36:41 up 10 days, 10:56, 2 users, load average: 1.01, 1.34, 1.91
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/lCsnmg bytes=31
|
||||
TAP version 13
|
||||
# usage: {"event":"usage-unknown","business":"acme","run":"r73cb69bf7381","instance":"assistant","turn":"7b64df9d-f571-4f43-a678-dbd5ce7714a9","source":"@none","code":"baseline-unknown"}
|
||||
# usage: {"event":"usage-unknown","business":"acme","run":"r89b03e6f1158","instance":"assistant","turn":"aaa36964-0a78-4ba1-9442-d6f0aa35f18e","source":"@none","code":"baseline-unknown"}
|
||||
# Subtest: row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
ok 1 - row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22
|
||||
---
|
||||
duration_ms: 703.937594
|
||||
type: 'test'
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 1
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 838.51754
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
||||
Sat Oct 24 00:37:25 UTC 2026
|
||||
19:37:25 up 10 days, 10:57, 2 users, load average: 1.61, 1.46, 1.93
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/IVLOcQ bytes=31
|
||||
TAP version 13
|
||||
# Subtest: row 323: committed Case 1 document loads unchanged through the business loader
|
||||
ok 1 - row 323: committed Case 1 document loads unchanged through the business loader
|
||||
---
|
||||
duration_ms: 3.816879
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
ok 2 - row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
---
|
||||
duration_ms: 70.395579
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
ok 3 - row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
---
|
||||
duration_ms: 164.092704
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
ok 4 - row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
---
|
||||
duration_ms: 48.455987
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: factory install refuses bus.tester false before writing
|
||||
ok 5 - row 323: factory install refuses bus.tester false before writing
|
||||
---
|
||||
duration_ms: 32.684801
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: literal library key refuses through final business validation
|
||||
ok 6 - row 323: literal library key refuses through final business validation
|
||||
---
|
||||
duration_ms: 147.619094
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: a library project refuses through the factory profile plan
|
||||
ok 7 - row 323: a library project refuses through the factory profile plan
|
||||
---
|
||||
duration_ms: 37.280986
|
||||
type: 'test'
|
||||
...
|
||||
1..7
|
||||
# tests 7
|
||||
# suites 0
|
||||
# pass 7
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 616.699665
|
||||
@@ -0,0 +1,56 @@
|
||||
Sat Oct 24 00:37:03 UTC 2026
|
||||
19:37:03 up 10 days, 10:56, 2 users, load average: 1.37, 1.40, 1.92
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/HNhoti bytes=31
|
||||
TAP version 13
|
||||
# Subtest: row 323: committed Case 1 document loads unchanged through the business loader
|
||||
ok 1 - row 323: committed Case 1 document loads unchanged through the business loader
|
||||
---
|
||||
duration_ms: 4.019742
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
ok 2 - row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
---
|
||||
duration_ms: 71.809045
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
ok 3 - row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
---
|
||||
duration_ms: 158.271252
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
ok 4 - row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
---
|
||||
duration_ms: 47.408675
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: factory install refuses bus.tester false before writing
|
||||
ok 5 - row 323: factory install refuses bus.tester false before writing
|
||||
---
|
||||
duration_ms: 33.173372
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: literal library key refuses through final business validation
|
||||
ok 6 - row 323: literal library key refuses through final business validation
|
||||
---
|
||||
duration_ms: 157.331263
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: a library project refuses through the factory profile plan
|
||||
ok 7 - row 323: a library project refuses through the factory profile plan
|
||||
---
|
||||
duration_ms: 31.660009
|
||||
type: 'test'
|
||||
...
|
||||
1..7
|
||||
# tests 7
|
||||
# suites 0
|
||||
# pass 7
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 611.516605
|
||||
@@ -0,0 +1,52 @@
|
||||
TAP version 13
|
||||
# Subtest: row 323: committed Case 1 document loads unchanged through the business loader
|
||||
ok 1 - row 323: committed Case 1 document loads unchanged through the business loader
|
||||
---
|
||||
duration_ms: 4.475258
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
ok 2 - row 323: real business validate accepts the Case 1 fixture without tracker credentials
|
||||
---
|
||||
duration_ms: 60.93225
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
ok 3 - row 323: real factory install accepts the document's scratch input and writes it unchanged
|
||||
---
|
||||
duration_ms: 166.799569
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
ok 4 - row 323: the Case 1 business boots without a tracker or @sync credential
|
||||
---
|
||||
duration_ms: 48.481068
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: factory install refuses bus.tester false before writing
|
||||
ok 5 - row 323: factory install refuses bus.tester false before writing
|
||||
---
|
||||
duration_ms: 33.722908
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: literal library key refuses through final business validation
|
||||
ok 6 - row 323: literal library key refuses through final business validation
|
||||
---
|
||||
duration_ms: 151.757296
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 323: a library project refuses through the factory profile plan
|
||||
ok 7 - row 323: a library project refuses through the factory profile plan
|
||||
---
|
||||
duration_ms: 33.339369
|
||||
type: 'test'
|
||||
...
|
||||
1..7
|
||||
# tests 7
|
||||
# suites 0
|
||||
# pass 7
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 613.804933
|
||||
@@ -0,0 +1,90 @@
|
||||
# Optional business tracker (Q247) implementation plan — row 324 / #1820
|
||||
|
||||
Goldie, 2026-10-23. Base 860afc2b66e7bbd045e9d5e25d227ef12f62c55b, approved row298 candidate. New worktree /home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324, branch goldie/row324. Spec: git object f375f4e92:docs/plans/2026-10-23_optional-tracker-brief.md, section Brief: tracker becomes optional in the business file. Sage-3836 and mos-1555/Q247 authorize optional tracker; Duckworth reviews credential registration.
|
||||
|
||||
## Goal and design
|
||||
|
||||
A raw business document may omit tracker. Normalize omission to tracker:null, consistent with existing optional launch/role-tracker representations. A present tracker (including null, false, malformed objects) must continue through checkTracker and its existing refusals; distinguish absence with Object.hasOwn rather than a truthiness check. Keep checkTracker unchanged. Required top-level keys no longer include tracker. Only compare role bots with the sync bot when a checked business tracker exists. Role definitions needing vikunja still require their own tracker and credentials unchanged.
|
||||
|
||||
CLI validate collects sync credential refs only when tracker is present. bootConfig's trackerFor returns null before interpreting tracker variables when business tracker is absent, so an old tracker.baseUrl variable cannot accidentally create sync/task adapter configuration. Console businessView emits no sync credential rows for absent tracker. Runtime registration, fixed-configuration reload comparison, instance-settings bot collision checks and broker credential display already use optional chaining on the row298 base; preserve those checks. The tasks adapter remains fail-closed for an explicitly inconsistent raw adapter config, and no-tracker bootConfig never configures it for this business.
|
||||
|
||||
## Reader audit (before implementation)
|
||||
|
||||
Command: git grep -n -E 'tracker\.sync|\.tracker\b' -- 'packages/*/src/*'. Every result is grouped here, with base line numbers:
|
||||
|
||||
| Reader | Absent business tracker behavior / action |
|
||||
| --- | --- |
|
||||
| business/src/business.mjs:85-96,296-300 | checkTracker remains exact for present input; add omission normalization and guard own-sync-bot comparison. |
|
||||
| business/src/business.mjs:143-150 | Per-role tracker, independent of business tracker. Keep requirement/collision checks unchanged. |
|
||||
| business/src/cli.mjs:85 | Guard Object.values sync credential refs; absent contributes zero. |
|
||||
| business/src/instance-settings.mjs:108,120,126-127,133,223 | Role add/change and bot collision/retirement: old.tracker?.sync already skips absent sync; role trackers unchanged. No edit needed. |
|
||||
| business/src/launch-settings.mjs:161,235-236 | Per-role tracker assistant view/update, independent of business tracker; no edit. |
|
||||
| business/src/resolve.mjs:93 | Per-role tracker in resolved output; no edit. |
|
||||
| bus/src/runtime.mjs:131 | Existing b.tracker?.sync?.credentials check registers no @sync when absent; preserve and exercise with new boot test. |
|
||||
| bus/src/runtime.mjs:243,247 | Reload compares per-role tracker/null and canonical business sync/null; safe already, no edit. |
|
||||
| bus/src/broker.mjs:3969-3970 | Optional per-role tracker bot map; independent, no edit. |
|
||||
| bus/src/broker.mjs:4497 | Optional per-role tracker returned from instance write; independent, no edit. |
|
||||
| bus/src/broker.mjs:6242 | Credential display resolves optional sync bot; no @sync row exists on trackerless boot. Safe already. |
|
||||
| cli/src/config.mjs:57-60,137-138 (trackerFor; not caught by literal grep) | Add business-tracker absence guard; do not configure tracker adapter even if tracker.baseUrl remains set. |
|
||||
| cli/src/instances.mjs:118 | Builds per-role tracker update request only when user supplies bot; unrelated to business tracker. |
|
||||
| tasks/src/adapter.mjs:25,28-30,35,45,81 | settings rejects missing sync with controlled tracker-config before dereferencing labels/botId; files uses optional chaining. Factory iterates only configured trackers; absent business never configured by bootConfig. Preserve explicit-inconsistent-config refusal. |
|
||||
| webui/src/reads.mjs:164,166 | Role credentials independent; guard sync credential display at166 so trackerless Console does not throw. |
|
||||
| webui/src/public/bus.js:785 | Per-role editor tracker patch; independent of business tracker. |
|
||||
| conversation/src/controller.mjs:443,502,748,807,820,831,888,935,961,997,1008,1021,1048,1056,1268,1308,1330,1334,1413,1447 | Different execution-tracking object/class, unrelated to business/Vikunja config. No change. |
|
||||
|
||||
Positive control: grep finds business.mjs:296 and webui reads:166, the unsafe business-tracker readers. Review focus: present null/malformed tracker still refuses; per-role Vikunja trackers still required; stale tracker variables do not enable sync; Console credential view remains usable; no @sync registration during runtime boot/reload.
|
||||
|
||||
## Steps and evidence plan
|
||||
|
||||
1. Document design and full reader list (this file); claim row324 in-progress as Goldie.
|
||||
2. Add new tests for absent tracker loader+CLI validate, malformed/present preservation, per-role tracker requirement, stale-variable bootConfig behavior, actual bus boot with empty credential list/no @sync, and Console business view. New test files only unless Mos expressly approves old-test change below.
|
||||
3. Make the minimal four-source-file change (business loader, business CLI, cli config, webui reads). Existing runtime/instance-settings/broker optional checks require coverage, not gratuitous edits.
|
||||
4. Static diff checks. Push untested candidate only as expressly ordered by sage-3836, then ask Sage for CLEAR. No tests before CLEAR.
|
||||
5. On CLEAR, new test files whole, applicable existing whole files/suites, then a mutant removing loader presence check. Restore and check source bytes. Record outputs; full landing gate is Sage's. Duckworth review follows.
|
||||
|
||||
## Pre-existing test boundary
|
||||
|
||||
Inspection finds packages/business/tests/business.test.mjs:98 loops over [human,arbiters,projects,tracker,roles] and :99 asserts deletion of each refuses. Q247 directly invalidates the tracker iteration. Proposed sole old-test edit: remove just the tracker string from that required-key list, leaving all other iterations/assertions intact, and add explicit tracker-omission coverage in a new test file. This requires Mos OK under mos-1272; stop implementation and report before editing existing tests. No tests have run.
|
||||
|
||||
|
||||
## Implementation draft and old-test ruling
|
||||
|
||||
Sage-3840 relays Mos mos-1556: mos-1272 OK to remove only tracker from business.test.mjs's required-key list, preserving the four other keys and the assertion. Done exactly, one addition/one deletion at original line98; no other old test edited. All positive/negative coverage is in two new files: business/tests/optional-tracker.test.mjs and cli/tests/optional-tracker.test.mjs. The latter covers Console businessView as required by sage-3840.
|
||||
|
||||
Design now implemented in the four source files identified above; README explains omission versus literal null. New tests cover loader/CLI absence, present value preservation, malformed/present undefined or null refusal, unchanged role-bot requirement, stale business/project tracker variables, actual runtime startup with no credential files or @sync reference, Console view, and continued missing-token refusal for a present tracker. Runtime test uses useFile on unknown @sync to distinguish an unregistered reference from a registered but unavailable credential; callback must never run. Fixture project tracker.project is set, so removing trackerFor's presence guard would actually configure sync instead of passing accidentally through the no-project guard.
|
||||
|
||||
No tests run. New node_modules symlink points to canonical checkout dependencies and is ignored; it will not be committed. Sage-3836 expressly requests push before any tests; that specific sequencing overrides the usual green-before-commit default only for this draft push. Status is untested until CLEAR. Claim wrote goldie-0324-start-1 revision3759, briefed→in-progress. Row323 remains paused at the product boundary until324 lands.
|
||||
|
||||
Static verification: node --check passed for all four edited source modules and both new test files; git diff --check passed. No node --test or shell suite was run. reader-audit.txt captures the requested grep on the draft. MANIFEST.sha256 pins README, four source files, the one existing test and both new tests. mutant.py prepares the required omission-presence mutant and restores/checks bytes in finally; it has not run.
|
||||
|
||||
Proposed CLEAR run order: new business optional-tracker.test.mjs whole (5 tests), new cli optional-tracker.test.mjs whole (4 tests), existing business.test.mjs whole, then the targeted presence mutant and restored new tests. Broaden to business/CLI/Console/bus package gates only when Sage assigns that scope/slot. All test runs use timeout -k 10 and an explicitly allocated TMPDIR under /mnt/storage/scratch/tmp.
|
||||
|
||||
## Rebased verification — sage-3850 CLEAR
|
||||
|
||||
Rebased c4b14952e onto pushed refactor `3569bf54f53870fcd4e92cdbbb95e3412f629f3a` as `7e59ba8b3d6f63c30a0fa308c0607dfc1e47dd12`. The landing range has no changes in this row's business/config/Console source paths. Only BUILD-LOG.md and docs/SESSIONS.md conflicted: retained the entire landed file then appended this row's original suffix. All eight manifest-pinned files remain byte-equal to the draft; range-diff differences are log context only. To obey no-force-push while preserving the published draft, the rebased candidate is published on `goldie/row324-r2`; original remote goldie/row324 remains at c4b14952e.
|
||||
|
||||
Scratch TMPDIR was created by `mktemp -d /mnt/storage/scratch/tmp/goldieXXXXXX`: `/mnt/storage/scratch/tmp/goldieOdgIjr`. All runs were serialized, with timeout -k 10 (120s new files/mutant driver, 180s existing whole file). Node v26.8.1. No test used /tmp or a real host configuration/credential.
|
||||
|
||||
| Order | Whole file / check | Result | Evidence |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | packages/business/tests/optional-tracker.test.mjs | 5/5 pass, exit0 | test-business-optional.log |
|
||||
| 2 | packages/cli/tests/optional-tracker.test.mjs | 4/4 pass, exit0 | test-cli-optional.log |
|
||||
| 3 | packages/business/tests/business.test.mjs | 21/21 pass, exit0 | test-business-existing.log |
|
||||
| 4 | Presence-check mutant, targeted omission test | 0/1 pass, exit1, killed as expected | mutant.log, mutant-driver.log |
|
||||
| 5 | Restored new business file whole | 5/5 pass, exit0 | test-business-restored.log |
|
||||
| 6 | Restored new CLI file whole | 4/4 pass, exit0 | test-cli-restored.log |
|
||||
|
||||
No unexpected first-run red or old-test red. The Console absent-tracker read test is the third test in the new CLI file and passed on both whole-file runs. There is no separate edited webui test file; the only test files touched by this row are the three whole files above. Existing fixture preservation is covered by the whole business.test.mjs, including its untouched fixture, role bot, credential, sync bot collision and example checks. Broader package/script gates remain Sage's.
|
||||
|
||||
Required mutant diff (business/src/business.mjs:296):
|
||||
|
||||
```diff
|
||||
- const tracker = Object.hasOwn(document, "tracker") ? checkTracker(document.tracker, file) : null;
|
||||
+ const tracker = checkTracker(document.tracker, file);
|
||||
```
|
||||
|
||||
The new omission test fails with BusinessError `tracker must be a JSON object`. mutant.py restores in finally and checks exact bytes. Restored business.mjs SHA256: `e76db42158f396ec6a44f97320355d6c2254dccb5fc53cd04e8ac42843b0d60a`. Full manifest check passes after restoration. git diff --check passes. The full reader list remains above; reader-audit.txt is its raw grep receipt.
|
||||
|
||||
The only pre-existing test delta still is exactly one list entry removed at business.test.mjs:98, under mos-1556: tracker is removed; human/arbiters/projects/roles and the assertion remain. No production/test source changed during verification. Only evidence and append-only log notes follow the tested rebased commit. Slot is returned in the completion report; Duckworth review and Sage's full landing gate remain pending.
|
||||
|
||||
Evidence log trailing whitespace is normalized for git diff --check; test results and output content are unchanged.
|
||||
@@ -0,0 +1,8 @@
|
||||
ecd63bcbeae04236c445e74493d425e4a5d1bfb723d72fb58446e1d020e4d429 packages/business/README.md
|
||||
e76db42158f396ec6a44f97320355d6c2254dccb5fc53cd04e8ac42843b0d60a packages/business/src/business.mjs
|
||||
afef5c581beb26d0b0797faeb2acd3597597d48c0ce03cf31cce8d9e9b2690b9 packages/business/src/cli.mjs
|
||||
2ec0c80361f12c0d90669a3c149dfc5c9607fc9dbad98ccd30d24cf14c0de9b6 packages/cli/src/config.mjs
|
||||
46defeb3b4be0e1a22e4b89445872ad6b15cdee182076e9e6222219ae29997f4 packages/webui/src/reads.mjs
|
||||
3f99c2d7e41b37f82c3f9edeac3bc795f982dac9a3728f7694cfd5668e944691 packages/business/tests/business.test.mjs
|
||||
e92a976bcd6bf97c1cf7832d9e3fe4dec350d92809f26f7b43b938bad7e5dc49 packages/business/tests/optional-tracker.test.mjs
|
||||
7723e299fb9a11cf313d3b26df8c0270b15982731dfc0af2463c96f23e1582a2 packages/cli/tests/optional-tracker.test.mjs
|
||||
@@ -0,0 +1,2 @@
|
||||
presence mutant: exit 1
|
||||
restored sha256 e76db42158f396ec6a44f97320355d6c2254dccb5fc53cd04e8ac42843b0d60a
|
||||
@@ -0,0 +1,32 @@
|
||||
TAP version 13
|
||||
# Subtest: Q247: omitting tracker loads as null and preserves independent role bots
|
||||
not ok 1 - Q247: omitting tracker loads as null and preserves independent role bots
|
||||
---
|
||||
duration_ms: 4.653674
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/tests/optional-tracker.test.mjs:21:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: '/mnt/storage/scratch/tmp/goldieOdgIjr/mosaic-business-IHRJco/config/businesses/acme.json tracker must be a JSON object'
|
||||
code: 'ERR_TEST_FAILURE'
|
||||
name: 'BusinessError'
|
||||
stack: |-
|
||||
refuse (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/src/errors.mjs:12:9)
|
||||
requireObject (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/src/util.mjs:11:30)
|
||||
checkTracker (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/src/business.mjs:87:3)
|
||||
validateBusinessDocument (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/src/business.mjs:296:19)
|
||||
loadBusiness (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/src/business.mjs:325:10)
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row324/packages/business/tests/optional-tracker.test.mjs:25:13)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 54.363835
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Q247 targeted presence-check mutant; run only in an approved test slot."""
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[4]
|
||||
source = root / 'packages/business/src/business.mjs'
|
||||
fixed = source.read_bytes()
|
||||
needle = b'Object.hasOwn(document, "tracker") ? checkTracker(document.tracker, file) : null'
|
||||
assert fixed.count(needle) == 1
|
||||
mutant = fixed.replace(needle, b'checkTracker(document.tracker, file)')
|
||||
try:
|
||||
source.write_bytes(mutant)
|
||||
result = subprocess.run([
|
||||
'timeout', '-k', '10', '90', 'node', '--test', '--test-reporter=tap',
|
||||
'--test-name-pattern=Q247: omitting tracker',
|
||||
'packages/business/tests/optional-tracker.test.mjs',
|
||||
], cwd=root, env=os.environ, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
Path(__file__).with_suffix('.log').write_text(result.stdout)
|
||||
print(f'presence mutant: exit {result.returncode}', flush=True)
|
||||
assert result.returncode == 1, result.stdout
|
||||
assert '# fail 1\n' in result.stdout and 'tracker' in result.stdout, result.stdout
|
||||
finally:
|
||||
source.write_bytes(fixed)
|
||||
assert source.read_bytes() == fixed
|
||||
print('restored sha256 ' + hashlib.sha256(fixed).hexdigest(), flush=True)
|
||||
@@ -0,0 +1,62 @@
|
||||
packages/bus/src/broker.mjs:3969: .filter(([, r]) => r.tracker?.botId !== undefined)
|
||||
packages/bus/src/broker.mjs:3970: .map(([role, r]) => [r.tracker.botId, role]),
|
||||
packages/bus/src/broker.mjs:4497: return { transaction: out.transaction, retry: false, ...(out.tracker ? { bot: out.tracker.bot } : {}) };
|
||||
packages/bus/src/broker.mjs:6242: bot = sync ? b.tracker?.sync?.bot : service === 'vikunja' ? b.roles?.[role]?.tracker?.bot : null;
|
||||
packages/bus/src/runtime.mjs:131: if (b.tracker?.sync?.credentials) references[business + '/@sync'] = b.tracker.sync.credentials;
|
||||
packages/bus/src/runtime.mjs:243: tracker: r?.tracker ?? null,
|
||||
packages/bus/src/runtime.mjs:247: sync: canonicalJson(b.tracker?.sync ?? null),
|
||||
packages/business/src/business.mjs:89: requireObject(tracker.sync, `${where}.sync`);
|
||||
packages/business/src/business.mjs:90: rejectUnknownKeys(tracker.sync, ["bot", "botId", "credentials"], `${where}.sync`);
|
||||
packages/business/src/business.mjs:92: ...checkBot({ bot: tracker.sync.bot, botId: tracker.sync.botId }, `${where}.sync`),
|
||||
packages/business/src/business.mjs:93: credentials: checkCredentialMap(tracker.sync.credentials, ["vikunja"], `${where}.sync.credentials`),
|
||||
packages/business/src/business.mjs:143: if (entry.tracker === undefined) refuse(`${at} needs "tracker" with its Vikunja bot, because ${entry.definition} uses vikunja`);
|
||||
packages/business/src/business.mjs:144: tracker = checkBot(entry.tracker, `${at}.tracker`);
|
||||
packages/business/src/business.mjs:145: if (bots.has(tracker.bot)) refuse(`${at}.tracker.bot ${tracker.bot} is used by another instance`);
|
||||
packages/business/src/business.mjs:146: if (botIds.has(tracker.botId)) refuse(`${at}.tracker.botId ${tracker.botId} is used by another instance`);
|
||||
packages/business/src/business.mjs:149: } else if (entry.tracker !== undefined) {
|
||||
packages/business/src/business.mjs:296: const tracker = Object.hasOwn(document, "tracker") ? checkTracker(document.tracker, file) : null;
|
||||
packages/business/src/business.mjs:298: if (tracker && role.tracker && (role.tracker.bot === tracker.sync.bot || role.tracker.botId === tracker.sync.botId)) {
|
||||
packages/business/src/business.mjs:299: refuse(`${file} tracker.sync must use its own bot, not the one roles.${instance} uses`);
|
||||
packages/business/src/cli.mjs:85: ...Object.values(business.tracker?.sync?.credentials ?? {}),
|
||||
packages/business/src/instance-settings.mjs:108:// needs a bus restart, then row 199's issue. A vikunja definition needs args.tracker, an existing
|
||||
packages/business/src/instance-settings.mjs:120: tracker = checkBot(args.tracker, "tracker");
|
||||
packages/business/src/instance-settings.mjs:126: const taken = Object.entries(old.roles).filter(([name, entry]) => name !== instance && entry.tracker).map(([name, entry]) => [name, entry.tracker]);
|
||||
packages/business/src/instance-settings.mjs:127: if (old.tracker?.sync) taken.push(["tracker.sync", old.tracker.sync]);
|
||||
packages/business/src/instance-settings.mjs:133: out.tracker = tracker;
|
||||
packages/business/src/instance-settings.mjs:223: const bot = old.roles[instance]?.tracker?.bot;
|
||||
packages/business/src/launch-settings.mjs:161: definition: entry.definition, vars: entry.vars ?? {}, tracker: entry.tracker ?? null,
|
||||
packages/business/src/launch-settings.mjs:235: if (entry.tracker === null) delete next.roles[instance].tracker;
|
||||
packages/business/src/launch-settings.mjs:236: else if (Object.hasOwn(entry, "tracker")) next.roles[instance].tracker = entry.tracker;
|
||||
packages/business/src/resolve.mjs:93: tracker: entry.tracker,
|
||||
packages/cli/src/config.mjs:58: if (!b.tracker) return null;
|
||||
packages/cli/src/instances.mjs:118: if (bot) args.tracker = { bot: values["--bot"], botId: Number(values["--bot-id"]) };
|
||||
packages/conversation/src/controller.mjs:443: exec.tracker = new Tracker({
|
||||
packages/conversation/src/controller.mjs:502: if (exec.link.poisoned || exec.tracker.overlapped || this.closers.has("uncertain")) return;
|
||||
packages/conversation/src/controller.mjs:748: return { fence: true, closer, fenceIdx: this.exec?.tracker.idx ?? 0 };
|
||||
packages/conversation/src/controller.mjs:807: seam: { replay: "unavailable", streamEpoch: exec?.streamEpoch ?? null, fromSequence: (exec?.seq ?? 0) + 1, reconcile: true, quiet: !exec?.tracker.current && !exec?.tracker.slot },
|
||||
packages/conversation/src/controller.mjs:820: if (exec?.tracker.slot) return refused(BUSY);
|
||||
packages/conversation/src/controller.mjs:831: exec.tracker.attach(slot);
|
||||
packages/conversation/src/controller.mjs:888: const tr = slot.exec.tracker;
|
||||
packages/conversation/src/controller.mjs:935: const tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:961: const exec = slot.exec, tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:997: this.#settleFailure(exec.tracker.slot, RUN_OVERLAP);
|
||||
packages/conversation/src/controller.mjs:1008: this.evidence.gaps.push({ kind, detail, idx: exec.tracker.idx });
|
||||
packages/conversation/src/controller.mjs:1021: const tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:1048: const tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:1056: const tr = exec.tracker, s = tr.slot;
|
||||
packages/conversation/src/controller.mjs:1268: const exec = this.exec, tr = exec?.tracker;
|
||||
packages/conversation/src/controller.mjs:1308: const tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:1330: exec.tracker.clearWindow = Math.max(0, exec.tracker.clearWindow - 1);
|
||||
packages/conversation/src/controller.mjs:1334: const { exec, stop } = ctx, tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:1413: const { exec } = ctx, tr = exec.tracker;
|
||||
packages/conversation/src/controller.mjs:1447: const { exec, stop } = ctx, tr = exec.tracker;
|
||||
packages/tasks/src/adapter.mjs:25: if (!positive(b.tracker?.sync?.botId)) throw new BusError('tracker-config');
|
||||
packages/tasks/src/adapter.mjs:28: if (r.tracker) {
|
||||
packages/tasks/src/adapter.mjs:29: if (!positive(r.tracker.botId) || typeof r.definition !== 'string') throw new BusError('tracker-config');
|
||||
packages/tasks/src/adapter.mjs:30: roles[role] = { definition: r.definition, botId: r.tracker.botId };
|
||||
packages/tasks/src/adapter.mjs:35: for (const [title, id] of Object.entries(b.tracker.labels ?? {})) {
|
||||
packages/tasks/src/adapter.mjs:45: if (b.tracker?.sync?.credentials?.vikunja?.file) out['@sync'] = b.tracker.sync.credentials.vikunja.file;
|
||||
packages/tasks/src/adapter.mjs:81: bots: new Set([businesses[name].tracker.sync.botId, ...Object.values(s.roles).map((r) => r.botId)]),
|
||||
packages/webui/src/public/bus.js:785: if(f==='tracker'&&text.trim()===''){patch.tracker=null;continue;}
|
||||
packages/webui/src/reads.mjs:164: for (const [service, ref] of Object.entries(r.credentials)) credentials.push(credential(service, ref, service === 'vikunja' ? r.tracker?.bot ?? null : null, instance, now));
|
||||
packages/webui/src/reads.mjs:166: for (const [service, ref] of Object.entries(b.tracker?.sync?.credentials ?? {})) credentials.push(credential(service, ref, b.tracker.sync.bot, 'tracker sync', now));
|
||||
@@ -0,0 +1,136 @@
|
||||
TAP version 13
|
||||
# Subtest: C2-04: library mappings load without roots, remain frozen, and retain mapping refusals
|
||||
ok 1 - C2-04: library mappings load without roots, remain frozen, and retain mapping refusals
|
||||
---
|
||||
duration_ms: 10.358877
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: C2-04: an otherwise valid publish grant cannot name a library repo
|
||||
ok 2 - C2-04: an otherwise valid publish grant cannot name a library repo
|
||||
---
|
||||
duration_ms: 1.405968
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: config directory and file path follow MOSAIC_CONFIG
|
||||
ok 3 - config directory and file path follow MOSAIC_CONFIG
|
||||
---
|
||||
duration_ms: 0.194682
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: the fixture business validates and comes back frozen
|
||||
ok 4 - the fixture business validates and comes back frozen
|
||||
---
|
||||
duration_ms: 0.883527
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: two instances may share a definition
|
||||
ok 5 - two instances may share a definition
|
||||
---
|
||||
duration_ms: 0.869413
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: top-level refusals
|
||||
ok 6 - top-level refusals
|
||||
---
|
||||
duration_ms: 2.622608
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: arbiters and projects
|
||||
ok 7 - arbiters and projects
|
||||
---
|
||||
duration_ms: 3.523393
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: role instances
|
||||
ok 8 - role instances
|
||||
---
|
||||
duration_ms: 2.015683
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Vikunja bots
|
||||
ok 9 - Vikunja bots
|
||||
---
|
||||
duration_ms: 4.64219
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a role without Vikunja takes no tracker block
|
||||
ok 10 - a role without Vikunja takes no tracker block
|
||||
---
|
||||
duration_ms: 5.70969
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: credential references match the definition's services
|
||||
ok 11 - credential references match the definition's services
|
||||
---
|
||||
duration_ms: 1.94586
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: launch
|
||||
ok 12 - launch
|
||||
---
|
||||
duration_ms: 6.957271
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses
|
||||
ok 13 - REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses
|
||||
---
|
||||
duration_ms: 3.58016
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: loadBusiness: file checks
|
||||
ok 14 - loadBusiness: file checks
|
||||
---
|
||||
duration_ms: 1.772126
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: loadBusiness: not a regular file
|
||||
ok 15 - loadBusiness: not a regular file
|
||||
---
|
||||
duration_ms: 40.121918
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: loading writes nothing
|
||||
ok 16 - loading writes nothing
|
||||
---
|
||||
duration_ms: 1.07771
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: names that are Object.prototype properties don't count as declared
|
||||
ok 17 - names that are Object.prototype properties don't count as declared
|
||||
---
|
||||
duration_ms: 3.194384
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: the shipped example refuses as written and validates once filled in
|
||||
ok 18 - the shipped example refuses as written and validates once filled in
|
||||
---
|
||||
duration_ms: 0.919964
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: persona vars: agent layer, creative definition only, both or neither
|
||||
ok 19 - persona vars: agent layer, creative definition only, both or neither
|
||||
---
|
||||
duration_ms: 7.866405
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 194 keep, noHire and retired validate, and a file without them loads as before
|
||||
ok 20 - row 194 keep, noHire and retired validate, and a file without them loads as before
|
||||
---
|
||||
duration_ms: 1.704708
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 194 each bad keep, noHire or retired entry refuses
|
||||
ok 21 - row 194 each bad keep, noHire or retired entry refuses
|
||||
---
|
||||
duration_ms: 5.095541
|
||||
type: 'test'
|
||||
...
|
||||
1..21
|
||||
# tests 21
|
||||
# suites 0
|
||||
# pass 21
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 159.214713
|
||||
@@ -0,0 +1,40 @@
|
||||
TAP version 13
|
||||
# Subtest: Q247: omitting tracker loads as null and preserves independent role bots
|
||||
ok 1 - Q247: omitting tracker loads as null and preserves independent role bots
|
||||
---
|
||||
duration_ms: 5.298841
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: real business validate passes without a sync credential reference or file
|
||||
ok 2 - Q247: real business validate passes without a sync credential reference or file
|
||||
---
|
||||
duration_ms: 71.475563
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: present tracker retains its values and validation without mutating the input
|
||||
ok 3 - Q247: present tracker retains its values and validation without mutating the input
|
||||
---
|
||||
duration_ms: 2.384907
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: present null, undefined and malformed trackers still refuse
|
||||
ok 4 - Q247: present null, undefined and malformed trackers still refuse
|
||||
---
|
||||
duration_ms: 3.746
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: absent business tracker does not waive a role's Vikunja bot requirement
|
||||
ok 5 - Q247: absent business tracker does not waive a role's Vikunja bot requirement
|
||||
---
|
||||
duration_ms: 0.804648
|
||||
type: 'test'
|
||||
...
|
||||
1..5
|
||||
# tests 5
|
||||
# suites 0
|
||||
# pass 5
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 135.467249
|
||||
@@ -0,0 +1,40 @@
|
||||
TAP version 13
|
||||
# Subtest: Q247: omitting tracker loads as null and preserves independent role bots
|
||||
ok 1 - Q247: omitting tracker loads as null and preserves independent role bots
|
||||
---
|
||||
duration_ms: 5.02203
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: real business validate passes without a sync credential reference or file
|
||||
ok 2 - Q247: real business validate passes without a sync credential reference or file
|
||||
---
|
||||
duration_ms: 68.916604
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: present tracker retains its values and validation without mutating the input
|
||||
ok 3 - Q247: present tracker retains its values and validation without mutating the input
|
||||
---
|
||||
duration_ms: 2.414133
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: present null, undefined and malformed trackers still refuse
|
||||
ok 4 - Q247: present null, undefined and malformed trackers still refuse
|
||||
---
|
||||
duration_ms: 3.820979
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: absent business tracker does not waive a role's Vikunja bot requirement
|
||||
ok 5 - Q247: absent business tracker does not waive a role's Vikunja bot requirement
|
||||
---
|
||||
duration_ms: 0.873886
|
||||
type: 'test'
|
||||
...
|
||||
1..5
|
||||
# tests 5
|
||||
# suites 0
|
||||
# pass 5
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 135.196828
|
||||
@@ -0,0 +1,34 @@
|
||||
TAP version 13
|
||||
# Subtest: Q247: bootConfig omits tracker setup even with tracker variables left in the business
|
||||
ok 1 - Q247: bootConfig omits tracker setup even with tracker variables left in the business
|
||||
---
|
||||
duration_ms: 5.854825
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: trackerless business boots and registers no @sync credential
|
||||
ok 2 - Q247: trackerless business boots and registers no @sync credential
|
||||
---
|
||||
duration_ms: 48.935736
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: Console business read works with tracker absent and shows no sync credential
|
||||
ok 3 - Q247: Console business read works with tracker absent and shows no sync credential
|
||||
---
|
||||
duration_ms: 1.663483
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: a present tracker with a missing token still refuses bus boot
|
||||
ok 4 - Q247: a present tracker with a missing token still refuses bus boot
|
||||
---
|
||||
duration_ms: 11.355417
|
||||
type: 'test'
|
||||
...
|
||||
1..4
|
||||
# tests 4
|
||||
# suites 0
|
||||
# pass 4
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 226.91643
|
||||
@@ -0,0 +1,34 @@
|
||||
TAP version 13
|
||||
# Subtest: Q247: bootConfig omits tracker setup even with tracker variables left in the business
|
||||
ok 1 - Q247: bootConfig omits tracker setup even with tracker variables left in the business
|
||||
---
|
||||
duration_ms: 5.203349
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: trackerless business boots and registers no @sync credential
|
||||
ok 2 - Q247: trackerless business boots and registers no @sync credential
|
||||
---
|
||||
duration_ms: 46.033814
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: Console business read works with tracker absent and shows no sync credential
|
||||
ok 3 - Q247: Console business read works with tracker absent and shows no sync credential
|
||||
---
|
||||
duration_ms: 1.547096
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: Q247: a present tracker with a missing token still refuses bus boot
|
||||
ok 4 - Q247: a present tracker with a missing token still refuses bus boot
|
||||
---
|
||||
duration_ms: 11.051525
|
||||
type: 'test'
|
||||
...
|
||||
1..4
|
||||
# tests 4
|
||||
# suites 0
|
||||
# pass 4
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 195.359585
|
||||
@@ -0,0 +1,48 @@
|
||||
# Row 325 / #1821 — Case 1 project key
|
||||
|
||||
Goldie, 2026-10-24. Branch goldie/row325, base99930362e4b8a64d5425b10ca087b2d1f93ae538. Separate worktree /home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row325. Brief pinned at60d78e44e:docs/plans/2026-10-24_case1-project-key-brief.md, section Brief: re-key the Case1 project to stack. Mos-1563 fixes the business document and preserves project id. Reviewer Gladstone; Sage owns review posting/landing. No queue writes authorized or performed.
|
||||
|
||||
## Defect and change
|
||||
|
||||
The real .mosaic/project.json declares id stack; the Case1 business document incorrectly keyed its project mosaic-stack. Real business validation checks equality at packages/business/src/cli.mjs:80, so the install refused once the host held the project file. Row323's scratch execution root omitted that file and exercised only the warning/absent branch, missing the deployment mismatch.
|
||||
|
||||
Only the projects object's key changes to stack in docs/guides/case1-business.mosaic-stack.json. Business id stays mosaic-stack, root stays /srv/mosaic/src/mosaic-stack, and every other field is unchanged. No .mosaic/project.json edit or product/schema/role change.
|
||||
|
||||
The new packages/business/tests/case1-project-key.test.mjs reads the repo's real .mosaic/project.json bytes, asserts exactly one matching Case1 key, copies those bytes unchanged into a scratch project root, and calls the actual business CLI validate path with a temporary system/business config. Positive output must report the project valid, not absent. Negative coverage re-keys the same fixture to mosaic-stack and requires exit2 plus the real project-id mismatch message; missing-file warnings cannot stand in for validation.
|
||||
|
||||
Mos-1564 (mos-1272) authorizes exactly one pre-existing test line: case1-business-file.test.mjs:70's expected key changes to stack; all other bytes/assertions remain unchanged. No other old test is touched. This is explicitly the obsolete expectation caused by the product correction, not a relaxed assertion.
|
||||
|
||||
## Verification plan / state
|
||||
|
||||
No tests run before Sage CLEAR. Static node --check, JSON parse and diff checks only. On CLEAR, one run at a time: new test file whole, business suite whole (includes the one updated old test), one old-key mutant, restored whole new file to verify restoration. Every run gets fresh `export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)`, timeout-k10, and the established UTC/load/busy-helper log header. Stop/report on any other old-test red; no further old-test edits. First unexpected new-test red is recorded/reported under the continuing mos-1272 rule.
|
||||
|
||||
mutant.py changes only the projects key back to mosaic-stack, runs the new exact-key assertion, requires expected exit1/fail1, then restores/checks bytes in finally. Prepared, not run. Sage's landing gate supplies CLI suite counts.
|
||||
|
||||
The dependency setup is an ignored root node_modules symlink to /mnt/storage/src/mosaic-stack/node_modules; no dependency installation/pin change. MANIFEST.sha256 covers business JSON, the one old test, new test, and real project document as an unchanged reference. Final review report goes to both Sage and Gladstone with tip/base/manifest. No host contact, credential access, queue write or refactor push.
|
||||
|
||||
## Verification — sage-3882 CLEAR
|
||||
|
||||
All four runs were serial on the same final source bytes, using timeout-k10, fresh31-byte TMPDIRs from /mnt/storage/scratch/tmp/XXXXXX, and date-u/uptime/busy-helper headers. Node v26.8.1. No first-run new-test red and no old-test red. No CLI suite run; Sage's landing gate supplies that scope.
|
||||
|
||||
| Run / log | UTC start | Load averages | Busy | Result / TMPDIR |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| test-new.log | 2026-10-24 01:13:40 | 1.34,1.60,2.50 | CLEAR | Whole new file3/3 pass, 172.161691ms; hchZu2 |
|
||||
| test-business.log | 2026-10-24 01:13:50 | 1.36,1.59,2.49 | CLEAR | Whole business suite264/264 pass, 6478.924028ms; yWVbwY |
|
||||
| mutant-driver.log + mutant.log | 2026-10-24 01:14:04 | 1.36,1.58,2.47 | CLEAR | Old-key mutant killed, targeted new test0/1, expected exit1; driver exit0; YsOE6N |
|
||||
| test-restored.log | 2026-10-24 01:14:14 | 1.15,1.53,2.44 | CLEAR | Restored new file3/3 pass, 169.948442ms; Y43Jju |
|
||||
|
||||
All TMPDIRs are under /mnt/storage/scratch/tmp/. New/restored/mutant driver limits180s, business600s, each timeout-k10. Positive runs have0 failures/skips/cancellations. The business suite includes the entire old case1-business-file.test.mjs, with only the mos-1564-approved key assertion changed.
|
||||
|
||||
Mutant diff in business JSON:
|
||||
|
||||
```diff
|
||||
"projects": {
|
||||
- "stack": {
|
||||
+ "mosaic-stack": {
|
||||
```
|
||||
|
||||
The exact-key new test fails deepStrictEqual (actual mosaic-stack, expected stack); raw evidence is mutant.log. mutant.py restores in finally and checks exact bytes. Restored JSON SHA256 `9e43bfef326e568488aaa4f0d8882e9b932750aeb39f587a113a226bfb30cf0f`; all4 MANIFEST.sha256 entries verify, including unchanged .mosaic/project.json. Whole new file passed again after restoration. git diff --check passed.
|
||||
|
||||
Final source accounting: business JSON1+/1-, only projects key; old test1+/1-, only expected key at70; new test file with3 cases; no other old-test line touched. Business id/root remain unchanged. Root-cause gap is now covered by byte-identical real project fixture plus required valid result and old-key refusal. No host action, credentials, queue writes, refactor push or unapproved scope change.
|
||||
|
||||
Logs have trailing whitespace normalized for git diff --check, with substantive output/results unchanged. The slot is returned in the report accompanying the pushed candidate; Gladstone first review and Sage landing gate remain pending.
|
||||
@@ -0,0 +1,4 @@
|
||||
9e43bfef326e568488aaa4f0d8882e9b932750aeb39f587a113a226bfb30cf0f docs/guides/case1-business.mosaic-stack.json
|
||||
9c3bf7f349d0ee8626743e1388b9a596c7f3c274beaedad5106db5f2ff8f65e7 packages/business/tests/case1-business-file.test.mjs
|
||||
b543ac6afbfb336c5566f359ff357904b36f413d5f75fcd54a5a8b72aef1dbd6 packages/business/tests/case1-project-key.test.mjs
|
||||
044714a37c267b70279977ea8ca429df7a28463271b884afd79a01bc27afa259 .mosaic/project.json
|
||||
@@ -0,0 +1,6 @@
|
||||
Sat Oct 24 01:14:04 UTC 2026
|
||||
20:14:04 up 10 days, 11:33, 2 users, load average: 1.36, 1.58, 2.47
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/YsOE6N bytes=31
|
||||
old-key mutant: exit 1
|
||||
restored sha256 9e43bfef326e568488aaa4f0d8882e9b932750aeb39f587a113a226bfb30cf0f
|
||||
@@ -0,0 +1,40 @@
|
||||
TAP version 13
|
||||
# Subtest: row 325: the Case 1 project's sole key matches the repository project id
|
||||
not ok 1 - row 325: the Case 1 project's sole key matches the repository project id
|
||||
---
|
||||
duration_ms: 2.058961
|
||||
type: 'test'
|
||||
location: '/home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row325/packages/business/tests/case1-project-key.test.mjs:38:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
+ 'mosaic-stack'
|
||||
- 'stack'
|
||||
]
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
0: 'stack'
|
||||
actual:
|
||||
0: 'mosaic-stack'
|
||||
operator: 'deepStrictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row325/packages/business/tests/case1-project-key.test.mjs:42:10)
|
||||
Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
Test.run (node:internal/test_runner/test:1402:25)
|
||||
Test.start (node:internal/test_runner/test:1262:17)
|
||||
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
|
||||
...
|
||||
1..1
|
||||
# tests 1
|
||||
# suites 0
|
||||
# pass 0
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 44.416722
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Row 325 old-project-key mutant; run only in Sage's test slot."""
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[4]
|
||||
source = root / 'docs/guides/case1-business.mosaic-stack.json'
|
||||
fixed = source.read_bytes()
|
||||
needle = b'"projects": {\n "stack": {'
|
||||
assert fixed.count(needle) == 1
|
||||
try:
|
||||
source.write_bytes(fixed.replace(needle, b'"projects": {\n "mosaic-stack": {'))
|
||||
result = subprocess.run([
|
||||
'timeout', '-k', '10', '90', 'node', '--test', '--test-reporter=tap',
|
||||
'--test-name-pattern=row 325: the Case 1 project',
|
||||
'packages/business/tests/case1-project-key.test.mjs',
|
||||
], cwd=root, env=os.environ, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
Path(__file__).with_suffix('.log').write_text(result.stdout)
|
||||
print(f'old-key mutant: exit {result.returncode}', flush=True)
|
||||
assert result.returncode == 1, result.stdout
|
||||
assert '# fail 1\n' in result.stdout and 'mosaic-stack' in result.stdout, result.stdout
|
||||
finally:
|
||||
source.write_bytes(fixed)
|
||||
assert source.read_bytes() == fixed
|
||||
print('restored sha256 ' + hashlib.sha256(fixed).hexdigest(), flush=True)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
||||
Sat Oct 24 01:13:40 UTC 2026
|
||||
20:13:40 up 10 days, 11:33, 2 users, load average: 1.34, 1.60, 2.50
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/hchZu2 bytes=31
|
||||
TAP version 13
|
||||
# Subtest: row 325: the Case 1 project's sole key matches the repository project id
|
||||
ok 1 - row 325: the Case 1 project's sole key matches the repository project id
|
||||
---
|
||||
duration_ms: 4.262251
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 325: real business validate reports the copied repository project valid
|
||||
ok 2 - row 325: real business validate reports the copied repository project valid
|
||||
---
|
||||
duration_ms: 67.962164
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 325: the old mosaic-stack project key refuses against those same project bytes
|
||||
ok 3 - row 325: the old mosaic-stack project key refuses against those same project bytes
|
||||
---
|
||||
duration_ms: 57.926665
|
||||
type: 'test'
|
||||
...
|
||||
1..3
|
||||
# tests 3
|
||||
# suites 0
|
||||
# pass 3
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 172.161691
|
||||
@@ -0,0 +1,32 @@
|
||||
Sat Oct 24 01:14:14 UTC 2026
|
||||
20:14:14 up 10 days, 11:34, 2 users, load average: 1.15, 1.53, 2.44
|
||||
CLEAR
|
||||
TMPDIR=/mnt/storage/scratch/tmp/Y43Jju bytes=31
|
||||
TAP version 13
|
||||
# Subtest: row 325: the Case 1 project's sole key matches the repository project id
|
||||
ok 1 - row 325: the Case 1 project's sole key matches the repository project id
|
||||
---
|
||||
duration_ms: 4.401838
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 325: real business validate reports the copied repository project valid
|
||||
ok 2 - row 325: real business validate reports the copied repository project valid
|
||||
---
|
||||
duration_ms: 60.555647
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: row 325: the old mosaic-stack project key refuses against those same project bytes
|
||||
ok 3 - row 325: the old mosaic-stack project key refuses against those same project bytes
|
||||
---
|
||||
duration_ms: 63.16989
|
||||
type: 'test'
|
||||
...
|
||||
1..3
|
||||
# tests 3
|
||||
# suites 0
|
||||
# pass 3
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 169.948442
|
||||
@@ -0,0 +1,331 @@
|
||||
# Row 226 build notes, C2-13b install script and library precheck (#1727)
|
||||
|
||||
Brief: docs/plans/2026-10-20_case2-rows.md "C2-13b install script and library precheck" (:1073-1134 at
|
||||
d37210288). Design: agents/gosalyn/work/case2/DESIGN.md r11 sections 4 and 10, on gosalyn/case2-design
|
||||
75fb3e10e. Row 195's needs: agents/darkwing/work/queue-195/DESIGN.md "What row 195 needs from row 226".
|
||||
|
||||
This is design round d2. Nothing is built yet. d1 was f46e66883, and Webby's review of it is webby-0085
|
||||
(CHANGES, B1 and N1-N6) and webby-0086 (the ordering problem). Sage ruled on both in sage-2189. The
|
||||
changes from d1 are listed at the end.
|
||||
|
||||
## Base
|
||||
|
||||
Branch gosalyn/c2-13b from refactor d37210288. Rows 215, 221, 222 and 230 are done.
|
||||
|
||||
## Facts the design rests on
|
||||
|
||||
- No CLI writes a business file. `mosaic business validate <id>` checks one and exits 0 ok, 2 invalid
|
||||
(packages/business/src/cli.mjs). A business file needs `human`, `arbiters`, `projects`, `tracker`
|
||||
with a sync bot and a Vikunja credential ref, and `roles`, each role with its tracker bot when its
|
||||
definition uses Vikunja (business.mjs:23, :84-100, :131-149).
|
||||
- The library is a project, `projects.<id>.kind === "library"`, checked by `checkMapping`
|
||||
(packages/library/src/mapping.mjs). There is no top-level `library` key.
|
||||
- `library.sync` and `library.status` are bus verbs, human only (broker.mjs:92, :2452-2476). The only
|
||||
client is `packages/bus/src/human-cli.mjs` over the bus socket.
|
||||
- The bus makes each library mirror at start, `ensureMirror({ business, project, dataRoot })`
|
||||
(packages/bus/src/runtime.mjs:143-145). Before the bus first starts there is no mirror.
|
||||
- `sync()` fetches outside `withLibraryLock`. Only a per-process `running` set keeps two syncs apart
|
||||
(packages/library/src/sync.mjs:97-98), so a second process calling `sync()` would race the bus's
|
||||
timer.
|
||||
- `libraryStatus(context)` reads the recorded state under the flock and writes nothing
|
||||
(mirror.mjs:232-239). A missing or unsafe mirror fails `library-git` (`ownedRoot`, mirror.mjs:43-49).
|
||||
A mirror that has never synced returns the empty state, `lastFetch: null`, `lastError: null`,
|
||||
`problems: []` (mirror.mjs:27, :205-210). `lastError` is `{ code, at }` with `code` matching
|
||||
`^[a-z-]{1,64}$` (mirror.mjs:225).
|
||||
- The bus builds the library context as `{ business, project, dataRoot }` and the slot as
|
||||
`<slotDir>/secrets/<business.id>/library-gitea` (runtime.mjs:146-151, :162-164).
|
||||
- `case2-persona-import.mjs install --staging DIR --config-dir DIR --business ID` writes
|
||||
`<configDir>/personas/<business>/<instance>.md` at 0600 in 0700 dirs, never overwriting, and prints
|
||||
`instance bean: persona.name Bean, persona.sha256 <hex>` per persona.
|
||||
- A Discord binding is `<dataRoot>/discord/<name>.json` (packages/discord/src/binding.mjs:70-83).
|
||||
- `case2-unit.sh --copy ID` needs `<home>/.local/share/mosaic-stack/copies/<ID>`, which
|
||||
`stack-copy.sh copy COMMIT --copies DIR` makes. The copy runs `npm ci` from package-lock.json
|
||||
(stack-copy.sh:70), which installs the pinned pi.
|
||||
|
||||
## Design
|
||||
|
||||
### D1. The business file is an input
|
||||
|
||||
Unchanged from d1. `case2-install.sh` takes `--business-file FILE`, a JSON business document written for
|
||||
the host from Jason's go. The script doesn't build one from flags, because bots, botIds and credential
|
||||
refs are host values and the brief says every host value is an input. It checks the input, adds what
|
||||
only it knows, writes it, and then runs `mosaic business validate`, so business.mjs stays the only
|
||||
schema.
|
||||
|
||||
What the script checks before it writes anything, beyond `validate`:
|
||||
- `id` equals `--business`.
|
||||
- `library`: at least one library project, `bus.tester` not true, roles `bean` and `chet` with
|
||||
definition `companion`, and no `persona.*` var in the input, since D3 adds them.
|
||||
- `factory`: no library project, and `bus.tester` true. These are the precheck's rules at
|
||||
case2-precheck.sh:127-135, so the install refuses what the precheck would refuse.
|
||||
|
||||
### D2. Install steps, in order
|
||||
|
||||
1. Refuse to run as root.
|
||||
2. `scripts/bootstrap.sh`. It's idempotent and never rewrites an existing config.
|
||||
3. `scripts/stack-copy.sh copy <--commit> --copies <home>/.local/share/mosaic-stack/copies`. The copy
|
||||
`case2-unit.sh --copy` names has to exist before root renders the units (row 195 need 3).
|
||||
4. `library` only: the persona import's `install` step from `--personas DIR` (D3).
|
||||
5. Write `<configDir>/businesses/<id>.json` at 0600 with O_EXCL (D4).
|
||||
6. `library` only: make `<configDir>/secrets/<business>/` at 0700, empty. The token is Jason's, design 10
|
||||
step 4.
|
||||
7. `mosaic business validate <id>`. If it refuses and step 5 created the file in this run, remove that
|
||||
file, then refuse. A file that already matched on a re-run (D4) is never removed (N4).
|
||||
8. Print the copy id and the next steps: root's 2c, Jason's 2d, 3 and 4, then the `--library-status`
|
||||
check (D6b).
|
||||
|
||||
d1's step 3, `release.sh package` and `activate`, is gone (Q4a, webby-0085 B1, sage-2189). Package is a
|
||||
`docker compose build`, and activate runs a pi Compose health check that mounts `PI_AUTH_FILE`
|
||||
(compose.yaml:43). Neither fits 2a, which has no Docker and no pi credential, and the hub doesn't run
|
||||
containers. The pinned harness comes from the copy's `npm ci` (Q3). The `CASE2_RELEASE` seam goes with
|
||||
it.
|
||||
|
||||
It starts no unit, writes no unit file, doesn't write `/etc/mosaic-stack/dev.args` and doesn't run
|
||||
`adopt`. These are my answers to row 195's needs 4 and 5 (gosalyn-0087), unchanged. Row 195's W8 now
|
||||
passes `--business-file` and has no release step.
|
||||
|
||||
### D3. Personas come from the staging dir
|
||||
|
||||
Unchanged from d1. `library` requires `--personas DIR`, the staging dir from `case2-persona-import.mjs
|
||||
stage`. The install runs `case2-persona-import.mjs install --staging DIR --config-dir <configDir>
|
||||
--business <id>` and reads its two `instance` lines into `roles.bean.vars` and `roles.chet.vars`. Only
|
||||
those two lines go into the business file. They hold a name and a digest, never text.
|
||||
|
||||
This moves the persona import's stage part of design 10 step 6 ahead of 2b: the read of her Hermes
|
||||
profiles and Jason's check of the printed pairing. Webby agreed in webby-0085 (Q1).
|
||||
|
||||
### D4. Re-runs
|
||||
|
||||
Unchanged from d1, plus N4. Each step is safe to run again. bootstrap and the copy are idempotent. An
|
||||
existing business file that matches the one the script would write passes, and the script remembers it
|
||||
didn't create it. One that differs refuses `business-file-differs`, and the script doesn't touch it.
|
||||
The persona import already refuses to overwrite. On a re-run the install compares each existing
|
||||
persona file's sha256 with the staging manifest, and refuses on a mismatch.
|
||||
|
||||
### D5. Inputs and seams
|
||||
|
||||
```
|
||||
case2-install.sh --business ID --business-file FILE --commit SHA [--profile factory|library]
|
||||
[--personas DIR] [--home DIR]
|
||||
```
|
||||
|
||||
`--home` defaults to `$HOME`, which is `/srv/mosaic` under `sudo -u mosaic -H`. `MOSAIC_CONFIG` is
|
||||
honoured as bootstrap.sh honours it. One test seam, in case2-unit.sh's `CASE2_*` pattern:
|
||||
`CASE2_STACK_COPY` is the copy script, and the test uses a stub that makes the copy dir.
|
||||
|
||||
Exit codes: 0 ok, 1 refused, 4 usage, as case2-unit.sh uses.
|
||||
|
||||
### D6. Precheck library checks, split in two (Q4b, sage-2189)
|
||||
|
||||
d1 ran `sync()` from the precheck. Webby's N1 and webby-0086 showed two faults. The precheck runs before
|
||||
the units start, so there is no mirror yet (runtime.mjs:143-145). And a precheck `sync()` after the bus
|
||||
is up would race the bus's timer, since `sync()` fetches outside the lock (sync.mjs:97-98). Sage split
|
||||
the check in sage-2189, and the brief's "runs library.sync" becomes "reads the bus's recorded sync
|
||||
state".
|
||||
|
||||
#### D6a. Before the units start, `--profile library`
|
||||
|
||||
These run where the precheck runs today, after 2c's render and before the start. They sync nothing and
|
||||
read no mirror. Each is a `refuse` line in the script's style.
|
||||
- The business file is owned by the uid, with no group or other bit (`business-file-mode`).
|
||||
`businesses/.history/<id>/` is 0700 and its `changes.jsonl` is 0600, when present (`history-mode`).
|
||||
- The persona dirs are 0700. Each creative role's persona file is regular, owned and 0600
|
||||
(`persona-mode`), and its sha256 equals the role's `persona.sha256` (`persona-mismatch`). The output
|
||||
prints names and digests, never text.
|
||||
- No `<dataRoot>/discord/*.json` exists (`discord-binding`).
|
||||
|
||||
#### D6b. `case2-precheck.sh --business ID --library-status`, after the bus is up and after step 4
|
||||
|
||||
A new mode, run as `<user>` once the bus is up and Jason has placed the token. It changes nothing. A
|
||||
node block builds the context the bus builds, `{ business, project, dataRoot }` for each library
|
||||
project, with `dataRoot` from the config (N3, runtime.mjs:146-151, :162-164). For each one it:
|
||||
- refuses `library-mirror-missing` when the mirror path doesn't exist. It checks this itself, so the
|
||||
message says the bus hasn't made it, not `library-git`;
|
||||
- calls `libraryStatus(context)` only, and refuses with the thrown code when that fails, for example
|
||||
`library-git` for an unsafe mirror or `library-sync-busy` when the flock wait runs out;
|
||||
- refuses `library-never-synced` when `lastFetch` is null;
|
||||
- refuses `library-last-error <code>` when `lastError` is not null. The code is the fixed
|
||||
`^[a-z-]{1,64}$` string, so it's safe to print. This also covers Webby's N2;
|
||||
- prints each recorded R-problem as `problem R<n> <path>`, with the path through `JSON.stringify`, then
|
||||
one `problems <count>` line (N5). Her file names are the only part that prints, never text.
|
||||
R-problems don't refuse (Q2, agreed by Webby).
|
||||
|
||||
The bus syncs each library with a `user` every 60 s, so the first run after step 4 may need a minute,
|
||||
or Jason's own `library.sync`. The run sheet says so.
|
||||
|
||||
### D7. host-run-sheet.md fix
|
||||
|
||||
`docs/guides/host-run-sheet.md:240` says "I2's install writes it" about `dev.args`. It becomes: root
|
||||
writes it at the unit render, from the values the go names, and the install never writes it, because
|
||||
`<user>` can't write `/etc` and root reads nothing `<user>` wrote. Sage asked in sage-1832 for this fix
|
||||
to go in this build commit, with its path in the manifest. Section 9's `library-gitea` line (:275) says
|
||||
"C2-13b's `library.sync` (not landed)". It becomes the `--library-status` check (D6b). The install
|
||||
section also gets a line that the copy's `npm ci` needs the npm registry from the host (Q3, webby-0085).
|
||||
|
||||
### D8. Design 10 text
|
||||
|
||||
Sage ruled that design 10 gets the same change when 226 lands, with the line in this row's manifest.
|
||||
In agents/gosalyn/work/case2/DESIGN.md:
|
||||
- step 2b drops `release.sh package` and `activate`;
|
||||
- a new step after step 4 runs `case2-precheck.sh --business <business> --library-status`;
|
||||
- the precheck's library text says it reads the bus's recorded sync state, not that it runs
|
||||
`library.sync`.
|
||||
|
||||
### D9. Tests, `packages/cli/tests/case2-install.test.mjs`
|
||||
|
||||
These follow case2-unit.test.mjs: scratch HOME, MOSAIC_CONFIG, stub bin dir and seams, a local bare repo
|
||||
cloned `--bare --no-local` as sync.test.mjs does, and the fixture's key never in output.
|
||||
- Install `library`:
|
||||
- writes the business file at 0600 with bean's and chet's persona vars;
|
||||
- writes the persona files at 0600 in 0700 dirs and the empty slot dir at 0700;
|
||||
- logs the copy call and no release call;
|
||||
- starts nothing;
|
||||
- passes on a re-run;
|
||||
- refuses `business-file-differs` and leaves the file unchanged.
|
||||
- Install `factory`:
|
||||
- writes no library mapping, no persona file and no slot dir, which is the brief's case;
|
||||
- refuses a library project and `bus.tester` off.
|
||||
- Install refuses: run as root, a business id mismatch, missing `--personas` under library, and a
|
||||
`validate` refusal, after which no business file is left.
|
||||
- N4: a re-run where the matching business file is already there and `validate` refuses leaves that
|
||||
file in place, byte for byte.
|
||||
- D6a refusals, one case each: a persona file at 0644, a digest mismatch, a business file at 0644, a
|
||||
`changes.jsonl` at 0644, and a Discord binding. A clean library host passes D6a with no mirror.
|
||||
- D6b, with the mirror made by `ensureMirror` and the state written by a real `sync()` against the local
|
||||
bare repo, as the bus would:
|
||||
- no mirror refuses `library-mirror-missing`;
|
||||
- a fresh mirror refuses `library-never-synced`;
|
||||
- a sync against a remote that doesn't resolve refuses `library-last-error` with its code;
|
||||
- a synced bare repo with one R2 path prints its `problem` line and `problems 1`, and passes.
|
||||
|
||||
Every new refusal gets a mutant that removes it. The count goes in the slot request.
|
||||
|
||||
### D10. Files
|
||||
|
||||
Owned by the brief: `scripts/case2-install.sh` (new), `scripts/case2-precheck.sh` (library checks and
|
||||
`--library-status` only) and `packages/cli/tests/case2-install.test.mjs` (new). Also
|
||||
`docs/guides/host-run-sheet.md` (D7, sage-1832), agents/gosalyn/work/case2/DESIGN.md (D8, sage-2189)
|
||||
and this BUILD.md. Before the slot request I'll grep for outside readers of case2-precheck.sh, since
|
||||
case2-unit.test.mjs runs it.
|
||||
|
||||
## Questions
|
||||
|
||||
All three d1 questions are settled:
|
||||
- Q1, persona stage before 2b: Webby agreed (webby-0085).
|
||||
- Q2, R-problems don't refuse: Webby agreed.
|
||||
- Q3, the pinned harness is the copy's `npm ci`: Webby agreed, and asked for the registry note in D7.
|
||||
- Q4a and Q4b: Sage, sage-2189.
|
||||
|
||||
One choice for Webby to check: D6b's `problem` line carries the path through `JSON.stringify`. N5 asked
|
||||
for a prefix and a count. I kept the path because Jason needs it to tell her which file to fix, and the
|
||||
names are hers, not text.
|
||||
|
||||
## Changes from d1
|
||||
|
||||
- B1: release package and activate dropped, with the `CASE2_RELEASE` seam and its test (D2, D5).
|
||||
- N1 and webby-0086: the sync is gone from the precheck. D6 splits into D6a before the start and D6b,
|
||||
`--library-status`, after the bus is up and step 4 (sage-2189).
|
||||
- N2: covered by D6b's `lastError` refusal.
|
||||
- N3: D6b builds the context and slot as the bus does.
|
||||
- N4: step 7 removes only a file this run created, with a test.
|
||||
- N5: `problem` prefix and a `problems` count.
|
||||
- N6: the run sheet lines are :240 and :275.
|
||||
- New D8, the design 10 text change, and its path in D10.
|
||||
|
||||
## Build
|
||||
|
||||
Webby approved d2 in webby-0087 (REVIEW-design-d2.md, webby/reviews 2f488b4ea). Nothing above this
|
||||
section changed after approval. This section records the build.
|
||||
|
||||
### D8 lands on case2-design
|
||||
|
||||
Sage ruled in sage-2195, after Webby's N1, that D8 goes on gosalyn/case2-design, because
|
||||
agents/gosalyn/work/case2/DESIGN.md isn't on refactor. The change is commit 624212842 there, pushed:
|
||||
step 2b drops `release.sh package` and `activate`, new step 4a runs `--library-status`, and the C2-13b
|
||||
row text says the precheck reads the bus's recorded sync state. DESIGN.md is not in this row's manifest.
|
||||
|
||||
The brief's lines docs/plans/2026-10-20_case2-rows.md:1093-1096 still name release package, activate and
|
||||
a library.sync fetch. Sage owns the brief and will amend those lines to match D8 in a doc-only commit
|
||||
after the re-pin window (sage-2205). They are not in this row's manifest.
|
||||
|
||||
### Webby's d2 notes
|
||||
|
||||
- N2: the `problem` line keeps the path. After `JSON.stringify` the precheck also escapes DEL, the C1
|
||||
controls U+0080-009F, U+200E and U+200F, U+2028 and U+2029, and the bidi controls U+202A-202E and
|
||||
U+2066-2069 as `\uXXXX`. Webby named C1 and bidi. I added the rest because `JSON.stringify` leaves
|
||||
them raw too, and each one can change how a terminal shows the line. The test path holds U+202E and
|
||||
U+0085, and the test checks that neither prints raw.
|
||||
- N3: `lastError` is checked before `lastFetch`. A first sync with no token records
|
||||
`library-credential-missing` and leaves `lastFetch` null, so it refuses `library-last-error`, not
|
||||
`library-never-synced`. The fresh-mirror test has `lastError` null and refuses `library-never-synced`.
|
||||
|
||||
### Choices made in the build
|
||||
|
||||
- The config dir. The persona import refuses a config dir with group or other bits, and bootstrap makes
|
||||
one with the default mode. So the install makes the config dir at 0700 when it's absent, before
|
||||
bootstrap, and refuses `config-dir-mode` when it exists with a group or other bit. It checks
|
||||
`secrets/` and `secrets/<id>/` the same way under library (`secrets-mode`, `slot-mode`).
|
||||
- `--home` must equal `$HOME` (`home-mismatch`). Every path comes from `$HOME` and `MOSAIC_CONFIG`, as
|
||||
bootstrap's does, so a different `--home` would only name a second home the script doesn't use.
|
||||
- A plan step runs before anything is written. It reads the input and the staging manifest, computes
|
||||
the persona vars from the manifest, and compares the result with what's on disk. It refuses every
|
||||
input rule, `business-file-differs` and `persona-differs` there, so a refused run writes nothing.
|
||||
After the import the script checks that the import's two `instance` lines carry the manifest's
|
||||
digests.
|
||||
- A re-run with both persona files present and matching skips the import, which would refuse to
|
||||
overwrite them. One matching file and one missing refuses `persona-differs`.
|
||||
- A diverged library prints one line under `--library-status`, naming the tip `library.sync --accept`
|
||||
would take. It doesn't refuse. The design doesn't list it as a refusal, and the bus still serves the
|
||||
old tip.
|
||||
- The precheck's library checks print `ok` lines for each check they pass, as the rest of the script
|
||||
does. A business file that's missing prints no extra line, since the existing business-file check
|
||||
already refuses it.
|
||||
- The business id must match the persona import's rule, `^[a-z0-9][a-z0-9-]{0,63}$`. That's narrower
|
||||
than the precheck's, which also takes `.` and `_`, because the persona dir uses the id.
|
||||
|
||||
### Outside readers
|
||||
|
||||
`git grep` over packages/*/tests/*, packages/*/src/* and scripts/test-*.sh finds case2-precheck.sh read
|
||||
by case2-unit.test.mjs, which runs it, and case1-accept.test.mjs, which names a copy's path but runs a
|
||||
fake. No test reads host-run-sheet.md. No test pins a hash of either script. case2-unit.test.mjs's
|
||||
precheck host has a 0600 business file and no persona vars, so the new D6a checks pass there.
|
||||
|
||||
### Mutants
|
||||
|
||||
24, in /mnt/storage/scratch/gosalyn-r226/mutate.py. Each replaces one exact string once. Line numbers
|
||||
are the candidate's. run.sh stops with BASE RED before any mutant unless the base run is rc=0 and
|
||||
fail 0.
|
||||
|
||||
| Mutant | Line | Check removed |
|
||||
|---|---|---|
|
||||
| I1 | install :73 | run-as-root |
|
||||
| I2 | install :74 | home-mismatch |
|
||||
| I3 | install :81 | config-dir-mode |
|
||||
| I4 | install :101 | business-id-mismatch |
|
||||
| I5 | install :107 | profile-library-business |
|
||||
| I6 | install :108 | bus-tester-off |
|
||||
| I7 | install :111 | bus-tester-on |
|
||||
| I8 | install :115 | persona-var-input |
|
||||
| I9 | install :158 | business-file-differs |
|
||||
| I10 | install :152 | persona-differs |
|
||||
| I11 | install :218 | N4, removes the file even when this run didn't write it |
|
||||
| I12 | install :218 | validate's removal of the file this run wrote |
|
||||
| P1 | precheck :229 | business-file-mode |
|
||||
| P2 | precheck :235 | history-mode for changes.jsonl |
|
||||
| P3 | precheck :246 | persona-mode for a persona file |
|
||||
| P4 | precheck :251 | persona-mismatch |
|
||||
| P5 | precheck :241 | persona-mode for a persona dir |
|
||||
| P6 | precheck :258 | discord-binding |
|
||||
| P7 | precheck :104 | library-mirror-missing |
|
||||
| P8 | precheck :107 | library-last-error |
|
||||
| P9 | precheck :107 | N3, the order swapped |
|
||||
| P10 | precheck :108 | library-never-synced |
|
||||
| P11 | precheck :92 | N2, the escape after stringify |
|
||||
| P12 | precheck :65 | --library-status refuses --profile |
|
||||
|
||||
`history-mode` for the history dir itself has no mutant and no test. The brief names only the
|
||||
`changes.jsonl` case.
|
||||
|
||||
### Tests
|
||||
|
||||
Not run yet. They wait for a slot.
|
||||
@@ -0,0 +1,177 @@
|
||||
# Row 239 (#1745) build notes (append only)
|
||||
|
||||
## 2026-10-22 b1 (Honker)
|
||||
|
||||
Design d1 5da5f28f6 was approved by Duckworth with C1 and C2 and no blockers (duckworth-0165, note
|
||||
agents/duckworth/work/row239/REVIEW-design-d1.md). The code is on honker/row239 from aca16404c. No test
|
||||
has run yet: every run waits for Sage's CLEAR.
|
||||
|
||||
### Duckworth's conditions and answers
|
||||
|
||||
- C1 (option a): `outcome-unknown-revoked` is in the Console's `OPEN` list (webui bus.mjs), so
|
||||
`sendOutcome` answers it as `outcome-unknown` with `sent: 'unknown'` and never "Nothing changed". T6
|
||||
covers it and M3f is its mutant.
|
||||
- C2: `write()` sets `revoked = true` after `beforeWrite()` returns, and a catch around the whole
|
||||
`withLock(...)` call marks the error. So a throw from the lock's release in its finally is marked as
|
||||
well. This is structural and has no test, as Duckworth said.
|
||||
- Q1: `beforeWrite` runs after both validations. Q2: two codes. Q3: M2 removes the `:188` check (now
|
||||
organization.mjs:195).
|
||||
|
||||
### Notes N1-N5
|
||||
|
||||
- N1: `beforeWrite` must not write the same business, because it would find its own lock and refuse
|
||||
`organization-busy`. This is said in the comments on `write()` and `commit()`. Today's revokes (the
|
||||
bus's credential revoke) write no business file.
|
||||
- N2: the lock is held across the revoke. A fire or a delete now keeps other writers of that business
|
||||
out for as long as the revoke takes. They refuse `organization-busy`, as they do for any held lock.
|
||||
- N3: a revoke that fails partway (some tokens revoked, then `credential-revoke-failed`) behaves as
|
||||
today. The error carries no `revoked` mark, because `beforeWrite` didn't return, and nothing is
|
||||
written. This row doesn't change that.
|
||||
- N4: a delete closed with `outcome-unknown-revoked` exits 3 in the CLI, as every closed refusal does.
|
||||
Exit 1 (transport EXIT) applies only to a direct verb answer.
|
||||
- N5: I grepped for "before the write" and similar comments. The ones left (lifecycle-revoke.test:2,
|
||||
instance-settings:56, cli instances.mjs:53, serve.mjs:365) are still true and are unchanged. T21 in
|
||||
bus lifecycle-revoke.test:173 is unchanged.
|
||||
|
||||
### Differences from the design
|
||||
|
||||
- The business tests are in lifecycle-settings.test.mjs, not instance-settings.test.mjs. The fire tests
|
||||
and their fixture already live there. The fixture now returns `dir` and `rolesDir` too.
|
||||
- T3 covers T3b (the delete) in the same test, looping over fire and delete. It also checks that a
|
||||
revoke throwing `credential-revoke-failed` carries no mark and writes nothing.
|
||||
- T4b is the plain-code check. The design's "T4b, outcome-unknown-revoked for an fs-style error" is in
|
||||
T4, which checks EIO with and without the mark for both a fire and a delete.
|
||||
- The bus test's fake org gets `deleteError`, thrown from its `instances.set` branch, so T4 can drive a
|
||||
delete.
|
||||
- transport.test gets one assertion, `busExit("outcome-unknown-revoked") === 1`, for M3g.
|
||||
- A latent Console text: a direct (not closed-request) `revision-conflict-revoked` answer in the
|
||||
Console would still get " Nothing changed." appended, because only `OPEN` codes skip it. That sentence
|
||||
is true there: the file is unchanged. No direct human path gives this code today, since a delete
|
||||
reaches the Console as a closed request's refusal.
|
||||
- The Console's STATUS entry for `outcome-unknown-revoked` (503) can't be reached while the code is in
|
||||
`OPEN`. It stays for symmetry with the CLI and is harmless.
|
||||
|
||||
### What changed
|
||||
|
||||
- packages/business/src/organization.mjs: `write()` takes `beforeWrite`, runs it under the lock after
|
||||
the revision check and both validations, and marks any later error `revoked`. The body is reindented
|
||||
inside the try.
|
||||
- packages/business/src/instance-settings.mjs: `instanceSettings` takes an optional `writer`. `commit()`
|
||||
passes `beforeWrite` to `writer.write` instead of calling it. The early check stays, and its comment
|
||||
is rewritten.
|
||||
- packages/bus/src/broker.mjs: `#organizationFail` maps a marked error to `revision-conflict-revoked` or
|
||||
`outcome-unknown-revoked` before the existing mappings. `#fireJob`'s notice adds "Its credential
|
||||
tokens may already be revoked." for a `-revoked` code.
|
||||
- packages/bus/src/runtime.mjs: comment only.
|
||||
- packages/cli/src/instances.mjs: two REASONS entries. packages/cli/src/transport.mjs: EXIT
|
||||
`outcome-unknown-revoked` 1.
|
||||
- packages/webui/src/serve.mjs: two AGENT_REASONS entries. packages/webui/src/bus.mjs: `OPEN` entry and
|
||||
two STATUS entries.
|
||||
- Tests:
|
||||
- business lifecycle-settings.test: "row 239 T1", "row 239 T2", "row 239 T3".
|
||||
- bus fire-revokes-grants.test: "row 239 T4", "row 239 T4b".
|
||||
- cli instances.test: "row 239 T5 CLI". cli transport.test: one assertion in "busExit and
|
||||
refuseInsideAgent".
|
||||
- webui agents.test: "row 239 T6".
|
||||
- bus channel-proof.test re-pins packages/cli/src/transport.mjs (662642a8...) and
|
||||
packages/webui/src/bus.mjs (1eba9774...), with a comment on each. Neither changes a proof or the
|
||||
transport. The design's file list missed this. The pin grep found it before the first run.
|
||||
### Mutants for the gate (exact-once replacements)
|
||||
|
||||
- M1 (instance-settings.mjs, beforeWrite before the lock): replace
|
||||
` const receipt = writer.write(business, next, { revision, actor, context, beforeWrite });` with
|
||||
` if (beforeWrite) beforeWrite();\n const receipt = writer.write(business, next, { revision, actor, context });`.
|
||||
The kill is lifecycle-settings "row 239 T1" (the revoke spy has a call).
|
||||
- M2 (organization.mjs, the fire refusal dropped): delete the line
|
||||
` if (revision !== before) fail("revision-conflict", "organization revision changed; read again");`.
|
||||
The kill is "row 239 T1" (the write lands over the hand edit).
|
||||
- M3a (organization.mjs, the mark dropped): ` if (revoked && e instanceof Error) e.revoked = true;`
|
||||
becomes ` void revoked;`. The kills are "row 239 T2" and "row 239 T3" (business). T4 drives the mark
|
||||
through a fake org, so it doesn't see M3a.
|
||||
- M3b (broker.mjs): delete the line
|
||||
` if (e.revoked) fail(e.code === "revision-conflict" ? "revision-conflict-revoked" : "outcome-unknown-revoked");`.
|
||||
The kill is fire-revokes-grants "row 239 T4".
|
||||
- M3c (cli instances.mjs): delete the five lines from ` "revision-conflict-revoked": "the business file`
|
||||
through ` "business file again with \`mosaic business instances\` before trying again",`. The kill is
|
||||
instances.test "row 239 T5 CLI".
|
||||
- M3d (webui serve.mjs): delete the two lines starting ` 'revision-conflict-revoked': 'the business file`
|
||||
and ` 'outcome-unknown-revoked': 'the agent\'s tokens`. The kill is agents.test "row 239 T6".
|
||||
- M3e (broker.mjs): ` const tokens = code.endsWith('-revoked') ? ' Its credential tokens may already be revoked.' : '';`
|
||||
becomes ` const tokens = '';`. The kill is "row 239 T4".
|
||||
- M3f (webui bus.mjs, C1): the `OPEN` line loses `, 'outcome-unknown-revoked'`. The kill is "row 239 T6"
|
||||
(the launch POST answers 503 outcome-unknown, sent unknown).
|
||||
- M3g (cli transport.mjs): delete the line ` "outcome-unknown-revoked": 1,`. The kill is transport.test
|
||||
"busExit and refuseInsideAgent".
|
||||
|
||||
A kill counts only as a failed assertion in the named test, not a load error or a different test.
|
||||
|
||||
### Gate plan (mos-1275 timeouts)
|
||||
|
||||
- My row 256 gate (2026-10-22 10:34Z, same machine) took 590s for the cli package, 57s for webui and
|
||||
38s for test-foundation, the longest shell script.
|
||||
- Packages: `timeout -k 10 1800` per package (3x the cli run).
|
||||
- Each scripts/test-*.sh: `timeout -k 10 300` (more than 3x the 38s longest).
|
||||
- Single files and mutants: `timeout -k 10 600` per file run (cli instances.test whole is the longest
|
||||
single file).
|
||||
- Order on Sage's CLEAR: the touched test files whole (business lifecycle-settings, bus
|
||||
fire-revokes-grants and channel-proof, cli instances and transport, webui agents), then M1-M3g, then
|
||||
the full gate.
|
||||
- Before the in-review move: grep packages/*/tests for the changed comment and error text and for
|
||||
64-hex pins on the changed files, and drift-check BASE..refactor against them.
|
||||
|
||||
## 2026-10-22 r2 rebase (Honker)
|
||||
|
||||
- Duckworth approved b1 b0c28366f statically, with no blockers (duckworth-0168). On N1 (`endsWith('-revoked')`
|
||||
would also match `access-revoked`, which can't reach a fire job today), the check stays as it is, so the
|
||||
approved candidate doesn't change before the gate (honker-0163).
|
||||
- Rows 253 (48e450ce8) and 275 landed. b1 is rebased onto refactor 94c33a43c as honker/row239-r2. The
|
||||
push branch honker/row239 isn't rewritten.
|
||||
- Drift between aca16404c and 94c33a43c on the 15 paths: only packages/bus/src/broker.mjs, where row 275
|
||||
changes the viewer verbs. Neither of row 239's two hunks (`#fireJob` and `#organizationFail`) moves in
|
||||
meaning. The rebase applied with no conflict. The two pinned files (cli transport.mjs, webui bus.mjs)
|
||||
and channel-proof.test have no upstream change, so the b1 pins stand.
|
||||
- Each M1-M3g replacement string still matches exactly once.
|
||||
- The touched test files run whole again on the new base, in the slot (the rebase rule).
|
||||
|
||||
## 2026-10-23 r3 rebase (Honker, sage-3730)
|
||||
|
||||
- r2 (70c6e3772) is rebased onto refactor 93694c518 as honker/row239-r3. honker/row239-r2 isn't rewritten.
|
||||
- Between 94c33a43c and 93694c518, five of the 16 paths changed upstream: bus broker.mjs and runtime.mjs,
|
||||
channel-proof.test, and webui bus.mjs and serve.mjs. Every file merged on its own except channel-proof.test,
|
||||
where row 301 (59f0e44f4, mos-1449) re-pinned webui bus.mjs for the user transport fold. The merged
|
||||
pin keeps row 301's comment and row 239's, and takes sha256 e3f52172…1c2a89 of the merged bus.mjs.
|
||||
The transport.mjs pin (662642a8…6e55b) still matches the file.
|
||||
- Row 239's hunks in broker.mjs (`#fireJob`, `#organizationFail`), runtime.mjs (the revoke comment),
|
||||
bus.mjs (OPEN, STATUS) and serve.mjs are unchanged apart from their position. The range-diff shows
|
||||
only the channel-proof pin and the hunk context moved.
|
||||
- Each M1-M3g replacement string still matches exactly once. M3f's target is the whole `OPEN` line,
|
||||
since `, 'outcome-unknown-revoked'` also appears in the STATUS line.
|
||||
- No 64-hex pin in packages/*/tests or scripts names a changed file except channel-proof.test.
|
||||
- On the CLEAR (sage-3730): the touched files whole, then M1-M3g, then the counts here. Sage runs the
|
||||
full gate on the replay at landing.
|
||||
|
||||
## 2026-10-23 r4 replay and the slot run (Honker, sage-3773 CLEAR)
|
||||
|
||||
- On the CLEAR, refactor was 546366f4a (row 320 landed at 094124b4e). None of the 16 manifest paths changed
|
||||
between 93694c518 and 546366f4a. Upstream did change library src/index.mjs, which bus runtime.mjs imports,
|
||||
and business src/index.mjs, which the cli imports, so the run was on a replay onto 546366f4a rather than
|
||||
on r3's base. That replay is honker/row239-r4 0cae2dad5. The range-diff of r3 against r4 is four `=`
|
||||
lines with equal patch-ids, so duckworth-0292's read of r3 applies to r4. r3 isn't rewritten.
|
||||
- The run was on 0cae2dad5 with a clean tree, TMPDIR under /mnt/storage/scratch/tmp, 22:01:27Z-22:01:52Z.
|
||||
- The touched files, run whole: business lifecycle-settings 12/12, bus fire-revokes-grants 24/24, bus
|
||||
channel-proof 2/2, cli instances 15/15, cli transport 3/3, webui agents 9/9. That's 65/65 with 0 skipped,
|
||||
and no first-run red.
|
||||
- Mutants: 9 of 9 killed. Each was applied exactly once, and each made its named test fail as an assertion:
|
||||
- M1 lifecycle: 3 fail (T1, T2, T3).
|
||||
- M2: T1.
|
||||
- M3a: T2 and T3.
|
||||
- M3b fire-revokes-grants: T4.
|
||||
- M3c instances: T5 CLI.
|
||||
- M3d agents: T6.
|
||||
- M3e: T4.
|
||||
- M3f agents: T6.
|
||||
- M3g transport: "busExit and refuseInsideAgent".
|
||||
|
||||
T4b passed under M3b and M3e. Every restore matched the HEAD blob by sha256, and the tree was clean
|
||||
after the run.
|
||||
- No full gate (sage-3730): Sage runs it on the replay at landing.
|
||||
@@ -0,0 +1,159 @@
|
||||
# Row 239 design: the revoke under the writer's lock, and a fire test
|
||||
|
||||
Brief: docs/plans/2026-10-20_agent-lifecycle.md, "Brief: row 194 follow-up, the revoke under the writer's
|
||||
lock and a fire test" (blob 3b3b3b2b9), from Duckworth's row 194 b3 N1 and mos-1158. Base aca16404c (row
|
||||
194 landed as fd6dd327c, row 281 as e479521d3). Builder Honker, reviewer Duckworth, gate owner Sage.
|
||||
Draft 1, 2026-10-22.
|
||||
|
||||
## Today
|
||||
|
||||
- `instance-settings.mjs` `commit()` runs the checks, then compares the caller's revision with a fresh
|
||||
`writer.read` (row 194 b3, sage-1634), then calls `beforeWrite` (the fire's or the delete's credential
|
||||
revoke), then `writer.write`.
|
||||
- `writer.write` (`organization.mjs`) takes the lock, runs `recover`, checks the revision again (`:188`),
|
||||
validates, prepares, checks the bytes once more before publication (`:221`), renames and commits.
|
||||
- So two windows are left.
|
||||
- A write that lands between `commit()`'s read and the writer's lock still gets a revoke and then a
|
||||
`revision-conflict`.
|
||||
- Any refusal after the revoke ran (the `:221` check, or an I/O failure the bus reports as
|
||||
`outcome-unknown`) uses the same text as one before it, so nothing says the tokens may be gone.
|
||||
- Row 281 left this path alone. Its fire job (`broker.mjs` `#fireJob`) runs step G (grants), then step B,
|
||||
which is this same `write('instance.fire')`, and reports a step B failure as `fire.failed` along with a
|
||||
human notice that names the code. Its delete job only does step G. The delete itself still commits
|
||||
through `lifecycleDelete` → `instances.set` op delete → `commit()`.
|
||||
|
||||
## What changes
|
||||
|
||||
### 1. `beforeWrite` runs inside `writer.write`, under the lock (organization.mjs)
|
||||
|
||||
`write(id, document, { revision, actor, detail, context, beforeWrite = null })`. The order under the lock:
|
||||
|
||||
1. `recover`, read, then the `:188` revision check. A refusal here revokes nothing.
|
||||
2. Projects-protected and both validations. A refusal here revokes nothing either. I put `beforeWrite`
|
||||
after these rather than straight after `:188`, so a document the writer would refuse anyway never
|
||||
costs a revoke. Q1 asks Duckworth to confirm this.
|
||||
3. `beforeWrite()`. If it throws (today `credential-revoke-failed`), the error propagates as it is and
|
||||
nothing has been written, as now.
|
||||
4. Prepare, the `:221` check, rename and commit, wrapped so that any error thrown after step 3 gets
|
||||
`revoked = true` set on it before it is rethrown. That covers `revision-conflict` from `:221`, an
|
||||
`organization-refused` from `log()`, a barrier throw, and an fs error.
|
||||
|
||||
`record()` and the other writers (`launch-settings`, `plugin-settings`, `credentialVerbs`) pass no
|
||||
`beforeWrite` and behave as before.
|
||||
|
||||
### 2. commit() passes beforeWrite through (instance-settings.mjs)
|
||||
|
||||
`commit()` passes `beforeWrite` to `writer.write` and no longer calls it itself. The row 194 b3 early check
|
||||
stays. It costs one read, keeps T21's refusal before any validation work, and is no longer what protects
|
||||
the revoke. Its comment is rewritten to say so, and the "(BUILD.md follow-up)" sentence goes.
|
||||
|
||||
`instanceSettings({ ..., writer = organizationWriter({ dir, rolesDir }) })` takes an optional writer, so a
|
||||
test can inject one (the brief's "injected writer"). `runtime.mjs` passes none.
|
||||
|
||||
### 3. The bus names a refusal after a revoke (broker.mjs `#organizationFail`)
|
||||
|
||||
The bus answers with a code only, and the CLI and Console look the text up by code. So the "may already be
|
||||
revoked" signal has to be a code. Two new codes:
|
||||
|
||||
- `revision-conflict-revoked`: `e.revoked && e.code === 'revision-conflict'`.
|
||||
- `outcome-unknown-revoked`: `e.revoked` with any other code. The write may or may not have landed, since a
|
||||
failure after the rename is recovered as committed.
|
||||
|
||||
Both are checked before the existing mappings. An error without `revoked` maps exactly as today, so a
|
||||
refusal before any revoke keeps today's code and text. Q2 asks about the names.
|
||||
|
||||
### 4. The texts
|
||||
|
||||
- CLI `instances.mjs` REASONS:
|
||||
- `revision-conflict-revoked`: "the business file changed before the write, after the agent's tokens
|
||||
were revoked; the tokens may already be revoked and the instance can't use them; read the instances
|
||||
again and run the same command again, which is safe".
|
||||
- `outcome-unknown-revoked`: "the write's outcome is unknown and the agent's tokens may already be
|
||||
revoked; read the instances before trying again".
|
||||
- Console `serve.mjs` AGENT_REASONS: the same two, in the Console's wording ("delete again").
|
||||
- `#fireJob`'s `fire.failed` notice: when the code ends in `-revoked`, it adds "Its credential tokens may
|
||||
already be revoked." The chief's synchronous fire (no grants) answers with the code, as every chief verb
|
||||
does.
|
||||
- `transport.mjs` EXIT: `outcome-unknown-revoked` exits 1, as `outcome-unknown` does. Console
|
||||
`bus.mjs` STATUS: `revision-conflict-revoked` 409, `outcome-unknown-revoked` 503. A delete's code
|
||||
reaches both as a closed request's `refusal`, so these two only matter for a direct verb answer.
|
||||
|
||||
## Tests
|
||||
|
||||
- **T1, the brief's fire test** (business `instance-settings.test.mjs`). The fire uses an injected writer
|
||||
that wraps the real one. Its `write` makes one hand edit of the business file (a vars change on another
|
||||
instance) and then calls the real `write`, so the edit lands after `commit()`'s early check and before
|
||||
the lock's `:188` check. Expected:
|
||||
- the fire refuses `revision-conflict` without `revoked`;
|
||||
- the revoke spy has no calls;
|
||||
- the business file's bytes are the hand edit's;
|
||||
- changes.jsonl has no new prepared entry.
|
||||
- **T2** (same file). A fire on a real writer built with `barrier: stage => stage === 'prepared' && edit()`.
|
||||
The revoke runs, then `:221` refuses. Expected: `revision-conflict` with `revoked === true`, one revoke
|
||||
call, the file's bytes are the edit's, and the last receipt is `aborted`.
|
||||
- **T3** (same file). The same, with a `prepared` barrier that throws. Expected: the error has
|
||||
`revoked === true`, one revoke call, and the file unchanged. A delete through the same writer gives the
|
||||
same result (T3b).
|
||||
- **T4** (bus `fire-revokes-grants.test.mjs`, its injected `f.org`). `f.org.fireError` is a
|
||||
`revision-conflict` with `revoked: true`. Expected:
|
||||
- the job's `fire.failed` carries `revision-conflict-revoked`, and the inbox message says "may already
|
||||
be revoked";
|
||||
- the same with no `grants` (the synchronous fire) refuses `revision-conflict-revoked`;
|
||||
- a plain `revision-conflict` still maps to `revision-conflict` and the message lacks the sentence.
|
||||
- T4b: `outcome-unknown-revoked` for an fs-style error with `revoked`.
|
||||
- **T5** (cli `lifecycle.test.mjs` or `instances.test.mjs`, whichever already reads a closed delete's
|
||||
refusal). A delete closed with `revision-conflict-revoked` prints the new text, and one closed with
|
||||
`revision-conflict` prints today's.
|
||||
- **T6** (webui `agents.test.mjs`). The same pair for the Console's reason.
|
||||
- T21 (bus `lifecycle-revoke.test.mjs`) stays as it is, for delete.
|
||||
|
||||
## Mutants
|
||||
|
||||
| Mutant | Change | Killed by |
|
||||
| --- | --- | --- |
|
||||
| M1 | `beforeWrite` before the lock: `commit()` calls `beforeWrite()` itself and passes none to `write` | T1 (the revoke spy has a call) |
|
||||
| M2 | The fire refusal dropped: the `:188` revision check removed | T1 (the write lands over the hand edit) |
|
||||
| M3a | The mark dropped: no `revoked = true` in the catch | T2, T3, and T4 through the codes |
|
||||
| M3b | `#organizationFail` ignores `revoked` | T4 |
|
||||
| M3c | CLI text: the two REASONS entries removed | T5 |
|
||||
| M3d | Console text: the two AGENT_REASONS entries removed | T6 |
|
||||
| M3e | The notice: the `fire.failed` sentence removed | T4 |
|
||||
|
||||
M3a-M3e together are the brief's "drop the may already be revoked text".
|
||||
|
||||
## Files
|
||||
|
||||
- `packages/business/src/organization.mjs`
|
||||
- `packages/business/src/instance-settings.mjs`
|
||||
- `packages/business/tests/instance-settings.test.mjs`
|
||||
- `packages/bus/src/broker.mjs`
|
||||
- `packages/bus/tests/fire-revokes-grants.test.mjs`
|
||||
- `packages/cli/src/instances.mjs`
|
||||
- `packages/cli/src/transport.mjs`
|
||||
- one cli test file
|
||||
- `packages/webui/src/serve.mjs`
|
||||
- `packages/webui/src/bus.mjs`
|
||||
- `packages/webui/tests/agents.test.mjs`
|
||||
- This packet.
|
||||
|
||||
## Gate
|
||||
|
||||
As the brief says:
|
||||
|
||||
- Duckworth approves the manifest.
|
||||
- The bus, business, cli and webui suites and every `scripts/test-*.sh` are green on Sage's slot, with
|
||||
the touched test files run whole.
|
||||
- M1-M3e are killed.
|
||||
|
||||
Before the in-review move I grep the tests for the changed comment and error text, and for hash pins on the
|
||||
changed files.
|
||||
|
||||
## Questions for Duckworth
|
||||
|
||||
- **Q1.** Should `beforeWrite` run after the writer's validations (my pick) or straight after `:188` (the
|
||||
brief's literal place)? Running it later means a document the writer would refuse revokes nothing.
|
||||
- **Q2.** Are two new codes, `revision-conflict-revoked` and `outcome-unknown-revoked`, the right way to
|
||||
carry the signal? The alternative is to give `BusError` a detail field, which every CLI and Console
|
||||
reader would then have to learn. The codes are additive and no existing code changes meaning.
|
||||
- **Q3.** The brief's mutant "drop the fire refusal": I read it as removing the `:188` check (M2). If you
|
||||
meant the fire test's own refusal, say so and I'll redefine it.
|
||||
@@ -0,0 +1,276 @@
|
||||
# Row 283 (#1788) C2-01d build notes (Honker)
|
||||
|
||||
Append-only. Design: DESIGN.md d2 16e809457, APPROVE webby-0172. Gate owner Sage, reviewer Webby.
|
||||
|
||||
## 2026-10-23 build on 45dca69f6 (sage-3577)
|
||||
|
||||
Branch honker/row283-build from origin/refactor 45dca69f6e4bce52834587f66f2ff33ba839ab8a. The old branch
|
||||
honker/row283 (16e809457) held only the design, so its two commits were cherry-picked as 398314cf4
|
||||
(d1) and 4f66dccde (d2). The DESIGN.md blob equals 16e809457's.
|
||||
|
||||
Commits:
|
||||
- 331653b38: code, README and tests T1-T8.
|
||||
- 28918824c: the survey.test:73 re-pin, alone (mos-1330 item 4).
|
||||
- this file.
|
||||
|
||||
Nothing has run. No node, test or mutant ran during the build: Darkwing held the slot. Every count
|
||||
below is a plan until Sage's CLEAR.
|
||||
|
||||
### Drift check (sage-3577)
|
||||
|
||||
`git diff --stat 94c33a43c 45dca69f6` (design base to build base) over the paths the tests read or
|
||||
hash-pin:
|
||||
- No change in packages/library, packages/business/src/business.mjs or
|
||||
packages/business/tests/business.test.mjs.
|
||||
- No test hash-pins a library file. bus library.test and library-sync.test import index.mjs, and
|
||||
stack-copy.test only checks that index.mjs exists.
|
||||
- webui library-cli.test changed one line and doesn't read scope. The `classify` in webui reads.mjs is
|
||||
the business one, not the library's.
|
||||
|
||||
I redo the drift check on BASE..refactor before the in-review move.
|
||||
|
||||
### N3 (webby-0169): deepEquals of the mapping output outside packages/library
|
||||
|
||||
`git grep -n playground -- 'packages/*/tests/*' ':!packages/library/*'` and the files naming
|
||||
`universes` give two files:
|
||||
- business.test.mjs:20 compares `validateBusinessDocument(...).projects.books` with
|
||||
`checkMapping(mappingEntry())`. That is output against output, so `supporting: []` shows up on both
|
||||
sides. :27 compares only `.playground`.
|
||||
- library-projects-protected.test.mjs compares file bytes (:31, :32) and change paths (:58), never the
|
||||
mapping output.
|
||||
|
||||
No re-pin is needed outside library. Inside library, only the draft deepEqual at mapping.test:48 gains
|
||||
`supporting: []`, as the design says.
|
||||
|
||||
### Q1 departure (sage-2923) and the :73 re-pin (mos-1330 item 4)
|
||||
|
||||
survey.mjs:65 counts `supporting: 0` and formatSurvey prints it. T8 is the only added test
|
||||
(survey.test "C2-01d: with --mapping the survey counts supporting files").
|
||||
|
||||
mos-1330 item 4, packages/library/tests/survey.test.mjs:73, commit 28918824c:
|
||||
- old: `'scopes canon 5, playground 1, unmapped 3; indexed 6',`
|
||||
- new: `'scopes canon 5, playground 1, supporting 0, unmapped 3; indexed 6',`
|
||||
|
||||
N2: the pin gains only `supporting 0`. No other text on the line or in that test moved.
|
||||
|
||||
### Q2 tradeoff (webby-0169)
|
||||
|
||||
A supporting prefix that holds a playground prefix refuses `library-mapping-invalid`. That goes past
|
||||
the brief, but it is the fail-closed reading, and it uses an existing code: classify checks supporting
|
||||
first, so that playground prefix could never match. The cost is that she can't nest a playground under
|
||||
a supporting folder, though she can nest the other way (a playground holding a supporting folder).
|
||||
|
||||
### Handoffs
|
||||
|
||||
- Q4 (webby-0169, sage-2930): the M12 delete rule ("A delete also needs the path to exist at the tip as
|
||||
a 100644 blob", case2 DESIGN.md :308) is not in supportingPath. It binds row 288 (C2-02) via Gitea
|
||||
#1793 comment 30738, with tests for a missing path and a non-100644 entry.
|
||||
- A save with no `editor` refusing is C2-02's (DESIGN.md section 1).
|
||||
|
||||
### Choices the design left open
|
||||
|
||||
- The book loop is now `againstBooks(owners, list, code)` in mapping.mjs, called once per list. The
|
||||
playground's messages and codes are byte-equal to before.
|
||||
- A non-string path refuses `library-write-story-path` ("the path must be a string") as part of
|
||||
check 1. A non-array `tree` is a TypeError (DESIGN.md section 3).
|
||||
- Check 8 builds the tree's names as every path and every folder above it, keyed by
|
||||
`normalize('NFC').toLowerCase()`. A name on the way refuses if its key holds any name other than
|
||||
itself, so a tree that already holds case twins refuses a write to either (R7).
|
||||
|
||||
### Mutants (exact strings)
|
||||
|
||||
Each applies one replacement to the build tip. Run one at a time, and restore with `git checkout --`
|
||||
on the one file before the next.
|
||||
|
||||
| Mutant | File | Replace | With | Killed by |
|
||||
|---|---|---|---|---|
|
||||
| M1 | src/scope.mjs | `const prefix = entry.supporting.find(p => inside(p, path));` | `const prefix = entry.supporting.find(p => inside(p, path) && !entry.playground.some(q => inside(q, path)));` | scope.test "supporting comes before playground", supporting.test "passes at book level" (both-lists path) |
|
||||
| M2 | src/mapping.mjs | `if ((inside \|\| holding) && owner !== id)` | `if ((inside \|\| holding) && owner !== id && list !== 'supporting')` | mapping.test "each supporting prefix rule", business.test C2-04 |
|
||||
| M3 | src/supporting.mjs | `for (const p of own) if (LINKS.has(modes.get(p)))` | `for (const p of own) if (false && LINKS.has(modes.get(p)))` | supporting.test "symlink or gitlink" |
|
||||
| M4 | src/supporting.mjs | `const fold = name => name.normalize('NFC').toLowerCase();` | `const fold = name => name.normalize('NFC');` | supporting.test "case or Unicode form" |
|
||||
| M4b | src/supporting.mjs | `const fold = name => name.normalize('NFC').toLowerCase();` | `const fold = name => name.toLowerCase();` | supporting.test "case or Unicode form" (NFD case) |
|
||||
| M5 | src/supporting.mjs | `Buffer.byteLength(path, 'utf8') > MAX_BYTES` | `path.length > MAX_BYTES` | supporting.test "1025 bytes" |
|
||||
| M6 | src/mapping.mjs | `const IDENT_BAD = /[\u0000-\u001f\u007f-\u009f<>]/;` | `const IDENT_BAD = /[<>]/;` | mapping.test "editor is optional" |
|
||||
| M6b | src/mapping.mjs | `const IDENT_BAD = /[\u0000-\u001f\u007f-\u009f<>]/;` | `const IDENT_BAD = /[\u0000-\u001f\u007f-\u009f]/;` | mapping.test "editor is optional" |
|
||||
| M7 | src/supporting.mjs | `if (place.scope !== 'supporting' \|\| place.universe !== universe)` | `if (place.scope !== 'supporting')` | supporting.test "outside the universe's supporting folders" |
|
||||
| M8 | src/mapping.mjs | `if (sid === pid && s !== p && holds(s, p))` | `if (false && sid === pid && s !== p && holds(s, p))` | mapping.test "each supporting prefix rule" (Workshop case) |
|
||||
|
||||
(The `\|` in the table is an escaped `|` in the source text.) Each mutant runs the one test file named,
|
||||
plus business.test for M2, under `timeout -k 10 600`. A mutant passes the gate only when its run is red
|
||||
on the named test and the restored file's sha256 equals the build tip's.
|
||||
|
||||
### Gate plan (on Sage's CLEAR)
|
||||
|
||||
1. The touched files whole, each under `timeout -k 10 600`: library mapping, scope, supporting and
|
||||
survey tests, and business.test.mjs. Counts go below.
|
||||
2. M1-M8 as above.
|
||||
3. The full gate, under mos-1275 timeouts: `node --test packages/library/tests/` and
|
||||
`node --test packages/business/tests/` each under `timeout -k 10 1800`, and every scripts/test-*.sh
|
||||
under `timeout -k 10 300`. pipefail and tee, with skips named (test-task Docker groups).
|
||||
4. Before in-review: grep packages/*/tests for a 64-hex pin on any changed file and for the changed
|
||||
message text, then the drift check on BASE..refactor.
|
||||
|
||||
## 2026-10-23 Webby early look (webby-0227, static, not a verdict)
|
||||
|
||||
- E1: business.test.mjs is an existing test file with a 4-line addition after the playground refusal.
|
||||
The in-review request lists it next to survey.test:73.
|
||||
- E3 fixed: supportingPath now runs the string check before `JSON.stringify`, so a BigInt (or any
|
||||
non-string) path refuses `library-write-story-path` instead of throwing a TypeError.
|
||||
supporting.test's string case now covers 7, null, undefined, 1n and an array.
|
||||
- E2 open: `editor` takes surrounding whitespace, and git trims it at commit, so the saved ident and
|
||||
the commit ident can differ. DESIGN.md section 1 says "non-empty after trim". Refusing
|
||||
`value.trim() !== value` would change that approved rule, so it went to Sage instead of into the
|
||||
build.
|
||||
|
||||
## 2026-10-23 E2 departure (sage-3601)
|
||||
|
||||
A departure from DESIGN.md section 1 (which stays unedited). The design says each `editor` value is
|
||||
"non-empty after trim". Sage ruled that surrounding whitespace refuses too, because git trims an
|
||||
ident at commit, so a padded value would save one name and commit another. mapping.mjs now adds
|
||||
`if (value.trim() !== value) bad(`editor.${key} must not start or end with whitespace`)` after the
|
||||
IDENT_BAD check. `trim()` covers Unicode whitespace, so U+00A0 and U+3000 refuse as well. Inner
|
||||
whitespace is kept ("Her Middle Name" passes).
|
||||
|
||||
T3 gains a case per key for leading, trailing and both-ends spaces, U+00A0 and U+3000, plus one inner
|
||||
whitespace pass.
|
||||
|
||||
M6 grows a third part:
|
||||
|
||||
| Mutant | File | Replace | With | Killed by |
|
||||
|---|---|---|---|---|
|
||||
| M6c | src/mapping.mjs | `if (value.trim() !== value) bad(` | `if (false && value.trim() !== value) bad(` | mapping.test "editor is optional" (padded cases) |
|
||||
|
||||
Fixture grep: `git grep -nE "\beditor\b|\bauthor\b" -- 'packages/library/tests/*' 'packages/business/tests/*'`,
|
||||
leaving out mapping.test.mjs, finds nothing. packages/business/tests has no fixtures directory. The
|
||||
library fixture mapping.json has no `editor`. The positive control (`vaultPrefix` in mapping.json)
|
||||
matches once. No fixture maps an editor or author with surrounding whitespace.
|
||||
|
||||
## 2026-10-23 E4 (webby-0230, sage-3606), same departure as E2
|
||||
|
||||
Claimed: `queue move 283 in-progress`, op honker-283-start, rev 3642, 19:10:49Z (briefed→in-progress).
|
||||
|
||||
Git's ident.c `crud()` strips any byte <= 0x20 and `, : ; < > " \ '` from both ends of an ident. I
|
||||
checked that with git 2.55.0: `Jane,` `"Jane"` `'Jane'` `Jane;` `Jane:` `Jane\` and `,Jane` all
|
||||
commit as `Jane`, while `Jane.` and `Ja,ne` are kept. mapping.mjs now takes the set from crud()
|
||||
rather than from that list: `IDENT_CRUD_END = /^[\u0000-\u0020,:;<>"\\']|[\u0000-\u0020,:;<>"\\']$/`.
|
||||
The E2 check became `if (value.trim() !== value || IDENT_CRUD_END.test(value))`, which refuses
|
||||
library-mapping-invalid with "editor.<key> must not start or end with whitespace or any of , : ; " ' \".
|
||||
`trim()` still catches Unicode whitespace (U+00A0, U+3000), which crud() leaves alone.
|
||||
|
||||
Inner characters: IDENT_BAD (`/[\u0000-\u001f\u007f-\u009f<>]/`) already refuses `<`, `>`, newline,
|
||||
tab and every other control anywhere in the value. T3 now pins that at the start, the end and the
|
||||
middle. Inner `, : ; " ' \` are kept, since git keeps them, and T3 pins that too.
|
||||
|
||||
Tests:
|
||||
- T3 has one case per character in [space, U+00A0, U+3000, `,`, `:`, `;`, `"`, `'`, `\`], at the start
|
||||
and at the end, for each key. `<`, `>`, newline and tab are pinned at the start, end and middle.
|
||||
Four accepted names have inner crud characters.
|
||||
- New round-trip test, "an accepted editor round-trips through a real git commit unchanged". It
|
||||
commits in a scratch repo with editor `Doe, Jane Q.` / `[email protected].` as author and
|
||||
committer, with GIT_AUTHOR_* and GIT_COMMITTER_* stripped from the env, and asserts `git log`
|
||||
shows exactly the saved values. I preflighted that commit with git 2.55.0 by hand: both values came
|
||||
back unchanged.
|
||||
|
||||
M6c now replaces the whole end check (this supersedes the earlier M6c row):
|
||||
|
||||
| Mutant | File | Replace | With | Killed by |
|
||||
|---|---|---|---|---|
|
||||
| M6c | src/mapping.mjs | `if (value.trim() !== value \|\| IDENT_CRUD_END.test(value))` | `if (false)` | mapping.test "editor is optional" (end cases) |
|
||||
| M6d | src/mapping.mjs | `if (value.trim() !== value \|\| IDENT_CRUD_END.test(value))` | `if (value.trim() !== value)` | mapping.test "editor is optional" (`,` `:` `;` `"` `'` `\` cases) |
|
||||
|
||||
## 2026-10-23 E5 (webby-0236, not blocking), fixed
|
||||
|
||||
The round-trip test stripped only GIT_AUTHOR_* and GIT_COMMITTER_*. With GIT_DIR, GIT_WORK_TREE or
|
||||
GIT_INDEX_FILE set (inside a hook, say), GIT_DIR beats `-C root` and the commit would land in the
|
||||
outer repo, and the test read global and system config. It now deletes GIT_DIR, GIT_WORK_TREE,
|
||||
GIT_COMMON_DIR and GIT_INDEX_FILE (as queue/tests/helpers.mjs gitEnv does) and sets
|
||||
GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_NOSYSTEM=1 (as ledger.test.mjs does). The `-c user.*`
|
||||
ident still applies. Test only; no mutant changes.
|
||||
|
||||
## 2026-10-23 sage-2925: optional editor.user (sage-3649)
|
||||
|
||||
sage-2925 (recorded in ludwig/row286-r2 agents/ludwig/work/queue-286/BUILD.md, Q4) ruled that row 283
|
||||
adds an optional `editor.user`, her Gitea login for the `library-human` slot, which row 288 (C2-02)
|
||||
passes to `humanCredential(context, {user})`. It reached this row after the DESIGN.md d2 approval
|
||||
(ludwig-0068, bubba-0009, sage-3649), so it departs from DESIGN.md section 1 ("exactly the keys
|
||||
`name` and `email`"). DESIGN.md stays unedited.
|
||||
|
||||
- mapping.mjs: `only(entry.editor, ['name', 'email', 'user'], 'editor')`. When present, `user` must be
|
||||
a string matching USER (`/^[A-Za-z0-9_.-]{1,100}$/`, the entry `user` check), else
|
||||
library-mapping-invalid "editor.user must be a plain account name". The output carries `user` only
|
||||
when given. An absent `editor.user` refusing library-human-missing is 288's (bubba-0009).
|
||||
- README: the `editor` line names `user` and the end-character rule from E2/E4.
|
||||
- Tests: T3 (mapping.test "editor is optional") gains one accepted case (`her.login-1`, kept in the
|
||||
output) and refused cases for '', an inner space, '@', 101 characters, 7 and null. They go in
|
||||
mapping.test with the other editor cases, not in the new supporting.test, which tests write paths.
|
||||
- No test outside mapping.test reads `editor` or pins mapping.mjs or README (git grep).
|
||||
|
||||
| Mutant | File | Replace | With | Killed by |
|
||||
|---|---|---|---|---|
|
||||
| M6e | src/mapping.mjs | `if (Object.hasOwn(entry.editor, 'user') && (typeof entry.editor.user !== 'string' \|\| !USER.test(entry.editor.user)))` | `if (false)` | mapping.test "editor is optional" (editor.user refusals) |
|
||||
| M6f | src/mapping.mjs | `...(Object.hasOwn(entry.editor, 'user') ? { user: entry.editor.user } : {})` | `...({})` | mapping.test "editor is optional" (`her.login-1` kept) |
|
||||
|
||||
## 2026-10-23 T2 run (sage-3695 CLEAR, lane A, library and business only)
|
||||
|
||||
Node v26.8.1. refactor was e2b873390 (226 landed); `git diff --stat 45dca69f6 e2b873390 -- packages/library
|
||||
packages/business` is empty. TMPDIR was a mktemp -d under /mnt/storage/scratch/tmp, with pipefail.
|
||||
Singles and mutants ran under `timeout -k 10 600`, suites under `timeout -k 10 1800`.
|
||||
|
||||
First-run red (mos-1272, placed): the first base run on 46bad7ea3 at 20:01:09Z was red in
|
||||
supporting.test "malformed paths refuse by class, in order". 5 of 6 passed, with `TypeError: Do not know
|
||||
how to serialize a BigInt`. The cause was the test helper, not the product: `refuses()` labelled each
|
||||
assertion with `JSON.stringify(path)`, and the 1n case added for E3 can't be stringified. In c18ad3e12
|
||||
the label uses `util.inspect(path)`, a 2-line test-only change. The product's 1n refusal was never in
|
||||
question: supportingPath type-checks before it stringifies (E3). The whole run below is on c18ad3e12.
|
||||
|
||||
Base, the touched files whole (20:01:43Z), all rc=0:
|
||||
- library mapping.test 16/16, scope.test 6/6, supporting.test 6/6, survey.test 7/7;
|
||||
- business business.test 21/21;
|
||||
- 0 skipped, 0 cancelled, 0 todo.
|
||||
|
||||
Mutants (20:01:44Z). Each was applied by exact string with count 1, then restored with `git checkout --`.
|
||||
Every restored sha256 equals HEAD's blob, and the tree was clean after.
|
||||
|
||||
| Mutant | Run | Result | Failing test |
|
||||
|---|---|---|---|
|
||||
| M1 | scope.test | rc=1, 5/6 | supporting comes before playground; a folder in both lists is supporting… |
|
||||
| M1 | supporting.test | rc=1, 5/6 | a path passes at book level, universe level, in a draft and in a folder in both lists |
|
||||
| M2 | mapping.test | rc=1, 15/16 | each supporting prefix rule refuses with its code |
|
||||
| M2 | business.test | rc=1, 20/21 | C2-04: library mappings load without roots, remain frozen, and retain mapping refusals |
|
||||
| M3 | supporting.test | rc=1, 5/6 | a symlink or gitlink on the way, or at the path itself, refuses |
|
||||
| M4 | supporting.test | rc=1, 5/6 | a name that differs from a tree name only in case or Unicode form refuses… |
|
||||
| M4b | supporting.test | rc=1, 5/6 | the same case/Unicode test |
|
||||
| M5 | supporting.test | rc=1, 5/6 | a path of exactly 1024 bytes passes, and 1025 bytes… refuses |
|
||||
| M6 | mapping.test | rc=1, 15/16 | editor is optional, kept when given, and refuses anything but a plain name and email |
|
||||
| M6b | mapping.test | rc=1, 15/16 | the same editor test |
|
||||
| M6c | mapping.test | rc=1, 15/16 | the same editor test |
|
||||
| M6d | mapping.test | rc=1, 15/16 | the same editor test |
|
||||
| M6e | mapping.test | rc=1, 15/16 | the same editor test |
|
||||
| M6f | mapping.test | rc=1, 15/16 | the same editor test |
|
||||
| M7 | supporting.test | rc=1, 5/6 | a path outside the universe's supporting folders refuses |
|
||||
| M8 | mapping.test | rc=1, 15/16 | each supporting prefix rule refuses with its code |
|
||||
|
||||
14 of 14 mutants killed, each on its named test only.
|
||||
|
||||
Suites (20:01:47Z):
|
||||
- `packages/library`: `node --test tests/*.test.mjs` rc=0, 98/98, 0 skipped.
|
||||
- `packages/business`: `node --test tests/` rc=0, 231/231, 0 skipped.
|
||||
|
||||
Not run here: every scripts/test-*.sh and the other packages. Those are Sage's 283 gate, after Webby's
|
||||
APPROVE.
|
||||
|
||||
## 2026-10-23 G1: mos-1528 OK for the mapping.test draft deepEqual (sage-3713)
|
||||
|
||||
This entry completes the N3 line above (BUILD.md:42, "only the draft deepEqual at mapping.test:48 gains
|
||||
`supporting: []`"). BUILD.md is append-only, so :42 stays as written. Mos OK'd the change of that old
|
||||
assertion in mos-1528. It is now at packages/library/tests/mapping.test.mjs:52, and commit 331653b38
|
||||
changed it. Everything else in that commit's hunk is new test lines, not changed old ones.
|
||||
|
||||
- Old, mapping.test.mjs:48 at 45dca69f6:
|
||||
`assert.deepEqual(m.universes.idea, { kind: 'draft', name: 'New World', books: [], playground: ['Drafts/New World/'] });`
|
||||
- New, mapping.test.mjs:52 at b2a1f4092:
|
||||
`assert.deepEqual(m.universes.idea, { kind: 'draft', name: 'New World', books: [], playground: ['Drafts/New World/'], supporting: [] });`
|
||||
|
||||
The line gains only `, supporting: []`; nothing else in it moved. The business.test and scope.test hunks
|
||||
add tests only (sage-3713: no OK needed). The other old-test change in this row is survey.test:73
|
||||
(mos-1330 item 4, 28918824c). Doc only.
|
||||
@@ -0,0 +1,191 @@
|
||||
# Row 283 (#1788) C2-01d design, draft 2 (Honker)
|
||||
|
||||
Brief: docs/plans/2026-10-20_case2-rows.md "C2-01d" at refactor 94c33a43c. Sources: the design at
|
||||
agents/gosalyn/work/case2/DESIGN.md b68d79663, the ruling after r11 (:207), the M12 paragraph (:300),
|
||||
1.1 (:755) and 1.2 (:817). Base 94c33a43c. Reviewer Webby, gate owner Sage. Nothing is built yet. The
|
||||
build waits for 282 to land (sage-2906).
|
||||
|
||||
## 1. Mapping (packages/library/src/mapping.mjs)
|
||||
|
||||
`supporting`:
|
||||
- Every universe kind takes an optional `supporting` list: plain `['name','books','playground','supporting']`,
|
||||
hybrid adds it to its seven keys, and a draft gets `['kind','name','playground','supporting']`.
|
||||
- Each value is checked as a playground prefix is: a string ending in `/` whose body passes `folder()`.
|
||||
- The output carries `supporting` on every universe, `[]` when absent, the way a draft carries
|
||||
`books: []`. classify then needs no `?? []`. The business writer writes the raw document
|
||||
(organization.mjs:195), not this output, so no business file changes shape.
|
||||
- The prefix rules, across every entry (ruling after r11):
|
||||
- A supporting prefix holding another supporting prefix, or one listed twice, refuses
|
||||
`library-mapping-invalid`.
|
||||
- A prefix equal to one of its own entry's book folders, or holding one, refuses `library-mapping-invalid`.
|
||||
- A prefix inside or holding another entry's book folder refuses `library-supporting-other-universe`.
|
||||
- Level comes from where the prefix sits, as for the playground: inside one of its own entry's books
|
||||
is book level, and outside every book is universe level. A draft's prefixes are all universe level.
|
||||
- Between the two lists (my choice, Q2):
|
||||
- A supporting prefix may equal a playground prefix of the same entry. That folder is writable.
|
||||
- A playground prefix may hold a supporting prefix of the same entry. The inner folder is supporting
|
||||
and the rest stays playground.
|
||||
- A supporting prefix that holds a playground prefix refuses `library-mapping-invalid`. classify
|
||||
checks supporting first, so that playground prefix could never match.
|
||||
- A supporting prefix equal to, holding or inside a playground prefix of another entry refuses
|
||||
`library-mapping-invalid`. Otherwise one path would have two universes.
|
||||
- The book loop at mapping.mjs:128-140 becomes a helper that both lists call with their own code and
|
||||
label. The playground's messages and codes stay byte-equal, so no existing test text moves.
|
||||
|
||||
`editor`:
|
||||
- The entry takes an optional `editor`. When present, it is an object with exactly the keys `name` and
|
||||
`email` (via `only()`). Each value is a string that is non-empty after trim and has no control
|
||||
character (`[\u0000-\u001f\u007f-\u009f]`), `<` or `>` (webby-0169 R1: git drops them from an ident
|
||||
and commits under a different name, so C2-02 would never see a failure). Anything else refuses
|
||||
`library-mapping-invalid`.
|
||||
- The output carries `editor: { name, email }` only when present, as `user` and `vaultPrefix` do. A save
|
||||
with no `editor` refusing is C2-02's job.
|
||||
|
||||
## 2. Scope (scope.mjs)
|
||||
|
||||
- `classify` gets a supporting loop before the playground loop (design 1.2). It returns `place(...)`
|
||||
with `{ scope: 'supporting', universe, book }`. The book is the book folder holding the prefix, or
|
||||
null at universe level and in a draft. The type and folder come from the first folder under the
|
||||
prefix, as for the playground.
|
||||
- The `.md` check stays first, so a `.canvas` or `.base` under a supporting prefix stays unmapped.
|
||||
- The comment above `classify` names the three prefix scopes and the order.
|
||||
|
||||
## 3. supportingPath (new supporting.mjs)
|
||||
|
||||
`supportingPath(mapping, universe, path, tree)` returns `path` or throws
|
||||
`LibraryError('library-write-story-path', ...)`. It is pure and reads no git.
|
||||
|
||||
- `tree` is the array `listTree` returns (reader.mjs:26, `{ mode, type, oid, size, path }`), so the
|
||||
C2-02 broker passes its listing as it is (Q3). A non-array `tree` throws a TypeError, because that is
|
||||
a caller bug, not a path class.
|
||||
- The checks run in this order, each with its own message naming the class:
|
||||
1. The path must be a string of at most 1,024 UTF-8 bytes (`Buffer.byteLength`).
|
||||
2. It must have no control character, using the same control class as `editor` (not `<` or `>`).
|
||||
3. It must have no backslash.
|
||||
4. It must not be absolute (a leading `/`).
|
||||
5. It must have no empty, `.` or `..` segment. That also catches `//` and a trailing `/`.
|
||||
6. `classify(mapping, path)` must give scope `supporting` and that same `universe`. This one check
|
||||
covers an unknown universe, an unmapped path, a `.canvas`, `.base` or attachment, a canon or
|
||||
story path, a playground path that isn't also supporting, and another universe's prefix. The scope
|
||||
rule then has one source, and M1 below hits both.
|
||||
7. Every proper prefix of the path (`a`, `a/b`, ...) and the path itself must not be in `tree` with
|
||||
mode `120000` (symlink) or `160000` (gitlink).
|
||||
8. No prefix of the path, and not the path itself, may equal a tree path or a tree directory
|
||||
(derived from the entries' paths) after `normalize('NFC').toLowerCase()` on both sides, unless
|
||||
it equals that tree path exactly. NFC as well as case, because APFS treats NFC and NFD forms of
|
||||
a name as one file (webby-0169 N1).
|
||||
Directories are checked too, because the folders under a supporting prefix are hers to name. A
|
||||
path in a tree that holds both `a.md` and `A.md` refuses, which matches R7.
|
||||
- The refusal messages quote the path with JSON, except for the control-character class, whose message
|
||||
leaves the path out.
|
||||
- The delete rule ("A delete also needs the path to exist at the tip as a 100644 blob", M12) isn't in
|
||||
the brief's supportingPath list. It stays with C2-02's delete (Q4).
|
||||
The C2-02 brief doesn't name it (only design :308), so BUILD.md carries it to C2-02 under Handoffs
|
||||
(webby-0169 Q4).
|
||||
- index.mjs exports `supportingPath`.
|
||||
|
||||
## 4. README (packages/library/README.md)
|
||||
|
||||
- Mapping: `supporting` and its rules, the cross-list rules, and `editor`.
|
||||
- Refusals: a `library-supporting-other-universe` row, and the new cases in the
|
||||
`library-mapping-invalid` row.
|
||||
- Scope: supporting first, then playground, then canon.
|
||||
- A new section, "Agent write paths", for supportingPath and its classes.
|
||||
|
||||
## 5. Callers of classify outside files owned (Q1 for Sage)
|
||||
|
||||
- survey.mjs:65 builds `{ canon: 0, playground: 0, unmapped: 0 }` and increments
|
||||
`scopes[classify(...).scope]`. A supporting file adds `supporting: NaN`, and formatSurvey (:92) never
|
||||
prints it. I propose a departure: add `supporting: 0` and print it, so the line becomes
|
||||
`scopes canon 5, playground 1, supporting 0, unmapped 3; indexed 6`. That re-pins survey.test:73 and
|
||||
adds a count test. Without it, the survey reports garbage once she marks a folder. The departure
|
||||
covers two files, packages/library/src/survey.mjs and packages/library/tests/survey.test.mjs.
|
||||
- No change in the rest, each for a reason:
|
||||
- reader.mjs:183 skips only unmapped, so supporting files are indexed like playground files.
|
||||
- R5 (:207) flags only canon to playground, as design 1.3 R5 says. A canon link to a supporting file
|
||||
raises nothing.
|
||||
- render.mjs:23 gives supporting a null badge. The supporting badge belongs to C2-10b (design :1415).
|
||||
- mirror.mjs:246 refuses only unmapped. Reading supporting files by scope is C2-05c's.
|
||||
- No bus or webui source filters on scope (git grep at 94c33a43c).
|
||||
- M9 already protects the whole `projects` key from agent edits (library-projects-protected.test). No
|
||||
test is added there.
|
||||
|
||||
## 6. Tests
|
||||
|
||||
- mapping.test:
|
||||
- T1: `supporting` at book level, at universe level, in a hybrid and in a draft. The output carries
|
||||
it, with `[]` when absent. The draft `deepEqual` at :48 gains `supporting: []`.
|
||||
- T2: one test per prefix rule.
|
||||
- These refuse `library-mapping-invalid`: no trailing `/`, a bad folder, one holding another, one
|
||||
listed twice, one equal to its own book, one holding its own book, one holding a playground
|
||||
prefix, and one overlapping another entry's playground.
|
||||
- These refuse `library-supporting-other-universe`: one inside another universe's book, and one
|
||||
holding another universe's book.
|
||||
- An equal same-entry playground passes.
|
||||
- T3: `editor` passes, and absent passes. It refuses `library-mapping-invalid` for a non-object, an
|
||||
array, an extra key, a missing key, an empty or blank value, a non-string, and a control character,
|
||||
`<` or `>` in either value.
|
||||
- scope.test, T4:
|
||||
- Supporting at book level, universe level and in a draft each give their universe and book.
|
||||
- A folder in both lists is `supporting`, and a playground-only folder is `playground`.
|
||||
- A supporting `.canvas` or `.base` is unmapped.
|
||||
- supporting.test (new):
|
||||
- T5 passes at book level, universe level, in a draft, in a both-lists folder, and at exactly 1,024 bytes.
|
||||
- T6 has one refusal per class against a fixture tree with a symlink, a gitlink and a case variant.
|
||||
The classes are:
|
||||
- unmapped, `.canvas`, `.base`, an attachment, canon, playground-only, another universe's
|
||||
supporting prefix, and an unknown universe;
|
||||
- absolute, `..`, `.`, `//`, a trailing `/`, a backslash, a C0 control and DEL;
|
||||
- a symlink folder, a gitlink folder, and a symlink at the path itself;
|
||||
- a case-variant file, a case-variant folder, and an NFD-form variant of an NFC tree name;
|
||||
- 1,025 bytes made with a multi-byte character, so bytes and characters differ.
|
||||
- Each asserts the code `library-write-story-path`.
|
||||
- business.test: T7 adds one assertion that `validateBusinessDocument` refuses
|
||||
`library-supporting-other-universe`.
|
||||
- survey.test (only if Q1 is approved): T8 counts one supporting file, and :73 is re-pinned.
|
||||
|
||||
## 7. Mutants (exact strings go in BUILD.md)
|
||||
|
||||
| Mutant | Change | Killed by |
|
||||
|---|---|---|
|
||||
| M1 | classify checks playground before supporting (the brief's mutant) | T4 both-lists, T5 both-lists |
|
||||
| M2 | the other-universe fail for supporting is dropped | T2 |
|
||||
| M3 | the symlink and gitlink check is dropped | T6 |
|
||||
| M4 | the case check is dropped | T6 |
|
||||
| M4b | check 8 drops `normalize('NFC')` | T6 NFD variant |
|
||||
| M5 | the byte length uses `path.length` | T6 1,025 bytes |
|
||||
| M6 | the `editor` control-character check is dropped | T3 |
|
||||
| M6b | the `editor` `<` and `>` check is dropped | T3 |
|
||||
| M7 | supportingPath skips the universe match | T6 other universe |
|
||||
| M8 | the supporting-holds-playground check is dropped | T2 |
|
||||
|
||||
## 8. Gate
|
||||
|
||||
- Webby approves the manifest.
|
||||
- The library and business suites and every scripts/test-*.sh are green on Sage's gate.
|
||||
- Timeouts (mos-1275): each package `timeout -k 10 1800`, each script `timeout -k 10 300`, each single
|
||||
file and mutant `timeout -k 10 600`. These come from my row 256 gate at 10:34Z.
|
||||
- Before in-review, grep packages/*/tests for 64-hex pins on the changed files and for the changed
|
||||
message text, then drift-check BASE..refactor.
|
||||
|
||||
## 9. Questions
|
||||
|
||||
- Q1 (Sage): approve the survey.mjs and survey.test.mjs departure in section 5?
|
||||
- Q2 (Webby): the cross-list rules in section 1?
|
||||
- Q3 (Webby): `tree` as the `listTree` array?
|
||||
- Q4 (Webby): the delete's 100644 check stays with C2-02?
|
||||
- Q5 (Webby): `editor` refuses only blank values and control characters, as the brief says. `<` and `>`
|
||||
would break a git ident. I'd leave them to C2-02's commit step unless you want them refused here.
|
||||
|
||||
## 10. Draft 2 (webby-0169)
|
||||
|
||||
- R1 folded: `editor` refuses `<` and `>` too, with T3 cases and mutant M6b.
|
||||
- Q2 accepted. BUILD.md records the tradeoff: refusing a supporting prefix that holds a playground
|
||||
prefix goes past the brief, but it is the fail-closed reading and uses an existing code. The cost
|
||||
is that she can't nest a playground under a supporting folder, though she can nest the other way.
|
||||
- Q3 accepted. Q4: carried to C2-02 under Handoffs in BUILD.md, and Sage was asked to add it to C2-02's brief.
|
||||
- N1 folded into check 8 (NFC and case), with an NFD case in T6 and mutant M4b.
|
||||
- N2: the survey.test:73 pin gains only `supporting`, and the count test is the only addition
|
||||
(mos-1330, its own commit).
|
||||
- N3: before the build, re-grep tests outside packages/library for a `deepEqual` of the mapping output
|
||||
on the build base.
|
||||
@@ -0,0 +1,342 @@
|
||||
# Row 272 / 264e (#1777): session CLI and Console chooser
|
||||
|
||||
Owner Huey; reviewer Scrooge per sage-2816 / mos-1310 item 2 (supersedes sage-2796). Current base 5fdfd7275, branch huey/row272-5fdfd7275 (sage-3354). Original draft base 2d0d407ba; round 1 was 850b22680 on b23783f39.
|
||||
Brief: docs/plans/2026-10-21_t1-sessions.md (ff0be0c79), section 8; row 264 rulings
|
||||
at 5f5b51dd6. Dependencies: 264a-d landed; 270/271 landing is 7328da8ca.
|
||||
Count/clear implementation and contracts are now in the current base. No runtime, launcher, library adapter,
|
||||
role, schema or queue edits belong to this slice.
|
||||
|
||||
## Execution and evidence
|
||||
|
||||
1. Read session/channel/HTTP/browser contracts and write isolation/validation tests.
|
||||
2. Add session CLI through the existing verified human channel, preserving defaults.
|
||||
3. Add authenticated Console chooser, per-session drafts/history/cursors, count and
|
||||
clear status. Unavailable dependency verbs refuse explicitly; no simulated success.
|
||||
4. On Sage's CLEAR: claim, run focused tests and required suites/mutants, repair and
|
||||
record exact evidence. No Node, tests, HTTPS git, claim or push under Gander's gate.
|
||||
5. After 253 and 269 land, rebase on a new branch name. Review packet to Scrooge,
|
||||
Mos, Gizmoduck and Sage; push only when permitted.
|
||||
|
||||
2026-10-22 preparation: clean worktree switched locally to the requested base. Read
|
||||
approved brief and all Q1-Q7 rulings. Della notified: no startBroker/library changes.
|
||||
Gizmoduck asked to pin count/clear contracts. Tests will be written first; Sage's
|
||||
explicit gate restriction defers RED/GREEN execution. No test result claimed.
|
||||
|
||||
Verification plan: CLI real-broker isolation and channel refusal; HTTP strict keys,
|
||||
Origin/Fetch Metadata, forged selector/refusal and no retries; fixture-browser two
|
||||
sessions, isolated draft/cursor/receipt, deep-link/refresh, count unknown/known,
|
||||
read-only writes, clear confirmation/pending. Add selector omission and cursor/draft
|
||||
sharing mutants. Broader cli/webui/plugins suites use Sage's gate only.
|
||||
|
||||
264c/d contracts received gizmoduck-0260: session.list gains compactions and revision/state;
|
||||
session.clear and session.operation use the pinned envelopes. CLI polls at most 30 s
|
||||
then reports pending; Console status checks use GET only. Counts absent on 264b show
|
||||
unavailable; unknown never becomes zero. Clear absent on 264b is disabled with a reason.
|
||||
USER_VERBS and tester listener write policy remain unchanged (Q7). Existing collaborators
|
||||
therefore cannot enter the new session surface until the reviewed RBAC integration.
|
||||
|
||||
Consumer audit found the explicitly deferred B6 user-reply scope (DEFERRED.md:455-459,
|
||||
queue-264/BUILD.md:317-322) bound to 272. Added a narrow broker predicate change from
|
||||
thread to session and a sibling-session regression fixture, without widening user verbs.
|
||||
This is the only planned broker change and does not overlap Della's runtime adapter.
|
||||
|
||||
2026-10-22 static review: added explicit --session to thread read/open and talk/follow;
|
||||
omission remains the saved default. CLI clear first reads a recorded operation, so a
|
||||
repeat after a lifecycle revision change cannot become a conflicting second write.
|
||||
Console deep links name both thread and session; accepted-send receipts, drafts and
|
||||
history pagination use that tuple. Clear-status GETs refresh the envelope without
|
||||
reissuing clear. Read failures discard private metadata; late generations cannot paint
|
||||
another selection. Scrooge is reviewer per sage-2816 / mos-1310, superseding the first
|
||||
assignment. CLEAR remains after 269, 275, 203 and 207. No execution evidence yet.
|
||||
|
||||
## Acceptance / mutant map (prepared, unexecuted)
|
||||
|
||||
| Check | Evidence file / intended kill |
|
||||
| --- | --- |
|
||||
| T5 legacy human, forged agent, foreign session, strict HTTP identity/Origin/site | session-cli.test.mjs, chat.test.mjs; unchanged USER_VERBS matrix in user-principal.test.mjs |
|
||||
| T7 saved default, explicit CLI session selector, metadata-only create | session-cli.test.mjs real broker and thread CLI compatibility tests |
|
||||
| T7 Console chooser, deep link, draft/cursor/receipt isolation, unknown count, confirmation/pending | chat-browser.test.mjs; existing history/sign-out/late-read tests run whole |
|
||||
| Count/clear contract absence | CLI unavailable refusal; Console disabled clear reason; exact zero vs unknown lower bound tests |
|
||||
| B6 assigned follow-up | user-principal.test.mjs sibling-session citation refusal and valid same-session reply |
|
||||
| M1 omit session in chat.mjs messages forwarding | chat.test.mjs explicit selector deepEqual fails |
|
||||
| M2 collapse browser state key from thread/session to thread | chooser draft isolation assertion fails |
|
||||
| M3 treat known:false count:0 as exact zero | CLI label assertion and browser unknown-count assertion fail |
|
||||
| M4 restore B6 mt.thread/s.thread predicate | user-principal sibling refusal assertion fails |
|
||||
| M5 remove session from CLI thread.send payload | explicit --session talk assertion fails |
|
||||
| M6 repeat a clear write in place of session.operation poll | CLI exactly-one-clear assertion fails |
|
||||
|
||||
Intended focused commands, only after CLEAR, one process at a time:
|
||||
|
||||
- `timeout -k 10 300 node --test packages/cli/tests/session-cli.test.mjs`
|
||||
- `timeout -k 10 300 node --test packages/cli/tests/thread-cli.test.mjs`
|
||||
- `timeout -k 10 300 node --test packages/bus/tests/user-principal.test.mjs`
|
||||
- `timeout -k 10 300 node --test packages/plugins/tests/channel.test.mjs`
|
||||
- `timeout -k 10 300 node --test packages/webui/tests/chat.test.mjs`
|
||||
- `timeout -k 10 600 node --test packages/webui/tests/chat-browser.test.mjs`
|
||||
|
||||
Whole touched suites on Sage's slot: bus (900 s), cli (1800 s, >=3x recorded 591 s),
|
||||
webui (600 s), plugins (600 s), plus required integration scripts per final gate brief.
|
||||
Mutants run in disposable copies of the pinned candidate, never against a live stack;
|
||||
only failed assertions count as kills, never a timeout or import/setup failure.
|
||||
No test process, browser, provider, credential read or live service action ran in this
|
||||
preparation. `git diff --check` is the only executed draft verification so far.
|
||||
|
||||
Next: Sage CLEAR for claim/focused runs; repair against actual results, then rebase on
|
||||
a newly named branch once 253 and 269 land. Generate final manifest only after those
|
||||
changes. Review handoff is Scrooge and Mos, copying Gizmoduck and Sage; no handoff to
|
||||
Duckworth under the superseded assignment. No candidate commit or green claim yet.
|
||||
|
||||
2026-10-22, gizmoduck-0272 / sage-2828 additive contract at 78e14352f: added
|
||||
session.cancel to CLI/channel/Console with confirmed human writes and strict keys.
|
||||
Cancelled envelopes retain `retired:null` (old active) or `true` (startup cancelled,
|
||||
old retired); cancelled is terminal for polling. CLI reports routed message cancellation
|
||||
with stored ID and exit 3, never waits for delivery or replays it. HTTP maps
|
||||
session-name-too-long and session-clear-complete without reflecting transport text.
|
||||
Added unexecuted before/after cancellation, identity/Origin, confirmation and message
|
||||
receipt tests. Existing clear/create/read wiring is preserved. Asked Gizmoduck whether
|
||||
the deterministic successor keeps 264a's UUID shape, as selectors currently require.
|
||||
No Node, test run, claim, commit, push, provider call or live action. Static diff only.
|
||||
|
||||
Gizmoduck-0274 resolves the selector compatibility check: successors are lowercase
|
||||
RFC 9562 v8 UUIDs (variant 10), while 264a sessions are v4. Existing generic UUID
|
||||
validators accept both and stay unchanged. CLI explicit-selector and Console chooser
|
||||
fixtures now include mixed v4/v8 IDs to prevent a later v4-only regression. Unexecuted.
|
||||
|
||||
|
||||
## sage-3141 rebase and landed-contract check (2026-10-23)
|
||||
|
||||
Reapplied the uncommitted draft onto b23783f39 in the same isolated worktree, on new
|
||||
branch huey/row272-b23783f39. No candidate commits existed to replay. Preserved the
|
||||
original draft in the named local stash `huey row272 draft before sage-3141 rebase`.
|
||||
Product files merged without conflict. Only append-only BUILD-LOG.md and SESSIONS.md
|
||||
conflicted: kept every new-base byte and appended the original Huey entries unchanged.
|
||||
No force push, fetch, Node, test, queue write or live change. No claim or candidate
|
||||
commit yet. Static `git diff --check` clean after resolution.
|
||||
|
||||
Checked the actual landed broker, not just the older contract packet:
|
||||
|
||||
- broker.mjs:687-707 clearEnvelope supplies the same op/thread/session/successor/state/
|
||||
start/refusal/at fields and cancelled-only retired:null|true. The generic UUID
|
||||
selectors still accept both original v4 and successor v8 IDs.
|
||||
- broker.mjs:6227-6245 session.list supplies compactions, state, revision and successor;
|
||||
count reader/unknown behavior is the landed 270 contract. Real-base tests must now
|
||||
exercise these available fields; stand-ins still exercise an unavailable dependency.
|
||||
- broker.mjs:6270-6330 clear/cancel/operation inputs and receipts match the wired contract.
|
||||
clear repeats preserve the op; cancel also refuses a failed clear as
|
||||
session-clear-complete. Our existing refusal mapping already covers it.
|
||||
- queue-271/BUILD.md:225-238 clarifies cancellation after retirement cancels this clear's
|
||||
startup, while later deliberate human open/send can start the fresh successor. It
|
||||
does not permanently disable that new session. No frontend authority change needed.
|
||||
- store.mjs:17 is 3n; migration is 3m-3n.sql. This slice adds/pins no schema step,
|
||||
migration blob or schema test count. No DDL copied from an older branch.
|
||||
|
||||
Plan after the Gander 574 run-2 hold: on a claims-only CLEAR, claim only; run no Node
|
||||
or tests beyond the explicit scope of Sage's grant. On test CLEAR run new CLI tests
|
||||
first, then new broker/HTTP/browser cases and whole touched files, sequentially under
|
||||
the recorded timeouts. Place/report a first-run red only in a new test; do not alter
|
||||
an old test, loosen an assertion or fix a second red without Mos's ruling via Sage.
|
||||
Whole-suite/mutant execution remains on Sage's slot. Candidate commit and push follow
|
||||
applicable green evidence and an allowed gap, then Scrooge/Mos handoff with final SHA,
|
||||
manifest, copying Gizmoduck/Sage.
|
||||
|
||||
Existing-test edits in the inherited draft need Mos's ruling through Sage before
|
||||
execution: channel.test.mjs extends its existing supported-verbs table for five
|
||||
session verbs; chat-browser.test.mjs extends the existing fetch fixture with session
|
||||
catalog/operation responses and changes its exact send-payload assertion to include
|
||||
the selected session. Assertions are not loosened. All other added test cases are
|
||||
new tests. These changes were prepared before sage-3141 and have never run. Ask Sage
|
||||
to forward this precise delta rather than treating a later old-test red as permission.
|
||||
|
||||
sage-3145 correction: refs/stash is shared across worktrees; using it as sole draft
|
||||
storage was wrong. Pinned the exact draft commit 07dc9d2f7944b4781b5e80dfa18425f4749f7a45
|
||||
on local branch huey/row272-stash-keep before any stash cleanup. The entry remains
|
||||
untouched; the branch retains its tracked and untracked snapshots. Future seat draft
|
||||
preservation uses owned branches/local commits, not shared stash positions.
|
||||
|
||||
Sage explicitly directed a local draft commit before tests to preserve the rebased
|
||||
work. This overrides the normal green-before-commit gate for this storage checkpoint
|
||||
only. It is an untested draft, not an approved candidate, test result, push or landing.
|
||||
Claims await the post-574-run-2 CLEAR; test execution awaits Mos's ruling and Sage's
|
||||
separate test CLEAR. Old-test deltas were forwarded as sage-3144; no ruling assumed.
|
||||
|
||||
## Scrooge early static read, 850b22680 (2026-10-23)
|
||||
|
||||
Review key scrooge-272-early-read-huey-1. B1 confirmed: the render catch used any
|
||||
403/404 to delete every sibling's draft/receipt and clear/create operation key.
|
||||
Uncommitted follow-up restricts that purge to thread-not-owned, thread-not-found and
|
||||
access-refused. Session-not-found still shows an unavailable read, preserving sibling
|
||||
state and pending recovery coordinates. New browser test retains A's draft and a lost
|
||||
clear key across a bad-session deep link, then proves Clear queries the retained op
|
||||
without another write.
|
||||
|
||||
N1: malformed clear/cancel write receipts now add the same redacted outcome-unknown
|
||||
audit line as other writes. New HTTP test checks one write log and no read log.
|
||||
N2: a valid recorded clear belonging to another session now refuses with a precise
|
||||
operation-key-reuse message (exit 2), rather than ambiguous outcome unknown; new CLI
|
||||
test proves no clear write. Malformed envelopes retain the outcome-unknown refusal.
|
||||
No approved old assertion or shared stub edited. Static diff check only; all new tests
|
||||
remain unrun pending Sage's test CLEAR. Published round 1 still pins 850b22680 and its
|
||||
original manifest; these follow-up edits do not replace that evidence. No new commit,
|
||||
push, queue write, provider call or live change for this early-read follow-up.
|
||||
|
||||
Execution watch: inspect message-filter.test first if the bus suite reports a B6
|
||||
fixture red (manual message_threads rows must name the run's bound session); do not
|
||||
change an old fixture/assertion without Mos's ruling via Sage. thread-shares uses the
|
||||
persisted session_defaults mapping per Scrooge's check. No runtime verdict claimed.
|
||||
|
||||
## Granted-user legacy compatibility (sage-3357; pre-candidate design)
|
||||
|
||||
219/295 are retained on base 5fdfd7275. USER_VERBS still excludes session verbs;
|
||||
193/T5 user session grants are a separate DEFERRED item, outside 272. No owner
|
||||
transport carries a user session operation. Bounded negotiation applies only to the
|
||||
session.list read: only `access-refused` and an omitted-session route permit legacy
|
||||
thread-default read/send/open. All other errors keep the unavailable/error state, and
|
||||
explicit session links always refuse. On fallback session controls are hidden and one
|
||||
status line says sessions are not available for this account. No fake session ID,
|
||||
count or lifecycle status is invented; the broker continues choosing its persisted
|
||||
default under current grants on every existing thread request. Owner routes keep the
|
||||
normal chooser and explicit selected-session arguments.
|
||||
|
||||
New file chat-sessions-access.test.mjs uses the real broker with an already granted
|
||||
principal over loopback and the existing repository browser fixture. It checks legacy
|
||||
read/send/open with no session controls, explicit-link refusal, another-error refusal,
|
||||
and unchanged owner chooser. No shared stub or existing assertion changes. The three
|
||||
required mutants remove the access-refused check, remove the omitted-session check,
|
||||
and display the controls during fallback; guard tests must kill each by assertion.
|
||||
|
||||
RED-first slot sage-3358: only this new file, before fallback implementation. Expected
|
||||
run 100 s including four Chromium fixtures; bound 300 s (3x), command
|
||||
`timeout -k 10 300 node --test packages/webui/tests/chat-sessions-access.test.mjs`.
|
||||
TMPDIR must be assigned from mktemp -d, with no underscore. No compose service is used;
|
||||
check/remove the generated directory's compose default network if any exists. Expected
|
||||
legacy-positive red is placed/reported afterward under mos-1272. Any other red is
|
||||
reported to Sage before a fix. No other test file/suite is authorized in this slot.
|
||||
|
||||
sage-3358 RED run completed before the fallback implementation. Only the new
|
||||
chat-sessions-access.test.mjs ran, under the 300 s bound, TMPDIR allocated by
|
||||
`mktemp -d /tmp/huey272-red-XXXXXXXX` as /tmp/huey272-red-0q66Jcf4. Exit 1;
|
||||
4 tests, expected legacy-positive assertion red and 3 passes, no other reds/skips/
|
||||
cancellations/timeouts, 3.504 s. Exact output: r2-red-access.tap. No matching compose
|
||||
network existed. Slot returned to Sage/Mos as huey-0035; no further tests run.
|
||||
|
||||
mos-1442 / sage-3361 adds explicit timeout, bus-error and malformed-answer refusal
|
||||
coverage. The new private fixture adds those guard cases (unrun), with a widening-key
|
||||
mutant alongside the access-refused/omitted-selector/control mutants. No shared stub
|
||||
or existing assertion changed. Native preview status/open both reported unavailable
|
||||
in this headless environment before the authorized repository-browser regression.
|
||||
|
||||
Implementation now local: the catalog request catches only access-refused on omitted
|
||||
session routes. Its internal null result selects the existing broker-chosen thread
|
||||
default with no fabricated session ID. Read/send/open omit the session property in
|
||||
that mode. One status line replaces all session controls. Explicit links and any
|
||||
other failure never select that mode. USER_VERBS and all owner transport routing
|
||||
remain byte-unchanged. Guard cases are preserved on the pre-fallback baseline; their
|
||||
RED evidence comes from the required mutants, as Sage accepted in sage-3358.
|
||||
|
||||
Fallback mutants are executable via fallback-mutants-r2.py, only on Sage's slot.
|
||||
It copies packages/roles into a private mktemp directory and mutates only the copied
|
||||
chat.js, never candidate bytes. Each run has a 300 s bound and requires a named
|
||||
assertion failure; timeout/import/setup failures do not count. F1 removes the exact
|
||||
access-refused key (human-required guard); F2 removes the omitted-selector check;
|
||||
F3 shows session controls; F4 widens the key to timeout/unreachable, no-bus-host and
|
||||
invalid-response (timeout guard). The test file also independently checks bus-error
|
||||
and malformed-answer refusal. These mutants and the post-implementation tests have
|
||||
not run locally. The earlier six non-fallback mutants in this packet remain required
|
||||
in the full gate; the new runner covers the four fallback mutants only.
|
||||
|
||||
|
||||
## Round 2 rebase receipt (sage-3354)
|
||||
|
||||
Queue rev 3510 returned row 272 to in-progress. On new branch huey/row272-5fdfd7275,
|
||||
replayed round 1 and the preserved B1/N1/N2 fixes onto 5fdfd7275 (219, after 295).
|
||||
Resolved exactly the seven named conflicts. Test append conflicts retain both complete
|
||||
sets of tests; existing test content equals the new base plus only the mos-1388 hunks.
|
||||
No old assertion or shared stub was changed beyond those approved hunks. Static byte
|
||||
comparison confirms the entire upstream test file prefixes and approved modifications.
|
||||
Both append-only logs retain the complete 5fdfd7275 prefix plus Huey's entries.
|
||||
|
||||
219's per-instance thread picker, explicit createThread instance forwarding, choice
|
||||
refresh/revocation behavior and tests remain present. 295's createChat owner-settings
|
||||
read stays unchanged: only plugins uses the owner transport; user chat verbs stay on
|
||||
the user transport. Removed a duplicate access-refused status entry introduced by
|
||||
combining those patches. USER_VERBS is unchanged. No schema or migration changed.
|
||||
|
||||
This is a pre-gate candidate per Sage's explicit instruction to commit/push/request
|
||||
round 2 before Sage runs the full gate. Only the expected-red new fallback test ran
|
||||
under sage-3358. No green test claim, no suite/mutant result and no old-file run is
|
||||
implied. Every touched test file still needs its whole-file run on Sage's slot, plus
|
||||
all touched suites and mutants. Any first-run red in launcher/creative/chat-browser
|
||||
returns to Mos via Sage before fixing. TMPDIR for gate runs comes only from mktemp -d,
|
||||
with no underscore; remove a matching compose default network only if created.
|
||||
|
||||
|
||||
## Round 3 preparation — gate run 1 (sage-3433)
|
||||
|
||||
Sage ran candidate b44332a93 over refactor 1e4c1e6e9 with the pinned esbuild
|
||||
and MOSAIC_REQUIRE_EDITOR_BUILD=1. Gate RED: bus 891/0, business 231/0,
|
||||
CLI 940/1, plugins 110/1, webui 372/20. No mutants ran. Sage reproduced
|
||||
every failing file on b44332a93 alone, excluding overlay drift as the cause.
|
||||
Evidence: /mnt/storage/scratch/sage-r272/run-*.txt and split-*.txt.
|
||||
|
||||
A: the new session CLI real-broker fixture omitted role authority. Verified
|
||||
checkBusiness requires withinRole and crossRole arrays on every role; added
|
||||
empty arrays to chief and assistant in that fixture only, as sage-3433 permits.
|
||||
No production authority or existing assertion changed. No local tests ran.
|
||||
|
||||
B remains held for Mos under mos-1316/1317: initial bare-chat rendering compares
|
||||
old?.id with undefined id, then dereferences null old at the sessions/session/key
|
||||
initializers. Sage proposes old && old.id === id at all three sites. No B edit
|
||||
made pending the ruling. C (plugins harness compactions) is separately owned.
|
||||
|
||||
Round 3 is not committed or pushed yet. The manifest path set remains the same
|
||||
17 paths; regenerate hashes when the authorized fixes are complete. Future
|
||||
launcher, creative and chat-browser runs use a TMPDIR allocated with
|
||||
mktemp -d /mnt/storage/scratch/tmp/<alnum>XXXXXX, because bwrap hides /tmp.
|
||||
Sage owns the next full gate. No tests, repo Node or queue writes here.
|
||||
|
||||
|
||||
### Round 3 candidate — mos-1455 / sage-3435
|
||||
|
||||
Mos authorized B: all three initial render comparisons now use old && old.id === id,
|
||||
so an initial list route cannot dereference null old. A remains fixture-only.
|
||||
chat-browser.test.mjs and granted-user.test.mjs are byte-identical to b44332a93.
|
||||
No pre-existing assertion, authority validator or shared stub changed.
|
||||
|
||||
Frozen manifest: manifest.r3.txt, 17 paths unchanged; SHA256 f98c5b7a884ed1e5951a584227ece16d403baf62199d405aaa1981d667e9e075.
|
||||
Parent candidate: b44332a9360504291dda3f81131c934b286f3226; base remains 5fdfd7275.
|
||||
Only session-cli.test.mjs and public/chat.js product hashes differ from round 2.
|
||||
This follow-up commit/push is authorized before the round 3 gate by sage-3435.
|
||||
No local tests, repo Node, mutants or queue writes ran. Gate remains RED until
|
||||
Sage reruns; plugins harness compactions is a separately owned dependency, and
|
||||
Mos declined an EXCEPT edit. Scrooge reviews this A/B delta; Sage posts round 3.
|
||||
|
||||
|
||||
## Round 4 — scoped browser heading waits (mos-1460/1461)
|
||||
|
||||
Round 3 gate on 72690cae7: bus 891/0, CLI 941/0, business 231/0; plugins
|
||||
had only the allowed exact base red, checked by Mos; webui 390/2. Both reds
|
||||
were newly added chat-browser missing-session waits at lines 361 and 437.
|
||||
Whole-file rerun reproduced 17/2. No round 3 green acceptance claim.
|
||||
|
||||
Diagnosis authorized by sage-3450/3453 and mos-1460: unchanged whole-file run
|
||||
17/2, 28.223 s; scratch-only CDP probes showed the route reaching chat.js:176,
|
||||
catalog rows A/B without M, and catch receiving session-not-found with status
|
||||
404. DOM had first h1 "Control board" (shell), then .chat-area h1 "Thread
|
||||
unavailable". Product refusal and painting worked. Evidence retained under
|
||||
/mnt/storage/scratch/tmp/hueyP1enyB/ (baseline.tap, probe.tap, probe-catch.tap,
|
||||
proposed.diff); scratch probe source removed, tracked tree remained clean.
|
||||
|
||||
mos-1461 / sage-3458 authorizes exactly two selector changes: document-wide
|
||||
h1 becomes .chat-area h1 in those waits. Expected text, timeout and every
|
||||
other line of chat-browser.test.mjs remain byte-identical to 72690cae7.
|
||||
This scopes the same assertion to its component; it does not weaken the
|
||||
composer/write/draft/recovery checks. No production code changes in round 4.
|
||||
|
||||
manifest.r4.txt retains the same 17 paths; SHA256 a47ed1a23fb98c1ffe6cbcc4e7130e5c515387a2c5dc41e5ae1cbf04640d8c1b.
|
||||
Only chat-browser.test.mjs hash differs from r3. New local follow-up commit
|
||||
is authorized before execution; push and single-file run await Sage CLEAR
|
||||
while Gizmoduck owns the slot. Required pass: whole chat-browser.test.mjs,
|
||||
19/19 and skipped 0, timeout -k 10 300, TMPDIR from
|
||||
mktemp -d /mnt/storage/scratch/tmp/hueyXXXXXX. No round 4 run yet.
|
||||
After green, claimant moves row to in-review; Sage posts request and
|
||||
Scrooge reviews the exact two-line delta. Full five-suite gate remains Sage's.
|
||||
@@ -0,0 +1 @@
|
||||
5fdfd7275ce3048e488d93dcdb1f9f2aa5961a69
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run only on Sage's slot. Mutate disposable sources, never the candidate tree."""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
GUARD = "if (err.code === 'access-refused' && !match[3]) return null;"
|
||||
CONTROLS = " if (current.legacyDefault) return '<p role=\"status\">Sessions are not available for this account.</p>';"
|
||||
MUTANTS = {
|
||||
'F1-any-code': (GUARD, "if (!match[3]) return null;", 'human-required catalog failure'),
|
||||
'F2-explicit-link': (GUARD, "if (err.code === 'access-refused') return null;", 'granted explicit-session route'),
|
||||
'F3-controls': (CONTROLS, ' // mutant: show session controls on a legacy fallback', 'granted legacy route'),
|
||||
'F4-widened-key': (GUARD, "if (['access-refused', 'unreachable', 'no-bus-host', 'invalid-response'].includes(err.code) && !match[3]) return null;", 'timeout catalog failure'),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument('--out', type=Path, required=True)
|
||||
ap.add_argument('--mutant', choices=list(MUTANTS), action='append')
|
||||
args = ap.parse_args()
|
||||
source = Path(__file__).resolve().parents[4]
|
||||
args.out.mkdir(parents=True, exist_ok=True)
|
||||
for name in args.mutant or MUTANTS:
|
||||
# Explicit mktemp, no underscores: same TMPDIR rule as the real gate.
|
||||
scratch = Path(subprocess.check_output(['mktemp', '-d', '/tmp/huey272-fallback-mutant-XXXXXXXX'], text=True).strip())
|
||||
try:
|
||||
for directory in ['packages', 'roles']:
|
||||
shutil.copytree(source / directory, scratch / directory)
|
||||
if (source / 'node_modules').exists():
|
||||
(scratch / 'node_modules').symlink_to((source / 'node_modules').resolve(), target_is_directory=True)
|
||||
for filename in ['package.json', 'RELEASE']:
|
||||
if (source / filename).exists():
|
||||
shutil.copy2(source / filename, scratch / filename)
|
||||
path = scratch / 'packages/webui/src/public/chat.js'
|
||||
old, new, target = MUTANTS[name]
|
||||
text = path.read_text()
|
||||
if text.count(old) != 1:
|
||||
raise RuntimeError(f'{name}: mutation anchor is not unique')
|
||||
path.write_text(text.replace(old, new, 1))
|
||||
env = dict(os.environ, TMPDIR=str(scratch))
|
||||
result = subprocess.run(['timeout', '-k', '10', '300', 'node', '--test', '--test-reporter=tap',
|
||||
'packages/webui/tests/chat-sessions-access.test.mjs'],
|
||||
cwd=scratch, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
||||
(args.out / f'{name}.tap').write_text(result.stdout)
|
||||
killed = result.returncode == 1 and 'ERR_ASSERTION' in result.stdout and bool(
|
||||
re.search(r'not ok [^\n]*' + re.escape(target), result.stdout))
|
||||
print(json.dumps({'mutant': name, 'returncode': result.returncode, 'assertion_kill': killed}), flush=True)
|
||||
if not killed:
|
||||
raise RuntimeError(f'{name}: not an assertion kill; stop, diagnose and report before any fix')
|
||||
finally:
|
||||
# These tests never use compose. Remove the disposable copy after browser teardown;
|
||||
# retained TAP lives only in --out. No live network/volume/service cleanup is performed.
|
||||
shutil.rmtree(scratch)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,16 @@
|
||||
fb518c370aeb531220de123464f0a809156fc546b51d7195f4f9ca660409cf22 packages/bus/src/broker.mjs
|
||||
35d044b0904ce913b3356b02cdc5bf22babecf738b511dca80c75f704e4ef78e packages/bus/tests/user-principal.test.mjs
|
||||
e795bc4ee5aa038835edfeabe3498e53fdd700df478198cac6767498e2fac0b7 packages/cli/README.md
|
||||
96c4f93628964b6ea10fb87d5cf936f9ecf2e7286aa20c0914ae8b0fb32c47e7 packages/cli/src/cli.mjs
|
||||
0c31ac7260e77556f30f33a9932dc59a30f394982d8556650af7c521d2bcc947 packages/cli/src/session.mjs
|
||||
58888b7731f8009d4c3f8be2caf4445f13dd8f534e1633f6e2720b259fcc3f92 packages/cli/src/thread.mjs
|
||||
27fb7dd91007ec5f729fbe854219f423177cf4db2d44d1f8b04c7e8c5e6db90a packages/cli/tests/session-cli.test.mjs
|
||||
5e7e1ec169392ee3065f8f47d19732ebbe8bdd90f9193581425b956e66da6b73 packages/plugins/channel/cli/index.mjs
|
||||
aa086c365c4a6b0eaf84359a20ef2b30e723013ada6573fb0133163d5bf98a74 packages/plugins/channel/webui/index.mjs
|
||||
143bf601badf5cc2b25cb24368ace2ec34840abec858d272af239d6a4ddcdecb packages/plugins/tests/channel.test.mjs
|
||||
0720f66a57d965c9275f598b7c7ac438f238a0fac52808fc8f10312b85ba2727 packages/webui/src/chat-http.mjs
|
||||
65fd3fd2337aaa88245a9fb10b55fd872f9150085ff9a53fe522e758a93c64fa packages/webui/src/chat.mjs
|
||||
88dd353f5df0603ed1fd43dd543ef7b45af93fee72b6bb6a03a26c068202fbd3 packages/webui/src/public/chat.css
|
||||
074d570aee4e2b6cdcbd6b8be399a2a21e863f2d620f95dad8d93ffc7d9c46da packages/webui/src/public/chat.js
|
||||
2161c4830a3f73f0343e76521b948b933e401f7b9ba4ef8a5c75bf99c565d6b6 packages/webui/tests/chat-browser.test.mjs
|
||||
b79a8809951580187b581450cdd4012245f2d88a371bf7d2f123580af8815449 packages/webui/tests/chat.test.mjs
|
||||
@@ -0,0 +1,17 @@
|
||||
c21f1bd9af6841126baf19f5c9926ca23ba8e56b9af342373d8e4fefcee63632 packages/bus/src/broker.mjs
|
||||
3957243d59e50eaa2326b88b9205ed8e8b5d33bde2ce1ef62e68f20e56283568 packages/bus/tests/user-principal.test.mjs
|
||||
0a920131a981c2a1c9b73428a998966f446babae4dfe7d10eebdd6455571c0c9 packages/cli/README.md
|
||||
96c4f93628964b6ea10fb87d5cf936f9ecf2e7286aa20c0914ae8b0fb32c47e7 packages/cli/src/cli.mjs
|
||||
1a30048032f5bb64419ae9716a1692978bc1a137220a094d1900298aca6ba1d7 packages/cli/src/session.mjs
|
||||
58888b7731f8009d4c3f8be2caf4445f13dd8f534e1633f6e2720b259fcc3f92 packages/cli/src/thread.mjs
|
||||
359c9fbfe30d860046ee3d29d23681900d9c73b3a4c3a91cd2f4e1d2c6be143e packages/cli/tests/session-cli.test.mjs
|
||||
5e7e1ec169392ee3065f8f47d19732ebbe8bdd90f9193581425b956e66da6b73 packages/plugins/channel/cli/index.mjs
|
||||
aa086c365c4a6b0eaf84359a20ef2b30e723013ada6573fb0133163d5bf98a74 packages/plugins/channel/webui/index.mjs
|
||||
143bf601badf5cc2b25cb24368ace2ec34840abec858d272af239d6a4ddcdecb packages/plugins/tests/channel.test.mjs
|
||||
6e743878e507be20d87ae10afdb01c105f6f4fab76d02410e065f3f20d18e597 packages/webui/src/chat-http.mjs
|
||||
7a5a18a9879fe7132f20a651c6927b007475571bd8fc45314eda9d6809288cea packages/webui/src/chat.mjs
|
||||
88dd353f5df0603ed1fd43dd543ef7b45af93fee72b6bb6a03a26c068202fbd3 packages/webui/src/public/chat.css
|
||||
e4c25743a6d8cbe15eaac2b4d8c3bd309ffa5793f35584d862690a9d89725b72 packages/webui/src/public/chat.js
|
||||
1aa313f1602cf87a0ddac145bf58bbd7f1073c02009c65797e85418fdfdbd838 packages/webui/tests/chat-browser.test.mjs
|
||||
23de8678566f24a6c8bf0ca75b4550969015b06253586070e229f4eaf0bba2b4 packages/webui/tests/chat-sessions-access.test.mjs
|
||||
773538c0c6a26ab525aa30e588249ba4b4ee981129533db0348f2107da5dd1df packages/webui/tests/chat.test.mjs
|
||||
@@ -0,0 +1,17 @@
|
||||
c21f1bd9af6841126baf19f5c9926ca23ba8e56b9af342373d8e4fefcee63632 packages/bus/src/broker.mjs
|
||||
3957243d59e50eaa2326b88b9205ed8e8b5d33bde2ce1ef62e68f20e56283568 packages/bus/tests/user-principal.test.mjs
|
||||
0a920131a981c2a1c9b73428a998966f446babae4dfe7d10eebdd6455571c0c9 packages/cli/README.md
|
||||
96c4f93628964b6ea10fb87d5cf936f9ecf2e7286aa20c0914ae8b0fb32c47e7 packages/cli/src/cli.mjs
|
||||
1a30048032f5bb64419ae9716a1692978bc1a137220a094d1900298aca6ba1d7 packages/cli/src/session.mjs
|
||||
58888b7731f8009d4c3f8be2caf4445f13dd8f534e1633f6e2720b259fcc3f92 packages/cli/src/thread.mjs
|
||||
a32bfeecd8589d41b298317954fae172dc936ee0559ec37990e84ed03bd38b07 packages/cli/tests/session-cli.test.mjs
|
||||
5e7e1ec169392ee3065f8f47d19732ebbe8bdd90f9193581425b956e66da6b73 packages/plugins/channel/cli/index.mjs
|
||||
aa086c365c4a6b0eaf84359a20ef2b30e723013ada6573fb0133163d5bf98a74 packages/plugins/channel/webui/index.mjs
|
||||
143bf601badf5cc2b25cb24368ace2ec34840abec858d272af239d6a4ddcdecb packages/plugins/tests/channel.test.mjs
|
||||
6e743878e507be20d87ae10afdb01c105f6f4fab76d02410e065f3f20d18e597 packages/webui/src/chat-http.mjs
|
||||
7a5a18a9879fe7132f20a651c6927b007475571bd8fc45314eda9d6809288cea packages/webui/src/chat.mjs
|
||||
88dd353f5df0603ed1fd43dd543ef7b45af93fee72b6bb6a03a26c068202fbd3 packages/webui/src/public/chat.css
|
||||
68696f41a7335db23c0786537de76ff1202142518647d3d7b89b6b6503baf4f1 packages/webui/src/public/chat.js
|
||||
1aa313f1602cf87a0ddac145bf58bbd7f1073c02009c65797e85418fdfdbd838 packages/webui/tests/chat-browser.test.mjs
|
||||
23de8678566f24a6c8bf0ca75b4550969015b06253586070e229f4eaf0bba2b4 packages/webui/tests/chat-sessions-access.test.mjs
|
||||
773538c0c6a26ab525aa30e588249ba4b4ee981129533db0348f2107da5dd1df packages/webui/tests/chat.test.mjs
|
||||
Loaded 100 of 311 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user