Compare commits
67
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 |
No files matched your search
@@ -5411,3 +5411,59 @@ Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault r
|
||||
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).
|
||||
|
||||
@@ -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,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,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,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,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,92 @@
|
||||
# Row 298 (#1804, Q228): the chief-of-staff grant scope
|
||||
|
||||
Owner Megavolt, reviewer Dewey. Assigned in sage-3584 (mos-1489), 2026-10-23 18:53Z.
|
||||
Base: origin/refactor 45dca69f6. Branch: megavolt/row298.
|
||||
Brief: docs/plans/2026-10-23_mvp-readiness-rows.md, "Brief: the chief-of-staff grant scope (Q228)".
|
||||
Decision: DECISIONS Q228 (jason-master-restart DECISIONS.md:2278). It replaces Mos's Q190 reading.
|
||||
|
||||
## What exists at 45dca69f6 (broker.mjs)
|
||||
|
||||
- Raise: `#raiseGrant` (:2558) is chief-only (`grant-chief-only`) and binds a grant body per method:
|
||||
gitea `#giteaGrantBinding` (:2665), host `#hostGrantBinding` (:2599), or host-enroll `enrollBinding`.
|
||||
The decision goes through `#dispatch(..., 'decision.raise', ...)`, routed to the human.
|
||||
- Approval: `#approvedGrant` (:1525) returns the binding only for a credential.mint routed to the
|
||||
human and closed with `approvalChoice` by the business's human, via 'cli', or via 'console' with
|
||||
the broker's own human.input (row 252).
|
||||
- Start: decision.resolve (:5653-5659) runs `#approvedGrant` after the close and, once the transaction
|
||||
commits, starts `#runEnrollment`, `#runHostGrant` or `#startGitea` with setImmediate.
|
||||
- Spend and records: each executor calls `#consumeGrant` and writes the 236 records (grant.call,
|
||||
grant.result, grant.step, grant.applied or grant.failed, gitea_grants or host_grants). Revoke
|
||||
goes through grants.revoke and hosts.revoke, as before.
|
||||
- Human-only settings: `#human(s)` (cli, outsideAgent) plus `#mayHuman`, as in veto.set (:4713).
|
||||
The organization file is checked whole by business.mjs `checkLaunch`, and a bad file refuses
|
||||
every read.
|
||||
|
||||
## Proposed design (waits on Sage's rulings, Q1-Q6 below)
|
||||
|
||||
D1. Storage. The scope gets its own key, `launch.grantScope`, in the organization file. The
|
||||
whole-file check doesn't refuse on it. A separate reader, `grantScope(document)`, returns the
|
||||
parsed scope or, when the key is missing or bad, the empty scope `{}`, plus a `fallback` flag.
|
||||
A bad value can't break every organization read, and it falls back to "everything needs
|
||||
Approve", which is the fail-closed direction. (Q1)
|
||||
D2. Shape. `{ gitea?: { agents: [id], projects: [id] }, host?: { agents: [id], hosts: [id],
|
||||
commands: [[argv prefix]] } }`. A request is in scope only if its method has an entry, its agent
|
||||
is listed, and every target is listed: each gitea project, the host, and each command prefix
|
||||
equal to a listed prefix. host-enroll is never in scope, because it adds a host to the registry.
|
||||
(Q2)
|
||||
D3. Writer. A new verb, `grants.scope.set { revision, scope }`, CLI-only for the human: `#human(s)`,
|
||||
then `#mayHuman`, with the verb added to LIFECYCLE_HUMAN_VERBS. The tester, the reader and every
|
||||
agent are refused. organization.set's ORGANIZATION_KEYS don't include it, so the chief's tool
|
||||
never reaches it, the same way instances.set and veto.set stay human-only. The write validates
|
||||
strictly and refuses a bad value; only the read falls back. Receipt: `grants.scope` with the old
|
||||
and new sha256. `grants.scope` reads the value for the human, the tester and the chief. (Q3)
|
||||
D4. In-scope raise. Inside the raise transaction, `#raiseGrant` checks the binding against the scope
|
||||
and, if it's in scope, closes the new decision at once: op resolved, choice approvalChoice, by
|
||||
`grant-scope`, via `scope`. It writes a `grant.scope.used` event carrying the scope sha256 and
|
||||
revision. `#approvedGrant` accepts via 'scope' only when that event exists for the close's seq
|
||||
and its sha256 matches the scope as read in the same transaction. After the commit, the same
|
||||
setImmediate starts the same executor, so 236's spend, records and revoke are unchanged. The
|
||||
decision is closed before any inbox read, so no inbox item ever shows. (Q4)
|
||||
D5. Out-of-scope raise: unchanged. The decision stays open, routed to the human, for the Console
|
||||
and CLI Approve.
|
||||
D6. Executor recheck. At the spend, `#consumeGrant` reruns the scope check for a via-'scope'
|
||||
approval. If the scope changed between raise and run and the grant is now out of scope, it
|
||||
fails with `grant.failed scope-changed` and the grant is spent. (Q5)
|
||||
|
||||
## Open questions for Sage
|
||||
|
||||
Q1. Storage: `launch.grantScope` in the organization file with a lenient reader (D1), or a separate
|
||||
file under the business directory?
|
||||
Q2. Shape: is per-method agent, project, host and command lists right? Should host-enroll stay
|
||||
out of scope always?
|
||||
Q3. "A scope change needs Approve": is the human's CLI verb itself the Approve (D3)? Or must the
|
||||
chief also be able to raise a scope change that Jason approves in the inbox (a new decision
|
||||
action, more code)? I recommend D3 alone. Should Console get a scope page in this row? I
|
||||
propose no.
|
||||
Q4. Recording an in-scope grant as a broker close via 'scope' with a scope event (D4), versus a
|
||||
decision that is never raised (a new table). D4 keeps 236's records and revoke unchanged.
|
||||
Q5. Scope narrowed between raise and run: fail scope-changed (D6), or honor the raise-time check?
|
||||
Q6. Chief enrollment ("Jason's Approve stays for the chief of staff's enrollment"): I read this
|
||||
as the chief's own instance and credentials, which the human's instances.set and the
|
||||
credential lifecycle already cover. So nothing new is needed in this row. Agree?
|
||||
|
||||
## Tests (access by brief; all on scratch stores, fake Gitea, fake host)
|
||||
|
||||
T1 agent, chief and tester callers of grants.scope.set are refused, and organization.set with
|
||||
grantScope is refused. T2 a bad or missing grantScope falls back to empty and the grant waits.
|
||||
T3 an in-scope gitea request issues with no open decision and no inbox item, and writes the 236
|
||||
records. T4 the same for a host request. T5 out of scope by agent, by project, or by command
|
||||
waits for Approve. T6 host-enroll always waits. T7 a scope change by the human's CLI writes a
|
||||
receipt; a scope change by any other path is refused. T8 an in-scope grant is logged and revoked
|
||||
with grants.revoke or hosts.revoke. T9 scope narrowed before run gives scope-changed.
|
||||
|
||||
## Mutants (brief)
|
||||
|
||||
M1 the scope check skipped (every chief raise auto-closes): killed by T5 and T6.
|
||||
M2 the fallback skipped (a bad value is used as-is or throws): killed by T2.
|
||||
M3 the `#human` check removed from grants.scope.set: killed by T1.
|
||||
|
||||
## Holds
|
||||
|
||||
Darkwing holds the slot: no push, no claim, no queue write, no node and no test run until Sage
|
||||
says so. Q188: no agent creates a grant; tests use scratch stores only.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Poe
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Sage under Q246 and mos-1566. Owns row 208, 193d: the Console's shared
|
||||
list, share controls and viewer read-only (docs/plans/2026-10-20_human-users-build.md), reassigned from Morgana 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 `poe/*` branches through the repository credential helper (mos-0981), never `refactor`,
|
||||
`main` or another seat's branch. Claude seats orchestrate and review guards (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,237 @@
|
||||
# Row 216 build notes (C2-05a, lookup index and queries, #1717)
|
||||
|
||||
Phase A only: code, tests and this file. No test, suite or mutant has been run. `node --check` passed on every changed `.mjs`.
|
||||
|
||||
- Branch: `researcher/row216`
|
||||
- Base: `d372102882d6858fa77af4aee453f866c886ad5c` (origin/refactor)
|
||||
- Design: `agents/gosalyn/work/case2/DESIGN.md` on origin/gosalyn/case2-design, section 3 (table at lines 625-631, bullets 633-641), 1.2-1.4, B12, D2
|
||||
- Brief: `docs/plans/2026-10-20_case2-rows.md`, C2-05a at lines 320-357
|
||||
|
||||
## Files
|
||||
|
||||
| Path | Status |
|
||||
|---|---|
|
||||
| `packages/library/src/index-build.mjs` | new |
|
||||
| `packages/library/src/query.mjs` | new |
|
||||
| `packages/library/src/index.mjs` | edit: 2 export lines added |
|
||||
| `packages/library/tests/query-fixture.mjs` | new, test helper |
|
||||
| `packages/library/tests/query.test.mjs` | new |
|
||||
| `packages/library/tests/index-build.test.mjs` | new |
|
||||
| `agents/researcher/work/queue-216/BUILD.md` | new (this file) |
|
||||
|
||||
No existing test or source file changed beyond `index.mjs`.
|
||||
|
||||
## Design rows and where each is tested
|
||||
|
||||
| Design row | Code | Tests (query.test.mjs unless noted) |
|
||||
|---|---|---|
|
||||
| `library_find` | `find` | find is bound to its universe; every result carries its scope; long list pages; large items page under the cap |
|
||||
| `library_get` | `get` | hybrid gets a source file; get lists visible links both ways; get pages its lines |
|
||||
| `library_search` | `search` | search returns scoped line hits; item that cannot fit alone is refused |
|
||||
| `library_manuscript` | `manuscript` | one chapter at most 400 lines; refused without chapter; hybrid reads a source chapter |
|
||||
| `library_universe` | `universeInfo` | universe info reports a hybrid's sources, constraints, notes; unknown universe refused |
|
||||
| Hybrid semantics (1.4) | `sees` | a hybrid sees its sources canon minus exclusions, no playground |
|
||||
| Unmapped never appears | index build + `sees` | unmapped files never appear (query.test); index holds mapped files only (index-build.test) |
|
||||
| Per-commit cache (3) | `indexFor` | same commit, path, mapping return one index; different path, mapping or commit get their own; eight-entry bound; failed read not kept; unchecked mapping refused (index-build.test) |
|
||||
| Rebuild time (3, C2-05a gate) | `indexFor` | 3,000-file rebuild, time reported with `t.diagnostic`, no threshold asserted |
|
||||
| REPLY_CAP parity | `QUERY_REPLY_CAP` | equals `REPLY_CAP` from harness tools.mjs |
|
||||
|
||||
## Choices where the design table is silent or differs
|
||||
|
||||
These are departures or gaps. Each is built as stated, so Sage can rule before Phase B. A ruling that changes one of them is a small edit.
|
||||
|
||||
1. **Paging arguments (gap a).** The table has no offset. `find` and `search` take an optional `offset` (a position in the filtered list, default 0). A reply carries `next`: the position of the next item, or `null`.
|
||||
2. **`library_get` has no universe (gap b).** Built with a required `universe`. Without it the reply could not be isolated. A missing file and a file the universe cannot see both return `null`, so a caller cannot tell them apart. `get` also takes an optional `from` line.
|
||||
3. **`get` over the cap (gap c).** Lines page by `from` with `next`. `links_out` and `links_in` come back whole on every page. If the frontmatter and links alone exceed the cap, the call is refused with `query-item-too-large`. A hub note with many backlinks may be refused. This needs a ruling: link paging, or accept the refusal.
|
||||
4. **`manuscript` without a chapter (gap d).** Refused with `query-chapter-required`. The design names no list form. A chapter name that matches two visible files is refused with `query-chapter-ambiguous`.
|
||||
5. **`search` excerpt (gap e).** The excerpt is the whole line, not a truncation. A line that alone exceeds the cap is refused. Truncating it would cut an item.
|
||||
6. **`exclude` form (gap f).** `checkMapping` accepts only a folder path for `exclude` (`folder()`), so an exclusion is a path prefix. There is no type or source form to build. Confirm.
|
||||
7. **Pin constraints.** `pin` does not hide any file. It is reported by `universeInfo` only. Confirm.
|
||||
8. **Playground through `get`.** `scope` applies to lists. `get` by an explicit path returns a playground file of the caller's own universe. Confirm.
|
||||
9. **Links.** A link into a file the universe cannot see is dropped from `links_out` and `links_in`, in line with N1. Links from a visible file that point into another universe are kept in the index (the reader keeps them under R6), and the query layer drops them for universes that cannot see them.
|
||||
10. **`find` matching.** Case-insensitive substring over the front matter `name` and the file name. An empty `query` matches every file in scope.
|
||||
11. **Cache key.** `repo`, `commit` and a sha256 of the canonical mapping. Keying by commit alone would return the wrong scopes after a mapping change. The cache holds eight entries, least recently used out. Failed reads are not kept.
|
||||
12. **`universeInfo`.** Reads only the checked mapping and the served commit. It builds no index.
|
||||
13. **Reply commit.** Every reply carries the index commit, which is the served commit passed in.
|
||||
|
||||
## Provisional refusal codes (pending Sage approval)
|
||||
|
||||
The brief says new refusal codes need Sage's approval first. These are provisional and must not be treated as settled:
|
||||
|
||||
- `query-argument-invalid`
|
||||
- `query-universe-unknown`
|
||||
- `query-chapter-required`
|
||||
- `query-chapter-ambiguous`
|
||||
- `query-item-too-large`
|
||||
|
||||
Reused, existing codes: `library-mapping-invalid`, plus the reader's `library-git`, `library-commit` through `indexFor`.
|
||||
|
||||
## Grep results (grep only, not a test run)
|
||||
|
||||
- Test references to the changed paths: `packages/library/tests/*.mjs` import `../src/index.mjs` and `../src/mapping.mjs`. `packages/bus/tests/library.test.mjs` and `library-sync.test.mjs` import the library index as a namespace. Neither enumerates its exports, so the two new export lines break no assertion.
|
||||
- `packages/cli/tests/stack-copy.test.mjs:52` checks that `packages/library/src/index.mjs` exists, and it still does.
|
||||
- 64-hex pins: none in `packages/library/tests`, none on `index.mjs`, `query.mjs` or `index-build.mjs`.
|
||||
- `scripts/test-*.sh`: no reference to the new modules.
|
||||
|
||||
## Flag for Sage
|
||||
|
||||
- `packages/library/tests/helpers.mjs` `repo()` builds its fixtures under `tmpdir()`, which is `/tmp`. The launcher rules forbid that. The new tests do not use it; they use `tests/query-fixture.mjs`, which builds under `/mnt/storage/scratch/tmp`. The existing helper should be fixed in a separate row, not here.
|
||||
|
||||
## Phase B commands (not run)
|
||||
|
||||
Run only after Sage sends "slot clear". Set the scratch TMPDIR first, per the launcher rule.
|
||||
|
||||
```
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-aa3cc98787bc5152c/packages/library
|
||||
export TMPDIR=/mnt/storage/scratch/tmp
|
||||
node --test tests/query.test.mjs tests/index-build.test.mjs
|
||||
node --test tests/*.test.mjs
|
||||
```
|
||||
|
||||
Also run `packages/bus/tests/library.test.mjs` and `library-sync.test.mjs`, since they import the index.
|
||||
|
||||
## Status
|
||||
|
||||
Not yet verified. Nothing has been tested and nothing is committed. The working tree holds the Phase A changes. AGENTS.md invariant 8 allows a commit only after the applicable suites are green, so the commit waits for Phase B. The dispatch asked for a local commit in Phase A; Sage to confirm that the commit waits for Phase B.
|
||||
|
||||
## Round 2 (2026-10-22, sage-2708, sage-2716): rulings applied, rebased onto aca16404c
|
||||
|
||||
Added after the text above; nothing above is edited. "Nothing is committed", "Provisional refusal codes",
|
||||
`query-chapter-required` and the tmpdir flag above were true when written and are superseded here.
|
||||
|
||||
- Branch `researcher/row216-r2`, one local commit on origin/refactor `aca16404c`. Row 268 (C2-01c) is in
|
||||
that base. Row 253 has not landed; the work rebases onto 253's tip before any gate. Not pushed, not run.
|
||||
- Rulings: `agents/sage/work/queue-216/RULINGS.md` at da9555ca2, rulings 1-13 and the tmpdir flag.
|
||||
- Drift check, d37210288..aca16404c against my paths: the library files that changed are `mapping.mjs`,
|
||||
`scope.mjs`, `README.md` and the mapping and scope tests. None is a file I add or edit. `query.mjs` reads
|
||||
the checked mapping through them, so the whole library package runs in phase B.
|
||||
|
||||
### What changed from phase A
|
||||
|
||||
| Ruling | Change in `query.mjs` | Test |
|
||||
|---|---|---|
|
||||
| 3 `get` over the cap | `links_out` and `links_in` page together with `links_from`; every reply carries `links_next`, `next` and the front matter. A page with lines left carries at least one line. Links take at most half the cap while lines remain. `query-item-too-large` is left for front matter alone or one line alone (or one link alone) | hub note with 900 backlinks pages and adds up; front matter alone and a line alone refused; bad `links_from` |
|
||||
| 4 `manuscript` no chapter | `chapter` optional: the first visible chapter in path order. Every reply carries `next_chapter`. `query-chapter-required` removed. Ambiguity still refuses | first chapter and `next_chapter`; ambiguous name refused |
|
||||
| 5 `search` long line | hit comes back `{path, line, excerpt: null, excerpt_too_long: true, scope}`, other hits still return | marked hit and unmarked hits in one reply |
|
||||
| 6 `exclude` | path-prefix reading only. The test names `EXCLUSION` and `EXCLUDED_PATHS` in `query-fixture.mjs`, so C2-15 (row 267) swaps two constants | exclusion hides from the hybrid and not from the source |
|
||||
| 8 hybrid `get` | unchanged code; test added | hybrid gets a source's playground file as `null`; the source gets it with `scope: "playground"` |
|
||||
| PRD 1.14 (row 268) | `universeInfo` reports `kind` from the mapping (`draft` included) | four tests below |
|
||||
|
||||
PRD 1.14 tests, one each: a lookup naming a `book` leaves `book: null` files out (find and search);
|
||||
`library_manuscript` never reads a `book: null` file; a draft universe has no canon, so its lookups return
|
||||
files only with `scope: "playground"`; `library_universe` on a draft returns kind, name and prefixes.
|
||||
The levels fixture is `WITH_LEVELS` in `query.test.mjs`: harbor gets `Notes/Harbor/` as a universe-level
|
||||
playground prefix, and `idea` is a draft over `Drafts/New World/`.
|
||||
|
||||
`query-fixture.mjs` builds under `tmpdir()`, as `helpers.mjs` does (ruling "tmpdir flag"). The earlier
|
||||
flag about `helpers.mjs` building under `/tmp` is closed by that ruling: the gate sets `TMPDIR`.
|
||||
|
||||
Refusal codes now: `query-argument-invalid`, `query-universe-unknown`, `query-chapter-ambiguous`,
|
||||
`query-item-too-large`. All four approved in the rulings.
|
||||
|
||||
### Grep results (grep only, not a test run)
|
||||
|
||||
- `git grep` over `packages/*/tests/*` and `scripts/*` for `query.mjs` and `index-build`: no hit outside
|
||||
`packages/library/tests/query*.test.mjs` and `index-build.test.mjs`.
|
||||
- 64-hex pins naming a library path: none. Positive control: the same pathspec finds the sha256 lines in
|
||||
`packages/cli/tests/case1-hostcheck.test.mjs` and the `packages/library/src/index.mjs` existence check at
|
||||
`packages/cli/tests/stack-copy.test.mjs:52`.
|
||||
- Tests that read `packages/library/src` as a directory: `packages/cli/tests/stack-release-fixture.mjs:309`
|
||||
copies `packages/library/src` whole into a release fixture. It carries no file list, so two new files break
|
||||
no assertion. `stack-release.test.mjs` is not in the gate unless Sage wants it.
|
||||
- Tests that import the library index as a namespace: `packages/bus/tests/library.test.mjs` and
|
||||
`library-sync.test.mjs`. Neither enumerates exports, so the two export lines add no break. Both run
|
||||
whole in phase B.
|
||||
|
||||
### Run limits (fixed before any run)
|
||||
|
||||
Every node file runs as its own `setsid -w timeout -k 10 LIMIT node --test FILE`; nothing runs unbounded.
|
||||
|
||||
- Node test files: LIMIT 900 s. The library test files have no recorded time. 900 s is above the longest
|
||||
recorded whole-package run I know of (cli, 591 s, row 281 BUILD.md "Runs" step 3), so it holds the 3x rule
|
||||
for any single file. The first run records each file's time, and a file past 300 s gets a limit re-derived
|
||||
from it before the next run. `query.test.mjs` builds a 900-file repo and a 3,000-file index rebuild is in
|
||||
`index-build.test.mjs`; both are the files most likely to need it.
|
||||
- No shell script is in this row's gate, so no script limit applies.
|
||||
|
||||
Any timeout goes to Sage, then Mos, unplaced (mos-1275).
|
||||
|
||||
### Phase B (not run; Sage's CLEAR first, then a rebase onto 253's tip)
|
||||
|
||||
```
|
||||
export TMPDIR=/mnt/storage/scratch/tmp
|
||||
cd <worktree>/packages/library
|
||||
for f in tests/*.test.mjs; do setsid -w timeout -k 10 900 node --test "$f"; done
|
||||
cd ../bus
|
||||
for f in tests/library.test.mjs tests/library-sync.test.mjs; do setsid -w timeout -k 10 900 node --test "$f"; done
|
||||
cd ../cli
|
||||
setsid -w timeout -k 10 900 node --test tests/stack-copy.test.mjs
|
||||
```
|
||||
|
||||
Mutants I propose (my list, not read off the brief; each is meant to be killed by the named test, and none
|
||||
has been thrown; the kill claims are unverified until CLEAR):
|
||||
|
||||
- M-a: `sees()` returns true for a file of another universe: killed by "find is bound to its universe".
|
||||
- M-b: the playground filter dropped: killed by "playground appears only when asked for".
|
||||
- M-c: the hybrid reads a source's playground: killed by "a hybrid gets a source playground file as null".
|
||||
- M-d: `links_next` always null: killed by the hub paging test.
|
||||
- M-e: the book filter lets `book: null` through: killed by the PRD 1.14 book test.
|
||||
|
||||
### Status
|
||||
|
||||
Built, not run. `node --check` has not been run on this round's files (no node before the CLEAR); the
|
||||
phase A pass above covered the earlier text only. The commit is local and waits for no gate under Sage's
|
||||
sage-2716 instruction, which allows git and docs work only; push and claim wait for Sage's go.
|
||||
|
||||
## Round 3: rebase onto 45dca69f6, drift check, hash-pin grep (2026-10-23T18:53:43Z, sage-3576)
|
||||
|
||||
Appended; nothing above is edited. Git only; nothing run, nothing pushed. Branch researcher/row216-r4 holds the
|
||||
one row 216 commit on 45dca69f6 (origin/refactor, 10-23). Earlier base: 48e450ce8 (row 253).
|
||||
|
||||
- Rebase: clean. `git range-diff 48e450ce8..038089e5f 45dca69f6..researcher/row216-r4` shows the commit as `=`.
|
||||
- Landed in 48e450ce8..45dca69f6: 1175 files. Path overlap with this row's 7 paths: none. packages/library: no
|
||||
landed file. packages/harness/src/tools.mjs (imported by query-fixture.mjs or the tests): not landed.
|
||||
- Hash-pin grep (the item 216 owed): 64-hex in packages/library: none. Positive control: 364 files in
|
||||
packages/*/tests and scripts match 64-hex. Tests and scripts that name library files by path:
|
||||
bus/tests/library.test.mjs and library-sync.test.mjs and bus/src/runtime.mjs import library/src/index.mjs;
|
||||
business/src/business.mjs and its test import library/src/mapping.mjs (unchanged by this row);
|
||||
cli/tests/stack-copy.test.mjs, stack-release-fixture.mjs:310 and stack-release.test.mjs:1991 copy
|
||||
packages/library/src as a tree; scripts/stack-copy.sh:37 copies it; scripts/mosaic:41 runs survey.mjs. None pins
|
||||
a hash of index.mjs, index-build.mjs or query.mjs.
|
||||
- bus/src/runtime.mjs landed changes after 48e450ce8 and uses libraryModule by named members (ensureMirror, SLOT,
|
||||
readLibraryFile, libraryStatus, sync, LibraryError, SYNC_INTERVAL_MS) only; the landed diff touches none of them,
|
||||
and this row's two new index.mjs exports (query and index build) are not iterated by any caller.
|
||||
- Gate additions for phase B: run bus library.test.mjs and library-sync.test.mjs whole (they import index.mjs), and
|
||||
the cli stack-copy and stack-release tests that copy packages/library/src.
|
||||
|
||||
## Round 4: phase B on 4e5d28ab2, two test fixes for r5 (2026-10-23T20:20:51Z, sage-3702, mos-1528)
|
||||
|
||||
Appended; nothing above is edited.
|
||||
|
||||
Phase B receipts on 4e5d28ab2 (researcher-r216-kit/out-b1): library whole 114 pass, 2 fail; bus library.test and
|
||||
library-sync.test 12 pass; cli 266 pass. The 3,000-file rebuild took 3807 ms (the test, with fixture setup, 4070 ms;
|
||||
`t.diagnostic` in index-build.test.mjs). No row 216 mutant was required or thrown in that round.
|
||||
|
||||
The 2 library reds are test faults, not product faults. Both are placed as first-run reds in files this row adds
|
||||
(mos-1272); Mos ruled both fixes in mos-1528 and Sage relayed it in sage-3712.
|
||||
|
||||
- R4: `packages/library/tests/index-build.test.mjs`, test "a different repo path, mapping or commit gets its own
|
||||
index". Old: `writeFileSync(join(repo, 'Book One/Places/New.md'), …)` with no `Book One/Places` folder in FILES, so
|
||||
the write failed with ENOENT. New: one `mkdirSync(join(repo, 'Book One/Places'), { recursive: true })` before the
|
||||
write (import line gains `mkdirSync`). No assertion changes.
|
||||
- R5: `packages/library/tests/query.test.mjs`, test "a hybrid sees its sources canon, minus exclusions, and no
|
||||
playground". The assertion sorts the actual paths and compared them to an unsorted literal, so the literal had
|
||||
the wrong order (Characters before Chapters; Second World Characters before Chapters). New: only the expected
|
||||
literal is reordered into sorted order. The `.sort()` on the actual side stays, and the assertion still checks
|
||||
order. The set of seven paths is unchanged.
|
||||
- Product code: no edit.
|
||||
|
||||
Reruns owed after the r5 commit (run on Sage's CLEAR, after the 283 gate): index-build.test.mjs whole,
|
||||
query.test.mjs whole, library whole, and the mutants those two files kill (M-a to M-e above). Results are appended
|
||||
below as a new entry.
|
||||
|
||||
Run limit note: stack-release.test.mjs ran 644 s in the cli run. A per-file limit of 3 x 644 = 1932 s covers it.
|
||||
Sage's gates run cli as one whole-package `node --test` under 2100 s.
|
||||
|
||||
Carry notes from Filbert's static check (doc only, no change in r5): N1 and N2 stay recorded as he gave them. The
|
||||
`After` list in this file still names row 268; row 268 landed at c27207f6e.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Row 220: C2-10a, no raw session view (M7) and no raw Discord engine (M8)
|
||||
|
||||
Phase A. No test, suite or mutant has been run. Sage opens the slot.
|
||||
|
||||
- Owner: researcher seat (Haiku 5.5 subagent), dispatched by Sage under lead decision 129.
|
||||
- Reviewer: Filbert.
|
||||
- Branch: `researcher/row220`, from `origin/refactor` at d37210288.
|
||||
- Brief: `docs/plans/2026-10-20_case2-rows.md`, C2-10a (line 495) and "Rules every row keeps".
|
||||
- Design: `agents/gosalyn/work/case2/DESIGN.md` on `origin/gosalyn/case2-design`, sections 5.1 (M7, M8), 7.6, 8 (row list).
|
||||
|
||||
## Status
|
||||
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| M7 reader side (`conversation-not-listed`) | Built. Tests written, not run. |
|
||||
| M7 production wiring (the caller that passes `isCreative`) | Not built. Outside the file list, and it needs a ruling (question 1). |
|
||||
| M8 `discord check` and `discord run` refusals | Not built. `packages/discord/src/cli.mjs` is untouched. Blocked on question 1 and question 2. |
|
||||
|
||||
Phase A is therefore partial. The row cannot pass its acceptance until Sage rules on the questions below.
|
||||
|
||||
## Files
|
||||
|
||||
- `packages/conversation/src/reader.mjs`: exports `NOT_LISTED`; `createReader` takes `isCreative` (default `() => false`); `catalogue()`, `open()` and `next()` hide a root for which it returns truthy.
|
||||
- `packages/conversation/tests/reader.test.mjs`: three new tests, inserted before the "unknown conversations" test.
|
||||
- `agents/researcher/work/queue-220/BUILD.md`: this file.
|
||||
|
||||
Nothing else is changed. `node_modules` in the worktree is a symlink to the canonical checkout and is ignored by git.
|
||||
|
||||
## Brief and design items mapped to tests
|
||||
|
||||
| Item | Test |
|
||||
|---|---|
|
||||
| M7: a creative session file under the root isn't listed | "M7: a creative seat's session is not listed and won't open by id; an assistant seat still lists" |
|
||||
| M7: and won't open by id (`conversation-not-listed`) | same test; second check of the open refusal in "M7: a creative session already open refuses its next page once the root is hidden" |
|
||||
| M7: an assistant session still lists | same test (the `sage` seat is listed and opens with its text) |
|
||||
| M7: an already-issued cursor stops once the root is hidden (my addition; the design's "won't open" implies it) | "M7: a creative session already open refuses its next page once the root is hidden" |
|
||||
| M7: refused rows and the unsupported placeholder of a creative root are hidden too (my addition; the design hides "creative sessions", and a refused row or placeholder names one) | "M7: a creative root's refused files and its unsupported placeholder are hidden as well" |
|
||||
| Default behavior without `isCreative` is unchanged | the existing reader suite; the default predicate returns false |
|
||||
| M8: `discord check` / `discord run` refuse `discord-engine-not-allowed` on a library business, and a `mosaic-bus` binding with `tools` | none (not built, question 1) |
|
||||
| M8: a pi binding on a business without a library still checks clean | none (not built, question 1) |
|
||||
|
||||
Every test's fixture text is `CREATIVE-MARKER` or a made-up assistant line. The M7 tests assert the marker is absent from the catalogue and refusal output.
|
||||
|
||||
## Choices made in phase A
|
||||
|
||||
- `isCreative(root)` is the reader's only input. The reader reads no business file. A truthy answer hides, so a doubtful answer fails closed. The predicate must depend on `root.seat` and `root.project` only, because a cursor is checked against the root object from an earlier listing.
|
||||
- Hidden check placed after the cursor's actor/purpose/conversation/branch checks in `next()`, and before the source is read.
|
||||
- `open()` checks hidden after the id resolves, so a creative id gets `conversation-not-listed`, and an unknown id still gets `unknown-conversation`.
|
||||
- `conversation-not-listed` is `reconcile: false`. The design names no reconcile flag. `unknown-conversation` uses true. Question 3.
|
||||
- The default predicate is `() => false`. Until a caller passes one, M7 does nothing. This is the open risk, and question 1 covers it.
|
||||
|
||||
## Grep over `packages/*/tests` and `scripts/test-*.sh` (static, phase A)
|
||||
|
||||
Changed path `packages/conversation/src/reader.mjs`:
|
||||
- `packages/conversation/tests/reader.test.mjs:14` imports `rootsFromSpecs, createReader, conversationId, cwdInProject, UNSUPPORTED_HARNESS`. It is the file under change.
|
||||
- `packages/control-board/tests/serve.test.mjs:26` imports `UNSUPPORTED_HARNESS`. That export is unchanged.
|
||||
- `packages/library/tests/sync.test.mjs:15` imports `packages/library/src/reader.mjs`, a different file. Not a hit.
|
||||
- `scripts/test-*.sh`: no reference to the conversation reader.
|
||||
- 64-hex pins naming this path: none. `packages/bus/tests/grant-migration.test.mjs:24` has a 64-hex value, but it is a DDL digest, not a pin on this path. That file matched only the word "reader" in a test title.
|
||||
|
||||
Changed path `packages/conversation/tests/reader.test.mjs`:
|
||||
- No other test or script names this file. It runs through `packages/conversation`'s `node --test tests/`.
|
||||
- No 64-hex value in the file.
|
||||
|
||||
Changed path `agents/researcher/work/queue-220/BUILD.md`: no pin.
|
||||
|
||||
`node --check` passed on both changed `.mjs` files.
|
||||
|
||||
## Phase B plan (not run)
|
||||
|
||||
The reader test uses `os.tmpdir()`. Set `TMPDIR` to a scratch dir first, per the rule against `/tmp`.
|
||||
|
||||
```
|
||||
export TMPDIR="$(mktemp -d /mnt/storage/scratch/tmp/r220XXXXXX)"
|
||||
cd packages/conversation && node --test tests/reader.test.mjs
|
||||
cd packages/conversation && node --test tests/
|
||||
cd packages/discord && node --test tests/
|
||||
cd packages/control-board && node --test tests/serve.test.mjs
|
||||
```
|
||||
|
||||
The first command is the touched file whole. The second and third are the conversation and discord suites whole, as the brief asks. The control-board serve test is not in the brief. It consumes `reader.mjs`, so I recommend it. Sage can drop it.
|
||||
|
||||
## Questions for Sage
|
||||
|
||||
1. **Business link (blocks M8 and the M7 wiring).** Neither rule can be built from this row's file list, because the inputs don't exist in the code:
|
||||
- A reader root carries `seat` (the spec's `agent`) and `project` (the repo directory name). It has no business id. Roots come from control-board scan specs (`packages/control-board/src/scan.mjs`, `discoverRepoAgents`) and seat registrations. Neither carries a definition.
|
||||
- A Discord binding has `seat` and no business key (`packages/discord/src/binding.mjs`, `TOP_KEYS`).
|
||||
- Creative status lives only in the business file: `roles.<instance>.definition` in `CREATIVE_DEFINITIONS` (`packages/business/src/vocabulary.mjs:176`). A library project is `projects.<id>.kind === "library"` (`packages/business/src/business.mjs:243`).
|
||||
- Creative thread sessions are stored at `<dataRoot>/threads/<business>/<thread>/session` (`packages/seat/src/session.mjs:59-64`), outside the `.pi/state` roots the board scans. M7 may therefore reach only creative seats that have a `.pi/state` session root. Please confirm.
|
||||
- The design does not define the mapping from a seat to a business.
|
||||
|
||||
Recommendation: add a required `business` key to the binding, validated against `businesses/<id>.json`. The board builds `isCreative` from `loadBusiness(id)`: a root is creative when its seat is a `roles` key whose definition is in `CREATIVE_DEFINITIONS`, and the business has a library project. This needs file scope for `packages/discord/src/binding.mjs` and `cli.mjs` (M8), and `packages/control-board/src/serve.mjs` (M7 wiring). None of those is on this row's list. I need that scope amendment and the mapping ruling before I build M8 or the wiring.
|
||||
|
||||
2. **M8 refusal code for a `mosaic-bus` binding with `tools`.** The design names `discord-engine-not-allowed` for the engine case and no code for the tools case. A new code goes to you first. My proposal is to reuse `discord-engine-not-allowed` with a message that names the tools key.
|
||||
|
||||
3. **`reconcile` on `conversation-not-listed`.** I chose `false`. Confirm or change.
|
||||
|
||||
4. **HTTP status for `conversation-not-listed`.** `REFUSAL_STATUS` in `packages/control-board/src/serve.mjs` has no entry, so the route returns 422. My proposal is 404, next to `unknown-conversation`. This needs that file on the scope list too.
|
||||
|
||||
Phase A stops here. On your "slot clear" I will run the phase B commands above. I will not build M8 or the wiring until question 1 is answered.
|
||||
@@ -0,0 +1,481 @@
|
||||
# Row 220 r2: phase A round 2 (M7, M8, business helper, REFUSAL_STATUS)
|
||||
|
||||
Phase A only. No node, npm, `node --check` or test run has happened in this round, and nothing is
|
||||
committed. Phase B (the suites) waits for the coordinator's "slot clear".
|
||||
|
||||
- Owner: researcher seat (Haiku 5.5 subagent), dispatched by Sage under lead decision 129.
|
||||
- Reviewer: Filbert.
|
||||
- Branch: `researcher/row220-r2`, from `origin/refactor` at da9555ca2. The r1 cherry-pick 3fa833715 is
|
||||
staged with `-n`.
|
||||
- Rulings: `agents/sage/work/queue-220/RULINGS.md` on origin/refactor (da9555ca2). Import-by-path is
|
||||
followed: no export line in `packages/business/src/index.mjs`, and `packages/discord/src/binding.mjs`
|
||||
is untouched.
|
||||
|
||||
## Coordinator rulings, applied
|
||||
|
||||
Round 1 answers (earlier packet): applied as before. Absent `businesses/` gives empty sets only on
|
||||
ENOENT; other failures throw. stack-copy test joins phase B. Webui fixtures pass `businessDir` (renamed `configDirectory` in round 2b below). Discord
|
||||
refusals exit 2.
|
||||
|
||||
Round 2 rulings (coordinator message, this round):
|
||||
|
||||
1. **Dangling symlink at `businesses/` refuses.** `loadSeatSets` calls `lstatSync(join(dir, "businesses"))`
|
||||
first. Only an lstat ENOENT (nothing at the path) returns empty sets. If something is there and
|
||||
`readdirSync` fails for any reason, including ENOENT through a dangling link, it throws `BusinessError`.
|
||||
Test added: "a businesses symlink to a missing target throws; no empty set". The absent-directory test is kept.
|
||||
2. **Sets are read once per public reader call.** `createReader` takes `creativeCheck`, a factory. Each
|
||||
`catalogue()`, `open()` and `next()` calls it once, then applies the returned predicate to every root.
|
||||
A load failure refuses `conversation-business-unreadable` for the whole call. `serve.mjs` exports
|
||||
`creativeCheck(dir = configDir())`, the factory, and `createServer` passes `creativeCheck(businessDir)`.
|
||||
Test added: "one catalogue calls the creative check once, however many roots it lists". It counts factory
|
||||
calls: 1 after `catalogue()`, 2 after one `open()`.
|
||||
3. **Phase B adds three test files:** `packages/webui/tests/tester-browser.test.mjs`,
|
||||
`tester-cli.test.mjs` and `proof-cli.test.mjs`, run whole (they reach `fixture.mjs` through
|
||||
`tester-fixture.mjs`). Phase B is 36 files: 33 plus these 3.
|
||||
4. **Scope additions approved** (coordinator round 2 ruling 4): `packages/control-board/tests/discord.test.mjs`
|
||||
line 123 and the five webui test files (`fixture.mjs`, `history-fixture.mjs`, `relaunch.test.mjs`,
|
||||
`return-flow.test.mjs`, `discord.test.mjs`). They stop tests reading the real config directory.
|
||||
5. **TMPDIR** for phase B is set by the coordinator under `/mnt/storage/scratch/tmp`. No code change.
|
||||
|
||||
## Discord exit code
|
||||
|
||||
Refusals from the library-seat check exit 2, the code the Discord CLI already uses for configuration and
|
||||
binding refusals.
|
||||
|
||||
- `packages/discord/src/errors.mjs` line 2: `// exitCode follows docs/TOOLS.md: 1 operation failed, 2 invalid data or configuration, 3 refused by a brake (STOP or a held binding; a supervisor must not retry), 4 usage.`
|
||||
- `packages/discord/src/errors.mjs` line 5: `constructor(message, exitCode = 2, details = undefined)`.
|
||||
- Config refusal precedent: `packages/discord/src/binding.mjs` line 67, `throw new DiscordError(\`config.dataRoot must be an absolute path: ${path}\`);`. No explicit code, so exit 2.
|
||||
- `checkLibraryEngine` in `packages/discord/src/cli.mjs` lines 115 to 122. Each refusal passes `, 2)`:
|
||||
`discord-business-unreadable` (115), `discord-engine-not-allowed` for a non-mosaic-bus engine (119),
|
||||
and `discord-engine-not-allowed` for a mosaic-bus binding that declares `tools` (122).
|
||||
- `grep -n ", 3)" packages/discord/src/cli.mjs` finds nothing. `grep -rn "status, 3" packages/discord/tests`
|
||||
finds only existing brake tests in recover.test.mjs and binding.test.mjs. Not changed.
|
||||
`library-seat.test.mjs` asserts 2 on all three refusals.
|
||||
|
||||
## Files (round 2, uncommitted)
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `packages/business/src/library-seats.mjs` | new: `loadSeatSets({ dir, rolesDir })` returns `{ librarySeats, creativeSeats }`; lstat-first (ruling 1); read per call |
|
||||
| `packages/business/tests/library-seats.test.mjs` | new: 7 tests (6 from r1 plus the dangling symlink) |
|
||||
| `packages/conversation/src/reader.mjs` | r1 plus r2: `NOT_LISTED`; `creativeCheck` factory option, called once per `catalogue()`, `open()` and `next()`; `catalogue()` wrapped so a refusal returns `{ ok: false }` |
|
||||
| `packages/conversation/tests/reader.test.mjs` | r1: 3 M7 tests, now on `creativeCheck`; r2: 1 spy test (one factory call per catalogue) |
|
||||
| `packages/control-board/src/serve.mjs` | `REFUSAL_STATUS`: `conversation-not-listed` 404, `conversation-business-unreadable` 503; `export creativeCheck(dir = configDir())` (factory); `createServer({ businessDir = configDir() })` wires `creativeCheck: creativeCheck(businessDir)` |
|
||||
| `packages/control-board/tests/creative-hidden.test.mjs` | new: 4 tests (3 from r1 plus known-and-unknown id both 503), switched to `creativeCheck` |
|
||||
| `packages/control-board/tests/serve.test.mjs` | pin test adds `NOT_LISTED` and `conversation-business-unreadable`; conversation fixture passes `businessDir` |
|
||||
| `packages/control-board/tests/discord.test.mjs` | line 123 passes `businessDir` (ruling 4) |
|
||||
| `packages/discord/src/cli.mjs` | `checkLibraryEngine` called from `prepare()`; exit 2 |
|
||||
| `packages/discord/tests/helpers.mjs` | `makeDeployment` creates an empty `businesses/` beside `config.json` |
|
||||
| `packages/discord/tests/library-seat.test.mjs` | new: 5 tests, all `--offline`, all asserting exit 2 |
|
||||
| `packages/webui/tests/fixture.mjs` | `businessDir` on the board start (ruling 4) |
|
||||
| `packages/webui/tests/history-fixture.mjs` | `businessDir` on the board start (ruling 4) |
|
||||
| `packages/webui/tests/relaunch.test.mjs` | `businessDir` on the board start (ruling 4) |
|
||||
| `packages/webui/tests/return-flow.test.mjs` | `businessDir` on the board start (ruling 4) |
|
||||
| `packages/webui/tests/discord.test.mjs` | `businessDir` on the board start (ruling 4) |
|
||||
|
||||
Not touched: `packages/business/src/index.mjs`, `packages/discord/src/binding.mjs`, `scripts/stack-copy.sh`.
|
||||
|
||||
## Brief item to test map
|
||||
|
||||
| Brief item | Test |
|
||||
|---|---|
|
||||
| Q1 helper: library and creative sets from business files | `business/tests/library-seats.test.mjs` "library seats come from library businesses..." |
|
||||
| Q1 a non-library business puts no seat in either set | `library-seats.test.mjs` "a business with no library project..." |
|
||||
| Q1 absent businesses directory gives empty sets | `library-seats.test.mjs` "an absent businesses directory gives empty sets" |
|
||||
| Q1 any other failure throws, no empty set | `library-seats.test.mjs` "a businesses path that is not a directory throws"; "a businesses symlink to a missing target throws"; "a business file that can't be read throws"; "a business file that is not valid JSON throws" |
|
||||
| Round 2 ruling 2: one read per public call | `conversation/tests/reader.test.mjs` "one catalogue calls the creative check once..." |
|
||||
| M7 hidden from catalogue, open and next | `control-board/tests/creative-hidden.test.mjs` "the check hides the creative seat..."; r1 reader tests (catalogue, open, next, symlink and placeholder) |
|
||||
| M7 second wall: thread store | `creative-hidden.test.mjs` "a thread store is not a root from a spec..." (asserts `rootsFromSpecs` drops it, then a root over it is hidden with 404 `conversation-not-listed`) |
|
||||
| M7 unreadable business refuses listing and open | `creative-hidden.test.mjs` "an unreadable business file refuses the listing and open with 503" |
|
||||
| Coordinator follow-up: known and unknown id both 503 when unreadable | `creative-hidden.test.mjs` "with an unreadable business file, a known and an unknown id both refuse with 503" |
|
||||
| M8 non-mosaic-bus engine on a library seat | `discord/tests/library-seat.test.mjs` "a library seat's binding on a non-mosaic-bus engine is refused" (exit 2, message names the engine) |
|
||||
| M8 mosaic-bus binding that declares tools | `library-seat.test.mjs` "...declares tools is refused, and the message names tools" (exit 2) |
|
||||
| M8 mosaic-bus, no tools, allowed | `library-seat.test.mjs` "...with no tools passes the engine rule" |
|
||||
| M8 unreadable business file | `library-seat.test.mjs` "an unreadable business file refuses check with discord-business-unreadable" (exit 2) |
|
||||
| M8 seat in no library business checks clean | `library-seat.test.mjs` "a binding on a seat that is in no library business checks clean" |
|
||||
| Q3 reconcile false | r1 reader test asserts `[false, "conversation-not-listed", false]` |
|
||||
| Q4 HTTP 404 and 503 | `creative-hidden.test.mjs` (status asserted); `serve.test.mjs` pin test |
|
||||
|
||||
## Choices (for Filbert to check)
|
||||
|
||||
1. **Absent versus unreadable `businesses/`.** Only nothing at the path reads as empty (lstat ENOENT). A
|
||||
dangling symlink, a file, or an unreadable directory throws. Coordinator ruling 1.
|
||||
2. **Sets are read once per public call.** `creativeCheck()` runs once in each of `catalogue()`, `open()` and
|
||||
`next()`. The returned predicate answers every root in that call. A catalogue with zero roots still
|
||||
loads the sets once, so an unreadable business file refuses even an empty list. Coordinator ruling 2.
|
||||
3. **Behavior change, kept on coordinator ruling (round 2 follow-up): an unknown id or cursor loads the sets
|
||||
first.** In `open()`, the check runs after `checkCaller` and before `resolveConversation`. In `next()`, it
|
||||
runs at the start of the try. So with an unreadable business file, a known id and an unknown id both give
|
||||
503 `conversation-business-unreadable`, where round 1 gave 404 `unknown-conversation` for the unknown id.
|
||||
Checking the id first would give 503 for a hidden creative id and 404 for an unknown one, which reveals
|
||||
whether the hidden id exists. Loading first answers every id the same way (fail closed). This reverses
|
||||
round 1 choice 3. Test: `creative-hidden.test.mjs` "with an unreadable business file, a known and an unknown id both refuse with 503".
|
||||
4. **Discord refusals use the `code: message` form and exit 2.** Exit 3 is for brake refusals (STOP or a held
|
||||
binding). A seat's engine or library membership is not a brake. The business CLI is not used as precedent.
|
||||
5. **The thread-store wall exists in part already.** `rootsFromSpecs` (`reader.mjs` line 44) drops any
|
||||
sessions directory that is not `<project>/.pi/state/<seat>/sessions`. The M7 check is the second wall, for
|
||||
roots built another way. The test checks both.
|
||||
|
||||
## Static checks (read-only; no node)
|
||||
|
||||
- **COPY_PATHS** (`scripts/stack-copy.sh` 29 to 41): `packages/business/src`, `packages/conversation/src`,
|
||||
`packages/discord/src` and `packages/library/src` are present. `packages/control-board/src` is not. COPY_PATHS
|
||||
is not changed.
|
||||
- **Stale names.** `grep -rn -E "isCreative|creativeRootPredicate" packages scripts` finds nothing after this round's edits.
|
||||
- **Board starts without `businessDir`.** Webui: all five direct starts pass it. Control board:
|
||||
- `serve.test.mjs` has seven single-line starts without it (lines 402, 445, 464, 483, 509, 528, 907) and four
|
||||
multi-line starts without it (114, 149, 229, 258). None of those tests reaches a conversation route.
|
||||
Every conversation route call in the file (lines 1027 to 1171) follows a start that passes `businessDir`
|
||||
(1006, 1075, 1124). Checked by line order.
|
||||
- `creative-hidden.test.mjs` builds `creativeCheck(f.dir)` on its own scratch directory. No default is used.
|
||||
- `discord.test.mjs` passes `businessDir` (line 123).
|
||||
- **Consumers of the two fixtures** (exact import strings, `from './fixture.mjs'`, `from './history-fixture.mjs'`):
|
||||
- Direct `fixture.mjs` importers, 31 test files: board-held, browser-edge, browser, bus-browser, bus,
|
||||
chat-browser, chat, discord, docs-browser, editor-browser, editor, instances-browser, limits-browser,
|
||||
limits-route, messages, preview, proof, relaunch, return-flow, routes-browser, send-browser, send, serve,
|
||||
settings-form, shell, sso-browser, sso, tester-listener, usage-browser, usage, views.
|
||||
- `history-fixture.mjs` importers, 2 test files: conversation, history-return-flow.
|
||||
- Total fixture consumers: 33. Plus `tester-browser`, `tester-cli` and `proof-cli` (through
|
||||
`tester-fixture.mjs`, ruling 3): 36 files in phase B.
|
||||
|
||||
## Phase B commands (after "slot clear" only)
|
||||
|
||||
TMPDIR is set by the coordinator under `/mnt/storage/scratch/tmp`. The commands below assume it. The new
|
||||
tests call `tmpdir()`, so an unset TMPDIR would put them under `/tmp`, which bwrap hides.
|
||||
|
||||
```sh
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/conversation && node --test tests/
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/discord && node --test tests/
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/business && node --test tests/
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/control-board && node --test tests/
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e && scripts/test-discord.sh
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/cli && node --test tests/stack-copy.test.mjs
|
||||
cd /mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1b36cb7769b3d00e/packages/webui && node --test tests/board-held.test.mjs tests/browser-edge.test.mjs tests/browser.test.mjs tests/bus-browser.test.mjs tests/bus.test.mjs tests/chat-browser.test.mjs tests/chat.test.mjs tests/conversation.test.mjs tests/discord.test.mjs tests/docs-browser.test.mjs tests/editor-browser.test.mjs tests/editor.test.mjs tests/history-return-flow.test.mjs tests/instances-browser.test.mjs tests/limits-browser.test.mjs tests/limits-route.test.mjs tests/messages.test.mjs tests/preview.test.mjs tests/proof.test.mjs tests/relaunch.test.mjs tests/return-flow.test.mjs tests/routes-browser.test.mjs tests/send-browser.test.mjs tests/send.test.mjs tests/serve.test.mjs tests/settings-form.test.mjs tests/shell.test.mjs tests/sso-browser.test.mjs tests/sso.test.mjs tests/tester-listener.test.mjs tests/usage-browser.test.mjs tests/usage.test.mjs tests/views.test.mjs tests/tester-browser.test.mjs tests/tester-cli.test.mjs tests/proof-cli.test.mjs
|
||||
```
|
||||
|
||||
The webui line is 36 files, run whole. `history-fixture.mjs` and `tester-fixture.mjs` run through their
|
||||
consumers. No test has been run. The expectations above come from reading the code.
|
||||
|
||||
## Open items
|
||||
|
||||
a. Phase B checks the dangling-symlink test on this platform (lstat then readdir ENOENT must throw).
|
||||
b. Phase B checks the spy count (1 after catalogue, 2 after one open). A failure here means a public call
|
||||
reads the factory more than once.
|
||||
c. Choice 3 is settled by the coordinator: keep 503 for unknown ids too. Phase B checks the new known-and-unknown test.
|
||||
d. The browser-based webui tests need a browser in this environment. A failure there would be environmental.
|
||||
Phase B will show which.
|
||||
e. Nothing here has been executed. Phase B checks every expectation, including the unreadable-file test
|
||||
(EACCES must throw), the root skip (uid 0 skips), and the webui consumers.
|
||||
|
||||
## Resolved
|
||||
|
||||
- Dangling symlink at `businesses/` (round 1 open item a, and round 1 question 1): refuses, ruling 1.
|
||||
- Tester and proof CLI tests (round 1 question 2): join phase B, ruling 3.
|
||||
- Per-root reads (round 1 open item b): replaced by one read per public call, ruling 2.
|
||||
|
||||
## Rebase onto aca16404c, run limits, index export (2026-10-22, sage-2708)
|
||||
|
||||
Added after the text above; nothing above is edited. "Nothing is committed" above was true when written.
|
||||
|
||||
- Commit `6f656abac` on `researcher/row220-r2`, one commit, rebased from da9555ca2 onto origin/refactor
|
||||
`aca16404c`. Row 253 has not landed; this rebases again onto 253's tip before any gate.
|
||||
- Drift check, da9555ca2..aca16404c against my 18 paths: one overlap, `packages/discord/src/cli.mjs`
|
||||
(the processing-control imports, rows 264 and neighbours). The conflict was the import block only. I
|
||||
kept both sides: the two landed `processing-control` imports and my `loadSeatSets` import. No landed hunk
|
||||
is reverted. `checkLibraryEngine` and its call in `prepare()` merged without a marker.
|
||||
- Board starts: no test added since da9555ca2 starts the control board or reads a conversation route
|
||||
without `businessDir`. `packages/webui/tests/shell.test.mjs` fakes the board and does not start it.
|
||||
- Two edits since the r2 text: (1) a missing space in `reader.mjs` `next()` (`held =typeof`) fixed;
|
||||
(2) `packages/business/src/index.mjs` gets `export { loadSeatSets } from "./library-seats.mjs";`. The text
|
||||
above says "no export line". RULINGS.md Q1 says the helper is "exported from the package index" and the
|
||||
scope amendment lists "its export line", so the file wins and the export is added. The two callers still
|
||||
import by path, so nothing needs the export today. Filbert: drop the line if you read the ruling otherwise.
|
||||
The export adds no test; `node --check` has not been run on it (no node before the CLEAR).
|
||||
|
||||
### Run limits (fixed before any run)
|
||||
|
||||
Every node file runs as its own `setsid -w timeout -k 10 LIMIT node --test FILE`; nothing runs unbounded.
|
||||
|
||||
- Node test files: LIMIT 900 s. I found no recorded per-file time for the conversation, discord, business,
|
||||
control-board or webui files. 900 s is above the longest recorded whole-package run I know of (cli, 591 s,
|
||||
row 281 BUILD.md "Runs" step 3), so it holds the 3x rule for any single file. The first run records each
|
||||
file's time; if a file passes 300 s, the limit is re-derived from it before the next run.
|
||||
- Shell scripts: `timeout -k 10 LIMIT bash scripts/test-X.sh`, LIMIT = 3x the longest recorded run plus
|
||||
120 s, taken from Megavolt's row 281 table (`agents/megavolt/work/queue-281/BUILD.md`, from Gander's
|
||||
`agents/gander/work/queue-278/SHELL-LIMITS.json`). A script with no limit does not run.
|
||||
|
||||
| Script | LIMIT | Script | LIMIT |
|
||||
|---|---|---|---|
|
||||
| test-auth | 126 s | test-land-work | 158 s |
|
||||
| test-conductor | 145 s | test-queue | 191 s |
|
||||
| test-config | 130 s | test-release | 157 s |
|
||||
| test-discord | 147 s | test-task | 231 s |
|
||||
| test-extension-package | 130 s | test-foundation | 240 s |
|
||||
|
||||
Any timeout goes to Sage, then Mos, unplaced (mos-1275).
|
||||
|
||||
## Round 2b: Filbert's early look and sage-2719 (2026-10-22T14:46Z)
|
||||
|
||||
Added after the text above; nothing above is edited. Where earlier text says `businessDir`, the parameter is
|
||||
now `configDirectory` (below). Second commit on `researcher/row220-r2`, on top of ea6dc9eff. Not pushed, not
|
||||
run; `node --check` has not been run on any of these files (no node before the CLEAR).
|
||||
|
||||
Rulings applied (sage-2719): Q1 keep the index export. C1, C2 and the fixture scope are changes.
|
||||
|
||||
### Fixture scope: the parameter is a config directory
|
||||
|
||||
`createServer` took `businessDir`, but `creativeCheck(dir)` reads `<dir>/businesses`, so every fixture that
|
||||
passed `join(root, 'businesses')` made the reader look in `<root>/businesses/businesses`. That read nothing
|
||||
(ENOENT, empty sets): isolation from the real config held by accident. Now:
|
||||
|
||||
- `createServer({ configDirectory = configDir() })` in `serve.mjs`. The parameter is not named `configDir`
|
||||
because the import of that name is the default's source.
|
||||
- Every fixture passes the config directory it owns: `configDirectory: root` in the five webui files,
|
||||
`f.root` in `control-board/tests/discord.test.mjs`, and `f.configDirectory` in `serve.test.mjs`.
|
||||
`conversationFixture()` and `replyFixture()` both return `configDirectory: root`. The seven reply starts
|
||||
that passed `f.businessDir` (undefined, falling back to the real `configDir()`) now pass a real value.
|
||||
- The dead `mkdirSync(join(root, 'businesses'))` lines in the five webui fixtures and in `serve.test.mjs`
|
||||
are removed. So is the one in `discord/tests/helpers.mjs` `makeDeployment`, whose comment said "a missing
|
||||
one refuses"; that was false (lstat ENOENT gives empty sets). The comment now says nothing there means no
|
||||
library seat. Tests that need a `businesses/` directory create it: `writeJson` does, and the two
|
||||
unreadable-file tests in `library-seat.test.mjs` call `mkdirSync` themselves.
|
||||
- Proof that the fixture's own business file is the one read: the new default-reader test (C2) hides `bean`
|
||||
and shows `pm`, and nothing else in that fixture says which is which.
|
||||
|
||||
### C1: M8 on `run`
|
||||
|
||||
`library-seat.test.mjs` gains three `run` tests: a raw engine, a mosaic-bus binding with `tools`, and an
|
||||
unreadable business file. Each asserts exit 2 and the code in stderr, and that `s.d.journalDir` does not
|
||||
exist (`ensureJournal` comes after `prepare()` in `run`, so nothing was started). `run()` calls
|
||||
`initializeControl(dataRoot)` first, because `run` stops at `assertProcessing` before `prepare()`, as the
|
||||
STOP test in `binding.test.mjs:203` does. There is no `run` test for a clean seat: that would go on to the
|
||||
network.
|
||||
|
||||
### C2: the default reader
|
||||
|
||||
`creative-hidden.test.mjs`, "the board's default reader hides a creative seat's session from the listing and
|
||||
the detail route". It calls `startServer` with no `conversationReader`, `configDirectory: f.dir`, a `bean`
|
||||
session and a `pm` session on disk, and drives HTTP: the listing holds `pm` only; bean's detail is 404
|
||||
`conversation-not-listed`; pm's detail is 200; after a broken business file lands the listing is 503
|
||||
`conversation-business-unreadable`.
|
||||
|
||||
### Mutants to throw (CLEAR only; none thrown; the kill claims are unverified)
|
||||
|
||||
| Id | Mutation | Meant to be killed by |
|
||||
|---|---|---|
|
||||
| M7-w | delete `creativeCheck: creativeCheck(configDirectory),` in `createServer` (`serve.mjs`) | the default-reader test, first assertion (the listing shows `bean`) |
|
||||
| M7-c | `hiddenCheck()` in `reader.mjs` open() returns `() => false` | reader.test "hides the creative seat" and the `creative-hidden` 404 asserts |
|
||||
| M7-o | `creativeCheck` catch returns an empty set instead of throwing | the 503 asserts in `creative-hidden` |
|
||||
| M8-p | delete `checkLibraryEngine(...)` in `prepare()` | the three `check` refusals and the three `run` refusals; one mutant covers both verbs because they share `prepare()` |
|
||||
| M8-e | `loadSeatSets` returns empty sets on any lstat error | `library-seats` ENOENT-vs-file tests |
|
||||
|
||||
### `refusalResponse` and `catalogue()`'s refusal (past "predicate wiring and REFUSAL_STATUS only")
|
||||
|
||||
Why it is needed: the reader's `catalogue()` now returns a refusal when the business check throws (M7: no
|
||||
partial list), and `conversationResponse` had no way to turn a catalogue refusal into a status. `open` and
|
||||
`next` already built `{ status: REFUSAL_STATUS[code] ?? 422, body: { error, refusal: { code, reconcile } } }`
|
||||
inline; that expression moved, character for character, into `refusalResponse(out)` so the listing could
|
||||
use it too.
|
||||
|
||||
No existing route's status or body changes:
|
||||
|
||||
- `open` and `next` refusals: the same expression, now behind a function. `git diff aca16404c..HEAD --
|
||||
packages/control-board/src/serve.mjs` shows the line as a move.
|
||||
- `/api/conversations` success: `{ status: 200, body: listing }` where `listing` is what the route returned
|
||||
before. `catalogue()` returned `ok: true` on every path before this row, so the new branch is reached only
|
||||
by the business refusal.
|
||||
- The "no parameters are accepted" 400 is untouched.
|
||||
|
||||
Phase B runs `serve.test.mjs`, `discord.test.mjs` and the conversation route tests whole, so Filbert can
|
||||
check this statement against a green run.
|
||||
|
||||
### Smaller changes from the early look
|
||||
|
||||
- Spy test: one `next()` call adds a third `creativeCheck` count (`reader.test.mjs`). The catalogue assert
|
||||
also checks `refusedRoots` is empty, in the same test and in the thread-store test in `creative-hidden`.
|
||||
- `library-seats.test.mjs` mode-000 test pins `BusinessError` and its message (`must be a regular,
|
||||
non-symbolic-link file`, from `readJsonFile`'s open failure).
|
||||
- "passes the engine rule" now asserts exit 0 and `offline check passed`. The expectation is unverified: if
|
||||
a mosaic-bus binding fails some other offline check, that is a first-run red to place (mos-1272).
|
||||
|
||||
### Carried, not changed here
|
||||
|
||||
- `conversation/src/controller.mjs:176` builds a reader with no `creativeCheck`. No `src` file imports it
|
||||
today; a live wiring must pass the check. Raised for Sage.
|
||||
- `discord run` reads the business file once at start. `engine` and `tools` are FIXED_KEYS, so only a
|
||||
business-file change while a connector runs goes unseen until the next start.
|
||||
- `creative-hidden` and `library-seats` use the repo `roles/` as the default `rolesDir`.
|
||||
|
||||
### Gate additions
|
||||
|
||||
Phase B list is unchanged: the same suites run whole, plus the files above are inside them. The drift check
|
||||
against `aca16404c..refactor`, including files these tests copy or read, repeats after the rebase onto 253.
|
||||
|
||||
## Round 2c: refusalResponse statement corrected from the diff, controller.mjs follow-up (2026-10-22T14:55Z, sage-2729)
|
||||
|
||||
Appended after Round 2b; nothing above is edited. This replaces Round 2b's refusalResponse statement, which I
|
||||
wrote from memory of the code. Source: `git diff aca16404c HEAD -- packages/control-board/src/serve.mjs` and
|
||||
`packages/conversation/src/reader.mjs`, read in full. No test has run.
|
||||
|
||||
### What the diff shows in serve.mjs
|
||||
|
||||
- `open` and `next` refusal: the object expression `{ status: REFUSAL_STATUS[out.refusal.code] ?? 422, body: {
|
||||
error: out.refusal.message, refusal: { code, reconcile } } }` moved unchanged into `refusalResponse(out)`.
|
||||
The two call sites reach it by `return refusalResponse(out)`. Status and body are identical for every code
|
||||
that reached it before.
|
||||
- `REFUSAL_STATUS` gains two keys, `conversation-not-listed` (404) and `conversation-business-unreadable`
|
||||
(503). Before, those codes did not exist, so no earlier response used them or changes. Every other key is
|
||||
untouched.
|
||||
- `/api/conversations` with a query string: the 400 `no parameters are accepted` line is untouched.
|
||||
- `/api/conversations` success: before, `{ status: 200, body: reader.catalogue() }`; now the same object
|
||||
when `listing.ok` (it is the same `listing`), so the 200 body is unchanged.
|
||||
- The one new response shape: `/api/conversations` can return a refusal (503 or the refusal's status) when
|
||||
the business check throws. That is the M7 behaviour, not a change to an existing route's response.
|
||||
|
||||
### What the diff shows in reader.mjs, past my Round 2b statement
|
||||
|
||||
`catalogue()` is now wrapped in `try { ... } catch (err) { return refuse(err); }`. `refuse` returns a refusal
|
||||
object for a `Refusal` and rethrows anything else (`reader.mjs:352-355`). So a non-Refusal error still
|
||||
propagates as before. A `Refusal` escaping the loop body would now come back as `{ ok: false }` where it
|
||||
used to throw. The loop already catches `Refusal` from `listSessionFiles` and from `summarize`, so the only
|
||||
`Refusal` that newly reaches the wrapper is the hidden check's. I have not confirmed that `listRoots()`
|
||||
(`rootsFromSpecs`, `loadRegistrations`) never throws a `Refusal`; if it does, that call now returns a
|
||||
refusal. Filbert's round check should read `rootsFromSpecs` for `Refusal` throws.
|
||||
|
||||
Round 2b said the old `catalogue()` "returned ok: true on every path". True for returns; it could throw.
|
||||
|
||||
### Named follow-up for DEFERRED (Sage lands it with 220; it does not block 220)
|
||||
|
||||
`packages/conversation/src/controller.mjs:176` builds a reader with no `creativeCheck`. No `src` file imports
|
||||
`controller.mjs` today, so no live path leaks. Whoever first wires `controller.mjs` live must pass
|
||||
`creativeCheck` (`creativeCheck(configDirectory)` from `control-board/src/serve.mjs`, or the reader's own
|
||||
equivalent) and add a test that a creative seat's sessions stay hidden through that path.
|
||||
|
||||
## Round 2d: can listRoots() throw a Refusal into catalogue()? No (2026-10-22T14:53Z, sage-2730)
|
||||
|
||||
Appended after Round 2c; closes the question Round 2c left open. Static read on 9a9f6dd54; nothing run.
|
||||
Filbert reached the same answer independently (filbert-220-r2c-static).
|
||||
|
||||
`catalogue()` calls `listRoots()` inside the new `try`. In production that is `serve.mjs:261`,
|
||||
`roots: () => rootsFromSpecs(specs, loadRegistrations(seatsDir).registrations)`. The only `Refusal` class
|
||||
the reader catches is the one in `conversation/src/safe-fs.mjs:21`.
|
||||
|
||||
- `rootsFromSpecs` (`conversation/src/reader.mjs:38-71`) calls `resolve`, `join`, `basename`, `Array.find`,
|
||||
`toTime` and `samePath`, and constructs no `Refusal`.
|
||||
- `toTime` (`conversation/src/pi.mjs:21-26`) returns a value or null and has no throw.
|
||||
- `samePath` (`seat/src/seat.mjs:261-269`) wraps `realpathSync` in try/catch and returns false.
|
||||
- `loadRegistrations` (`control-board/src/scan.mjs:272-292`) catches `SeatError` and rethrows anything
|
||||
else. It calls `readRegistration` (`seat/src/seat.mjs:200-216`), which throws `SeatError` only.
|
||||
- `grep -n "Refusal\|refuse(" packages/control-board/src/scan.mjs packages/seat/src/seat.mjs` finds
|
||||
nothing, and neither file imports `safe-fs.mjs` (checked with `grep -n "^import"`). A `Refusal` cannot
|
||||
originate in either.
|
||||
- Inside the loop, `listSessionFiles` is caught at `reader.mjs` (the `catch (err)` that rethrows
|
||||
non-Refusals) and `summarize` turns an `openSessionFile` Refusal into a `denied` row.
|
||||
|
||||
So the only `Refusal` that reaches the new catch is the hidden check's `conversation-business-unreadable`.
|
||||
Any other error still throws through `refuse()` (`reader.mjs:352-355`) exactly as before.
|
||||
|
||||
Therefore `/api/conversations` has no status change for a path that existed before this row: the old
|
||||
throw paths are unreachable by a `Refusal`. The one new response is the business refusal, already pinned by
|
||||
the creative-hidden 503 assertion and the reader catalogue-refusal test. No extra test is added.
|
||||
|
||||
The exception is an injected `roots` function that throws a `Refusal` itself; only a test could pass one,
|
||||
and none does.
|
||||
|
||||
Time correction (2026-10-22T14:51:01Z): the heading times of Round 2c (14:55Z) and Round 2d (14:53Z) were written before I read the clock. The commits were made at 14:49:40Z (9a9f6dd54) and 14:50:52Z (176ac262e). The heading of Round 2b (14:46Z) is also an estimate; its commit e4a204836 was made before 14:47:53Z.
|
||||
|
||||
## Round 4: rebase onto 45dca69f6, drift check (2026-10-23T18:53:43Z, sage-3576)
|
||||
|
||||
Appended; nothing above is edited. Git only; nothing run, nothing pushed. Branch researcher/row220-r4 holds the
|
||||
five row 220 commits on 45dca69f6 (origin/refactor, 10-23). Earlier base: 48e450ce8 (row 253).
|
||||
|
||||
- Rebase: clean. `git range-diff 48e450ce8..3ef6da90d 45dca69f6..researcher/row220-r4` shows all five commits as `=`,
|
||||
so no patch changed.
|
||||
- Landed in 48e450ce8..45dca69f6: 1175 files (47 packages/bus, 32 webui, 29 cli, 10 plugins, 9 seat, 4 harness,
|
||||
2 tasks, 7 scripts, BUILD-LOG.md, the rest agents/ and docs/).
|
||||
- Path overlap with this row's 19 paths: none.
|
||||
- Landed files in the packages these tests touch: webui (src/serve.mjs, src/cli.mjs, chat and bus files,
|
||||
tests). Row 220's webui test changes are five fixtures that start the control-board server with
|
||||
`configDirectory: root` and start the webui server with only `{ port, board }`; no landed webui file passes or reads
|
||||
configDirectory. The landed webui tests are not in this row's change set, but a whole webui run covers them.
|
||||
No landed file in control-board, conversation, discord or business.
|
||||
- Other users of the touched symbols (git grep on the r4 tree for createReader, rootsFromSpecs, creativeCheck,
|
||||
configDirectory, businessDir, loadSeatSets, checkLibraryEngine, conversationReader outside this diff):
|
||||
only conversation/src/controller.mjs:173-176 (the follow-up named in Round 2c) and the conversation README.
|
||||
- Hash pins: grep for 64-hex in packages/*/tests and scripts finds 364 files (positive control), and none pins a
|
||||
row 220 source file by hash. Path-string hits for packages/discord/src/cli.mjs (copy-service.test.mjs,
|
||||
scripts/test-discord.sh, discord-service.sh) are paths, not hashes.
|
||||
- Added to the gate list: `packages/discord/tests/helpers.mjs` is imported by 19 discord test files, and this row
|
||||
removed its empty `businesses` mkdir. Phase B must run the discord package whole (and `scripts/test-discord.sh`),
|
||||
not only library-seat.test.mjs.
|
||||
|
||||
## Round 5: phase B on 2bb146b28, R1 and R2 fixes (mos-1528) (2026-10-23T20:21:39Z, sage-3712)
|
||||
|
||||
Appended; nothing above is edited. The tree is 2bb146b28 plus the commits of branch researcher/row220-r5.
|
||||
|
||||
Phase B receipts on 2bb146b28 (researcher-r220-kit out-b1, out-b2, out-b3, out-mut, out-npm):
|
||||
|
||||
- discord 20 files, 200 pass; conversation 7 files, 187 pass; scripts/test-discord.sh rc=0, 82 passed.
|
||||
- control-board 7 files, 128 pass, 1 fail (serve.test.mjs, R2 below); business 26 files, 237 pass, 1 fail
|
||||
(library-seats.test.mjs, R1 below).
|
||||
- webui: first run 409 pass, 1 fail (editor-provenance.test.mjs). Cause: the esbuild in the worktree's
|
||||
node_modules resolved to the root copy (0.28.1) and not to packages/webui's own pin. Fix (R3, no repo change):
|
||||
`cd packages/webui && npm ci --ignore-scripts --no-audit --no-fund` in the worktree, output in out-npm/npm-ci.txt;
|
||||
esbuild is then 0.28.2. Rerun of all 65 webui files whole with MOSAIC_REQUIRE_EDITOR_BUILD=1: 65 of 65 rc=0, 410
|
||||
pass, 0 fail.
|
||||
|
||||
Fixes (both are test-only; src/ is unchanged from 2bb146b28; Mos ruled both in mos-1528):
|
||||
|
||||
- R1, `packages/business/tests/library-seats.test.mjs`, test "M7/M8: a business file that can't be read throws; no
|
||||
empty set". The fixture registers `rmSync(root)` as a `t.after` hook before the test registers its
|
||||
`chmodSync(file, 0o600)` restore, so on the way out the restore found the file already removed and threw ENOENT.
|
||||
Old: `t.after(() => chmodSync(file, 0o600));`. New: the same call inside try/catch that rethrows anything but
|
||||
ENOENT. The assertion is unchanged.
|
||||
- R2 (option A, mos-1528), `packages/control-board/tests/serve.test.mjs:1073`, test "conversation routes (F16): a
|
||||
foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers".
|
||||
Failure: the same-origin request at the end reached the route, the spy's `catalogue` returned the number
|
||||
`calls.push(...)` gives (1), and 220's `conversationResponse` reads `listing.ok`, so `refusalResponse(1)` threw on
|
||||
`out.refusal.code` and the route answered 500. Old text:
|
||||
`const spy = { catalogue: () => calls.push("catalogue"), open: () => calls.push("open"), next: () => calls.push("next") };`
|
||||
New text: `catalogue` records the call, then returns `{ ok: true, conversations: [], refusedRoots: [] }`; `open` and
|
||||
`next` are unchanged (the test never reaches them with a passing request). The assertions are unchanged: 200 for the
|
||||
same-origin request and `calls` equal to `["catalogue"]`. serve.mjs stays as 220 has it.
|
||||
Added test, same file before "every refusal code the reader can raise has an HTTP status": a reader whose
|
||||
`catalogue()` returns `{ ok: false, refusal }` gets `conversation-business-unreadable`'s status (503) and the body
|
||||
`{ error, refusal: { code, reconcile } }`. No such route test existed; the earlier claim that no route's status or
|
||||
body changed was wrong for a reader that returns a bare value, which the old spy did.
|
||||
|
||||
Mutants thrown on 2bb146b28 (out-mut; the base run for library-seats was red from R1, so a kill by that file is not
|
||||
shown there):
|
||||
|
||||
| Mutant | Result |
|
||||
| --- | --- |
|
||||
| M7-w | killed by control-board creative-hidden ("the board's default reader hides a creative seat's session…") |
|
||||
| M7-c | killed: creative-hidden 4 fail, conversation reader 3 fail |
|
||||
| M7-o | killed: creative-hidden 4 fail |
|
||||
| M8-p | killed: discord library-seat 6 fail |
|
||||
| M8-e | SURVIVED: no test failed other than the base red in library-seats; handled in the next entry |
|
||||
|
||||
Run limit: stack-release.test.mjs took 644 s. The per-file limit is 3 x 644 = 1932 s. Sage's gates run cli whole
|
||||
under 2100 s.
|
||||
|
||||
Reruns owed after r5 (run on Sage's CLEAR): the touched files whole (library-seats, serve, and the M8-e test),
|
||||
control-board whole, business whole, and every 220 mutant, M8-e included. Results are appended as a new entry.
|
||||
|
||||
## Round 5b: correction of the M8-e kill claim, and the test that kills it (2026-10-23T20:21:52Z, sage-3715)
|
||||
|
||||
Appended; nothing above is edited. This is a correction to the "Mutants to throw" table in this file (the M8-e
|
||||
row, around line 270), which says M8-e, "`loadSeatSets` returns empty sets on any lstat error", is killed by the
|
||||
`library-seats` ENOENT-vs-file tests. That claim is wrong. M8-e changes the lstat catch to
|
||||
`return { librarySeats: new Set(), creativeSeats: new Set() };` for every error code, so only a test that makes lstat
|
||||
fail with a code other than ENOENT can kill it. The file-as-businesses test and the dangling-symlink test I named
|
||||
make lstat succeed or fail with ENOENT, never another code, so they pass under M8-e. Phase B on 2bb146b28 showed
|
||||
the mutant surviving (out-mut, M8-e: no failure besides the base red).
|
||||
|
||||
Added in this commit, `packages/business/tests/library-seats.test.mjs`: "M7/M8: a config directory under a regular
|
||||
file throws (lstat fails with ENOTDIR); no empty set". The config path is a regular file, so
|
||||
`lstat(<file>/businesses)` fails with ENOTDIR; the test expects `BusinessError` matching "cannot list businesses".
|
||||
It is a new test in this row's own file, in its own commit, and droppable without touching R1 or R2.
|
||||
|
||||
The rerun of M8-e on r5 (the test must fail under it) is owed and runs on CLEAR; the result is appended as a new
|
||||
entry.
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"version": 1,
|
||||
"agents": [
|
||||
{
|
||||
"host": "api.anthropic.com",
|
||||
"port": 443,
|
||||
"path": "adapters/claude-code; adapters/pi (anthropic)",
|
||||
"note": "Candidate from S6; real-token provider-host measurement by Mos pending. Claude nonessential traffic disabled."
|
||||
},
|
||||
{
|
||||
"host": "api.z.ai",
|
||||
"port": 443,
|
||||
"path": "adapters/pi (zai)",
|
||||
"note": "Candidate for the configured zai provider; unmeasured until Mos runs provider-hosts.sh. No authority to widen on failure."
|
||||
},
|
||||
{
|
||||
"host": "api.openai.com",
|
||||
"port": 443,
|
||||
"path": "adapters/pi (openai)",
|
||||
"note": "S6 dummy-key CONNECT measured; real-token provider-host measurement by Mos pending."
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"host": "tasks.woltje.com",
|
||||
"port": 443,
|
||||
"path": "packages/tasks/src/vikunja.mjs:41",
|
||||
"note": "fetch; requires NODE_USE_ENV_PROXY=1 and HTTPS_PROXY in container startup (I6)."
|
||||
},
|
||||
{
|
||||
"host": "git.mosaicstack.dev",
|
||||
"port": 443,
|
||||
"path": "packages/bus/src/gitea-grant.mjs:115",
|
||||
"note": "NOT PROXIED today: https.request agent:false with custom CA; transport ruling owned by Sage before broker container rollout."
|
||||
},
|
||||
{
|
||||
"host": "git.mosaicstack.dev",
|
||||
"port": 443,
|
||||
"path": "packages/seat/src/working.mjs:471",
|
||||
"note": "NOT VERIFIED through proxy: broker publishRemote spawns git with a scrubbed environment and credential on fd 3; core design excludes git. Sage transport ruling required."
|
||||
},
|
||||
{
|
||||
"host": "discord.com",
|
||||
"port": 443,
|
||||
"path": "packages/cli/src/notifier-process.mjs:45 -> packages/discord/src/rest.mjs:45",
|
||||
"note": "Notifier REST fetch only; requires NODE_USE_ENV_PROXY=1 and HTTPS_PROXY (I6)."
|
||||
},
|
||||
{
|
||||
"host": "gateway.discord.gg",
|
||||
"port": 443,
|
||||
"path": "packages/discord/src/gateway.mjs:193",
|
||||
"note": "NOT PROXIED today: native WebSocket in separate Discord connector, not notifier. Design names gateway; retained pending Sage transport ruling."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1593,3 +1593,38 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-23T17:48:43Z | Scrooge (629b6cd3) | Row 252 Console inbox Approve and Deny (#1753) | Rebased onto 306 landing as 580037105 (range-diff all =), slot run bus 907/0 webui 410/0 cli 943/0, in-review revs 3608/3609, Webby r1 APPROVE, J1-J19 specs to Sage; Sage landed 45ea3fa0d (done rev 3616). Part 1 mutant results were never in BUILD.md; they went to Sage by T3 (scrooge-0252-mutant-specs).
|
||||
2026-10-23 | Sage (1ef1e4f8) | Rows 308 and 252 landings, 571 Connections design amendment, Vikunja 597 | 308 (PRD 1.17, #1815) landed 30e6bb680 and 252 (#1753) landed 45ea3fa0d (replay of 580037105 onto 30e6bb680, range-diff all =), each after a full gate on the landed tree (252 also J1-J19, J11c and J18 equivalent); Gyro's 571 amendment (Fenton APPROVE) landed e8bb13df8; Vikunja 570, 435 and 597 done, canonical webui esbuild 0.28.2; queue revs 3589-3616 committed 59f013247
|
||||
2026-10-23 | Sage (1ef1e4f8) | Vikunja 571 W build rows; rows 13, 42, 257 and 289 check | Filed the design's ten W rows as queue rows 309-318 (revs 3617-3626, owners set to unassigned 3627-3636, queue commit 293b42fee pushed); Vikunja 571 done (comment 994). Row 13's candidate fd72d2689 is on refactor, gate is Monday's row 7 ledger run; 42 paused under mos-1184; 257 post-v1; 289 waits on 214 and 288. Slot released 17:56Z.
|
||||
2026-10-23T21:08:32Z | Filbert (048f3c63) | Rows 220 and 216 round 2 verdicts, r5 static deltas, replay drift check | 220 APPROVE 739b868b6 (comment 31383 #1721, rev 3696), 216 APPROVE dae76ccc2 (comment 31384 #1717, rev 3697) on Researcher's r5 reruns and mutants and Sage's joint gate GREEN on fbe57a05b (sage-3748); static r5 deltas clean; 216 N3 (get returns a universe's own supporting files on 283's base) binds C2-05c; no tests run by me
|
||||
2026-10-23 | Gladstone (433058fc-9cea-46e3-8554-6ddefb312762, Codex gpt-6.1-sol) | #1816 / Vikunja 614, mos-1531 | Built hostcheck octal-mode fix and two bare-scratch bundle verifies on gladstone/hostcheck-item5 from 93694c518. Three append-only test blocks; Node/tests held by Sage-3738, untested push authorized sage-3740. Queue number pending; Morgana static review, Sage gate/landing.
|
||||
2026-10-23T21:58:38Z | Sage (1ef1e4f8) | Rows 220, 216, 320 landings and queue-commit (mos-1528, mos-1532/1534/1536/1537) | 220+216 joint gate GREEN 21:05:34Z on fbe57a05b, pushed with Filbert's SESSIONS line 582b86935; #1721 and #1717 closed. Row 320 (#1816, Gladstone F1-F6): faf55618e replayed onto 45e3ca7bd as 094124b4e (code patch-id equal, log appends in one final commit), Morgana APPROVE r2 c31403, full gate GREEN 21:53:00Z 0 fail, pushed 21:53Z, done rev 3717, #1816 closed c31413, Vikunja 614 done. queue-commit 604523b20 holds revs 3678-3717. Mos sent N=094124b4e to ops-01 (mos-1540).
|
||||
2026-10-23T22:06Z | Duckworth (Opus 5.5) | row 239 (#1745) round 1 on r4 e0cb1db2c (honker-0206, rev 3719) | APPROVE (static): r4 range-diff all =, 16 paths byte-equal to r3, e0cb1db2c BUILD.md only, 4 channel-proof pins match; record waits for Sage's comment id and gap (duckworth-0295) |
|
||||
|
||||
2026-10-23T19:13:00Z | Boyd (48a3ae38, Codex gpt-6.1-sol) | Row 298 (#1804, Q228) takeover from Megavolt under mos-1496 | Started on boyd/row298 at 45dca69f6; reading brief, D1-D6 and human-shell proof; awaiting Megavolt handoff. No Node/tests before Sage CLEAR, no queue writes.
|
||||
|
||||
2026-10-23T19:29:00Z | Boyd (48a3ae38) | Row 298 (#1804) targeted T1 lane B | Five whole files 29/0/0, ten mutants killed, source restored; reviewer delta prepared. Earlier startup timestamp corrected in Boyd BUILD.md. Full gate and queue/landing remain Sage's.
|
||||
|
||||
2026-10-23T22:41:07Z | Boyd (48a3ae38) | Row 298 rebase under sage-3794; row 321 review assigned | Rebased candidate on boyd/row298-r2 at base833ec1cc8; append-only log conflicts resolved preserving upstream, writer-lock revoke retained; no Node/tests before CLEAR. Row321 awaits pushed candidate/review request.
|
||||
2026-10-23 | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 322 / #1818 F8/F9 | Investigated clone dependency failure, recovered pinned brief and queue assignment; building on goldie/row322 under mos-1550, tests pending Sage CLEAR.
|
||||
2026-10-23T22:48Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 322 / #1818 F8/F9 builder verification | sage-3808 slot: 54/54 across three whole files, new tests first-run/restored2/2, original-N installer and both mutants fail as intended; bytes restored. Candidate ready for goldie/row322 push and Morgana review; no merge/full-gate claim.
|
||||
2026-10-23T23:15Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 324 / #1820 optional tracker design | New goldie/row324 worktree from approved860afc2b6, pinned brief f375f4e92, complete reader audit/plan. Existing required-key test contradicts Q247; requesting Mos exception before implementation. Tests held until CLEAR; row323 remains deferred until324 lands.
|
||||
2026-10-23T23:18Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 324 / #1820 untested draft | Claim3759; optional tracker implementation, new loader/CLI/boot/Console tests, complete reader audit and prepared mutant. Mos-1556 permits sole existing required-key-list change. Syntax/diff checks pass; no tests before CLEAR. Untested push authorized sage-3836.
|
||||
2026-10-23T23:44Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 324 / #1820 verified rebased candidate | sage-3850: rebase onto3569bf54f, only append-log conflicts; 5/5 +4/4 +21/21, mutant killed/restored, new files rerun9/9; no unexpected red. Publishing goldie/row324-r2 avoids force push. Duckworth review and Sage full gate pending.
|
||||
2026-10-23T23:43:13Z | Sage (1ef1e4f8) | Landed rows 298 (860afc2b6), 322 (467b05fe2) and 321 (5283d3745..3569bf54f), one full gate GREEN on tip 3569bf54f, pushed 23:32:54Z; #1804 #1817 #1818 closed; queue revs 3733-3765 in fb49fa63e; row 324 CLEAR to Goldie; B2-2 to DEFERRED | landed
|
||||
2026-10-23T23:52Z | Duckworth (Opus 5.5) | row 324 round 1 static review on dbdfcfd52 (sage-3853) | APPROVE (static), manifest 52874356 matches, every business.tracker reader handles absence, present null refuses, no @sync when absent, N1 doc (duckworth-0298) |
|
||||
2026-10-23T22:59Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 Case 1 factory business | New goldie/row323 worktree from da537e7d1; source review finds host/project/human/tracker/model values absent from DESIGN, asked Sage under explicit no-guess boundary (goldie-0008). No tests/config/host action; claim retries authorized.
|
||||
2026-10-23T23:02Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 no-bot static trace | Confirmed required absent tracker token refuses both install validation and bus boot regardless of tracker.baseUrl; reported exact source lines to Sage (goldie-0009), stopped product work per sage-3823. No tests/config/credentials/host action.
|
||||
2026-10-24T00:11Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 resumed after Q247 | On dd6cb5872, trackerless Case1 JSON and seven new scratch tests drafted; no old test changes or credentials. Prepared mutant, syntax/diff/static JSON checks only; asking Sage for test CLEAR.
|
||||
2026-10-24T00:12Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 pre-test commit | sage-3861 CLEAR explicitly requests a committed draft before testing. Field provenance recorded; no old tests changed. Runs pending, no green claim.
|
||||
2026-10-24T00:22Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 first slot stop | Commit571376c30 new7/7/business261/261; CLI existing row306 revoke-window red no RESULT, suite stopped (partial631 pass/1 fail). No old-test changes/reruns/mutant; manifest unchanged. Sage goldie-0016 has failure evidence and slot back; awaits ruling.
|
||||
2026-10-24T00:25Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 mos-1560 stop | First isolated revoke-window whole-file rerun failed0/1 at20.126s under load1.46/1.68/2.70/helperCLEAR. Second-red rule: stopped with no further tests/edits, manifest unchanged, remote571 unverified. Log and load lines in BUILD.md; Sage notified goldie-0018, slot returned.
|
||||
2026-10-24T00:38Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row 323 / #1819 accepted verification | mos-1561/sage-3869: short31-byte TMPDIR fixes socket-length refusal; revoke-window1/1, business261/261, new7/7, mutant killed/bytes restored, new7/7 again. Every run logs time/load/helper; no local CLI rerun, Sage gate supplies it. Notes/logs evidence only; Duckworth review/in-review move next.
|
||||
2026-10-24T00:41Z | Duckworth (Opus 5.5) | row 323 round 1 static review on 6a0f0f4f8 (sage-3865, sage-3870) | APPROVE (static) C1 Sage full gate cli, manifest 903ef022 matches, fields sourced, tests read shipped bytes, tester mutant real, N1-N2 doc (duckworth-0300) |
|
||||
2026-10-24T01:13Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row325 / #1821 Case1 project key | New goldie/row325 worktree at99930362e; pinned brief60d78e44e. Draft JSON key correction, new real-project-byte tests, sole old line70 key edit under mos-1564. Static checks only; requesting Sage CLEAR, no queue writes.
|
||||
2026-10-24T01:14Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row325 / #1821 verified | sage-3882: new3/3, business264/264, one key mutant killed/restored, new3/3 again, per-run fresh shortTMP/load/helperCLEAR. Four manifest entries verify; sole old assertion key change, no queue writes. Candidate push/report to Sage and Gladstone next.
|
||||
|
||||
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 I2 / #1652 pre-build recovery at ec86f2c84 | Blocked: canonical queue stale brief pin plus non-fetch broker outbound stop condition; sent Sage bentina-0001, recorded source drift in agents/bentina/work/queue-157/BUILD.md. No tests, Docker, credential reads, queue writes, commit or push.
|
||||
|
||||
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 first CLEAR stop | sage-3910 tests31/31 helper/config,11/11 proxy. Own output regression stops old config run3 (55 pass/1 fail); run4 not run, no Docker. Slot returned Sage bentina-0004; source-only correction applied under sage-3916, no rerun until next CLEAR. Logs in agents/bentina/work/queue-157.
|
||||
|
||||
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 candidate verified | sage-3919 config56/56/helper31/31; sage-3923 final proxy12/12 and install exit0 (rootless builds/pins, internal network, services fetch). Source-only transport gaps recorded under sage-3907. Slot returned bentina-0007; preparing own-branch candidate for Boyd then Darkwing, with Mos measurement/Sage full gate still pending. No protected branch/live-state/real-token action.
|
||||
|
||||
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 round2 B1/B2 | sage-3933 scoped corrections; sage-3936 archived red proves7defect assertions/5controls, fixed12/12, helper/config33/33, egress22/22, rootless install exit0. No old assertion changes. Slot returned bentina-0014; preparing followup commit/push for Boyd then Darkwing, provider measurement/Sage gate still pending.
|
||||
@@ -1365,3 +1365,42 @@ public sign-in smoke and a forged-loopback 401 check; rollback drops flags
|
||||
before middleware. Same-uid callers can still read the file; separate service
|
||||
uid is Jason's question on #385. The Authentik allowed-uid file remains an
|
||||
identity input and is never reused for proof.
|
||||
|
||||
## Containerized image preparation (I2)
|
||||
|
||||
`scripts/images.sh build|list` uses RELEASE tags mosaic-core, mosaic-session
|
||||
and mosaic-egress. It requires install.mode containerized and an explicit
|
||||
Unix endpoint in the selected config. Build prepares an isolated runtime
|
||||
source context and uses only the configured rootless daemon; list inspects
|
||||
the three exact tags. It does not start the stack. See images/README.md.
|
||||
|
||||
`scripts/lib/docker.sh` is a sourceable Bash helper exposing
|
||||
`mosaic_docker <arguments...>`. It uses common.sh's load_config (which reads
|
||||
mosaic-config.mjs env), caches one successful rootless check in that Bash
|
||||
process, and adds --host to every Docker call. The first call checks the
|
||||
exit status of info before parsing .SecurityOptions as a JSON array for
|
||||
exactly name=rootless. Failure prints endpoint-not-rootless, exits the
|
||||
operation with status 2, and never forwards the requested command. Local
|
||||
mode prints containerized-install-required. No ambient context, DOCKER_HOST
|
||||
or caller-supplied mode/endpoint variable selects the target.
|
||||
|
||||
The config loader accepts optional install with mode local/containerized.
|
||||
Absent defaults to local without adding a validation-output key; local forbids
|
||||
endpoint, containerized requires
|
||||
`^unix:///[^\s]+\.sock$`. Unknown keys, mode or bad type exit 2. validate
|
||||
checks shape only and never changes config. env adds MOSAIC_INSTALL_MODE
|
||||
and MOSAIC_DOCKER_ENDPOINT for the helper; no configVersion bump.
|
||||
|
||||
`scripts/test-install.sh` runs the I2 config/helper/proxy cases, then builds
|
||||
and probes the images on MOSAIC_TEST_DOCKER_ENDPOINT (default explicitly
|
||||
unix:///run/user/1000/docker.sock). It creates only i2-prefixed containers
|
||||
and networks, cleans only those names, and refuses an unavailable or
|
||||
non-rootless endpoint. It neither installs nor starts a daemon. It uses
|
||||
scratch MOSAIC_CONFIG and dummy/no tokens. All test execution requires the
|
||||
assigned test slot. Run with fresh short scratch TMPDIR and timeout -k 10.
|
||||
|
||||
Mos's real-provider measurement script and expected output are in
|
||||
agents/bentina/work/queue-157/provider-hosts.sh and PROVIDER-EXPECTED.md.
|
||||
The baked agents list is provisional until matching evidence appears on
|
||||
#1652. The services contract notes transport gaps; I2 starts no broker behind
|
||||
the proxy. Sage owns the ruling on those gaps before later install rows.
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"businessVersion": 1,
|
||||
"id": "mosaic-stack",
|
||||
"human": "jason",
|
||||
"arbiters": {
|
||||
"delivery": "chief",
|
||||
"technical": "chief"
|
||||
},
|
||||
"projects": {
|
||||
"stack": {
|
||||
"root": "/srv/mosaic/src/mosaic-stack"
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"bus.tester": true
|
||||
},
|
||||
"plugins": {
|
||||
"harness": ["claude-code"]
|
||||
},
|
||||
"roles": {
|
||||
"chief": {
|
||||
"definition": "chief-of-staff",
|
||||
"vars": {
|
||||
"harness": "claude-code",
|
||||
"model": "claude-opus-5-5",
|
||||
"thinking": "high"
|
||||
}
|
||||
},
|
||||
"assistant": {
|
||||
"definition": "assistant",
|
||||
"vars": {
|
||||
"harness": "claude-code",
|
||||
"model": "claude-opus-5-5",
|
||||
"thinking": "high"
|
||||
}
|
||||
}
|
||||
},
|
||||
"launch": {
|
||||
"by": "chief",
|
||||
"instances": ["assistant"],
|
||||
"models": {
|
||||
"claude-opus-5-5": {
|
||||
"provider": "anthropic",
|
||||
"model": "claude-opus-5-5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,12 @@ First install:
|
||||
```sh
|
||||
# Copy the bundle in, then check it before anything reads it.
|
||||
echo "<bundle sha256> /root/<sha>.bundle" | sha256sum -c -
|
||||
git bundle verify /root/<sha>.bundle
|
||||
(
|
||||
bundle_check="$(mktemp -d)" || exit 1
|
||||
trap 'rm -rf "$bundle_check"' EXIT
|
||||
git init -q --bare "$bundle_check" &&
|
||||
git -C "$bundle_check" bundle verify /root/<sha>.bundle
|
||||
) || echo "STOP: bundle verify failed; do not continue"
|
||||
|
||||
# Root's checkout. stack-upgrade.sh and case2-unit.sh run from here, never from a copy.
|
||||
git clone -q -b refactor /root/<sha>.bundle /root/mosaic-stack
|
||||
@@ -202,11 +207,29 @@ install -o mosaic -g mosaic -m 0600 /root/<sha>.bundle /srv/mosaic/incoming/<sha
|
||||
sudo -u mosaic -H git clone -q -b refactor /srv/mosaic/incoming/<sha>.bundle /srv/mosaic/src/mosaic-stack
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
```sh
|
||||
echo "<bundle sha256> /root/<sha>.bundle" | sha256sum -c -
|
||||
git bundle verify /root/<sha>.bundle
|
||||
(
|
||||
bundle_check="$(mktemp -d)" || exit 1
|
||||
trap 'rm -rf "$bundle_check"' EXIT
|
||||
git init -q --bare "$bundle_check" &&
|
||||
git -C "$bundle_check" bundle verify /root/<sha>.bundle
|
||||
) || echo "STOP: bundle verify failed; do not continue"
|
||||
git -C /root/mosaic-stack fetch -q /root/<sha>.bundle refs/heads/refactor:refs/remotes/origin/refactor
|
||||
git -C /root/mosaic-stack checkout -q --detach <sha>
|
||||
install -o mosaic -g mosaic -m 0600 /root/<sha>.bundle /srv/mosaic/incoming/<sha>.bundle
|
||||
@@ -231,6 +254,24 @@ When a run doesn't exit 0, send Mos the exit and the last lines of `/var/log/mos
|
||||
the old copy. The next run refuses `args-differ`, and a render from that file would put the old copy back.
|
||||
Move the file the result line names into place with `mv -f`. The two differ only in the `--copy` value.
|
||||
|
||||
After `case2-install.sh` succeeds, create the human CLI wrapper before running
|
||||
`stack-copy.sh check --units external`. Creating this wrapper is a separate live
|
||||
step requiring Mos's go (`scripts/stack-copy.sh:3`); the install does not create it. Run only on that go:
|
||||
The step runs under sudo's `secure_path`, so it sets its own PATH and umask.
|
||||
|
||||
```sh
|
||||
sudo -u mosaic -H /bin/sh <<'MOSAIC_WRAPPER'
|
||||
cd / || exit 1
|
||||
umask 077
|
||||
PATH=/opt/mosaic-stack/node-<v>/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH
|
||||
mkdir -p -- "$HOME/.npm-global/bin" || exit 1
|
||||
"$HOME/src/mosaic-stack/scripts/stack-copy.sh" wrapper <sha> --create
|
||||
MOSAIC_WRAPPER
|
||||
```
|
||||
|
||||
`--create` refuses an existing wrapper; stop and review it rather than replacing
|
||||
it. The wrapper must name the same `<sha>` copy as the units.
|
||||
|
||||
Precheck lines: none. Root's checkout at `<sha>` and the stack's user's clone: precheck line missing.
|
||||
After install, `stack-copy.sh check --units external` reads the copy the units run.
|
||||
|
||||
@@ -278,16 +319,56 @@ file with `stat -c '%U %a %n' PATH`, never `cat`. `<business>` is the host's bus
|
||||
|---|---|---|---|---|---|
|
||||
| `/srv/mosaic/.config/mosaic-dev/secrets/` | `claude-session.env`, one line `ZAI_API_KEY=...` | 0600 | mosaic | library | `<path> sets ZAI_API_KEY` |
|
||||
| `/srv/mosaic/.config/mosaic-dev/secrets/` | the Console's proof file, the path the args file's `--proof-file` names | 0600 or 0400 | mosaic | library | none. `case2-unit.sh` refuses at render. Precheck line missing. |
|
||||
| `/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 ...` |
|
||||
| `/srv/mosaic/.config/mosaic-dev/secrets/agents/<business>/<agent>/` | one file per service | 0600, dirs 0700 | mosaic | both | none. Filled only by `mosaic credentials issue`, or Approve after #436. Never by hand. |
|
||||
|
||||
The installer key, `<configDir>/secrets/hosts/<business>/<hostId>/installer`, is not a slot anyone places.
|
||||
Enrollment (section 8) writes it under the enrolling stack's config directory and deletes it when the host
|
||||
leaves.
|
||||
|
||||
## 9a. Notifier decision, before starting units
|
||||
|
||||
Make the explicit per-host decision before section 10: the default below is
|
||||
`binding: null`, so the bus runs without DMs. A named binding is a later per-host
|
||||
choice that needs Mos's go. The installer does not make this decision or write
|
||||
this file. Replace `<dataRoot>` with the configured data root and `<business>`
|
||||
with this host's business id. If the file already exists, stop and review the
|
||||
existing decision; this command refuses to overwrite it.
|
||||
|
||||
```sh
|
||||
sudo -u mosaic -H /bin/sh <<'MOSAIC_NOTIFY'
|
||||
cd / || exit 1
|
||||
umask 077
|
||||
notify_root='<dataRoot>/notify'
|
||||
notify_dir="$notify_root/<business>"
|
||||
mkdir -p -m 0700 -- "$notify_root" && chmod 0700 -- "$notify_root" || exit 1
|
||||
mkdir -p -m 0700 -- "$notify_dir" && chmod 0700 -- "$notify_dir" || exit 1
|
||||
(set -C; printf '%s\n' '{"notifyVersion": 1, "binding": null}' > "$notify_dir/notify.json") || exit 1
|
||||
chmod 0600 -- "$notify_dir/notify.json"
|
||||
MOSAIC_NOTIFY
|
||||
```
|
||||
|
||||
Precheck line: `ok notifier config <dataRoot>/notify/<business>/notify.json validates (directory 0700, file 0600)`.
|
||||
|
||||
## 10. Console start
|
||||
|
||||
None by hand. I2's `case2-unit.sh` enables both units, starts the bus, then the Console (Q189, mos-1122).
|
||||
`stack-upgrade.sh` stops and starts both units itself.
|
||||
|
||||
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
|
||||
)
|
||||
```
|
||||
|
||||
Precheck line: none. Both units active after the render: precheck line missing.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Case 1 factory business file for .53 (#414 stage 2)
|
||||
|
||||
Written 2026-10-23 by Sage for ops-01's stage-2 request (ops-0277, mos-1551, sage-3807).
|
||||
|
||||
## Brief: the Case 1 factory business file
|
||||
|
||||
**Why.** The .53 factory install (#414) runs `scripts/case2-install.sh --business mosaic-stack
|
||||
--business-file FILE --commit SHA --profile factory`. No Case 1 business file is committed. Row 195 left the
|
||||
role set to the go (agents/darkwing/work/queue-195/DESIGN.md:141 at b65014647). ops-01 needs FILE as a path
|
||||
at a commit.
|
||||
|
||||
**What to write.** One business document, `docs/guides/case1-business.mosaic-stack.json`, and one test that
|
||||
loads it.
|
||||
|
||||
The file's terms come from these sources. Read each one before writing.
|
||||
- Id `mosaic-stack` (DESIGN.md section 7).
|
||||
- `vars` has `bus.tester` true. The file has no `library` key and no library project
|
||||
(case2-install.sh:106-108; case2-precheck.sh's factory profile, `profile-library-business`).
|
||||
- The smallest role set that passes Case 1: a chief of staff from `roles/chief-of-staff.json` with
|
||||
`vars.harness` set to the claude harness, and `launch.by` naming it (case1-accept.sh F3 and F7, :545-559;
|
||||
packages/business/src/business.mjs:123 and :177-199). Add no other role unless the loader or the Case 1
|
||||
checks refuse without it. If one is needed, say which check needs it in BUILD.md.
|
||||
- Claude only. No pi harness and no pi role (P19 #383, mos-0865).
|
||||
- No credential value anywhere. A credential entry, if the loader needs one, is a file path under the
|
||||
install's home, and BUILD.md says which file the operator creates. The chief holds no credential and no
|
||||
tracker bot (packages/business/tests/helpers.mjs:71-72).
|
||||
- `human`, project roots and any other host path use the .53 install's values from DESIGN.md (the `mosaic`
|
||||
user and its home). Where DESIGN.md doesn't give a value, stop and ask me; don't guess a host path.
|
||||
|
||||
**Test.** A new test file, packages/business/tests/case1-business-file.test.mjs, that:
|
||||
1. loads the committed file through the business loader with a temporary config dir and passes;
|
||||
2. runs case2-install.sh's factory-profile checks on it (the same input path the install uses) and passes;
|
||||
3. fails when `bus.tester` is false and when a `library` key is added, with the install's refusal codes.
|
||||
|
||||
No old test changes. If one seems needed, stop and ask me (mos-1272).
|
||||
|
||||
**Gate.** The new test file whole, then packages/business and packages/cli suites whole, on my CLEAR only.
|
||||
One mutant: flip `bus.tester` in the committed file and show the test red, then restore and check bytes.
|
||||
|
||||
**Boundaries.** No host action, no .53 contact, no credential reads, no queue writes, no push to refactor.
|
||||
Work in a new worktree off refactor, on branch `<seat>/row323`. Push the branch and report the tip,
|
||||
base and manifest to Sage. Record design choices in agents/<seat>/work/queue-323/BUILD.md.
|
||||
|
||||
**Acceptance.** The file at a landed commit loads and passes the factory checks. ops-01 can pass its path to
|
||||
case2-install.sh unchanged.
|
||||
@@ -0,0 +1,141 @@
|
||||
# Brief: Case 1/2 host fixes for the 433 P1 rerun
|
||||
|
||||
Sage, 2026-10-23, filed for Mos (mos-1532). Issue #1816, Vikunja 614. On the 10-27 path.
|
||||
|
||||
## Brief: hostcheck item 5, bundle verify, case2-unit @, stack-copy cwd
|
||||
|
||||
ops-01 found two defects in the 433 P1 Run A on VM 1013 (ops-0256). Row 240 (c515273db) introduced the first one.
|
||||
|
||||
1. `scripts/case1-hostcheck.sh:117` compares `dpkg-statoverride --list /usr/bin/bwrap` to the literal
|
||||
`root mosaic 0750 /usr/bin/bwrap`. dpkg 1.22.6 on Ubuntu 24.04 prints `750`, so item 5 FAILs on every
|
||||
correctly built host. The apt hook and mosaic-hostcheck.service then close bwrap and write DRIFT. The fake
|
||||
at `packages/cli/tests/case1-hostcheck.test.mjs:73` writes `0750`, so the suite missed it.
|
||||
2. `docs/guides/host-run-sheet.md:192` and `:209` run `git bundle verify` outside a repository, which fails on
|
||||
a fresh host.
|
||||
|
||||
The fix:
|
||||
- Item 5 accepts `750` and `0750` as the same mode for the statoverride line. Owner, group and path stay exact,
|
||||
and every other mode still fails. The stat check at :118 is unchanged.
|
||||
- The run sheet verifies the bundle inside an empty bare scratch repo, as `scripts/stack-copy.sh:91-93` does.
|
||||
A failure there prints a FAIL line and does not `exit` the interactive root shell (Morgana N1, sage-3743).
|
||||
|
||||
Tests are append-only blocks in case1-hostcheck.test.mjs (mos-1272 pre-approved by Mos: 0 lines deleted, the :73
|
||||
fake unchanged). They cover: a `750` statoverride passes; `0750` still passes; `755`, `0755`, `7500`, `075`, `00750`, a tab
|
||||
or doubled space, and a wrong owner or group fail. Any other change to an existing test goes to Mos first.
|
||||
|
||||
Scope add (mos-1534):
|
||||
3. F3: `scripts/case2-unit.sh:62` plain() rejects `@`, so the run sheet's required `--claude` path
|
||||
(host-run-sheet.md:85-89, `@anthropic-ai`) refuses on every host (ops-0258, section 10). plain() accepts `@`.
|
||||
The :61 comment doesn't list it as unsafe, so no requirement changes.
|
||||
4. F4: `scripts/stack-copy.sh` fails when run as mosaic from a cwd mosaic can't read: the sheet's `sudo -u mosaic`
|
||||
lines and stack-upgrade.sh AS_USER at :154 and :293. It works from any cwd.
|
||||
Specs and test terms are in mos-1534. Mos pre-approved append-only test blocks in case2-unit.test.mjs under mos-1272.
|
||||
One new N carries F1 to F4 for the #433 rerun.
|
||||
|
||||
Owner Gladstone. Reviewer Morgana. Gate: Morgana approves. Mutants are killed: dropping the `750` form, widening the
|
||||
match to any mode, rejecting `@` again in plain(), and the F4 cwd fix reverted. The touched test files run whole, and Sage's full landing gate runs every
|
||||
package suite and every `scripts/test-*.sh`. After landing, Mos sends ops-01 the new N for a full 433 P1 rerun.
|
||||
No hostcheck install on .53 or .54 until it lands.
|
||||
|
||||
Scope add (mos-1536, ops-0260):
|
||||
5. F5: on a host that follows the run sheet, `mosaic-bus@<business>` exits 3 because nothing writes
|
||||
`<dataRoot>/notify/<business>/notify.json` (bus cli.mjs:104-122 requires it, README :876-892), and case2-precheck
|
||||
has no line for it, so it passed with the bus down. The run sheet gains a step that writes binding `null` as the
|
||||
default, and case2-precheck gains a notify line with tests. Install does not write the file: the README wants an
|
||||
explicit decision. A named binding for .53 is a later per-host choice that needs Mos's go. Spec in mos-1536.
|
||||
Gate adds a mutant: the precheck notify line removed or passing on a missing file. #1816 carries F1 to F5.
|
||||
|
||||
Scope add (mos-1537, ops-0261):
|
||||
- F5 fixture: Mos OK under mos-1272 for the host(t) notify fixture in case2-unit.test.mjs and the world(t) one in
|
||||
case2-install.test.mjs (a 0700 notify/acme dir and a null notify.json, with no assertion changed or deleted).
|
||||
Conditions: the negative F5 tests build their host without the file; one append-only install test shows
|
||||
notify.json bytes and mode are unchanged after install; the fixture lines are their own follow-up commit.
|
||||
- Section 9a creates both `notify/` and `notify/<business>` at 0700 (ops-0261 measured a 755 parent).
|
||||
6. F6: sheet :235 runs `stack-copy.sh check`, which requires `$HOME/.npm-global/bin/mosaic` (stack-copy.sh:160-173).
|
||||
Nothing creates it: install runs only `copy` (:178), bootstrap has no .npm-global, and the sheet has no wrapper
|
||||
step. The sheet gains a step after install: as mosaic from cwd `/`, create the bin dir, then run
|
||||
`wrapper <sha> --create`, naming the Mos gate (stack-copy.sh:3). No requirement change. #1816 carries F1 to F6.
|
||||
|
||||
|
||||
## Brief: F7, the wrapper step's PATH and umask under sudo
|
||||
|
||||
Row: F7 for the 433 P1 rerun (ops-0271, mos-1544). Docs only. Builder: a Codex thread. Reviewer: a separate Codex
|
||||
thread (mos-1542). It can land with any row and is not N+1.
|
||||
|
||||
Defect, from ops-01 on 094124b4e: the run sheet's wrapper step (`docs/guides/host-run-sheet.md` :249-253) fails
|
||||
rc=127 `node: command not found`. `sudo -u mosaic -H /bin/sh` runs under sudo's `secure_path`, which drops
|
||||
`/opt/mosaic-stack/node-<v>/bin`, and `scripts/stack-copy.sh wrapper` reaches `scripts/instance.sh:78`, which calls
|
||||
bare `node`. The heredoc also sets no umask, so `$HOME/.npm-global/bin` came out 0775.
|
||||
|
||||
Change, inside the `MOSAIC_WRAPPER` heredoc only:
|
||||
|
||||
1. Before the `mkdir`, add `umask 077`, the same way the notify step does (:325).
|
||||
2. Before the `mkdir`, add `PATH=/opt/mosaic-stack/node-<v>/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH`, using the
|
||||
sheet's own PATH form (:69).
|
||||
3. Add one sentence above the block: the step runs under sudo's `secure_path`, so it sets its own PATH and umask.
|
||||
|
||||
Nothing else in the sheet changes, and no script changes. ops-01 is testing this exact form on 1013 (mos-1544).
|
||||
|
||||
Acceptance:
|
||||
|
||||
- The heredoc has `umask 077` and the PATH line before `mkdir`, with `<v>` written the same as the sheet's other
|
||||
node paths.
|
||||
- `git grep -n 'host-run-sheet' -- 'packages/*/tests/*' 'scripts/test-*.sh'` shows no test that reads the changed
|
||||
lines (positive control: a grep for a path a test does read). If a test does read them, stop and report.
|
||||
- BUILD.md under `agents/<seat>/work/queue-<row>/` records the diff and the grep.
|
||||
|
||||
F10, added to this row (ops-0274, mos-1548). Both are confirmed at 094124b4e.
|
||||
|
||||
- After a first install, the sheet has no `stack-release.sh adopt <business> --instance <i> --units external` step,
|
||||
so the first activate refuses `no-pointer` (`scripts/stack-release.sh:1297`, "no $POINTER; run adopt first"). Add
|
||||
that step where the sheet first moves from install to activate or upgrade. It runs on Mos's go, and Jason or Mos runs
|
||||
it (`stack-release.sh:14`).
|
||||
- The sheet doesn't say that the first `stack-upgrade.sh upgrade` after adopt needs `--no-rehearsal`. Adopt writes no
|
||||
backup, so without the flag the upgrade refuses `no-backup` (`scripts/stack-upgrade.sh:324-325`), and with the flag
|
||||
on a stack that has a backup it refuses `rehearsal-required`. Say so next to the upgrade command (sheet :210-223):
|
||||
only the first upgrade after adopt takes `--no-rehearsal`.
|
||||
|
||||
ops-01 is testing both on 1013 (mos-1548). F10 is also docs only. The grep in the acceptance list covers its lines too,
|
||||
and BUILD.md records them.
|
||||
|
||||
F11, added to this row (ops-0275, mos-1549): wait for healthz 200 before the precheck. On 1013 a precheck run 1 s
|
||||
after unit start found no `bus.sqlite` yet. Section 10 (Console start) and the `--library-status` row in section 9
|
||||
(":306, after the bus is up") get one instruction: after a unit start, wait until
|
||||
`curl -fsS -o /dev/null http://127.0.0.1:<port>/healthz` succeeds (HTTP 200) before running `case2-precheck.sh`.
|
||||
Use the same loopback form as `scripts/case1-accept.sh:458`, with a bounded retry. F11 is docs only, and the
|
||||
acceptance grep covers its lines too.
|
||||
|
||||
## Brief: F8 and F9, the library persona import runs without the clone's node_modules
|
||||
|
||||
Row: F8 and F9 for the 433 Run B rerun (ops-0273, mos-1546). Builder: a new Codex thread (mos-1542). Reviewer:
|
||||
Morgana (Claude), because the row touches the install path (mos-1542 rule 4). When it lands, ops-01 reruns Run B P1
|
||||
at that commit.
|
||||
|
||||
F8, confirmed at N = 094124b4e: `scripts/case2-install.sh:185` runs
|
||||
`node "$REPO/scripts/case2-persona-import.mjs"` from the clone (`REPO`, :38). `case2-persona-import.mjs:32` imports
|
||||
`yaml`, and a fresh clone has no `node_modules`, so the import fails `ERR_MODULE_NOT_FOUND`. The copy just made at
|
||||
`$COPIES/$COMMIT` (:178) has `node_modules` (mos-1546). ops-01 reproduced `ERR_MODULE_NOT_FOUND` from a checkout
|
||||
with no `node_modules`.
|
||||
|
||||
Fix F8 one of two ways, and say in BUILD.md which and why: run the importer from the copy
|
||||
(`$COPIES/$COMMIT/scripts/case2-persona-import.mjs`), or make node resolve its modules from the copy. The importer's
|
||||
relative import of `../packages/harness/src/bundle.mjs` must still resolve to the same commit's file. Whatever you
|
||||
pick has to work when the clone has no `node_modules` at all.
|
||||
|
||||
F9: :186 keeps only `tail -n 1` of the importer's output. For an uncaught node error that line is the
|
||||
node banner (`Node.js v24.22.1` on 1013, mos-1546), so the refusal hides the cause. Make the refusal carry the error line, for example the first line
|
||||
matching `Error` or `ERR_`, falling back to the last line. Keep it to one line. Don't put a full stack trace in the
|
||||
refusal.
|
||||
|
||||
Class check, same row: at N, `grep -n 'node "$REPO/' scripts/case2-install.sh scripts/case2-precheck.sh
|
||||
scripts/stack-copy.sh` finds only :185. Rerun that grep. Also list every clone-side `.mjs` those three scripts run
|
||||
that imports a package (a bare specifier, not `node:` or a relative path), and record the list in BUILD.md and the
|
||||
review request. Fix any other hit in this row only if it is the same defect, and say so.
|
||||
|
||||
Test: add a new, append-only test block to `packages/cli/tests/case2-install.test.mjs`. It runs the library persona
|
||||
import with no `node_modules` beside the script, and must fail at N. It needs no mos-1272 OK (mos-1546). Add a second
|
||||
assertion that the F9 refusal carries the error line, not `Node.js v...`. In BUILD.md, explain why the existing
|
||||
case2-install tests missed this. ops-01 thinks the checkout's `node_modules` hid it; check that.
|
||||
|
||||
Gate: Morgana approves the manifest. Every package suite (except queue) and every `scripts/test-*.sh` are green on
|
||||
Sage's gate, the new test fails on N, and a mutant that puts back `$REPO` at :185 is killed by the new test.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Optional tracker in the business file (Q247)
|
||||
|
||||
Written 2026-10-23 by Sage under Mos's mos-1555 (DECISIONS Q247; Jason can reverse it).
|
||||
|
||||
## Brief: tracker becomes optional in the business file
|
||||
|
||||
**Why.** Row 323 (#1819) writes the .53 Case 1 factory business file. The factory has no tracker, and .53
|
||||
has no Vikunja sync bot. Today every business file must carry `tracker.sync` with a bot and a vikunja token
|
||||
file, and both validate and boot refuse a missing token file (goldie-0009). Mos ruled the requirement an
|
||||
implementation constraint, not a PRD one (Q247), and chose an optional tracker over a dummy credential or a
|
||||
new bot (mos-1555).
|
||||
|
||||
**What changes.** A business file may omit the top-level `tracker` key.
|
||||
- Absent `tracker` means no tracker sync, no `@sync` credential, and no `@sync` registration in the bus.
|
||||
- A present `tracker` behaves exactly as today, with the same checks and the same refusals.
|
||||
- Every reader of the business tracker handles the absent case. The ones I found:
|
||||
packages/business/src/business.mjs:85-96 and :293-297 (checkTracker and the own-bot check),
|
||||
packages/business/src/cli.mjs:84-86 (validate's credential refs), packages/bus/src/runtime.mjs:131 and
|
||||
:247, packages/bus/src/broker.mjs:6123, packages/business/src/instance-settings.mjs:126, and
|
||||
packages/cli/src/config.mjs's trackerFor. Grep for the rest
|
||||
(`git grep -n -E 'tracker\.sync|\.tracker\b' -- 'packages/*/src/*'`) and list every reader in BUILD.md
|
||||
with what it does when tracker is absent.
|
||||
- A role whose definition uses vikunja still needs its own `tracker` bot (business.mjs:143). That rule stays.
|
||||
|
||||
**Tests.** New tests only. No pre-existing test changes without Mos's OK (mos-1272); if one seems needed,
|
||||
stop and tell me.
|
||||
1. A business file with no `tracker` passes the loader and `mosaic business validate`.
|
||||
2. The bus boots with that file and raises no credential-file error, and `@sync` isn't registered.
|
||||
3. Existing fixtures, all of which carry a tracker, load unchanged. The existing suites show this.
|
||||
4. One mutant that skips the presence check, treating an absent tracker as present, is killed by a new test.
|
||||
|
||||
**Gate.** New test files whole, then the whole touched test files, then the mutant, all on my CLEAR. My
|
||||
landing gate is the full one (mos-1455).
|
||||
|
||||
**Boundaries.** No host action, no credentials read, no queue writes beyond your own claimant moves, no
|
||||
push to refactor. Use a new worktree off refactor on branch `<seat>/row324`. Push the branch, then report
|
||||
the tip, base and manifest to Sage. Record design notes in agents/<seat>/work/queue-324/BUILD.md.
|
||||
Duckworth reviews, because this touches credential registration.
|
||||
|
||||
**Acceptance.** Row 323's file, written without `tracker`, passes validate and boots. Every file with a
|
||||
tracker behaves exactly as before.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Case 1 business file: project key must match the project file (#414 stage 2)
|
||||
|
||||
Written 2026-10-24 by Sage for ops-0288 and Mos's ruling mos-1563.
|
||||
|
||||
## Brief: re-key the Case 1 project to stack
|
||||
|
||||
**Why.** The .53 stage 2 run on 99930362e stopped at step 8 (ops-0288). `case2-install.sh --profile factory`
|
||||
refused with:
|
||||
`validate: mosaic business: project file /srv/mosaic/src/mosaic-stack/.mosaic/project.json has id stack, but
|
||||
business mosaic-stack declares it as mosaic-stack`. Row 323's `docs/guides/case1-business.mosaic-stack.json`
|
||||
keys its one project `mosaic-stack`. The repo's `.mosaic/project.json` has id `stack`, and the loader requires
|
||||
the key to equal that id (packages/business/src/cli.mjs:80, packages/cli/src/config.mjs:67). Row 323's test
|
||||
missed it because it validates against a scratch root with no project file, which only warns (cli.mjs:74-78).
|
||||
|
||||
**Ruling (mos-1563).** Fix the business file. Don't touch the project id: `stack` is read elsewhere.
|
||||
|
||||
**What to change.**
|
||||
1. In `docs/guides/case1-business.mosaic-stack.json`, rename the `projects` key from `mosaic-stack` to
|
||||
`stack`. Keep its `root` (`/srv/mosaic/src/mosaic-stack`). Business id stays `mosaic-stack`. Change nothing
|
||||
else in the file.
|
||||
2. A new test file, `packages/business/tests/case1-project-key.test.mjs`, that validates the committed case1
|
||||
file against the repo's own `.mosaic/project.json`, so a mismatch fails in CI instead of on a host:
|
||||
- read the repo's `.mosaic/project.json` and assert the case1 file has exactly one project whose key equals
|
||||
its `id`;
|
||||
- run the business `validate` path (the same one cli.mjs:80 runs) with that project's root pointed at a
|
||||
scratch copy holding the repo's real `.mosaic/project.json` bytes, and assert it passes with the project
|
||||
reported `valid`, not `absent`;
|
||||
- a negative case: the same run with the key set back to `mosaic-stack` refuses with the cli.mjs:80
|
||||
message.
|
||||
3. The one old-test line this change breaks, `packages/business/tests/case1-business-file.test.mjs:70`
|
||||
(`assert.deepEqual(b.projects, { "mosaic-stack": ... })`), changes its key to `stack` and nothing else. This
|
||||
edit has Mos's go (mos-1564, under mos-1272). Don't change any other line of any old test. If another old
|
||||
test reds, stop and report it.
|
||||
|
||||
**Gate.** Use `export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)` (BRIEF-TEMPLATE Gate). On my CLEAR
|
||||
only: the new test file whole, then the packages/business suite whole. One mutant: set the case1 key back to
|
||||
`mosaic-stack`, show the new test red, restore and check the bytes. The cli suite counts from my landing gate.
|
||||
|
||||
**Boundaries.** No host action, no .53 contact, no credential reads, no queue writes, no push to refactor.
|
||||
Work in a new worktree off refactor at 99930362e or later, on branch `goldie/row325`. Push the branch and
|
||||
report the tip, base and manifest to Sage and to the reviewer. Record design choices in
|
||||
agents/goldie/work/queue-325/BUILD.md.
|
||||
|
||||
**Acceptance.** At a landed commit, `case2-install.sh --business mosaic-stack --business-file
|
||||
docs/guides/case1-business.mosaic-stack.json --profile factory` passes step 8's validate against
|
||||
`/srv/mosaic/src/mosaic-stack/.mosaic/project.json`, and the new test fails if the key and the project id
|
||||
drift apart again.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Host run sheet: five operator gaps from the .53 and Run B installs (#414 stage 2)
|
||||
|
||||
Written 2026-10-24 by Sage for Mos's mos-1566 item 1, from ops-0282, ops-0288, ops-0291, ops-0292,
|
||||
Duckworth's 323 N2 and Mos's rulings mos-1563 and mos-1565.
|
||||
|
||||
## Brief: five run-sheet gaps
|
||||
|
||||
**Why.** Five things stopped or slowed operators on the .53 stage 2 install and 433 Run B. Each is a gap
|
||||
in `docs/guides/host-run-sheet.md` (checked at 3d2569ce2), not a product bug. The code they describe has
|
||||
landed. This row writes them down.
|
||||
|
||||
**What to write.** Edit only `docs/guides/host-run-sheet.md`. Source every command, path and message from
|
||||
the code or the receipts named here, and cite the line you read, the way the sheet already does
|
||||
(`scripts/stack-release.sh:14`). Don't invent a flag, path or exit code.
|
||||
|
||||
1. **B2-2, the Console proof file format** (DEFERRED.md "ops-0282 B2-2", ops-0282). In section 9's .54
|
||||
library table (:321), the proof file row's File cell gives mode and owner but no format. Add it: one
|
||||
base64url line of at least 32 characters, `[A-Za-z0-9_-]` only (packages/webui/src/serve.mjs:264). The
|
||||
Console exits 2 at start otherwise (:267). `case2-unit.sh` doesn't open the file, so a bad proof fails
|
||||
only at Console start. Read serve.mjs:255-270 yourself and match what it checks, including whether a
|
||||
trailing newline is accepted.
|
||||
2. **N2, a missing project file only warns** (Duckworth 323 round 1 N2, ROUND1-6a0f0f4f8.md). When the
|
||||
project root a business file names has no `.mosaic/project.json`, `mosaic business validate` passes
|
||||
with the warning "no project file at ... it has no project variables" (packages/business/src/cli.mjs:74-78).
|
||||
An install on such a host passes step 7 with the project's variables missing. Say so where the sheet
|
||||
describes `case2-install.sh` (near :278), and tell the operator to treat that warning as a stop.
|
||||
3. **The pre-install validate step** (mos-1563, ops-0288). Before `case2-install.sh`, check by hand that
|
||||
each `projects` key in the business file equals the `id` in `<that project's root>/.mosaic/project.json`
|
||||
in the stack's user's clone, and that the file exists. The loader refuses a mismatch
|
||||
(packages/business/src/cli.mjs:80) at step 7, after the copy is made. The check must use only what the
|
||||
host has at that point: coreutils, grep, or the pinned node under `/opt/mosaic-stack/node-<v>/bin`
|
||||
(section 2), run as the stack's user. Give the expected output for a match and say a mismatch is a
|
||||
stop: fix the business file, not the project id (mos-1563).
|
||||
4. **The retry path after a failed first install** (mos-1565, ops-0291). Section 6 has no retry form.
|
||||
What happened on .53: a refused `case2-install.sh` left `<home>/.config/mosaic-dev/config.json` behind
|
||||
(bootstrap ran at step 1), and the first refusal also left an empty `businesses/` dir in that config
|
||||
dir. The operator then used the upgrade form's fetch, which writes the bundle to
|
||||
`refs/remotes/origin/refactor` and doesn't move `refs/heads/refactor`, so `stack-copy.sh copy` refused
|
||||
with "not reachable from refs/heads/refactor". Mos's ruling: move the clones aside and rerun section
|
||||
6's first-install form. Write that as a short "If the first install refuses" block in section 6:
|
||||
- which paths move aside: root's checkout `/root/mosaic-stack`, the stack's user's clone
|
||||
`/srv/mosaic/src/mosaic-stack`, and the stack's user's config dir. Read `scripts/case2-install.sh`
|
||||
(bootstrap at step 1, :172, :200, :210-211) and `scripts/bootstrap.sh` to list what an install
|
||||
writes before it can refuse, and name only those paths;
|
||||
- move, never delete: `mv` to a name with a UTC timestamp suffix, as root, keeping owner and mode;
|
||||
- what the operator must re-place afterwards if a credential slot under that config dir was already
|
||||
filled (section 9). If nothing in section 9 can be filled before the first install, say that;
|
||||
- then rerun the first-install block from its first line. Never use the upgrade form's fetch on a
|
||||
clone that a first install hasn't finished with.
|
||||
5. **Checking a backup's SHA256SUMS by hand** (ops-0292). The sheet has no backup check today. A backup set
|
||||
under `<dataRoot>/backups/stack/<activation>/` holds `SHA256SUMS` with paths relative to `bus/`
|
||||
(scripts/stack-release.sh:482 writes `(cd "$o/bus" && sha256sum bus.sqlite) > "$o/SHA256SUMS"`), and
|
||||
the tool checks it with `(cd "$o/bus" && sha256sum -c --quiet ../SHA256SUMS)` (:502, :2244). Running
|
||||
`sha256sum -c SHA256SUMS` from the backup dir itself fails (rc 1, "FAILED"), because `bus.sqlite` is
|
||||
in `bus/`. Add a short paragraph after the upgrade exits in section 6 that gives the `bus/` form for an
|
||||
operator who wants to check a backup before a rollback or restore. Read :475-510 to confirm the
|
||||
layout first.
|
||||
|
||||
Keep the sheet's voice: short sentences, the path and line for each claim, no em dashes. Don't renumber
|
||||
sections. Don't touch any other file except your BUILD.md.
|
||||
|
||||
**Test.** None ships. Before the review request, show that no test or script reads the sheet:
|
||||
`git grep -n host-run-sheet -- 'packages/*/tests/*' 'scripts/*'` prints nothing, with a positive control
|
||||
that the same command form finds a path you know a test reads. Also grep the tests for a 64-hex pin next
|
||||
to the sheet's name. If any test reads the sheet, stop and tell Sage.
|
||||
|
||||
**Gate.** Doc only (decision 137). No suite run is needed from you, and no CLEAR. Sage's landing gate
|
||||
runs every suite (mos-1455). The reviewer checks every new claim against the cited line.
|
||||
|
||||
**Boundaries.** No host action, no .53, .54 or 1013 contact, no credential reads, no push to refactor.
|
||||
Claimant queue moves are yours (start, in-review). Work in a new worktree off refactor at 3d2569ce2 or
|
||||
later, on branch `goldie/row-run-sheet`. Push the branch and report the tip, base and manifest to Sage
|
||||
and to the reviewer. Record choices in `agents/goldie/work/queue-<row>/BUILD.md`.
|
||||
|
||||
**Acceptance.** An operator following the sheet can see the proof file format, treats the missing
|
||||
project file warning as a stop, checks the project key before install, has a written retry path after a
|
||||
refused first install, and can check a backup by hand with the form that works. Every new claim cites a
|
||||
line that says it.
|
||||
@@ -48,6 +48,17 @@ What this piece deliberately leaves alone, and where that work goes.
|
||||
Who says yes, and on what evidence: the command to run, the output to
|
||||
expect, or the comment to read.
|
||||
|
||||
Every seat, Claude or Codex, runs the suites with a short TMPDIR:
|
||||
`export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)`, 31 bytes. A
|
||||
launch refuses when its run's `tools.sock` path passes 107 bytes
|
||||
(`packages/seat/src/session.mjs:278`). Tests build that path under TMPDIR
|
||||
and drop the launcher log, so the refusal shows only as a 20-second "no
|
||||
RESULT". `packages/cli/tests/revoke-window.test.mjs` needs TMPDIR at 35
|
||||
bytes or less. A seat-named TMPDIR such as `.../tmp/goldieXXXXXX` (37)
|
||||
fails it on any commit (row 323, mos-1561). A builder whose cli suite
|
||||
can't run says so in the row, and the cli suite counts from the lead's
|
||||
landing gate.
|
||||
|
||||
### Handoffs
|
||||
|
||||
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner
|
||||
|
||||
@@ -711,3 +711,48 @@ webby/reviews c6ea27aab). docs/case2/SURVEY-RUN.md:96 documents the `--mapping`
|
||||
`scopes canon .., playground .., unmapped ..; indexed ..`, which predates the supporting scope 283 adds.
|
||||
Nothing reads the file (decision 137). Bound to the next docs commit that touches SURVEY-RUN.md: add the
|
||||
supporting count to that line.
|
||||
|
||||
## ops-0275 O2 and O3: triage, no row
|
||||
|
||||
Sage, for Mos's mos-1549 (433 factory on 1013, ops-0275). Neither is a defect, so neither gets a row.
|
||||
|
||||
O2, `bus status` shows writer.lock pid 4174 while the unit's host pid is 4078. By design. `scripts/mosaic`
|
||||
execs node (scripts/mosaic:44), so the unit's MainPID is the bus host. The host forks the broker
|
||||
(packages/cli/src/host.mjs:1, :215), and the broker's Store writes its own `process.pid` into
|
||||
`<dataRoot>/bus/writer.lock` (packages/bus/src/store.mjs). The lock holder is the host's child. The
|
||||
status line's liveness check (packages/cli/src/cli.mjs:612) reported it running, so it isn't stale.
|
||||
If an operator note is wanted, the next row that edits `bus status` output can name the broker pid as the
|
||||
host's child; nothing binds it now.
|
||||
|
||||
O3, the copy's npm step warns that @google/genai, esbuild and protobufjs have install scripts outside
|
||||
allowScripts. scripts/stack-copy.sh:74 runs `npm ci --no-audit --no-fund`. On npm 12.0.2 (dragon-lin)
|
||||
dependency install scripts are blocked by default: npm skips any dependency script with no allowScripts
|
||||
entry and lists the skipped packages at the end (`npm help install-scripts`). So the copy skips all three.
|
||||
The 433 factory passed on that copy, and the landing gates (webui included) pass with the same skip.
|
||||
Recommendation: refuse, not allow. An explicit `false` entry for each in package.json allowScripts makes
|
||||
the tested behavior the written policy and drops the warning (`npm help config`, strict-allow-scripts:
|
||||
explicit denials are always skipped silently). That edits package.json, so it waits for the next reviewed
|
||||
row that touches package.json; bound there.
|
||||
|
||||
## ops-0282 B2-2: the run sheet doesn't state the Console proof file format
|
||||
|
||||
Sage, for Mos's mos-1557 (433 Run B P1 at 3569bf54f). docs/guides/host-run-sheet.md:321, the .54 library
|
||||
table, gives the Console proof file's mode (0600 or 0400) and owner but not its format. The Console reads
|
||||
one base64url line of at least 32 characters, `[A-Za-z0-9_-]` only (packages/webui/src/serve.mjs:264), and
|
||||
exits 2 at start otherwise (:267). case2-unit.sh doesn't open the file, so a short or malformed proof fails
|
||||
only at Console start. That's what stopped Run B P1 (B2-1, ops-01's 24-byte fixture). Fix: add the format to
|
||||
the File cell of that row. Doc only, no test reads it; bound to the next doc row that edits the run sheet.
|
||||
|
||||
## Row 323: revoke-window hides a socket-path refusal as a 20-second "no RESULT"
|
||||
|
||||
Sage, for Mos's mos-1560/mos-1561. packages/cli/tests/revoke-window.test.mjs failed on every commit in
|
||||
Goldie's environment (base dd6cb5872 included) and passed in Sage's. The cause is TMPDIR length, not Codex.
|
||||
The test's root is `$TMPDIR/mosaic-revoke-window-XXXXXX`, and a run's `tools.sock` sits at
|
||||
`<root>/data/launches/acme/r<12 hex>/tools.sock`. newRun refuses past 107 bytes
|
||||
(packages/seat/src/session.mjs:278). Measured lengths: `.../tmp/sageXXXXXX` (35 bytes) gives 107 and passes,
|
||||
`.../tmp/goldieXXXXXX` (37 bytes) gives 109 and fails. Sage reproduced it with a 37-byte TMPDIR: 0/1 after
|
||||
20235 ms, the same "no RESULT". The test passes `log: () => {}` to the launcher, so the refusal never shows.
|
||||
The guard is right. The test is the weak part: its prefix leaves no margin, and a launch refusal reads as a
|
||||
timeout. Fix: shorten the prefix and fail on a launch-failed log line instead of waiting 20 s. That's an
|
||||
old-test change, so it needs Mos's go (mos-1272). Bound to the next row that edits revoke-window.test.mjs.
|
||||
Until then, docs/plans/BRIEF-TEMPLATE.md's Gate section sets TMPDIR to `mktemp -d /mnt/storage/scratch/tmp/XXXXXX`.
|
||||
+13
-7
@@ -33,7 +33,7 @@ Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
|
||||
<!-- mosaic-queue:begin -->
|
||||
|
||||
Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
|
||||
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
@@ -78,7 +78,7 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 39 | Slice 1 S4: the mosaic CLI, inbox, decide, tasks, agents, trail | rocko; reviewers darkwing, filbert | #1521 | done | 37 done | filbert approves on #1521, darkwing approves the packages/discord change and the broker host; cli tests, test-discord, every test-*.sh green (sage) | `docs/plans/2026-10-04_slice-1.md` § Slice 1 S4: the `mosaic` CLI (inbox, decide, tasks, agents, trail) | Follow-ups from round 1 (sage): (a) one host that boots every business, since writer.lock allows one broker per dataRoot while the unit is per business; accepted for slice 1 with one business. (b) the queue's PATH_RE refuses '@' in manifest paths, so the S4 unit template was renamed to mosaic-bus.service.in; packages/discord/systemd/[email protected] would hit the same refusal in any future candidate manifest. |
|
||||
| 40 | Slice 1 S5: WebUI inbox, tasks, agents and trails; CHAT-03 Gate E | dewey; reviewers darkwing, filbert | #1522 | done | 39 done | darkwing and filbert approve on #1522; webui, conversation, control-board and every test-*.sh green; row 5 Gate E is Jason's (sage) | `docs/plans/2026-10-04_slice-1.md` § Slice 1 S5: WebUI (inbox, tasks, agents and trails; CHAT-03 Gate E) | — |
|
||||
| 41 | Slice 1 S6: meta-harness and launching; the PM moves off T3 | filbert; reviewers darkwing | #1523 | done | 34 done, 36 done, 38 done | darkwing approves on #1523; harness and seat tests, every test-*.sh; recorded PM launch of a coder (sage) | `docs/plans/2026-10-04_slice-1.md` § Slice 1 S6: meta-harness and launching (the PM moves off T3) | — |
|
||||
| 42 | Slice 1 S7: acceptance, five pieces in a row | sage | #1524 | in-progress | 40 done, 41 done | Jason, from the five trails: only gated decisions reached him, every step left a trail (jason) | `docs/plans/2026-10-04_slice-1.md` § Slice 1 S7: acceptance, five pieces in a row | mos-1184 (2026-10-21T16:58Z): pieces 1-5 are Jason's requests to pm through the Console or mosaic talk pm, a route Q194 suspends until Jason says the Stack is operational. The pieces are paused, the 10-23 23:59Z date is dropped and no new date is set. mos-1148's C1/C2 rules wait with them. The 10-24 window goes ahead without the pieces (sage-1622). |
|
||||
| 42 | Slice 1 S7: acceptance, five pieces in a row | sage | #1524 | in-progress | 40 done, 41 done | Jason, from the five trails: only gated decisions reached him, every step left a trail (jason) | `docs/plans/2026-10-04_slice-1.md` § Slice 1 S7: acceptance, five pieces in a row | mos-1559 (00:00Z 10-24): the 10-23 23:59Z date passed with no pieces sent. Under the mos-1148 default the unopened pieces stay unopened. New latest-by date: Vikunja 442 due 2026-10-26 18:00Z. Pieces stay paused under Q194 until Jason says the Stack is operational; Mos raises them with Jason in the 10-24 session. |
|
||||
| 43 | S2b: gated approvals are single-use (authorize records consumption) | rocko; reviewers darkwing | #1525 | done | 37 done | darkwing approves on #1525; Sage's integration gate (sage) | `docs/plans/2026-10-05_s2b-single-use-approvals.md` § S2b: gated approvals are single-use (authorize records consumption) | 2026-10-05: S2 is in the tree at 38828a2c. Rocko's README edit (ae643fba, decision 62 human-proof note) is an uncommitted change on packages/bus/README.md and belongs in this row's candidate. |
|
||||
| 44 | S2c: a within-role message cites a decision without consuming it | rocko; reviewers darkwing | #1526 | done | 43 done | darkwing approves on #1526; Sage's integration gate (sage) | `docs/plans/2026-10-05_s2c-message-decision-citation.md` § S2c: a within-role message cites a decision without consuming it | — |
|
||||
| 45 | S4 follow-up: notifier retry limit, journal types and the round 2 test gaps | rocko; reviewers darkwing, filbert | #1527 | done | — | filbert and darkwing approve on #1527; every node package suite and every test-*.sh green on Sage's gate rerun (sage) | `docs/plans/2026-10-09_s4-follow-up-and-cohort.md` § S4 follow-up: notifier retry limit, journal types and the round 2 test gaps | lead decision 72; follows row 39 (2f5303c1) |
|
||||
@@ -252,11 +252,11 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 213 | Case 2 C2-08 creative roles, persona file and block, M1, M2, M6, M10, prompt passed by file | morgana; reviewers webby | #1714 | done | — | Webby approves the manifest. The business, bus and harness suites, `scripts/test-config.sh` and every `scripts/test-*.sh` are green on Sage's gate, and the `models` mutant is killed. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-08 creative roles, persona file and block, M1, M2, M6, M10, prompt passed by file | LANDED 361ba2387 (39 paths incl. 4 packet files). Patch-id f313e69b6f7e = c771a36f4..2b3e67e36; verify-commit ok, 35 manifest paths. Same combined gate as 195: business 200/0 bus 542/0 harness 140/0 cli 663/0, test-config 31/0 and all 10 scripts green, models mutant L1 killed. Webby N2 is row 235 acceptance (mos-1163). |
|
||||
| 214 | Case 2 C2-11a editor wikilink node, after measuring whether [[ round-trips | herb; reviewers filbert | #1715 | in-progress | — | Filbert approves the manifest. The webui suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-11a editor wikilink node, after measuring whether [[ round-trips | — |
|
||||
| 215 | Case 2 C2-04 the library entry in the business file, consumer skips, M9, mirror, library.file and library.status | herb; reviewers filbert | #1716 | done | — | Filbert approves the manifest. The business, seat, cli, project, bus and webui suites are green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-04 the library entry in the business file, consumer skips, M9, mirror, library.file and library.status | LANDED e54e05463 on 6096b051b (sage-2094). Patch-id 2f68188b6c9d equals a36523713..dde3445ab; 118 paths, blobs and modes equal; tree 5b1613245 equals merge-tree. No path overlap with rows 66/243; no hash pins either way. Gate: Filbert APPROVE rev 2924; herb-0095 grant sage-2071 2634 pass 0 fail, 1 webui skip (editor rebuild needs esbuild 0.28.2, env, not a 215 path), accepted. |
|
||||
| 216 | Case 2 C2-05a lookup index and queries | researcher; reviewers filbert | #1717 | in-review, round 1, request posted | 268 done | Filbert approves the manifest. The library suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05a lookup index and queries | Rulings on phase A: agents/sage/work/queue-216/RULINGS.md (da9555ca2). r2 rebases on row 268's landing and adds the four PRD 1.14 playground tests. |
|
||||
| 216 | Case 2 C2-05a lookup index and queries | researcher; reviewers filbert | #1717 | done | 268 done | Filbert approves the manifest. The library suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05a lookup index and queries | LANDED fbe57a05b (replay of r5 dae76ccc2 onto 8e2885d26, 3 commits, 0 patch-id mismatches), pushed 21:09Z with 582b86935. Sage joint gate GREEN 21:05:34Z on fbe57a05b, every package suite but queue and every scripts/test-*.sh green. Filbert APPROVE r2 c31384 rev 3697. N3 (get returns a universe own supporting files) binds the C2-05c row; N1 doc, N2 universeInfo echo and BUILD.md pronoun nit go to a later docs commit. |
|
||||
| 217 | Case 2 C2-06 the citation guard, the page store, the runner texts and the retry | beakley; reviewers webby | #1718 | briefed | 268 done, 283 done | Webby approves the manifest. The library, bus and harness suites are green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-06 the citation guard, the page store, the runner texts and the retry | — |
|
||||
| 218 | Case 2 C2-12 the setup sheet for Melanie and the survey run sheet | sage; reviewers webby | #1719 | done | — | Doc only. Webby approves it against C2-01's real flags. Before landing, grep `packages/*/tests` and `scripts/test-*.sh` to confirm no test reads either file. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-12 the setup sheet for Melanie and the survey run sheet | LANDED f8276c5eb (pushed, on Darkwing sessions b65f3a0e8). Webby APPROVE r1-r3 (revs 2848, 2856, 2860; r3 candidate 1a38baf5af2a = 89ba79d94). Patch-id 18ba1e40ad29 seat 5c6f73ed7..89ba79d94 equals landed b65f3a0e8..f8276c5eb; three blobs equal 89ba79d94. Gate: doc only, 0 test readers (control 3). Melanie handover waits on C2-06 and Jason (#419). |
|
||||
| 219 | Case 2 C2-09a one thread per creative instance | rocko; reviewers dewey | #1720 | done | — | Dewey approves the manifest. The bus, cli and webui suites are green on Sage's gate, along with every `scripts/test-*.sh`. Watch for hash pins on `bus.js` and `broker.mjs` (`packages/bus/tests/channel-proof.test.mjs` pins a webui file). (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-09a one thread per creative instance | LANDED 5fdfd7275 on refactor 10:40Z 10-23 (8 row commits cherry-picked onto 2efd127ce, range-diff all =, patch-ids match, tree e86a84a85 = gated merge 054da642f; top commit packet-only evidence). Gate manifest 4a2271dd: node 2523/0/2, ten shells 449/0, 24 mutants killed, audit ok (Rocko). Dewey approve rev 3507 c31108. N1 U6 persona label DEFERRED line follows. Slug fix Vikunja 590 and network leak 591 rows next. |
|
||||
| 220 | Case 2 C2-10a no raw session view (M7) and no raw Discord engine (M8) | researcher; reviewers filbert | #1721 | in-review, round 1, request posted | — | Filbert approves the manifest. The conversation and discord suites and `scripts/test-discord.sh` are green on Sage's gate, along with every `scripts/test-*.sh`. No test reaches a Discord server. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-10a no raw session view (M7) and no raw Discord engine (M8) | Rulings on phase A: agents/sage/work/queue-220/RULINGS.md (da9555ca2). Scope adds packages/business/src/library-seats.mjs (imported by path, no index export), its test, and control-board serve.mjs. r2 building as a Haiku subagent. |
|
||||
| 220 | Case 2 C2-10a no raw session view (M7) and no raw Discord engine (M8) | researcher; reviewers filbert | #1721 | done | — | Filbert approves the manifest. The conversation and discord suites and `scripts/test-discord.sh` are green on Sage's gate, along with every `scripts/test-*.sh`. No test reaches a Discord server. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-10a no raw session view (M7) and no raw Discord engine (M8) | LANDED fbe57a05b (replay of r5 739b868b6 onto 93694c518, 9 commits, 0 patch-id mismatches; tip 8e2885d26 for 220), pushed 21:09Z with 582b86935. Sage joint gate GREEN 21:05:34Z on fbe57a05b: every package suite but queue 0 fail 0 skipped, every scripts/test-*.sh rc 0. Filbert APPROVE r2 c31383 rev 3696. Researcher r5 reruns and mutants green. Filbert N1 (route test catalogue only) open, not blocking. |
|
||||
| 221 | Case 2 C2-20 Bean and Chet persona import, with a count-only dry run | gosalyn; reviewers webby | #1722 | done | — | Webby approves the manifest. The test file runs green on its own and the cli suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-20 Bean and Chet persona import, with a count-only dry run | LANDED 300766450, pushed. Cand 8f403d0d4, manifest 20f05e3a6ad5, 22 paths, verify-commit ok, patch-id dfbb754c6fb4 equals 62ddc96cc..8f403d0d4. Gate 11:07:50-11:19Z on b1fece6f0: test file alone 11/0, harness 140/0, cli 677/0, all scripts green. Brief Gate met: webby-0073 approve, test file alone, cli, scripts. No live run in the Gate; the import itself waits on Jason. N1 N2 N4 go to C2-20b, N3 to DEFERRED. |
|
||||
| 222 | Case 2 C2-03a fetch-only sync | megavolt; reviewers dewey | #1723 | done | — | Dewey approves the manifest. The library and bus suites are green on Sage's gate, along with every `scripts/test-*.sh`, and the mutant is killed. A search of the test run's output for the fixture token finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-03a fetch-only sync | sage-2024 on megavolt-0089: core.createObject=rename in the fetch argv is accepted as a brief departure, since C2-04 inspectMirror refuses nlink 2. Dewey checks it. The askpass env keys, export SAFE in project/src/git.mjs, bus/tests/library-sync.test.mjs and the SURVEY-RUN user line are accepted. The 5 s client timeout residual goes in BUILD.md and Dewey rules fix or defer. Slot after 197 proof 3 and the 249 gate. |
|
||||
| 223 | Case 2 C2-07 runner retry on a guard refusal, and the runner's notices | researcher; reviewers filbert | #1724 | blocked (from briefed): Q226: C2-07 folded into C2-06 (row 217). No work here; Jason parks it. | — | Filbert approves the manifest. The harness suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-07 folded into C2-06 | — |
|
||||
@@ -275,7 +275,7 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 236 | #436b Gitea grant path build: seat accounts and tokens through an approved grant, schema 3j-3k (design r5 1be535aca, due 10-27 at risk; live waits for Jason's section 8 approval and bootstrap step) | megavolt; reviewers dewey | #1742 | done | 210 done, 234 done | Dewey approves the manifest. The bus, business and cli suites and every scripts/test-*.sh are green on Sage's gate. Section 9's mutants are killed, N9's restart test included. (sage) (sage) | `docs/plans/2026-10-20_agent-credentials.md` § Brief: #436b Gitea grant path, build (schema 3j-3k) | Landed bd5f11ded on refactor, a fast-forward on be2b008cc. It is 08840f3c1 rebased past 194 (commit 1 unions only, 2-7 =) plus 2 stale-pin fixes and a gitea-not-configured raise test (sage-2559). Dewey approve on manifest 0b00269b. Megavolt gate under Sage CLEAR: 2c green, bus 765, business 228, cli 832, ten scripts rc=0, mutants 40/40; skips named, env-only. Overlap 13/257 clean. Schema 3j-3k. #1742 closed. |
|
||||
| 237 | Row 199 follow-up N1: one decision publishes once (publish-busy or #spent recheck) | megavolt; reviewers duckworth | #1743 | done | 199 done | Duckworth approves the manifest. The bus suite and every scripts/test-*.sh are green on Sage's gate, and the mutant is killed. (sage) (sage) | `docs/plans/2026-10-20_agent-credentials.md` § Brief: row 199 follow-up N1, one decision publishes once | Landed d7d943199 on refactor (local, push pending), patch-id c5f4beb4 = e42415935, manifest bb777cb0 blobs checked. Sage gate 01:31-01:44Z on 816d48b03+4 paths: M1 killed (bus 1/13, cli 1/8), every package suite and scripts/test-*.sh green (bus 478, cli 564). |
|
||||
| 238 | row 200 follow-up N1: the sso-config hint carries --instance NAME (#1744) | launchpad; reviewers webby | #1744 | done | 200 done | Webby approves the manifest; stack-release.test.mjs whole, cli suite and every scripts/test-*.sh green on Sage's gate; the mutant killed (sage) | `docs/plans/2026-10-20_stack-release-sso.md` § Brief: row 200 follow-up N1, the sso-config hint names the instance | Landed d49b1f2bd (pushed 07:25Z) on 86249ac55: the 3 manifest paths from 4c90099ea. Patch-id b9d0bb0ee3c7 equals the candidate's; modes and blobs match; verify-commit ok (3 paths). Sage's combined 228+238 gate on c515273db plus both rows' 6 paths, 07:11:30Z to 07:23:34Z: seat 165/0, cli 640/0, all ten scripts/test-*.sh 585/0. Mutant (sso-config drops --instance) killed, survived none. Round 1 N1 stays on the #1744 carry list. |
|
||||
| 239 | Row 194 follow-up: the credential revoke runs under the writer's lock, refusals after a revoke say so, and a stale-revision fire test (duckworth-0074, mos-1158) | honker; reviewers duckworth | #1745 | in-progress | 194 done | Duckworth approves the manifest. The bus, business, cli and webui suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) (sage) | `docs/plans/2026-10-20_agent-lifecycle.md` § Brief: row 194 follow-up, the revoke under the writer's lock and a fire test | — |
|
||||
| 239 | Row 194 follow-up: the credential revoke runs under the writer's lock, refusals after a revoke say so, and a stale-revision fire test (duckworth-0074, mos-1158) | honker; reviewers duckworth | #1745 | done | 194 done | Duckworth approves the manifest. The bus, business, cli and webui suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) (sage) | `docs/plans/2026-10-20_agent-lifecycle.md` § Brief: row 194 follow-up, the revoke under the writer's lock and a fire test | LANDED e0cb1db2c (ff from 546366f4a) 22:24:58Z 10-23. Duckworth APPROVE c31422 rev 3723. Honker slot: touched files 65/65, mutants 9/9. Sage full gate GREEN 22:24:24Z: all package suites 0 fail (bus 909, cli 976, plugins 119, webui 411) and every scripts/test-*.sh rc 0; skips are queue canonical-root and task live-provider only. Overlap: 298 shares broker.mjs, runtime.mjs, instance-settings.mjs and rebases. N+1 for 433 P2/P3 (mos-1540). |
|
||||
| 240 | Installer I4: hostcheck, the bwrap profile check, pins and boot unit, from r6 B2 (MVP, Q187, Vikunja #432; split from I3) | darkwing; reviewers filbert | #1746 | done | 233 done | Filbert approves the manifest. The test file, the cli suite and every scripts/test-*.sh are green on Sage's gate, with the mutants killed. (sage) (sage) | `docs/plans/2026-10-20_installer-rows.md` § I4 hostcheck: the bwrap profile check, pins and boot unit | Landed c515273db (pushed 06:57Z) on da3051ccc: the 9 manifest paths from ad1b23ab2. Patch-id 48e376efe8a4 equals the candidate's; modes and blobs match; verify-commit ok (9 paths). Sage's gate on 060a17b54 plus the 9 paths, 06:45:05Z to 06:56:21Z: cli 639/0, all ten scripts/test-*.sh 585/0. Darkwing's r2: singles 37/0, M1-M8 8/8. Filbert N1 and N2 in DEFERRED (da3051ccc); N3 by design. |
|
||||
| 241 | Row 237 follow-up: one decision lands once, a two-ticket broker land test and three mutants on the landing guards (megavolt-0069, sage-1651, P19 441) | megavolt; reviewers duckworth | #1747 | done | 237 done | Duckworth approves the manifest. The bus, seat and cli suites and every scripts/test-*.sh are green on Sage's gate, and the three mutants are killed. (sage) (sage) | `docs/plans/2026-10-20_agent-credentials.md` § Brief: row 237 follow-up, one decision lands once, with tests at the broker | Landed 5d4a61776 on refactor, pushed (base e65a3ab4d). Blobs from 3e3ed303e on 2 paths; patch-id a1649f80 equals git diff f5753c0e8..3e3ed303e on those paths; ls-tree modes equal; manifest e8266864 verifies on the landed files. Sage slot on bbf3b1ee8: M1-M3 killed; gate 15 packages 2797/0, 10 script suites 429/0. Duckworth approve 29870 rev 2628. |
|
||||
| 242 | Row 237 follow-up N1: a retry after publish-timeout pushes once; hold the decision or kill and wait for the push, a slow-push retry test and a mutant (duckworth-0069, P19 440) | megavolt; reviewers duckworth | #1748 | done | 237 done | Duckworth approves the manifest. The bus and cli suites and every scripts/test-*.sh are green on Sage's gate, and the mutant is killed. (sage) (sage) | `docs/plans/2026-10-20_agent-credentials.md` § Brief: row 237 follow-up N1, a retry after publish-timeout pushes once | Landed 5fb736b92 on refactor, pushed (base 96bac588f). Blobs from 635c10924 on 3 paths; patch-id ceb741ee equals git diff 8fede5fc6..635c10924 on those paths; ls-tree modes equal; manifest 1fbfab6c verifies on the landed files. Megavolt M1/M2 killed on Sage's slot; Sage gate 15 packages 2826/0, 10 script suites 429/0. Duckworth approve 29889 rev 2639. |
|
||||
@@ -319,7 +319,7 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 280 | Bus tests: the scratch sshd proves it owns its port (194 gate follow-up, MVP, mos-1270) | rocko; reviewers duckworth | #1785 | done | — | Duckworth approves the manifest. Both files run whole, the bus suite is green on Sage's gate, the 30-run repeat has no timeout, and M1 is killed. Row 194 b17's full gate then runs on top. Any red goes to Mos (mos-1270). (sage) | `docs/plans/2026-10-22_sshd-port-race.md` § Brief: the scratch sshd's port is ours before a test uses it | Landed b0fd28b6f 07:57:30Z, ff from da5d3651d: b765f5cb8 (Duckworth approve rev 3153, manifest 27806c05) + slim packet. Sage gate bus 656/0/0, ge 41, hr 26 (c30431). Repeat 2010/2010 under cli 1588/1588, no timeout; M1/M2 killed 4/4. Full evidence d6c924764 on rocko/row280-da5d-20261022. N2-N4 carry in BUILD.md. 194 b17 rebases on b0fd28b6f. |
|
||||
| 281 | A fire or a delete revokes the instance's host and Gitea grants (194/236 follow-up, MVP, mos-1271) | megavolt; reviewers dewey | #1786 | done | 236 done | Dewey approves the design, then the manifest. Bus, business, cli and every touched test file run whole, plus the Docker gitea-grant test and every scripts/test-*.sh, green on Sage's gate. M1-M4 killed. Lands before .53 Case 1 hires a granted instance. Red goes to Sage, then Mos. (sage) | `docs/plans/2026-10-22_fire-revokes-grants.md` § Brief: no grant outlives its instance | LANDED e479521d3 ff on 385a7194c (17 commits, manifest b73d4c81). Slot on 7979fb41e green: bus 819, business 231, cli 837, dewey-bus 76 with gitea-grant-docker run, shells green under 3x limits, M1-M22 killed. M1 hang fixed in b4a (mos-1273). Skips: test-task row 244 live-provider pair, test-queue canonical root. DEFERRED: N1 host-unknown hint verb, N2 fire tool text, to the follow-up row. Dewey APPROVE c30614. |
|
||||
| 282 | PRD REQ-LIB-6 rewritten to Q223 and Q226 (mos-1320 item 8) | gosalyn; reviewers webby | #1787 | done | — | Webby approves against the ruling. requirements.test.mjs and every scripts/test-*.sh that reads the PRD or requirements.json are green on Sage's gate. Jason gets a report. (sage) | `docs/plans/2026-10-20_case2-rows.md` § PRD REQ-LIB-6 rewritten to Q223 and Q226 (mos-1320 item 8) | Landed 647424442 on 86a665bdf: 1841f7d4e and 6b5b06819 rebased, patch-ids equal (4e50b4b6, 16a7a02f), range-diff =, tree 4f30ed10a = Gosalyn's gated trial tree. Webby APPROVE rev 3308 c30764 on 6b5b06819. Gate 19:06Z on the tip tree: bus+tasks requirements and tasks verbs 37/0/0, 0 skips; no scripts/test-*.sh reads the PRD (control ok). PRD 1.15. 1.16 is row 291, waits for Jason. |
|
||||
| 283 | Case 2 C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | honker; reviewers webby | #1788 | in-review, round 1, request posted | 268 done, 282 done | Webby approves the manifest. The library and business suites are green on Sage's gate, along with every scripts/test-*.sh. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | E1 (webby-0227): the manifest's existing test changes are survey.test.mjs:73 (mos-1330 re-pin, 28918824c) and business.test.mjs +4 (T7, library-supporting-other-universe). Candidate b2a1f4092. T2 receipts in agents/honker/work/queue-283/BUILD.md. |
|
||||
| 283 | Case 2 C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | honker; reviewers webby | #1788 | done | 268 done, 282 done | Webby approves the manifest. The library and business suites are green on Sage's gate, along with every scripts/test-*.sh. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | LANDED 8d2856ee3 (replay of 0bf308dba onto 59226f878, 13 patch-ids equal) plus DEFERRED N1 93694c518. Webby APPROVE r1 c31338 rev 3677, r2 c31342 rev 3683 (mos-1528 G1 mapping.test:52). Sage full gate GREEN 20:37:16Z: all package suites but queue plus every scripts/test-*.sh, 0 fail, library 98/0, webui npm ci, editor build required. supporting.test first-run red placed (mos-1272, 54d9e07bb). #1788 closed c31358. Live unchanged 45dca69f6. |
|
||||
| 284 | Case 2 C2-05c the supporting scope and blob in the lookups | della; reviewers dewey | #1789 | briefed | 224 done, 283 done | Dewey approves the manifest. Library, bus and harness suites and every scripts/test-*.sh green on Sage's gate. Grep packages/*/tests for the scope enum pin and ['canon', 'playground'] before in-review; update each. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05c the supporting scope and blob in the lookups (sage-2833) | — |
|
||||
| 285 | Case 2 C2-08b role changes for agent writes and the chief's read grant | scrooge; reviewers webby | #1790 | briefed | 213 done, 291 done | Webby approves the manifest. Business suite, test-config.sh and every scripts/test-*.sh green on Sage's gate. Grep for CREATIVE_KINDS and 64-hex pins of changed role files. Sage checks it against mos-1309 item 5. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-08b role changes for agent writes and the chief's read grant (mos-1309 item 5) | — |
|
||||
| 286 | Case 2 C2-03b push and per-seat credentials for the library mirror (with R4, mos-1323) | ludwig; reviewers dewey | #1791 | briefed | 222 done | Dewey approves the manifest. Library and bus suites and every scripts/test-*.sh green on Sage's gate, mutant killed. A search of the run output for the fixture token finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-03b push and per-seat credentials for the library mirror | — |
|
||||
@@ -334,7 +334,7 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 295 | FED R4: revocation | morgana; reviewers dewey | #1801 | done | — | Dewey approves the manifest. The bus, cli and business suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: FED R4, revocation | LANDED 2efd127ce on refactor 10:10Z 10-23 (9 commits cherry-picked onto 07802dc57, range-diff all =, patch-ids match, HEAD~1 tree a86592ffc = gated tree, top commit BUILD.md doc-only). Gate d68b4300d manifest e94c9aeb: 13 whole files, bus 886/0, business 231/0, cli 924/0, harness 151/0, all test-*.sh rc 0, M1-M16 killed, M8 per mos-1436. Dewey approve rev 3500 c31102. Re-pin precheck mos-1436 cond 11 binds. |
|
||||
| 296 | FED R7: scratch two-instance acceptance | lena; reviewers filbert | #1802 | briefed | 190 done, 294 done, 295 done | Filbert approves the manifest. The script test and every scripts/test-*.sh are green on Sage's gate, and no unit, port or socket of the script's outlives the run. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: FED R7, scratch two-instance acceptance | — |
|
||||
| 297 | GitHub grant method (Bushroot's half of #436b) | violet; reviewers dewey | #1803 | briefed | 198 done | Dewey approves the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: the GitHub grant method (Bushroot's half of #436b) | — |
|
||||
| 298 | Q228: chief-of-staff grant scope | boyd; reviewers dewey | #1804 | in-review, round 1, request posted | 252 done | Dewey approves the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: the chief-of-staff grant scope (Q228) | — |
|
||||
| 298 | Q228: chief-of-staff grant scope | boyd; reviewers dewey | #1804 | done | 252 done | Dewey approves the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: the chief-of-staff grant scope (Q228) | LANDED 860afc2b6 (ff from 833ec1cc8), pushed with tip 3569bf54f 23:32:54Z 10-23. Dewey APPROVE c31449 rev 3754. Boyd reruns 29/0, M1-M10 killed. Sage full gate GREEN 23:32:28Z on combined tip 3569bf54f (298+322+321): every package suite 0 fail 0 skip (bus 924, business 249, cli 982, plugins 119, webui 411), every scripts/test-*.sh rc 0, queue 156/0. Live unchanged 45dca69f6 schema 3n. |
|
||||
| 299 | 573: the P10 hop band (Vikunja 573) | gander; reviewers filbert | #1806 | done | 293 done | Filbert approves. The whole file under load at least 3 times, then the cli suite, are green on Sage's gate. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: 573, the P10 hop band (Vikunja 573) | landed ffb2fedfb 08:03Z on 15e688cc0 via Filbert SESSIONS c1f910b4c; 5 commits = gander/row299-gate-277ace0b 63db96d8b (range-diff 5/5 =, tree differs only BUILD-LOG/SESSIONS); manifest 277ace0b sha256 -c OK. Gate green on ffa93b054: case1 whole 3x 53/0/0, cli whole 917/0/0 (59 files), P10 inner 2.27 outer 4.01, M1-M6 killed (gander-0189). Filbert approve rev 3485 c31084; N1 M1 1.23 s margin. |
|
||||
| 300 | R6-N1: HTTP-level test of the user-session option swap (294 N1) | launchpad; reviewers duckworth | #1807 | done | 294 done | Duckworth approves the manifest; new test file whole, webui suite, mutant M12 killed (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N1, an HTTP-level test of the user-session option swap | Landed on refactor as 9d929911c..07315e651 (from launchpad/row300 a00b25b37, range-diff equal), pushed 5b846b048 12:28Z. Duckworth approved r2 (c31158, rev 3559). Sage gate on bcf3c1dac: user-option-swap 1/0, webui 375/0 with pinned esbuild and MOSAIC_REQUIRE_EDITOR_BUILD=1, M12 killed at :151. Run 1 red placed under mos-1272 (headers). N1 deferred: board proxy upstream has no recorder. |
|
||||
| 301 | R6-N4: fold user-bus.mjs back into bus.mjs (294 N4) | launchpad; reviewers duckworth | #1808 | done | 294 done | Duckworth approves the manifest; channel-proof.test, webui suite and 294 user-transport tests whole and green; re-pin only with Mos go (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N4, fold user-bus.mjs back into bus.mjs | Landed on refactor as bb031ec84..5b846b048 (from launchpad/row301 acc97fb9c, range-diff equal), pushed 12:28Z. Re-pin of webui bus.mjs to 39fcb27c is its own commit 59f0e44f4 (mos-1449); fixture stubs per mos-1451. Duckworth approved r2 (c31159, rev 3560). Sage gate: channel-proof 2/0, webui 374/0 pinned esbuild, granted-user 19/0. Live unchanged; 301 makes its own re-pin window. |
|
||||
@@ -356,6 +356,12 @@ Generated from `docs/plans/queue.json` revision 3677 by `queue render`. Do not e
|
||||
| 317 | 571 W7: human one-way chapter import and Case 2 format routing, production import closed; the first real import opens only after W8 lands (post-10-27, mos-1470) | unassigned | — | queued | 314 done, 315 done | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W7 human one-way chapter import and Case 2 format routing | — |
|
||||
| 318 | 571 W8: Console Connections view for Markdown and DOCX story files, before W7's first real import (post-10-27, mos-1470) | unassigned | — | queued | 314 done, 315 done, 317 done | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W8 Console Connections view for Markdown and DOCX story files | fenton-0017 carry: connections.test.mjs must test that links inside comments and tracked deletions are excluded. W8 follows W7's closed routing candidate and lands before W7's first real import opens (mos-1470) |
|
||||
| 319 | GitHub consumer for row 297's protected githubWithToken callback, honoring expiry and cancellation (post-10-27) | unassigned; reviewers dewey | — | queued | 297 done | Dewey approves the design and the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the signal and expiry mutants are killed. (sage) | `docs/plans/2026-10-23_github-consumer-brief.md` § Brief: a GitHub consumer for Broker.githubWithToken | — |
|
||||
| 320 | Case 1/2 host fixes for the 433 P1 rerun: hostcheck item 5 mode 750, run sheet bundle verify, case2-unit --claude @, stack-copy unreadable cwd, notify.json step and precheck, mosaic wrapper step (10-27 path) | gladstone; reviewers morgana | #1816 | required; done | — | Morgana approves. Touched test files run whole (case1-hostcheck, case2-unit, case2-install, stack-copy) and the 8 named mutants are killed; Sage's full landing gate runs every package suite and every scripts/test-*.sh. After landing Mos sends ops-01 the new N. (sage) | `docs/plans/2026-10-23_host-fixes-1816-brief.md` § Brief: hostcheck item 5, bundle verify, case2-unit @, stack-copy cwd | LANDED 094124b4e 21:53Z (replay of faf55618e onto 45e3ca7bd, code paths patch-id equal, log appends in one final commit). F1-F6 hostcheck item 5, bundle verify, case2-unit scoped paths and cwd, precheck notify decision, run sheet 9a notify dirs 0700, wrapper step. Morgana APPROVE r2 c31403 rev 3715. Full gate GREEN 21:53:00Z, 0 fail (mos-1453/1455/1456). Vikunja 614 done. 433 P1 reruns on this N (mos-1532). |
|
||||
| 321 | F7, F10, F11: run sheet wrapper step umask and node PATH under sudo; adopt step after a first install; --no-rehearsal on the first upgrade; wait for healthz 200 before a precheck (433, ops-0271/0274/0275); docs only | gladstone; reviewers boyd | #1817 | required; done | — | Boyd (Codex) approves. A git grep with a positive control shows no test reads the changed run sheet lines; BUILD.md records the diff and the grep. Doc only, lands with any row (mos-1544). (sage) | `docs/plans/2026-10-23_host-fixes-1816-brief.md` § Brief: F7, the wrapper step's PATH and umask under sudo | LANDED 5283d3745, 3fd5307e1, 3569bf54f: cherry-picks of b5d4d7903, d9400f0d3, 9aae73ade with equal patch-ids. Doc only. Pushed tip 3569bf54f 23:32:54Z 10-23. Boyd APPROVE c31450 rev 3757, grep with positive control in BUILD.md. Rode the full gate GREEN 23:32:28Z on 3569bf54f. Live unchanged 45dca69f6. |
|
||||
| 322 | F8/F9: case2-install.sh runs the library persona import from the stack copy, and its refusal carries the error line (433 Run B, ops-0273) | goldie; reviewers morgana | #1818 | required; done | — | Morgana approves (install path). The new case2-install test fails on 094124b4e and passes on the candidate; a mutant restoring REPO at :185 is killed; Sage's full landing gate runs every package suite and scripts/test-*.sh. Then ops-01 reruns Run B P1. (sage) | `docs/plans/2026-10-23_host-fixes-1816-brief.md` § Brief: F8 and F9, the library persona import runs without the clone's node_modules | LANDED 467b05fe2, cherry-pick of 7b4ea4217 on 860afc2b6; added and removed lines identical, patch-id differs only by BUILD-LOG/SESSIONS append context (both sides kept). Pushed with tip 3569bf54f 23:32:54Z 10-23. Morgana APPROVE c31451 rev 3758, N1-N3 non-blocking. Sage full gate GREEN 23:32:28Z on 3569bf54f, all suites 0 fail, every scripts/test-*.sh rc 0. Next: ops-01 Run B P1 rerun on 467b05fe2 (mos-1553). Live unchanged 45dca69f6. |
|
||||
| 323 | Case 1 factory business file for .53: docs/guides/case1-business.mosaic-stack.json plus a loader and factory-check test (#414 stage 2, ops-0277) | goldie; reviewers duckworth | #1819 | done | — | Sage full gate: all package suites except queue plus every scripts/test-*.sh (mos-1455); Duckworth review; new test file and one bus.tester mutant on CLEAR (sage) | `docs/plans/2026-10-23_case1-business-file-brief.md` § Brief: the Case 1 factory business file | LANDED 571376c30 + evidence 6a0f0f4f8 (ff from dd6cb5872), tip 99930362e. Duckworth approve c31477 rev 3778 on 6a0f0f4f8. Sage gate green 00:54Z on 6a0f0f4f8: all package suites 0 fail, every scripts/test-*.sh rc 0. Root cause of the revoke-window red: TMPDIR length, tools.sock over 107 bytes (session.mjs:278). Brief template rule and DEFERRED entry in c25799928 (mos-1561). N1/N2 doc only. |
|
||||
| 324 | Tracker optional in the business file (Q247) | goldie; reviewers duckworth | #1820 | done | — | New tests only. Absent tracker: loader and validate pass, the bus boots with no credential-file error and no @sync. Present tracker unchanged. A mutant that skips the presence check is killed. Then whole touched test files, then the full landing gate. (sage) | `docs/plans/2026-10-23_optional-tracker-brief.md` § Brief: tracker becomes optional in the business file | LANDED 7e59ba8b3 + evidence dbdfcfd52 (ff from 3569bf54f), pushed with tip dd6cb5872 00:06:58Z 10-24. Duckworth APPROVE c31470 rev 3770 (static, N1 doc only). Goldie: new 5/5 and 4/4, business.test 21/21, presence mutant killed. Sage full gate GREEN 00:05:49Z on 64ea54083: every package suite 0 fail 0 skip (business 254, cli 986, bus 924, plugins 119, webui 411), every scripts/test-*.sh rc 0. Live unchanged 45dca69f6. Run B N1 for ops-01 P2/P3 (mos-1558). |
|
||||
| 325 | Case 1 business file: re-key project to stack to match .mosaic/project.json, plus a project-file test (#414 stage 2, ops-0288, mos-1563) | goldie; reviewers gladstone | #1821 | in-review, round 1, request posted | — | New test file and packages/business whole on CLEAR; one key mutant killed; Gladstone review; Sage full landing gate (mos-1455) (sage) | `docs/plans/2026-10-24_case1-project-key-brief.md` § Brief: re-key the Case 1 project to stack | — |
|
||||
|
||||
<!-- mosaic-queue:end -->
|
||||
|
||||
|
||||
+3611
-40
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,34 @@
|
||||
# Containerized install images (I2)
|
||||
|
||||
All images use the I1 base digest
|
||||
`node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20`
|
||||
(Node 24.21.0). A base or contract change requires a reviewed commit and
|
||||
new build. Tags are mosaic-core:RELEASE, mosaic-session:RELEASE and
|
||||
mosaic-egress:RELEASE, where RELEASE is the repository file's value.
|
||||
|
||||
- core: packages, scripts, roles, contracts, production npm dependencies and
|
||||
RELEASE, under /opt/mosaic. No git is installed. NODE_USE_ENV_PROXY=1
|
||||
enables fetch proxy support; startup in I6 supplies HTTPS_PROXY.
|
||||
- session: core plus global Claude Code 2.1.296 (decision 91; harness README)
|
||||
and pi at package.json's exact pin, currently 0.85.1. Claude nonessential
|
||||
traffic is disabled. No host MOSAIC_CLAUDE_BIN is used.
|
||||
- egress: Node base, dependency-free packages/egress and baked egress contract.
|
||||
Its required argument selects agents or services. No engine socket mount.
|
||||
|
||||
`scripts/images.sh build` and `list` require a valid scratch/operator
|
||||
containerized config and rootless endpoint. Both use scripts/lib/docker.sh;
|
||||
local mode refuses before Docker. Build first verifies the endpoint, then
|
||||
copies only versioned runtime source plus I2's explicit new runtime paths
|
||||
into an ephemeral context. No .git, host credentials, agent work directories,
|
||||
or host node_modules enter the context. npm never receives a credential.
|
||||
|
||||
These are image definitions, not a deployment: I4-I6 own mounting, hardening,
|
||||
networks, process entry points and actual stack launch. All run as container
|
||||
uid 0; the later launchers must set read-only rootfs, dropped caps, no new
|
||||
privileges, resource limits and their exact allowed mount lists.
|
||||
|
||||
Verification is scripts/test-install.sh with a scratch config and explicit
|
||||
rootless endpoint. It builds images, checks pins, runs an internal-network
|
||||
CONNECT test, and exercises the Vikunja and notifier REST clients through
|
||||
the services proxy without a real credential. No broker is started behind
|
||||
that proxy by I2. The provider-host measurement is a separate Mos gate.
|
||||
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user