Compare commits
185
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
864ffce69f | ||
|
|
1367642213 | ||
|
|
cb68cf1645 | ||
|
|
78e18e3720 | ||
|
|
c051bc4395 | ||
|
|
5b7089ea14 | ||
|
|
3ec813bbc7 | ||
|
|
b855dbdc58 | ||
|
|
11e074afae | ||
|
|
f304a4bf08 | ||
|
|
a5a7173919 | ||
|
|
23f4f45dce | ||
|
|
da4c08ba9b | ||
|
|
7295e18a21 | ||
|
|
9a3321aef7 | ||
|
|
387e4653ef | ||
|
|
6a0fbf5023 | ||
|
|
12e9865ef4 | ||
|
|
ee5d48d951 | ||
|
|
f35d686722 | ||
|
|
8e557de8e5 | ||
|
|
db3ee40346 | ||
|
|
9d3f65ad21 | ||
|
|
d5d64e0c12 | ||
|
|
00bdbf843f | ||
|
|
9cedd7a6b2 | ||
|
|
2f6284bda5 | ||
|
|
0634996e8f | ||
|
|
023b7d8fee | ||
|
|
b1b6dde8e4 | ||
|
|
b023b34fe7 | ||
|
|
64447b9112 | ||
|
|
d45230b919 | ||
|
|
6884e8cd71 | ||
|
|
7ee5069a0d | ||
|
|
14bc31d258 | ||
|
|
5e00522c73 | ||
|
|
43c79fbcc5 | ||
|
|
1660d99bd3 | ||
|
|
05bdd37e99 | ||
|
|
2070f84502 | ||
|
|
a0c4bcc517 | ||
|
|
70bf03b894 | ||
|
|
01f29379bb | ||
|
|
cd0fb90e28 | ||
|
|
efe384873d | ||
|
|
a57edee74b | ||
|
|
98dc69f0ca | ||
|
|
84458606db | ||
|
|
a28967e78a | ||
|
|
8b15bb6627 | ||
|
|
25e668b849 | ||
|
|
c92311b941 | ||
|
|
6b24da6582 | ||
|
|
8d0a316ff3 | ||
|
|
f572d5e032 | ||
|
|
27923d9f1d | ||
|
|
ecd716ee6c | ||
|
|
8cd899952e | ||
|
|
4d7d698cec | ||
|
|
ad51e6b87a | ||
|
|
13cfe24f45 | ||
|
|
d7bb4a5132 | ||
|
|
871d72f5ef | ||
|
|
681011bad3 | ||
|
|
34cbb8fd10 | ||
|
|
b7191e72f0 | ||
|
|
a5f0f0ce50 | ||
|
|
884adb3ec6 | ||
|
|
da8fde7107 | ||
|
|
9baf98cd3a | ||
|
|
d3d98dc809 | ||
|
|
1e1255f41c | ||
|
|
862b0d4c03 | ||
|
|
48c21b8a58 | ||
|
|
97d49a43f0 | ||
|
|
630312f1b5 | ||
|
|
7cfc71f6e8 | ||
|
|
c8ed0d57c0 | ||
|
|
252b2f45a3 | ||
|
|
3a6d0a7118 | ||
|
|
8fe7577e65 | ||
|
|
352b475efc | ||
|
|
6d1121bf4c | ||
|
|
7045b17bf9 | ||
|
|
c000205d41 | ||
|
|
35529c7385 | ||
|
|
7f503ed6d4 | ||
|
|
f33d2d9121 | ||
|
|
21938f2be2 | ||
|
|
86a74d92f0 | ||
|
|
e15a7f25ad | ||
|
|
22c00fa9cf | ||
|
|
d1b0505e4e | ||
|
|
98f49e6b00 | ||
|
|
fa28538caf | ||
|
|
df60cae2ea | ||
|
|
13fb9abc59 | ||
|
|
02069f3148 | ||
|
|
ad3f9f31c0 | ||
|
|
8f67ec8f7b | ||
|
|
8d3fa3f202 | ||
|
|
bb5340d871 | ||
|
|
45288e5846 | ||
|
|
2c49dd80c1 | ||
|
|
8a3c24159d | ||
|
|
56f8a33a34 | ||
|
|
56d26e962a | ||
|
|
cdbf7b64ec | ||
|
|
659dd4e498 | ||
|
|
b9c7089576 | ||
|
|
28174fe08c | ||
|
|
44b252df5b | ||
|
|
d9ecec5147 | ||
|
|
6afcee2a5b | ||
|
|
58d930f893 | ||
|
|
7c8c38b698 | ||
|
|
4531834677 | ||
|
|
048d907b20 | ||
|
|
947d9ce69c | ||
|
|
f922cc51dd | ||
|
|
88402c25ee | ||
|
|
088ea2f960 | ||
|
|
10672772e4 | ||
|
|
00ba0cc5c3 | ||
|
|
bf52628f9f | ||
|
|
c8049e6d2d | ||
|
|
07fddf56f4 | ||
|
|
ffca8e4fc7 | ||
|
|
e52305c5d8 | ||
|
|
556c67fb0d | ||
|
|
4e7092478f | ||
|
|
a4691affdc | ||
|
|
634eef5605 | ||
|
|
1c58506006 | ||
|
|
db8d41a480 | ||
|
|
ee01f6f9cd | ||
|
|
8ea792256a | ||
|
|
c3dc58f307 | ||
|
|
78bdae693d | ||
|
|
9dceb21486 | ||
|
|
d20f28ea9f | ||
|
|
7490801154 | ||
|
|
d025ef492e | ||
|
|
23f5520b5a | ||
|
|
62200362b5 | ||
|
|
364c5b000b | ||
|
|
9bffb2b2a9 | ||
|
|
6711d55be7 | ||
|
|
9f4d5556ba | ||
|
|
9ba8385a9a | ||
|
|
182ff49b15 | ||
|
|
317bb05e93 | ||
|
|
56d59c4c7e | ||
|
|
4c052837e3 | ||
|
|
38cc4f114e | ||
|
|
808aa41a30 | ||
|
|
52b5fee634 | ||
|
|
8140b7f393 | ||
|
|
2a1d17da7d | ||
|
|
a76767e877 | ||
|
|
f1809238d8 | ||
|
|
606df83ba2 | ||
|
|
da03f5bf53 | ||
|
|
d273963e5b | ||
|
|
c15c70f367 | ||
|
|
c2a9fb8404 | ||
|
|
640011518b | ||
|
|
3cb6888e1b | ||
|
|
523a6bddf3 | ||
|
|
d2e305db49 | ||
|
|
8a22d08976 | ||
|
|
0b1f0336ed | ||
|
|
4065ed59f9 | ||
|
|
6ab9cd9271 | ||
|
|
912b1d325a | ||
|
|
4cf3ce2327 | ||
|
|
cb6d840df1 | ||
|
|
aa7f082bf4 | ||
|
|
b14f111362 | ||
|
|
86be181a2a | ||
|
|
4966136d40 | ||
|
|
45cbe58966 | ||
|
|
03a663cd72 | ||
|
|
b402197ff3 |
No files matched your search
@@ -25,15 +25,10 @@ green at every step. Not production software — a proven foundation.
|
||||
1. **Root is bootstrap-only.** First-class system configuration lives at the
|
||||
repository root; everything else gets a dedicated directory (`roles/`,
|
||||
`contracts/`, `missions/`, `tasks/`, `docs/`). Do not add new files to root.
|
||||
2. **Configuration**: each stack instance has exactly one system config. The
|
||||
default instance's is `~/.config/mosaic-dev/config.json`. Instance NAME's
|
||||
is `~/.config/mosaic-NAME/config.json`, and its data root carries a marker
|
||||
that names it. `~` is the home of the OS user that runs the instance. A
|
||||
process reads only the config `MOSAIC_CONFIG` names, or the default's when
|
||||
that is unset. Configs are created only by
|
||||
`scripts/bootstrap.sh [--instance NAME]`, never overwritten, fail-closed on
|
||||
any problem. Repo-scoped role authority lives in `roles/*.json`
|
||||
(versioned, reviewed commits only).
|
||||
2. **Configuration**: `~/.config/mosaic-dev/config.json` is the sole system
|
||||
config — created only by `scripts/bootstrap.sh`, never overwritten,
|
||||
fail-closed on any problem. Repo-scoped role authority lives in
|
||||
`roles/*.json` (versioned, reviewed commits only).
|
||||
3. **Secrets** never enter the repository or container images; auth is
|
||||
runtime-only (read-only mount or environment variable).
|
||||
4. **Contracts** (`contracts/`) are immutable and image-baked. Missions and
|
||||
@@ -182,8 +177,8 @@ Full reference — usage, fields, exit codes, safety notes:
|
||||
|
||||
## Data map (canon)
|
||||
|
||||
- `~/.config/mosaic-dev/config.json`, or `~/.config/mosaic-NAME/config.json`
|
||||
per named instance: system config (user-authored; never auto-written).
|
||||
- `~/.config/mosaic-dev/config.json` — system config (user-authored; never
|
||||
auto-written).
|
||||
- `<dataRoot>` (from config; default `~/.mosaic-dev`):
|
||||
- `runs/` — write-once run evidence (`result.json`, snapshots, `stderr.txt`)
|
||||
- `sessions/` — pi JSONL session trees, one directory per named session
|
||||
|
||||
-293
@@ -5174,296 +5174,3 @@ Before: managed project/thread verbs had no Console conversation area. After: fi
|
||||
2026-10-20 04:44:54 UTC | Row 178 round 2 preparation | Before: Dewey C1 five render guards lacked killing tests. Added independent refusal fixtures, a temporary uid+1 id stub with cleanup, and empty loopback proof case; activation ordering recorded in r2 BUILD. Product unchanged; syntax/text preflight only, no test or mutant execution. Pending row 181 rebase and Sage slot.
|
||||
|
||||
2026-10-20 05:10:34 UTC | Row 178 #1676 round 2 verified | Before: C1 render refusals lacked killing tests and row 181 base was pending. Rebased onto 931651f2d, kept all COUNTED/count code and tests, appended T178 tail; added independent owner/collision/absolute/charset/header and empty-loopback proof tests. Four files singly 156/156, all 24 mutants killed, metadata and sandbox fixture probes pass; scratch sweep empty. Packet agents/rocko/work/queue-178/r2/, candidate 9300a670, inventory d0dcde7b (59/59), scan 58 files no findings; in-review revs 2166-2167, expected no-token request failure. No live actions or commits.
|
||||
2026-10-21T01:28:05Z | Darkwing | row 232 #1734 installer I1 build, round 1 in review | Before: stack-release.sh managed only user units (systemctl --user), and stack-copy.sh had no bundle copy. After: --units external (adopt, activate, rollback --to, status, confirm, revert, gate --since, AWAITING-UNITS, stale-start, external-pm, --plan --expect-from) and stack-copy check --units external, copy --bundle FILE SHA, wrapper ID --create; user mode byte-identical to the base by test. First build 0af0c73c6 on 00d4cc1f6; rebased onto row 200 (cb5fdb064) as a0ffabc3f on darkwing/row232-r1; --sso refused under external (D9), row 200 config_checks run under external (D10). Gate on Sage's slot (sage-1618): cli 561/0, every scripts/test-*.sh green; 17/17 mutants killed. Results commit 84f1a91e6 (pushed, seat branch only). Manifest 7a3b4ea1 (9 paths), queue revs 2595-2596, request comment 29825, reviewer Filbert. Packet agents/darkwing/work/queue-232/BUILD.md.
|
||||
2026-10-21T04:50:44Z | Darkwing | row 233 #1735 installer I3 build, round 1 in review | Before: root had no upgrade driver for root-owned units and no host run sheet; stack-release.sh had no rehearse --backup last and no external plan refusals. After: scripts/stack-upgrade.sh upgrade and rollback (r6 A1-A5, A2f, R1-R4) with 20 tests, docs/guides/host-run-sheet.md, TOOLS.md lines, and on Sage's rulings stack-release.sh --backup last, BACKUP_STORE and the --plan refusals open-activation and migrate-required (3 tests). Base e65a3ab4d (row 232's landing). Gate on Sage's slot (sage-1704) on d99166ade: cli 616/0, every scripts/test-*.sh green, 1201/0 summed by awk; m1-m8 8/8 killed. Correction: the process sweep after the gate found an orphaned sleep 60 from the lock test's flock holder (N1); 08db6ac3f fixes it test only, Sage ruled the gate stands (sage-1707), rerun c0 20/0 and m1 killed with 0 left (sage-1708). Tip 1a1dc80ca (pushed, seat branch only). Manifest 464ce9eb (7 paths), queue revs 2644 and 2647, request comment 29901, reviewer Filbert. Packet agents/darkwing/work/queue-233/BUILD.md.
|
||||
2026-10-21T06:37:41Z | Darkwing | row 240 #1746 installer I4 build, round 1 in review | Before: root had no host check for the bwrap AppArmor profile, its pins or a boot unit, and stack-upgrade.sh exited 1 args-failed when the args move after A5 failed, while exit 1 means the old copy runs. After: scripts/case1-hostcheck.sh (seven items, --check, --enforce, --upstream), scripts/case1-pins.sh, mosaic-hostcheck.service and 16 tests; the args move failure exits 5 args-failed, documented in TOOLS.md and run sheet section 6 and tested; run sheet section 3 puts the pinned npm on PATH and names the bin/claude.exe --claude path; sections 8 and 10 have precheck lines. Verification: slot run r1 on 47ec9fe2f failed at the singles (the new N1 test's after hook threw ENOENT after the fixture cleanup removed the root); b45852daa drops the hook; r2 on b45852daa green: singles 16/0 and 21/0, c0 plus M1-M8 8/8 killed, cli 639/0, scripts 585/0. Candidate darkwing/row240-r3 ad1b23ab2 on c771a36f4, manifest 573ece95 (9 paths), revs 2694 and 2695, comment 29943.
|
||||
2026-10-21T09:00Z | Herb | row 215 C2-04 implementation started | Before: business files reject library entries and root consumers assume code projects; the organization writer can change all project mappings. Plan: approved manifest 2522025af on C2-01 a2c72ac95, Filbert reply to herb-0056, scope sage-1848/1852/1860. Focused slot sage-1869 observed expected red mapping and M9 cases, then 14/0 green for new T1/T2 cases, no skips; clear herb-0062 with empty test/fixture process scan. After that clear, T3/T4 tests and host-only mirror/bus implementation continue under sage-1874, unrun while Sage holds the slot. Full singles, suites and implementation review remain pending. No source commit, push or live change.
|
||||
2026-10-21T13:29Z | Herb | row 215 C2-04 source verified for candidate review | Before: business code assumed every project had a root and organization writes could replace project mappings. After: library mappings load without agent checkouts or publish grants, project mappings are protected before and after serialization, host-only bare mirrors share a bounded publication lock with sync, and library.file/library.status are human-only async bus reads. Base f8276c5eb preserves all nine landed overlaps. Granted sage-2008 whole singles: 16 files, 451/0, no skips or cancellations, ended 13:27:27.279728Z; all source hashes unchanged, process scan empty, no own-path drift through 8a04f4aa4. Full landing gate and implementation review remain pending; no HTTP, sync writer or live change.
|
||||
|
||||
2026-10-20 19:09:44 UTC | Gander row 196 #1694 design phase | Before: tester TCP reaches every local uid and human proofs do not bind the socket peer. After: design draft at agents/gander/work/queue-196/DESIGN.md inventories listeners, proposes private tester HTTP and SO_PEERCRED binding, aligns the per-request principal contract with Morgana and Gizmoduck, and lists migration, tests, mutants and same-uid limits. Python versus other credential options and Console provenance require Duckworth ruling. Documentation only. No product code, suite, live probe, secret access or deployment.
|
||||
|
||||
2026-10-20 19:16:20 UTC | Gander row 196 #1694 design r2 | Before: Duckworth r1 requested concurrent helper-fd evidence (B1) and selected O1 with interpreter, proof-only and image conditions. After: design adds eight concurrent genuine proof processes plus a racing copier, M20 swapped socket and post-helper socket survival, root-owned system ELF pinning and activation evidence, safe gid validation, cap traffic without helpers, and unchanged P19 R2 text. Mos accepted socket-only tester access (mos-1015 via sage-1363); any authenticated browser relay is a separate row. Documentation coverage checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:21:19 UTC | Gander row 196 #1694 design approval and N5 | Before: Duckworth approved r2 (duckworth-0009, cb6d840df) with a nonblocking interpreter-upgrade note. After: r3 chooses startup safety revalidation, appends actual interpreter identity to bus/peercred-starts.jsonl and retains old activation evidence. Operator re-pin uses the next reviewed copy, respecting already-active refusal. Added task-by-task PLAN.md for inline implementation and independent manifest review. Documentation checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:34:28 UTC | Gander row 196 build paused at design mismatch | Before: approved design promised Unix tester cleanup only for the owned inode. After: scratch test shows Node/libuv close unlinks a replacement inode before JS close handlers. Evidence agents/gander/work/queue-196/evidence/tester-cleanup-blocker.txt. Sent Sage/Duckworth gander-0016/0017, recommending explicit same-uid residual with private 0700 parent retained. Interpreter, concurrent peer, principal, human, peer-transport, transport, tester and process single files passed. Partial code uncommitted, tester transport not wired. No suites, mutant batches or live actions.
|
||||
|
||||
2026-10-21 18:45 UTC | Gander row 196 preservation before rebase | Sage-2051, Mos order mos-1193, explicitly authorizes commit and fast-forward push of the current build before deferred full verification. Product and evidence preserved together; prior focused checks and stopped gates are recorded in the row196 packet, with no claim of full acceptance. Next: rebase onto a36523713 on new gander/row196-r2, report range-diff/patch-id/conflicts, then await Sage's clear for M1-M29 and all requested suites. No live action, force push, branch deletion or tests during hold.
|
||||
|
||||
2026-10-21T16:34:44Z | Huey | Q198/Q199 system-manager design, mos-1183, Vikunja 452 | Before: pinned foundation 6c75887b0 has separate bus, development queue, run evidence, turn/cohort controls and launch/usage paths; the assignment requests a mechanical manager design. After: docs/plans/2026-10-21_q198-system-manager.md proposes admission/cooldown, priority and safe interruption, durable requests, bounded retry/DLQ/replay/rerouting, Console monitoring, optional decision adapters with USD reservations, six ordered slices and three recommended owner decisions. Verification: 26 pinned file:line references resolve, ten-line summary, manual source/contract review, git diff --check and installed queue guard pass. No builds/suites, runtime writes, credential reads, service changes or paid calls. Huey queue next refused the linked worktree, then returned nothing from canonical checkout; direct brief controls this assignment. Gitea issue and queue-row capture are handed to Sage under the no-credential-read/no-queue-write boundary.
|
||||
|
||||
2026-10-21T18:45:47Z | Huey | Q198 system-manager design r2, mos-1194, Vikunja 452 | Before: Scrooge requested changes on add7cb506 for decision-key worker exposure, cap scope and absent sampler ownership. After: distinct MOSAIC_DECISION_OPENROUTER_KEY with S6 sessionEnv canary requirement; exclusive per-instance key with per-key caps and provider limit on every enablement; S2 owns supervisor sampler and waits until after MVP/re-pin. Also records P1 interpretation, missing-confidence fallback and stand-in timeout selection, explains verified Noul spelling, excludes notifier/gateway retry loops with citations, and cites lifecycle revocation requirement. Verification: 30 pinned citation spans resolve, ten-line summary, manual diff/source review and whitespace/queue-guard checks. No builds, suites, live changes, credential reads or paid calls.
|
||||
|
||||
2026-10-21T19:56:11Z | Huey | Q198 design r3, mos-1201/1201a, Vikunja 452 | Before: Scrooge approved r2 7e785a08f with three notes. After: N1 names proposed system.health bus read verb and includes broker handler/authorization in S2 scope and review; N2 labels exclusive ownership as a setup attestation that cannot be mechanically proved and identifies provider key limit as real ceiling; N3 adds recommended Q4 for P1 narrowing and removes the Mos-flag instruction. Other design content retained. Verification: manual r2-to-r3 delta review, ten-line summary, whitespace and installed queue-guard checks. No builds, suites, live changes or paid calls.
|
||||
|
||||
2026-10-21T20:19:53Z | Huey | Q198 design r4, mos-1203, DECISIONS Q204, Vikunja 452 | Before: r3 a36952564 presented Q1–Q4 for owner approval. After: section 9 records Q20a–Q20c verbatim as approved; D11 labels Huey’s reading of configurable daily/monthly caps with $0.10/$1 defaults in the one instance config via reviewed schema; provider key ceiling and explicit enable/key requirements retained; Q4 pending, Jason restated 2026-10-21, P1 unchanged. Verification: quotes match DECISIONS ledger, unrelated sections and P1 equal r3, ten-line summary, whitespace and installed queue guard checked. No builds, suites, live changes or paid calls.
|
||||
|
||||
2026-10-21T20:48:49Z | Huey | Q198 design r5, mos-1206, DECISIONS Q205/Q206, Vikunja 452 | Before: r4 1e8bccff1 left Q4 pending and long outages could exhaust request retry budgets. After: protected mode separate from unchanged D1 human kill authority; mechanical detection, safe-point pause/proof, JEV-first endpoint-before-headless probes at 60-second increments to 600 seconds without attempt limit, upstream reset waits, shared D11 probe accounting, one test session then batches, proposed three-failed-exit escalation, frozen outage clocks/counters without DLQ, advisory online-service routing, Q4 superseded and Q22 pending. Thresholds/deadlines/batches/escalation explicitly labelled Huey readings. Acceptance proposals updated, no implementation. Verification: complete Q205/Q206 read, quotes match ledger, D1/inventory/Q1–Q3 unchanged, 30 pinned citation references resolve, 11-line summary, manual consistency/diff review, whitespace and installed queue guard checked. No builds, suites, real probes, live changes or paid calls.
|
||||
|
||||
2026-10-21T21:04:37Z | Huey | Q198 design r6, mos-1208/mos-1210, DECISIONS Q207, scrooge-0003, Vikunja 452 | Before: r5 fd44f7c60 gated probes on JEV and lacked request-local resume failure separation. After: parallel independent probes; central JEV/smaller-model/rules endpoint chain, per-model timeout/calibration, 4 KiB metadata limit and isolated fallback credentials; subscription quota vs metered USD accounting and separately enabled paid-probe budget (proposed $0 defaults). S-F1–S-F7: bounded outage classification, request-local D6/two-resume DLQ rule and batch definition; escalation latch with authorized revision-bound system.recovery.release separate from kill clear; cooperative F5 pause/F6 proof only; proposed configurable 24-hour cumulative pause expiry; full Q205 kill sentence with labelled protected-mode interpretation; r3–r6 history. S5/S6 acceptance proposals updated. Verification: Q205/Q207 quotes match ledger, D1/inventory/Q1–Q3 unchanged, 30 pinned references resolve, summary within 15 lines, manual consistency/delta review, whitespace and installed queue guard checked. No builds, suites, real probes, live changes, credential reads or paid calls.
|
||||
|
||||
2026-10-21T21:39:25Z | Huey | Q198 r6.1 touch-up, mos-1222, scrooge-0004 N1–N3, Vikunja 452 | Before: approved r6 2ee68cd69 had unprovable session-call denial wording, implicit queue/late-call behavior and a partial Q207 paragraph. After: rule/S6 tests require session decision calls/output have no routing authority; non-waiting dispatchers use internal rules immediately; timed-out JEV does not occupy fallback decision slot but retains spend reservation; full Q207 paragraph copied exactly from DECISIONS; matching Q22 reading corrected. ENV_ALLOW and all runtime code unchanged. Verification: exact full-quote equality, obsolete wording absent, D1 unchanged, summary within 15 lines, manual N1–N3 delta review, whitespace and installed queue guard checked. No builds, suites, probes, live changes or paid calls. Review/handoff via T3 as Mos authorized.
|
||||
|
||||
2026-10-21T21:11:44Z | Della mos-1213 / Vikunja 460 planning phase | Before: PRD 1.10 has no email feature; MP 95b0298 and EP 7a64921 are read-only prior inputs. Scope: independent post-v1 email plan on della/email-plan, no implementation/live access/MVP or queue changes. Gitea issue enrollment belongs to Mos/Sage because this seat has no API credential.
|
||||
|
||||
2026-10-21T21:12:18Z | Della mos-1213 / Vikunja 460 planning verification | After: docs/plans/2026-10-21_email-plan-della.md is 341 lines, with 13 post-v1 rows and six Jason questions. Verified 42 pinned citation anchors, ten requested coverage areas, identical prior src trees, append-only records, clean whitespace and repository queue guard. No configured CI pipeline or CI-queue wrapper found in the current foundation; documentation-only validation applies, no product suites or live tests run. Phase B/C and Gitea issue/queue enrollment remain with Mos/Sage. Only della/email-plan is authorized for push.
|
||||
|
||||
2026-10-21T21:37:07Z | Della mos-1220 / Vikunja 460 phase B cross-review | Before: independent plans Della 191dff32f and Ludwig 4a27b73ac delivered. After: docs/plans/2026-10-21_email-review-by-della.md reviews all 218 Ludwig plan lines, records ten findings, five grafts, six retained strengths, nine disagreements and eleven merged Jason questions. Ludwig review 143415779 read in full after ludwig-0002 arrived; reconciliation records new mail-access action, no agent reroute and correction of Della dataRoot credential example. Original plan unchanged. Planning only, no product/MVP/queue/live change.
|
||||
|
||||
2026-10-21T21:43:02Z | Della mos-1223 / Vikunja 460 phase C synthesis | Before: independent plans/reviews complete, Mos selected Della base. After: docs/plans/2026-10-21_email-plan.md merges Ludwig grafts and accepted corrections, with 16 post-v1 rows (eight READY, eight WAIT/optional), eleven deduplicated Jason questions, per-row test paths and explicit Q210 non-v1 scheduling. Original Della plan/review preserved; Ludwig originals remain on their branch. Static checks cover graft/row/question/citation anchors and original blobs. No source code, suite, mailbox, secret file, service, MVP or queue change. Candidate goes to Ludwig before Sage landing/queue handoff, copying Mos.
|
||||
|
||||
2026-10-21T21:45:20Z | Della mos-1223 / ludwig-0004 focused corrections | Before: b096a16cc left content-key location implicit and E05 core registration marked ready. After: merged plan 399 lines fixes external configDir secret key/lifecycle namespace outside dataRoot backup/restore, E01 schema and E03 missing/expired-key restore tests, and E05/E06 test-local registry readiness with core kinds.mjs registration gated on approved E01/Q4. Sixteen rows and eleven questions retained; originals unchanged. Focused static checks and queue guard apply, no product/live changes. Reviewer will check changed lines only.
|
||||
|
||||
2026-10-21T22:24:12Z | Huey | T1 item 7 sessions design r1, mos-1232, Vikunja 463 | Before: origin/refactor cca206cdc keys assistant claims/stores/messages by thread and renders Pi compaction markers; row 194 r4 excludes thread context clear. After: docs/plans/2026-10-21_t1-sessions.md proposes independent per-agent sessions within threads on Pi/Claude, scoped claims/stores/messages/workspaces, completed compaction counts, owner-write/RBAC matrix, idempotent proven-stop/fresh-successor clear, common admission/kill checks, migration and seven acceptance groups, replacement REQ-ROLE-2/REQ-THREAD-2 text and three owner questions. PRD 1.12 USER-3/4 candidate 1509c7cc5 cited separately from base. Verification: 22 pinned references resolve, eight-line summary and three exact owner quotes; manual source/consistency review, whitespace and installed queue guard checked. No code, queue/PRD edits, builds, suites, credential reads, live changes or paid calls. MVP priority ahead of Q198 S1. Review via Scrooge, then Sage/Ludwig as Mos authorized.
|
||||
|
||||
2026-10-21T22:28:56Z | Huey | T1 sessions design r2, scrooge-0006, mos-1235/1236, Vikunja 463 | Before: r1 347278044 lacked the exact per-session checkout/single-writer invariant and safe runner drain ordering, and admitted other role instances. After: F1 exact workspace path, fenced checkout setup, legacy shared-workspace quiescence, clear retains old dirty/untracked/unpushed checkout and creates fresh approved checkout, T2 mutant; F2 dequeue fence and generation-bound turn-boundary idle proof before runner signal, busy drain stays clearing, T4 cases; F3 THREAD_DEFINITION only per Mos scope ruling, no role eligibility change; N1 private titles/history vs owner-visible count/activity projection; N2 Claude offline adapter/managed-store-reader dependency flagged as MVP risk. Section 8 records Mos’s Q1–Q3 readings, no questions to Jason. Verification: 27 pinned references resolve, summary within 15 lines, source/delta consistency review, whitespace and installed queue guard checked. No code, queue/PRD edits, builds/suites/live changes/provider calls/credential reads.
|
||||
|
||||
2026-10-21T22:30:20Z | Huey | T1 sessions design r3, scrooge-0007 R1, Vikunja 463 | Before: r2 e19e30df2 nested new session checkouts under the retained legacy instance workspace, allowing project-name collision or historical parent inspection to reach live siblings. After: exact path is dataRoot/session-workspaces/business/instance/session, outside the legacy root; canonicalized ancestor/descendant overlap refuses; T2 nesting mutant covers historical mount/inspection and a legacy project named sessions. All other r2 content retained as approved, revision label updated. Verification: old nested path absent, manual R1-only delta, whitespace and installed queue guard checked; no code/builds/suites/live changes/provider calls/credential reads.
|
||||
|
||||
2026-10-20 18:51:10 UTC | Row 191 #1689 design phase | Before: per-business launch revoke and per-binding Discord STOP leave task containers, active sessions and background work without one switch. Proposed per-data-root durable halt with human-only resume, generation-fenced starts, bounded concurrent termination, write-once stop receipts and existing event mirror; no schema migration. DESIGN.md inventories starter/continuation paths and container control-mount restriction. Sent to Dewey with Sage copied; implementation waits independent design approval. No tests, commits or live actions.
|
||||
|
||||
2026-10-20 18:57:51 UTC | Row 191 design revision | Dewey r1 changes: interim Console --send absence, reserved processing events and missing-state semantics required correction. DESIGN-r2.md now names CLI interim path, unavailable UI controls, trusted initialization and fail-closed missing leaf, container delete/replace mount tests, reservations and id-bound reconciliation. Small notes addressed including diagnostic seats and aliases. Recommended smaller Docker scope to Sage; design and scope approval pending, no code or test runs.
|
||||
|
||||
2026-10-20T19:50:56Z | Rocko | row 191 #1689 implementation stage: from independent brakes to shared root state, generation-fenced launcher and tracked stops, Discord/task guards and control HTTP/UI. Evidence is agents/rocko/work/queue-191/logs/ and PROGRESS.md. Fixture single files only so far; heavy verification and candidate not complete. Scope additions sage-1367 (one read-only CODE file) and sage-1392 (processing-only client socket timeout); no schema, proof baseline, live file/unit, commit or push change.
|
||||
|
||||
2026-10-20 22:58:45 UTC | Rocko | row 191 #1689 rebase inspection candidate | Rebuilt uncommitted kill-switch tree from da230ca48 onto landed row 190 base 00d4cc1f6 under new branch rocko/row191-r2-00d4. Hand merges retained instance flags and SSO/sign-out changes; fixture tests exposed and fixed SSO signer compatibility and two unknown-runtime cleanup defects. Final touched singles 249/249 across 26 files; 15 overlapping Client reader files 102/102. Manifest a4f780c2 has 78 paths; packet 185 files verified with scan clear. Mutants and owner gate remain pending; no review-ready or activation claim, no commit/push/live action.
|
||||
|
||||
2026-10-21 23:39:21 UTC | Rocko | row 191 #1689 inspection snapshot | Sage-2242 authorized a local inspection commit for Honker's row 194 S6. Created d52b4db1c on new rocko/row191-inspection-20261021, parent 00d4cc1f6, preserving the reviewed-design build and candidate a4f780c2 (78 paths). Static manifest/index/source scan and diff check verified; no tests run, no push credential or push attempt. Existing single-file evidence is historical, 249/249 and overlapping Client readers 102/102; suites/mutants/gate remain pending. Current code has verified runtime signalling and instance halt, but no dedicated per-run stop API yet; Honker and Sage notified of that seam directly. Snapshot is not review-ready or approved for live use.
|
||||
|
||||
2026-10-22 00:04:05 UTC | Rocko | row 191 #1689 per-run stop inspection | Implemented agreed Honker-0079/0081 stopRun contract with verified per-signal identity, fixed 5s+2s, durable first-owner join and immutable result plus later-exit observations. Focused CLEAR sage-2250: final 9 files 62/62, no skipped, after correcting host guard placement and peer-discovery fixture timeout isolation; initial failures and failed teardown logs retained, only own failed group stopped. Slot returned with cwd sweep clear. Sage published f7bf7dfec; sage-2265 clean rebase creates 7aa18b0f6 on dcd8d4dda under new branch, explicit Rocko identity, all 79 manifest product blobs identical so 62/62 evidence carried. Direct S6 handoff sent to Honker and Sage. Wider suites, mutants, review and activation remain pending.
|
||||
|
||||
2026-10-22T00:18:12Z | Sage (Opus 5.5) | row 254 landing (#1759), library.sync human timeout | Before: the human transport gave library.sync the 5-second default, so a sync longer than that reported outcome-unknown, and six library-* codes reached the Console as 502. After: 331597709 lands Megavolt's candidate (7 paths, tree d49e4626f, manifest digest be63cfbe recomputed and matched). human-cli.mjs waits 130 s for library.sync, past the server's 120 s hold, and 5 s for other verbs. webui bus.mjs maps library-sync-busy, library-diverged and library-accept-stale to 409, library-accept-invalid to 400, and library-credential-missing and library-fetch-failed to 503. channel-proof re-pinned. Merged-tree gate on the landing commit: library 77/0, bus 628/0, webui 305/0 (1 skip), cli 749/0 (23:44:33-23:55:11Z). eb9672c91 adds the README request-timeout sentence (Dewey N1) and a DEFERRED entry for the four library codes still at 502 (Dewey N2, binds the Console library routes row). Overlap sweep before push: 268 rebased to r2 ecd9b35eb (README only, approval carried webby-0096), 194 rebased to b7. Done rev 3072, #1759 closed.
|
||||
|
||||
2026-10-22 00:24:34 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 full gate on c78f48946 | stopped on second integration failure per sage-2275; singles 269/269, processing-control 61/61, bus 646/646; CLI interrupted with 26 observed failures; no further edits; cwd sweep empty and slot returned to Sage.
|
||||
|
||||
2026-10-22 00:48:20 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 eb3abba12 full gate | stopped at Discord 190/192; 32 singles 304/304, processing-control 61/61, bus 646/646, cli 761/761; two uninitialized Discord fixtures reported to Sage; no later run; cwd sweep empty, slot clear.
|
||||
|
||||
2026-10-22 00:58:27 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2294 fixture fix | e6800380d on eb3abba12, two test files/four additions, assertions preserved; 338-entry static all-package sweep recorded; no tests; sent Sage for publication and Honker for dependent gate.
|
||||
|
||||
2026-10-22 01:30:04 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 e6800380d resume | binding15/15, recover5/5, Discord192/192; stopped WebUI314 passed/4 failed/1 esbuild skip; processing polling violates sign-out/manual redirect and bar changes layout; no fixes/retries, cwd sweep empty, slot returned.
|
||||
|
||||
2026-10-22 01:38:08 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2317 Console integration fix | 8e1f55c1d on e6800380d; shared sign-out/manual redirect/interval and desktop frame fixes, assertions retained, six browser cases, 73 mutant sites preflight; syntax only, no tests; sent Sage/Honker.
|
||||
|
||||
2026-10-22 02:06:38 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 rebase sage-2329 | eafcbd3e7 on ddfb29af7, new branch, dispatcher retains processing and access; CLI/readme auto-merges checked; 87-path fd0f2740 manifest and 73-site preflight; Sage2333 published, awaiting CLEAR; no tests.
|
||||
|
||||
2026-10-22 02:20:51 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 eafcbd3e7 full gate | stopped singles313/6: six new browser cases count app freshness timer as processing timer; diagnosis sent Sage, no fix/retry; no suites/shells/mutants; cwd sweep empty, slot clear.
|
||||
|
||||
2026-10-22 02:23:13 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2345 timer fixture | 4db8b5778 on eafcbd3e7, one line attributes timer to processing.js; all assertions retained, no product change or tests; sent Sage and Honker; awaiting publication/CLEAR.
|
||||
|
||||
2026-10-22 02:25:29 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2349 record commit | 0f8235277 on 4db8b5778 adds packet BUILD.md cause record; product manifest remains ffb46fbd/87 paths; no amend, product change or tests; Sage/Honker notified.
|
||||
|
||||
2026-10-22 02:48:36 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 0f8235277 full gate second red | singles330/330, processing61/61, bus646/646, cli783/783, discord192/192; WebUI323/1/1 host esbuild skip; shell geometry1062vs1000; stopped/no retry, no later suites/shells/mutants; cwd sweep empty, slot returned, Sage escalates Mos.
|
||||
|
||||
2026-10-22 02:57:36 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 mos-1248 desktop layout restart | FF 5cd32f4c1 on 0f8235277: desktop grid allocates processing bar, existing shell assertions kept, visibility/no-scroll assertions added; 74 static mutant sites; candidate 2f93ef19; sent Webby/Sage for static review; no runs, awaiting review and CLEAR.
|
||||
|
||||
2026-10-22 03:11:09 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 static nav layout, mos-1249/1250 | FF da4334806 on 5cd32f4c1; controls first in nav, non-sticky per sage-2386/webby-0108; candidate-only placement assertion changed, landed shell assertions preserved; 77 mutants statically preflighted; manifest 245dceb1; Webby/Sage handoff sent, no runs, awaiting static look/publish/debug CLEAR.
|
||||
|
||||
2026-10-22 03:15:11 UTC | Rocko (d99326cc) | row 191 sage-2394 debug | da4334806 shell5/5 and processing-browser9/9, awk14/0/0; geometry heights reported with read-only distinction; cwd sweep[] and slot returned; full window awaits separate CLEAR.
|
||||
|
||||
2026-10-22 02:01:30 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 202 #1700 FED R5 landing | ddfb29af7 on 9c9732983: 6 manifest paths from a74cd9f87 (digests match CANDIDATE.sha256, patch-id 9d89077b equal); Duckworth APPROVE r5 rev 3026; C1 gate by Quackerjack on merge 3b86180b9: cli 771/771 skip 0 (run 2, run 1 red on gate TMPDIR length), 10 test-*.sh rc 0, M1-M13 killed; done rev 3108; landing note refused for length before the done move, so the record is the commit body and #1700 c30265; 191 and 194 rebased onto it (one scripts/mosaic case line)
|
||||
|
||||
2026-10-22 03:35:55 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 273 #1778 activate --send/--no-sso landing | 0bbf0f6d1 landed on 378a76b85, a rebase of cac7d0f67 (0042f713d) with all 6 range-diff pairs = and equal patch-ids; 4-path manifest e1031023 unchanged; Filbert APPROVE rev 3111 (c30308) carried merge-scoped under mos-1160, carry comment c30319 names 0bbf0f6d1. Gate on 0bbf0f6d1 by Darkwing: stack-release.test.mjs 218/218 skip 0, stack-release-external.test.mjs 25/25 skip 0, test-release.sh rc 0 (selftest 14/0, no skips), T58 controls c0 11/11 pass and c1 11/11 fail, M1-M7 killed. Rerun because row 202 changed scripts/mosaic, which stack-release.test.mjs copies. Red 1 was 978143bf4 (T53 test 6 usage pin). This doc commit adds the TOOLS.md usage line and F39 rules (Filbert N1).
|
||||
|
||||
2026-10-22 04:02:12 UTC | Rocko (d99326cc) | row191 full window0fa7bf421 | touched35files336pass, packages2403pass/1esbuildskip, shells425selftests+156queue node pass; SR01–07killed, SR08timeout,69unrun; stopped/no retry;88blobs restored,cwd sweep[],owned network removed,slot returned Sage; await Mos ruling.
|
||||
|
||||
2026-10-22 04:14:56 UTC | Rocko (d99326cc) | row191 control/resume sage-2427 | control12/12;SR08separate timeoutkill;49resumed assertionkills thenKS43survived;total56assertionkills+1timeout,1survivor,19unrun;88blobs restored,cwd sweep[],slot returned,Sage/Mos informed;no retry/edit.
|
||||
|
||||
2026-10-22 04:21:30 UTC | Rocko (d99326cc) | row191 KS43+19 retarget run | KS43assertionkill thenKS44invalid mutation syntax;57assertionkills+1timeout,1invalid,18unrun;stopped,no edit/retry;88hashes restored,cwd sweep[],slot returned,Sage/Mos notified.
|
||||
|
||||
2026-10-22 04:30:06 UTC | Rocko (d99326cc) | row191 KS44+18 resume | 10assertionkills thenKS54timeout-without-control despite dependency assertion;total67assertionkills+1timeoutkill,1unqualifiedtimeout,8unrun;88hashes restored,cwd sweep[],slot returned,Sage/Mos notified,no retry/edit.
|
||||
|
||||
2026-10-22 04:41:46 UTC | Rocko (Codex gpt-6.1-sol,d99326cc) | row191 #1689 builder complete/in-review | published0fa7bf421,manifest0bcbf337;35singles336pass,9packages2403pass/1knownskip,10shells425selftests+156queue nodepass;75assertionkills+2controlledtimeoutkills=77;88hashrestore,cwd sweep[],slotreturned;in-review rev3114,expected no-token failure3115(exit1);Sage/Dewey handoff,no live action.
|
||||
|
||||
2026-10-22 05:03:18 UTC | Rocko (Codex gpt-6.1-sol,d99326cc) | Vikunja502 processing test/runner hardening | brief+isolatedrocko-v502;expected wordingred→green,Node27/27,Python4/4,5/5assertionkills(includingSR08andKS54nohang),0skips/timeouts;sweep[],candidate5hashrestored,slotreturned05:01:29Z;candidate968d6ee3,runnerc5bdc764;packetready,no commit/push/liveaction.
|
||||
|
||||
2026-10-22 05:07:34 UTC | Rocko (d99326cc) | Vikunja502 committed/review handoff sage-2482 | newbranchrocko/vikunja502-ce6a-20261022,source8e3c6a79b+packet9bf4683c6 on ce6a96ad6;5paths no base drift,range-diff=,patch-id37135ebe unchanged;manifest968d6ee3,packet93889519 verifies39/39;focused runs carry;Sage publish/Dewey review handoff sent,no push/liveaction.
|
||||
|
||||
2026-10-22 05:10:00 UTC | Sage (1ef1e4f8) | serial landing 205 then 268 | 205: 3 commits, 4 paths, digest 858463b1, every scripts/test-*.sh green on ce6a96ad6, Duckworth N1-N4 carried (N1/N3 to 205b or R3, N2 to the 193b2 brief, N4 #1782 before any user thread goes live). 268 r4: c27207f6e on 205, 9 paths, digest caa0fa4e, patch-id 2942acc8 equal to r3. Overlap checks clean before both pushes. No live action; LIVE stays 86f95bb27.
|
||||
|
||||
2026-10-22 05:58:59 UTC | Rocko | row 279 #1784: before, deadline overshoot skipped final observation and returned unknown; after, one-line for (;;) retains final mapping and unchanged unreachable fallback. Regression red as specified; whole stop-run 13/13 and processing-control 62/62; M1 assertion kill, no skips, restored and swept. Source 1016b8584, packet cad927137; manifest 312ed511d8f1cff826ee2fd547de8368923130c7bfe06236d8e29d8655b80aaa. Pending Sage publication and Dewey review; no live action.
|
||||
|
||||
2026-10-22 06:15:54 UTC | Sage (1ef1e4f8) | row 279 #1784 landed (mos-1269) | Before: stopRun returned unknown for a live run when a sleep overshot the deadline. After: LANDED ceadad993 as a carry of cad927137 onto 343b6dd76 (range-diff 1:= 2:=, patch-ids 6d8ae7f4/e484c297 equal, Dewey carry c30410). Sage gate on cad927137: business 209/0/0, cli 794/0/0, discord processing + processing-stop 9/0/0. Correction: the first discord run wrote to a wrong out path and ran nothing; the rerun is the result. Overlap sweep clean (13, 257). Note rev 3144, done rev 3145. No live action; LIVE stays 86f95bb27.
|
||||
|
||||
2026-10-22 06:37:43 UTC | Sage (1ef1e4f8) | row 194 b16 third red (mos-1269/mos-1270) | Gate on ceadad993 + 9cdc47d04: business 228/0, cli 826/0 (T9a passes), control-board 124/0, bus 705/1 at grant-executor.test.mjs G13 (timed out 10044 ms); conversation, webui and scripts/test-*.sh never ran. runb8 green 436/0/0, 17/17 mutants. Correction to sage-2524 (mos-1270): the stuck wait is the first until at :733, the holding file after decision.resolve, so the executor never started the install and hosts.revoke at :734 never ran; it is not a revoke-path hang. Ruling: a diagnosis run, 30 base + 30 candidate alternating under cli load, no gate retry.
|
||||
|
||||
2026-10-22 07:51:04 UTC | Rocko | row 280 #1785: before, any TCP connect could accept a port our sshd failed to bind; after, attempt-local exact log proof plus liveness and connect, non-ephemeral picker, bounded scratch remote sleep cleanup. Both occupied regressions red on old acceptance; CRLF build failure captured and fixed under sage-2535. Committed source b765f5cb8, packet d6c924764, manifest27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5. Repeats07:29:03Z–07:48:38Z grant1230/1230,host780/780,CLI1588/1588; final M1/M2 four assertion kills,0skips/timeouts,sweepclear. Sage publishes/gates; Duckworth reviews.
|
||||
2026-10-22 08:00:02 UTC | Sage (1ef1e4f8) | row 280 #1785 landed | before: the bus tests' scratch sshd helper returned a port on a connect alone, and 194 b16's gate hung at G13 (base hung too, honker-0127). After: b0fd28b6f on refactor, a ff from da5d3651d (b765f5cb8 + Rocko slim packet; full evidence d6c924764 on rocko/row280-da5d-20261022). Duckworth approve rev 3153 (manifest 27806c05). Sage gate bus 656/0/0, ge 41/0/0, hr 26/0/0 (c30431). Rocko repeat 2010/2010 under cli 1588/1588, no timeout; M1/M2 killed 4/4. Two build-time slips recorded in Rocko's packet: a runner root path (no test ran) and the CRLF sshd log line. Overlap 13/257 clean. Note rev 3154, done 3155, #1785 closed c30432, P19 511 comment 623. 194 b17 rebases on b0fd28b6f (sage-2542).
|
||||
|
||||
2026-10-22 08:38:11 UTC | Sage (1ef1e4f8) | row 194 #1692 landed | before: 194 b16 9cdc47d04 went red three times (stopRun deadline, then the bus test sshd port race), fixed by rows 279 and 280. After: fd6dd327c on refactor, a ff on e122aad8e. b18 is b17 3f451772d rebased after my doc and queue push blocked a ff (16 =, series patch-id 64303253f8e6, packages equal b17). Webby approve rev 3160 on manifest 29f131ab. Honker gate on fd6dd327c: runb8 503/0/0, mutants 17/17; 18 packages 3668/0, ten scripts rc=0, Docker up, env skips named. The secret scan hit three fake_*_token fixtures in bus tests (false positive). Note 3161, done 3162; #1692 closed c30435; P19 414 comment 625. LIVE still 86f95bb27.
|
||||
2026-10-22 08:42:04 UTC | Honker (Claude Opus 5.5, ecd17786) | row 194 #1692 agent lifecycle build | before: no Console or CLI lifecycle for agents (create, launch, terminate, regenerate, delete) and no per-agent hire/fire veto. After: 16 commits landed as fd6dd327c (b18, a rebase of b17 3f451772d on e122aad8e: 16 =, series patch-id 64303253f8e6, packages equal b17). Manifest 53 paths, digest 29f131ab. Final run on fd6dd327c: runb8 39 files whole 503/0/0, mutants 17/17 killed, restores matched; gate.sh merge onto e122aad8e, 18 packages 3668/0, ten scripts rc=0, Docker up, four env skips named (webui esbuild pin, queue render outside canonical root, test-task live provider x2); sweep clean. Corrections: b16 went red at G13; my diagnosis (60 runs, base hang on port 38927 sshd bind EADDRINUSE) placed it outside 194, fixed by row 280; the g13diag load kill missed its process group and overran about 8 minutes; one runb8 launch failed rc=126 on a lost exec bit before any test ran; the b17 runb8 was stopped unscored when refactor moved. In-review revs 3156/3157, Webby approve rev 3160.
|
||||
2026-10-22 09:43:34 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 236 #1742 Gitea grant landing, and row 281 filing | 236 landed bd5f11ded at 09:37:31Z, a fast-forward on be2b008cc: 08840f3c1 rebased past 194 (commit 1 unions only, 2-7 =), e40b74beb for two stale out-of-manifest pins, bd5f11ded for a gitea-not-configured raise test. Dewey approve rev 3168 on manifest 0b00269b (34 paths). Megavolt gate under my slot: 2c bus 131, cli 224, route 28, docker 1; whole bus 765, business 228, cli 832, ten scripts rc=0; mutants 40/40; skips named, env only. Overlap with 13 and 257 clean. Note 3169, done 3170; #1742 closed c30451; P19 436 comment 639. Correction: the first whole run on ce74477e1 went red 762/764 on the two stale pins; that run is void. Follow-up per mos-1271: row 281 (#1786), a fire or delete revokes host and Gitea grants, Megavolt owns, Dewey reviews; brief docs/plans/2026-10-22_fire-revokes-grants.md; DEFERRED entry points at it.
|
||||
2026-10-22 10:30:51 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 274 #1779 FED R3 broker user principal landing | 274 landed 4c485c63b at 10:28:26Z, a fast-forward on 47a35bf43: six Morgana commits d20c92bf6..4c485c63b. r5 f58cfc361 moved covers, LEVELS and the all marker into business/src/vocabulary.mjs so the broker keeps its sandbox import closure (M16/M16b/M16c). r6 4c485c63b pins USER_VERBS as the ruled seven and widens the R3 off-list set. Dewey approve rev 3178 on manifest 00047b52 (13 paths), comment 30487. Morgana gate under my slot, 10:09-10:22Z: 23 touched files 0 fail, bus 787, harness 151, business 228, cli 832, seat 167, ten scripts rc=0, mutants 22/22; skips named, env only (test-queue 1, test-task 2). Overlap with 13 and 257 clean. Note 3179, done 3180; #1779 closed c30491; P19 414 comment 646. Corrections: the r5 gate on f58cfc361 went red (M3x/M6x survived the R3 off-list set), void; my gate comment 30490 said 24 touched files, the count is 23; the kit comment guessed human-required for the M3x wall, the run code is not-holder.
|
||||
2026-10-22 10:57:34 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 256 #1761 the human deletes a creative instance, landing | before: row 194's deleteRefusal refused every END_PROTECTED definition for every actor, so nobody could delete a creative instance; the human could only delist one with launch: false. After: 256 landed f12441d4f, a fast-forward on 0165be266 (the 274 landing plus its docs and queue commits): four Honker commits b5f1e09d6..f12441d4f, patch-id b28e63746f8c. One protectedRefusal predicate serves delete and the listing's per-viewer endProtected; any non-human actor gets organization-creative-human-only, fire unchanged. Rebased twice (past 236, then past 274), each range-diff 4/4 =, carries checked by Webby (webby-0126, webby-0128). Webby APPROVE on manifest-r3 01eb75aa (10 paths), comment 30519, posted by Sage as jarvis because Webby has no token; review request c30518 (Sage, revs 3183/3184) after Honker's request failed (no token, revs 3181/3182). Gate by Honker under Sage's slot 10:33Z-10:52Z on merge tree ced6bf5f8: touched files 31/0/0, 18 packages 3757/0 (bus 788, business 231, cli 832, webui 335), ten scripts/test-*.sh rc=0, mutants M1, M2, M3, M4a, M4b killed with blob-checked restores. Skips, named and environment-only: webui editor rebuild byte check (esbuild 0.28.1 installed, pin 0.28.2), test-queue non-canonical root, test-task live provider x2. Overlap with in-review rows 13 and 257 clean. Order change sage-2594: 256 landed before 277 because 277's gate had not run. LIVE is still 86f95bb27. Queue note rev 3186, done rev 3187; #1761 closed c30520; P19 416 comment 649.
|
||||
2026-10-22 11:19:28 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 277 #1782 the owner sees a thread agent's claims, landing | before: a thread run's role.claim wrote thread_claims only, and the agents verb read role_claims only, so the owner had no way to see a thread agent's claim (PRD :1083-1084). After: 277 landed 62f53b1e0, a fast-forward on 46dedf925 (the 256 landing plus its docs and queue commits): seven Morgana commits 1d5d00939..62f53b1e0, range patch-id 7f0533520bd2. A bus verb thread.claims serves human and reader sessions; mosaic agents adds thread lines without --tester or --json, --json keeps the agents array, the tester path makes one call, a refused thread.claims prints the role lines and the refusal code, and an error with no bus code still throws. Rebased onto 0165be266, then onto 46dedf925 after 256 landed first (sage-2594); both range-diffs all =, manifest e281aa2c unchanged (duckworth-0126). Duckworth APPROVE comment 30539, recorded rev 3192, posted by Sage as jarvis; review request c30538 (revs 3190/3191) after Morgana's request failed (no token, revs 3188/3189). Gate by Morgana under Sage's slot 11:01:23Z-11:13:56Z: 7 touched files 0 fail (thread-claims 5, message-filter 6, user-principal 22, format 4, tester 11, other-tree 4, host 43), bus 793, cli 836, harness 151, business 231, seat 167, ten scripts/test-*.sh rc=0, M1-M9 killed on committed bytes. Skips named, environment only: test-queue non-canonical root, test-task live provider x2. Overlap with in-review 13 and 257 clean. Duckworth N1 (M8 dies on a rethrown error) to DEFERRED; 0123 N3 per-session claims binds 264a r7. Note rev 3193, done rev 3194; #1782 closed c30540; P19 506 comment 652. Also landed as doc-only picks: Honker's 256 BUILD.md gate results (80620a3a0 as b83173305) and Megavolt's 236 BUILD.md runs (9c6e1a165 as 01ea1fb11). LIVE is still 86f95bb27.
|
||||
|
||||
2026-10-22T11:37:16Z | Gander row278 #1783 implementation prepared, verification held | Before: owner thread claims were visible through row277 but absent from the Console Agents view. After: prepared a separate four-field table, a Console-owned read adapter, section-local refusal handling and an explicit tester refusal before busRead. Ten tests and six unique check definitions are prepared but unrun. Base moved from980cec88c to6af85df4b under sage-2625 on new gander/row278-b; no candidate path overlapped. No Node process, syntax check, test, browser, queue command, push helper or live action ran. Next: Sage CLEAR for the queue claim and gate; Webby reviews the verified manifest.
|
||||
|
||||
2026-10-22T12:09:45.285844+00:00 | Gander row278 run1 void, loader fake repaired | First new-file run stopped3/1/0 because the fake omitted BusReadError imported by base chat.mjs. Sage-2638 places the first-own-test red and permits the assertion-preserving export; test-only commit964b6b184. Runtime bytes unchanged. All run1 results void, no timeout or run leftovers. Run2 must restart every step after busy CLEAR; a second red goes to Sage then Mos before any change. No product acceptance or push claimed.
|
||||
|
||||
### 2026-10-22 | Gander | row278 gate run3
|
||||
|
||||
Fresh gate on3e4c77ac6 under sage-2648: bus793/0, WebUI345/0/1 (esbuild version skip), all ten shell files425/0, touched files15/0, M1-M6 killed6/6. No timeout; sweep0 run-created leftovers. Runs1/2 void. Seven-path manifest8fa859a0 goes to Webby for final review. No landing or live change.
|
||||
|
||||
2026-10-22 11:29:09 UTC | Rocko | V502 carry/rerun on980cec88c: source407c8d7be and packet04a86cf33 match approved8e3c6a79b/9bf4683c6 patch-ids, no conflicts. Dewey0834 carry conditioned on rerun, satisfied under sage2616: stop-run13,processingHTTP6,bustasks9 allgreen (Node28/28),Python4/4,0skips/timeouts. Manifest5758d699,source5/5restored,cwdsweep[]. Doc rerun packet6af85df4b explicitly pushed to rocko/vikunja502-980c-20261022; no live action.
|
||||
|
||||
2026-10-22 11:30:17 UTC | Sage | Vikunja 502 (row 191 follow-up, no queue row) LANDED 6af85df4b, a fast-forward from 980cec88c. Before: the processing failure diagnostics and cleanup in stop-run, processing HTTP and bus tasks tests were Dewey-approved on the pre-194 base only (8e3c6a79b/9bf4683c6). After: Rocko's picks 407c8d7be/04a86cf33 carry the same patch-ids (37135ebe/0dbcda54), plus a doc-only rerun record 6af85df4b. The rerun on the new base was green: stop-run 13/13, processing HTTP 6/6, bus tasks 9/9, Python 4/4, 0 skips. Dewey carried the approve (dewey-0836). mosaic-task.mjs is unchanged, so N7 (runs suite) didn't trigger. Overlap with in-review rows 13 and 257 was clean. LIVE is still 86f95bb27.
|
||||
|
||||
2026-10-22 13:10 UTC | Sage | Rows 278 and 281 LANDED. Before: refactor at 6af85df4b (V502). Row 278 (#1783, Gander, Webby): the Console Agents view shows thread claims in their own section. It landed as 385a7194c at 12:39Z, ff, 9 commits, manifest 8fa859a0. Run 3 was green: bus 793, webui 345/0/1, ten shells 425/0 under the 3x limits (mos-1275), M1-M6 killed. Runs 1 and 2 were void, on fixture fixes 964b6b184 (placed by Sage) and 9696d1fcb (ruled by Mos, mos-1275). Webby approved (c30588, rev 3200). Done rev 3202, #1783 closed c30589. Row 281 (#1786, Megavolt, Dewey): a fire or a delete revokes the instance's host and Gitea grants. It landed as e479521d3 at 13:06Z, ff, 17 commits, manifest b73d4c81. Slot on 7979fb41e: bus 819, business 231, cli 837, dewey-bus 76 with the Docker gitea-grant test, shells green, M1-M22 killed. The M1 hang was fixed at its cause in b4a (mos-1273). Dewey approved (c30614, rev 3207). Done rev 3209, #1786 closed c30621. Skips on both: test-task's row 244 live-provider pair and test-queue's canonical-root check, plus webui's esbuild rebuild test on 278. After: refactor at e479521d3. DEFERRED moves the grant gap to Done and opens Dewey's N1/N2 text gaps. LIVE is still 86f95bb27.
|
||||
|
||||
2026-10-22 14:18:23 UTC | Sage | Row 264a LANDED (#1769, Gizmoduck, Duckworth): sessions per thread, schema 3k-3l. Before: refactor at e479521d3 (row 281). It landed as 84f53540d, ff, 21 commits, manifest 257408e9 (Duckworth approve rev 3214, comment 30640). Gate r13 on 2cc34aef8: 20 touched files, bus 827, cli 837, webui 345/1 named skip, ten shells under 3x limits, mutants M1-M4 killed on assertions. Corrections on the way: the kit stopped on first red, so four reds surfaced one at a time. r10 later3l missed a trigger (mos-1276). T2 had failed since r2 because refusals log action.refused (mos-1277, my sage-2257 wording). threads.test:341 kept the old trigger text (mos-1278). M2 was caught by the store trigger before any assertion, so it now drops the trigger too (mos-1279, four files with the SCHEMA_SHA256 pin). Deferred: actor_run pin binds row 269, esbuild skip to a later gate, session-scoped user reply binds row 272. No live change; LIVE is still 86f95bb27.
|
||||
2026-10-21 21:24:47 UTC | Gander row253 #1758 design r1 | Investigation began21:12Z under sage-2162; recorded now. Before: case2 installer still renders tester TCP and case1 acceptance requires it. After: proposed design covers socket marker migration, actual configured dataRoot within unchanged hardening, target-uid metadata queries, kernel-peer acceptance observation and explicit same-uid residual. Initial inventory saved; no passing final static-check claim, product edit, test, host or live action. Duckworth design review precedes code.
|
||||
|
||||
### 2026-10-21 row253 implementation prepared, gate pending
|
||||
|
||||
Gander: after Duckworth-0100 design approval with C1-C5, prepared the two host-script socket migrations, fake fixtures, updated retirement docs and 33 unique change checks. Static syntax/diff and source inventory checks pass; behavioral tests have not run. Sage-2191 queues the requested full scope and will send CLEAR. No host, unit or live config changed.
|
||||
|
||||
2026-10-22 02:13:30 UTC | Gander row253 C4 committed candidate, gate pending | Sage-2335 explicitly directs committing runnable source and tests before the whole gate. The repair implements the approved after-uid supervisor with separate inner/outer bounds; the static packet is b64aa316a. Current repaired bytes have run no syntax check, test, probe or host experiment. Earlier static syntax results apply only to the old build. Results from 8a8df4171 are historical and do not carry. Duckworth static verdict and Sage CLEAR precede all gate execution. F5 is row276 / #1781 in DEFERRED.md.
|
||||
|
||||
2026-10-22 14:45 UTC | Gander row253 focused27 on finalaca16404c base | Fresh0604d91fc / manifest573c51cf:27 whole files604/0/0, no timeout/retry, case134/0 in99.281s, case243/0 in66.418s, stack-release includingT46221/0 in583.453s. N9 sandbox bind ran and passed, no focused skips. Seven source blobs unchanged; sweep0 run-created leftovers, pre-existing infrastructure receipts retained. Whole gate and M1-M40 await separate Sage CLEAR after Duckworth rebase check. No live action.
|
||||
|
||||
2026-10-22 14:46 UTC | Gander correction to the row253 focused27 entry immediately above | stack-release whole was218/0/0, not221/0. The27-file sum604/0/0 and duration583.453s are unchanged; raw files/results.json and SUMMARY.json give the exact per-file totals.
|
||||
|
||||
2026-10-21T16:04:23Z | Q195/Q196 writing editor design, #1752 / Vikunja #448 | Before: row 154 edits Markdown in a separate Docs view, with no selected-word thesaurus or document pins. After: design draft on gyro/editor-design compares three editor approaches, records WordNet source/license/size, folds the pane/pin model and specifies guarded context/proposal APIs, DesignCraft handoff and seven proposed briefs. Q196 changes the basis to a Word-like interface. Verification so far: editor/provenance checks 10 pass, 1 explicit rebuild skip (pinned esbuild absent); brief fields, unique headings and 11-line checklist checked. Product, queue and live state unchanged. WordCraft scratch evaluation is being incorporated before publication. Review and Jason choices remain open.
|
||||
|
||||
2026-10-21T16:22:36Z | Q195/Q196 design verification, #1752 / Vikunja #448 | Folded Fenton 7caf8008b and Louie 880ef19e0. Recommend bounded O3: Word-like DOM editor plus isolated headless DOCX export, with explicit feature cut and semantic loss checks. WordCraft web 44,179,239 bytes, identical twice; measured comment/footnote conversion limits recorded. Seven briefs and 12-line checklist validated. Fresh editor/provenance: 10 pass, 0 fail, 1 explicit rebuild skip (esbuild 0.28.2 absent). CI queue guard identity gyro/purpose push returned documented degraded-pass (exit 0), CANNOT_ASSERT credential-unresolvable, scratch audit gyro-q195-ci-queue-audit.jsonl; no fallback or CI-green claim. Documentation-only candidate for Mos/Fenton review, no source, queue or live changes.
|
||||
|
||||
2026-10-22T16:06:34Z | Q195/Q196 design review fixes, #1752 / Vikunja #448, mos-1300 / fenton-0004 | Before: draft 105811400 had owner-only thread wording, stale source positions, two mismatched seat/model assignments, an assumed library save boundary and 12 owner questions. After: grant-aware pin validation and stale/revoked access redaction; references refreshed against aca16404c, including public/bus.js and README offsets; Sage assigns model-only seats; unimplemented library.file.save/C2-11b brief blocks book editing; tabs explicitly cover 62-76rem; J7 stricter context choice recorded; pronouns removed; six owner questions. Static docs/source-anchor checks only, no builds, product suites, MVP gate slot or live action. Fenton re-check pending; O3 and both companion folds retained.
|
||||
|
||||
2026-10-22T16:09:56Z | Della mos-1303 / Vikunja 460 Q217/Q222 revision | Before: Q1/Q2 open and Q3-Q7 phrased as Jason questions. After: Jason-created InMotion/woltje.com ingress recorded; per-source mailboxScope defaults none with human-only CLI then Console writer and named default/change/agent-refusal tests. Q3-Q7 settings/defaults/owners documented; no new external decision required for default development, conditional provider/spend/exposure list retained. PRD/grant/data.delete/live gates preserved. Docs only, no product suite or live operation. Earlier uncommitted append-only PRD review session receipts are preserved in this docs commit. Ludwig reviews before Sage landing.
|
||||
|
||||
2026-10-22T16:12:26Z | Della ludwig-0035 / mos-1303 / Vikunja 460 focused corrections | Verified REQ-WAIT-2/3 at db1e7732a: Jason approval is required to route mail.access out of hard waits. Restored explicit Jason-approved PRD gate; no Q211 waiver inferred. Named tunable parser/attachment settings, defaults and schema/test review ownership of resource bounds; E04 checks changed values and bound refusals. Clarified Q8 extraction stays Jason choice. Docs-only checks, no product/live action; focused review before Sage handoff.
|
||||
|
||||
2026-10-22 13:06 UTC | Megavolt | Row 281 (#1786) build. Before: a fire or delete left host_grants/gitea_grants rows, the host line and the Gitea token live. After: step G in broker.mjs revokes them in a detached job per agent (the host line first, then Gitea local-first with an admin preflight); a refusal is grant.revoke.refused plus a human message; open grant decisions are in the set; a raise for a fired agent refuses instance-fired. Tests T1-T14 (bus fire-revokes-grants, lifecycle-revoke, grant-executor, gitea-grant; cli lifecycle). Gate on 7979fb41e: base 121/121 + 24/24, 29 anchors, M1 exit check rc1/17, M1-M22 22/22 killed, dewey-0840 files 76+14+23, bus 819, business 231, cli 837, ten shells rc 0 under the mos-1275 limits; skips: test-task row 244 live-provider pair, test-queue canonical-root check. Corrections: the first base run on c174f37ac went red on five first-run test defects (b3, test only); the first mutant run on 06ee5ed6b hung on M1 because T6c's held loop never ended (b4a fixed the teardown, and the runner now has timeout -k 10 180 and an exit check without --test-force-exit).
|
||||
|
||||
2026-10-22T16:36:30Z | Della row224 C2-05b #1725 preparation | Before: C2-05a queries exist at 4f3fbc25a but no broker lookup verbs or typed tools. Scope: broker/tools plus Sage-authorized runtime/bundle integration, tests and docs. Existing human-only library reads retained; five agent queries require a live holder and trusted configured source. Code/docs only under row253 gate; no Node/test/push/claim. Runtime ownership coordinated with Rocko219/Huey272.
|
||||
|
||||
2026-10-22T16:52:55Z | Rocko | Row219 prepared under sage2793/2807; explicit creative thread/default-session selection, narrow metadata route/picker U1–U5,22 static mutant sites; unrun while253 holds.
|
||||
|
||||
2026-10-22T17:42:16Z | Sage | Row 253 (#1758, Gander, Duckworth) LANDED 48e450ce8. Before: refactor at aca16404c; case-sudo calls had no supervised session check and the M21 survivor hid behind a fast-exit fixture (mos-1297). After: a fast-forward of 22 commits; manifest 591020a1 rechecked with 0 mismatches; run 5 whole gate green with M1-M40 killed, the M38 PID equals SID assertion added under mos-1298; Duckworth APPROVE rev 3252, note rev 3253, done rev 3254; #1758 comment 30701 and closed; P19 416 comment 719. N1 (fast child reported as exit 85) is DEFERRED as Vikunja 532.
|
||||
|
||||
2026-10-22T18:11:57Z | Sage | Row 275 (#1780, Morgana, Duckworth) LANDED 3410df7c3. Before: refactor at 48e450ce8; viewers had no verb list of their own and board reads were not refused to users. After: a fast-forward of 3 commits; manifest 39847b15 (4 paths) blobs match; gate 17:52:12Z-18:05:00Z green (whole files 7/22/6, bus 834, cli 861, harness 151, business 231, seat 167, 10 scripts with 3 named skips), M6 and M10 killed; Duckworth APPROVE rev 3260 (comment 30717); note and done by sage-2897; #1780 comment 30728 and closed. N1 (board refusal test as a loop over BOARD_VERBS) is non-blocking.
|
||||
|
||||
2026-10-22 18:11:58 UTC | Gander row276 #1781 prepared, gate pending | Before: row253 left5legacy Case1 AS_USER calls unsupervised across sudo pty sessions. After: candidate uses its unchanged supervisor with per-call inner/outer budgets, separate streams, strict display-only failure trailer, fake-only paired process-model cases and15unique mutants. Duckworth0182 B1 corrected the plan before product code;0183 cleared build. Parse-only Bash checks pass; no Node, behavioral test, host, unit or config action. Runtime gate and publication await Sage.
|
||||
|
||||
2026-10-22T22:51:32Z | Megavolt | Row 207 (193c, #1705): rebased onto 01983cd3f as 712f79c0a; gate green under sage-3041 (access.test 31/31, M1-M8 killed on named tests, cli 894/894, ten scripts under mos-1275 limits). Correction: the first run's kill check grepped TAP "not ok" against node's spec output and was stopped and rerun. Runs note in 9bb406c6c.
|
||||
|
||||
2026-10-22T22:51:32Z | Sage | Row 207 (#1705, Megavolt, Duckworth) LANDED c4f77373f. Before: refactor at f0e3dcdf4; mosaic access had no user verbs. After: fast-forward of 4 commits, a cherry-pick of the gated 01983cd3f..9bb406c6c (range-diff 4/4 equal, patch-ids equal, the 4 manifest blobs equal; the base moved only by the #1797 brief, which no test reads; no open branch touches the manifest). Duckworth APPROVE c30849 rev 3356; done rev after the note. N1 (any-business refusal untested) DEFERRED. No live change. Correction: my first 207 review request ran under timeout 120, which killed it while it wrote the outcome; the comment (c30841) had posted, so I removed the dead lock and resolved it at rev 3350.
|
||||
|
||||
2026-10-22T23:03:39Z | Gyro Q195 O2 rewrite, #1752 / Vikunja #448, mos-1356 | Before: landed O3 design 95e3f7166/blob 4cb59999. After: Q231/Q233 full WordCraft O2 design on gyro/editor-o2 from refactor c4f77373f; canonical git DOCX, original Markdown/wiki preservation, human cadence default 0 with instant accepted-save commits, full rich-feature first slice. Ten briefs include independent source/dependency security clearance before any build queue entry, exact editor-only CSP approval gate, restricted source/build/bridge, separate reviewed DOCX citation gate and flags-only agent access. Existing Case 2 semantics unchanged; any integration requirement change goes through Mos to Jason. Static checks: ten briefs/seven fields, independent reviewers, unique anchors, current source/base/blob pins, docs scope and append-only logs pass. No builds, browser/worker runs, code, queue or live changes. Fenton review pending; CSP and implementation gates remain closed.
|
||||
|
||||
2026-10-22T23:03:53Z | Gyro Q195 O2 push guard | CI queue guard identity gyro / purpose push returned exit 0, documented degraded-pass: CANNOT_ASSERT credential-unresolvable. Audit: /mnt/storage/scratch/gyro-q195-ci-queue-audit.jsonl. No credential fallback or CI-green claim.
|
||||
|
||||
2026-10-22T23:09:38Z | Gyro Q195 O2 fenton-0006 fixes | Before: e012bab0b left stale-source ambiguity, incomplete migration/shipping ownership, undefined typing transactions and an unmeasured CSP gate. After: D12 explicitly routes superseded-source/C2 reply notices to Mos/Jason and refuses adoption before W4b/notice path; Q2 remains open. F2-F6 add schema-edge placeholders and restore-accounting files, shipped runtime data/writing closure with no sandbox store mount, per-chapter DOCX and 2s-pause/15s-span save groups, volume/latency/growth gates, split-run clickable wikilinks and historical Obsidian graph warning, and S0 isolated existing-artifact CSP measurement before exact-header approval. Static ten-brief/ownership/settings/checklist checks pass. No build/browser run, product/queue/live change. Independent re-check pending.
|
||||
|
||||
2026-10-22T23:09:50Z | Gyro fenton-0006 push guard | identity gyro, purpose push, exit 0 documented degraded-pass CANNOT_ASSERT credential-unresolvable; audit gyro-q195-ci-queue-audit.jsonl. No fallback credentials or CI-green claim.
|
||||
|
||||
2026-10-22T23:10:14Z | Gyro mos-1358 ruling recorded | Q36 / Vikunja 562 keeps C2 superseded notices OPEN with Jason. Markdown adoption refusal until W4b PASS is now a separate settled Mos build-order ruling, not a Jason question. D12 and Q2 distinguish them; no notice build authorized.
|
||||
|
||||
2026-10-22T23:13:27Z | Gyro Q195 O2 fenton-0007 F7/F8 | Named human-only editor.saveGroupPauseMs=2000 and editor.saveGroupMaxMs=15000 with integer/range/cross-field validation and W3a UI/CLI refusal tests. W3b owns production adoption refusal until W4b; isolated fixture conversion remains testable without request-flag bypass. W4b owns adoption module/API tests and reviewed lifting only with committed-reader/citation readiness and, if Q36 approves, the separate notice row PASS. Q36 unresolved stays closed. Static ten-brief, setting-key/bounds, refusal/lift ownership and two-question checks pass. Docs only; no build, browser/worker run, product/queue/live change. Fenton checks F7/F8 on next push.
|
||||
|
||||
2026-10-22T23:13:48Z | Gyro mos-1359 / r3 push guard | D13 records save-group timing as Mos's accepted reading of Q226 F41, not Jason's ruling; both defaults remain human-only settings. Push guard identity gyro/purpose push: exit 0 documented degraded-pass, CANNOT_ASSERT credential-unresolvable; audit gyro-q195-ci-queue-audit.jsonl, no credential fallback or CI-green claim.
|
||||
|
||||
2026-10-22T23:23:42Z | Rocko, W4 #1799 | Before, D24-D26 had no separate exact DOCX citation/access contract. After, 15e91538f proposed a versioned schema, parser/story/view/refusal rules, live authorization/sharing and generic/git/session bypass closure, compatibility gates and 22 acceptance rows. Two JSON examples parse, diff check clean; no code or implementation acceptance. Megavolt review pending.
|
||||
|
||||
2026-10-22T23:55:11Z | Sage, W4 #1799 docs landing | Megavolt reviewed four rounds (CHANGES 15e91538f, 019a0ea5b, 54246cb32; APPROVE 994c3f7fb, #1799 c30868/c30870/c30876/c30884). Landed here as cherry-picks: docs/plans/2026-10-22_q195-docx-citations.md blob 2af460ab7 and docs/reviews/2026-10-22_q195-docx-citations.md blob bedde1716, both new files. 32 acceptance rows go to W4b; R4-N1 (re-migration after a pre-W4b restore keeps the rotated fence and needs owner confirmation) goes into W4b's brief and A32. W4 is design-approved only: its gate still needs Sage's W4b implementation brief, which waits for S0 (#1798) PASS. Q36 open, adoption closed. Docs only, no test reads these files.
|
||||
|
||||
2026-10-23T00:52:58Z | Gyro Q235-Q237 policy rewrite started, Vikunja 571, mos-1366/1367 | Before: approved O2 e36bce352 keeps Markdown originals at their paths and Q36 notices open; landed W4 2af460ab7 has the same boundary. Imported that byte-identical W4 baseline as 1ac76a417, from refactor 062646487, to make the policy-only revision diff explicit. Prepared P1-P6/B1-B4: human one-way per-story import, human-set source-repo archive, DOCX current reader, scoped format map/archived non-canon reads, W7 conversion publication after W4b/approved PRD/10-27 and Jason first-real-book go. Q234 exact CSP, mos-1363 and S0 M1-M14 retained. W4 locator/extractor, shares and restore-fence blocks compare byte-identical to the approved baseline. Scope/source review ongoing; Mos impact map must be folded before Fenton handoff. No build/browser/worker run, code, queue, private-book or live action.
|
||||
|
||||
2026-10-23T01:37:07Z | Gyro Vikunja571 final impact-map / mos-1372/1375 fold | Prepared parent/W4 rewrite against brain-redux d183246 impact map at stack 062646487. P1-P6/B1-B4 and engineering Q1-Q16 are mapped to eleven briefs. D9 replaces the earlier private O2-only context/per-document/share model: normal Case 2 access in any authorized thread/room, supporting writes intact, changed policy awaits PRD 1.17 approval. New DOCX story creation has the same opening gate as human per-story import. Host-only push to human-set off-host remote replaces the no-remote ban; remoteURL defaults unset and same-host bare is disallowed. W7 gates on Hermes #423 step5, W4b, PRD approval, post-10-27 and first-real-book go; owns conversion events COUNTED/--events/pre-conversion restore reconciliation, manifest order/next_chapter, creative role cite wording, archive/old-path edits and format/link/citation routing. Store+off-host acknowledgment precedes archive commit/push, then catalogue publication; post-push failures remain resumable pending with pinned source. Current checks pass: complete brief fields, policy/map coverage, exact Q234 CSP, locator/projection mechanics byte-equivalent except explicit authority wording, A01-A35 IDs, immutable map pin, four-doc scope and append-only logs. W4 authority/remote/recovery sections reopened for Fenton; older APPROVE not reused. No product/build/browser/worker/private-book/queue/live action. Earlier entry describing preserved shares/fence applies only to the pre-D9 intermediate draft; D9 now withdraws those unimplemented structures.
|
||||
|
||||
2026-10-23T01:37:55Z | Gyro 571 push guard | identity gyro/purpose push: exit 0 documented degraded-pass CANNOT_ASSERT credential-unresolvable, scratch audit gyro-q195-ci-queue-audit.jsonl; no credential fallback or CI-green claim.
|
||||
|
||||
2026-10-23T01:41:59Z | Gyro sage-3121 A1-A3 PRD alignment | Before: 8d209016b adds a current Connections graph, excludes new Markdown stories in converted books and defaults archiveFolder. After: parent/W4 align with PRD1.17 d6 sha256 886b93c571f87bfbb2f27ee3113b6800a32d8f89196d95ace33566031bee380a: clickable editor links only; graph/tests later pending J-b; new different-path Markdown in a converted book remains canon until import, only recreated converted old paths are unmapped; archive folder is human-set in library mapping with no default, missing folder refuses conversion. Static A1-A3/11-brief checks pass; W4 locator/extractor/access/remote/recovery blocks unchanged. Local commit only, push held for Sage's named gap while Gizmoduck holds 270+271 run5. No product/build/browser/worker/queue/live action. Fenton gets local SHA through shared object store.
|
||||
|
||||
2026-10-23T01:49:08Z | Gyro fenton-0012 B1/B2 and N1-N6, mos-1379 | B1: off-host remote must deny the real shared jason.woltje seat-helper credential as well as seat identities until #431; private visibility/dummy-token denial is insufficient; W3b/A16/A26 require that future fixture proof. B2 follows mos-1379: human-only editor.agentReadComments and editor.agentReadTrackedDeletions both default false, comment/original/markup reads/resolves/index/context refuse when off; final access remains D9 and no role allowlist/per-document grant. N1 staging-ref allowlist and retained private refused/published audit refs; N2 URLs literal with fidelity-report loss and confirmation; N3 future paths marked new or predecessor row; N4 rename/sharing wording fixed; N5 historical thread text not refreshed; N6 protected VM1012 backup cited in P5. Static checks pass, locator JSON/package/projection mechanics retained. Local commit only; HTTPS push still held for Sage's named gap. No credentials tested/read/printed, no product/build/browser/worker/queue/live action. Fenton gets exact local SHA/blobs.
|
||||
|
||||
2026-10-23T01:53:25Z | Gyro fenton-0013 N7 mixed-format chapter order | Fenton approved local 0f4fa4f93. D10 now explicitly orders every chapter ID regardless of Markdown/DOCX format, inserts new canon Markdown by row216 or human committed order, and preserves ID/position at import. W7 tests cover mixed traversal/insertion/import stability. Static diff/append-only checks and docs hook pass. Local commit only, narrow re-check requested, HTTPS push still held for Sage gap. No runtime, queue or live change.
|
||||
|
||||
2026-10-23T02:21:06Z | Sage, rows 270+271 landing, 571 landing, MVP rows | Before: 270 (#1775) and 271 (#1776) were approved on gizmoduck/row270-271-r4 49cee846e and 2b964a082 after combined gate C2 run 5 GREEN on 53406f3e4 (bus 873, seat 202, cli 905, ten scripts, 38/38 mutants). After: 33 commits cherry-picked onto 767bb62c7 as 7328da8ca, range-diff all "=", 40 paths identical to the candidate; 270 manifest f4174458 matches, 271 manifest 3ab1f5c8 matches on 29 paths, the other 4 moved under 270 and match its manifest. Done revs 3396/3398, #1775 c30962 and #1776 c30964 closed, P19 463 c861. 571 (Gyro, Fenton APPROVE) picked as ee5f7b9ef, 6345aae3d, 767bb62c7 (blobs 9382f85b, 7ec0897b), 1ac76a417 left out; N7 911dad2aa (Fenton-0014) picked after. Rows 293-299 added (574, FED R6, R4, R7, GitHub grant, Q228, 573), queue commit 253346e3c. Correction: the push sync step checked out O..S paths over the canonical worktree without testing for uncommitted edits, so any pending SESSIONS or BUILD-LOG line in the canonical checkout at 02:06Z would have been overwritten; the sync now skips edited paths. Schema 3n is not live (LIVE 0cbe2e9b3 at 3m).
|
||||
2026-10-23T03:59:52Z | Sage, row 293 (Vikunja 574) landing and 3m-3n live window | Before: live 0cbe2e9b3 at schema 3m; 574 approved by Filbert (c30982, cdda41f5) on gander/v574-r2 a9cae2bd6 after run 2 GREEN on 6bfab273b (four files 153/0, seat 202/0, cli 905/0, 312 flagged commits with 0 maintenance children). After: Darkwing's window put live on 464010fbe at 3n (activation 20261023T034557Z-464010fbe4c6, 26/97/5/28, Console 7332 200, W9b pm ra38826f3c6e2 left live, STATE darkwing/window-migrate-3n 8708927e5); 574's six commits cherry-picked onto 464010fbe as e51d11640, range-diff all =, patch-ids equal, only docs differ from a9cae2bd6. #1805 closed c31013, P19 386 c887 and 574 c888. Residual: queue store.mjs, discord git.mjs, harness tools.mjs commit without the maintenance flags; no whole-root test covers them yet.
|
||||
2026-10-23T05:47:17Z | Sage, row 292 (#1797) landing, idle-stop race row 302, rows 300/301, row 294 rounds 2-3 | Before: refactor 4a9c536e8; 292's gate on 0d0021454 hit one red at compactions-launch.test.mjs:229, ['session-invalid',22,null] where ['stopped',0,null] was expected, product and test unchanged. Gizmoduck read it as a race: threadIdle releases the claim, then the launcher sends SIGTERM, and a runner call in between gets not-holder and exits 22. Mos (mos-1411) rejected loosening :229, let 292 count one whole unedited green cli rerun, and allowed one rerun for that exact signature on other gates until the race row lands (hits logged on Vikunja 588). The 10-run diagnostic passed 10/10, but one runner log (r846b8d083502) showed not-holder then exit 22. After: the cli rerun was 910/910. 292 LANDED 6aab979e6 05:18Z (7 cherry-picks, range-diff all =, patch-ids equal), done rev 3447, #1797 closed c31038, Vikunja 588 c891. Briefs 86d5ac69b (R6-N1 #1807, R6-N4 #1808, rows 300/301) and e3ef1eaf6/fdedc56e2 (race, #1809, row 302). Row 302 design d1/d2 approved by Dewey with D1 (role.claim refuses a released run), c31044. Mos ruled landRelease folded in, code launcher-released, and four old-test lines moved to exact values (mos-1414/1415/1416): threads.test:394, runner.test:771-772, land.test:464. Row 294 round 2 stopped at webui G5 granted-user.test:269: max(seq) over an empty events table is NULL. Round 3 is one line, coalesce(max(seq),0) (mos-1417), Duckworth APPROVE c31045 rev 3462, gate pending. Queue commit 756284e8e (revs 3448-3456). Correction: two review-request attempts this window ran without MOSAIC_GITEA_CREDENTIAL_FILE exported and failed (3450 Launchpad, 3451/3452 Sage); the posted request was 3453/3454.
|
||||
2026-10-23T07:11:43Z | Sage, row 294 (#1800) rounds 3-4 and landing, row 219 edits 2-3, R9 transient and row 303 | Before: refactor 09d0d6faf; 294 run 3 on cb1648f3 stopped with 20 cli reds from two causes. A: the stack-release rehearsal fixture copies lacked cli access.mjs and transport.mjs, which 294's config.mjs now imports. B: the B1-B3 boot tests read warnings that now carry the fixture tracker line. Mos ruled A as two copy-list entries and B as a full pin, no filter: a TRACKER const and deepEqual (mos-1419). After: round 4 8616b9a4f, manifest a1ff5d67, Duckworth APPROVE c31053 rev 3469 with C1/C2. Run 4 from npm ci green: webui 365, cli 909, bus 873, 10 scripts, M1-M11 killed. Merged tree d3059c2c3 (11 cherry-picks, range-diff all =, patch-id 8371405a equal): stack-release-external 25/25, stack-upgrade 21/21. stack-release 223/224 on the first run, R9 at :2178: TypeError on join(r.dir) with r.dir null, so rehearse stopped before its directory and the output was lost. R9 passed 6/6 run alone on d3059c2c3, 09d0d6faf and 24b745447. Mos (mos-1422) allowed one unedited whole rerun: 224/224. 294 LANDED d3059c2c3 07:04Z, done rev 3471, #1800 closed c31068, P19 269 c895. Row 303 (#1810, Vikunja 589, brief ad6ca7930) adds an r.dir guard in R9 only. Queue commit 80793d824 (revs 3464-3473). Row 219: Rocko's second edit 7cbd839d0 (mos-1418, Dewey dewey-0883) restarted and went red at thread-launch C2-09a :489 on a legacy history path. The third edit 5f2ef92e5 (mos-1420, Dewey dewey-0885) reads the native session path. 219 now rebases onto 80793d824, because 294 overlaps its webui files. Correction: the first merged-tree check counted on the 292 drift and found an unrelated transient, and my P19 placeholder sed also rewrote the script's own guard line; the guard refused to post, and I restored the line before posting.
|
||||
|
||||
Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault run minus a separate baseline run, and a loaded whole-suite run went red. Mos approved direct timing from host /proc/uptime stamps with ambient load only (mos-1409). After: rebased onto 80793d824 as ffa93b054 (range-diff 3/3 =, manifest 277ace0b unchanged). Gate 07:26Z-07:50Z: case1 whole 3x 53/0/0, cli whole 917/0/0 (59 files, load 1.54-7.17), P10 inner 2.27 s, outer 4.01 s, M1-M6 killed. M4's planned guard return then exact outer-reason failure counts as its kill (sage-3271, mos-1426). Filbert APPROVE c31084 rev 3485, N1 M1 margin 0.02 s. 299 LANDED ffb2fedfb 08:03Z, done rev 3487, #1806 closed c31087; queue commit 8a35ffadb (revs 3474-3487). Row 303: Duckworth APPROVE c31079 rev 3480 with C1, the whole-file gate. Rows 219 (bfa421b6c) and 295 r2 (b16cef1d0) rebases: Dewey static clean. Corrections: Launchpad and Gander have no token, and the CLI refuses another seat's token file, so I posted both requests as sage (c31082, c31083). My sage-3265 wording led Duckworth to poll queue show into Gander's slot (07:20Z-07:26Z); I ruled it ambient load (sage-3274).
|
||||
2026-10-23T10:48:40Z | Sage, rows 302, 295 and 219 landings, the Docker pool diagnostic, rows for Vikunja 590 and 591 | Before: refactor 1ac655005. 302 (#1809, Gizmoduck, Dewey) LANDED 3ed9fd754 08:49Z: 7 commits onto 1ac655005, range-diff all =, tree = gated merge 783602e06; queue commit 07802dc57 (revs 3488-3494). Row 219's gate went 11/3 in test-release.sh: release activate's health check failed because Docker could not create sage-diag1437_default, since all predefined address pools were used up. Under mos-1437 I reproduced it on clean 07802dc57 (11/3, the same stderr). I then removed 18 unused gate _default networks, and the rerun went 14/0 with the run-task marker ok. Mos accepted the removal and ruled that a probe that can create something waits for the go and a removal waits for the report (mos-1438). 295 (#1801, Morgana, Dewey) round 2 gate on d68b4300d (manifest e94c9aeb) was green. It LANDED 2efd127ce 10:10Z: 9 commits onto 07802dc57, HEAD~1 tree = gated a86592ffc. Done rev 3502, #1801 c31105, P19 414 c911. 219 (#1720, Rocko, Dewey): the restart on the post-295 merge 054da642f (manifest 4a2271dd, my mos-1439 call) passed node 2523/0/2 and shells 449/0, with 24 mutants killed. It LANDED 5fdfd7275 10:40Z (8 row commits plus packet-only evidence, tree e86a84a85 = gated). Done rev 3509, #1720 c31110, P19 416 c913. Queue commit 90633c864 (revs 3495-3510). Brief docs/plans/2026-10-23_gate-env-rows.md names rows for 590 (claude-session slug) and 591 (compose network cleanup), and DEFERRED.md gets 219 N1 (U6). Row 272 (Huey) conflicts with 5fdfd7275 in code, so it moves back to in-progress for a rebase. Its new fallback is bounded to a session.list access-refused on an omitted-session route (sage-3357). Corrections: my network create probe ran before Mos's go, and the removal ran before the report. My first slot-order line put the diag start at 09:44Z; it was about 09:41Z. My 219 push piped its output through grep, but the after-fetch confirmed origin. Queue commit 90633c864 also carries Huey's rev 3510 (272 back to in-progress), and its message names only 3495-3509.
|
||||
|
||||
|
||||
2026-10-23T11:03:46Z | Gyro Vikunja571 mos-1444 PRD d8 follow-on | Branch gyro/editor-571-d8 from origin/refactor c16e943b9. Both designs now keep library.convert outside ACTIONS/ROUTED and broker processing, retain REQ-WAIT-1/hard-wait test unchanged, constrain unmarked story eligibility to mapped book/playground scope, and add whole-host pre-import restore recovery from remote import manifests plus the Gitea archive commit. Lookups refuse until one current version is proven; VM1012 restore needs Jason present under Q185. W7 no longer owns hard-waits.test.mjs. Canonical queue next gyro returned nothing; direct Mos assignment governs. Static diff, exact replacement and append-only checks pass. No code, tests, build, browser, private-file or live action. Fenton re-check requested after authorized branch push.
|
||||
|
||||
2026-10-22 | Huey | Row 272 / 264e (#1777) preparation on huey/row272, base 2d0d407ba. Before: bus session metadata and launcher isolation exist, CLI/Console expose only the saved thread default. Draft: session CLI, Console chooser with isolated drafts/cursors/receipts, metadata counts and safe-clear operation receipts against Gizmoduck's 0260 contracts; narrow deferred B6 reply fence. Tests authored, not run: Gander row 253 holds Sage's slot; no Node, claim, HTTPS git, push or live changes. This entry records preparation after the first draft, not a green acceptance result.
|
||||
|
||||
2026-10-22 | Huey | Row 272 draft, 264d additive contract gizmoduck-0272 (78e14352f). Added confirmed cancellation through the human channel, cancelled receipt retirement distinction, named refusals and routed-message cancellation output. Before: cancelled was not recognized as terminal; after: draft CLI stops polling and Console shows the before/after retirement outcome. Acceptance remains unverified: no Node/tests/claim/commit/push under Sage's gate. No runtime/lifecycle authority expanded.
|
||||
|
||||
2026-10-23 | Huey | Row 272 / 264e sage-3141 local rebase. Before: uncommitted draft on 2d0d407ba. After: same draft on new huey/row272-b23783f39 at b23783f39, with 270/271 landed (7328da8ca) and schema 3n. Product changes auto-merged; both append-only logs retain the full new base and original Huey additions. Checked landed session.list and clear/cancel/operation envelopes against the wired draft: no incompatible contract change. Historical unavailable-dependency cases stay as stand-ins; actual base supplies counters and lifecycle state. No tests/Node/claim/commit/push while Gander holds row 574 run 2. Existing-test fixture/assertion delta is named in the packet for Mos's ruling via Sage before execution.
|
||||
|
||||
2026-10-23 | Huey | Row 272 sage-3145 preservation correction. refs/stash is shared, so it was unsafe as sole draft storage. Pinned exact stash commit 07dc9d2f7944b4781b5e80dfa18425f4749f7a45 to local huey/row272-stash-keep and left the entry untouched. Sage explicitly directed committing the rebased draft before testing to preserve it: an untested local checkpoint, no acceptance claim. No Node/test/queue/network/live action. Separate claims and test CLEARs remain pending; old-test delta is with Mos via sage-3144.
|
||||
|
||||
2026-10-23 | Huey | Row 272 early static review follow-up, scrooge-272-early-read-huey-1. Before: a bad-session 404 erased sibling draft/recovery caches, malformed clear/cancel write receipts omitted the audit line, and another session's op key produced an ambiguous error. Draft after: purge only on named thread-level refusals, write one redacted malformed-receipt audit line, and identify key reuse without a write. New browser/HTTP/CLI tests authored, unrun under test hold; approved old-test hunks unchanged. Published 850b22680/round-1 manifest unchanged; follow-up is uncommitted, no acceptance claim.
|
||||
|
||||
2026-10-23 | Huey | Row 272 round 2 on row 219 landing 5fdfd7275, after 295. Replayed round 1 and B1/N1/N2; seven conflicts resolved retaining both log histories, both test sets, 219 instance chooser and 295 owner-settings-only transport. Static comparison confirms old tests differ only by mos-1388 approved hunks. Sage3357/Mos1442 authorize bounded granted-user legacy fallback: only session.list access-refused and omitted-session route; no new session grants or owner routing. RED first under3358 on new file only: 1 expected legacy assertion failure, 3 guards pass, 3.504s, TMPDIR mktemp/no underscore, no compose network; slot returned as huey0035. Then implemented fallback and added timeout/bus/malformed guards plus isolated-copy mutant runner, all unrun afterward. Candidate commit/push precedes Sage's full gate by3354; no green claim.
|
||||
|
||||
2026-10-23 | Huey | Row 272 round 3 per mos-1455 / sage-3435. Gate run 1 on b44332a93 RED: bus 891/0, business 231/0, CLI 940/1, plugins 110/1, webui 372/20; no mutants. Fixed only new CLI fixture role authority and three null-old render guards. Catching browser tests unchanged. manifest.r3 freezes the same 17 paths. Authorized pre-gate follow-up commit/push; no local tests, Node or queue writes; separate compactions dependency and Sage gate remain pending.
|
||||
|
||||
2026-10-23 | Sage (1ef1e4f8) | Row 272 landing (#1777, 264e) | Gate r4 on bea0c8e1c over 1e629c892: bus 891/0, cli 941/0, webui 392/0, business 231/0; plugins 110/1, the single red being row 65 harness.test.mjs:192 naming compactions.mjs, which row 307 owned and fixed. M1-M6 and F1-F4 killed, restore clean. Landed d33989230, queue commit 8dc132bec.
|
||||
|
||||
2026-10-23 | Sage (1ef1e4f8) | Row 307 landing (#1814, Vikunja 598) | Six first-parent commits replayed onto 8dc132bec with matching patch-ids, 16 paths equal to 6fdfe3790, manifest digest 17c9f8d9 rebuilt from git. Gate green 14:37:21Z: bus 891/0, cli 941/0, plugins 119/0, webui 392/0, business 231/0, every other package 0 failed 0 skipped, every scripts/test-*.sh passed (skips: test-queue canonical root, test-task live provider x2). M1-M4 killed, restore clean. Landed d243f6b25, DEFERRED 307 N1 0621c439e, queue commit fe4ee8c7b. Correction: my first note said Vikunja 598 done when I had only commented (951); Mos closed it (952).
|
||||
|
||||
2026-10-23T17:29:46Z | Gyro Vikunja571 sage-3542 Connections amendment | Branch gyro/editor-connections-design cut exactly from landed PRD1.17 30e6bb680; read REQ-LIB-5 in that PRD. Replaced stale J-b waits in parent and W4. Proposed W8 owns current authorized Markdown/DOCX links/backlinks list, labels, versioned refresh, leakage refusals and fixture tests. Build order under mos-1470/comment961 is W7 closed routing candidate, W8 landing, then W7 human first-real-import opening. No REQ-LIB-8 condition or acceptance change. Q234/mos-1363/S0 M1-M14 and post-10-27/MVP priority fixed. Static diff, twelve brief sections and append-only checks pass. No repo Node, tests, build, browser, queue mutation or live action. Fenton reviews after authorized docs branch push; Sage lands on approval.
|
||||
|
||||
2026-10-23T17:32:37Z | Gyro sage-3544 fenton-0016 N1/N2 delta | Before: W7 Gate/final real-conversion list omit the W8 build-order cross-reference and W8 human-view filtering cites agent settings. After: both locations explicitly put W8 landing before human opening as mos-1470 build order, not acceptance; comment/tracked-deletion links are not current story links and never appear in the human Connections view regardless of agent settings. New commit atop approved 5af060fb5, no amend. Static diff and append-only checks pass. Docs only, no repo Node/tests/live action. Fenton checks only this delta.
|
||||
|
||||
2026-10-23 | Gladstone | Gitea #1816 / Vikunja 614 hostcheck item 5 and bundle verification | Before: dpkg's correct 750 form falsely fails item 5, and two run-sheet bundle verifies require a missing repository. Draft after: exact root/mosaic/path with octal 0750 equivalence; stat/output unchanged; both verifies use cleaned-up bare scratch repos. Three additive-only test blocks, two planned mutants. Untested under Sage-3738; Sage-3740 authorizes untested branch push for static review. Queue row pending; Sage owns gate and landing.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 scope amendment mos-1534, sage-3741 | F1 narrowed to exact 750/0750 and single spaces; F3 allows @ in absolute unit paths with unsafe-character refusals retained; F4 moves copy_tree to / after path resolution. AS_USER audit found git -C and stack-release's existing checkout cd cover the other steps. Added new unit and unreadable-cwd copy tests only. Untested under Sage's no-Node hold; no live action.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 F5 draft (mos-1536) | Before: the run sheet omits the explicit notify decision and precheck misses a bus-start refusal. After: section 9a creates a private null-binding decision as mosaic; precheck directly reuses readNotifyConfig and private_dir, and new tests cover accepted and refused decisions. Existing fixture setup additions await Mos approval through Sage; installer unchanged. Untested under no-Node hold.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 fixture ruling mos-1537 | Added only three setup lines each to host(t)/world(t) for the explicit null notifier decision. Existing assertions/fakes/lines intact. New install test checks decision bytes/mode preserved and missing decision remains absent. F5 missing-file fault removes the setup file. Untested under Sage's hold; separate follow-up atop 33950cf9b.
|
||||
|
||||
2026-10-23 | Gladstone | #1816 / Vikunja 614 F6 and notify parent (mos-1537) | Before: the run sheet's copy check lacks its required CLI wrapper; mkdir -p -m does not guarantee the notify parent mode. After: a separately Mos-gated wrapper --create step follows install and precedes check, as mosaic from cwd /; section 9a explicitly creates both notify directories at 0700. Docs only, no live action; Node/tests still held.
|
||||
|
||||
2026-10-23 | Gladstone | Row320 / #1816 first review CHANGES (morgana-0217) | Reviewer run: hostcheck19/19, unit47/47, install24/24, copy17/18; seven of eight mutants killed, M4 survived because both base/mutant failed only cleanup. Fixed this row's F4 test to enter cwd then chmod000, assert no read/search, pass an absolute outside bundle, and restore0700 in finally. Existing tests/setup and production scripts unchanged. Correction untested locally pending Morgana's focused rerun; no acceptance claim.
|
||||
|
||||
2026-10-23 | Boyd | Row 298 (#1804, Q228), before implementation completion | Took over Megavolt 7a141269c on boyd/row298 at 45dca69f6 under mos-1496/1502. Approved D1-D6 govern human-only grants.scope.set, empty fallback, in-scope close/evidence and spend recheck. All inherited code UNTESTED; no Node/tests until Sage CLEAR. Plan and static gaps recorded in agents/boyd/work/queue-298/BUILD.md; no live grant or credential access.
|
||||
|
||||
2026-10-23 | Boyd | Row 298 (#1804, Q228), targeted verification | On sage-3642 T1 lane B, five whole files serial: business scope 7/0, bus scope 14/0, scope runtime 1/0, CLI scope 4/0, project runtime 3/0 (29/0, no skips). Initial bus fixture typo recorded and corrected. M1-M10 killed by named assertions, source restored and final files green. Dewey dewey-0933 positive control and notes addressed; manifest and evidence in agents/boyd/work/queue-298/BUILD.md. Full package/script gate remains Sage's. No live scope set.
|
||||
|
||||
2026-10-23T22:41:07Z | Boyd | Row 298 (#1804), rebase after row 239 | Rebased onto 833ec1cc8d7c11842efb82e57c1a0b256c8d22d7 on boyd/row298-r2 without altering published boyd/row298. Only append-log conflicts; upstream bytes preserved and Boyd suffixes appended. Product merges automatic; row 239 writer-lock revoke and post-revoke error handling preserved, organization.mjs unchanged from new base. Static checks only; UNTESTED on new base pending Sage CLEAR. Details in Boyd BUILD.md.
|
||||
|
||||
2026-10-23 | Goldie | Row 322 / #1818 F8/F9 before | The library installer runs the clone importer, whose yaml dependency is absent on fresh clones, and tail -n 1 hides uncaught errors behind Node's version banner. Plan: run the same-commit copy importer, include it in COPY_PATHS, keep the first error line with last-line fallback, append isolated regression coverage. Mos-1550 approves setup-only world(t) additions and the one-path copy-file pin addition. No tests run yet; Sage slot required.
|
||||
|
||||
2026-10-23 | Goldie | Row 322 / #1818 F8/F9 after | Importer now runs from the same-commit dependency-equipped copy; COPY_PATHS includes it. One-line refusal selects an actual Error/ERR_ line, avoiding Node's source excerpt/banner, with last-line fallback. Mos-1550-approved fixture setup and one-file pin changes; two append-only regression tests. Under sage-3808 CLEAR, serialized timeout -k 10 runs in /mnt/storage/scratch/tmp/goldieuQ2664: install26/26, copy18/18, pinned-copy10/10; new tests2/2 first run and restored; N installer expected-red2/2, F8 and F9 mutants killed, installer SHA256 restored each time. No unexpected red. Evidence and exact old-test changes in agents/goldie/work/queue-322/BUILD.md; full gate and Morgana review belong to Sage.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 before (Q247, mos-1555) | New branch goldie/row324 at row298 approved860afc2b6. Design/readers audit in agents/goldie/work/queue-324/BUILD.md: normalize omitted tracker to null, preserve present validation, guard sync readers/adapter config; runtime/instance-settings/broker checks already optional. Existing business.test.mjs98 explicitly requires tracker, so implementation held for Mos approval of removing that one list entry; no old test or product edit, no tests run.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 draft built (sage-3836, mos-1556) | Claim rev3759. Omitted tracker normalizes null; present including null/malformed stays validated. Loader, business validate, bootConfig trackerFor and Console sync reader handle absence. Row298's guarded runtime/instance-settings/broker paths preserved. Two new test files (9 planned tests); sole old-test edit drops tracker from required-key list under mos-1556. node --check6/6 and diff check pass. No tests run: untested branch push expressly ordered before CLEAR; Duckworth review and Sage gate pending.
|
||||
|
||||
2026-10-23 | Goldie | Row 324 / #1820 rebased verification (sage-3850) | Rebased onto3569bf54f as7e59ba8b3; only append-log conflicts, eight manifest files byte-equal. Serialized timeout-k10 scratch TMPDIR goldieOdgIjr: new business5/5, new CLI4/4 (includes Console absent read), existing business21/21; presence mutant killed, bytes restored, new files rerun5/5 and4/4. No unexpected red. Published rebased branch goldie/row324-r2 preserves original remote draft without force-push. Evidence/mutant diff/reader list in BUILD.md; review/full gate pending.
|
||||
|
||||
2026-10-23 | Goldie | Row 323 / #1819 before | Case 1 lacks a committed business file. Brief requires actual .53 values and refuses guessing. New branch goldie/row323 from local refactor da537e7d1; source review shows project root, literal human id, tracker sync identity/reference metadata and launch model values absent from DESIGN, plus nonempty launch.instances requires another role. Asked Sage (goldie-0008); no config or test written, no test run. Details agents/goldie/work/queue-323/BUILD.md.
|
||||
|
||||
2026-10-23 | Goldie | Row 323 / #1819 static blocker (sage-3823) | Mos-1554 requires no bot and an absent, never-created tracker token path. Static tracing confirms this refuses install validation (business cli84-97, credentials78-83) and bus boot (runtime131/157, bus credentials225-228), even without tracker.baseUrl; unset env also refuses at bus credentials278. Reported Sage goldie-0009; no JSON/test/credential written, no tests. Accepted source values/role and refusal-test rulings are recorded in BUILD.md; waiting for product-boundary resolution.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 resumed draft (sage-3859) | Rebasing prior notes onto Q247 landing dd6cb5872 resolved only append logs. Added trackerless .53 factory JSON: jason, approved project root, chief+assistant for required nonempty launch.instances, claude-code/Opus5.5/high, tester on. No credentials or token placeholder. Seven new tests prepare exact-doc loading plus scratch real validate/install/boot and three refusal paths; one restoring tester-switch mutant prepared. No old test edits; no tests before Sage CLEAR. Source choices/rulings in BUILD.md.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 pre-test commit (sage-3861 CLEAR) | Sage explicitly requests commit before runs; JSON/new7-case test/restoring mutant and field provenance committed untested before slot work. Exact sources distinguish mos-1554 human/no-bot intent, sage-3820 roles, sage-3822 project/model, and Q247 tracker omission. No old test edits. Next runs: new file, business whole, CLI whole, mutant/restored file, serialized scratch TMPDIR timeout-k10.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 first slot STOP (sage-3861) | On committed571376c30: new file7/7 and business261/261 green; whole CLI hit unchanged revoke-window.test.mjs170 (test579, row306 R4), no RESULT after20.1s at ask167/Promise.all194. Stopped suite on observing red, cleaned only positively identified own scratch fixture. Partial631 pass/1 fail before termination, not full-suite green. No mutant/restored run, no old-test edit/rerun. Manifest unchanged; Sage notified goldie-0016 and slot returned. Evidence local under queue-323, awaits ruling.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 second red STOP (mos-1560, sage-3864) | Isolated revoke-window whole-file run1 on unchanged571376c30 failed0/1 after20.126s, same no RESULT at ask167/test194. Start00:24:22Z, load1.46/1.68/2.70, busyhelperCLEAR, fresh scratch goldieBObDQ9. No run2/3/CLI retry/mutant/restored run; no source/old-test edit. Sage goldie-0018 has log/load/error; slot returned, awaits base-dd6cb5872 root cause. Remote remains unverified571.
|
||||
|
||||
2026-10-24 | Goldie | Row 323 / #1819 accepted evidence (mos-1561, sage-3869) | Sage traced revoke-window reds to37-byte TMPDIR making109-byte tools.sock >107 limit (session.mjs278), not Codex. Fresh31-byte /scratch/tmp/XXXXXX confirmation passes1/1 in0.84s. On unchanged571376c30 serialized timeout-k10: business261/261, new7/7, tester-switch mutant killed/restored SHA85e6b9ac, restored new7/7. Every log starts UTC/load/helperCLEAR; all positive runs0 fail/skip/cancel. CLI whole acceptance comes from Sage gate under mos-1561; no local CLI rerun. Evidence-only notes/log commit follows, no old test/source changes.
|
||||
|
||||
2026-10-24 | Goldie | Row325 / #1821 before (mos-1563/1564) | Real .mosaic/project.json idstack disagrees with Case1 project's mosaic-stack key; row323 scratch fixture omitted project file and only warned. New goldie/row325 at99930362e changes only that JSON key and expressly authorized old assertion key; new real-byte project-copy validation tests require valid and reject old key. Mutant prepared; no tests before Sage CLEAR, no queue writes/host actions.
|
||||
|
||||
2026-10-24 | Goldie | Row325 / #1821 after (sage-3882 CLEAR) | Only Case1 projects key becomesstack; only old test70 expected key updated under mos-1564. New3-case test copies real .mosaic/project.json bytes, requires CLI project valid, rejects old key. Serialized shortTMP timeout-k10: new3/3, business264/264, old-key mutant killed/restored SHA9e43bfef, restored new3/3; all positive0 fail/skip/cancel. Manifest4/4 verifies including unchanged project document. Logs/load lines and mutant diff in BUILD.md; no queue writes/host action, Gladstone review/Sage gate pending.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 before (sage-3906, Q246, mos-1566) | Recovered seat/brief/design at ec86f2c84 on bentina/row157. Canonical queue next flags stale brief pin. Outbound audit finds broker gitea-grant node:https and web DNS-pinned node:https paths, triggering I2 explicit stop rule. Sage notified bentina-0001; BUILD.md records shifted cited sites. No implementation/test/Docker/credential/live-state action; awaiting lead re-pin and transport ruling.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 first slot STOP (sage-3910) | New config/helper31/31 and proxy11/11 green. test-config exit1:55 pass/1 old output regression; new install20/20 green. Validator added absent install key to JSON. Stopped before Docker/install suite; slot returned Sage bentina-0004. sage-3916 authorizes preserving absent output shape/defaulting env to local: fixed own loader/new test only, no rerun before next CLEAR. test-config diff +4/-0. No commit/push; seat BUILD and01/02/03 logs.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 after (sage-3919, sage-3923) | Absent install JSON shape preserved; helper defaults local. Existing config56/56, helper/config31/31, final proxy12/12, final test-install exit0 with three release0.0.12 rootless builds/pins, internal TLS/deny/DNS checks and real Vikunja publicGET200/notifier REST dummyGET401 through services proxy. Upstream-reset correction verified. Slot returned bentina-0007; no broader suites by Sage direction. Boyd then Darkwing review, Mos provider-host measurement and Sage landing gate pending. Evidence in seat BUILD and04-08 logs; only bentina/row157 candidate push authorized.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 round2 before (boyd-0023, sage-3933) | Read reproduced B1 half-open refusal socket retention and B2 same-PID exec inherited cache bypass. Added two new regression files; no existing assertion edits. Proxy drains then destroys refused sockets with1s fallback; helper clears inherited cache/export attributes at source. No tests before CLEAR; Goldie holds slot. Source/causes recorded in seat BUILD.
|
||||
|
||||
2026-10-24 | Bentina | Row157 / #1652 round2 after (sage-3936) | B1 refused sockets now drain/destroy with1s bound; B2 source clears inherited cache/export attributes. Archived a14+new regressions red as intended (5controls pass/7defects fail), fixed12/12, helper/config33/33, egress22/22, rootless install exit0 incl image pins/network/services dummyGET. Logs09-13 in seat work. No existing test/assertion edit for round2. Slot returned bentina-0014; followup own-branch commit/manifest for Boyd re-review; Mos and Sage landing gates pending.
|
||||
@@ -22,7 +22,6 @@ RUN npm ci --ignore-scripts
|
||||
# runtime adapters.
|
||||
COPY contracts /opt/mosaic/contracts
|
||||
COPY src /opt/mosaic/src
|
||||
COPY packages/processing-control/src/state.mjs packages/processing-control/src/entrypoint.mjs /opt/mosaic/processing-control/
|
||||
COPY adapters /opt/mosaic/adapters
|
||||
RUN chmod 0555 /opt/mosaic/contracts /opt/mosaic/contracts/* \
|
||||
&& chmod 0555 /opt/mosaic/src /opt/mosaic/src/*.sh \
|
||||
|
||||
@@ -76,16 +76,6 @@ 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).
|
||||
|
||||
+4
-24
@@ -75,8 +75,7 @@ session's workspace and its own process group. Same contract, plus:
|
||||
| `MOSAIC_POLICY_FILE`, `MOSAIC_TOOLS_FILE` | The bundle's gate policy and typed tools. |
|
||||
| `MOSAIC_TOOL_SOCKET` | The runner's tool socket, which the typed tools call. |
|
||||
| `MOSAIC_TURN_MARKER` | `pi`: the extension writes it at `agent_end`; a pi turn that exits 0 without it failed. |
|
||||
| `MOSAIC_EXTENSIONS` | `pi` only: extension files loaded with `-e`; a missing one exits 2. The pi module names two: the gate and typed tools extension, then the unslop check's `tools/unslop-hook/extension.ts` (row 66). |
|
||||
| `MOSAIC_UNSLOP_LOG` | The unslop check's JSONL log, `<run>/unslop.jsonl`, which the runner sets for both adapters and reads after each turn (row 66). |
|
||||
| `MOSAIC_EXTENSIONS` | `pi` only: extension files loaded with `-e`; a missing one exits 2. |
|
||||
| `MOSAIC_CLAUDE_SETTINGS`, `MOSAIC_CLAUDE_MCP_CONFIG` | `claude` only: the bundle's gate hook and MCP server; required. |
|
||||
| `MOSAIC_THINKING` | The route's thinking level (row 65), which the harness module's `levels()` already checked. `pi` passes `--thinking` (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`) and omits it when unset, as for `run-task.sh` workers. `claude` passes `--effort` (`low`, `medium`, `high`, `xhigh`, `max`) and requires it; unset exits 2. |
|
||||
| `MOSAIC_CLAUDE_BIN` | `claude` only: absolute path of the CLI to run, from the host's environment (the bus unit pins it). Unset or empty runs `claude` from `PATH`; a relative path or a non-executable file exits 2. |
|
||||
@@ -87,8 +86,7 @@ The layers each adapter relies on, and what they don't cover, are in
|
||||
### A role's skills (row 64)
|
||||
|
||||
A managed session's skills reach both adapters as text in the bundle's
|
||||
`prompt.md`, which they pass as a file: `pi` with `--system-prompt <path>`
|
||||
and `claude` with `--system-prompt-file <path>` (row 213). Neither adapter loads a
|
||||
`prompt.md`, which they pass with `--system-prompt`. Neither adapter loads a
|
||||
skill of its own: `pi` keeps `--no-skills`, since role sessions never get
|
||||
`MOSAIC_SKILLS`, and `claude` keeps `--disable-slash-commands` and
|
||||
`--restricted`. Sage chose this on 2026-10-19 from a probe, recorded in
|
||||
@@ -106,23 +104,5 @@ skill of its own: `pi` keeps `--no-skills`, since role sessions never get
|
||||
|
||||
The cost is prompt size: a role's skill text is in every turn, where the
|
||||
other routes load a skill when it's used. The bundle caps a role's skills at
|
||||
64 KiB together. The cap dates from when `claude` passed `prompt.md` as one
|
||||
argument, which Linux caps at 128 KiB; row 213 passes the file instead, so no
|
||||
local user reads the prompt in `/proc/<pid>/cmdline`. Pi 0.85.1 uses the path
|
||||
string itself as the prompt when the file is missing or can't be read, so the
|
||||
pi adapter exits 2 unless the path is a readable regular file.
|
||||
|
||||
### The unslop check (row 66)
|
||||
|
||||
Both adapters run the mechanical writing check from `tools/unslop-hook`,
|
||||
driven by one `lists.json`. `pi` loads it as its second `-e` extension.
|
||||
`claude` runs `claude-hook.js` from the bundle's `settings.json`: the Stop
|
||||
hook checks the turn's replies, and the UserPromptSubmit hook adds the
|
||||
correction notice as context on the next turn when the last reply had
|
||||
tells. Each hook command ends `; exit 0`, so neither blocks. Both log to
|
||||
`MOSAIC_UNSLOP_LOG`, and the runner adds a note for the operator to the
|
||||
next RESULT it sends (`packages/harness/README.md`, the runner). The
|
||||
harness modules mount the three files read-only at their own paths, and
|
||||
the manifest records their digests. `MOSAIC_UNSLOP_HOOK` and `UNSLOP_LISTS`
|
||||
aren't in the session environment's allowlist, so a managed session can't
|
||||
switch the check off or swap its lists.
|
||||
64 KiB together, since `claude` passes `prompt.md` as one argument and Linux
|
||||
caps one argument at 128 KiB.
|
||||
@@ -27,11 +27,6 @@ need MOSAIC_CLAUDE_MCP_CONFIG
|
||||
for f in "$MOSAIC_SYSTEM_PROMPT_FILE" "$MOSAIC_CLAUDE_SETTINGS" "$MOSAIC_CLAUDE_MCP_CONFIG"; do
|
||||
[ -r "$f" ] || { echo "claude adapter: not readable: $f" >&2; exit 2; }
|
||||
done
|
||||
PROMPT_FILE=$MOSAIC_SYSTEM_PROMPT_FILE
|
||||
case "$PROMPT_FILE" in
|
||||
/*) ;;
|
||||
*) PROMPT_FILE="$(pwd)/$PROMPT_FILE" ;;
|
||||
esac
|
||||
[ "${MOSAIC_INTERACTIVE:-}" != "1" ] || { echo "claude adapter: interactive mode isn't supported" >&2; exit 2; }
|
||||
CLAUDE=claude
|
||||
if [ -n "${MOSAIC_CLAUDE_BIN:-}" ]; then
|
||||
@@ -61,10 +56,7 @@ fi
|
||||
# Flags:
|
||||
# -p one-shot: print the answer and exit
|
||||
# --output-format text stdout is the answer only
|
||||
# --system-prompt-file the generated prompt replaces the default, given
|
||||
# as a file so no local user reads it in
|
||||
# /proc/<pid>/cmdline (row 213); absolute, since
|
||||
# the cd above changed the working directory
|
||||
# --system-prompt the generated prompt replaces the default
|
||||
# --effort the instance's thinking var (row 65; 2.1.302 takes
|
||||
# low, medium, high, xhigh, max)
|
||||
# --restricted no user/project/local settings files; --settings
|
||||
@@ -84,9 +76,10 @@ fi
|
||||
# text in the system prompt (row 64)
|
||||
ALLOWED=mcp__mosaic
|
||||
[ -z "${MOSAIC_TOOLS:-}" ] || ALLOWED="$MOSAIC_TOOLS,mcp__mosaic"
|
||||
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
||||
"$CLAUDE" -p "$MOSAIC_REQUEST" \
|
||||
--output-format text \
|
||||
--system-prompt-file "$PROMPT_FILE" \
|
||||
--system-prompt "$PROMPT_CONTENT" \
|
||||
--model "$MOSAIC_MODEL" \
|
||||
--effort "$MOSAIC_THINKING" \
|
||||
--restricted \
|
||||
|
||||
+4
-14
@@ -8,17 +8,7 @@
|
||||
set -eu
|
||||
|
||||
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "pi adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
|
||||
# Row 213 (C2-08): pi gets the prompt as a path, so no local user reads it in /proc/<pid>/cmdline.
|
||||
# Pi 0.85.1 reads --system-prompt as a file only when the path exists and reads, and otherwise
|
||||
# uses the path string itself as the prompt, without a warning for a missing file
|
||||
# (dist/core/resource-loader.js resolvePromptInput). So the path must be a regular readable
|
||||
# file here, and is made absolute before the cd below, since pi resolves it from its own cwd.
|
||||
PROMPT_FILE=$MOSAIC_SYSTEM_PROMPT_FILE
|
||||
case "$PROMPT_FILE" in
|
||||
/*) ;;
|
||||
*) PROMPT_FILE="$(pwd)/$PROMPT_FILE" ;;
|
||||
esac
|
||||
[ -f "$PROMPT_FILE" ] && [ -r "$PROMPT_FILE" ] || { echo "pi adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
|
||||
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "pi adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
|
||||
# MOSAIC_AGENT_NAME is optional in headless mode (identity section is then
|
||||
# omitted); interactive launches always set it via scripts/agent.sh.
|
||||
|
||||
@@ -104,8 +94,7 @@ fi
|
||||
# All flags documented in the pi package README (CLI Reference):
|
||||
# -p/--print one-shot mode: print the response and exit (omitted in
|
||||
# interactive TUI mode)
|
||||
# --system-prompt replace the default prompt with the generated one, given
|
||||
# as the file's absolute path (checked above), never its text
|
||||
# --system-prompt replace the default prompt with the generated one
|
||||
# --no-* no ambient context/skills/extensions/templates/themes
|
||||
# EXT_FLAGS the explicitly provided extensions only (-e)
|
||||
# SESSION_FLAGS ephemeral | persistent | forked (per env)
|
||||
@@ -114,6 +103,7 @@ fi
|
||||
# --offline no startup network operations (update checks/telemetry)
|
||||
# --no-approve ignore project-local .pi/ files (settings, SYSTEM.md)
|
||||
# whatever trust is saved for the workspace
|
||||
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
||||
set -- \
|
||||
--offline \
|
||||
--no-approve \
|
||||
@@ -128,7 +118,7 @@ set -- \
|
||||
--provider "$PI_PROVIDER" \
|
||||
--model "$PI_MODEL" \
|
||||
$THINKING_FLAG \
|
||||
--system-prompt "$PROMPT_FILE"
|
||||
--system-prompt "$PROMPT_CONTENT"
|
||||
# One-shot mode appends -p and the request (both safely quoted);
|
||||
# interactive mode appends nothing - clean TUI.
|
||||
[ "$INTERACTIVE" = "1" ] || set -- "$@" -p "$MOSAIC_REQUEST"
|
||||
|
||||
@@ -16,22 +16,6 @@ not fleet seats. Keep changes in `/mnt/storage/src/mosaic-stack`; do not modify
|
||||
| Rocko | General development, investigation, testing, and review | T3 session (Codex, gpt-6.1-sol, thread d99326cc, lead decision 122; Claude thread b84bb264 retired 2026-10-12); launcher Claude Code, Sonnet model | `agents/rocko/launch.sh` |
|
||||
| Filbert | General development, investigation, testing, and review | T3 session (Claude, Opus 5.5, thread 048f3c63, non-sandbox reviews only, lead decisions 130 and 134; Codex threads f712d8af and 50342f6a retired 2026-10-13, Claude thread 9cb9731e retired 2026-10-12); launcher Pi, `openai-codex/gpt-6-astra:low`, not to be used under R26 until it changes (lead decision 69) | `agents/filbert/launch.sh` |
|
||||
| Researcher | Source-grounded technical research and evidence | T3 session (Claude, Sonnet 5.5, thread 9bac0794, rows 97 and 98, lead decision 124); Haiku rows (71, 80, 84, 88, later 96) run as subagents Sage dispatches with model haiku, effort high and a worktree (lead decision 129); Haiku thread c98aaf4a retired 2026-10-13 unused; launcher Pi, configured Mosaic model | `agents/researcher/launch.sh` |
|
||||
| Launchpad | Builder, MVP T1 row 192 (Q175 SSO phase 1) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Morgana | Builder, MVP T1 row 193 (human users and RBAC) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Honker | Builder, MVP T1 row 194 (agent lifecycle) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Gander | Builder, the human boundary across uids (P19 333, #1694) | T3 session (Codex, gpt-6.1-sol), started by Sage under mos-0977 | none, T3 only |
|
||||
| Herb | Builder, a second gate lane (#1695) | T3 session (Codex, gpt-6.1-sol), started by Sage under mos-0977 | none, T3 only |
|
||||
| Bushroot | Builder, retire the control board writer on 7331 (#1696) | T3 session (Claude, claude-haiku-5-5), started by Sage under mos-1008 | none, T3 only |
|
||||
| Megavolt | Builder, MVP T1 item 6 (agent credentials and git identity, #1697) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Quackerjack | Builder, federation R5 and R2 (#414) | T3 session (Claude, claude-haiku-5-5), started by Sage under mos-1023 | none, T3 only |
|
||||
| Gizmoduck | Federation design (#414) and builder of FED R1 | T3 session (Claude, Opus 5.5, thread 9312a896) | none, T3 only |
|
||||
| Webby | Reviewer: Case 2 designs, rows 192 and 194 | T3 session (Claude, Opus 5.5, thread 03086006), started by Mos (mos-0981) | none, T3 only |
|
||||
| Duckworth | Reviewer: federation designs, row 193 | T3 session (Claude, Opus 5.5, thread ffceafec), started by Mos (mos-0977) | none, T3 only |
|
||||
| Della | Builder, email plan (Vikunja 460) and row 224 (C2-05b) | T3 session (Codex, gpt-6.1-sol, thread 66735a7f), started by Mos | none, T3 only |
|
||||
| Scrooge | Builder of row 252 and reviewer of row 272 | T3 session (Claude, Opus 5.5, thread 629b6cd3), started by Mos | none, T3 only |
|
||||
| Huey | Builder, row 272 (T1 sessions) | T3 session (Codex, gpt-6.1-sol, thread cabd45ca), started by Mos | none, T3 only |
|
||||
| Louie | Builder and research, editor evaluation and row 174 | T3 session (Codex, gpt-6-luna, thread bfd13d49), started by Mos | none, T3 only |
|
||||
| Ludwig | Builder and reviewer, post-v1 email PRD and row 258 (E01) | T3 session (Claude, Opus 5.5, thread 8615b2a6), started by Mos | none, T3 only |
|
||||
| Sage | Project lead: coordination, review, integration; earlier DYOR strategy records retained | T3 session (Claude Code); Pi launcher `zai/glm-5.3:high` retained | `agents/sage/launch.sh` |
|
||||
|
||||
Each script supports `--check` and `--fresh`. Normal launches resume the agent's
|
||||
@@ -47,14 +31,6 @@ its launch configuration remains fixed, including on resume. Use Filbert's
|
||||
wrapper to select that configuration; a direct shared-helper invocation uses
|
||||
its own supplied flags or the system defaults.
|
||||
|
||||
Any run that includes the webui suite, whether a seat's slot run or a landing
|
||||
gate, sets `MOSAIC_REQUIRE_EDITOR_BUILD=1`, so a missing editor toolchain fails
|
||||
the run instead of skipping the rebuild test. A worktree that links the
|
||||
canonical `node_modules` also runs `npm ci --ignore-scripts --no-audit --no-fund`
|
||||
in its own `packages/webui` first. The root install carries esbuild 0.28.1
|
||||
through pi-coding-agent, and the rebuild needs the 0.28.2 that
|
||||
`packages/webui/package-lock.json` pins (Vikunja 597, mos-1453).
|
||||
|
||||
All agents follow repository governance and current user direction. Team
|
||||
leadership does not add deployment or push authority. Coordinate overlapping
|
||||
work with Sage and preserve other sessions' changes.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Beakley
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 217 (C2-06, handed over from Launchpad).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 88b97f65) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `beakley/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -1,10 +0,0 @@
|
||||
# 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).
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
@@ -1,23 +0,0 @@
|
||||
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
|
||||
@@ -1,90 +0,0 @@
|
||||
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
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
@@ -1,90 +0,0 @@
|
||||
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
|
||||
@@ -1,245 +0,0 @@
|
||||
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
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
@@ -1,207 +0,0 @@
|
||||
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
|
||||
@@ -1,165 +0,0 @@
|
||||
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)
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
@@ -1,45 +0,0 @@
|
||||
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
|
||||
@@ -1,34 +0,0 @@
|
||||
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
|
||||
@@ -1,219 +0,0 @@
|
||||
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
|
||||
@@ -1,363 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -1,36 +0,0 @@
|
||||
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
|
||||
@@ -1,37 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/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"
|
||||
@@ -1,17 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
--- 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 ]
|
||||
@@ -1,8 +0,0 @@
|
||||
# Boyd
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 298 (the chief-of-staff grant scope, handed over from Megavolt).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 48a3ae38) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `boyd/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -1,272 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,8 +0,0 @@
|
||||
# Bubba
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos under Q245 (mos-1506). Owns row 288 (agent write notices and library revert, handed over from Honker).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 3d6672b5) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `bubba/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Claude seats orchestrate and review (Q245). Registered by Sage on
|
||||
2026-10-23 so the queue can name the seat as row owner (store.mjs isSeatDir).
|
||||
@@ -1,8 +0,0 @@
|
||||
# Bushroot
|
||||
|
||||
Builder seat, Claude Haiku 5.5, started by Sage under mos-1008 item 2 (a low-level model under Q177) for the row
|
||||
that retires the control board writer on 7331 (#1696).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `bushroot/*`
|
||||
branches through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,161 +0,0 @@
|
||||
# Row 195 build: `scripts/case1-accept.sh`
|
||||
|
||||
Row 195 (#1693), the Case 1 install on 10.1.1.53. Design r7 at 5425ea2f3, approved by Duckworth
|
||||
(duckworth-0087, `agents/duckworth/work/row195/REVIEW-r7.md` at 58d930f89; Gitea 29962). Sage's go to build
|
||||
is sage-1817.
|
||||
|
||||
## What this commit holds
|
||||
|
||||
- `scripts/case1-accept.sh` (new, 100755): section 5's acceptance, F1-F11 and P1-P11.
|
||||
- `packages/cli/tests/case1-accept.test.mjs` (new): section 5's cases.
|
||||
- `docs/TOOLS.md`: the `case1-accept.sh` entry after the hostcheck entry.
|
||||
- This file.
|
||||
- `DESIGN.md`, doc-only fixes Sage named in sage-1817, with no new design round:
|
||||
- Duckworth's N1. Section 1's human-id row cited `broker.mjs:245`; the check is `broker.mjs:321`.
|
||||
- Row 226's state at :21 and :326. It said "unclaimed"; 226 is assigned to Gosalyn, briefed and not
|
||||
started (sage-1814).
|
||||
- :340 said I'd send the section 6 items to Gosalyn once 226 was claimed. I sent them on 2026-10-21
|
||||
and Gosalyn acknowledged (gosalyn-0085), so the line now says that.
|
||||
|
||||
Nothing else changes. No `case2-*.sh`, `stack-upgrade.sh`, `stack-release.sh`, `case1-hostcheck.sh` or run
|
||||
sheet edit, and nothing touches .53 (sage-1809).
|
||||
|
||||
## Deviations from section 5, and choices it left open
|
||||
|
||||
1. P4's mark is the single token `accepted Q182+Q183`. Section 5 lists `accepted Q182` and `accepted
|
||||
Q183` as marks and gives P4 both. One line can carry one mark, and P4 is the only Q182 path, so the
|
||||
count list has `accepted Q182+Q183` in place of `accepted Q182`.
|
||||
2. The test seam replaces `getent`. Under `--root`, the script reads `DIR/etc/passwd` and `DIR/etc/group`
|
||||
with awk. Without it, the same helper calls `getent`. The groups of a user are its primary gid, then
|
||||
each group whose member list names it, which is what `id -G` prints. So there is no fake `id`, and
|
||||
the script never calls `id` for another user.
|
||||
3. P10 reads the port floor from `/proc/sys/net/ipv4/ip_unprivileged_port_start` rather than calling
|
||||
`sysctl`, so the seam covers it the way hostcheck's item 7 is covered. There is no fake `sysctl`.
|
||||
4. There is no fake `bwrap`, `claude`, `node` or `hostcheck.sh` on PATH. The script runs each by the
|
||||
absolute path the args file or the install names, so the test puts stubs at those paths under
|
||||
`--root`, and the fake `sudo` runs the stack's user's commands from the tree.
|
||||
5. The args file must say `--profile factory` and name `--business`. Both are new refusals beyond
|
||||
`stack-upgrade.sh`'s rules. A library file, or one for another business, would make F4 and F7
|
||||
check the wrong thing. After `--`, `--port` and `--tester-port` must be numeric. The args file is the
|
||||
only source of the ports.
|
||||
6. F8, F9 and F11 read file metadata as root: `stat`, `-L` and `find -maxdepth 1 -name '*.json'`. None
|
||||
opens the credential. F9 parses `managed-settings.json` with the pinned node, run by root: that node
|
||||
and that file are both root's own, installed by ops-01.
|
||||
7. F7 reads `launch.by` from `$HOME/.config/mosaic-dev/businesses/<id>.json` as the stack's user. That
|
||||
path is `businessFilePath(id)` under the default config, which is what the unit uses. It doesn't load
|
||||
the copy's `business.mjs` as `stack-release.sh`'s `plugins launchby` does. The user runs the whole
|
||||
check and prints one line, and root accepts only `ok <instance> run <run>` (sage-1653: root doesn't
|
||||
parse the user's output beyond a fixed pattern).
|
||||
8. F5 names a process claude when its argv0's basename or its `comm` starts with `claude`. The window
|
||||
confirms this against a real role session. If the bwrap'd claude shows another name, F5 fails closed
|
||||
and the fix is this match, not the label check.
|
||||
9. F3 and F5 run together. `talk` goes out in the background under `timeout 330`, and the `/proc` scan
|
||||
repeats every half second until it finds a labelled pair or `talk` exits. F3's line prints first, then
|
||||
F4, then F5, so the output keeps id order. F3 passes on exit 0 with the `sent` line and a reply line,
|
||||
both counted on stdout only (`cli.mjs:395` and the `formatMessage` write); talk's error goes to stderr.
|
||||
12. `--claude` must match `^/[A-Za-z0-9/._+@-]+$`. The `@` is for `node_modules/@anthropic-ai/...`;
|
||||
`stack-upgrade.sh` doesn't check `--claude` at all.
|
||||
10. F1 requires all four uids in `/proc/<MainPID>/status` (real, effective, saved, filesystem) to be the
|
||||
user's.
|
||||
11. Evidence text the stack's user controls (talk's last line, the precheck's last line, F6's and F10's
|
||||
`--version`) is cut to printable ASCII, one line, 200 bytes.
|
||||
|
||||
## Tests
|
||||
|
||||
`packages/cli/tests/case1-accept.test.mjs`, 22 tests. Every case builds a scratch host with every F check
|
||||
passing and every P check closed or accepted, then changes one thing:
|
||||
- all pass: exit 0, ids in order, each P mark, the count lines match the check lines, and the stand-in
|
||||
credential's value appears nowhere;
|
||||
- each F check failing alone exits 1 with only its line failing;
|
||||
- F3 on exit 0 with only the `sent` line, and its evidence taking talk's stderr;
|
||||
- F1 on a failed unit and on `User=root`; F2 on healthz failing and on another pid holding the port;
|
||||
- F5 on `unpriv_bwrap (enforce)`, `bwrap//&unpriv_bwrap (complain)`, a trailing space on either label,
|
||||
no bwrap of the user's, and passing on `comm` alone;
|
||||
- F6 on a stub that prints nothing and on a path outside `claude-<pin>`;
|
||||
- F7 on two live PM launches, a live one for another business, and no `launch.by`;
|
||||
- F8 on 0640, on another owner and on a symlink, never printing the credential;
|
||||
- F9 on a group-writable mode, another owner and a number for the flag;
|
||||
- F11 on one `*.json` under `discord/`, and passing on an empty directory;
|
||||
- each P check's unnamed mark, with the line count each fault makes: P6 one line per member, P9 one per
|
||||
missing item; P2, P5, P10 and P11 in more than one way;
|
||||
- every P check unnamed at once still exits 0;
|
||||
- nine bad args files refuse with exit 2 before any command runs, each with its own refusal text;
|
||||
- usage errors, and a user's run without `--root`, exit 2;
|
||||
- every in-instance call goes through `sudo -u mosaic -H env PATH=<node dir>:/usr/local/bin:/usr/bin:/bin`,
|
||||
read from the fake `sudo`'s argv, and root's other `sudo` calls are exactly `-l -U mosaic`,
|
||||
`-l -U localadmin` and the jarvis bwrap probe;
|
||||
- the host check records `--check` and never `--enforce`.
|
||||
|
||||
## Mutants
|
||||
|
||||
Section 5's twelve, each one exact replacement in `scripts/case1-accept.sh`, run by
|
||||
`/mnt/storage/scratch/darkwing-195-out/mutants.py` in Sage's slot (sage-1865). All twelve were killed.
|
||||
After each one the script's bytes were restored, and the final sha256 matched (`d434f15ff638...`). The
|
||||
killers below are the tests that failed in each mutant's saved log.
|
||||
|
||||
| # | Mutant | Killed by |
|
||||
|---|---|---|
|
||||
| 1 | F1 passes on a MainPID owned by root | each F alone |
|
||||
| 2 | F2 accepts a listener on 0.0.0.0 | each F alone |
|
||||
| 3 | F3 passes without a reply | each F alone; F3 exit 0 with no reply line |
|
||||
| 4 | F5 matches a substring | F5 labels (the trailing space) |
|
||||
| 5 | F6 skips the `--version` check | each F alone; F6 stub and path |
|
||||
| 6 | F8 skips the owner check | F8 0640, owner and symlink |
|
||||
| 7 | F11 ignores `discord/` | each F alone; F11 `*.json` |
|
||||
| 8 | P1 closes on any output | P unnamed; every P unnamed |
|
||||
| 9 | P5 maps to `closed` with jarvis in docker | P unnamed; every P unnamed; P2 and P5 names docker |
|
||||
| 10 | P9 drops the jarvis item | P unnamed; P9 per item |
|
||||
| 11 | P11 accepts any `Before=` | P unnamed; every P unnamed |
|
||||
| 12 | a P check's `unnamed` sets the exit to 1 | P unnamed; every P unnamed; P10 and P11; P2 and P5; P9 per item |
|
||||
|
||||
Mutant 1 survived the F1 variants test (a failed unit, `User=root`), because those faults fail F1 on the
|
||||
unit's state and `User=`, not on the uid line. Each F alone covers the uid line.
|
||||
|
||||
## Pre-slot read review
|
||||
|
||||
Since every test run is slot work, a read-only subagent traced each case through the script before the
|
||||
first slot run, executing nothing. It found four defects, all fixed in this commit:
|
||||
- The `--claude` regex had no `@`, so every run refused the args file, the real .53 one included.
|
||||
- The fake `stat` logged the args-file call, so the bad-args test's "no command ran" check would fail.
|
||||
- The P5 and P6 faults each rewrote `etc/group` from the constant, so applying both lost P5's change.
|
||||
- F3 counted stderr into its line count, so a mutant dropping the exit check survived. F3 now counts
|
||||
stdout only, and a `sent`-only case covers the line count.
|
||||
|
||||
## Run environment
|
||||
|
||||
The build commit ddf7dce6c sits on base 4ced5e63d (origin/refactor when it was pushed). It was first built
|
||||
on c515273db as 4a51d392c. Row 238 then changed `docs/TOOLS.md`, so I cherry-picked the three commits onto
|
||||
4ced5e63d as `darkwing/row195-b`. `git range-diff` shows all three identical. node v26.8.1 on dragon-lin,
|
||||
every run behind my busy check and Sage's, chained with `&&`.
|
||||
|
||||
- Step 1, sage-1829 (07:41:54Z to 07:42:05Z), TMPDIR `/tmp/dw195`:
|
||||
`node --test packages/cli/tests/case1-accept.test.mjs`, 22 pass, 0 fail, 11.0 s.
|
||||
- Step 2, sage-1865 (08:34:27Z to 08:36:21Z), TMPDIR `/mnt/storage/scratch/tmp/r195`: the twelve mutants
|
||||
above, all killed.
|
||||
- Step 3, the gate (08:36:21Z to 08:47:06Z), same TMPDIR, `COMPOSE_PROJECT_NAME=darkwing-r195`:
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| packages/cli (`node --test tests/*.test.mjs`) | 662 pass, 0 fail, 0 skipped |
|
||||
| test-auth.sh | 15 passed, 0 failed |
|
||||
| test-conductor.sh | 17 passed, 0 failed |
|
||||
| test-config.sh | 31 passed, 0 failed |
|
||||
| test-discord.sh | 78 passed, 0 failed |
|
||||
| test-extension-package.sh | 18 passed, 0 failed |
|
||||
| test-foundation.sh | 44 passed, 0 failed |
|
||||
| test-land-work.sh | 86 passed, 0 failed |
|
||||
| test-queue.sh | 156 node pass, 0 fail; 28 passed, 0 failed |
|
||||
| test-release.sh | 14 passed, 0 failed |
|
||||
| test-task.sh | 98 passed, 0 failed |
|
||||
|
||||
Summed: 818 node tests passed with 0 failed, and 429 selftests passed with 0 failed. The compose network
|
||||
was removed afterwards, and the sweep found no process left in the worktree, the output directory or the
|
||||
TMPDIR. This file's results are a doc-only commit on top of the build, so nothing that ran has changed.
|
||||
|
||||
## Correction to deviation 6 (follow-up, 2026-10-21)
|
||||
|
||||
Deviation 6 says the node F9 runs is root's own. At 37171f65b the script didn't check that. F9 ran
|
||||
`--node` as root before F10's path check, so the header's "root itself only reads" was false
|
||||
(Duckworth's N1, `agents/duckworth/work/row195/REVIEW-m1.md` at 6afcee2a5). A host installed as designed
|
||||
was safe, because `case2-unit.sh` refused a non-root-owned `--node`. Row 245 carries the fix and its
|
||||
tests: `agents/darkwing/work/queue-245/BUILD.md`.
|
||||
@@ -1,388 +0,0 @@
|
||||
# Row 195 design: the Case 1 install on 10.1.1.53 (r7)
|
||||
|
||||
Darkwing, 2026-10-21. Brief: `docs/plans/2026-10-20_hub-own-uid.md`, blob 45be7f09afc1, section "Brief: the
|
||||
Case 1 install on 10.1.1.53 (row 195)". Gitea #1693. Reviewer Duckworth, gate owner Sage. Branch
|
||||
`darkwing/row195-r7` on c515273db. r6 (b7239bb66 on `darkwing/row195`, approved duckworth-0055) is the base,
|
||||
carried unchanged as 7398b7dd7. The r6 text is in that commit and in b7239bb66.
|
||||
|
||||
## Changes from r6
|
||||
|
||||
Q187 (mos-1108) made install and upgrade product commands, and mos-1118 split r6 into the installer rows
|
||||
(`docs/plans/2026-10-20_installer-rows.md`). That brief says row 195 "shrinks to the factory profile's
|
||||
values and the Case 1 acceptance". So r7 is mostly a cut. What r6 designed now ships elsewhere:
|
||||
|
||||
| r6 piece | Now | State |
|
||||
|---|---|---|
|
||||
| `stack-release.sh --units external`: `confirm`, `revert`, `gate`, `rollback --to`, external `adopt` and `status`; `stack-copy.sh check --units external` and bundle copies | row 232, I1 | landed |
|
||||
| `case1-repin.sh` (A1-A5, A2f, R1-R4) | `scripts/stack-upgrade.sh`, row 233, I3 | landed |
|
||||
| the host steps 2a, 2a', 2a'' and the window's root steps | `docs/guides/host-run-sheet.md` sections 1-10, row 233 | landed |
|
||||
| `case1-hostcheck.sh`, `case1-pins.sh`, `mosaic-hostcheck.service` (the drift stop, r6 B2) | row 240, I4 | landed c515273db |
|
||||
| the unit render, `--profile factory`, `--pm`, `--claude`, the factory serve flags, the precheck | row 230, C2-13a, I2 | landed |
|
||||
| the install as `mosaic`: bootstrap, business file, copy | row 226, C2-13b, I2, `scripts/case2-install.sh` | assigned to Gosalyn, briefed, not started |
|
||||
| `docs/guides/case1-install.md` | the host run sheet, plus section 6 of this file | |
|
||||
|
||||
Other changes:
|
||||
|
||||
- Claude moves under `/opt` (sage-1522, run sheet section 3). `--claude` is
|
||||
`/opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe`, root's, and root
|
||||
installs and upgrades it. r6's `/srv/mosaic/.local/bin/claude` is gone. F6 reads the new path.
|
||||
- The args file is `/etc/mosaic-stack/dev.args` (run sheet section 7), not `case1-unit.args`. It holds
|
||||
`--copy` too, and `stack-upgrade.sh` rewrites only that line, after confirm passes.
|
||||
- Both units are `WantedBy=multi-user.target` and start at boot (Q189, mos-1122). r6's "the Console has no
|
||||
`[Install]`, root starts it by hand after a reboot" no longer holds.
|
||||
- The business id is stated, with the DEFERRED 240 N1 reasoning (sage-1809, sage-1813). Section 7.
|
||||
- The window can't run until row 226 lands. Sage added 226 to row 195's after list at rev 2718
|
||||
(sage-1813). Section 6 lists what row 195 needs from 226's install.
|
||||
- The acceptance runs as root through ops-01, not as `localadmin`. `aa-status`, `sudo -l -U`,
|
||||
`systemd-run --uid` and the hostcheck all need root. It gains four functional checks for items the run
|
||||
sheet marks "precheck line missing" (F8-F11) and one host line (P11). Section 5.
|
||||
- The Discord row in section 2 changed. The precheck's "no Discord binding" line is C2-13b's and is
|
||||
library-only, so for the factory it's F11 in the acceptance.
|
||||
|
||||
What row 195 still builds: `scripts/case1-accept.sh` and its test (section 5). The rest of this file is
|
||||
inputs, the window order and the record the reviewer checks.
|
||||
|
||||
## 0. What I checked on the host
|
||||
|
||||
Read only, as `localadmin` over ssh, 2026-10-20 between 20:40Z and 21:51Z. Nothing in r7 touched .53
|
||||
(sage-1809). The window's first step re-reads these, since they can drift in a day: unattended-upgrades
|
||||
runs daily.
|
||||
|
||||
| Item | 10.1.1.53 on 2026-10-20 | Needed |
|
||||
|---|---|---|
|
||||
| OS | Ubuntu 24.04.5, kernel 6.8.0-124, x86_64, 12 cores, 94 GiB, 217 GiB free on `/` | fits |
|
||||
| `mosaic` user, group, `/srv/mosaic` | absent | run sheet section 1 |
|
||||
| node | `/usr/local/bin/node` v22.22.2, root-owned | 24 or later, under `/opt` (run sheet section 2) |
|
||||
| claude | not on localadmin's PATH | under `/opt` (run sheet section 3) |
|
||||
| bwrap | absent; apt candidate `bubblewrap 0.9.0-1ubuntu0.3` | run sheet section 4 |
|
||||
| user namespaces | `kernel.apparmor_restrict_unprivileged_userns = 1`, apparmor 4.0.1, no bwrap profile loaded | run sheet section 4 |
|
||||
| profiles that name bwrap | none (`grep -rlw bwrap /etc/apparmor.d` exits 1) | hostcheck item 3 |
|
||||
| `fs.protected_hardlinks` | 1 | hostcheck item 6 |
|
||||
| `net.ipv4.ip_unprivileged_port_start` | 1024 | run sheet section 5 |
|
||||
| listeners below 1024 | 22 (sshd) and 53 (the resolver); 732 is free | the Console's port |
|
||||
| ports 7330-7333 | free | 7333 is the tester port |
|
||||
| `localadmin` | NOPASSWD sudo (#423 step 1, mos-1066, mos-1077) | ops-01's root path |
|
||||
| `jarvis` | groups sudo, docker, lxd, adm | #423 step 4 removes them (P5) |
|
||||
| unattended-upgrades | on, with the security pocket, which carries bubblewrap | the drift stop (row 240) |
|
||||
| journald | `SplitMode=uid` | the PM check reads `launches list`, not the journal (row 232) |
|
||||
|
||||
## 1. The factory profile's values
|
||||
|
||||
Instance `dev`, row 190's default (mos-1118 choice 4). `~` is `mosaic`'s home.
|
||||
|
||||
| Input | Value | Who gives it |
|
||||
|---|---|---|
|
||||
| user | `mosaic`, primary group `mosaic` with no other member | fixed |
|
||||
| home | `/srv/mosaic`, 0700 | fixed |
|
||||
| config dir | `/srv/mosaic/.config/mosaic-dev` | row 190 |
|
||||
| data root | `/srv/mosaic/.mosaic-dev`, marker `{"instanceVersion":1,"name":"dev"}` | row 190 |
|
||||
| business | `mosaic-stack`, file `<configDir>/businesses/mosaic-stack.json` | fixed (section 7) |
|
||||
| profile | `factory` for `case2-install.sh`, `case2-unit.sh` and `case2-precheck.sh` | fixed |
|
||||
| roles | the factory set from `roles/`, with a chief that has a harness var | the go names the set (section 2) |
|
||||
| human id | Jason's, not `tester` (`broker.mjs:321`) | the go |
|
||||
| model credential | `<configDir>/secrets/claude-session.env`, `mosaic` 0600, one line `CLAUDE_CODE_OAUTH_TOKEN=...` | Jason places it (section 3) |
|
||||
| token slot path | none | section 2 |
|
||||
| node | `/opt/mosaic-stack/node-<v>/bin/node`, `<v>` 24 or later | the go names `<v>` and its sha256 |
|
||||
| claude | `/opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe`, `<pin>` 2.1.304 unless the go names a newer one | the go |
|
||||
| PM | on (`--pm`) | fixed |
|
||||
| Console | `--port 732` on 127.0.0.1, with the unit's `CAP_NET_BIND_SERVICE` | fixed |
|
||||
| tester | `--tester-port 7333` on 127.0.0.1 (P10) | fixed |
|
||||
| board | none | fixed |
|
||||
| release | a git bundle of `refs/heads/refactor` and the 40-hex commit (run sheet section 6) | the go names both and the bundle's sha256 |
|
||||
|
||||
The args file. `/etc/mosaic-stack/dev.args`, `root:root` 0644, one argument per line (run sheet section 7):
|
||||
|
||||
```
|
||||
--profile
|
||||
factory
|
||||
--user
|
||||
mosaic
|
||||
--home
|
||||
/srv/mosaic
|
||||
--business
|
||||
mosaic-stack
|
||||
--copy
|
||||
<40-hex commit>
|
||||
--node
|
||||
/opt/mosaic-stack/node-<v>/bin/node
|
||||
--claude
|
||||
/opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe
|
||||
--pm
|
||||
--
|
||||
--port
|
||||
732
|
||||
--business
|
||||
mosaic-stack
|
||||
--tester-port
|
||||
7333
|
||||
```
|
||||
|
||||
`case2-unit.sh` takes exactly these before `--` (`scripts/case2-unit.sh:54`), and `stack-upgrade.sh`
|
||||
refuses `args-unreadable` on anything else (`scripts/stack-upgrade.sh:126-148`). The factory profile
|
||||
refuses the preview trio, `--send` and the proof pair (`case2-unit.sh:169`). .53 has no proxy, and Jason
|
||||
reaches the Console with `ssh -L` (sage-1451 Q3). Changing a serve flag is a re-install with its own go,
|
||||
not an upgrade.
|
||||
|
||||
Why the port is 732 (r6, mos-1101 item 5). Role sessions share the host's network namespace
|
||||
(`packages/seat/src/sandbox.mjs:67`), so while the Console is down a session could bind a port above the
|
||||
floor and answer in its place. Below 1024 only the Console unit can bind, through its capability. The
|
||||
tester port can't go below 1024 (`packages/webui/src/cli.mjs:33`), so P10 records it.
|
||||
|
||||
PATH for `mosaic`'s steps. `sudo -u mosaic -H` resets PATH to sudo's `secure_path`, which finds
|
||||
`/usr/local/bin/node`, v22. So every `mosaic` step runs as
|
||||
`sudo -u mosaic -H env PATH=/opt/mosaic-stack/node-<v>/bin:/usr/local/bin:/usr/bin:/bin ...`, the list
|
||||
`case2-unit.sh` renders as the units' PATH. `stack-upgrade.sh` builds it from the args file's `--node`.
|
||||
|
||||
## 2. Where Case 1 differs from Case 2
|
||||
|
||||
| Case 2 (library) | Case 1 (factory) | Reason |
|
||||
|---|---|---|
|
||||
| business file with the library mapping | `mosaic-stack` with no `library` key. The precheck refuses a library project under `factory` (`profile-library-business`, `case2-precheck.sh:127-128`) | the factory has no books repo |
|
||||
| `bean`, `chet`, persona vars and files, specialists | the factory roles from `roles/`, with a chief that has a harness var | the go names the set. I haven't read Jason's live business file and won't. The acceptance needs only the chief (F3); a chief with no harness var answers `no-route`. The human adds roles later with `instances.set`, no new row |
|
||||
| token slot `library-gitea` | none | the factory's per-agent git identity is row 199's credential store (Megavolt, #1697). Until it lands, git work stays on dragon-lin (Q182) |
|
||||
| `bus.tester` off | on; the precheck refuses it off under `factory` (`case2-precheck.sh:135`) | the acceptance and the Q173 test system need the tester |
|
||||
| persona modes and digests, `library.sync`, `library.status` | skipped | no persona files, no library (C2-13b brief, `factory` profile) |
|
||||
| `ZAI_API_KEY` in `claude-session.env` | not checked; the file holds `CLAUDE_CODE_OAUTH_TOKEN` | no instance has provider `zai` (run sheet section 9) |
|
||||
| no Discord binding (C2-13b, library only) | none either. F11 checks that `<dataRoot>/discord/` holds no binding | the factory's connector stays on dragon-lin. Two connectors on one bot token would split its events |
|
||||
| the claude marker prompt | runs, since the chief's harness is claude-code | |
|
||||
| serve flags: preview trio, `--send`, proof pair | `--port 732`, `--business mosaic-stack`, `--tester-port 7333` | no proxy on .53 |
|
||||
| bus: no `--pm`, no `MOSAIC_CLAUDE_BIN` | `--pm`, and `Environment=MOSAIC_CLAUDE_BIN=<--claude>` | the PM dispatches the factory's work; without the path the bus finds no claude on the unit's PATH |
|
||||
| Console proof file (run sheet section 9, .54) | none | no proof pair |
|
||||
|
||||
All of these are inputs to the landed scripts. Row 230 implemented the four factory items r6 asked for
|
||||
(serve flags, `--pm`, `--claude`, row 190's tokens), and nothing in them hardcodes a Case 1 value.
|
||||
|
||||
## 3. Model access
|
||||
|
||||
Q185: Case 1 uses the account the factory uses today, the Claude subscription behind dragon-lin's
|
||||
`claude-session.env`. No script copies that file and none reads its value. In the window, Jason, or the
|
||||
person he names under the Q173 secrets rule, makes a credential for .53 from that account and writes it
|
||||
to `/srv/mosaic/.config/mosaic-dev/secrets/claude-session.env` as `mosaic`, mode 0600 (run sheet
|
||||
section 9). Nobody prints it. The check is `stat -c '%U %a %F' PATH` only (F8).
|
||||
|
||||
The bus unit reads it through `EnvironmentFile=-` (`case2-unit.sh:243`). The leading `-` means a missing
|
||||
file doesn't stop the bus, so the chief's first launch would fail instead. The window places the file
|
||||
before the units start (section 6, W9), and the precheck's claude marker prompt then proves it works
|
||||
without printing it.
|
||||
|
||||
Quota: the factory on dragon-lin and on .53 share one subscription. Q185's "use existing" accepts that.
|
||||
The pi harness stays off on .53 until P19 #383 settles (mos-0865). The business file names claude only.
|
||||
|
||||
## 4. Upgrade, rollback and backup on .53
|
||||
|
||||
All three are `scripts/stack-upgrade.sh` (row 233), run as root by ops-01 from `/root/mosaic-stack` at the
|
||||
reviewed commit. Usage, exits and the step log are in `docs/TOOLS.md`, "Root's upgrade driver", and run
|
||||
sheet section 6.
|
||||
|
||||
| Action | Command | Who | Go |
|
||||
|---|---|---|---|
|
||||
| upgrade | `stack-upgrade.sh upgrade --instance dev --bundle /srv/mosaic/incoming/<sha>.bundle --sha <sha>` after run sheet section 6's per-upgrade lines | ops-01 | Mos per run; `--migrate` also needs Jason present (decision 96) |
|
||||
| rollback | `stack-upgrade.sh rollback --instance dev --to <copy> [--restore <ACT> [--events N]]` | ops-01 | Mos per run. Mos or Sage picks `<copy>` from `stack-release.sh status mosaic-stack --units external`, read as `mosaic`. The plan refuses unless it is the previous distinct copy |
|
||||
| backup of the store | A3's `backup_store` into `<dataRoot>/backups/stack/<ACT>`, inside every upgrade | the driver | the upgrade's |
|
||||
| rehearsal | `stack-release.sh rehearse` against the log's newest backup, before anything stops, on `--rehearse-port`, default 7339, which section 0 found free | the driver | the upgrade's |
|
||||
| backup of the VM | a PBS backup of VM 1008 before the install window and before any `--migrate` | ops-01 | the window's go (Q182, Q183: agents keep PBS) |
|
||||
|
||||
Three .53 specifics:
|
||||
|
||||
1. The first upgrade after install needs `--no-rehearsal` in its go. The install's adopt takes no backup,
|
||||
so the log records none, the rehearsal exits 3 (`no-backup`), and the driver goes on only with the
|
||||
flag (`stack-upgrade.sh:323-324`). Every later upgrade refuses the flag (`rehearsal-required`).
|
||||
2. Exit 5 (`args-failed`): the new stack is live and confirmed, but `dev.args` still names the old copy.
|
||||
ops-01 moves the file the result line names into place with `mv -f`, and nothing else (run sheet
|
||||
section 6).
|
||||
3. Exit 2 leaves both units stopped and the activation open. ops-01 sends Mos the exit and the last
|
||||
lines of `/var/log/mosaic-stack/upgrade-dev.log`, and Jason or Mos settles it with `confirm` or `revert`
|
||||
as on dragon-lin. A unit that doesn't stop within `--timeout` ends here on purpose (A2f's
|
||||
`stale-start`, duckworth-0055 N1, now in run sheet section 6).
|
||||
|
||||
The store backup doesn't hold the config dir or the credential. PBS backups of VM 1008 hold the whole disk,
|
||||
the credential included (P4). A restore swaps only the store and leaves `claude-session.env` in place.
|
||||
|
||||
Re-install, for a serve flag or role change, is not an upgrade. It needs its own go and runs: stop the
|
||||
Console, then the bus; rewrite `dev.args` with the same `--copy`; `case2-unit.sh` with it; both units start
|
||||
again from the render. Row 230 N1 (webby-0046) holds here: a re-render with the units running leaves each on
|
||||
its old copy, so ops-01 stops both first. `stack-upgrade.sh` already stops both before it renders.
|
||||
|
||||
## 5. Acceptance: `scripts/case1-accept.sh`
|
||||
|
||||
New in this row. ops-01 runs it as root on .53, from `/root/mosaic-stack`:
|
||||
|
||||
```
|
||||
case1-accept.sh --business mosaic-stack --args /etc/mosaic-stack/dev.args --claude-pin <pin> --node-pin <v>
|
||||
```
|
||||
|
||||
It reads the user, home, node, claude and ports from the args file and refuses (exit 2) when that file
|
||||
fails `stack-upgrade.sh`'s rules. Every check inside the instance runs as
|
||||
`sudo -u mosaic -H env PATH=<node dir>:/usr/local/bin:/usr/bin:/bin`. It changes nothing on the host
|
||||
except what F3's message writes to the store. It prints one line per check, `<id> <path> <mark>
|
||||
<evidence>`, then a count of each mark. Marks: `closed`, `accepted Q182`, `accepted Q183`, `accepted
|
||||
mos-1096`, `accepted mos-1101`, and `unnamed`, which means "not named in Q182, Q183 or a Mos ruling".
|
||||
It exits 1 when any F check fails and 0 otherwise. A P check never changes the exit. Each `unnamed`
|
||||
line goes to Mos, who takes it to Jason (R8 N1). Duckworth reads the whole output.
|
||||
|
||||
It prints no value from `claude-session.env` and never opens it.
|
||||
|
||||
Functional checks. Any failure fails the row:
|
||||
|
||||
| Id | Check | How |
|
||||
|---|---|---|
|
||||
| F1 | the units run as `mosaic` | for `[email protected]` and `mosaic-webui.service`: active, `User=mosaic`, and the uid in `/proc/<MainPID>/status` is `mosaic`'s |
|
||||
| F2 | the Console answers on 127.0.0.1:732 only | `curl -fsS http://127.0.0.1:732/healthz` succeeds, and `ss -Hltnp` shows 732 bound to 127.0.0.1 only, by the Console's MainPID. Jason reaches it with `ssh -L 7332:127.0.0.1:732 [email protected]`. A LAN listener waits for Q175 SSO (sage-1451 Q3) |
|
||||
| F3 | one tester message gets a reply | as `mosaic`: `mosaic talk chief <marker> --business mosaic-stack --tester --wait 300`. A stored reply to the marked message passes. The inbox shows decisions only, so it isn't used |
|
||||
| F4 | the precheck passes | `case2-precheck.sh --business mosaic-stack --profile factory` as `mosaic` exits 0 |
|
||||
| F5 | a role session runs under bwrap with the profile | while F3's reply is pending: a `/usr/bin/bwrap` process owned by `mosaic` has a claude child. Its `/proc/<pid>/attr/current` equals `bwrap (enforce)` and the child's equals `bwrap//&unpriv_bwrap (enforce)`, exact strings after the trailing newline is cut |
|
||||
| F6 | claude is the pin | the args file's `--claude` path sits under `/opt/mosaic-stack/claude-<pin>/`, and its `--version` as `mosaic` prints `<pin> (Claude Code)` |
|
||||
| F7 | the PM run is live | `launches list --json` as `mosaic` has exactly one live entry for the PM's instance (`launch.by`), the check `confirm` runs |
|
||||
| F8 | the credential's file | `stat -c '%U %a %F'` on `claude-session.env` reads `mosaic 600 regular file`, and it isn't a symlink. Run sheet section 9 marks this "precheck line missing" |
|
||||
| F9 | the autoupdater is off | `/etc/claude-code/managed-settings.json` is `root:root` 0644 and its `env.DISABLE_AUTOUPDATER` is `"1"`. Run sheet section 3 marks this "precheck line missing" |
|
||||
| F10 | node is the pin | the args file's `--node` is `/opt/mosaic-stack/node-<v>/bin/node` with `<v>` the `--node-pin`, and its `--version` prints `<v>`. Run sheet section 2 marks this "precheck line missing" |
|
||||
| F11 | no Discord binding | `/srv/mosaic/.mosaic-dev/discord/` is absent or holds no `*.json` (section 2) |
|
||||
|
||||
Host paths. These report and never fail the row:
|
||||
|
||||
| Id | Path | Mark | Evidence |
|
||||
|---|---|---|---|
|
||||
| P1 | `mosaic` has sudo | closed only when `sudo -l -U mosaic` prints "is not allowed to run sudo"; any other output is unnamed | the `sudo -l` result |
|
||||
| P2 | `mosaic` has supplementary groups or a key | closed when `id -G mosaic` is one entry and `~mosaic/.ssh/authorized_keys` is absent or empty | `id`, file state |
|
||||
| P3 | `localadmin` NOPASSWD sudo, reached with the dragon-lin key | accepted Q183 | `sudo -l -U localadmin` |
|
||||
| P4 | root on pve over VM 1008, and PBS backups that hold `/srv/mosaic` with the credential | accepted Q182 and Q183 | a fixed line; the script can't see pve |
|
||||
| P5 | `jarvis`, which runs Hermes, reaches `/srv/mosaic` through sudo, docker or lxd | closed when `jarvis` is in none of the three; unnamed otherwise. #423 step 4 removes all four groups before the install (mos-1091 4c) | `id jarvis` |
|
||||
| P6 | other members of sudo, docker, lxd | closed when sudo holds only `localadmin` and docker and lxd are empty; each other member is its own unnamed line | `getent group` lines |
|
||||
| P7 | a file under `/etc/sudoers.d` naming `mosaic` | closed when none | file names |
|
||||
| P8 | the Console or tester reachable from the LAN | closed when 732 and 7333 are bound to 127.0.0.1 only, each by the Console's MainPID, and nothing listens on 7331 | listener table |
|
||||
| P9 | a user namespace through `/usr/bin/bwrap` | accepted mos-1096 only while all eight hold, unnamed otherwise: hostcheck items 1 to 6, read from `/usr/local/lib/mosaic-stack/hostcheck.sh --check`'s own `ok N` lines (item 7 is P10's floor); `sudo -u jarvis /usr/bin/bwrap --version` fails with "Permission denied"; `getent group mosaic` lists no member and no other account has its gid. F5 adds the label of a real role session | the hostcheck lines and each item |
|
||||
| P10 | a session binds the tester port 7333 while the Console is down | accepted mos-1101 while the port floor is above 732 and 7333 is held by the Console's MainPID | the floor, the owner pid |
|
||||
| P11 | the boot check runs before the bus | closed when `mosaic-hostcheck.service` is enabled and `systemctl show -p Before` on it names `mosaic-bus@<business>.service` for the args file's business; unnamed otherwise (section 7) | the `Before=` value |
|
||||
|
||||
P9 reads hostcheck's output, which root's own script prints, by matching `^ok [1-6] ` lines. It runs `--check` only,
|
||||
never `--enforce`, so the acceptance can't close bwrap.
|
||||
|
||||
Tests, in `packages/cli/tests/case1-accept.test.mjs`. The script takes a test seam, `--root DIR`, for
|
||||
`/proc`, `/etc`, `/srv/mosaic` and `getent`, and refuses it when run as root, as the hostcheck does. Fake
|
||||
`systemctl`, `curl`, `ss`, `sudo`, `bwrap`, `sysctl`, `stat`, `claude`, `node` and `hostcheck.sh` on PATH.
|
||||
Nothing reaches .53 or dragon-lin's live state, and no test reads a real credential. Cases:
|
||||
- every check passing exits 0, with the counts matching the lines;
|
||||
- each F check failing alone exits 1 with its line;
|
||||
- F5 fails on `unpriv_bwrap (enforce)`, `bwrap//&unpriv_bwrap (complain)` and a trailing space;
|
||||
- F6 fails on a stub `claude.exe` that prints nothing and on a path outside `claude-<pin>`;
|
||||
- F8 fails on mode 0640, on another owner and on a symlink, and the stand-in credential's value never
|
||||
appears in the output;
|
||||
- F11 fails on one `*.json` under `discord/`;
|
||||
- each P check in each of its marks: P1 with output that grants nothing but lacks the refusal text
|
||||
(unnamed), P9 unnamed once for each item missing, P10 unnamed with the floor at 700 and with 7333 held
|
||||
by another pid, P11 unnamed with `Before=` naming another business;
|
||||
- every P check `unnamed` still exits 0;
|
||||
- a bad args file refuses with exit 2 before any check;
|
||||
- every in-instance call goes through `sudo -u mosaic -H env PATH=<node dir>:...`, read from the fake
|
||||
`sudo`'s recorded argv;
|
||||
- the hostcheck fake records `--check` and never `--enforce`.
|
||||
|
||||
Mutants, each killed by a named case:
|
||||
1. F1 passes on a MainPID owned by root.
|
||||
2. F2 accepts a listener on 0.0.0.0.
|
||||
3. F3 passes without a reply.
|
||||
4. F5 matches a substring.
|
||||
5. F6 skips the `--version` check.
|
||||
6. F8 skips the owner check.
|
||||
7. F11 ignores `discord/`.
|
||||
8. P1 closes on any output.
|
||||
9. P5 maps to `closed` with `jarvis` in docker.
|
||||
10. P9 drops the jarvis item.
|
||||
11. P11 accepts any `Before=`.
|
||||
12. A P check's `unnamed` sets the exit to 1.
|
||||
|
||||
## 6. The .53 window
|
||||
|
||||
Nothing in this row touches .53 (sage-1809). Every root step below is ops-01's, over
|
||||
`ssh [email protected]` with `sudo`, and waits on a Jason decision (mos-1086). The window needs:
|
||||
- row 226 landed;
|
||||
- Jason's or Mos's go naming the commit, the bundle's sha256, the node and claude pins, the role set and
|
||||
Jason's human id;
|
||||
- a backup first;
|
||||
- a STATE report after.
|
||||
|
||||
| # | Step | Runs as | Source |
|
||||
|---|---|---|---|
|
||||
| W0 | re-read section 0's items; stop on any change and report it | `localadmin`, read only | section 0 |
|
||||
| W1 | PBS backup of VM 1008 | ops-01 on pve | section 4 |
|
||||
| W2 | the stack's user and home | root | run sheet 1 |
|
||||
| W3 | node under `/opt` | root | run sheet 2 |
|
||||
| W4 | claude under `/opt`, managed settings | root | run sheet 3 |
|
||||
| W5 | bubblewrap, the profile, the statoverride, the no-new-privs test, the drift stop; `hostcheck.sh --check` exits 0 with item 7 at 1023 | root | run sheet 4 |
|
||||
| W6 | the port floor (read) | root | run sheet 5 |
|
||||
| W7 | the bundle, root's checkout at `<sha>`, `mosaic`'s clone | root, then `sudo -u mosaic -H` | run sheet 6 |
|
||||
| W8 | `case2-install.sh --profile factory` with section 1's inputs: bootstrap, the business file, roles, human id, the copy | `sudo -u mosaic -H env PATH=...` | row 226 |
|
||||
| W9 | place `claude-session.env` | Jason, or who he names | run sheet 9, section 3 |
|
||||
| W10 | write `dev.args` (section 1); `case2-unit.sh` from it, which enables both units and starts the bus, then the Console | root | run sheet 7, 10 |
|
||||
| W11 | `case2-precheck.sh --business mosaic-stack --profile factory` exits 0; `hostcheck.sh --check` exits 0 with item 7 at 732 | `mosaic`; root | row 230, 240 |
|
||||
| W12 | `stack-release.sh adopt mosaic-stack --units external`, unless row 226's install does it | `sudo -u mosaic -H` | row 232 |
|
||||
| W13 | `case1-accept.sh` (section 5); the output to Duckworth, then Mos confirms | root | this row |
|
||||
| W14 | STATE report to Mos | ops-01 | brief |
|
||||
|
||||
Install rollback, any step before W13 passes. Stop and disable both units, remove their files and
|
||||
`dev.args`, then the run sheet's rollbacks for sections 4, 3 and 2 in that order. Removing the user and
|
||||
home (`userdel -r mosaic`) needs its own Mos go, since it deletes the home. W1's backup covers the rest.
|
||||
|
||||
What row 195 needs from row 226. Row 226 is assigned to Gosalyn, briefed and not started, so I'm listing what the window
|
||||
needs from its design, not designing it:
|
||||
|
||||
1. `--profile factory` writes the business file with `bus.tester` on and no `library` key, and the roles
|
||||
the go names.
|
||||
2. It writes no token slot and no persona files (its brief says so).
|
||||
3. It runs before the unit render, since the copy must exist for `case2-unit.sh --copy`, and it starts no
|
||||
unit.
|
||||
4. Who writes `dev.args`. The C2-13b brief says the install doesn't write unit files, and the run sheet
|
||||
says "I2's install writes it". `mosaic` can't write `/etc/mosaic-stack`. So either root's part of I2
|
||||
writes it, or ops-01 writes it from section 1 at W10. Either works for row 195. The file's content is
|
||||
fixed above.
|
||||
5. Whether it runs `adopt`. If it doesn't, W12 does.
|
||||
|
||||
I sent these to Gosalyn on 2026-10-21 (gosalyn-0085 acknowledged), and the run sheet takes whichever answer 226 gives.
|
||||
|
||||
## 7. The business id
|
||||
|
||||
.53's business id is `mosaic-stack` (sage-1813). It appears in:
|
||||
- the args file, twice;
|
||||
- the bus unit `[email protected]`;
|
||||
- the business file name;
|
||||
- `case1-accept.sh --business`.
|
||||
|
||||
The landed boot unit `packages/cli/systemd/mosaic-hostcheck.service` has
|
||||
`[email protected]`. So at boot the drift stop runs before .53's bus, and DEFERRED 240
|
||||
N1 (da3051ccc: `After=mosaic-hostcheck.service` in `mosaic-bus.service.in`) doesn't bind this row.
|
||||
|
||||
If a go ever names another id for .53, the window stops before W8, and the N1 fix lands first as its own
|
||||
reviewed row. P11 is the check on the host. It reads `unnamed` when the boot unit's `Before=` doesn't
|
||||
name the installed bus.
|
||||
|
||||
DEFERRED 240 N2 (the apt hook goes in before the first `--check`) is a run sheet order note. This row
|
||||
doesn't edit the run sheet, so it stays deferred.
|
||||
|
||||
## 8. Rulings this design rests on
|
||||
|
||||
- sage-1451: Q1 system units; Q3 the Console on 127.0.0.1 with `ssh -L`; Q4 a rebase goes to a new branch
|
||||
name (#426(a), mos-1086).
|
||||
- mos-1091, mos-1096, mos-1098, mos-1101: the bwrap path, its profile and P9; the port squat and P10; .53
|
||||
runs the PM. Their r6 sections stand, and rows 232, 233, 240 and 230 implemented them.
|
||||
- mos-1108 (Q187) and mos-1118: the installer rows and the scope cut. mos-1122 (Q189): both units start at
|
||||
boot.
|
||||
- sage-1522: claude under `/opt`, root-owned.
|
||||
- sage-1809: no step on .53, root steps named and left to ops-01, the business id stated.
|
||||
- sage-1813: 226 in the after list (rev 2718); the id is `mosaic-stack` and 240 N1 doesn't apply.
|
||||
- Q185: the factory's model account. Q173: credentials placed by Jason or his named person.
|
||||
|
||||
## 9. Manifest (planned)
|
||||
|
||||
- `scripts/case1-accept.sh` (new), mode 100755.
|
||||
- `packages/cli/tests/case1-accept.test.mjs` (new). Section 5's cases and mutants.
|
||||
- `docs/TOOLS.md`: a `case1-accept.sh` entry with usage, marks and exits, beside the hostcheck entry.
|
||||
- `agents/darkwing/work/queue-195/BUILD.md` (new): run environment, the mutant results, deviations.
|
||||
- No change to `case2-*.sh`, `stack-upgrade.sh`, `stack-release.sh`, `case1-hostcheck.sh` or the run
|
||||
sheet. Two follow-ups stay out of this row:
|
||||
- row 230 r2 N1. The "still on copy" line reads the id from the unit file. On .53 neither the install
|
||||
nor `stack-upgrade.sh` reaches that line, since both start from stopped units.
|
||||
- DEFERRED 240 N2.
|
||||
- No change to `~/.mosaic-dev`, `~/.config/mosaic-dev`, dragon-lin's units or `~/.mosaic`.
|
||||
|
||||
Gate: Duckworth approves this design, then the manifest; the cli suite and every `scripts/test-*.sh`
|
||||
green on Sage's gate; section 5's twelve mutants killed in a slot Sage grants.
|
||||
@@ -1,287 +0,0 @@
|
||||
# Row 232 (I1): stack-release external units and bundle copies
|
||||
|
||||
Brief: `docs/plans/2026-10-20_installer-rows.md` section I1, blob `f35e7e57daee` (commit ad6f0be7a). Gitea
|
||||
#1734, Vikunja P19 #432. Owner Darkwing, reviewer Filbert, gate owner Sage. Design input: row 195 DESIGN r6,
|
||||
`agents/darkwing/work/queue-195/DESIGN.md` at b7239bb66 (approved in duckworth-0055). Where the brief and
|
||||
r6 differ, the brief wins. sage-1561 ruled where the stack user's `stack-release.sh` runs: from its own
|
||||
clone of the release bundle, so `check_target`, the plan's commit list and status drift are unchanged and
|
||||
nothing adds `safe.directory`.
|
||||
|
||||
## Files
|
||||
|
||||
Base: cb5fdb064 (row 199's landing, on top of row 200's 9e1085816). Branch `darkwing/row232-r1`. The first
|
||||
build, 0af0c73c6 on `darkwing/row232`, sat on 00d4cc1f6 (row 190); row 200 landed first, so this one is
|
||||
rebased onto it (see D9 and D10).
|
||||
|
||||
- `scripts/stack-release.sh`: `--units user|external`, the `sc` and `jctl` backend, `confirm`, `revert`,
|
||||
`gate --since`, `rollback --to`, `--plan --expect-from`, the external gate (start floor, PM check from
|
||||
`launches list`), `external-pm`, and the `awaiting units:` status line.
|
||||
- `scripts/stack-copy.sh`: `check --units external`, `copy --bundle FILE SHA`, `wrapper ID --create`.
|
||||
- `packages/cli/tests/stack-release-external.test.mjs` (new, 25 tests).
|
||||
- `packages/cli/tests/stack-release-fixture.mjs` (new): the helpers `stack-release.test.mjs` defined, moved
|
||||
so both files share them. See deviation D1.
|
||||
- `packages/cli/tests/stack-release.test.mjs`: the helpers leave and one import line comes in. The 74 tests
|
||||
have the same names and the same bodies as on the base.
|
||||
- `packages/cli/tests/stack-copy.test.mjs`: three new `row 232:` tests and a wider `node:fs` import.
|
||||
- `docs/TOOLS.md`: the stack-copy usage block and three paragraphs, the stack-release usage block, and a new
|
||||
subsection "Root-owned units (`--units external`, row 232)".
|
||||
- `packages/cli/README.md`: one paragraph.
|
||||
- This file.
|
||||
|
||||
The worktree's `node_modules` symlink is not part of the candidate.
|
||||
|
||||
## User mode is unchanged
|
||||
|
||||
Every `systemctl` call goes through `sc` and every `journalctl` call through `jctl`. Under `user` they
|
||||
are `systemctl --user "$@"` and `journalctl --user "$@"`. Under `external`, `sc` allows only `show`,
|
||||
`is-active` and `list-units`, and `jctl` returns 1, so an external run can't start, stop, install or read
|
||||
the journal even through a path the tests miss.
|
||||
|
||||
Two tests hold the brief's "every command line and every byte of output":
|
||||
|
||||
- `user mode: every systemctl call carries --user, and activate, rollback and check make the base's
|
||||
calls`. A fixture repo gets the base's two scripts from `git show cb5fdb064:`. The same activate,
|
||||
rollback and `stack-copy.sh check` run from both trees, and the recorded argv (with the fixture root and
|
||||
port replaced by placeholders) must match line for line. Every call carries `--user`.
|
||||
- `user mode: a default activate --plan prints the base's bytes`, for four plans.
|
||||
|
||||
The base output is produced live from the base blobs, not stored as a fixture file. See deviation D3.
|
||||
|
||||
## r6 section 7, test by test
|
||||
|
||||
All in `stack-release-external.test.mjs` unless named.
|
||||
|
||||
| r6 :742-812 item | Test |
|
||||
|---|---|
|
||||
| `units-running` (N4), store, log and TAKEN left | `units-running (N4) refuses while a unit is active...`; also checks that `failed` counts as down and that the Console is checked first |
|
||||
| activate leaves the pointer, writes `AWAITING-UNITS` and a backup | `activate stages: ...`, which also checks the set's file list, `UNITS.sha256`, no `--user`, no journal call and `status` |
|
||||
| `open-activation` with zero, two, the wrong copy, one not open | `confirm and revert refuse open-activation ...` |
|
||||
| `confirm` polls: 3 s fails at `--timeout 1`, passes at `--timeout 10` | `confirm polls: bus status good after 3 s fails ...` and `confirm polls to a pass ...` |
|
||||
| `stale-start` floor with `started` at .900 | `stale-start: @1792540799 fails ...` and `stale-start is a gate code ...` |
|
||||
| `confirm` pass writes pointer, ok, removes marker; timeout writes `FAILED` and exits 1 | the two polling tests |
|
||||
| `activation-failed` when `FAILED` exists | `confirm polls: ... FAILED then refuses activation-failed` |
|
||||
| lines carry the started op, from, to, activation and backup | `sameAsStarted` in every confirm test, and sequence 7 |
|
||||
| `gate` (N2) with and without `--since` | `gate (N2, A2f): ...` and the usage test |
|
||||
| `--plan --expect-from` (N5), activate and `rollback --to` | sequence 7 |
|
||||
| A3 order: capture-failed, marker-failed, wrapper failure after migrate | `A3 order: ...` and sequence 2 |
|
||||
| refusals log no activation; `interrupted` unchanged | `open-activation` test and sequence 5 |
|
||||
| PM check: one, none, two, early, not live, other business, other instance; no journal call | `the external PM check reads launches list ...` (modes '', coder, none, twolive, early, dead, other) and `journalCalls` in every external test |
|
||||
| `external-pm` | `pm-on and activate --pm-after-harness refuse external-pm` |
|
||||
| `revert` restores TAKEN only after a migration | `revert puts the wrapper back and leaves the store ...` and sequences 2 and 8 |
|
||||
| `confirm --rollback` per cause, `unrecorded`, `rollback-failed`, mismatch | sequences 1, 2, 3, 5 and 8, and `confirm --rollback refuses rollback-mismatch, and logs rollback-failed ...` |
|
||||
| sequences 1-8 | `sequence 1:` to `sequence 8:`, each ending with the `closed` check (one started and one result line, empty `interrupted`, no marker or `FAILED`, a following `--plan` that doesn't refuse `interrupted-run`) |
|
||||
| `rollback --to` refuses not-previous; `rollback --restore` finds an external backup | sequence 7, and `confirm --rollback after a --restore swap ...` |
|
||||
| `gate` writes nothing | the `gate (N2, A2f)` test and sequence 4 compare the log, the pointer and the state tree |
|
||||
| `confirm`, `revert`, `gate`, `--to` refuse without `--units external` | `confirm, revert, gate, --to and --expect-from need --units external ...` |
|
||||
| default path: every call carries `--user`; default `--plan` bytes | the two user-mode tests above |
|
||||
|
||||
The brief's additions, in `stack-copy.test.mjs`:
|
||||
|
||||
- `row 232: copy --bundle copies a SHA the bundle's refactor reaches, byte-identical to copy SHA, and
|
||||
refuses the rest`: a short SHA, a missing file, a symlink, a bundle that fails `git bundle verify`, a
|
||||
bundle with no `refs/heads/refactor`, a SHA not in the bundle, and a SHA the bundle carries on another
|
||||
branch that refactor doesn't reach. The bundle copy's tree matches `copy SHA`'s byte for byte, and the
|
||||
`release:` and `bundle sha256:` lines are checked.
|
||||
- `row 232: wrapper --create writes the first wrapper only, the same bytes wrapper ID writes, and refuses
|
||||
over any file`, a symlink included. Without `--create` a missing wrapper still refuses.
|
||||
- `row 232: check --units external reads the system units, the default reads the user units`.
|
||||
|
||||
## Mutants
|
||||
|
||||
`mutants.sh` in my scratch (not committed, it edits the worktree), 17 mutants: r6's re-pin list (:817-838)
|
||||
minus the three `case1-repin.sh` ones, plus the brief's two. Each one applies exact replacements after a
|
||||
checked backup, runs `bash -n`, runs its named killers, restores the file and checks its sha256. Two
|
||||
controls run first with no change: the whole external file, and stack-copy's `row 232:` tests.
|
||||
|
||||
| Mutant | Killers |
|
||||
|---|---|
|
||||
| m01 `confirm` writes the pointer on a gate failure | `confirm polls: bus status` |
|
||||
| m02 `gate_once` in place of `gate` | `confirm polls to a pass`, `stale-start is a gate code` |
|
||||
| m03 PM check counts any instance | `external PM check` |
|
||||
| m04 PM check drops the `startedAt` bound | `external PM check` |
|
||||
| m05 `confirm` takes the newest `AWAITING-UNITS` | `open-activation` |
|
||||
| m06 `rollback --to` skips the comparison | `sequence 7:` |
|
||||
| m07 the default path drops `--user` from one call (`is-active`) | `user mode: every systemctl` |
|
||||
| m08 the timeout writes r4's interim `rollback pending` line | `confirm polls: bus status`, `sequence 1:`, `sequence 5:` |
|
||||
| m09 `AWAITING-UNITS` last, after the wrapper | `sequence 2:` |
|
||||
| m10 a refusal names the open activation | `sequence 5:`, `open-activation` |
|
||||
| m11 `stale-start` is a refusal | `stale-start is a gate code` |
|
||||
| m12 `revert` restores without `MIGRATING` | `revert puts the wrapper back` |
|
||||
| m13 `confirm` logs op `confirm` | `sequence 7:`, `confirm polls to a pass` |
|
||||
| m14 `MIGRATING` after the migration | `sequence 8:` |
|
||||
| m15 `gate` takes its floor from the log without `--since` | `gate (N2` |
|
||||
| m16 `copy --bundle` skips the reachability check | stack-copy `row 232:` |
|
||||
| m17 `wrapper --create` overwrites | stack-copy `row 232:` |
|
||||
|
||||
## Deviations and choices
|
||||
|
||||
D1. The shared helpers moved to a new file, `packages/cli/tests/stack-release-fixture.mjs`. The brief's
|
||||
files list names `stack-release.test.mjs` "where a shared helper moves" but not a destination. Importing
|
||||
from the other test file would run its 74 tests twice, so the helpers need a module of their own.
|
||||
|
||||
D2. The fixture's T17 check (what a run may never remove) now exempts a rollback set's `AWAITING-UNITS` and
|
||||
`FAILED`. `confirm` removes both when it closes the activation (r6 A5 and R4). User mode writes neither, so
|
||||
user-mode runs check exactly what they did.
|
||||
|
||||
D3. The user-mode argv and `--plan` tests read the base's scripts with `git show cb5fdb064:` rather than
|
||||
storing their output as a fixture file. A later row that changes user-mode output on purpose moves the
|
||||
`BASE` constant in the test to its own base, or the test fails. I chose this over a stored fixture because
|
||||
a stored copy can drift from the base without anyone noticing.
|
||||
|
||||
D4. `marker-failed` is reached by making `state/stack-rollback` unwritable. r6 says "an existing `$RS`", but
|
||||
`next_activation` never picks a taken name, so no test can make the run meet an existing `$RS`. The
|
||||
exclusive `mkdir` is still what refuses.
|
||||
|
||||
D5. The stale-start polling case turns the enter time to `@1792540800`, not r6's `@1792540805`, with the
|
||||
started line at 1792540800.900. That also checks that the floor is the whole second, so .900 doesn't push it
|
||||
to 1792540801.
|
||||
|
||||
D6. No fake `sudo` or `case2-unit.sh`. r6's sequences ran under them because r6 had `case1-repin.sh` drive
|
||||
the steps, and that script is I3's. Here the test plays root: it stops and starts the fake system units and
|
||||
installs with the existing `bus-service.sh` and `webui-service.sh` stand-ins, between the real
|
||||
`stack-release.sh` calls, on one log.
|
||||
|
||||
D7. The fake marker in the `open-activation` test is named `20000101T000000Z-<C1 prefix>` so it sorts
|
||||
before the real one. A marker that sorted after it would leave m05 (newest marker) alive.
|
||||
|
||||
D8. m10 sets `ACT` inside `open_activation`, where r6's wording ("ACT set before the checks") lands in this
|
||||
code. `ACT` is set only after every check in `confirm`.
|
||||
|
||||
D9. Row 200's `--sso` is a usage error under `--units external`, with the proof flags, `--no-proof` and
|
||||
`--no-send`. It is a Console flag, and root's Console unit carries the Console flags; the script under
|
||||
external renders no Console line and never calls `console_for_target`, so `sso_checks` doesn't run there
|
||||
either. The usage text, the TOOLS.md bullet and the external usage test name it.
|
||||
|
||||
D10. Row 200's `config_checks` runs under `--units external` too, after `plugin_checks`, in both the
|
||||
`--plan` and run paths of `activate` and `rollback --to`. It checks the target's loader against the stack
|
||||
user's `config.json`, which the bus on a VM still reads, so a `console` key the target refuses would break
|
||||
root's bus start the same way. The cost is one more refusal (`config-console-key`) an external run can
|
||||
print; I took it over a bus that fails after root's start. The rebase changed no user-mode line: the
|
||||
conflicts were the `SSO=0` globals line, the `plan` body (row 200's `SSO_PLAN` line sits in the user
|
||||
branch) and the two `--plan` lines that gained `config_checks`. In the tests, row 200's fixture lines
|
||||
(the loaders, `sso-config.mjs` and the `--sso` cli.mjs line for C1 and C) moved into
|
||||
`stack-release-fixture.mjs` with the rest of the copy setup, and the `ALGS` import stays in
|
||||
`stack-release.test.mjs`, which T57 uses.
|
||||
|
||||
## duckworth-0055's notes on r6 (brief line 169)
|
||||
|
||||
Duckworth's r6 approval (REVIEW-r5.md at 23f5520b5) carried two notes.
|
||||
|
||||
- N1, A2's stop timeout under `gate --since`. A unit that never stopped keeps its old `ActiveEnterTimestamp`,
|
||||
so `gate --since` fails it with `stale-start`, and the run ends at exit 2 with root stopping the units.
|
||||
That is intended: a unit that won't stop is a fair reason to stop. In I1 this is the gate code and its
|
||||
polling (`stale-start: @1792540799 fails ...`, `gate (N2, A2f)`, m11). The run sheet's A2f line and the
|
||||
re-pin test that points "exits 1 with OLD's gate passing" at an A3 exit 3 belong to I3, which owns
|
||||
`scripts/stack-upgrade.sh` and `docs/guides/host-run-sheet.md`. I'll name both in the I3 build.
|
||||
- N2, the claude path under `/opt/mosaic-stack` (gosalyn-0058, sage-1522). I1 doesn't touch it. Under
|
||||
`--units external` the script checks no claude pin of its own (`no-claude-pin` applies only to user
|
||||
mode), because root's unit carries it. The `--claude` value, the managed-settings reasoning, F6 and the
|
||||
run sheet's claude step go to I2 and I3.
|
||||
|
||||
## Run environment
|
||||
|
||||
Worktree `/mnt/storage/scratch/darkwing-r232`, `TMPDIR=/mnt/storage/scratch/tmp/r232`, node from PATH.
|
||||
One external file run takes about 94 s.
|
||||
|
||||
## Results
|
||||
|
||||
Singles on the rebased candidate, before the gate:
|
||||
|
||||
| File | Tests | Pass | Fail |
|
||||
|---|---|---|---|
|
||||
| `packages/cli/tests/stack-release-external.test.mjs` | 25 | 25 | 0 |
|
||||
| `packages/cli/tests/stack-copy.test.mjs` | 16 | 16 | 0 |
|
||||
| `packages/cli/tests/stack-release.test.mjs` (row 200's T57 included) | 192 | 192 | 0 |
|
||||
| `packages/cli/tests/stack-instances.test.mjs` | 23 | 23 | 0 |
|
||||
| `packages/webui/tests/pinned-copy.test.mjs` | 10 | 10 | 0 |
|
||||
| `packages/cli/tests/channel-recovery.test.mjs` (imports `channel-fixture.mjs`, which names the scripts) | 1 | 1 | 0 |
|
||||
| `packages/cli/tests/host.test.mjs` (reads `packages/cli/README.md`) | 42 | 42 | 0 |
|
||||
|
||||
These are every test file that reads `stack-release.sh` or `stack-copy.sh` (a grep of `packages/*/tests` and
|
||||
`scripts/`). None of them pins a sha256 of a changed file. `packages/cli/tests/host.test.mjs` reads
|
||||
`packages/cli/README.md` for a unit file name; this row only adds a paragraph there. The grep and the runs
|
||||
were repeated on the rebased tree on 2026-10-21; the reader set is the same. A dry run of `mutants.sh`
|
||||
matches each of the 17 replacements once on the rebased scripts.
|
||||
|
||||
### Gate, 2026-10-21, Sage's slot (sage-1618)
|
||||
|
||||
Candidate a0ffabc3f on `darkwing/row232-r1`, run by `~/darkwing-scratch/r232/gate.sh`: the cli suite, then
|
||||
every `scripts/test-*.sh`, one after another, stopping at the first failure. busy.sh passed at 01:07:49Z
|
||||
after Launchpad's single `stack-release.test.mjs` run ended. The gate stopped at the first one, so the
|
||||
chain waited for that PID to exit and checked again. The EXIT trap removed the compose network; no
|
||||
container was left.
|
||||
|
||||
| Step | Time (UTC) | Result |
|
||||
|---|---|---|
|
||||
| `packages/cli`, `node --test tests/*.test.mjs` | 01:07:50 to 01:17:53 | 561 pass, 0 fail, 0 skipped |
|
||||
| `scripts/test-auth.sh` | 01:17:53 to 01:17:54 | 15 passed, 0 failed |
|
||||
| `scripts/test-conductor.sh` | 01:17:54 to 01:18:00 | 17 passed, 0 failed |
|
||||
| `scripts/test-config.sh` | 01:18:00 to 01:18:02 | 31 passed, 0 failed |
|
||||
| `scripts/test-discord.sh` | 01:18:02 to 01:18:12 | 78 passed, 0 failed |
|
||||
| `scripts/test-extension-package.sh` | 01:18:12 to 01:18:13 | 18 passed, 0 failed |
|
||||
| `scripts/test-foundation.sh` | 01:18:13 to 01:19:01 | 44 passed, 0 failed |
|
||||
| `scripts/test-land-work.sh` | 01:19:01 to 01:19:14 | 86 passed, 0 failed |
|
||||
| `scripts/test-queue.sh` | 01:19:14 to 01:19:37 | 156 node tests pass, queue suite 28 passed, 0 failed |
|
||||
| `scripts/test-release.sh` | 01:19:37 to 01:19:51 | 14 passed, 0 failed |
|
||||
| `scripts/test-task.sh` | 01:19:51 to 01:20:44 | 98 passed, 0 failed |
|
||||
|
||||
### Mutants, 2026-10-21, same slot
|
||||
|
||||
`~/darkwing-scratch/r232/mutants.sh`, right after the gate. Both controls (the external file whole, and
|
||||
stack-copy's `^row 232: ` tests) exited 0 on the unmutated tree. Each mutant was applied after a checked
|
||||
backup and a syntax check, ran its killers from the table above, and was put back with its sha256
|
||||
checked. The script stops on any restore mismatch, and it ran to its end.
|
||||
|
||||
| Mutant | Killer exit | Result |
|
||||
|---|---|---|
|
||||
| m01 pointer on gate failure | 1 | killed |
|
||||
| m02 gate once | 1 | killed |
|
||||
| m03 PM from any instance | 1 | killed |
|
||||
| m04 no startedAt check | 1 | killed |
|
||||
| m05 newest marker | 1 | killed |
|
||||
| m06 --to not compared | 1 | killed |
|
||||
| m07 one call without --user | 1 | killed |
|
||||
| m08 interim pending line | 1 | killed |
|
||||
| m09 marker written last | 1 | killed |
|
||||
| m10 refusal names the open activation | 1 | killed |
|
||||
| m11 stale-start refuses | 1 | killed |
|
||||
| m12 revert always restores | 1 | killed |
|
||||
| m13 confirm op | 1 | killed |
|
||||
| m14 MIGRATING after migrate | 1 | killed |
|
||||
| m15 gate floor from the log | 1 | killed |
|
||||
| m16 bundle reachability unchecked | 1 | killed |
|
||||
| m17 --create overwrites | 1 | killed |
|
||||
|
||||
17 of 17 killed, 01:22:16 to 01:24:32Z. I read the logs for m01 and m09, two of the quick kills: each failed on
|
||||
its named test's assertion (m09 on `AWAITING-UNITS` in sequence 2), not on a load error.
|
||||
|
||||
## Round 2: rebase onto 8fede5fc6 (sage-1673)
|
||||
|
||||
Rebase only. Before the landing gate, a check of BASE..HEAD on the manifest paths found that row 230
|
||||
(c8bc5cf4c) had changed one of them after the round 1 base, cb5fdb064. Sage ruled a re-round with a
|
||||
scoped review, the same shape as row 210 round 3.
|
||||
|
||||
Branch `darkwing/row232-r2`: the two round 1 commits, a0ffabc3f and 84f1a91e6, cherry-picked onto
|
||||
8fede5fc6 with no conflict, then this section. No code or test changed.
|
||||
|
||||
- `git merge-tree --write-tree origin/refactor darkwing/row232-r1` (refactor at 8fede5fc6) gives tree
|
||||
837bde79236c with no conflict. The cherry-picked tip before this section has the same tree.
|
||||
- `packages/cli/README.md` is a clean 3-way merge. Row 230's hunk is at lines 710-717 (the session credential
|
||||
paragraph); this row's is a new paragraph at 773. With `git patch-id --stable`, the README diff from r1 to
|
||||
r2 is 02b679e2, the same as row 230's README diff `cb5fdb064..8fede5fc6`. This row's README diff is
|
||||
c395ecd2 on both bases (`cb5fdb064..r1` and `8fede5fc6..r2`).
|
||||
- The seven code, test and doc paths other than the README hash the same as in manifest 7a3b4ea1, which
|
||||
Filbert approved in round 1: `docs/TOOLS.md`, `packages/cli/tests/stack-copy.test.mjs`,
|
||||
`packages/cli/tests/stack-release-external.test.mjs`, `packages/cli/tests/stack-release-fixture.mjs`,
|
||||
`packages/cli/tests/stack-release.test.mjs`, `scripts/stack-copy.sh`, `scripts/stack-release.sh`.
|
||||
This file also matched until this section was appended. Its other lines are unchanged.
|
||||
- The one test that reads the README, `host.test.mjs:855`, looks for the unit's `EnvironmentFile=` line.
|
||||
That line is unchanged context in row 230's hunk.
|
||||
- No row 230 test reads `stack-release.sh` or `stack-copy.sh`, and no test of this row reads a row 230 file.
|
||||
|
||||
Nothing was run for this round. The landing gate runs on the r2 tip on Sage's slot.
|
||||
|
||||
Follow-up, doc-only (sage-1673): `scripts/case2-unit.sh:108` and `scripts/case2-precheck.sh:157` cite
|
||||
`scripts/stack-copy.sh:8` and `:22` for `COPIES` and `copy_root`. With this row those lines are `:10` and
|
||||
`:26`. Only comments are affected and no test reads them. Row 233 doesn't touch those scripts, so the fix
|
||||
goes on the #1732 carry list.
|
||||
@@ -1,249 +0,0 @@
|
||||
# Row 233 (I3): root's upgrade driver and the host run sheet
|
||||
|
||||
Brief: `docs/plans/2026-10-20_installer-rows.md` section I3, blob `e61291bcf1c6` (commit a0e3d7cfd), re-pinned
|
||||
by Sage (sage-1685, rev 2631) from `f35e7e57daee` (ad6f0be7a). The later blob only appends I4; the I3 text
|
||||
is the same. Gitea #1735, Vikunja P19
|
||||
#432. Owner Darkwing, reviewer Filbert, gate owner Sage. Design input: row 195 DESIGN r6,
|
||||
`agents/darkwing/work/queue-195/DESIGN.md` at b7239bb66, sections 2, 4 and 7. Where the brief and r6
|
||||
differ, the brief wins. Sage's rulings during the build: sage-1650 and sage-1652 (`--backup last`, the
|
||||
`--no-rehearsal` mapping, `--rehearse-port`, hostcheck), and sage-1653 (`from_log newest`, the two external
|
||||
plan refusals, the `BACKUP_STORE` line).
|
||||
|
||||
## Files
|
||||
|
||||
Base: e65a3ab4d, row 232's landing, on `darkwing/row233-r1`. The build started on ea1e85c7b
|
||||
(`darkwing/row233`, row 232's candidate e6ca230d2 and its BUILD.md) before row 232 landed. The three row 232
|
||||
files this row changes are identical on the two bases, so the uncommitted diff moved across unchanged.
|
||||
|
||||
- `scripts/stack-upgrade.sh` (new): `upgrade` and `rollback`, root's side of r6 A1-A5, A2f and R1-R4.
|
||||
- `packages/cli/tests/stack-upgrade.test.mjs` (new, 20 tests).
|
||||
- `docs/guides/host-run-sheet.md` (new).
|
||||
- `docs/TOOLS.md`: a new subsection "Root's upgrade driver (`scripts/stack-upgrade.sh`, row 233)", plus the
|
||||
row 232 lines below.
|
||||
- Row 232 files, changed on Sage's rulings (the brief's files list doesn't name them):
|
||||
- `scripts/stack-release.sh`:
|
||||
- `rehearse --backup last` takes the newest backup the log records (`from_log newest`) and prints
|
||||
`PASS backup-last`. With none it fails `no-backup`, exits 3 and makes no rehearsal directory.
|
||||
A newest backup that isn't an activation id aborts `backup-unreadable`, exit 1.
|
||||
- The rehearsal prints `BACKUP_STORE <activation id> schema <version>` before its migration step.
|
||||
- `--plan` under `--units external` refuses `open-activation` when any rollback set holds
|
||||
`AWAITING-UNITS`, and `migrate-required` for a migrating target without `--migrate`. Root reads
|
||||
only the plan's exit, so these two have to be refusals there. User mode prints the same bytes.
|
||||
- `packages/cli/tests/stack-release.test.mjs`: three new `row 233:` tests for `--backup last`.
|
||||
- `docs/TOOLS.md`: `--backup ACTIVATION|PATH|last` in the rehearse usage, a paragraph on `last`, and a
|
||||
paragraph on the two external plan refusals.
|
||||
- This file.
|
||||
|
||||
## The brief, item by item
|
||||
|
||||
| Brief item | Where |
|
||||
|---|---|
|
||||
| Root runs from a root-owned checkout, never a copy | `head-mismatch` (A1); TOOLS.md; run sheet section 6 |
|
||||
| I2's render with `/etc/mosaic-stack/<instance>.args`, `--copy` the one changed value | `read_args`, `args_with`; the args test |
|
||||
| The args file changes only after a confirmed run | `prepare_args` before A2, `mv` after A5; sequences 1 and 7, m6 |
|
||||
| `copy --bundle` as the stack's user before A1 | the `fetch`, `ff`, `copy` steps; sequence 1's sudo keys |
|
||||
| A rehearsal before A2, a failed one refuses with nothing stopped | `rehearse`; three rehearsal tests, m4 |
|
||||
| The root lock, held to the end, rollback included | `flock -n 9`; the lock test and sequence 3's probe, m1, m2 |
|
||||
| A1 refuses `open-activation` | stack-release.sh's external plan; the AWAITING-UNITS test, m3 |
|
||||
| `migrate-required` without `--migrate` | stack-release.sh's external plan; its test, m5 |
|
||||
| The step log, 0640, one line per step | `logl`, `step`; every test checks lines against `STEP` |
|
||||
| Exit codes 0-3 | `finish`, `refuse`; exit 4 is usage |
|
||||
| duckworth-0055's notes | the next section |
|
||||
| Root parses no stack user output beyond exits | the step log test; the one exception is sage-1653's `BACKUP_STORE` |
|
||||
| The host run sheet | `docs/guides/host-run-sheet.md` |
|
||||
|
||||
## duckworth-0055's notes on r6 (brief line 169)
|
||||
|
||||
- N1, A2's stop timeout under `gate --since`. Row 232's BUILD.md left two pieces to I3. The run sheet's
|
||||
A2f line is in section 6 ("When a run doesn't exit 0", exit 2). The test is `N1: a bus that outlives
|
||||
A2's timeout keeps its old start`: a bus that ignores `stop` keeps its old `ActiveEnterTimestamp`,
|
||||
`wait-down` logs `rc=1 ... state: active`, A2f restarts and gates, the gate fails `stale-start`, and the
|
||||
driver stops both units and exits 2. Sequence 4 is the other half: an A3 exit 3 goes to A2f and exits
|
||||
1 with the old copy's gate passing.
|
||||
- N2, the claude path under `/opt/mosaic-stack`. Run sheet section 3 installs it there as root with
|
||||
managed settings. The args file's `--claude` carries it, and `case2-unit.sh` already refuses a binary
|
||||
that isn't root's or sits under the home. The driver passes the args file through and adds no claude
|
||||
check of its own. F6 is I2's.
|
||||
|
||||
## Sage's rulings during the build
|
||||
|
||||
- sage-1650 and sage-1652. Root always runs the rehearsal and maps its exit:
|
||||
- 0 goes on, or refuses `rehearsal-required` with `--no-rehearsal`.
|
||||
- 3 refuses `no-backup`, or with `--no-rehearsal` logs `rehearsal-skipped` and goes on.
|
||||
- Anything else refuses `rehearsal-failed`.
|
||||
|
||||
`--rehearse-port N`, default 7339. Hostcheck: a missing file or a nonzero `--check` refuses
|
||||
`profile-drift`.
|
||||
- sage-1653. `from_log newest`, and the external plan refusals `open-activation` and `migrate-required`.
|
||||
Residual: the `BACKUP_STORE` id and schema land in root's step log, but nothing ties that backup to the
|
||||
one the activation's own backup step takes later. A stopped backup before the upgrade would close that
|
||||
gap; it's a later row, not this one.
|
||||
|
||||
## Tests
|
||||
|
||||
`stack-upgrade.test.mjs` runs the driver as the test's user against a scratch root, with the test seams
|
||||
`STACK_UPGRADE_*`. The stand-ins are a fake `sudo` (logs its argv, checks the driver's form, then runs the
|
||||
real command as the test's user, r6's B1 pattern), a fake `case2-unit.sh` render that installs and starts
|
||||
the fixture units, a fake `systemctl` for root's system units, and a shell `hostcheck.sh`. The stack's
|
||||
user's steps run the real `stack-release.sh` and `stack-copy.sh` from a clone made from a real bundle. The
|
||||
units, copies and store are `stack-release-fixture.mjs`'s.
|
||||
|
||||
| Test | Covers |
|
||||
|---|---|
|
||||
| sequence 1 | confirm fails healthz, rollback with `--cause A5`, exit 1; every sudo call in the driver's form |
|
||||
| sequence 2 | `activate --migrate` fails at the wrapper, store restored, `--cause A3`, exit 1 |
|
||||
| sequence 3 | render fails, `--cause A4`; a probe run during `render-old` refuses `upgrade-running` |
|
||||
| sequence 4 | stage exits 3, A2f restarts and gates the old units, exit 1 |
|
||||
| N1 | a unit that won't stop: A2f's gate fails `stale-start`, exit 2, units stopped |
|
||||
| sequence 5 | confirm of the wrong copy refused, `--cause A5`, exit 1 |
|
||||
| sequence 6 | revert fails, exit 2; the next run refuses `interrupted-run` at its plan |
|
||||
| sequence 7 | two upgrades and a `rollback --to` with `--restore`; the args file after each confirm; `to-not-previous` |
|
||||
| sequence 8 | a migration that writes and fails, store back byte for byte, exit 1 |
|
||||
| lock | a second run while the lock is held: one log line, no call |
|
||||
| open-activation | any `AWAITING-UNITS` refuses at A1, nothing stopped |
|
||||
| migrate-required | refused without `--migrate`, no rehearsal, nothing stopped |
|
||||
| rehearsal-failed | the rehearsal's argv, exit 3, nothing stopped |
|
||||
| `BACKUP_STORE` | the line after a match; four malformed lines log `BACKUP_STORE none` |
|
||||
| no-backup | the real rehearsal with no backup refuses; `--no-rehearsal` logs the skip and goes on |
|
||||
| backup-last | the real rehearsal names the newest backup; `--no-rehearsal` with one refuses `rehearsal-required` |
|
||||
| args file | group write, other write, two `--copy`, an unknown flag, a symlink, `args-differ` |
|
||||
| hostcheck | nonzero `--check` and a missing file refuse `profile-drift` before the plan |
|
||||
| head-mismatch | checkout not at `--sha`; a commit the clone can't fast-forward to refuses `fetch-failed` |
|
||||
| usage | twelve bad argvs exit 4 with no log and no lock |
|
||||
|
||||
The step log check (`STEP`) runs in every test that reads the log: each line must be a step line or the
|
||||
result line, and none may hold the stack user's output (`stack-release:`, `PASS `, `FAIL `, `REHEARSAL`,
|
||||
the fake rehearsal's text).
|
||||
|
||||
## Mutants
|
||||
|
||||
`~/darkwing-scratch/r233/mutants.sh`, not committed because it edits the worktree. It has the brief's six
|
||||
mutants and two for sage-1650's mapping. The method is row 232's: a checked backup, exact replacements, a
|
||||
syntax check, the named killers, then the file put back with its sha256 checked. One control runs first:
|
||||
the whole `stack-upgrade.test.mjs` with no change. `DRY=1` matched each replacement once on the candidate
|
||||
(2026-10-21).
|
||||
|
||||
| Mutant | File | Killers |
|
||||
|---|---|---|
|
||||
| m1 drop the flock | stack-upgrade.sh | `a second run while the lock is held`, `sequence 3:` |
|
||||
| m2 release the lock when the rollback starts | stack-upgrade.sh | `sequence 3:` |
|
||||
| m3 skip A1's open-activation check | stack-release.sh | `A1 refuses open-activation` |
|
||||
| m4 skip the rehearsal | stack-upgrade.sh | `the rehearsal runs after the plan`, `sequence 1:` |
|
||||
| m5 accept a migrating target without `--migrate` | stack-release.sh | `migrate-required` |
|
||||
| m6 write the args file before confirm | stack-upgrade.sh | `sequence 7:`, `sequence 1:` |
|
||||
| m7 `--no-rehearsal` accepted with a backup | stack-upgrade.sh | `refuses rehearsal-required` |
|
||||
| m8 drop the `rehearsal-skipped` line | stack-upgrade.sh | `a real rehearsal with no backup` |
|
||||
|
||||
## Deviations
|
||||
|
||||
D1. The brief has A1 refuse `open-activation` for an open `AWAITING-UNITS` or a non-empty `interrupted`
|
||||
set. The interrupted case refuses with row 232's existing code, `interrupted-run`; only `AWAITING-UNITS`
|
||||
gives `open-activation`. Both reach root as `plan-refused`, exit 3 (sequence 6, the open-activation test).
|
||||
|
||||
D2. Root copies one line of the stack user's output into its log, `BACKUP_STORE`, on sage-1653. It
|
||||
matches a fixed pattern first, takes the last match, logs `BACKUP_STORE none` otherwise, and decides
|
||||
nothing on it.
|
||||
|
||||
D3. The stack user's steps carry no `--instance`. The driver takes `--instance dev` only, row 190's default
|
||||
instance, which the stack user's commands select with no flag. A named instance's units are a later row.
|
||||
|
||||
D4. Most tests fake the rehearsal in the fake `sudo`: the fixture's stub copies can't pass a real one. Two
|
||||
tests run the real rehearsal (`faults/real-rehearse`): no-backup and backup-last. `stack-release.test.mjs`
|
||||
covers `--backup last` itself.
|
||||
|
||||
D5. Any nonzero `--plan` exit refuses `plan-refused`, exit 3, whatever the code. The stack user's output
|
||||
above it names the refusal; root doesn't parse it.
|
||||
|
||||
D6. `rollback --restore ACT` is accepted without `--events`, as `stack-release.sh rollback` accepts it.
|
||||
The brief's usage pairs them. `--events` without `--restore` is a usage error.
|
||||
|
||||
D7. The refusal of any `STACK_UPGRADE_*` seam when run as root has no test, because the tests don't run
|
||||
as root. It is three lines at the top of the script.
|
||||
|
||||
D8. The fake `sudo` keeps the test's environment, where real `sudo` resets it. The driver passes
|
||||
everything the stack user needs through `env PATH=...` and `-H`, and the fake checks that form.
|
||||
|
||||
D9. Exit 3 means nothing stopped, not nothing changed. A refusal after `fetch` leaves the stack user's
|
||||
clone fast-forwarded and the new copy made. Both are the user's own state, and the next run reuses them.
|
||||
TOOLS.md and the run sheet say so.
|
||||
|
||||
D10. The brief's usage has neither `--no-rehearsal` nor `--rehearse-port`. Both come from sage-1650 and
|
||||
sage-1652.
|
||||
|
||||
## Decisions
|
||||
|
||||
- `rollback` has no HEAD check. Its target is an older copy than root's checkout, which already exists.
|
||||
It fetches, copies and rehearses nothing (sequence 7).
|
||||
- The new args file is written next to the old one (`.dev.args.XXXXXX`, same mode) before A2, so a full
|
||||
`/etc` refuses before anything stops. It moves into place after A5. A2f and the rollback remove it; a failed move keeps it and names it.
|
||||
- Every stack user step gets `--copies` explicitly, so the copies directory comes from the args file's
|
||||
`--home`, never from the user's environment.
|
||||
- The stack's user does the fetch, the fast-forward and the copy, so root never runs git on a tree that
|
||||
user can write.
|
||||
- A4 is the render, and `case2-unit.sh` starts the bus, then the Console. The forward path has no
|
||||
`systemctl start` of its own; only A2f does.
|
||||
|
||||
## Run environment
|
||||
|
||||
Worktree `/mnt/storage/scratch/darkwing-r233`, `TMPDIR=/mnt/storage/scratch/tmp/r233`, node from PATH. One
|
||||
whole `stack-upgrade.test.mjs` run takes about 56 s.
|
||||
|
||||
## Results
|
||||
|
||||
### Singles, run 1, 2026-10-21, Sage's slot (sage-1689)
|
||||
|
||||
`~/darkwing-scratch/r233/singles.sh` on `darkwing/row233-r1` (base e65a3ab4d, uncommitted tree), 03:48:20Z
|
||||
to 03:59:00Z. These are the test files that read a changed script. `stack-instances.test.mjs` reads
|
||||
`stack-release.sh`, so it joined the three named in the plan.
|
||||
|
||||
| File | Result |
|
||||
|---|---|
|
||||
| `stack-upgrade.test.mjs` | 20 pass, 0 fail |
|
||||
| `stack-release.test.mjs` | 194 pass, 1 fail |
|
||||
| `stack-release-external.test.mjs` | 25 pass, 0 fail |
|
||||
| `stack-instances.test.mjs` | 23 pass, 0 fail |
|
||||
|
||||
The failure was my test, not the script. "row 233: --backup last takes the newest backup..." compares
|
||||
`untouched(e)` before and after the run, and `untouched` includes `existsSync(e.log)`. The test took its
|
||||
before snapshot ahead of `rlog()`, which writes the log, so that flag went from false to true. Every hash in
|
||||
the snapshot matched. The fix takes the snapshot after `rlog()`. No other row 233 test calls `untouched`.
|
||||
|
||||
### Singles, run 2
|
||||
|
||||
`~/darkwing-scratch/r233/rerun.sh` on aacfa2d7a, Sage's slot (sage-1695), 04:13:05Z to 04:21:28Z:
|
||||
`stack-release.test.mjs` whole, 195 pass, 0 fail, 0 skipped. The fixed test passed. The other three files
|
||||
did not change after run 1. The gate and the mutants run next, in Sage's slot.
|
||||
|
||||
### Gate and mutants, 2026-10-21, Sage's slot (sage-1704)
|
||||
|
||||
`~/darkwing-scratch/r233/gate.sh` then `mutants.sh`, chained behind `busy.sh`, on d99166ade (code
|
||||
aacfa2d7a; d99166ade changes only this file). Counts are an awk sum over `gate-r1.out`.
|
||||
|
||||
| Part | Time (UTC) | Result |
|
||||
|---|---|---|
|
||||
| cli suite (`node --test tests/*.test.mjs`) | 04:29:01-04:37:27 | 616 pass, 0 fail, 0 skipped |
|
||||
| the 10 `scripts/test-*.sh` | 04:37:27-04:39:38 | selftests 429 passed, 0 failed; `test-queue`'s node part 156 pass, 0 fail |
|
||||
| gate total | | 1201 pass, 0 fail, 11 suites, `GATE_PASSED` |
|
||||
| c0 control | 04:39:38-04:40:35 | exit 0 |
|
||||
| m1-m8 | 04:40:35-04:41:25 | 8 of 8 killed, every one exit 1 |
|
||||
|
||||
Every restore passed its sha256 check, and `git diff` was clean at d99166ade afterwards. The EXIT trap
|
||||
removed the `darkwing-r233_default` network.
|
||||
|
||||
N1, found in the process sweep after the run: a `sleep 60` was left with its cwd in `packages/cli`.
|
||||
`stack-upgrade.test.mjs:312` spawns `flock LOCK sleep 60`, and its `t.after` SIGKILLs only the flock, so
|
||||
flock's child outlives the test by up to 60 s. It exited on its own. Row 232's T20
|
||||
(`stack-release.test.mjs:370`) spawns its holder `detached` and kills the group with `process.kill(-pid)`.
|
||||
The fix is the same two lines. That test is one of m1's killers.
|
||||
|
||||
Sage ruled it a fix now (sage-1707), and 08db6ac3f makes it, test only. After the gate's d99166ade
|
||||
the only change outside this file is that test:
|
||||
|
||||
```
|
||||
$ git diff --stat d99166ade 08db6ac3f
|
||||
packages/cli/tests/stack-upgrade.test.mjs | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
```
|
||||
|
||||
The gate stands on Sage's ruling. The rerun is the whole `stack-upgrade.test.mjs` singly plus m1, in
|
||||
Sage's slot, with a sweep for a leftover `sleep` afterwards.
|
||||
@@ -1,173 +0,0 @@
|
||||
# Row 240 (I4): root's host check, its pins and the boot unit
|
||||
|
||||
Brief: `docs/plans/2026-10-20_installer-rows.md` section I4, blob `4a5a65020f00` (commit b00d7d94d, rev 2665,
|
||||
the pin row 240 carries; the I3 carry items came in with it). Sage split it out of I3 (sage-1650). Gitea #1746, Vikunja P19 #432. Owner Darkwing, reviewer Filbert, gate owner Sage.
|
||||
Design: row 195 DESIGN r6 B2, `agents/darkwing/work/queue-195/DESIGN.md` at b7239bb66, :262-303 (the script,
|
||||
the three callers) and :839-860 (files, test cases, mutants). Where the brief and r6 differ, the brief wins.
|
||||
|
||||
## Files
|
||||
|
||||
Drafted on `darkwing/row240-r1` from d99166ade (row 233's candidate) while row 233's gate ran, then applied
|
||||
as one patch onto row 233's landing, 57f59e0c3, on `darkwing/row240-r2` (sage-1753). It applied cleanly; between
|
||||
the two bases the only change on this row's paths is 233's N1 test fix in `stack-upgrade.test.mjs`. Row 234
|
||||
then landed as c771a36f4 on 57f59e0c3. Sage preferred that the gate run with it in the tree (sage-1773), so both
|
||||
commits were picked onto c771a36f4 as `darkwing/row240-r3`. That was a clean pick: the 42 paths changed from
|
||||
57f59e0c3 to c771a36f4 share none with this row's 9.
|
||||
|
||||
- `scripts/case1-pins.sh` (new, 0644): `PIN_PROFILES_VERSION`, `PIN_PROFILE_SHA256`, `PIN_BUBBLEWRAP_VERSION`,
|
||||
r6 section 0's values.
|
||||
- `scripts/case1-hostcheck.sh` (new, 0755): the seven items and `--check`, `--enforce`, `--upstream`.
|
||||
- `packages/cli/systemd/mosaic-hostcheck.service` (new): the boot oneshot.
|
||||
- `packages/cli/tests/case1-hostcheck.test.mjs` (new, 16 tests). The cli suite's `tests/*.test.mjs` glob picks
|
||||
it up.
|
||||
- `docs/guides/host-run-sheet.md` section 4: the install lines that replace "Install them from row 240's run
|
||||
sheet lines once it lands", plus rollback, drift recovery and `--upstream` before a re-pin window. Sections
|
||||
3, 6, 8 and 10 for the I3 carry items below.
|
||||
- `scripts/stack-upgrade.sh` and `packages/cli/tests/stack-upgrade.test.mjs`: I3 N1 below.
|
||||
- `docs/TOOLS.md`: a new subsection "Root's host check (`scripts/case1-hostcheck.sh`, row 240)" after the
|
||||
upgrade driver's.
|
||||
|
||||
Brief line 257 said I3's `stack-upgrade.sh` doesn't change. Sage's ruling sage-1727 widened the row to it for
|
||||
N1 alone. Its default `STACK_UPGRADE_HOSTCHECK=/usr/local/lib/mosaic-stack/hostcheck.sh` is where the run
|
||||
sheet installs this script.
|
||||
|
||||
## Carried from I3
|
||||
|
||||
Filbert's row 233 approve (Gitea 29922) left five notes. Sage ruled N1, N4 and N5 into this row and N2 and N3
|
||||
to DEFERRED.md (sage-1727).
|
||||
|
||||
| Note | Change |
|
||||
|---|---|
|
||||
| N1: `stack-upgrade.sh` exits 1 `args-failed` when the args move after A5 fails, but exit 1 means the old copy runs | The move's failure exits 5 `args-failed`. The header, TOOLS.md's step 5 and exit line, and run sheet section 6's exit list name exit 5 and what root does: move the new file the result line names into place. Test `row 240: I3 N1: a failed args move after A5 exits 5 ...` makes `/etc/mosaic-stack` read-only at confirm and checks exit 5, the step names, `args rc=1`, the result line, both units on the new copy, the old args file's bytes and the new file beside it. The step log regex takes `exit=5`. Mutant M8. |
|
||||
| N4: section 3's `npm install` has no npm on root's PATH, and the `--claude` path is unnamed while `node_modules/.bin/claude` is a symlink `case2-unit.sh:140` refuses | The install line runs with section 2's pinned node first on PATH (the postinstall runs `node` too). The `--claude` path is `node_modules/@anthropic-ai/claude-code/bin/claude.exe`, and a `--version` line catches the postinstall's 500-byte stub. |
|
||||
| N5: sections 8 and 10 lack precheck lines | Each gets "Precheck line: none" and its missing line. |
|
||||
|
||||
Exit 5 is new rather than 2 because 2 means both units are stopped, and here both run. Exit 4 stays usage.
|
||||
|
||||
The `claude.exe` path comes from the 2.1.304 package itself: `npm view` gives `bin.claude` as `bin/claude.exe`,
|
||||
and its `install.cjs` hardlinks the platform binary over that placeholder, or copies it across devices, then
|
||||
sets 0755. I downloaded the tarball into `/mnt/storage/scratch/tmp/r240/cc304` and read it; nothing ran. The
|
||||
run sheet says a newer pin needs the same check.
|
||||
|
||||
## The brief, item by item
|
||||
|
||||
| Brief line | Where |
|
||||
|---|---|
|
||||
| 247 `case1-pins.sh` holds the three pins | `scripts/case1-pins.sh`; the pins test checks the values against r6's |
|
||||
| 248-249 seven items, `--check` exits 0 or 1 | `items()` and the final dispatch; the check tests |
|
||||
| 250-251 `--enforce`: `apparmor_parser -R`, `root:root 0700`, `DRIFT`, a log line, exit 0 | `enforce()` and `stop_bwrap()`; the four enforce tests |
|
||||
| 252 `--upstream` compares the current deb to the pin | `upstream()`; the upstream test |
|
||||
| 253 the boot oneshot | `mosaic-hostcheck.service`; the boot unit test |
|
||||
| 254-255 fakes for every host tool, r6's seven case groups, seven mutants | the test file; Tests and Mutants below |
|
||||
| 257 the I3 run sheet item points here | run sheet section 4 |
|
||||
|
||||
## Tests
|
||||
|
||||
Fakes on PATH, each logging its argv to `$HC/calls.log`: `aa-status`, `dpkg`, `dpkg-statoverride`,
|
||||
`apparmor_parser`, `systemd-run`, `stat`, `chown`, `chmod`, `apt-get`, `dpkg-deb`. `host(t)` builds a scratch
|
||||
root (`/etc`, `/proc/sys`, `/usr/bin/bwrap`, `/var/log`) and a lib dir with a copy of the script and fixture
|
||||
pins that hold the fixture profile's sha256. Every run passes `--root`.
|
||||
|
||||
r6's case groups (:843-856) against the tests:
|
||||
|
||||
| r6 case | Tests |
|
||||
|---|---|
|
||||
| `--check` exits 0 with all seven, 1 with each failing alone, its line naming it | `check: exits 0 ...`, `check: exits 1 with each item failing alone ...`, plus one test per item for the item's edge cases |
|
||||
| item 3: a second file naming `bwrap` as a word, `/{usr/,}bin/bwrap` included; passes with the profile alone | `check: item 3 ...` |
|
||||
| item 4: `unpriv_bwrap (enforce)` or a trailing space fails; the argv carries `-p NoNewPrivileges=yes` and the 2a' properties | `check: item 4 ...` checks the whole argv |
|
||||
| item 7: the floor at 732 fails | `check: item 6 ...; item 7 ...` (733 passes as the control) |
|
||||
| `--enforce` exits 0 on a pass and every failure; unload, close, `DRIFT`, one log line; `DRIFT` present with every item passing still closes | `enforce: a pass ...`, `enforce: each failure ...`, `enforce: with DRIFT present ...` |
|
||||
| `--enforce` never calls `dpkg-statoverride` with `--update`, `--add` or `--remove` | `enforce: no unload ...; never writes the statoverride` (every call is `--list /usr/bin/bwrap`) |
|
||||
| a bubblewrap version other than the pin is a line, not a failure | `check: a bubblewrap version other than the install pin ...` |
|
||||
| `--upstream` against a differing deb prints the difference, exits 0 | `upstream: ...` (also: a match, a deb with no profile, a failed download) |
|
||||
| the unit: `Type=oneshot`, `After=apparmor.service`, `[email protected]`, runs `--enforce` | `boot unit: ...` |
|
||||
|
||||
The last test also checks that the repo script reads `scripts/case1-pins.sh` beside it, that bad usage exits
|
||||
2, and that a non-root run without `--root` refuses.
|
||||
|
||||
Singles are the two files that run a changed script: `case1-hostcheck.test.mjs` and `stack-upgrade.test.mjs`.
|
||||
Sage asked (sage-1759) whether `stack-release.test.mjs` or `stack-release-external.test.mjs`, which share
|
||||
`stack-release-fixture.mjs`, run `stack-upgrade.sh` or `case1-pins.sh`. They don't. `grep -cF` for
|
||||
`stack-upgrade`, `case1-pins`, `case1-hostcheck` and `hostcheck` gives 0 in all three files; the control
|
||||
`stack-release.sh` gives 2 in the fixture and 8 in `stack-release.test.mjs`. `scripts/stack-release.sh` names
|
||||
`stack-upgrade.sh` once, in a comment (:2061). The gate's cli suite runs both files anyway.
|
||||
|
||||
## Mutants
|
||||
|
||||
r6's seven (:858-860) and one for I3 N1, scripted in `~/darkwing-scratch/r240/mutants.sh` with r233's method: a checked backup,
|
||||
an exact replacement that must match once, `bash -n`, the named killer by `--test-name-pattern`, the restore
|
||||
and a sha256 check. A dry run matched all eight once each. Results below.
|
||||
|
||||
| Mutant | Change | Killer |
|
||||
|---|---|---|
|
||||
| M1 | `--enforce` exits 1 after the stop | `enforce: each failure exits 0 ...` |
|
||||
| M2 | `[ ! -e "$DRIFT" ] \|\| had=1` becomes `true \|\| had=1` | `enforce: with DRIFT present ...` |
|
||||
| M3 | item 1's `dpkg -S` becomes `owner=""; rc=1` | `check: item 1 fails when a package owns ...` |
|
||||
| M4 | item 3's `grep -rlw bwrap` becomes `grep -rlF /usr/bin/bwrap` | `check: item 3 ...` |
|
||||
| M5 | item 4's `[ "$label" = "$LABEL" ]` becomes a substring match | `check: item 4 ...` |
|
||||
| M6 | item 4 drops `-p NoNewPrivileges=yes` | `check: item 4 ...` (argv) |
|
||||
| M7 | item 7's `-gt` becomes `-ge` | `check: item 6 ...; item 7 fails with the floor at 732 ...` |
|
||||
| M8 | `stack-upgrade.sh`'s `finish 5 args-failed` becomes `finish 1 args-failed` (I3 N1) | `row 240: I3 N1: a failed args move ...` |
|
||||
|
||||
## Deviations
|
||||
|
||||
- Repo name `scripts/case1-hostcheck.sh` (r6), installed as `/usr/local/lib/mosaic-stack/hostcheck.sh`, the
|
||||
name in brief line 239 and I3's `stack-upgrade.sh` default. r6's apt hook line says `case1-hostcheck.sh`;
|
||||
the run sheet's hook uses the installed name.
|
||||
- `stat` is a fake too. r6 lists seven fakes; a test can't make a root-owned file, so item 5's `stat` reads a
|
||||
fixture.
|
||||
- Item 7 with no Console unit installed reads the port as 1023 and says so ("1023, because no Console unit
|
||||
is installed"). Until I2 installs the unit the floor must be above 1023. A unit with no `--port` fails.
|
||||
- Item 4 runs under `timeout 60`. A hung `systemd-run` would otherwise hang the apt hook and the boot unit.
|
||||
|
||||
## Decisions
|
||||
|
||||
- Fail closed where r6 is silent:
|
||||
- pins with no 64-hex sha fail item 1;
|
||||
- `dpkg -S` exit 1 (no owner) passes, exit 0 fails with the owner, any other exit fails with the code;
|
||||
- a failed `aa-status` fails item 2 and counts as loaded, so `--enforce` tries the unload.
|
||||
- `--root DIR` is the test seam (r6 :840). Root refuses it; a non-root user needs it. Both exit 2. Root runs
|
||||
with `PATH=/usr/sbin:/usr/bin:/sbin:/bin`.
|
||||
- `DRIFT` is append-only (`<stamp> <failed line>`), and `--enforce` never removes it. The log line is written
|
||||
only when the stop runs: `<stamp> enforce stop items=<ids or none> drift-before=<0|1> unload=<rc=N|no-file|skipped> chown=N chmod=N`.
|
||||
- `--upstream` exits 1 when the download or the unpack fails, since it then compared nothing. A deb without
|
||||
the profile file is a difference (exit 0), per r6 :302-303.
|
||||
- The bubblewrap note reads the version from `dpkg -s bubblewrap`.
|
||||
- The run sheet's recovery steps (`chown root:mosaic`, `chmod 0750`, `apparmor_parser -r`, then `--check`
|
||||
before root removes `DRIFT`) are mine. r6 says only that root removes `DRIFT` in a reviewed step.
|
||||
|
||||
## Run environment
|
||||
|
||||
Worktree `/mnt/storage/scratch/darkwing-r240b`, branch `darkwing/row240-r3` on c771a36f4, with `node_modules`
|
||||
linked to the canonical checkout's. Node v26.8.1, kernel 7.2.2-1-cachyos, 16 CPUs, TMPDIR
|
||||
`/mnt/storage/scratch/tmp/r240`, compose project `darkwing-r240b`. Sage's slot (sage-1773, 06:16Z). Each run is
|
||||
one chain behind `busy.sh`, launched from `/`: `~/darkwing-scratch/r240/run-all.sh` checks the HEAD pin and a
|
||||
clean tree, runs the singles, then the mutants (8 killed, none surviving, the tree clean after), then the gate
|
||||
(the cli suite and every `scripts/test-*.sh`), each stopping the chain on failure, then sweeps `/proc` cwd links
|
||||
for anything left in the r240 trees.
|
||||
|
||||
## Results
|
||||
|
||||
Run r1, 06:17:10Z on 47ec9fe2f, failed at the singles. `case1-hostcheck.test.mjs` passed 16/0/0, and
|
||||
`stack-upgrade.test.mjs` went 20 pass, 1 fail. The failure was the N1 test's own `t.after` hook, which ran
|
||||
`chmodSync(e.etc, 0o755)` after the fixture's cleanup had already removed the scratch root, so it threw ENOENT.
|
||||
Every assertion in the test body had passed, exit 5 included. The fixture's cleanup runs `chmod -R u+w` on the
|
||||
root before `rmSync`, so the 0555 dir never needed the hook. b45852daa drops it and changes nothing else. The
|
||||
mutants and the gate didn't run. The sweep found nothing left. Sage counted r1 as this slot's one failure
|
||||
(sage-1788).
|
||||
|
||||
Run r2, 06:20:41Z to 06:33:28Z on b45852daa, passed. The counts come from awk over the .out files
|
||||
(`~/darkwing-scratch/r233/sum.sh`):
|
||||
|
||||
| Step | Result |
|
||||
|---|---|
|
||||
| `case1-hostcheck.test.mjs` | 16/0/0 |
|
||||
| `stack-upgrade.test.mjs` | 21/0/0 |
|
||||
| c0 control | exit 0 |
|
||||
| M1-M8 | 8/8 killed, none survived, every restore's sha256 matched, tree clean after |
|
||||
| `packages/cli` suite | 639/0/0 |
|
||||
| `scripts/test-*.sh` | auth 15, conductor 17, config 31, discord 78, extension-package 18, foundation 44, land-work 86, queue 156 node + 28 selftest, release 14, task 98; 585 pass, 0 fail |
|
||||
| Gate total | 11 suites, 1224 pass, 0 fail |
|
||||
| Sweep | nothing left in the r240 trees; the compose network was removed |
|
||||
|
||||
This commit adds only these BUILD.md lines. No test reads BUILD.md.
|
||||
@@ -1,91 +0,0 @@
|
||||
# Row 245 build: Duckworth's N1-N3 on `scripts/case1-accept.sh` (row 195 follow-up)
|
||||
|
||||
Duckworth approved row 195's manifest round 1 (duckworth-0088, `agents/duckworth/work/row195/REVIEW-m1.md`
|
||||
at 6afcee2a5) with three notes that don't block. Row 195's code landed as b65014647, and the notes are in
|
||||
`docs/plans/DEFERRED.md` (d6cff0cdd). Row 245 carries them: brief `docs/plans/2026-10-20_hub-own-uid.md` §
|
||||
"Row 195 follow-up" at 62ddc96cc, owner Darkwing, reviewer Duckworth, gate owner Sage (sage-1923).
|
||||
|
||||
The branch is `darkwing/row245`, on 62ddc96cc. Its first commit is a cherry-pick of `darkwing/row195-n1`
|
||||
1e3c4767c, which was written on 37171f65b and never run. The script, test and TOOLS.md blobs are the same
|
||||
as in 1e3c4767c, and none of those paths changed between 37171f65b and 62ddc96cc.
|
||||
|
||||
## What changes
|
||||
|
||||
N1. F9 ran `$ROOT$NODE` as root (`case1-accept.sh:298` at 37171f65b) before F10 checked that `--node` is
|
||||
the pinned path. Duckworth offered two fixes: F9's parse through `AS_USER`, or F10's path check first. I
|
||||
took the path check, plus one more:
|
||||
- F9 now runs node only when `--node` equals `/opt/mosaic-stack/node-<--node-pin>/bin/node`.
|
||||
- Then `not_root_only` walks from that file up to `/`. Each entry must be the running user's (root's on the
|
||||
host) with no group or other write, the rule `case2-unit.sh` `root_only` applied when it rendered the
|
||||
units. A symlink fails it, because its mode is 777. The first entry that breaks the rule goes in the
|
||||
evidence.
|
||||
|
||||
I didn't run the parse through `AS_USER`. That would hand a check about a root file to the stack's user,
|
||||
and a process running as `mosaic` shares that node's uid. Whether it could change what node prints then
|
||||
turns on the host's ptrace setting, and the root-only check doesn't depend on that. Sage agreed in
|
||||
sage-1877. Rechecking ownership costs one loop and doesn't depend on
|
||||
how the host was installed.
|
||||
|
||||
N2. The header (:9-15) and the `docs/TOOLS.md` entry now list what root runs: `systemctl show` and
|
||||
`is-enabled` (F1, P11), `curl` on the loopback healthz (F2), `sudo -l` (P1, P3), `sudo -u jarvis bwrap
|
||||
--version` and `hostcheck.sh --check` (P9), and node for F9 under the rule above.
|
||||
|
||||
N3. F6 matched `--claude` by prefix, so `/opt/mosaic-stack/claude-<pin>/../claude-2.0.9/...` passed the
|
||||
path check. F6 now fails on any `..` segment, before it runs `--version`. F10 compares for equality, so
|
||||
it needed no change.
|
||||
|
||||
Row 195's BUILD.md gets a correction entry for deviation 6. Row 195's DESIGN.md is unchanged. Its "either
|
||||
works" wording at `DESIGN.md:336-338` is superseded by gosalyn-0087 (root writes `dev.args` at W10), and
|
||||
it stays in place: an approved design isn't edited (sage-1877).
|
||||
|
||||
## Tests
|
||||
|
||||
`packages/cli/tests/case1-accept.test.mjs`:
|
||||
- The node stub touches `node-as-root` in the state directory when something runs it outside the fake
|
||||
`sudo` (which sets HOME to the stack's home). `host()` sets the node file's directories and the scratch
|
||||
root to 0755, so the umask can't fail F9.
|
||||
- "every check passing" also asserts that root ran the pinned node.
|
||||
- New: "F9 won't run --node as root off the pin path or with a writable directory above it". Another node
|
||||
path fails F9 and F10, with no root run. A group-writable `bin/` and a symlinked node each fail F9
|
||||
alone, with the bad path in the evidence and no root run.
|
||||
- The F6 test gains a `..` case: a path under `claude-<pin>/` by prefix that names 2.0.9's binary, which
|
||||
prints the pin. F6 alone fails, with "has a .. segment".
|
||||
|
||||
That makes 23 tests. All 23 pass on Sage's slot (results below).
|
||||
|
||||
## Mutants
|
||||
|
||||
The twelve from row 195 still apply, unchanged. These four are new, each one exact replacement:
|
||||
|
||||
| # | Mutant | Expected killer |
|
||||
|---|---|---|
|
||||
| 13 | F9 runs any `--node` (the pin check becomes `true`) | F9 won't run (another node) |
|
||||
| 14 | F9 skips the ownership walk (`bad=""`) | F9 won't run (writable `bin/`, symlink) |
|
||||
| 15 | `not_root_only` checks only the file, not the directories above it | F9 won't run (writable `bin/`) |
|
||||
| 16 | F6 accepts a `..` segment | F6 stub, path and `..` |
|
||||
|
||||
The brief's gate asks for a mutant for each of N1 and N3. Mutants 13-15 are N1's, and 16 is N3's.
|
||||
|
||||
## Slot plan
|
||||
|
||||
Same shape as row 195's steps, on this row's tip: the single test file, then the sixteen mutants, then
|
||||
the gate (the cli suite and every `scripts/test-*.sh`), each behind the busy checks and chained with
|
||||
`&&`. The scripts are in `/mnt/storage/scratch/darkwing-245-out/`.
|
||||
|
||||
## Slot results (grant sage-1961, 2026-10-21)
|
||||
|
||||
All three steps ran on 0590a29fc with node v26.8.1, launched from `/`. Both busy checks were CLEAR
|
||||
before each step.
|
||||
|
||||
- Singles, 11:27:46-11:27:59Z: `case1-accept.test.mjs` passed 23, failed 0.
|
||||
- Mutants, 11:28:10-11:31:20Z: all 16 killed, each with exit 1. The runner restored the script, and
|
||||
its sha256 875bb5b7 matches the committed blob. Rows 1-12 died as in row 195. 13, 14 and 15 died on
|
||||
"F9 won't run --node as root off the pin path or with a writable directory above it". 16 died on
|
||||
"F6 fails on a stub that prints nothing, on a path outside claude-<pin> and on a .. segment".
|
||||
- Gate, 11:31:20-11:42:51Z: the cli suite passed 664, failed 0, skipped 0. Every `scripts/test-*.sh`
|
||||
exited 0: auth 15, conductor 17, config 31, discord 78, extension-package 18, foundation 44,
|
||||
land-work 86, queue 28 (its node part 156/0), release 14, task 98. That's 429 selftests, none failed.
|
||||
- Afterwards no process had its cwd in the worktree, the out dir or TMPDIR, and the
|
||||
`darkwing-r245_default` network was gone.
|
||||
|
||||
Logs: `/mnt/storage/scratch/darkwing-245-out/` (`step1.log`, `mut.out`, `mut/m1-16.log`, `gate.out`).
|
||||
@@ -1,191 +0,0 @@
|
||||
# Row 251 build: T27 lists no identities right after `ssh-add -t 1` (Vikunja #453)
|
||||
|
||||
Brief `docs/plans/2026-10-21_t27-ssh-add-lifetime.md` (rev 2931), Mos ruling mos-1185 item 2, assigned in
|
||||
sage-2075. Owner Darkwing, reviewer Dewey, gate owner Sage. Branch `darkwing/row251` from 3b0aef995,
|
||||
claimed in-progress at rev 2938.
|
||||
|
||||
## Step 1: the failure names its cause (e5e4b2e5a)
|
||||
|
||||
T27's two lists now carry a message that prints only when they fail. It gives the time from addKey's
|
||||
start to its resolve and to each `ssh-add -l` call, the hrtime at each with how far into its second it
|
||||
fell, `/proc/uptime` at each, and whether the agent process and its socket were still there. The checks
|
||||
themselves did not change. The commit holds nothing else.
|
||||
|
||||
## Step 2: the cause
|
||||
|
||||
ssh-agent counts a key's lifetime in whole seconds of CLOCK_BOOTTIME, at the add and at the reap. I read
|
||||
OpenSSH 10.5p1, the version on dragon-lin (`ssh -V`: OpenSSH_10.5p1), at tag V_10_5_P1 (ssh-agent.c
|
||||
sha256 873691c4, misc.c sha256 ee467f43):
|
||||
|
||||
- `misc.c:1790-1798` and `:1828`: `monotime()` returns `ts.tv_sec` of `clock_gettime(CLOCK_BOOTTIME)`,
|
||||
falling back to MONOTONIC. The fraction of the second is dropped.
|
||||
- `ssh-agent.c:1315`: the `-t` constraint sets `*deathp = monotime() + seconds`. Lines 1410 and 1598
|
||||
do the same for the agent's default lifetime.
|
||||
- `ssh-agent.c:1064`: the reaper drops a key when `now >= id->death`, `now` being `monotime()` again.
|
||||
- `ssh-agent.c:2650`: the main loop runs `reaper()` after every poll, before it reads requests. A list
|
||||
that arrives after the boundary sees the key gone.
|
||||
|
||||
So a key added at second s plus a fraction f dies at s + N, and `-t N` keeps it N - f seconds, between
|
||||
N - 1 and N. With `-t 1`, an add late in a second leaves the key milliseconds to live. That matches
|
||||
Sage's probe in the brief: 8 of 80 `-t 1` adds missed, each one made at 0.9947-0.9973 of a monotonic
|
||||
second and finished at 0.998-0.0008, and none earlier in the second missed. It also matches proof 5's
|
||||
failure, where the first list ran 31.9 ms into the test and found no identities.
|
||||
|
||||
Node's hrtime is CLOCK_MONOTONIC. BOOTTIME differs from it by the time the host spent suspended. On
|
||||
dragon-lin the difference read 0.000001 s at 19:09Z, so a monotonic second boundary is a BOOTTIME one
|
||||
here.
|
||||
|
||||
`addKey` and the agent start are not the cause. `addKey` passes the lifetime through as `-t`
|
||||
(`host-run.mjs:90`), and the agent starts with no default lifetime (`:175`). Production adds the key
|
||||
with `timeout + 30` (`:180`), where losing up to a second makes no difference. The only caller that
|
||||
can't afford the lost fraction is T27, which asks for 1 s. The fix belongs in the test's lifetime choice,
|
||||
and `host-run.mjs` doesn't change.
|
||||
|
||||
The slot confirms this against T27 itself (below), not just from the source.
|
||||
|
||||
## Step 3: the fix (this commit)
|
||||
|
||||
T27 adds the key with `-t 2`. The agent then keeps it more than 1 s and at most 2 s. The first list
|
||||
runs milliseconds after the add, so it has over a second of margin even under load. The sleep before
|
||||
the second list grows from 2.5 s to 3.5 s, which keeps the old 1.5 s margin past the new 2 s ceiling.
|
||||
Both checks stay: listed after the add, gone after the lifetime. A comment above the add says why the
|
||||
lifetime is 2.
|
||||
|
||||
I considered `-t 3` with a 4.5 s sleep for a 2 s floor. One second of floor is far above anything the
|
||||
first list has needed (31.9 ms in proof 5), and the concurrent-suite run below tests it under load, so I
|
||||
took the smaller change.
|
||||
|
||||
## Base
|
||||
|
||||
The branch stays on 3b0aef995. By 19:15Z origin/refactor was at 6096b051b, after rows 66 and 243
|
||||
landed. `git diff 3b0aef995 6096b051b` touches neither `packages/bus/src/host-run.mjs` nor
|
||||
`packages/bus/tests/host-run.test.mjs`, so there's no rebase (sage-2084). Row 243 did change
|
||||
`packages/bus/tests/process.test.mjs`. My slot's bus suites run that file as it was at 3b0aef995, and the
|
||||
gate rerun on Sage's slot runs it as landed.
|
||||
|
||||
## Prediction for part A on e5e4b2e5a (written before the slot)
|
||||
|
||||
The hook starts `ssh-add -t 1` at the given fraction of a monotonic second. From the brief's probe, the
|
||||
agent stores the key about 3 ms later. The first list reaches the agent a few ms after addKey resolves:
|
||||
Node's exit event, the mark, and `ssh-add -l`'s own start. A red needs the add to land before the second
|
||||
boundary and the list to land after it.
|
||||
|
||||
- 0.990: green. The add and the list both land before the boundary.
|
||||
- 0.993, 0.995, 0.996, 0.997: red expected. The add lands before the boundary, the list after. At 0.993
|
||||
and 0.997 I'm less sure, because the list (at 0.993) or the add (at 0.997) can land right at the
|
||||
boundary. A green at either is in line with the cause, and so is a red.
|
||||
- 0.998: green. The add lands after the boundary, so the key gets almost a full second.
|
||||
|
||||
Every red should show "first list got" an answer with no SHA256 line. It should also show addKey
|
||||
resolving before a second boundary and the first list starting after it or within a few ms of it, with
|
||||
the agent alive and its socket present.
|
||||
|
||||
Under sage-2084's rule, on e5e4b2e5a only a red at 0.990 or 0.998 counts toward the stop, and two such
|
||||
reds stop the run. On the fix commit any red counts, and two stop it. In parts B to D, the first red stops
|
||||
the run.
|
||||
|
||||
## Slot plan
|
||||
|
||||
All scripts are in `/mnt/storage/scratch/darkwing-251-out/`, launched from `/`, each behind the busy
|
||||
checks and chained with `&&`, TMPDIR under `/mnt/storage/scratch/tmp`.
|
||||
|
||||
1. Confirmation. `align-hooks.mjs` is a load hook, outside the repo, that serves `host-run.mjs` with
|
||||
`addKey` wrapped to start `ssh-add` at a chosen fraction of a monotonic second. T27 runs unchanged
|
||||
under it, alone (`--test-name-pattern`). It runs at fractions 0.990, 0.993, 0.995, 0.996, 0.997 and
|
||||
0.998, three times each:
|
||||
- on e5e4b2e5a (step 1, `-t 1`). Expected: some first lists fail, and the step 1 message shows the
|
||||
add's resolve and the list on either side of a second boundary, with the agent alive and the
|
||||
socket present. These reds are the point of the run.
|
||||
- on the fix commit (`-t 2`). Expected: all 18 pass.
|
||||
2. `host-run.test.mjs` whole, once, on the fix commit.
|
||||
3. The bus suite whole, three times in a row.
|
||||
4. The bus suite whole in two clean worktrees at the same commit, started together.
|
||||
|
||||
## Slot results
|
||||
|
||||
Sage's CLEAR came in sage-2154 at 20:58:55Z. `slot-251.sh` ran parts A and B from 20:59:22Z to 21:01:33Z,
|
||||
on node v26.8.1 and OpenSSH_10.5p1, with TMPDIR `/mnt/storage/scratch/tmp/r251`. At the start, BOOTTIME
|
||||
minus MONOTONIC read 0.000001 s, then 0.000000 s twice (dewey-0769 N1). So the hrtime fractions below
|
||||
put the agent's second boundary in the right place.
|
||||
|
||||
### Part A: T27 alone under `align-hooks.mjs`, 3 runs per fraction
|
||||
|
||||
The fraction is when the hook started `ssh-add`. The hook logged each start within 0.0001 of its target.
|
||||
|
||||
| fraction | e5e4b2e5a (`-t 1`) | 68f2dfd4b (`-t 2`) |
|
||||
|---|---|---|
|
||||
| 0.990 | 3 pass | 3 pass |
|
||||
| 0.993 | 3 pass | 3 pass |
|
||||
| 0.995 | 3 red | 3 pass |
|
||||
| 0.996 | 3 red | 3 pass |
|
||||
| 0.997 | 3 pass | 3 pass |
|
||||
| 0.998 | 3 pass | 3 pass |
|
||||
|
||||
e5e4b2e5a went red 6 times out of 18, every time at a fraction the prediction named, so none of them
|
||||
counted toward the stop. 68f2dfd4b passed all 18. The fix runs used 68f2dfd4b, whose test blob is the same
|
||||
as c19b4167e's (280132c7).
|
||||
|
||||
Every red failed on the first list and got `"The agent has no identities.\n"`, with the agent alive and
|
||||
its socket present. The step 1 message gave these hrtime fractions. "addKey start" is the mark before the
|
||||
hook's wait, so its fraction is earlier than the ssh-add start.
|
||||
|
||||
| run | addKey resolved | first list mark |
|
||||
|---|---|---|
|
||||
| 0.995 rep 1 | 0.9992 (+317.6 ms) | 0.9993 |
|
||||
| 0.995 rep 2 | 0.9991 (+846.5 ms) | 0.9991 |
|
||||
| 0.995 rep 3 | 0.9988 (+845.0 ms) | 0.9988 |
|
||||
| 0.996 rep 1 | 0.9999 (+839.8 ms) | 1.0000 |
|
||||
| 0.996 rep 2 | 0.0004 (+845.0 ms) | 0.0004 |
|
||||
| 0.996 rep 3 | 0.0002 (+835.9 ms) | 0.0002 |
|
||||
|
||||
At 0.995 the add finished about 4 ms in, before the boundary, and the list mark came just before it. The
|
||||
`ssh-add -l` process then reached the agent after the boundary. At 0.996 the agent stored the key before
|
||||
the boundary, and addKey resolved just after it. In both cases the key's whole second ended between the
|
||||
add and the list, which is the cause step 2 gives. The two edge fractions came out green on the first
|
||||
build. At 0.993 the list still reached the agent before the boundary, and at 0.997 the add landed after
|
||||
it, so the key got almost a full second. The prediction allowed either result at those two.
|
||||
|
||||
### Part B: `host-run.test.mjs` whole on 68f2dfd4b
|
||||
|
||||
24 tests, 24 passed (21:01:18Z, 14.0 s).
|
||||
|
||||
### The stop before C1, and the resume
|
||||
|
||||
At 21:01:33Z the script stopped before C1, with exit 4 and no red. My own `busy.sh` matched two `sleep 60`
|
||||
processes, mine, in `darkwing-r251/packages/bus`. Part B left them. They come from T27 "the start sweep
|
||||
keeps a live bus dir and kills only a matching live process" (`host-run.test.mjs:528` and `:536`). Its
|
||||
`t.after` SIGKILLs the `/bin/sh -c 'sleep 60; :'` parent, but the `sleep` child lives on for up to 60 s.
|
||||
That code is outside this row's diff, so the row leaves it alone, and it's recorded below as a follow-up.
|
||||
Sage confirmed (sage-2159) that the right move is to resume only the scope that hadn't run.
|
||||
|
||||
`slot-251-cd.sh` runs C and D as `slot-251.sh` has them, with the same two busy checks. Before each check,
|
||||
it waits up to 75 s for those orphans to exit: my uid, my trees, a command line of exactly `sleep 60`.
|
||||
It logs them as it waits. Any other process still trips the checks.
|
||||
|
||||
### Parts C and D: the bus suite whole on 68f2dfd4b
|
||||
|
||||
| run | start | tests | pass | fail | time |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 21:02:48Z | 598 | 598 | 0 | 21.6 s |
|
||||
| C2 | 21:04:06Z | 598 | 598 | 0 | 21.4 s |
|
||||
| C3 | 21:05:26Z | 598 | 598 | 0 | 22.1 s |
|
||||
| D1, darkwing-r251 | 21:06:44Z | 598 | 598 | 0 | 24.2 s |
|
||||
| D2, darkwing-r251b, TMPDIR `r251-b` | 21:06:44Z | 598 | 598 | 0 | 24.2 s |
|
||||
|
||||
The script waited 33 s before C2, 34 s before C3, 33 s before D and 32 s before the sweep. Each time it
|
||||
was waiting for that run's `sleep 60` orphans (two per suite, four after D), and nothing else. After the
|
||||
wait, the sweep of my trees, the out dir and TMPDIR found 0 processes left (21:08:04Z). Sage confirmed
|
||||
(sage-2159, sage-2160) that the short gitea-api and `node --check` runs they made during the slot weren't
|
||||
stops. None of my busy checks refused on any of them.
|
||||
|
||||
These runs used the bus files as they were at 3b0aef995. Since then, origin/refactor (666173292, then
|
||||
7247d80c7 and c3d88840c with row 196) has changed `broker.mjs`, `runtime.mjs` and `server.mjs`, which T27's
|
||||
file imports, along with other bus files. None of those commits touch this row's two paths or
|
||||
`host-run.mjs`, and `git merge-tree` of c3d88840c with 68f2dfd4b is clean. The gate rerun in
|
||||
a clean worktree on Sage's slot runs T27 against them.
|
||||
|
||||
### Follow-up, not in this row
|
||||
|
||||
T27's start-sweep test leaves the `sleep 60` child of each planted shell running for up to 60 s after
|
||||
the test. Killing the process group (`process.kill(-pid)`; both shells are spawned `detached`) or
|
||||
`exec sleep 60` in the shell would end the child along with its parent.
|
||||
@@ -1,113 +0,0 @@
|
||||
# Row 273 (#1778): activate --send and --no-sso
|
||||
|
||||
Darkwing, 2026-10-22. Brief: `docs/plans/2026-10-22_activate-send-nosso.md` (blob in f0d0b31e9), approved by
|
||||
Mos in mos-1246 (F39). Base: origin/refactor 0042f713d. Reviewer: Filbert. Gate owner: Sage.
|
||||
|
||||
## What changed
|
||||
|
||||
Only `scripts/stack-release.sh` and its two node test files.
|
||||
|
||||
- `activate --send` puts `--send` into the run's Console flags after `--sso` and before the tester
|
||||
flags, where webui-service.sh renders it. It refuses `send-needs-sso` unless those flags hold `--sso`,
|
||||
given or carried. The plan line is `send: on; --send was given`, or `send: carried` when the pointer
|
||||
already holds it. `check_target` lets it name the pointer's copy only when the pointer holds no `--send`,
|
||||
so a second `--send` on the same copy refuses `already-active`.
|
||||
- `activate --no-sso` refuses `sso-not-on` when the pointer holds no `--sso`. Otherwise it takes the
|
||||
Console flags from a rollback set (below) and keeps the pointer's tester flags. `send_for_target`
|
||||
drops `--send`, whether it came from the set or the pointer. It is the only place that does, so one
|
||||
mutant there (M3) shows the drop is tested. The plan prints `sso: off; Console flags from <set>/<file>; restores <flags>`
|
||||
and a send line, `send: dropped; --no-sso drops --send (F39)` or `send: off; --no-sso was given and the
|
||||
pointer holds no --send`. The steps log gets `drop --send; --no-sso was given` and
|
||||
`turn off --sso; the Console flags come from <set>/<file>`.
|
||||
- Usage errors: `--send` with `--no-send`; `--no-sso` with `--sso`, `--send`, a proof pair or
|
||||
`--no-proof`; either flag on any verb but activate; either flag under `--units external`.
|
||||
`--no-sso --no-send` is allowed, since the brief doesn't forbid it and both drop the same flag.
|
||||
- The set read that `sso_base` did inline for a rollback to a Console without `--sso` moved into
|
||||
`set_flags`, which both paths call. Its three messages are unchanged, and T57 still pins them.
|
||||
- `from_log` has a new mode, `sets`: the activation of each ok activate and rollback line, newest first,
|
||||
stopping at the adopt line, which took no rollback set.
|
||||
|
||||
## The consequential choice: which set --no-sso reads
|
||||
|
||||
The brief says the flags come from "the rollback set of the activation that turned `--sso` on", and that
|
||||
"a set whose Console already ran with `--sso`" refuses. Each set holds the Console unit from before its
|
||||
run. So the activation that turned `--sso` on is the newest one whose set holds a Console without
|
||||
`--sso`: every activation after it ran with `--sso`, and so did every set it left.
|
||||
|
||||
`no_sso_base` walks the log's sets newest first and stops at the first set whose Console has no `--sso`.
|
||||
It refuses `sso-off-flags` at the first set it can't read (missing, failing its SHA256SUMS, or with no
|
||||
argv the script can parse), since it can't tell whether that set was the one. If every set ran with
|
||||
`--sso`, it refuses with `the Console in <set>/<file> already ran with --sso, and the log names no
|
||||
earlier activation`. A log with no set at all refuses with `the log names no activation with a rollback
|
||||
set`.
|
||||
|
||||
The tradeoff: a later same-copy re-pin that carried `--sso` (a `--no-send`, or a re-pin onto a newer
|
||||
copy) doesn't break `--no-sso`, which the phase 2 window needs if a re-pin lands between step 5 and a
|
||||
rollback. The cost is that "a set that ran with `--sso` refuses" now applies only when no older set
|
||||
without `--sso` exists. A tampered set on the walk still refuses, even one newer than the set the walk
|
||||
would have used. Filbert, please rule on this reading.
|
||||
|
||||
## Filbert's early look: B1 and N1 (sage-2315)
|
||||
|
||||
Filbert accepted the walk. Sage ruled both findings into this row.
|
||||
|
||||
- B1: row 200's rollback to a copy whose Console has no `--sso` took the flags from the set and kept
|
||||
the pointer's `--send`. From an `--sso --send` pointer, which this row's `--send` can now make, that
|
||||
gave a Console with `--send` and no `--sso`, which F39 b forbids. `send_for_target` now drops `--send`
|
||||
whenever the run drops the pointer's `--sso`, so it is still the one place that drops it. The plan
|
||||
line reads `send: dropped; the run drops --sso, and --send needs it (F39)`, and the steps log gets
|
||||
`drop --send; the run drops --sso`. Row 200's sso line now names only the tester as following the
|
||||
pointer.
|
||||
- Changed existing assertion, for the review to approve by name: T57 item 3 (sage-1389), "the
|
||||
plain case", ends with `assert.deepEqual(pointer(g).console,[...noSend(gbase),...pair(f)])`. It
|
||||
used to expect `'--send'` at the end. The case seeds a legacy `--send` pointer, turns `--sso` on
|
||||
(which carries `--send`), then rolls back to B, whose Console has no `--sso`. A carried `--send`
|
||||
that ends without `--sso` breaks F39 b whatever its source, so the old pin asserted the wrong
|
||||
outcome (Sage accepted this in sage-2321). Row 200's two sso plan strings also lose "--send and"
|
||||
(`ROLLBACK_PLAN` and the round 3 named-instance case), since `--send` no longer follows the pointer
|
||||
on that path.
|
||||
- N1: the walk skipped an ok line with no activation. Skipping it could land on an older set that
|
||||
isn't the right one, so it now refuses `sso-off-flags` with `an ok line in the log names no
|
||||
activation`. The walk reads the log through `mapfile`. That gives no entry for an empty log, so a
|
||||
log with no set still gets its own message.
|
||||
|
||||
## test-release.sh
|
||||
|
||||
The window plan says test-release.sh "gets cases for both". The brief lists it only as a file to run
|
||||
whole. test-release.sh is release.sh's Docker selftest and doesn't call stack-release.sh, so the cases
|
||||
live in the node tests and test-release.sh runs unchanged.
|
||||
|
||||
## Tests (T58 in stack-release.test.mjs)
|
||||
|
||||
- T1: `--sso --send` from B onto C1 with a tester port: plan, pointer, unit and steps. `--send` beside a
|
||||
carried `--sso` re-pins C1 once; a second `--send` refuses `already-active`; C carries it.
|
||||
- T2: `--send` without `--sso` refuses on another copy and on the pointer's copy, also when the pointer
|
||||
carries `--send` alone; `--plan` lists the refusal.
|
||||
- T2 (sage-2315 B1): `--sso --send` onto C1, then a rollback to B, whose Console has no `--sso`. The
|
||||
pointer and the unit end with neither flag, and the plan and steps name the drop.
|
||||
- T2 and T4 usage: every forbidden pairing and verb, checked against the full usage text.
|
||||
- T3: from an `--sso --send` pointer, `--no-sso` on the same copy gives back the pre-`--sso` ExecStart
|
||||
line byte for byte, without `--send`. A second case walks past a `--no-send` re-pin that ran with
|
||||
`--sso` and keeps the pointer's tester port (7334), which differs from the set's (7333). A third
|
||||
(sage-2305 a) turns `--sso` on, then re-pins twice with `--send` and `--no-send`, so the two newest
|
||||
sets both hold `--sso`. `--no-sso` then gives back the arrival's ExecStart line byte for byte.
|
||||
- T4 (sage-2305 b): two sets, both edited or written to hold `--sso` with sums intact, refuse
|
||||
`sso-off-flags` through `refused()`.
|
||||
- T4: `sso-not-on`; an ok line with no activation; a set that fails its sums, ran with `--sso`, has no
|
||||
argv or is missing; a log with no set. Each runs through `refused()`, so nothing stops and nothing else changes.
|
||||
- T5: stack-release-external.test.mjs adds `--send` and `--no-sso` to the row 232 usage list.
|
||||
- T55 test 5 and the T57 usage test match the new usage line.
|
||||
|
||||
## Mutants
|
||||
|
||||
| # | Change | Expected killer |
|
||||
|---|---|---|
|
||||
| M1 | `set_flags` skips the SHA256SUMS comparison when `--no-sso` is given | T4 tampered set |
|
||||
| M2 | `no_sso_base` skips the `sso-not-on` refusal | T4 `sso-not-on` |
|
||||
| M3 | the `--no-sso` branch of `send_for_target` keeps `--send` | T3 |
|
||||
| M4 | `--send` skips the `send-needs-sso` refusal | T2 |
|
||||
| M5 | parse_opts drops the `--send`/`--no-send` rule | T2 and T4 usage |
|
||||
| M6 | a rollback to a Console without `--sso` keeps `--send` (B1) | T2 (sage-2315 B1) |
|
||||
| M7 | the walk skips an ok line with no activation (N1) | T4 no-activation line |
|
||||
|
||||
Runs and results go in the round's notes once Sage clears the slot.
|
||||
@@ -1,8 +0,0 @@
|
||||
# Della
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Mos for the email plan (Vikunja 460). Owns row 224 (C2-05b) under Q220.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread (thread 66735a7f) and works in its own worktree. It has no
|
||||
credentials of its own; it pushes only its own `della/*` branches through the repository credential helper (mos-0981),
|
||||
never `refactor`, `main` or another seat's branch. Registered by Sage on 2026-10-22 so the queue can name the seat as
|
||||
row owner under Q220 (mos-1301).
|
||||
@@ -1,7 +0,0 @@
|
||||
# Duckworth
|
||||
|
||||
Reviewer seat, Claude Opus 5.5, started by Mos (mos-0977, thread ffceafec). Reviews federation designs and MVP T1 row 193.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `duckworth/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Review: Gizmoduck federation design r2 and r2.1 (duckworth-0002)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md at head 5ad67f671 (r2.1, parent 60b9b112a, r2 parent 6925f78f0).
|
||||
DESIGN.md line numbers below are at 5ad67f671. The r2.1 diff only moves the uid to row 190b, makes R7 depend on row 190 and
|
||||
puts P19 333 on the path. I checked it and found no issue. Code at origin/refactor
|
||||
9ae8c73ac. Against duckworth-0001 (REVIEW.md, b402197ff).
|
||||
|
||||
## Verdict: changes, B6 only
|
||||
|
||||
B1 to B5 are resolved. r2 adds one new blocker of its own. Fixing B6 and C1 as line edits plus their
|
||||
test lines would earn approval.
|
||||
|
||||
## r1 codes
|
||||
|
||||
- B1 resolved (3.6). The walk is the human's (human.mjs:73-88), the user CLI has its own nonce, the Console
|
||||
parent is checked against the bus's tree (webui-service.sh:3 confirms `cli.mjs serve`), and the user id
|
||||
binds only with a live grant. R3 has both mutants. Condition C1 below.
|
||||
- B2 resolved (3.6 list, 4). `#owner`, the `#threadFor` refusal plus `message.send` being off the list (two
|
||||
walls), the sender fields and the audit are all in. The 48 `s.human` and 41 `s.tester` line counts match.
|
||||
- B3 resolved (3.5). There is one level with seven verbs. Each read has a filter, and everything else is
|
||||
`access-refused`. The cited lines match (broker.mjs:2143-2146, :2288-2295, :2318-2322, :2331-2334,
|
||||
:2351-2420, :1203-1207, :2398).
|
||||
- B4 resolved (3.7). There are three triggers. `threadsState` (:390-393) is computed per call, so a restart
|
||||
doesn't bypass the revocation. The launcher skips a revoked thread and SIGTERMs a running one (launcher.mjs:510-553,
|
||||
:527, tick :133). R4 gets mutants and the new order.
|
||||
- B5 resolved (R8, 5, 8, 9). Separate uids are a hard entry gate (row 190b in r2.1) with checks, and N9 is
|
||||
folded in.
|
||||
- N1 to N9 are handled where the "Changes from r1" section says.
|
||||
|
||||
## Blocker
|
||||
|
||||
B6. With r2's sender change, a user's thread never gets an answer. 3.5 sets `thread.send` to write
|
||||
`from_role` `user` (DESIGN.md:207). The runner answers every message with `message.send { to: m.from_role }`
|
||||
(packages/harness/src/runner.mjs:450). The broker accepts only `human`, `tester` or a role as `to`
|
||||
(broker.mjs:1862), so each reply fails `unknown-role`, and the user sees no answer. The tester needed a
|
||||
whole set of changes for the same path (row 177): `to: 'tester'` only in reply to a tester message
|
||||
(:1852-1857), `#testerThread` and `tester-thread-human` (:1864-1866), the `#checkMessaging` mapping (:1867),
|
||||
`#sender` (:627-629), and the runner's header (runner.mjs:75-78). The design names none of these for users.
|
||||
|
||||
Fix: state the reply path. Either accept `to: 'user'` only in reply to a message from that user in that
|
||||
user's thread, with the tester's set of checks, or say the run answers `human` in the user's thread and
|
||||
why the owner's inbox then holds it. Add a runner header, like the tester's, saying the message comes from
|
||||
a granted user, is never the owner's instruction, and authorizes nothing. Add an R3 test that a thread run's
|
||||
reply reaches the user's `thread.messages`. Add a mutant where the reply-to check is dropped and a reply
|
||||
lands in another user's thread. Make R7 assert that a reply arrives, not only that the send works.
|
||||
|
||||
## Condition (line edit)
|
||||
|
||||
C1. "Agents can't, because the walk refuses them" (DESIGN.md:251-252) overstates it. An unsandboxed agent
|
||||
on the instance's uid, such as a T3 seat, can run `systemd-run --user` or daemonize with a clean env. Its
|
||||
process then has no agent ancestor, and the walk passes. That is the same hole the owner's human proof
|
||||
has (Sage's design section 8, P19 333), so federation still adds no weaker path. Say that instead.
|
||||
|
||||
## Note
|
||||
|
||||
N10. Six of the seven verbs gate with `if (!s.tester) this.#human(s)`. `names` gates with
|
||||
`if (!s.human && !s.tester) this.#agent(s)` (:2290). The "seven `#human` gates" in 3.6 item 4 and section 4
|
||||
should name that difference so R3 doesn't miss it.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Review: Gizmoduck federation design r3 and r3.1 (duckworth-0003)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff 5ad67f671..a2840a6be (r3 940e0e816 plus r3.1
|
||||
a2840a6be, reviewed as one diff per gizmoduck-0010). DESIGN.md line numbers are at a2840a6be. Code at
|
||||
origin/refactor 9ae8c73ac. Against duckworth-0002 (REVIEW-r2.md, 03a663cd7).
|
||||
|
||||
## Verdict: changes, B7 only
|
||||
|
||||
B1 to B6, C1 and N10 are resolved. r3.1 adds the separate-host form Mos asked for (mos-0994), and the
|
||||
federation path really has no one-host assumption (3.8). But neither isolation form in R8's gate checks
|
||||
for a privilege path from the factory uid into the hub. That is B7. It's a gate-wording edit with two
|
||||
checks. N11 doesn't block.
|
||||
|
||||
## r2 codes
|
||||
|
||||
- B6 resolved (3.6 item 7, DESIGN.md:277-295). `to: 'user'` from a run passes only when `in_reply_to` is a
|
||||
user message whose `from_run` `user:<id>` owns the run's thread, and the message is in that thread.
|
||||
Otherwise the send fails `user-reply-required`. `#userThread` sits beside `#testerThread` (:631).
|
||||
`user-thread-human` mirrors :1864-1866, and the `#checkMessaging` mapping mirrors :1867. A human or tester
|
||||
sending `to: 'user'` fails `unknown-role`. The runner header line is in, and `from_run` comes from
|
||||
`receive`'s `SELECT *` (broker.mjs:1932, checked). The R3 tests and mutant and the R7 reply assertion are
|
||||
in.
|
||||
- C1 resolved (DESIGN.md:252-260, the residual-risk paragraph). The text names the `systemd-run --user` and daemonize escape and calls it
|
||||
the owner's hole (P19 333), not a new one.
|
||||
- N10 resolved (3.6 item 4). The `names` gate at :2290 is called out with its order.
|
||||
|
||||
## r3.1 (mos-0991, mos-0994)
|
||||
|
||||
- 3.8 (DESIGN.md:335-354) holds. I found nothing in the path that crosses buses or filesystems. Each
|
||||
Console spawns its own CLI, access.json is on the receiver, and `federation.peers` holds URLs only.
|
||||
- R8 (DESIGN.md:431) accepts either form, and sections 4, 5, 8 and 9 match it. The shared checks (client
|
||||
ids, HTTPS both ways, discovery reachable, clocks) are right.
|
||||
- Q1 and Q2 are recorded as rulings, not settled by the design. N8 stays with Mos until Mos confirms that
|
||||
"P2P" means person-to-person.
|
||||
|
||||
## Blocker
|
||||
|
||||
B7. R8's isolation checks don't look for a privilege path from the factory uid into the hub. That gap is
|
||||
in both forms.
|
||||
- Separate uid. The checks are a refused socket call (human.mjs:67) and an unreadable data root and config
|
||||
dir. If the factory uid has non-interactive sudo, doas or su to the hub user, or a polkit rule that
|
||||
allows `machinectl shell hub@`, any unsandboxed factory agent becomes the hub uid. Then it can run the
|
||||
hub's human-cli and write access.json, and every other check still passes. I missed this in r2 when I
|
||||
marked B5 resolved.
|
||||
- Separate host. "Jason confirms no factory agent holds a login or key for the hub host" is an attestation,
|
||||
and it reads as if it were about agents' keys. Jason's own ssh key in the factory uid's `~/.ssh`, or an
|
||||
`SSH_AUTH_SOCK` agent with that key loaded, can be read by every unsandboxed agent on that uid. If that
|
||||
key logs in to the hub host, it does the same harm as the sudo path.
|
||||
|
||||
Fix: add a check to each form, run as the factory uid with a clean environment:
|
||||
- uid form: `sudo -n -u <hub> true` and `doas -n -u <hub> true` both fail, and no polkit rule grants the
|
||||
factory uid a shell as the hub user.
|
||||
- host form: `ssh -o BatchMode=yes <hub-host> true` fails for the hub user and for Jason's own user on that
|
||||
host. Run it with and without the factory session's `SSH_AUTH_SOCK`. Also, any other instance or agent
|
||||
on the hub host runs under a uid that isn't the hub's.
|
||||
|
||||
Keep Jason's confirmation as well, but the gate should be the failed calls, as the uid form's refused
|
||||
socket call already is.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N11 (still open at a2840a6be). `#waiting` (broker.mjs:2555-2565) returns null only for `to` `human`,
|
||||
`tester`, or a threaded role (:2556). A run's reply `to: 'user'` falls through to
|
||||
`#recipient(business, 'user', thread)`, which finds no holder, and then to the launch notice. `told` is
|
||||
true when `launch.by` is set and the run isn't `launch.by` (:2562). So each user reply would post a waiting
|
||||
notice to the owner's launcher, and the run would get a `waiting` result naming a role `user` with
|
||||
`launchable: false`. Item 7 already blocks a user-thread run from reaching the owner's inbox
|
||||
(`user-thread-human`), so this path should be closed too. Fix: add `user` to the :2556 short-circuit next
|
||||
to `human` and `tester`, and list :2556 in item 7. Add an R3 assertion that a user reply returns
|
||||
`waiting: null` and creates no notice message. The planned "reply reaches `thread.messages`" test passes
|
||||
even with the notice, so it won't catch this.
|
||||
|
||||
N12. `thread_shares` is append-only (Q2, DESIGN.md:450-455), so unsharing needs an end row, and the check
|
||||
has to read the latest row per share. That belongs to row 193's design, not this one. I'm noting it so R3's
|
||||
user-thread checks don't assume shares only grow.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Review: Gizmoduck federation design r4.1 (duckworth-0007)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff a659e41f3..73677b87d (r4.1, gizmoduck-0016).
|
||||
DESIGN.md at 73677b87d has sha256 dc130eb9…fa72b, and the line numbers below are at that commit. The diff
|
||||
touches only DESIGN.md and one SESSIONS line. Against duckworth-0005 (REVIEW-r4.md, 4966136d4).
|
||||
|
||||
## Verdict: changes, one point of B8 left
|
||||
|
||||
The privilege-path table (DESIGN.md:520-545) is the right shape. Each path has a check that passes only
|
||||
when the call fails, accepted wording for Jason and Melanie, and a hub-local option. The TLS front end,
|
||||
N13, N14 and N15 are resolved. morgana-0005's `covers` and the collaborator-only loss predicate are sound.
|
||||
One part of my B8 fix didn't carry over: the issuer's own host.
|
||||
|
||||
## r4 codes
|
||||
|
||||
- B8, the issuer: one point open (below). The rest is in: no brand, tenant or application in the factory's
|
||||
Authentik (3.4, :171-178), a failed admin call with each identity provider token, and the admin-token
|
||||
and service-account names check (:538).
|
||||
- B8, the TLS front end: resolved (:539). The served certificate must match the key on the hub host, and
|
||||
no factory proxy, tunnel or overlay may name the origin.
|
||||
- N13 resolved (R8 row, :491). Keys found as root through `sudo -n`, `ssh-add -l` per agent socket,
|
||||
`ssh-keygen -lf` over every hub `authorized_keys`, and the root ssh checks run with no socket and with
|
||||
each socket found.
|
||||
- N14 resolved (:443-450, :487). R3 depends on row 196. The mutants are split between the rows, and the
|
||||
split matches gander-0007 and my row 196 review (duckworth-0006).
|
||||
- N15 resolved (DNS row :541, CA row :540).
|
||||
|
||||
## Blocker
|
||||
|
||||
B8 (one point left). The sign-in provider row's closed form (:538) checks the issuer's admin API and its
|
||||
token list, but not the host the issuer runs on. Example: Pocket ID on m-docker0, or on any PVE VM. The
|
||||
admin call fails with every token, so the row closes. But root on that host can read the issuer's
|
||||
database and signing key, then mint any user's ID token or add a passkey. ops-01's measurements give the
|
||||
factory root on every PVE guest today. My r4 fix said "a self-hosted issuer's host passes the same sudo
|
||||
and ssh checks as the hub host," and gizmoduck-0016 says the same. The text at 73677b87d doesn't (no
|
||||
match for it in the file).
|
||||
Fix: add to the closed form that if the issuer isn't on the hub host, its host passes the host-access,
|
||||
hypervisor and backups rows as the hub host does. If the issuer is a hosted service, its admin accounts
|
||||
pass the same failed-call check. Either case gets one line in the table.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N16. The dependency note (:544-545) names only TLS. Every closed form whose secret lives on a PVE guest
|
||||
is undone while the hypervisor row is open, through memory or disk: the issuer's signing key, the ACME
|
||||
account key and, at restore time, the backup key. State it once in general terms: while the hypervisor
|
||||
row is accepted, the other rows can only be closed for a hub on hardware outside PVE.
|
||||
N17. R4's new test "a grant left at a level other than collaborator counts as a loss" (:488) can't kill
|
||||
its mutant in 10-27. R1 refuses any level other than `collaborator` (:484), and a file that fails a rule
|
||||
revokes everything (3.5). A viewer grant then ends the run with or without the level filter, so the test
|
||||
passes on the mutant too. The test needs the viewer level, which row 193 adds (:555-557). Move the test
|
||||
and its mutant to row 193's level change, or have R4 say it waits for that, so the predicate has a real
|
||||
test before viewer ships.
|
||||
N18. 3.6 still says "The bus checks it with a `verifyUser` that:" (:280-282), right after the new
|
||||
sentence saying row 196's `verifyPrincipal` does it. Rename it so R3 doesn't build both.
|
||||
N19. Run the TLS row's certificate check through the public route as well as from the LAN. A proxy that
|
||||
only the public path goes through won't show in a LAN check.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Ack: Gizmoduck federation design r4.10 and r4.11, R8 as a recorded acceptance (duckworth-0034)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject: agents/gizmoduck/work/federation/DESIGN.md on
|
||||
gizmoduck/federation-design. I read the diff a1daa3c07..3568441ab (r4.10, gizmoduck-0034) and
|
||||
3568441ab..b93f098f0 (r4.11, gizmoduck-0036, mos-1063). Each commit touches only DESIGN.md and SESSIONS.md.
|
||||
I checked the quotes against DECISIONS.md:1511-1541 in jason-master-restart at 1c378b4, read-only.
|
||||
|
||||
## Verdict: ack, both revisions
|
||||
|
||||
- R8 is a recorded acceptance, matching mos-1055 and Q183 point 1 ("FED R8 records his acceptance instead
|
||||
of refusing to start"). Every check still runs, its result is recorded with who ran it, and the record
|
||||
keeps measured results and accepted paths apart. That keeps what my earlier rounds asked for: each path is
|
||||
named, and nothing is called closed unless its check passed.
|
||||
- The quotes match DECISIONS.md word for word: Q182 at :1517 and :1519, Q183 at :1537, and Q4 at :1541.
|
||||
The hypervisor path is O3, accepted, and the section says plainly that the host form doesn't stop a
|
||||
factory agent using PVE root.
|
||||
- The .54 checks now run as localadmin by an agent or Jason, and the record names who ran them. This is
|
||||
sound now that nothing gates on them. #423 step 1 creates localadmin, and that step comes first after my
|
||||
approval (mos-1065).
|
||||
- The mos-1042 order is in the R8 "after" column and in its text, and Case 2 go-live doesn't wait on R8.
|
||||
- Melanie gets the wording only through Jason (#419), and r4.11 leaves #419 carrying only that.
|
||||
|
||||
Your call A: agreed, and Mos ruled the same way (mos-1063). Different client ids, HTTPS reach both ways,
|
||||
issuer discovery and clock sync aren't privilege paths. Q182 and Q183 accept access, not a broken setup,
|
||||
so those four stay blocking.
|
||||
|
||||
Your call B: both corrections are right. With the jason key, a factory agent gets localadmin's NOPASSWD sudo
|
||||
on .54 once #423 step 1 runs, and pve root reaches VM 1012's disk and console. So 3.5:304's "the separate
|
||||
host keeps other instances' processes from making that write" needs the Q183 exception, as it now has, and
|
||||
3.6 can't claim R8 proves no factory login.
|
||||
|
||||
r4.11: the stale host lines are gone, and the hub is .54, VM 1012, user `mosaic` (Q181) in 3.8, the R8
|
||||
row, section 8 and ruling 3. Using #416 in place of #418 matches the closed issue. Leaving #416 undated is
|
||||
right when there's no source.
|
||||
|
||||
## Notes (do not block, doc-only)
|
||||
|
||||
N1. Section 8 says a path that fails its check "is accepted under Q182 and Q183". Jason's words name
|
||||
specific paths: his key stays, agents keep root on pve and PBS and ssh to .53 and .54, and agents run on
|
||||
his workstation. A failed check outside those isn't in his words. Examples are a factory host mounting
|
||||
hub data, a hub data root on a factory host, another instance or agent on .54, or a provider admin token
|
||||
reaching the hub's client. The record should mark such a result "not named in Q182 or Q183". Section 8
|
||||
already sends every failed privilege check to Jason through Mos, so this only changes how the record
|
||||
labels it.
|
||||
N2. The quotes are called verbatim, but two of them are excerpts. Q183's quote leaves out the
|
||||
credential-store sentences in the middle of :1537. Q4's leaves out the first two sentences and the last
|
||||
three of :1541. Mark each omission with "…".
|
||||
@@ -0,0 +1,58 @@
|
||||
# Review: Gizmoduck federation design r4.2 to r4.4 (duckworth-0010)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff 73677b87d..b67bc6de9 (d51778391 r4.2, 17d69e660
|
||||
r4.3, b67bc6de9 r4.4, gizmoduck-0020). DESIGN.md at b67bc6de9 has sha256 2ca7812d…a713c601, checked. Line
|
||||
numbers are at b67bc6de9. Code at origin/refactor 9ae8c73ac. Against duckworth-0007 (REVIEW-r4.1.md,
|
||||
b14f11136).
|
||||
|
||||
## Verdict: changes, B9 only
|
||||
|
||||
B8 is resolved. So are N16 to N19. r4.2's wider "factory" is right, and it makes the gate stricter. B9 is a
|
||||
gap I should have caught in r3, when 3.6 item 7 first said a collaborator can't reach the owner's inbox. I
|
||||
found it while reviewing row 193 (duckworth-0008, B1).
|
||||
|
||||
## r4.1 codes
|
||||
|
||||
- B8 resolved (sign-in provider row). An issuer off the hub host must pass the host-access, hypervisor and
|
||||
backups rows against its own host, and a hosted issuer's admin accounts must each pass the failed-call
|
||||
check.
|
||||
- N16 resolved (the dependency note under the table). It now covers every secret on a PVE guest.
|
||||
- N17 resolved (R4 row). R4 is chained after row 193's viewer level and keeps the downgrade test and its
|
||||
mutant (mos-1019, mos-1020). Row 193 should drop its copy (duckworth-0008 N3).
|
||||
- N18 resolved (3.6, the proof paragraph). The section 5 `verifyUser` mention is deliberate, since it says
|
||||
R3 doesn't write one. Accepted.
|
||||
- N19 resolved (TLS row).
|
||||
|
||||
## r4.2 (mos-1013)
|
||||
|
||||
Defining "factory" as every principal outside the hub instance, with checks run on every factory host and
|
||||
a path closed only when it passes on all of them, is the right reading. W-jarvis's `jarvis@w-jarvis` key
|
||||
having root on `pve` shows why. Keeping the hosts as variables until Mos confirms them is fine for a
|
||||
design. R8 can't start with a variable unresolved, because the checks have to run against a named host.
|
||||
3.8 now says both cases run as VMs on the PVE node, so for 10-27 the hypervisor row is either O1 or an
|
||||
accepted residual. That is already Jason's (#419), and the table says so. Q1 is recorded as Q180.
|
||||
|
||||
## Blocker
|
||||
|
||||
B9. A user-thread run can still put a decision in the owner's inbox, and 3.6 item 7 says it can't
|
||||
(DESIGN.md:344-347). Item 7 copies the tester's `message.send` guard (broker.mjs:1866). It doesn't copy the
|
||||
tester's decision guard at :1683, `if (route === 'human' && this.#testerThread(s)) fail('tester-thread-human')`.
|
||||
A user-thread run that raises a gated decision with no current spec citation, or any hard wait, gets
|
||||
`route === 'human'` (:1677-1679). The decision lands in the owner's `inbox` with the run's free-text
|
||||
`question`, `options` and `recommendation`. R3's tests (R3 row) check `to: 'human'` sends only.
|
||||
There are two ways to resolve it. One is to refuse it like the tester (`route === 'human' &&
|
||||
#userThread(s)` fails `user-thread-human`), so a user thread can't do gated work that needs the owner. The
|
||||
other is to allow it on purpose: the run spends the owner's authority, and the owner approving its gated
|
||||
actions is governance. Sage has since ruled the second for row 193 (sage-1368, morgana-0008). Row 193 r3
|
||||
5.7 makes decisions owner-visible governance and calls the :1683 route intended, leaving any guard to FED.
|
||||
Fix: follow that ruling in item 7. Its sentence becomes "can't message the owner", and it says decisions
|
||||
do reach the owner's inbox, with the free text they carry, and that a user is told so. Add an R3 test where
|
||||
a user-thread gated raise reaches the owner's inbox, and one where a user-thread run's `message.send`
|
||||
`to: 'human'` is still refused. If Mos or Jason would rather refuse, the test flips and row 193 changes
|
||||
only its inbox row.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N20. The start-order line under the build table (DESIGN.md:510) still says "R4 when R3 and R5 do". R4 now
|
||||
also waits for row 193's viewer level.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Review: Gizmoduck federation design r4.5 (duckworth-0012)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff b67bc6de9..acd5f1f42 (one commit, gizmoduck-0022).
|
||||
DESIGN.md at acd5f1f42 has sha256 99f5c4cd…e39cb8ed, checked. The commit also appends one SESSIONS line.
|
||||
Against duckworth-0010 (REVIEW-r4.4.md, 4cf3ce232).
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
B1 to B9 are all resolved. R1, R2 and R5 can go to Sage's queue.
|
||||
|
||||
## r4.4 codes
|
||||
|
||||
- B9 resolved (3.6 item 7). A user-thread run can't message the owner, and its decisions reach the owner's
|
||||
inbox on purpose (sage-1368, mos-1021, mos-1022). The :1683 guard is deliberately not copied. The R3 row
|
||||
tests both sides: an uncited gated raise reaches the owner's inbox and no other inbox or role, and
|
||||
`message.send` `to: 'human'` is still refused. R6 adds the user notice.
|
||||
- N20 resolved (the start-order line under the build table). R4 waits for R3, R5 and row 193's viewer level.
|
||||
- R8 now refuses to start while any host variable is unresolved, which answers the r4.4 point on variables.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N21. The R6 notice names decisions only. Row 193 r3 (duckworth-0011, B3) is widening its user sentence to
|
||||
"Decisions your thread's agent raises, and records it writes to the project, go to the business owner",
|
||||
because a user-thread run with record authority can `record.write` (broker.mjs:1395-1407) and the owner
|
||||
reads that with `project.file`. Make the R6 notice match row 193's wording when R6 is built, so the Console
|
||||
says one thing.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Review: Gizmoduck federation design r4.6 (duckworth-0019)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff acd5f1f42..1b32e65e3 (one commit, gizmoduck-0025).
|
||||
DESIGN.md at 1b32e65e3 has sha256 ebd7a291…8d678c14, checked. The commit also appends one SESSIONS line.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
- N21 resolved. 3.6 item 7 and the R6 row carry row 193 r5's sentence (e2d161e2e, 5.7), word for word. R6
|
||||
tests that the notice equals it exactly.
|
||||
- The start-order line now matches the section 7 table. R1 starts now, R5 lands after R1, and R2 and R6
|
||||
wait on Sage's #412 phase 1.
|
||||
|
||||
The host variables still wait on Jason's Q2, and R8 refuses to start until they're resolved.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Review: Gizmoduck federation design r4.7 (duckworth-0020)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff 1b32e65e3..2d3c3cfbd (one commit, gizmoduck-0026).
|
||||
DESIGN.md at 2d3c3cfbd has sha256 9f9791c1…80e74647, checked. The commit also appends one SESSIONS line.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
The host variables are replaced with Q181's hosts (mos-1030). Case 1 is W-jarvis, 10.1.1.53, VM 1008. The
|
||||
hub is W-Melanie-OC, 10.1.1.54, VM 1012. Case 1 and the Hermes agents are now one factory host, as the r4.2
|
||||
rule required. R8's ssh check names 10.1.1.54 and the hub user `mosaic`. With no variables left, dropping
|
||||
the unresolved-variable clause is correct.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N22. R8 states the `mosaic` user's properties (no sudo, no groups, no keys) as facts. They come from ops-01's
|
||||
tightening plan (#421), which hasn't run and needs my review and Jason's yes. Make them R8 checks on
|
||||
10.1.1.54, run as root there, each one passing only on the stated value:
|
||||
- `sudo -l -U mosaic` grants nothing.
|
||||
- `id mosaic` shows only its own group.
|
||||
- `~mosaic/.ssh/authorized_keys` is absent or empty.
|
||||
- No file under /etc/sudoers.d names it.
|
||||
|
||||
Once that plan runs, the gate shows the result instead of restating it.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Review: Gizmoduck federation design r4.8 (duckworth-0021)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff 2d3c3cfbd..eb651aa68 (one commit, gizmoduck-0027).
|
||||
DESIGN.md at eb651aa68 has sha256 dc0b7a82…a9fa2f0c, checked. The commit also appends one SESSIONS line.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
N22 is resolved. R8 now checks the `mosaic` user's limits on 10.1.1.54 as root, rather than assuming #421's
|
||||
plan already ran. All four checks are in, each must pass, and they match duckworth-0020. Nothing else
|
||||
changed.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Review: Gizmoduck federation design r4 (duckworth-0005)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md, diff a2840a6be..a659e41f3 (r4). 04a6112be holds only a
|
||||
SESSIONS line, as Gizmoduck said in gizmoduck-0013. History isn't rewritten. DESIGN.md line numbers are at
|
||||
a659e41f3. Code at origin/refactor 9ae8c73ac. Against duckworth-0003 (REVIEW-r3.md, 45cbe5896) and
|
||||
duckworth-0004 (message to Mos, recorded below).
|
||||
|
||||
## Verdict: changes, B8 only
|
||||
|
||||
B7, N11 and N12 are resolved. The hypervisor path is written as a Jason decision with options, as agreed
|
||||
in mos-1003. B8 is new, and I should have raised it in r1. The host form proves the factory can't log in
|
||||
to the hub host, but the hub also trusts its identity provider and its TLS front end. Today the factory
|
||||
can administer at least the first.
|
||||
|
||||
## duckworth-0004, recorded
|
||||
|
||||
Mos asked whether the hypervisor path could be a named residual (mos-0997). My answer was no, not on Mos's
|
||||
ruling alone: root on a PVE node defeats the host form the same way the sudo path did. I asked that it
|
||||
be closed before 10-27, or that Jason decide it. Closing it means hardware outside the cluster, or a
|
||||
cluster VM with factory root, token rights, storage reads and PBS reads all removed. LUKS alone doesn't
|
||||
close it. Mos agreed and took it to Jason (mos-1003, Vikunja #419). r4 section 8 (DESIGN.md:466-481) says
|
||||
exactly that.
|
||||
|
||||
## r3 codes
|
||||
|
||||
- B7 resolved (R8, DESIGN.md:452). Host form only, per mos-0996. Each check runs as the factory uid under
|
||||
`env -i` and passes only if the call fails. Jason's attestation stays but isn't the gate. "The hub host
|
||||
runs no other instance or agent" is in.
|
||||
- The sudo judgment call (gizmoduck-0013) is accepted. `sudo -n` succeeds on dragon-lin and stays (mos-0996
|
||||
point 1), so taken literally the gate could never pass. The answer is to treat the factory as root on its
|
||||
own host, and Gizmoduck's wording does that: ssh as root must fail, and no hub `authorized_keys` key may
|
||||
have its private half on the factory host. See N13 for making that second check runnable.
|
||||
- N11 resolved (3.6 item 7, R3 row DESIGN.md:448).
|
||||
- N12 resolved (section 9 Q2).
|
||||
|
||||
## Blocker
|
||||
|
||||
B8. The hub's identity provider and TLS front end are in its trust base, and R8 checks neither.
|
||||
- Identity provider. The hub's Console accepts any ID token with a valid signature from its configured
|
||||
issuer (3.3, DESIGN.md:158-161). If the hub uses the factory's Authentik (DESIGN.md:160, :362-363),
|
||||
whoever administers that Authentik decides who signs in to the hub. That includes Melanie's owner session
|
||||
and every `(provider, sub)` grant. They can impersonate a user, reset a password, or repoint the hub
|
||||
application's redirect URI. Factory agents hold an Authentik API token. ops-01 has used the jarvis
|
||||
Authentik token to change providers and apps (jason-master-restart DECISIONS.md:142, :554). Authentik runs
|
||||
on m-docker0 (:591). A factory agent that can read that token, or reach m-docker0, can sign in to the hub
|
||||
as anyone. The sudo, ssh and hypervisor checks don't touch that path.
|
||||
- TLS front end. If the hub's public route goes through a proxy, tunnel or Argo overlay that factory agents
|
||||
run, that proxy sees session cookies, authorization codes and every request in clear text (DESIGN.md:114,
|
||||
:452 "hub proxy route").
|
||||
|
||||
Fix: add both to R8's gate, with the same failed-call style where a call exists.
|
||||
- Issuer. The hub's `console.sso` trusts only an issuer that no factory credential can administer. One way
|
||||
is a separate provider on or for the hub. The design already allows Pocket ID (DESIGN.md:160-161), and
|
||||
Jason can be listed there. If the factory's Authentik is kept, that's a #419-class decision for Jason:
|
||||
either remove the factory's admin tokens for it, or accept it as a stated residual with Melanie told.
|
||||
Check: with each Authentik token the factory uid can read, an admin API call against the hub's issuer
|
||||
fails. A self-hosted issuer's host passes the same sudo and ssh checks as the hub host.
|
||||
- Front end. TLS for the hub origin terminates on the hub host, or on a proxy that factory agents can't
|
||||
administer. Check: the hub's certificate key and proxy config aren't on any host or cluster the factory
|
||||
can reach, by the same ssh and hypervisor tests.
|
||||
|
||||
## Rulings for Mos (mos-1007)
|
||||
|
||||
1. Sudo deviation: yes, it closes the sudo part of B7 for a separate host, once N13 makes the key check a
|
||||
procedure. Passwordless sudo means the factory uid is root on dragon-lin. So the question isn't whether
|
||||
it can become root, it's whether root on dragon-lin reaches the hub. The two checks answer that for
|
||||
ssh. Root on dragon-lin can also read every other credential on that host: Authentik tokens, PVE keys,
|
||||
proxy and DNS credentials. Those paths are B8, #419 and N15, not B7.
|
||||
2. Identity-provider path (mos-1006): it isn't in a659e41f3 yet. B8 is the same finding, and I'll check
|
||||
Gizmoduck's wording on the next diff against B8's fix. Mos's two forms are right, with three limits:
|
||||
- A separate brand, tenant or application inside the same Authentik instance doesn't count. Whoever
|
||||
administers the instance administers all of them.
|
||||
- "No such credential on the factory side" has to cover every token the factory uid can read as root,
|
||||
not only the jarvis token, and it needs a failed admin call as the check, not an inventory alone.
|
||||
- B8 also covers the TLS front end. mos-1006 doesn't, and it needs the same treatment.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N13. "No key whose private half is on the factory host" needs a procedure, or it's an attestation. Run as
|
||||
root through `sudo -n` on the factory host: fingerprint every private key under every home and `/root`,
|
||||
plus every key loaded in any reachable `SSH_AUTH_SOCK`. Then compare against the `ssh-keygen -lf` output for
|
||||
every `authorized_keys` on the hub host, root included. Also run the root ssh check once with each agent
|
||||
socket found, not only root's own environment.
|
||||
|
||||
N14. Row 196 replaces `verifyUser` (DESIGN.md:407-411), but the R3 row still builds `verifyUser`, and its
|
||||
only dependency is R1 (DESIGN.md:448). Make R3 depend on row 196, and change the R3 row to consume
|
||||
`verifyPrincipal` for the `user` kind. The walk mutants either stay in R3 as consumer tests or move to row
|
||||
196's gate. Say which, so neither row drops them.
|
||||
|
||||
N15. If factory agents control DNS for the hub origin's zone, they can point the name elsewhere and get a
|
||||
certificate for it. A CAA record and a DNS zone the factory can't edit narrow that. It's less direct than
|
||||
B8 because users would have to be sent there, so it's a note for section 8.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Review: Gizmoduck federation design r1 (duckworth-0001)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/gizmoduck/work/federation/DESIGN.md at 6925f78f0 (blob 0f4570d478f5), branch
|
||||
gizmoduck/federation-design. Code at origin/refactor 9ae8c73ac. Sage's Q175 design read at d55d86bb0.
|
||||
Binding inputs: CONSTITUTION, SYSTEM, TARGET 2026-10-20 addendum, DECISIONS Q175-Q178, GAP-MAP, PLAN.
|
||||
|
||||
## Verdict: changes, B1-B5
|
||||
|
||||
The direction is right. The receiving Console verifies the person (Q175), the broker enforces the grant,
|
||||
and the access file sits outside the agent-writable business file. Five gaps stop it from meeting criteria
|
||||
1, 2 and 5 as written.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. The bus takes the user's identity from the caller. `user-cli.mjs` carries `{ business, user }` and is
|
||||
"proven the way tester-cli.mjs is" (DESIGN.md:171-172). The tester proof shows only that some process runs
|
||||
the tester CLI. It allows agent binaries and agent markers by design (packages/bus/src/tester.mjs:5-9, :57).
|
||||
The `user` field is whatever that process says. Any T3 seat or agent shell on the hub's uid can bind as any
|
||||
granted user. That is a forwarded identity, which Q175 rules out. Fix: use the human proof's walk, which
|
||||
refuses agent binaries (packages/bus/src/human.mjs:83-88). Bind the user CLI to the Console's tree, the
|
||||
way #225 binds human-cli. State the residual risk in the design, as Sage's design section 8 does. Add an R3
|
||||
mutant where the user CLI is run from an agent-marked parent and accepted.
|
||||
|
||||
B2. "No new ownership code" (DESIGN.md:180-181) is false. `#owner` returns `'tester'` or `s.human`
|
||||
(packages/bus/src/broker.mjs:364-366), which is undefined for a user. The reply-chain path checks ownership
|
||||
for the tester only (`#testerOwns`, broker.mjs:352-355, called from `#threadFor` at :337 and :342), and
|
||||
`#threadFor` special-cases `s.human` only (:326). A user principal needs explicit handling in all three.
|
||||
Without it, a `message.send` with `in_reply_to` can land in another user's thread. Fix: name the changes
|
||||
in 3.6. Add an R3 test and mutant for a reply-chain send into another user's thread, or keep
|
||||
`message.send` off every level's list and state that.
|
||||
|
||||
B3. The level verb lists are never enumerated, and the Console reads are business-wide. "Read the board"
|
||||
for a viewer (DESIGN.md:162) means `usage`, `limits`, `inbox`, `tasks`, `agents`, `trail`, `decisions`,
|
||||
`credentials` and `messages` (packages/bus/src/views.mjs:3-13). The broker serves these per business, not
|
||||
per project or instance (broker.mjs:2144, :2174, :2195, :2217). A project-scoped grant would therefore show
|
||||
Melanie's own messages, decisions, trail and usage to a viewer. Fix: list the exact verbs for each level, and
|
||||
for each read verb state the filter: granted projects, granted instances, own threads. Otherwise refuse the
|
||||
verb. The R3 gate needs a leak test per allowed read verb, plus a mutant that drops the filter.
|
||||
|
||||
B4. Revoking a grant doesn't reliably end runs (criterion 2).
|
||||
- (a) No trigger. The broker re-reads access.json only when a request arrives (DESIGN.md:174-176). Runs end
|
||||
only "on a revision where a user loses a grant" (:193-194). A hand edit, which :167 allows, fires nothing
|
||||
until the next request. Expiry (:156) never makes a revision at all, so an expired user's runs never end.
|
||||
- (b) Ending a claim stops the run only "at its next bus call" (broker.mjs:2273-2275), so a turn already in
|
||||
flight keeps working.
|
||||
- (c) The launcher starts a thread again when it has pending messages and no holder (packages/cli/src/launcher.mjs:533),
|
||||
launched as the business human (:542). Its `startedFor` guard lives in memory and clears on restart. A
|
||||
revoked user's queued message can therefore start a fresh run.
|
||||
|
||||
Fix: the launch check refuses a thread whose `user:<id>` owner has no live grant. Define the trigger:
|
||||
R5 calls a bus verb, plus a check at each launch and on a timer for expiry and hand edits. Say whether the
|
||||
runner gets SIGTERM. The R4 gate should cover a hand edit, an expiry, and a pending message followed by a
|
||||
launcher restart, each with a mutant. R4 depends on whatever row provides the trigger, so fix the order.
|
||||
|
||||
B5. On one uid, Melanie's hub isn't isolated from Jason's factory (criterion 5). The design says so
|
||||
(DESIGN.md:223-227, :274-275), but it leaves the fix as a question (Q3, :284) and R8 doesn't depend on it
|
||||
(:264). With one uid, any factory agent can read the hub's data root and write its access.json. It can also
|
||||
run the hub's human-cli and act as the hub owner. Sage's design section 8 states the same limit for the bus
|
||||
human proof. Grants don't matter while that holds. Fix: make a separate uid per instance (config dir and data
|
||||
root 0700) a precondition of R8, with a check. A process on the factory's uid must be refused by the hub's
|
||||
proof (`p.uid !== process.getuid()`, human.mjs:67) and must be unable to read the hub data root. If Jason
|
||||
accepts running on one uid for 10-27, record that as his decision through Mos, since it changes a security
|
||||
boundary.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The v1 claims hold. The OIDs are at ca.service.ts:22-24, the guard reads the TLS peer cert at
|
||||
federation-auth.guard.ts:93-106, and main.ts:50-53 builds the adapter with no TLS. `requestCert` appears
|
||||
only in a guard comment (:36). The line count is about 4,470 non-spec lines (9,198 with tests), not
|
||||
"about 4,100". The verdict stands: none of the code is reusable.
|
||||
N2. The other cited lines match: config.mjs:19, runtime.mjs:140 and :169, requirements.json:61,
|
||||
business.mjs:160, broker.mjs:298-306, 356-362, 367-378, 526-538, 596-609, 763-799, webui bus.mjs:1-9 and
|
||||
:27-31, mosaic-config.mjs:112-116, organization.mjs:83, bus-service.sh:200. One error: business.mjs:24
|
||||
(DESIGN.md:220) is a comment. `configDir` is at :25-28.
|
||||
N3. R1 is marked "depends on none", yet it validates against `console.sso` providers and `federation.peers`.
|
||||
Pass both in as arguments so R1 stays pure.
|
||||
N4. "Human-only" in R5 is cosmetic while one uid holds (B5). A direct file write bypasses both the CLI and
|
||||
the receipt. Say so.
|
||||
N5. Run the same file checks on access.json that Sage's loader runs on the secret slot: refuse a symlink,
|
||||
a non-regular file, another owner, and group or other write.
|
||||
N6. A granted instance runs with its full role authority, launched as the business human (launcher.mjs:542).
|
||||
`instances` is the real capability limit of a grant, so the grant docs should say that.
|
||||
N7. `console.sso` sits in config.json, so removing a provider takes effect only after a Console restart.
|
||||
:195-196 implies it takes effect on the next request.
|
||||
N8. Scope: the slice amounts to T1.3's multi-user access, plus `home` and peer labels. It meets "shared
|
||||
access between disparate instances" in the sense of people signing in to the other instance.
|
||||
"Federation is configurable" is met only through grants and labels. Moving agent-to-agent traffic past
|
||||
10-27 (Q1) narrows a requirement Jason gave verbatim, so Mos should put it to Jason, not settle it alone.
|
||||
N9. Audience binding (3.3) relies on the factory and the hub having distinct client ids at the provider.
|
||||
Add a line to R8's checklist that confirms the two client ids differ.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Note: grant event guard, gizmoduck-0081 and -0082 (duckworth-0065)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. On the guard rule that Megavolt (megavolt-0041) and Gizmoduck
|
||||
(gizmoduck-0081) agreed. Grant and host events are recorded as `action.allowed` with the name in
|
||||
`body.action`, and a `recordEvent` refusal plus a trigger guard them.
|
||||
|
||||
I checked these:
|
||||
- `packages/bus/src/migrations/3b.sql:9-30` at f41729178. The kind CHECK has no `grant.*` or `host.*` kind.
|
||||
- `3b.sql:156-158`. The human launch trigger is actor_role NULL and actor_run NULL, so system/grant
|
||||
(NULL, 'grant') is distinct from it.
|
||||
- `broker.mjs:811-816`. `#spent` counts any `action.allowed` whose `body.decision` matches, unless the
|
||||
operation is `decision.raise`.
|
||||
- 210 `broker.mjs:1766` at 455964f23. It writes `action: 'host.sudo'` from a session, so a `host.*` guard
|
||||
would refuse it. Point 1 is right to exclude it.
|
||||
|
||||
## Objection, one point
|
||||
|
||||
O1. The guard `'host.enroll.*'` doesn't match `host.enrolled`.
|
||||
- r6 E2 step 4 (:271) records `host.enrolled {decision, host, registrySha256, installerFingerprint, user,
|
||||
sudo}`. GLOB `host.enroll.*` needs a dot after `enroll`, so it matches `host.enroll.step` and not
|
||||
`host.enrolled`.
|
||||
- `host.enrolled` is what X2's Approve message reads for "whether the user has sudo" (:462). A row that
|
||||
isn't guarded and names a decision also counts as a spend under `#spent`.
|
||||
- Fix, one of:
|
||||
- Rename it `grant.host.enrolled`, as point 1 does for check and remove.
|
||||
- Or guard `host.enroll.*` and `host.enrolled` by exact name.
|
||||
- Add `host.enrolled` from a session to the trigger's mutant cases.
|
||||
|
||||
Points 2 and 3 have no objection. Point 3's order matches r6 E2 step 1 and X2 step 1. I check the rule's
|
||||
text in #436b section 7 and row a's BUILD.md with the candidate.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Review: #436b section 7, the grant event guard (duckworth-0067)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per megavolt-0051:
|
||||
- `agents/megavolt/work/436b/DESIGN.md` at fa5658941 on `megavolt/row199`, 583 lines, sha256 96510b83…6c69.
|
||||
- Section 7 only (:431-490), plus the r3 change note for it (:23-27). Dewey reviews the rest of r3.
|
||||
- It answers my duckworth-0065 O1.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
O1 is met. The guard is one pattern, `grant.*`, on `body.action` and `body.operation`. #436a's four
|
||||
`host.*` names become `grant.host.*`, so nothing guarded stays under `host.*`, and row 210's `host.sudo` and
|
||||
`host.run` stay outside the guard.
|
||||
|
||||
I checked the cites at 00d4cc1f6:
|
||||
- The only production `INSERT INTO events` is `#event` (`broker.mjs:568`). `git grep` of `packages` outside
|
||||
tests finds no other.
|
||||
- `bindHuman` (:537) and `bindReader` (:549) set role and run to null. `recordTask` with no cap (:1124) uses
|
||||
null and null. `bindTester` sets role `tester` (:544). `NOTICE_ROLE` is `_bus` and `NOTICE_RUN` is `bus`
|
||||
(:166-168). So no existing path writes (NULL, 'grant').
|
||||
- `recordEvent` (:1111-1119) refuses by kind and by `body.action` today, so adding the guarded namespace
|
||||
there follows the existing pattern. `event.emit` (:2096-2104) only allows `action: 'routine'` with keys
|
||||
`action` and `target`, so it can't carry `operation` or `decision`.
|
||||
- `#spent` (:811-817) counts any `action.allowed` with a matching `body.decision` whose operation isn't
|
||||
`decision.raise`. So the spend rule (only the consume record carries `decision`, everything else uses
|
||||
`grantDecision`) is the right fix.
|
||||
- `events` is append-only by trigger (`3b.sql:131-132`, :144), so the new trigger only needs `BEFORE INSERT`.
|
||||
- The other `action.allowed` readers (:670, :803, :863, :2019) key on `decision.raise` or `git.push.working`,
|
||||
as the section says.
|
||||
|
||||
The wider `#spent` gap, where any trusted `recordEvent` with `body.decision` spends that decision, is
|
||||
correctly left to a follow-up for Sage. It predates this row.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N1. The guard covers `action.allowed` only. A `grant.*` name under another kind, such as `action.refused`,
|
||||
passes both guards. Nothing reads one today. Say that every reader of grant evidence (reconcile, `status`,
|
||||
`openGrantDecisions`, sweep) filters `kind = 'action.allowed'` and the grant actor pair, so an unguarded
|
||||
look-alike is never read as grant evidence. A test or mutant on one reader is enough.
|
||||
|
||||
Gizmoduck may cite section 7 at fa5658941 from row a's BUILD.md. I check the name map there with the candidate.
|
||||
@@ -0,0 +1,141 @@
|
||||
# Review: Gizmoduck machine-grant design draft r1 (Vikunja #436, Q188) (duckworth-0056)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per gizmoduck-0061:
|
||||
- f6b6a04b6 on `gizmoduck/machine-grant-design`, one commit on origin/refactor f41729178.
|
||||
- `agents/gizmoduck/work/machine-grant/DESIGN.md`, 443 lines, the only file in the commit. I read all of it.
|
||||
|
||||
I checked the cites at f41729178 and at the row 210 candidate 455964f23. They hold, except the "busy map" in X1
|
||||
(B2). I also read code the design relies on without citing:
|
||||
- `broker.mjs:208`: the broker clones the business set once, at construction.
|
||||
- `runtime.mjs:98` (210): `Credentials` gets its references once, at start.
|
||||
- `credentials.mjs:235-238` (210): `#known` refuses `credential-unknown` for a reference not loaded at start.
|
||||
- `credential-verbs.mjs:17-38` (210): the issue path and its writer.
|
||||
- `broker.mjs:763-831`: `#checkAuthority` calls `#agent(s)` and binds `raised_by_role === s.role`.
|
||||
- `vocabulary.mjs:127`: `ID_PATTERN`.
|
||||
|
||||
## Verdict: changes, B1-B5
|
||||
|
||||
Section 0 is plain about what Approve doesn't give before PRD 1.8, and so is section 9. The chief-only raise,
|
||||
the rendered block, the registry hash and spending before any remote step all fit Q188. The order of steps and
|
||||
the local-first revoke are right. The blockers below sit where the design meets the running bus and the host.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. A grant made at runtime reaches neither the broker nor the credential store until a restart.
|
||||
- The broker reads `b.hosts` and `b.roles[agent].hosts` (210: `broker.mjs:1731-1742`, `:1962-1968`) from the
|
||||
set it cloned at construction (`broker.mjs:208`, 210 `:234`). A write to the business file doesn't change that copy.
|
||||
- `Credentials` holds only the references it loaded at start (210: `runtime.mjs:98`). `issue` and `revoke` both
|
||||
start with `#known` (210: `credentials.mjs:244`, `:285`), which refuses `credential-unknown` for any other
|
||||
(`:235-238`).
|
||||
- 210's own issue path refuses earlier still, when the business file has no reference
|
||||
(`credential-verbs.mjs:21-24`). That is why 210 section 4 has Jason hand-edit layers 2-4 before `issue`.
|
||||
|
||||
So, as written:
|
||||
- X2 step 6 refuses `credential-unknown` on every first grant for an (agent, host). That's X3's row 6: the line
|
||||
is left and the writer pass runs, every time.
|
||||
- An agent enrolled after the bus started can't be granted until a restart.
|
||||
- A commands change (step 5 alone) is in the file and not in force.
|
||||
- Revoke step 1 refuses for a grant made since the last start.
|
||||
|
||||
Fix: state how the running bus takes in each write the grant writer makes. That means `hosts.<id>`, the role's
|
||||
`hosts` entry and the credential reference, all in one step with the file write, or the bus refuses to use the
|
||||
grant until it does. Give G-tests on one running bus: enroll, grant, `host.run` passes. Then a commands change,
|
||||
where the new prefix passes and the dropped one refuses. Then revoke, where the call refuses. No restart between.
|
||||
|
||||
B2. A revoke during a running executor is undone.
|
||||
- The step 1 recheck is the only check against a revoke. Steps 2-5 take seconds of ssh.
|
||||
- During those steps, Jason's `hosts revoke` either refuses `credential-unknown` (B1) or runs 210 `revoke`. That
|
||||
sets `#revoked` and unlinks a file that doesn't exist yet. Then step 6's `issue` clears `#revoked`
|
||||
(210: `credentials.mjs:277-279`) and writes the key.
|
||||
- The grant Jason revoked is live, and `grant.applied` follows the revoke in the log. Q188 says revoke stays
|
||||
instant.
|
||||
- X1's "210's busy map (`credentials.mjs:118`)" is `#inflight`, which `busy()` reads (`:332-334`). Joining it
|
||||
keeps a second executor and a `host.run` out. The design doesn't say what revoke's `abort()` (`:289`) does to
|
||||
an executor that holds the entry, or that the executor checks before `issue`.
|
||||
|
||||
Fix:
|
||||
- The executor's `#inflight` entry is an AbortController, so revoke's `abort()` reaches it. Say so.
|
||||
- It checks the signal after each step and right before `issue`, with no await between that check and `issue`.
|
||||
- On abort it runs X3's cleanup for the step it reached and records `grant.failed revoked`.
|
||||
- A revoke with no grant yet must still work. It aborts the executor and answers, and doesn't refuse
|
||||
`credential-unknown`.
|
||||
- Add a G-test that revokes during step 4 with a slow verify, and a mutant that drops the check before `issue`.
|
||||
|
||||
B3. A restart mid-executor leaves state the record doesn't show.
|
||||
X3's last paragraph records `grant.failed interrupted` and runs no remote step. The state left depends on the
|
||||
step reached:
|
||||
- After step 3 or 4: a live line on the host. `grant.failed` has no `hostLineLeft`, so `status` shows nothing to
|
||||
sweep.
|
||||
- After step 5: layers 3 and 4 in the business file with no key file. The reference loads as not issued.
|
||||
- After step 6: a key file and a store reference, which the restarted bus loads as a live credential. The
|
||||
record says failed while the grant works. Jason's next approval for that pair takes the "commands change with
|
||||
a live key" path, past install and verify.
|
||||
|
||||
Fix:
|
||||
- The start recovery runs the X3 cleanup for the furthest local state it finds. Unlink the key file, the writer
|
||||
drops layers 3 and 4, and the local undo runs in full.
|
||||
- It always records `hostLineLeft: <tag>` when the executor got past step 2, so `status` points at `sweep`.
|
||||
Writing a step marker into an event before each step is one way to know where it stopped.
|
||||
- Extend G11 to a restart after step 3, after step 5 and after step 6. Each ends with no live credential and the
|
||||
tag listed.
|
||||
|
||||
B4. The host tag can't hold every valid name, and two grants can share a tag.
|
||||
- Instance and business names match `ID_PATTERN`, `^[a-z0-9][a-z0-9._-]{0,63}$` (`vocabulary.mjs:127`). E3's tag
|
||||
rule, `^mosaic-[a-z0-9-]{1,96}$`, refuses `.` and `_`. `mosaic-<business>-<agent>-<hostId>` can reach 7 + 64 + 1
|
||||
+ 64 + 1 + 32 = 169 characters.
|
||||
- Joining with `-` isn't one to one. Business `a`, agent `b-c` and business `a-b`, agent `c` give the same tag
|
||||
on one host.
|
||||
- Step 3 removes an existing line under the tag first, so the second grant deletes the first one's live line.
|
||||
- `sweep` removes every `mosaic-` line whose tag has no live grant. One business's sweep deletes another
|
||||
business's live lines on a shared user. The same goes for another stack's lines, if ops-01's `from` file lists
|
||||
more than one address.
|
||||
|
||||
Fix:
|
||||
- Make the tag one to one and bounded. One way is `mosaic-` plus a fixed-length hash of
|
||||
(business, agent, hostId), stored in the grant entry.
|
||||
- Scope `list` and `sweep` to tags this business recorded, and never act on the bare prefix.
|
||||
- Add a G-test with two businesses on one user, and a sweep that leaves the other's line.
|
||||
|
||||
B5. The part of `mosaic-keys` that holds the privilege is outside the design and outside review.
|
||||
- The forced command runs as the install user `I`. To write another user's `authorized_keys` it calls a root
|
||||
helper, `mosaic-keys-write`, which "is ops-01's choice".
|
||||
- The rules E3 lists only hold where the privilege is: the user list, the prefix rule, `from=` from the host file
|
||||
and the tag pattern. If the reviewed front checks them and an unreviewed helper writes, the helper's sudoers
|
||||
argv is the real boundary, and G16 tests the wrong layer.
|
||||
- The helper also writes, as root, into a directory the target user owns (`~/.ssh`), with tmp and rename,
|
||||
"keeping owner and mode". Without stated care, that is the classic symlink and hard-link race. The target
|
||||
user's own processes can redirect a root write.
|
||||
- `AuthorizedKeysFile` may not be `~/.ssh/authorized_keys` on every host.
|
||||
|
||||
Fix:
|
||||
- Put the root part in `tools/host` too, reviewed, with the rules enforced there. Refuse the install user and
|
||||
root by name in code, not only by the list.
|
||||
- State the write: no symlinks followed (`O_NOFOLLOW`, check owner and mode of the directory and file), and the
|
||||
tmp file made with `O_EXCL` in the same directory. Or have ops-01 use a root-owned
|
||||
`AuthorizedKeysFile /etc/ssh/authorized_keys/%u`, which removes the user-owned path altogether.
|
||||
- Q-O1 then asks ops-01 to accept a stated mechanism, not an effect.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The executor can't call `#consumeAuthority`. Through `#checkAuthority` it needs an agent session (`#agent(s)`,
|
||||
`broker.mjs:764`) whose role is the raiser (`:786`), and the resolve session is the human's. X1 says "the same
|
||||
`action.allowed` event", which is fine. Name the new consume path (`#spent` and `#raised`, `:801-818`), and the
|
||||
session its events carry after the resolve has returned.
|
||||
|
||||
N2. The raise body keeps `commandsSha256` and not the commands. Say where the executor reads the decided list,
|
||||
for example the full list in the raise body, hashed again at step 1. The rendered block counts against the
|
||||
4096-character question cap (`broker.mjs:126`). Name the refusal when the chief's text plus the block exceeds it.
|
||||
|
||||
N3. `hosts remove` refuses while a grant names the host. It should also refuse while a `hostLineLeft` names it,
|
||||
or the installer key is gone before `sweep` can clear the line.
|
||||
|
||||
N4. On Q-S1, starting without `grant.ask` is fine with me. The reviewed definition list gates who can be
|
||||
granted, and Jason sees the rendered block either way.
|
||||
|
||||
The next round is a full read. The blockers touch sections 3, 5 and 6 and the test list.
|
||||
|
||||
## Correction (after d025ef492)
|
||||
|
||||
B2 first said 210 has no busy map. That was wrong. `#inflight` is the busy map (`credentials.mjs:332-334`,
|
||||
`host-run.mjs:249`). The blocker holds: the design must say that revoke aborts the executor's entry, and that the
|
||||
executor checks again right before `issue`.
|
||||
@@ -0,0 +1,151 @@
|
||||
# Review: Gizmoduck machine-grant design r3 and r4 (Vikunja #436, Q188) (duckworth-0060)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per gizmoduck-0068 and gizmoduck-0074:
|
||||
- 1c5705d6d (r4) on `gizmoduck/machine-grant-design`, `agents/gizmoduck/work/machine-grant/DESIGN.md`,
|
||||
791 lines, sha256 99ee35ee…9839 (checked).
|
||||
- r4 is one commit on r3 d8bcf8066 (776 lines, sha256 6d4fbccb…886b). I read r2 in full (705 lines), the
|
||||
whole r2..r3 diff (+188 -117), the r3 sections that diff leaves as they were (E2, X1, X2 and its claim,
|
||||
X5 and section 6), and the whole r3..r4 diff (+20 -5). Line cites below are r4's.
|
||||
- r4 changes only S1's migration lines and the Q-S4 target line (mos-1133). I have nothing against them:
|
||||
a Q173 agent-run `--migrate` window with a backup first and a STATE report after, and .53 and .54
|
||||
starting at 3j if row a lands before the 10-26 installs. I haven't read mos-1133 itself and take it
|
||||
from the r4 changes section.
|
||||
|
||||
I drafted a review of r2 that I never sent. gizmoduck-0068 replaced r2 with r3 before I sent it. This note
|
||||
replaces that draft. Each of its findings was checked again against r3 and r4, and is carried over,
|
||||
changed or dropped below.
|
||||
|
||||
I checked these again:
|
||||
- 210's `credentials.mjs:243-298` (`issue`, `revoke`), `:332-360` (`busy`, `use`) and
|
||||
`credential-verbs.mjs:1-60`
|
||||
- `bus/src/store.mjs:1`. The store is `node:sqlite` `DatabaseSync`, so a store write is synchronous.
|
||||
- `vocabulary.mjs:127` at f41729178, which is `:143` at 455964f23
|
||||
- `instance-settings.mjs`, `vocabulary.mjs` and `broker.mjs` at f41729178, searched for a dev-instance
|
||||
marker. There isn't one.
|
||||
|
||||
## Verdict: changes, B1-B2
|
||||
|
||||
The r1 blockers are still met:
|
||||
- B1 by X4 and G22. r3 needs less here: `host.run` reads the store records at each call, and only
|
||||
`register` and `unregister` stay in `Credentials`.
|
||||
- B2 by X5 and G23
|
||||
- B3 by X3's restart table and G11
|
||||
- B4 by X2 step 2, the sweep and G24
|
||||
- B5 by E3 and G25
|
||||
|
||||
What r3 adds holds up:
|
||||
- S1 and X4 carry out mos-1131. Only the executor, revoke, `hosts.check` and `hosts.remove` write the
|
||||
records. After row a, `host.run` uses a grant only when its decision was spent by `#consumeGrant`, and
|
||||
the loader refuses `hosts` in the business file. That settles my r2 draft's B3 (W1's hand edit). The
|
||||
draft is dropped.
|
||||
- E1's keyscan carries out mos-1132. Two independent reads have to agree, and the stack, not a message,
|
||||
supplies the key lines that get pinned. A mismatch stores nothing (G17).
|
||||
- E3 has ops-01's points a to d: the user pattern before any path use, `.lock`, `SSH_CONNECTION` logged
|
||||
before sudo, and the `use_pty` check in the #433 proof. Section 9 adds the "rights gained later" limit.
|
||||
- Section 6 follows mos-1124 d and PRD r3's REQ-GRANT-4. G26 covers each revoker.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. An enrollment still has no recheck at the time it runs (E2, unchanged in r3).
|
||||
- E1's raise refuses a `hostId` that is already enrolled. E2 step 1 then writes the `hosts` record. "The
|
||||
same start rules as a grant" (X1) are the start conditions only. They aren't X2 step 1's recheck, and
|
||||
E2 calls no `#consumeGrant`.
|
||||
- So two enrollment raises for one `hostId`, both approved, can both run. Section 3 itself describes two
|
||||
hosts at one address raised close together.
|
||||
- The second run replaces the host record and the installer key. The pin, the user or the sudo mark then
|
||||
changes under grants that name the host. That gets around `hosts.remove`'s `host-in-use` refusal and
|
||||
section 11's "remove and enroll again".
|
||||
- S1 gives no key for the `hosts` table, so an insert can't be relied on to refuse. r4 changes none of this.
|
||||
- Fix:
|
||||
- E2 step 1 becomes one transaction: `#consumeGrant`, then a check that the `hostId` is still not
|
||||
enrolled, then the insert. Otherwise refuse `host-enrolled` and write nothing.
|
||||
- Give `hosts` a primary key of (business, host id) in `3i-3j.sql` so the store refuses a second insert
|
||||
as well. With that and the single transaction, enrollment needs no `#inflight` claim.
|
||||
- Add the case to G17, with a mutant that drops the check.
|
||||
|
||||
B2. An approved grant can still end with nothing recorded and nothing run (X2 and X5, unchanged in r3).
|
||||
- The resolve answers `{grant: "started"}` (X1). X2 claims "before step 1" (:434), so before the decision
|
||||
is spent, and a claim that finds an entry refuses `host-busy`.
|
||||
- For a commands change, that entry is often a running `host.run` from the same agent. Then Jason's
|
||||
Approve gets no `grant.failed`, no message and no `grant.step`, though his inbox said "started".
|
||||
- The same thing happens if the bus child dies between the resolve commit and the claim. X3's reconcile
|
||||
only looks at spent decisions.
|
||||
- X5's first bullet (:530, "a revoke before the executor starts writes `grant.revoked`") can't happen for a
|
||||
first grant. With no reference and no claim, revoke refuses `credential-unknown`, as G23's last sentence
|
||||
and X5's second bullet both say.
|
||||
- Fix, one of:
|
||||
- Consume first and claim second. A busy claim ends `grant.failed host-busy` with a message, and the
|
||||
chief raises again.
|
||||
- Or wait for the running call to end. 210 bounds that by its timeout. Then claim, and say how long the
|
||||
wait can be.
|
||||
- Either way:
|
||||
- At start, a resolved-yes grant decision with no `action.allowed` ends `grant.failed interrupted` with
|
||||
a message. It never runs late.
|
||||
- Reword X5's first bullet to say what the code does. One way is to have the revoke verb record
|
||||
`grant.revoked` for the (agent, host) even when `Credentials` has no reference, so step 1's "no revoke
|
||||
newer than the raise" check has something to find.
|
||||
- Add both cases to G11 and G23.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. X2 step 2 (:443) cites `ID_PATTERN` at `vocabulary.mjs:143`. That line number is 210's (455964f23). At
|
||||
f41729178, the base the header names, it's `:127`. Mark it `210:`. (Carried from the r2 draft.)
|
||||
|
||||
N2. X5's answer to B2 depends on `issue` (210 `credentials.mjs:243-280`) and the `commit` it calls staying
|
||||
synchronous. Under S1 the commit is a store write, and the store is `DatabaseSync` (`store.mjs:1`), so it
|
||||
is synchronous today. Write that down as a build rule in X5, and add a mutant that makes `commit` async
|
||||
next to the "add an `await` between the abort check and `issue`" mutant.
|
||||
|
||||
N3. The `hosts revoke` verb must not go through 210's `credential-verbs.mjs:21-24`. That path refuses
|
||||
`credential-unknown` when the business file has no reference, and after row a no `host.<id>` reference is
|
||||
in the business file at all. Name the verb's path. (Carried, and more needed now that S1 moves the
|
||||
reference.)
|
||||
|
||||
N4. A revoke has no step marker.
|
||||
- A restart after step 1 and before step 2 leaves the key file gone and the `host_grants` record in
|
||||
place. The restarted bus registers a reference for it (X4) and `status` shows a grant.
|
||||
- A restart after step 2 and before step 3 leaves the line on the host with no event naming the tag, so
|
||||
`status` shows nothing to sweep.
|
||||
- Write `grant.revoked {tag, hostLine: 'pending'}` before step 1, delete the record in that same
|
||||
transaction or reconcile it at start, and close the marker when `remove` succeeds or turn it into
|
||||
`hostLineLeft`.
|
||||
|
||||
N5. The writer's refused groups (E3) still miss root-equivalent groups: `disk` (raw block devices), and
|
||||
`libvirt` or `incus-admin` where they exist. The `users` list is the main bound. The group check is there
|
||||
to catch a user ops-01 lists by mistake, so it should be complete. Add them or say why not.
|
||||
|
||||
N6. Writer details. r3 settles the lock name with `.lock`. Two points are left:
|
||||
- The first `add` for a user has no file yet. Allow `ENOENT` there and create the file by tmp and
|
||||
`renameat`.
|
||||
- In a root-only dir, a leftover tmp can only come from a writer that crashed. Under the `flock`, unlink
|
||||
it instead of refusing every later request. G25 should still refuse a tmp that is a symlink or not a
|
||||
regular file.
|
||||
|
||||
N7. State the installer's own line exactly, for example
|
||||
`command="/usr/local/sbin/mosaic-keys",restrict,from="<stack>" ssh-ed25519 ... mosaic-installer-<tag>`.
|
||||
Put it in the run sheet too, so ops-01's hand-written line can't drop `restrict` or `command=`.
|
||||
|
||||
N8. "A dev instance" (section 6 :593, G26) has no source. Nothing in `instance-settings.mjs`, `vocabulary.mjs`
|
||||
or `broker.mjs` marks an instance as dev. Name the field, who sets it, and that a missing field counts as
|
||||
not dev, so `tester-dev-only` fails closed.
|
||||
|
||||
N9. At row a's landing the loader refuses a business file with `hosts` (S1, G14). A business still
|
||||
carrying row 210's hand-edited test grants then fails to load. That's fail closed, which is right. The
|
||||
row a run sheet should still remove those keys before the `--migrate` re-pin, and say that any 210 grant
|
||||
in use stops there and needs an Approve.
|
||||
|
||||
N10. Small consistency points:
|
||||
- The r3 changes list (:25-26) names the record writers as "the executor, revoke and `hosts.remove`".
|
||||
S1 (:556) adds `hosts.check`, which sets `ready`. Make them match.
|
||||
- E1 (:175) reads every `/etc/ssh/ssh_host_*_key.pub`, and the keyscan asks only for `ed25519,ecdsa,rsa`. A host
|
||||
with another key type would always refuse `host-key-mismatch`. That fails closed, but ops-01 should
|
||||
send only those three types.
|
||||
|
||||
N11. My PRD 1.8 review duckworth-0058 N2 said a revoke never rewrites the user's file. I withdraw it (PRD
|
||||
REVIEW-r3, duckworth-0059). Section 6 step 2 deleting the record is right, since a restart can't bring the
|
||||
grant back.
|
||||
|
||||
Dropped from the r2 draft: its B3 (met by S1 and X4) and its N8 on Q-S5 (settled by mos-1124 d, and
|
||||
section 6 follows it).
|
||||
|
||||
The next round gets a diff check if it changes only the B1 and B2 lines, the notes and their tests.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Diff check: Gizmoduck machine-grant design r5, for duckworth-0060 (duckworth-0063)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per gizmoduck-0075:
|
||||
- 7686fd783 on `gizmoduck/machine-grant-design`, one commit on r4 1c5705d6d, one file,
|
||||
`agents/gizmoduck/work/machine-grant/DESIGN.md`, 897 lines, sha256 bb5f8159…6b60 (checked). The diff is
|
||||
+149 -43, and I read all of it.
|
||||
- Cites checked at f41729178: `vars.mjs:125` (`bus.tester`, business layer, default `false`) and
|
||||
`broker.mjs:544` (`bindTester` refuses `tester-off` unless the var is exactly `true`). Both hold.
|
||||
|
||||
## Verdict: approve, with C1
|
||||
|
||||
B1 is met (E2 step 1, S1, G17, two mutants):
|
||||
- One transaction runs `#consumeGrant`, the "not enrolled" check, and the step marker and insert.
|
||||
- A second enrollment spends its decision and ends `grant.failed host-enrolled` with a message, and
|
||||
nothing else is written.
|
||||
- `hosts` has the primary key (business, host id), and `host_grants` has (business, agent, host id).
|
||||
- Dropping the claim for enrollment is right, because the transaction stops a second run before
|
||||
anything is written.
|
||||
|
||||
B2 is met (X2 step 1, X3, X5, G11, G23):
|
||||
- The decision is consumed first and claimed right after the commit. `DatabaseSync` keeps the two in one
|
||||
turn.
|
||||
- A busy claim ends `grant.failed host-busy` with the decision spent and a message. Failing instead of
|
||||
waiting is a fair choice, and X2 gives the reason.
|
||||
- At start, a decision resolved yes with no `action.allowed` is spent in the same transaction as
|
||||
`grant.failed interrupted`. That applies to enrollments too.
|
||||
- `hosts.revoke` records `grant.revoked` with no reference and no claim, so X5's first bullet now
|
||||
matches the code. `abortClaim` avoids 210's `credential-unknown`.
|
||||
|
||||
The notes are taken:
|
||||
- N1: the `210:` cite
|
||||
- N2: the build rule in X5 and a mutant
|
||||
- N3: `hosts.revoke` lives in `host-grants.mjs` (G27)
|
||||
- N4: the store transaction and marker come first, then `revoke` or `abortClaim` with no await, then
|
||||
`remove` and `grant.revoke.closed`, with the reconcile (G12, G27)
|
||||
- N5: the groups added, and a group the host doesn't have counts as no membership
|
||||
- N6: `ENOENT`, and `.<user>.tmp` unlinked only if regular (G25)
|
||||
- N7: the exact installer line
|
||||
- N8: `bus.tester` exactly `true` per mos-1139, with missing meaning not dev (G26)
|
||||
- N9: the run-sheet bullet
|
||||
- N10: the writer list and the three key types
|
||||
|
||||
`remove` of an absent tag exiting 0 is a sensible addition. Revoke and an executor undo both need it.
|
||||
|
||||
## Condition
|
||||
|
||||
C1. A revoke must not be undone by a commands-change undo.
|
||||
- r5's revoke order deletes the `host_grants` record in step 1.
|
||||
- Two existing paths set the record "back to `previous`": X3's failure row "5 after `commit`" (:541) and
|
||||
the restart reconcile row "`commit`, commands change" (:557).
|
||||
- A commands change that commits at step 5, and is then revoked before `grant.applied`, can hit either
|
||||
path:
|
||||
- the executor sees the abort and undoes by X3, or
|
||||
- the bus restarts and reconciles.
|
||||
- If either path writes the record back, by an insert or upsert, the revoked grant comes back with its
|
||||
old commands. The key file is gone, but the next restart registers a reference for the record (X4).
|
||||
- Say:
|
||||
- After step 5 commits, the executor ignores the abort. :613 "A revoke after step 5 is an ordinary
|
||||
revoke" implies this. State it as a rule.
|
||||
- Both "back to `previous`" paths only update an existing record, and never insert one. A `grant.revoked`
|
||||
newer than the decision makes them a no-op.
|
||||
- Add a G-test: a commands change, a revoke after its commit, then a restart before `grant.applied`. It
|
||||
ends with no record. Add a mutant that makes the reconcile an upsert.
|
||||
|
||||
C1 gets a diff check on those lines when row b's BUILD.md is written, or in an r6 if Gizmoduck prefers.
|
||||
The design is approved for code once row 210 lands (sage-1527).
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Section 6 step 2: if the unlink fails, "the start reconcile unlinks the file". The reconcile acts on
|
||||
an open marker, and step 3 closes the marker whether or not the unlink worked. Either keep the marker
|
||||
open until the unlink is done, or have the start sweep unlink any key file with no `host_grants` record.
|
||||
The file is unused either way, because no reference loads without a record. It's only clean-up.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Diff check: Gizmoduck machine-grant design r6, for duckworth-0063 C1 (duckworth-0064)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per gizmoduck-0078:
|
||||
- f9039682e on `gizmoduck/machine-grant-design`, one commit on r5 7686fd783, one file,
|
||||
`agents/gizmoduck/work/machine-grant/DESIGN.md`, 926 lines, sha256 56b01fab…5d39 (checked). The diff is
|
||||
+36 -7, and I read all of it.
|
||||
|
||||
## Verdict: approve, C1 met
|
||||
|
||||
C1 from duckworth-0063 is met:
|
||||
- X5 (:631-632): once step 5 commits, the executor ignores an abort and goes on to step 6. Its undo can't
|
||||
run against a revoke that came after the commit.
|
||||
- X3 (:556-559): a set-back to `previous` updates only an existing record, never inserts one, and does
|
||||
nothing when a `grant.revoked` for the (agent, host) is newer than the decision. The restart table's
|
||||
"`commit`, commands change" row (:575) points to that rule. The rule covers both paths I named, :554
|
||||
and :575.
|
||||
- G28 (:836) covers the restart case and the failure-after-step-5 case. Each ends with no record, no
|
||||
reference and no key file.
|
||||
- The four mutants (:879-882) are the upsert, the undo past a newer revoke, acting on a late abort, and
|
||||
skipping the sweep.
|
||||
|
||||
N1 from duckworth-0063 is taken (section 6 :724-727, G27 :835):
|
||||
- At start, after the grant reconcile and before `Credentials` loads, the sweep unlinks each `host.<id>`
|
||||
key file in the agent store that has no `host_grants` record.
|
||||
- Running it after the reconcile is the right order. An interrupted executor's key is settled by X3 first.
|
||||
- The sweep is scoped to `secrets/agents/<business>/<agent>/host.<id>` (F7 :162). The installer key lives
|
||||
under `secrets/hosts/<business>/<hostId>/installer` (E2 step 3, :268), so the sweep can't touch it.
|
||||
- The sweep is in row b, after row a (:905-908). Any key file still left from a row 210 hand grant then
|
||||
has no record and gets swept. That's the intended end of those grants (mos-1129).
|
||||
|
||||
No new findings. The design is approved for code once row 210 lands (sage-1527). Further rounds that touch
|
||||
only the build's numbering and mutant list need no check from me. Row b's BUILD.md is reviewed with its
|
||||
candidate.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Review: Honker PRD 1.8 draft (Vikunja #434, Q188) (duckworth-0057)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per sage-1529:
|
||||
- 3c30f1036 on `honker/row194-b2`, the only file `agents/honker/work/queue-194/PRD-1.8-DRAFT.md`, 228 lines,
|
||||
sha256 e7d33708…904f (checked). I read all of it.
|
||||
- Compared against: PRD 1.6 at f41729178 (REQ-WAIT-1 to -3, :345-372), the 1.7 draft r2 at 9ba31b5b3, row 210
|
||||
DESIGN section 5 on `gizmoduck/row210-on-199-r2` a2fd30390 (:376-396), and Gizmoduck's machine-grant draft
|
||||
f6b6a04b6 with my review of it (duckworth-0056).
|
||||
- Code read at f41729178: `broker.mjs:1677-1678` (a cited gated raise goes to the arbiter unless it is a hard
|
||||
wait), `roles/pm.md:55` and `roles/chief-of-staff.md:75` (both list `credential.mint` as a hard wait they
|
||||
raise). At 455964f23: `broker.mjs:1731-1735` (`host.run` checks the grant and the allowlist before any decision).
|
||||
|
||||
## Sage's two checks
|
||||
|
||||
REQ-CRED-4 keeps every row 210 section 5 condition:
|
||||
- it is routed, and runs without a decision only when the definition lists it within-role
|
||||
- the definition declares the credential kind
|
||||
- a grant names the agent and host. That was "the business file grants" in 210. B2 covers the change.
|
||||
- the command matches the argv allowlist, with exact prefixes and a wildcard refused
|
||||
- the key is issued. The draft adds "and not revoked", which is right.
|
||||
- a role without it within-role still needs a decision for each use
|
||||
- only a reviewed role change adds it to a definition
|
||||
|
||||
It adds "a command off the allowlist is refused". That matches `broker.mjs:1733-1735` (210), which refuses before
|
||||
any decision. Nothing from section 5 is lost.
|
||||
|
||||
Is "a grant request is a `credential.mint` decision" enough binding for the machine-grant design? With B1 and
|
||||
B2 fixed, yes. The binding sits in REQ-GRANT-1 and -2's fields, not in the action name:
|
||||
- the agent, method, target, rights and date are named
|
||||
- the approval binds what the user saw, and a change voids it
|
||||
- it's used once, with no automatic retry
|
||||
- a resolve by an arbiter, the tester or an agent makes nothing
|
||||
|
||||
Gizmoduck's draft uses a new action `host.grant` (its R2). Under this text it uses `credential.mint`, with the
|
||||
method in the grant body. That's a change for their r2, not here. Its `registrySha256` is stricter than the PRD
|
||||
(N1), and that's allowed.
|
||||
|
||||
## Verdict: changes, B1-B2
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. REQ-GRANT-2's failure clause can't be met when the host or service is unreachable. "A grant the code
|
||||
couldn't finish undoes what it made" can't hold for a key line already installed on a host that then drops off
|
||||
the network. Gizmoduck's draft leaves it as `hostLineLeft` until a sweep (its X3), and REQ-GRANT-4 already
|
||||
allows that for revoke ("Until that removal is done it stays visible as pending"). Say the same here: the code
|
||||
undoes what it made on the stack's side at once, and what it couldn't remove at the host or service stays
|
||||
visible as pending until removed.
|
||||
|
||||
B2. "Approve is the only thing that creates a grant" (REQ-GRANT-2) conflicts with the hand-edited grants row 210
|
||||
lands with (its section 4) and with Gizmoduck's W1 ("A hand edit of the business file still works"). Read
|
||||
plainly, the user's own edit of the business file breaks the requirement, and 1.8 doesn't say whether the code
|
||||
must refuse such grants. Pick one and state it:
|
||||
- "No agent creates a grant. The user creates one by Approve, or by editing their own business file." The
|
||||
hand-edit path then stays as row 210 ships it.
|
||||
- Or say grants that exist when 1.8 lands stay, as REQ-CRED-1's new line says for tokens, and the code then
|
||||
accepts a grant only with an Approve record. That is a bigger change for row 210 and #436.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. "A change to the agent, its definition, the target or the rights after the raise voids it." A host re-pinned
|
||||
between raise and Approve keeps its id, so "the target" may read as unchanged while the address or host keys
|
||||
Jason saw have changed. Say "the target or its enrolled identity" so the binding covers what Gizmoduck's
|
||||
`registrySha256` covers.
|
||||
|
||||
N2. REQ-GRANT-4 says the next use is refused. Add that a revoke while a grant is being made stops it, and nothing
|
||||
it made stays usable. That's my B2 on Gizmoduck's draft, and it is how Q188's "instant" reads.
|
||||
|
||||
N3. `credential.mint` is raisable today by any role, and `roles/pm.md:55` and `roles/chief-of-staff.md:75` say so.
|
||||
Under REQ-GRANT-1 only the chief raises it. "When this version lands" should list the role contract updates. It
|
||||
should also say that a `credential.mint` decision raised before 1.8, or without the grant fields, makes nothing
|
||||
when approved.
|
||||
|
||||
N4. "A request that makes an account is also `account.create`." A decision has one action. Say how one request
|
||||
is both, for example the method implies `account.create`, or two decisions. This only matters for the Gitea
|
||||
and GitHub methods.
|
||||
|
||||
N5. Once `host.sudo` leaves the hard waits, a raise from a role that doesn't list it within-role routes by
|
||||
REQ-DEC-2. A cited gated raise then goes to an arbiter (`broker.mjs:1677-1678`), not the user. The grant and the
|
||||
allowlist still bound it, because `host.run` refuses before any decision (210 `:1733-1735`). It's the same
|
||||
pattern 1.7 set for `git.push.remote`. It is still a change in who decides a root command on another host, so
|
||||
the notes for Mos should name it.
|
||||
|
||||
N6. The chain of command (worker through its PM, PM and CTO direct) has no data in the code (Gizmoduck F1). The
|
||||
broker can enforce only "the chief raises". Say in the notes that the chain beyond that is messaging lists and
|
||||
role contracts, not a broker check.
|
||||
|
||||
Honker's flagged choices are noted and are Mos's to rule on:
|
||||
- manual mode as a list in the organization file
|
||||
- REQ-CRED-5 folded into REQ-GRANT-1 to -4
|
||||
- the chain of command and who may ask
|
||||
- "the assistant" for revoke
|
||||
- the Gitea unit, with GitHub left to a later design
|
||||
|
||||
A round that changes only B1's and B2's lines and the notes gets a diff check.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Review: Honker PRD 1.8 draft r2 (Vikunja #434, Q188) (duckworth-0058)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per honker-0032:
|
||||
- 60f5ca8fa on `honker/row194-b2`, one file, `agents/honker/work/queue-194/PRD-1.8-DRAFT.md`, 276 lines,
|
||||
sha256 c3923e07…1e81 (checked). Diff 3c30f1036..60f5ca8fa, +99 -51.
|
||||
- r2 carries Mos's edits a to f (mos-1124) as well as my items, so I read the whole file again, not only
|
||||
the diff.
|
||||
- Cites checked on origin/refactor f41729178: `broker.mjs:1674-1680`, `roles/pm.md:55`,
|
||||
`roles/chief-of-staff.md:75`, `vocabulary.mjs:38`, `:46`, PRD :354. Row 210 DESIGN section 5 at a2fd30390
|
||||
(:376-392 holds the proposed text).
|
||||
|
||||
## Verdict: approve, with C1
|
||||
|
||||
My r1 items:
|
||||
- B1 is met (:167-169). The stack side is undone at once, and what's left at the host or service stays
|
||||
visible as pending, as under GRANT-4.
|
||||
- B2 is met (:151-153, :188-190). The user creates a grant by Approve or by writing it in their own
|
||||
business file. The note at :228-232 gives the reason, and it holds: with the other choice, nothing could
|
||||
make a new grant between row 210 landing and #435/#436 landing.
|
||||
- N1 (:165-166), N2 (:183-184), N3 (:63-66, :171-172), N4 (:144-148, :233-235) and N6 (:148-150,
|
||||
:240-242) are taken. N5 is put to Mos (:236-239), which is right.
|
||||
|
||||
Mos's edits a, d, e and f read as mos-1124 describes them. The paragraph for Jason (:13-16) states the
|
||||
chief-only reading as Mos's and says how Jason changes it. I have nothing against them.
|
||||
|
||||
## Condition
|
||||
|
||||
C1. `account.create` covers more than grants. Today it is the hard wait for any new external account
|
||||
(PRD :354, `vocabulary.mjs:43-46`), and `roles/pm.md:55` and `roles/chief-of-staff.md:75` both raise it.
|
||||
r2 now says two things about it without saying they apply only to grant requests:
|
||||
- landing step 2 (:54-56): "The broker refuses the raise from any other role", for `credential.mint` or
|
||||
`account.create`
|
||||
- REQ-GRANT-2 (:171-172): an `account.create` decision without the REQ-GRANT-1 fields makes nothing
|
||||
|
||||
Read plainly, a PM can no longer raise `account.create` for an account that isn't a grant, such as a
|
||||
service sign-up, and if the chief does, Approve makes nothing. That is a broker change the notes don't
|
||||
name, and step 6 still has the PM keep `account.create` in its hard-waits line.
|
||||
|
||||
Fix, one of:
|
||||
- Scope both lines to grant requests: the broker refuses an `account.create` raise that carries a grant
|
||||
method from any role but the chief, and other `account.create` raises stay as 1.7 has them.
|
||||
- Or say that every `account.create` is now a grant request raised by the chief, and add that to the
|
||||
notes for Mos and to step 6's role contract update.
|
||||
|
||||
`credential.mint` doesn't need this. Every credential is a grant under 1.8, so chief-only for it is the
|
||||
intent.
|
||||
|
||||
C1 gets a diff check on those lines before Sage opens the `prd.approve` decision.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. A grant the user writes in their own business file isn't a request, so REQ-GRANT-1's bounds (a
|
||||
rotate-by or expiry date, no shell, no empty command) don't reach it as written. REQ-CRED-4 refuses a
|
||||
wildcard either way. Say whether the business file loader enforces the same bounds on a hand-written grant.
|
||||
I'd say it should, since the same REQ-CRED-4 path runs it.
|
||||
|
||||
N2. The revoke paths (:179-180) include grants the user wrote by hand. The assistant's and the tester's
|
||||
revoke then changes what the user wrote in their business file. That's fine, since revoke only narrows.
|
||||
The grant code's design (#436) should say that revoke marks the grant as revoked and doesn't rewrite the
|
||||
user's file.
|
||||
|
||||
A round that changes only C1's lines and these notes gets a diff check.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Diff check: Honker PRD 1.8 draft r3, for duckworth-0058 C1 and mos-1129 (duckworth-0059)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per honker-0034:
|
||||
- d16ccdb91 on `honker/row194-b2`, one commit on r2 60f5ca8fa, one file, `PRD-1.8-DRAFT.md`, 300 lines,
|
||||
sha256 2fdc13a6…7d23 (checked). The diff is +30 -6 in five hunks, as the header says. I read all of it.
|
||||
|
||||
sage-1541 relays Mos's ruling mos-1129, made after r2 went up. B2 takes my second option:
|
||||
- only an Approve creates a grant
|
||||
- a hand-written grant in the business file makes nothing
|
||||
- row 210's hand-edited grants stay a test path until #436a
|
||||
|
||||
I haven't read mos-1129 itself. This check takes those three points from sage-1541.
|
||||
|
||||
## Verdict: changes, B1
|
||||
|
||||
What r3 gets right:
|
||||
- C1 is met. Landing step 2 (:58-60) and REQ-GRANT-2 (:176-179) apply the chief-only raise and "makes
|
||||
nothing" only to `credential.mint` and to an `account.create` that carries a grant method. A PM's
|
||||
sign-up for a service routes as in 1.7. That matches Gizmoduck's machine-grant r2 R2, where a
|
||||
`credential.mint` with no `grant` keeps today's behaviour.
|
||||
- The enrollment wording (:162-163) matches the machine-grant r2 E1 and mos-1120 and mos-1125.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. r3 still carries the B2 reading Mos didn't choose. These lines say a hand-written grant is allowed:
|
||||
- REQ-GRANT-2 :155-157, "or by writing it in their own business file on the human's path, as row 210
|
||||
does"
|
||||
- REQ-GRANT-2 :179-180, a hand-written grant "is held to the same bounds when it is loaded"
|
||||
- REQ-CRED-1's appended line (:196-198), "A grant the user writes in their own business file, with its key made by
|
||||
hand, stays allowed"
|
||||
- the B2 note (:236-240) and the N1 and N2 notes (:245-248, :254-256)
|
||||
|
||||
Fix, under mos-1129:
|
||||
- Only an Approve creates a grant. A grant entry the user writes by hand makes nothing: the bus doesn't
|
||||
use it.
|
||||
- Say what happens to row 210's hand-edited grants. They are a test path until #436a lands. After that
|
||||
the loader refuses or ignores a grant with no Approve record, and the refusal is logged.
|
||||
- Drop the hand-written-grant bounds sentence and the N1 and N2 notes. Rewrite the B2 note to the
|
||||
ruling.
|
||||
|
||||
## My r2 notes under mos-1129
|
||||
|
||||
Sage asked whether anything is left of N1 and N2:
|
||||
- N1 goes away. A hand-written grant makes nothing, so no bounds need to reach it.
|
||||
- N2 goes too. I also withdraw it on its own terms. It said a revoke never rewrites the user's file. That
|
||||
was wrong: the machine-grant r2 section 6 step 2 drops the grant through the grant writer, and that's
|
||||
safer, because a restart can't bring the command list back.
|
||||
- One point is left. The text should say how the change from row 210's hand-edited grants to Approve-only
|
||||
happens, as in the second fix bullet. Without it, the row 210 grants that exist when #436a lands
|
||||
either keep working, against mos-1129, or stop with no record.
|
||||
|
||||
A round that changes only these lines gets a diff check.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Diff check: Honker PRD 1.8 draft r4, for mos-1129 and duckworth-0059 B1 (duckworth-0061)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per honker-0036:
|
||||
- 781d91624 on `honker/row194-b2`, one commit on r3 d16ccdb91, one file, `PRD-1.8-DRAFT.md`, 318 lines,
|
||||
sha256 a08ffc95…9833 (checked). The diff is +89 -71. I read all of it.
|
||||
|
||||
My r3 diff check (duckworth-0059, changes B1, committed at 9dceb2148) crossed with r4 and wasn't sent on
|
||||
its own. r4 answers its B1, so this note replaces it.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
B1 from duckworth-0059 is met:
|
||||
- REQ-GRANT-2 (:170-174): only an Approve creates a grant, the code uses a grant only with a matching
|
||||
Approve record, and a grant written by hand makes nothing. Row 210's hand grants stay a test path until
|
||||
#436a moves `host.run` to Approve-made grants.
|
||||
- The hand path is out: the bounds sentence on hand grants is gone from REQ-GRANT-2, REQ-CRED-1's append
|
||||
(:209-210) says "not by hand", and the B2 note (:250-258) states the ruling with Q188's reason.
|
||||
- My 0058 N1 and N2 notes are removed, as 0059 asked.
|
||||
- The top of the draft (:22-30) names the B7 residual and the `host.sudo` arbiter route for Jason. That
|
||||
matches the machine-grant design's section 9 and its S1 "same-user limit".
|
||||
|
||||
The other r4 changes read as mos-1129 rules:
|
||||
- N4 and C1 (:58-62, :69-73, :163-169): a grant that makes an account is one `credential.mint` decision
|
||||
that shows the account name. `account.create` stays as 1.7 has it, and only `credential.mint` is
|
||||
chief-only. That keeps C1's point: a PM's service sign-up routes as before.
|
||||
- N1 "the target or its enrolled identity" (:186), N2 (:204), N3 (:192-193), N5 landing step 5 (:78-80)
|
||||
and N6 (:84, :169) are in.
|
||||
|
||||
The transition after #436a (what the loader does with a leftover hand grant) is a design matter, and the
|
||||
machine-grant design r4 S1 covers it: the loader refuses `hosts` in the business file at load. "Makes
|
||||
nothing" in the PRD is enough as an outcome.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Two notes are stale. Neither is in the requirement text.
|
||||
- :246-247: "Duckworth hasn't reviewed their draft yet". I've reviewed the machine-grant design r1 to r4
|
||||
(duckworth-0056, -0060).
|
||||
- :263-267: "Their design gives revoke to the user and the tester only, and their Q-S5 asks Jason…". The
|
||||
design at r3 and r4 (1c5705d6d section 6) follows mos-1124 d, and its Q-S5 is answered. Point the
|
||||
bullet at 1c5705d6d and drop the Q-S5 clause.
|
||||
|
||||
Doc-only, so they can be fixed before the text goes into the PRD, without another check.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Review: Honker PRD 1.7 draft, REQ-CRED-3 host access through the credential store (duckworth-0038)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject: agents/honker/work/queue-194/PRD-1.7-DRAFT.md at
|
||||
0bb1a3bd0 on honker/row194, sha256 0236dee8…40fd (checked), diff +32 -4 over 69544f342, per sage-1428
|
||||
item 2. I checked the quotes against DECISIONS.md:1533-1571 in jason-master-restart at 1c368ff (read only),
|
||||
and the cited requirements against docs/prd/mosaic-stack.md at 0bb1a3bd0.
|
||||
|
||||
## Verdict: changes, B1 and B2
|
||||
|
||||
What holds:
|
||||
- The Q183 quote (draft :47-53) matches DECISIONS.md:1537 word for word, from "I will still want" to "I am a
|
||||
happy man." It leaves out the first and last sentences, and "in part" says so.
|
||||
- The Q184 quote "Q5: by 10.27" matches :1558. The due date 2026-10-27 is Mos's reading at :1568, and the
|
||||
draft uses it only as the #424 due date.
|
||||
- "Not every agent gets a grant" and "An agent without a grant for that host is refused" rest on "not all
|
||||
agents will need to have that access" and "The system will enforce access to the credentials".
|
||||
- "The agent never sees the key or any other secret value" is already PRD law. REQ-CRED-1 keeps tokens out
|
||||
of an agent's environment and files, and REQ-VAR-2 lets only the broker read a secret's contents. Citing
|
||||
both is right.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. REQ-CRED-3 (draft :113-114) says the stack "then does the ssh and the sudo for the agent", and landing
|
||||
item 5 (:30) says "the stack's ssh and sudo for a granted agent". Jason didn't say that. He said the
|
||||
credential "works as some sort of variable for the agent to call" and that "the system will enforce access
|
||||
to the credentials". Running the ssh on the agent's behalf is one design. Lending the agent a signing socket
|
||||
that never exposes the key is another, and both meet REQ-CRED-1. Which one it is belongs to the #424 design
|
||||
(row 210, Gizmoduck), which I review. Fix: state the outcome and the enforcement only. For example: "The
|
||||
agent asks for the host credential by name. The stack checks that a grant exists for that agent and that
|
||||
host before the credential is used. The agent never sees the key or any other secret value." Change item 5
|
||||
to "adds the host credential grant check and the host access it allows (REQ-CRED-3, Vikunja #424)".
|
||||
|
||||
B2. The last sentence of REQ-CRED-3 (:116), "Jason grants one through a reviewed role or config change",
|
||||
answers the open point that the note at :151-153 says Q183 leaves open. The note says both "Q183 doesn't
|
||||
say who grants" and "The text leaves it as Jason through a reviewed role or config change", so the
|
||||
requirement invents the answer the note calls open. Fix: in the requirement, say it is open. For example:
|
||||
"Who grants a host credential, and how, is set by the #424 design, which Jason approves." Reword the note to
|
||||
match. If Sage wants a floor that already exists in the PRD, "a grant changes only through a reviewed
|
||||
commit (REQ-ROLE-1)" is sourced, but "Jason grants" is not.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Jason's words name "infra agent(s) on my .53 instance". REQ-CRED-3 is general, which is fine because
|
||||
the grant gates it. The source bullet carries the scope, so nothing is lost.
|
||||
N2. Jason's outcome is NOPASSWD sudo. REQ-CRED-3 says only "with sudo". That's fine if the design keeps
|
||||
sudo passwords out of the agent's hands too, which REQ-CRED-1 already requires of any secret. The design
|
||||
review checks it.
|
||||
N3. "Jason through Mos (mos-1067), DECISIONS Q183" (:47): Jason said it in Mos's thread 38bc4c81, and Mos
|
||||
recorded it. "Recorded by Mos" would be more exact than "through Mos". Doc-only.
|
||||
|
||||
A round with only B1 and B2 changed gets a diff check, not a full re-read. Sage's cutoff is 23:00Z.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Review: Honker PRD 1.7 draft r2, REQ-CRED-3, diff check (duckworth-0039)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject: agents/honker/work/queue-194/PRD-1.7-DRAFT.md at
|
||||
11cb0423b on honker/row194, sha256 e896746d…8647 (checked), per honker-0019. 11cb0423b is a fast-forward
|
||||
of 0bb1a3bd0, and the diff touches that one file only, +12 -11. I read the diff only, as REVIEW-r1 said I
|
||||
would.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
- B1 resolved. REQ-CRED-3 states the check, "before the credential is used", and no longer says how the
|
||||
stack reaches the host. Item 5 reads "the host access it allows". The new note says the #424 design
|
||||
picks the path.
|
||||
- B2 resolved. The requirement now says who grants, and how, is set by the #424 design, which Jason
|
||||
approves. The open-point note matches it and no longer names a granter.
|
||||
- The build line (row 210, #1707, Gizmoduck, on top of row 199) is Sage's routing (sage-1430). It is a
|
||||
reviewer note, not PRD text.
|
||||
- Nothing outside B1, B2 and those notes changed. The quotes I checked in r1 are untouched.
|
||||
|
||||
N1 to N3 from r1 stay as notes.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Review: PRD 1.7 and 1.8 landing (duckworth-0075)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in sage-1636 and sage-1641. Subject:
|
||||
`honker/prd-17-18` 46c8adaeb, parent 011d41ba6, 4 files. Checked:
|
||||
- `docs/prd/mosaic-stack.md`: 1196 lines, sha256 a5bba06c…4fc6209f3.
|
||||
- Sources: 1.7 is `PRD-1.7-DRAFT.md` at 9ba31b5b3, sha256 e896746d…1d328647. 1.8 is r7 at 82df17347,
|
||||
sha256 de079281…727440cd.
|
||||
- refactor 816d48b03 hasn't touched the four paths since 011d41ba6.
|
||||
|
||||
## Verdict: approve, no change to what any requirement allows or forbids
|
||||
|
||||
1. Every word of each requirement matches its source. A script took each quoted block of both drafts,
|
||||
collapsed the whitespace and checked each item for one contiguous match in the landed file. All 42
|
||||
items match, including every sub-bullet, the CRED-1 and WAIT-3 appends and the risks bullet. The only
|
||||
two misses are the status sentences, where the drafts had a `<prd.approve decision id>` placeholder.
|
||||
I read the whole diff (8 hunks), and no added line comes from anywhere but the drafts or the five
|
||||
deviations below.
|
||||
2. The deviations are layout and approval record only:
|
||||
- The status field goes from 1.6 to 1.8. The 1.6 landing bumped this same field in place
|
||||
(63c82f1c4).
|
||||
- The 1.7 status cites decision d52febb5 (prd.approve, yes). DECISIONS:1611 confirms decision 21
|
||||
was resolved yes by jason at 2026-10-20T20:36:47Z on the 9ba31b5b3 file.
|
||||
- The 1.8 status says Jason approved it in T3 through Mos (Q192). Its source bullet quotes
|
||||
DECISIONS:1765 exactly, and the Q191 source bullet quotes :1745 ("Q13: that looks better")
|
||||
exactly.
|
||||
- COS-3's 1.7 block sits after ASSIST-3, the last item of the 1.2 list, so the 1.2 items keep
|
||||
their heading.
|
||||
- The CRED-1 append is a paragraph inside CRED-1 after its Vikunja sub-bullet. The two WAIT-3
|
||||
appends are paragraphs inside WAIT-3, the last item before `### Messages`.
|
||||
- TRUST sits after DEC-6, the last Decisions item. CRED-4 and GRANT-1 to 4 sit after CRED-3.
|
||||
LAND-2 sits after LAND-1. The risks bullet ends that list.
|
||||
3. The ten ids are COS-3, CRED-3, CRED-4, GRANT-1 to 4, TRUST-1, TRUST-2 and LAND-2. Each appears in
|
||||
the PRD as a `- **REQ-X-N.**` bullet and in `requirements.json` with the same id and in the same
|
||||
order. `tasks/tests/requirements.test.mjs` asserts the id list equals the PRD's in order, that no
|
||||
bold id is missed, the count of 68 and version 1.8, and that the status line names that version.
|
||||
4. The titles don't change what any requirement allows or forbids. COS-3, CRED-3, GRANT-1, GRANT-3,
|
||||
GRANT-4, TRUST-1, TRUST-2 and LAND-2 are fair summaries. GRANT-2 drops "and the target", but it
|
||||
leaves nothing allowed that the text forbids. On CRED-4 see N1.
|
||||
5. Pins. The tasks test moves 58 to 68, '1.6' to '1.8', and the `twice` index to [68]. Bus
|
||||
`requirements.test.mjs:48` moves 58 to 68. No other test pins the count or the version. No test or
|
||||
script pins a hash of the PRD or `requirements.json` (0 hits, control 11).
|
||||
`requirements.json` is in `scripts/stack-copy.sh:30` and `cli stack-copy.test.mjs:37` as a path
|
||||
only.
|
||||
|
||||
My runs, in a detached worktree of 46c8adaeb:
|
||||
- tasks: requirements 3/0, verbs 31/0, and the whole tasks package 97/0.
|
||||
- bus: requirements 3/0.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The CRED-4 title, "...only with its kind declared, a grant, an allowlisted command and a live key",
|
||||
is stricter than the requirement in two places:
|
||||
- The allowlist applies only "for a command", but the title reads as if every grant action needs an
|
||||
allowlisted command. A Gitea token use has none.
|
||||
- "live key" drops "or token".
|
||||
The PRD text governs, and a stricter label permits nothing extra, so this isn't an allow/forbid
|
||||
change. Agents do read the titles through the bus `requirements` verb, though. A possible wording:
|
||||
"A within-role grant action needs no decision only with its kind declared, a grant, a live key or
|
||||
token and, for a command, an allowlisted command". Sage's call.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Delta check: REQ-CRED-4 title (duckworth-0076)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in honker-0064 (sage-1644, my duckworth-0075 N1).
|
||||
Subject: `honker/prd-17-18` 46c8adaeb..c03d660e7. One line of `packages/tasks/requirements.json`
|
||||
changes, and nothing else.
|
||||
|
||||
## Result: approve, one note
|
||||
|
||||
The new title is "Within-role grant actions need no decision with the kind declared, a grant, a live key
|
||||
or token, any command allowlisted". It's 120 characters, exactly the schema's limit
|
||||
(`packages/tasks/src/requirements.mjs:33`).
|
||||
- "live key or token" now matches the text.
|
||||
- "any command allowlisted" applies only when there is a command, which also matches.
|
||||
- Nothing else in the file changed.
|
||||
|
||||
N1 (doesn't block). The old title said "only with". The new one drops "only", so it lists the
|
||||
conditions as enough to skip a decision but no longer says they're required. REQ-CRED-4 says "only when
|
||||
all of these hold". The title states nothing false and permits nothing the text forbids, and the PRD
|
||||
governs. Still, the one word that carries the requirement's restriction is gone.
|
||||
|
||||
This wording keeps "only" in 117 characters: "A within-role grant action needs no decision only if kind
|
||||
declared, grant, live key or token, any command allowlisted". Sage's call.
|
||||
|
||||
## Follow-up: 2884ee496 (duckworth-0077)
|
||||
|
||||
Asked in honker-0066 (sage-1648). 2884ee496 on c03d660e7 changes one line of
|
||||
`packages/tasks/requirements.json` and nothing else. The REQ-CRED-4 title is now my 117-character
|
||||
wording, byte for byte. That resolves N1. Approve, no notes.
|
||||
|
||||
My runs, in a detached worktree of 2884ee496: tasks requirements 3/0, bus requirements 3/0.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Scoped check: PRD 1.8 r7 with follow-up, 762a6711f..82df17347 (duckworth-0073)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in sage-1624, sage-1627, sage-1632 and sage-1633 (Jason
|
||||
Q191, mos-1149, mos-1152). Subject: `honker/prd18-r7` at 82df17347, two commits on r5 762a6711f: 47c2cb33c
|
||||
(r7) and 82df17347 (Mos's r6 rulings). One file, `agents/honker/work/queue-194/PRD-1.8-DRAFT.md`, 342
|
||||
lines, sha256 de079281…727440cd (checked), 8+/4- against 47c2cb33c (checked). This is the one verdict
|
||||
for both commits. It includes duckworth-0072 (`REVIEW-r7-scoped.md`, the r7 commit and the enforcement
|
||||
note at 011d41ba6).
|
||||
|
||||
## Result: passes the scope, no findings
|
||||
|
||||
r7 (47c2cb33c), from duckworth-0072:
|
||||
1. The only changes are the revision paragraph, the top note, one sentence added to REQ-GRANT-1, and the
|
||||
risks notes. No other requirement text changes.
|
||||
2. The top note and the REQ-GRANT-1 sentence match sage-1624 word for word.
|
||||
3. "one role under two names" is gone from the file.
|
||||
4. Every file:line in the enforcement note is true at refactor 011d41ba6 (list in `REVIEW-r7-scoped.md`).
|
||||
|
||||
Follow-up (82df17347 against 47c2cb33c). There are four hunks, all in scope:
|
||||
- `:18-20` revision clause: it names Mos's rulings on r6 items 1, 3 and 4 (mos-1152) and describes the
|
||||
three edits correctly. Item 2 isn't claimed.
|
||||
- `:25` (b): "Also for Jason at approval:" is now "For Jason at approval:".
|
||||
- `:27-28` (1): the first bullet under the heading is Mos's sentence, word for word as sage-1632 quotes it:
|
||||
"Q188's 'you, Mos or the tester' is read as the user, the assistant for grants in its own organization,
|
||||
and the tester only on a dev instance (Mos's reading, mos-1124 d)."
|
||||
- `:253` (3): the citation is now "design r1 to r4 (duckworth-0056 and duckworth-0060)". Both labels are
|
||||
right. 0056 is my machine-grant r1 review (f6b6a04b6). 0060 is my r3+r4 review (1c5705d6d), and my r2
|
||||
draft was dropped unsent, as SESSIONS records.
|
||||
- `:273` (4): ", and their Q-S5 is answered" is removed. The sentence now ends at "(mos-1124 d)." Nothing
|
||||
else in that bullet changed.
|
||||
|
||||
No other line changed, and the file no longer contains "Also for Jason" or "Q-S5".
|
||||
@@ -0,0 +1,46 @@
|
||||
# Scoped check: PRD 1.8 draft r7 (duckworth-0072)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in sage-1624 and sage-1627 (Jason Q191, mos-1149).
|
||||
Subject: `honker/prd18-r7` at 47c2cb33c, one commit on r5 762a6711f, one file:
|
||||
`agents/honker/work/queue-194/PRD-1.8-DRAFT.md`, 338 lines, sha256 a50ce730…b0551e (checked), 29+/10-.
|
||||
Sage's scope only. The dangling "Also for Jason at approval:" is known (sage-1627) and not reviewed
|
||||
here.
|
||||
|
||||
## Result: passes the scope, no findings
|
||||
|
||||
1. No other requirement text changed. The diff has four hunks:
|
||||
- the revision paragraph (`:16-18`, convention)
|
||||
- the top note (`:20-21`)
|
||||
- one added sentence at the end of REQ-GRANT-1 (`:170-171`)
|
||||
- the two "Risks and open questions" notes (`:293-314`)
|
||||
REQ-GRANT-2 onward and every other requirement are unchanged.
|
||||
2. The quoted lines are exact.
|
||||
- Top note: "Only the chief of staff raises a grant request. The assistant, the user's main agent,
|
||||
never raises one. It passes the user's request to the chief and can revoke a grant in its own
|
||||
organization."
|
||||
- REQ-GRANT-1: "The assistant and the chief of staff are separate roles. No agent holds both, and the
|
||||
broker refuses a claim that would."
|
||||
Both match sage-1624 word for word.
|
||||
3. "Which roles may request" no longer reads "one role under two names". That phrase appears nowhere in
|
||||
the file. The note now says the assistant is a separate role, passes the request to the chief
|
||||
(REQ-ASSIST-1) and can revoke in its own organization (REQ-GRANT-4).
|
||||
4. Every file:line in the enforcement note is true at refactor 011d41ba6:
|
||||
- `business/src/business.mjs:118-120`: the instance's single `definition` is required and loaded.
|
||||
- `business/src/vocabulary.mjs:134`: `THREAD_DEFINITION = "assistant"`.
|
||||
- `bus/src/broker.mjs:164` (`STAFF_TO = 'chief-of-staff'`) and `:2137` (the `no-chief-of-staff`
|
||||
check against `STAFF_TO`).
|
||||
- `broker.mjs:451-456`: `bindLaunch` takes one `role` (keys at `:454-455`). `:478` sets the run key
|
||||
as `business:run`. `:479` gives `duplicate-run`, and `:499` gives `launch-record-mismatch` on a
|
||||
rebind under another role.
|
||||
- `cli/src/launcher.mjs:438-449`: one `spawnSession` with `role: instance`, then one `bindLaunch`
|
||||
for that instance.
|
||||
- `broker.mjs:1689-1734`: `role.claim` writes only `s.role`. `:1693-1709` is the thread branch,
|
||||
which writes `thread_claims` and returns before `role_claims`. A threaded definition must bind a
|
||||
thread (`:474`, `thread-required`), so an assistant run always takes that branch.
|
||||
- `bus/src/migrations/3b.sql:117-119`: `role_one_holder`, unchanged in `schema.sql:117`.
|
||||
- `broker.mjs:34`: `credential.mint` is in ACTIONS (`:9-43`) and not in ROUTED (`:46-69`), so it's
|
||||
in HARD_WAITS (`:71`). There's no other use in bus or business src, so no check limits which role
|
||||
raises it, as the note says.
|
||||
|
||||
The "not enforced" claims also hold. The run key is per business, and nothing in the broker ties two
|
||||
runs to distinct processes or users beyond the launcher's record.
|
||||
@@ -0,0 +1,107 @@
|
||||
# Review: ops-01 Q180 tightening plan r2 (duckworth-0023)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review of the document. I ran nothing on pve, PBS, .53 or
|
||||
.54. The PVE and PBS permission rules below come from ops-01's reading of the local schemas and source, and
|
||||
I didn't re-check them.
|
||||
Subject: /mnt/storage/scratch/tmp/mosvk/q180-tightening-plan.md, written 19:31:42Z, sha256 1084aa15…585f
|
||||
(checked), Vikunja #421. Mos asked for four checks (mos-1025), and ops-01 asked five questions. Both are
|
||||
answered at the end. Cross-checked against FED R8 at eb651aa68.
|
||||
|
||||
## Verdict: changes, B1 to B5
|
||||
|
||||
The diagnosis in §0 is right and the order is right: §1 first, because one key that both Jason and the
|
||||
factory use opens root everywhere. Removing sudo entirely, instead of moving to a sudo password, is right
|
||||
for accounts that run agents. Five gaps block it.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. Lockout (Mos check 1). The plan relies on "nothing if 1a holds" and an open console session. Neither is
|
||||
proven before the step that depends on it.
|
||||
- Before 1d, and before each of 2a to 2c, Jason logs in with the new key from a fresh session to each account
|
||||
that step leaves him: root on pve, root on PBS, and localadmin on .53 and .54, plus a `sudo -v` there. The
|
||||
step runs only after he has.
|
||||
- Say whether localadmin's sudo takes a password. The `sudo` group asks for one by default, and localadmin
|
||||
has none today. If it's NOPASSWD, say so on purpose.
|
||||
- The rollbacks for 2a to 2c say "console in". A cloud-init image usually has root locked. So a console login
|
||||
on .53 and .54 needs a local password that only Jason holds, tested through the pve console before 2c.
|
||||
- Jason's pve web login (root@pam or his own user) is the fallback for all of the above. Confirm it works
|
||||
and that no factory-readable file holds it.
|
||||
|
||||
B2. Where the new key lives. Two things decide it.
|
||||
- FED R8 fingerprints every private key under every home on every factory host, dragon-lin included. The
|
||||
gate fails if any of them matches an `authorized_keys` on the hub host.
|
||||
- localadmin on .54 trusts Jason's new key.
|
||||
|
||||
So the new key, or a FIDO key handle file, can't sit under any home on dragon-lin, and R8 would fail if it
|
||||
did. A FIDO key used from dragon-lin also lets any factory process in Jason's agent socket ask for a touch.
|
||||
The plan should say "a key on another machine" and drop the dragon-lin FIDO option. After this, Jason admins
|
||||
.54 from a machine that isn't a factory host, or through the pve console.
|
||||
|
||||
B3. Group removal doesn't reach processes already running (2a, 2b). Supplementary groups are fixed when a
|
||||
process starts. Hermes's 12 user units, openclaw-gateway and the others keep `docker`, `lxd` and `sudo`
|
||||
until jarvis's user manager restarts. melanie's two gateways are the same.
|
||||
- After the change, restart `user@<uid>.service` for each account, or end every process the account has.
|
||||
Then show that no process of that uid lists the removed gids (`Groups:` in /proc/<pid>/status).
|
||||
- List any uid 0 process started from a jarvis or melanie session through sudo (by loginuid), and end it.
|
||||
- Losing the `docker` group doesn't close a Docker API on TCP, or a container that mounts docker.sock behind
|
||||
a UI jarvis can reach. Check both on .53, by name only.
|
||||
|
||||
B4. Cloud-init order (Mos check 3). Yes, cloud-init can rewrite sudoers. A change to a VM's cloud-init
|
||||
settings on pve regenerates its config drive. A new instance id makes per-instance modules run again at the
|
||||
next boot, and those write `90-cloud-init-users` and the keys back. 1012 has a boot scheduled at
|
||||
2026-10-21T09:00Z (§5). The order has to be:
|
||||
1. In the guest: remove `90-cloud-init-users` and create `/etc/cloud/cloud-init.disabled`.
|
||||
2. Then on pve: clear `sshkeys`, and on 1012 clear `ciuser` too if it isn't needed.
|
||||
|
||||
The plan has these the other way round. Also check after the 09:00 boot that `90-cloud-init-users` is still
|
||||
gone.
|
||||
|
||||
B5. Paths the plan leaves unaccounted for (Mos check 4). The plan inventories ssh keys only. These other
|
||||
paths can reach the hub VM, its backups or its issuer. Each one needs to be either closed here or listed as
|
||||
open with an owner, so R8's table can name it.
|
||||
- PVE and PBS accounts: `pveum user list`, token and ACL lists, and the same on PBS. Any account or token
|
||||
whose secret is factory-readable (dragon-lin, .53, a Hermes profile) is a path.
|
||||
- PBS `root@pam!pve-backup`, which has Admin on `/`. Where is its secret? Remove it unless something needs it.
|
||||
- Rollback copies (§4). The 1e rollback restores the pve→PBS password file "from the copy". Copies of
|
||||
secrets and of sshd or sudo files have to stay on the host they came from, root-only (for example
|
||||
`/root/q180-rollback/`). They can't go under `~/.config/mosaic-dev/` on dragon-lin, which the factory can
|
||||
read.
|
||||
- Storage backing. Say where 1012's disks and the PBS datastore physically live. If either is on TrueNAS or
|
||||
other network storage, then root or a share on that storage reads the guest disk or the backup chunks,
|
||||
around every PVE and PBS check. The `jwoltje@truenas` key and the "tank" migration point that way.
|
||||
- The two step-ca containers on .53, which is a factory host. Say what they sign. If they sign anything the
|
||||
hub or its sign-in provider trusts, they are the R8 "certificate authority" item.
|
||||
- The hub's sign-in issuer: which host runs it and who can admin it. The plan doesn't name it.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. OpsAudit (§1.1) has to be two roles. A role brings all its privileges to every path it's granted on.
|
||||
One role holding `Sys.Audit` and `VM.Audit` granted on `/vms` would open monitor `info`. Use one role
|
||||
(`Sys.Audit`, `Datastore.Audit`, `Pool.Audit`) on `/nodes` and `/storage`, and a second role (`VM.Audit`
|
||||
only) on `/vms`. Add three endpoints to the table, with what each one shows for 1012:
|
||||
`nodes/{node}/report`, other users' task logs (`tasks/{upid}/log`) and the VM firewall log. Check whether
|
||||
1012 sits in a pool with an ACL, since that is a second grant path to the VM.
|
||||
N2. Melanie's Hermes on .54 conflicts with FED R8 ("the hub host runs no other instance or agent"). R8
|
||||
can't pass until those gateways leave .54. Also, all 11 melanie logins came from dragon-lin with the jason
|
||||
key, so after 2b Melanie has no ssh into .54 at all. Say whether that's intended.
|
||||
N3. The 1b watch fails every hour until it's switched to the API. Switch it in the same step, or silence the
|
||||
row for the change window, so the failure doesn't train people to ignore that row.
|
||||
|
||||
## Answers
|
||||
|
||||
Mos (mos-1025):
|
||||
1. Lockout: possible as written. B1 closes it, and B2 decides where Jason admins from.
|
||||
2. PVEAuditor and DatastoreAudit on 1012: the §1.1 and §1.2 tables are consistent with what I know of
|
||||
PVE 9 and PBS 3. Monitor `info` is the one inside-guest leak, and N1's two-role split closes it. Config
|
||||
and listings show metadata only, with cipassword masked.
|
||||
3. Cloud-init can rewrite sudoers: yes. B4.
|
||||
4. Remaining factory paths: yes, the B5 list. The factory key and jarvis@w-jarvis are closed by §1 and §2.
|
||||
Accounts, tokens, storage, the CA and the issuer aren't covered yet.
|
||||
|
||||
ops-01:
|
||||
1. §0: agreed, and §1 first.
|
||||
2. §1.1: N1. Nothing else from inside a guest that I can name. B5's storage item is the larger leak.
|
||||
3. §1.2: agree with deferring encryption, on the three conditions plus B5. That means no factory reach to
|
||||
the datastore's backing storage, and no factory-readable PBS credential, including `pve-backup`.
|
||||
4. §2: agree, with B1, B3 and B4.
|
||||
5. §6: Jason's call. I agree that Q180 has to override Q172 on these four hosts, or the hub has no boundary.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Review: ops-01 Q180 tightening plan r3 (duckworth-0028)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I reviewed the document only and ran nothing on pve, PBS, .53 or .54.
|
||||
Subject: /mnt/storage/scratch/tmp/mosvk/q180-tightening-plan.md, sha256 cd7165bf…0bc3fce (checked), Vikunja
|
||||
#421. Checked against duckworth-0023 (REVIEW-r2.md, 523a6bddf) and mos-1041 as ops-01 relays it.
|
||||
|
||||
## Verdict: changes, B6
|
||||
|
||||
B1 to B5 are resolved, and so are N1 to N3:
|
||||
- B1: the Jason checklist does what B1 asked. It adds a fresh login with the new key on every remaining
|
||||
account before 1d, 2a, 2b and 2c. It adds a console password tested through noVNC, and a pve web login
|
||||
from the non-factory machine only. That last one is Mos's N4, and the browser-profile reason is right.
|
||||
localadmin's sudo takes a password.
|
||||
- B2: the new key never touches a factory host, and the FIDO-on-dragon-lin option is gone.
|
||||
- B3: after the change, each account restarts its user manager and runs terminate-user. A /proc `Groups:`
|
||||
count must then be 0. uid 0 processes are found by loginuid. The Docker TCP and socket-mount checks were
|
||||
read at 19:39Z and get re-read right before 2a.
|
||||
- B4: the order is guest first, then pve, and is checked at the next boot. The 09:00Z boot of 1012 gets a
|
||||
meta dump before and after, an instance-id comparison and hashes.
|
||||
- B5: §1.4 closes each path or gives it an owner. The hub's sign-in issuer is open, and Sage owns it. The
|
||||
plan says that the row has to be filled before the plan is complete. That meets B5. It also means R8
|
||||
can't pass until the row is filled.
|
||||
- N1: two roles, and no pools. Your `nodes/pve/report` finding (Report.pm dumps guest configs raw, so the
|
||||
`cipassword` hash comes through `Sys.Audit`) is a good catch. Step 1a2, done before `OpsAuditHost` is
|
||||
granted, is the right fix.
|
||||
- N2 and N3: done.
|
||||
|
||||
## Blocker
|
||||
|
||||
B6. Root access already granted isn't undone by removing it. This is new in r3, and it is my miss in r2.
|
||||
For a long time, factory processes had root on all four hosts:
|
||||
- the jason key, which is dragon-lin's key, on pve, PBS and root on .53 and .54
|
||||
- jarvis@w-jarvis on pve, 1416 Hermes logins
|
||||
- NOPASSWD sudo for jarvis and melanie
|
||||
|
||||
The plan removes these paths, but anything a factory process left behind as root survives that. Examples:
|
||||
- another file in sudoers.d
|
||||
- a key in a different user's `authorized_keys`, or in `/etc/pve/priv/authorized_keys`
|
||||
- a uid 0 line in `/etc/passwd`
|
||||
- a root cron entry or systemd unit
|
||||
- a setuid binary, `ld.so.preload`, or a PAM or sshd drop-in
|
||||
The hub (.54) is the host where R8 needs this ruled out. The plan needs one of these, before 2b, with an
|
||||
owner:
|
||||
- (a) A read-only sweep on pve, PBS, .53 and .54, reported by name, owner, mode and hash only:
|
||||
- every file in `/etc/sudoers.d` and `/etc/sudoers`
|
||||
- every `authorized_keys` file on the host, found with `sshd -T`'s `authorizedkeysfile`, plus any
|
||||
`authorizedkeyscommand`
|
||||
- uid 0 and gid 0 accounts in `/etc/passwd`
|
||||
- root and system crontabs and `/etc/cron.*`
|
||||
- units in `/etc/systemd/system` and `/etc/systemd/user` that no package owns
|
||||
- setuid and setgid files that no package owns, and `dpkg -V` failures for sudo, openssh, pam and
|
||||
systemd
|
||||
- `/etc/ld.so.preload`, and drop-ins under `/etc/pam.d` and `/etc/ssh/sshd_config.d`
|
||||
|
||||
Jason reviews anything unexpected, and the sweep runs again after §2.
|
||||
- (b) Rebuild 1012 from a clean image instead of cleaning it in place. That fits N2 too, since Melanie's
|
||||
Hermes has to leave the hub anyway.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N4. localadmin's password has to exist at 1a. 1a creates localadmin with sudo by password, and the
|
||||
checklist runs `sudo -v` there before 2a. But the console-password line says "before 2c". Jason sets the
|
||||
password himself with `passwd localadmin` in the pve console when 1a runs. It never goes through a factory
|
||||
session or script. The noVNC test can stay before 2c, or move up to 1a.
|
||||
N5. Before `OpsAuditHost` is granted, run `pvereport` once as root on pve. Count the matches for password,
|
||||
secret, token, key and `BEGIN` lines, and print counts only. That checks your by-filename reading of
|
||||
Report.pm against this PVE build.
|
||||
N6. On pve, `/root/.ssh/authorized_keys` is a link to `/etc/pve/priv/authorized_keys`, which the cluster
|
||||
filesystem holds. Edit that file, copy that file, and say so in 1d. Then check that `pvecm updatecerts` (or
|
||||
an upgrade) doesn't add back anything other than root@pve.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Review: ops-01 #423 VM tightening r4, steps 1 to 3 (duckworth-0032)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I reviewed the document only and ran nothing on pve, .53 or .54.
|
||||
Subject: /mnt/storage/scratch/tmp/mosvk/q183-vm-tightening-r4.md, sha256 03cd1075…cbc05f (checked), 122
|
||||
lines, Vikunja #423. This is ops-01's 20:00:49Z amendment of eadff6b6. I read eadff6b6 in full. In the
|
||||
amendment, lines 1-49 (§0 to §3) are the same apart from the §0 step-ca correction at :20, and §8 is new.
|
||||
Checked against the mos-1054 remap of my r2 items, my mos-1060 carry item and mos-1062 (step 4 is on hold
|
||||
per mos-1061, and step 5 waits for Bean and Chet). Per mos-1065, step 1 runs first after approval.
|
||||
|
||||
## Verdict: changes on steps 1 to 3, B1 only
|
||||
|
||||
Step 1 and step 3 can run as written once B1 is in the text. If r5 changes only B1 and any notes ops-01
|
||||
takes, I will check the diff and approve it without a full re-read.
|
||||
|
||||
What holds:
|
||||
- mos-1060 carry: met. NOPASSWD comes from its own `50-localadmin` (0440, `visudo -cf` then `visudo -c`),
|
||||
localadmin is made with `useradd`, and neither `90-cloud-init-users` nor a cloud-init `users:` block is
|
||||
involved. The §3 post-boot check covers the `50-localadmin` hash.
|
||||
- B1 (r2) as remapped: one lockout check, `ssh localadmin@<vm> sudo -n true`, before any removal and again
|
||||
before each later step (:30).
|
||||
- B4 (r2): `cloud-init.disabled` in the guest first, then the field deletes on pve, then a meta dump and an
|
||||
instance-id and hash check at the next planned boot (:44-46). Deleting fields will change the config
|
||||
Proxmox renders, so a new instance-id is expected. With cloud-init already disabled it does nothing,
|
||||
which is why this order is the right one.
|
||||
- B5 (r2) as remapped: in-VM paths and the 1008/1012 cloud-init fields only (:5).
|
||||
- Rollback copies stay root-only on the host they came from (:8).
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. Step 2's verify can't tell whether `PermitRootLogin no` took effect (:36-37). sshd keeps the first value
|
||||
it reads for a keyword. Ubuntu's and Debian 12's `sshd_config` put `Include /etc/ssh/sshd_config.d/*.conf`
|
||||
at the top, and drop-ins load in lexical order. §0 records `PermitRootLogin without-password` but not where
|
||||
it is set. If it is in a drop-in that sorts before `60-q183.conf` (`50-cloud-init.conf` is a common one),
|
||||
that value wins and `60-q183.conf` does nothing. `sshd -t` only checks syntax. `ssh root@<vm>` is refused
|
||||
either way, because the key is gone, so the stated verify passes in both cases.
|
||||
Fix, in step 2's verify:
|
||||
- `sshd -T -C user=root,host=x,addr=127.0.0.1 | grep -i '^permitrootlogin'` prints `permitrootlogin no`.
|
||||
If it doesn't, find the earlier setting, then rename the drop-in to sort first or edit the setting itself.
|
||||
- From the same `sshd -T`, read `authorizedkeysfile`, and confirm that every file it names under `/root`
|
||||
holds no key (`.ssh/authorized_keys2` is in the default list). Report counts only.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Step 1 `useradd` does not make a home directory without `-m`, and the default shell may be `/bin/sh`.
|
||||
Write `useradd -m -s /bin/bash localadmin`. `~/.ssh` should be 0700 and `authorized_keys` 0600, both owned
|
||||
by localadmin, or `StrictModes` refuses the key. The lockout check would catch either, but it's cheaper to
|
||||
get it right the first time.
|
||||
N2. Step 1 copies "the jason key" from root's file (:29). Copy only that key line, matched by fingerprint,
|
||||
without any options in front of it. Cloud-init's `disable_root` writes a `command="echo 'Please login as
|
||||
…'"` prefix into root's file. If that prefix is copied, every localadmin login runs it. The lockout check
|
||||
would fail closed on it.
|
||||
N3. Write the sudoers file to a temp path, run `visudo -cf` on it, then `install -m 0440 -o root -g root` it
|
||||
into place, then `visudo -c`. A file that fails the check never sits in `/etc/sudoers.d`, where a syntax
|
||||
error makes sudo refuse to run.
|
||||
N4. Before step 2, check whether anything logs in as root by key on a schedule. Count `Accepted publickey
|
||||
for root` lines in the ssh journal for the last 14 days, by source address. Report counts only. A
|
||||
scheduled job like that would break at step 2 and nobody would notice until it fails.
|
||||
N5. The step 2 rollback names the pve console as a way in (:40). A console login needs an account with a
|
||||
password, and none is planned (B1 was remapped to the key check). The real fallback is pve root. Before
|
||||
step 2, check `qm agent <vmid> ping` for 1008 and 1012. If the guest agent answers, `qm guest exec` is the
|
||||
way in. If not, say the fallback is a rescue boot.
|
||||
N6. Add to the §3 post-boot check: `ssh localadmin@<vm> sudo -n true` = 0, and the VM's address is
|
||||
unchanged. Disabling cloud-init leaves the rendered netplan in place, but this check proves it.
|
||||
N7. Step 3 rollback (:49): `qm set --cipassword` takes plaintext and hashes it, and `--sshkeys` takes a key
|
||||
file. Neither can take the values saved in the conf. Restore those lines by writing them back into
|
||||
`/etc/pve/qemu-server/<vmid>.conf` from the root-only copy.
|
||||
|
||||
## Your questions
|
||||
|
||||
(a) Yes, the statement is accurate. One more thing should be stated: pve root reaches every guest's disk
|
||||
and `qm guest exec`. So for a process running as jarvis on .53, the jarvis key is root over 1008 and 1012
|
||||
too, through pve. What §4 narrows is mistakes made inside .53, not an attacker with the jarvis key. Q183
|
||||
point c and R8 record that this is accepted, so this doesn't block.
|
||||
(b) Agreed. localadmin holds NOPASSWD sudo with the same key, so taking the jason key out of jarvis's or
|
||||
melanie's file narrows nothing.
|
||||
(c) On step 4 I'll give my verdict later, per mos-1062. mos-1061 holds it until Case 1 is live on .53 and
|
||||
the sudo and docker flows have another path. That covers the step-ca stacks.
|
||||
I have not reviewed steps 4 and 5 in this round. §6 is unchanged from r3.
|
||||
One count for when step 4 comes back: §8 (:121) says 20 sudo uses, but 9 + 12 = 21, and the per-command
|
||||
list adds up to 18. Recount them before step 4's review.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Review: ops-01 #423 step 4 scripts, revised, C1 to C4 check (duckworth-0048)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I read the scripts and ran nothing on pve or .53. Subject,
|
||||
per ops-01's REVIEW-REQUEST, in /mnt/storage/scratch/tmp/mosvk/o423/ (sha256 prefixes checked):
|
||||
- step4.sh 5aea3856fdd48d27, 46 lines
|
||||
- check4.sh 8f49a40937948be6, 46 lines
|
||||
- containers.sh 837bb7a439e58f14 (new), 33 lines
|
||||
- postboot.sh 0f2f510bbaba09e0 (unchanged)
|
||||
- boot-session.md f0645604f4483cbb
|
||||
|
||||
I read all five in full. I had already read postboot.sh in full for duckworth-0046, whose C3 rests on its
|
||||
:36-38. This round I read it again.
|
||||
|
||||
## Verdict: C1 to C4 met. The optional block may run once C5 is fixed
|
||||
|
||||
C1. step4.sh :14 aborts before any change if `/etc/cloud/cloud-init.disabled` is missing. In 0 and 0',
|
||||
anything other than a dry-run result of exactly fail=1 is a stop. If step 3.1 hasn't run on 1008, it runs
|
||||
first. postboot.sh :41-42 checks the file and `cloud-init status` again after the boot.
|
||||
|
||||
C2.
|
||||
- After the change, step4.sh :41 matches the refusal line "User jarvis is not allowed to run sudo".
|
||||
- After the boot, check4.sh :29 matches the same line again, and :30 requires `/etc/sudoers.d` to be
|
||||
exactly `50-localadmin README`.
|
||||
- ops-01 checked the line format against `nobody` on .53.
|
||||
- `sudo -n true` refused is kept as an extra line, not the proof.
|
||||
|
||||
C3, including Mos's addition.
|
||||
- step4.sh :16-20 compares the current hashes and addresses with the step 3 baseline before any change,
|
||||
and aborts with the names that differ. So drift in `50-localadmin` or `01-q183.conf` can't become the new
|
||||
baseline.
|
||||
- The new baseline (:44) is written after the change and records `90-cloud-init-users` as absent.
|
||||
- postboot.sh :36-38 reports a DIFF if the file reappears.
|
||||
|
||||
C4.
|
||||
- containers.sh save records names and restart policies before the shutdown, and refuses an empty save.
|
||||
- compare reports each saved name that isn't running and requires homeassistant, esphome and
|
||||
mosaic-gw-valkey. check4.sh :45 counts compare's DIFF lines as failures.
|
||||
- start uses `ssh -n`, and compare reads its list from a here-string, so neither loop eats its input.
|
||||
- The plain 1008 path calls save and compare too (boot-session :23 and :29).
|
||||
|
||||
N1, from duckworth-0046: the fallback order in 1' is now `qm guest exec`, then the console, then the offline
|
||||
edit. `qm guest exec 1008 -- /bin/true` was tested. The rollback line names `qm guest exec` too.
|
||||
|
||||
## Condition
|
||||
|
||||
C5. boot-session.md's optional block, line 2, still names the old shas: step4.sh 0ab0f36e…, check4.sh
|
||||
408325f5…. Its dry-run line says fail=8. Change the line to the reviewed shas, step4.sh 5aea3856…, check4.sh
|
||||
8f49a409… and containers.sh 837bb7a4…, with the fail=10 dry run. Then 0' runs `sha256sum` on the three
|
||||
scripts and stops on any mismatch. That way the scripts that run are the ones reviewed here. It's a doc
|
||||
line, so it doesn't come back to me. Report the sha check's result with the step's output.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. step4.sh exits 0 even when `groups_left` isn't 0 or `jarvis_not_allowed` isn't 1. It only prints them,
|
||||
and 1' relies on the operator reading the Expect line. An `exit 1` on either, after the changes and
|
||||
before the baseline is written, would make 1' stop by itself before the shutdown. The rollback is already
|
||||
written by then.
|
||||
N2. step4.sh creates the rb dir (:8) before its prechecks, so an abort leaves an empty
|
||||
`step4-<ts>` dir behind. That's harmless, but use the newest dir with a `rollback.sh` in 3' and in any
|
||||
rollback, not just the newest dir.
|
||||
N3. The two step-ca containers are in a restart loop. Between attempts, `docker ps` may not list them, so
|
||||
compare can report a step-ca DIFF that comes from the loop, not from the boot. If the only DIFF lines are
|
||||
step-ca, run compare again after a minute before treating it as a failure. Don't `start` them, because
|
||||
they were looping before.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Check: ops-01 #423 step 4, N1 and N2 diff and C5 (duckworth-0049)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I read the files and ran nothing on pve or .53. Subject, from
|
||||
ops-01's FYI after duckworth-0048, in /mnt/storage/scratch/tmp/mosvk/o423/ (sha256 checked):
|
||||
- step4.sh cc16d38401e5792e, 47 lines, read in full
|
||||
- boot-session.md ed44ec81b74361cf, sha and N3 lines read
|
||||
- check4.sh 8f49a409, containers.sh 837bb7a4 and postboot.sh 0f2f510b are unchanged from duckworth-0048
|
||||
|
||||
Mos checked the same diff in mos-1095 and cleared the block. This is my own read, so the record shows it.
|
||||
|
||||
## Verdict: the diff holds, and so does duckworth-0048
|
||||
|
||||
- N2. `install -d -m 0700 "$rb"` is at :20, after the localadmin check (:8), the C1 guard (:13) and the C3
|
||||
comparison (:15-19). An abort at any of them leaves no step4 dir.
|
||||
- N1. :47 exits 1 with "do not shut down" unless `groups_left` is 0, the refusal line matched,
|
||||
`90-cloud-init-users` is gone and `/etc/sudoers.d` is exactly `50-localadmin README`. An `A && B || {}`
|
||||
list doesn't trip `set -e`, so the message prints. By then rollback.sh is written (:25-34).
|
||||
- C5. boot-session.md :33-34 names all four reviewed shas and the fail=10 dry run. :39 makes 0' a sha256sum
|
||||
gate that stops on any mismatch.
|
||||
- N3 is at :29 and :63 for both 1008 paths.
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N1. If :47 fails, `baseline.txt` (:44) already holds the failed state. Nothing reads it because the step
|
||||
stops before the shutdown, but a rerun after a fix writes a new `step4-<ts>` dir. Use that one for 3'.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Review: ops-01 #423 step 4 scripts against duckworth-0045 C1 to C4 (duckworth-0046)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I read the scripts and ran nothing on pve or .53. Subject,
|
||||
from ops-01's FYI, in /mnt/storage/scratch/tmp/mosvk/o423/ (sha256 checked):
|
||||
- step4.sh 0ab0f36e…902a, 36 lines
|
||||
- check4.sh 408325f5…ac754, 41 lines
|
||||
- postboot.sh 0f2f510b…7de5, 48 lines
|
||||
- boot-session.md 048c9bfa…1d21, the OPTIONAL 1008 block at lines 27-48
|
||||
|
||||
## Verdict: approve the optional block once C2 and C4 are in the scripts
|
||||
|
||||
ops-01's two departures are the ones I approved in duckworth-0045. The full stop and start replaces
|
||||
user@1000's restart, and postboot.sh reads a baseline written after the change.
|
||||
|
||||
What holds:
|
||||
- step4.sh refuses to start without localadmin's NOPASSWD grant (:9). It copies the sudoers file and the
|
||||
group list root-only before changing anything, and writes rollback.sh with the removed groups fixed at
|
||||
write time (:15-24). It removes only the groups jarvis is in (:13), without a `grep -q` under pipefail.
|
||||
- 1' checks localadmin over ssh again before the shutdown, and stops there if that fails.
|
||||
- check4.sh covers 4.4 (uid-1000 pids holding the four gids, :12-18), 4.5 (uid-0 with loginuid 1000,
|
||||
listed, :19-24), the 14 units, and localadmin sudo, with jarvis's sudo and docker refused.
|
||||
|
||||
C1, met by the 0' dry run. postboot.sh :41-42 fails on a missing `cloud-init.disabled` or a status other
|
||||
than `disabled`. The 1008 dry run's expected result is fail=1 (sshkeys only), so a missing disable file
|
||||
would show as fail=2. ops-01 must treat any dry-run result other than the expected fail=1 as a stop.
|
||||
A one-line guard at the top of step4.sh, aborting when `/etc/cloud/cloud-init.disabled` is absent, would
|
||||
make that hold even if the dry run is skipped. That's optional.
|
||||
|
||||
C3, met. The step 4 baseline records `absent /etc/sudoers.d/90-cloud-init-users` (:33). If the file came
|
||||
back, postboot.sh :36-38 would see a sha line where the baseline has `absent` and report DIFF.
|
||||
|
||||
## Still open
|
||||
|
||||
C2, partly met. step4.sh :29-30 checks groups and `sudo -l -U jarvis`, but only before the boot.
|
||||
check4.sh, after the boot, checks groups, the one sudoers file and `visudo -c`. It doesn't run
|
||||
`sudo -l -U jarvis`, and it doesn't list `/etc/sudoers.d`. A new file there under another name wouldn't
|
||||
be caught, because the baseline names only two files. Add to check4.sh's root block:
|
||||
- `sudo -l -U jarvis` must print "is not allowed to run sudo". Match that line. Don't count
|
||||
"may run the following" lines, because a `sudo -l` that errors (stderr to /dev/null) also counts 0. The
|
||||
same applies to step4.sh :30.
|
||||
- `ls -A /etc/sudoers.d` must list exactly README and 50-localadmin.
|
||||
|
||||
C4, not met. Neither check4.sh nor the block records the running containers. boot-session.md :48 says
|
||||
homeassistant, esphome, valkey and step-ca "keep running", but a reboot restarts dockerd. Only containers
|
||||
with a restart policy come back. Add to 0': as localadmin, record the names of the running containers
|
||||
(`sudo docker ps --format '{{.Names}}'`, sorted, into the rb dir or on dragon-lin). Add to check4.sh: the
|
||||
same list after the boot, compared to the saved one, with names only. A container missing after the boot is
|
||||
a DIFF, and localadmin starts it. This applies to the 1008 boot without step 4 too, since that's the same
|
||||
reboot.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The FYI and 1' give the fallback after step4.sh as Jason at the console, then pve root offline. On
|
||||
1008 there is a tested step between them: the 1008 guest agent answers, so pve root can run
|
||||
`qm guest exec 1008 -- /root/q183-rollback/step4-<ts>/rollback.sh` (my duckworth-0035 N5 and N8). Name it
|
||||
second, ahead of the console.
|
||||
N2. check4.sh :34 asks `systemctl --user is-active` over a jarvis ssh login. That works with linger (the
|
||||
dry run got 14 of 14). It's also the one place in check4.sh that needs jarvis's own login. That's fine
|
||||
because jarvis's authorized_keys stays (§4).
|
||||
@@ -0,0 +1,64 @@
|
||||
# Review: ops-01 #423 VM tightening r5, step 4 (.53 jarvis sudo and groups) (duckworth-0045)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I reviewed the document only and ran nothing on pve or .53.
|
||||
- Subject: /mnt/storage/scratch/tmp/mosvk/q183-vm-tightening-r5.md, sha256 4cbdc48e…f380 (checked). It's
|
||||
the same file I approved for steps 1 to 3 in duckworth-0035.
|
||||
- Scope: §4, the §8 pre-check result, and mos-1092's timing: make the changes, then shut 1008 down and
|
||||
boot it in Jason's boot session, then run the B3 checks, the step 6 verify and postboot.sh.
|
||||
- postboot.sh wasn't sent to me, so I haven't read it. C3 covers what it has to expect.
|
||||
|
||||
## Verdict: approve step 4 with C1 to C4
|
||||
|
||||
The step is sound. §8 shows no Hermes config, unit, cron, hook or script, and no root unit or crontab,
|
||||
that relies on jarvis's sudo, docker or lxd. The skill text that mentions them is instructions, not
|
||||
callers. 18 sudo uses in 30 days, none tied to a unit, fit an interactive habit, not a dependency. What
|
||||
breaks is listed (sudo and docker flows, compose management moving to localadmin). When step 4 is done,
|
||||
jarvis has no local path to root, so row 195's P5 can be marked closed. jarvis's `id_ed25519` reach to
|
||||
pve stays and is P4, accepted under Q183.
|
||||
|
||||
Mos's question: the reboot can stand in for B3's restart, and I don't want the explicit restart as well.
|
||||
A shutdown ends every jarvis process, including sessions outside user@1000 and any uid-0 process with
|
||||
loginuid 1000, so it covers both `systemctl restart [email protected]` and `loginctl terminate-user`.
|
||||
Linger brings user@1000 back at boot with the group list from the new /etc/group. The groups check (4.4)
|
||||
and the leftover-root check (4.5) still run after the boot, as mos-1092 says, and they are the proof. The
|
||||
reboot isn't. Keep the gap between the group change and the shutdown short, because running jarvis
|
||||
processes keep the docker gid until they end.
|
||||
|
||||
## Conditions
|
||||
|
||||
C1. Step 3's `/etc/cloud/cloud-init.disabled` must be in place on 1008 before this boot. If it isn't,
|
||||
and 1008 boots with a new instance-id, cloud-init's users module runs again, writes
|
||||
`90-cloud-init-users` back and gives jarvis NOPASSWD sudo again. If step 3 hasn't run on 1008 by then, run
|
||||
3.1 first in the same session. It's approved and has no restart of its own.
|
||||
|
||||
C2. The verify has to check the stored state, not only a running shell. `sudo -n true` failing as jarvis
|
||||
also happens when a grant exists but asks for a password. Add, as localadmin:
|
||||
- `sudo -l -U jarvis` reports that jarvis may not run sudo,
|
||||
- `id -nG jarvis` lists none of sudo, docker, lxd or adm,
|
||||
- `/etc/sudoers.d` holds only README and `50-localadmin`, and `visudo -c` passes.
|
||||
Report names and results only.
|
||||
|
||||
C3. Since step 4 runs before this boot, the post-boot check for 1008 has to expect
|
||||
`90-cloud-init-users` absent and `50-localadmin` and `01-q183.conf` unchanged. Fix that expectation in
|
||||
postboot.sh before the boot. If it was written against the pre-step-4 hashes, it reports a false change.
|
||||
If it treats "absent" as a pass either way, it would also pass a file that came back. The file has to be
|
||||
absent, and its reappearance has to be a failure.
|
||||
|
||||
C4. A reboot restarts dockerd, which the user@1000 restart didn't. Before the shutdown, record the names
|
||||
of running containers as localadmin (homeassistant, esphome and valkey per §8). After the boot, confirm
|
||||
those three are running again, and start any that has no restart policy through localadmin. Add it to the
|
||||
step 6 verify, with names and states only. The step-ca loop and the exited stacks stay as they are.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Rollback under the reboot plan is: restore `90-cloud-init-users` from the root-only copy, run
|
||||
`usermod -aG`, then either restart user@1000 or reboot. Once step 4 is done, jarvis's sudo is no longer a
|
||||
way back in (my duckworth-0035 N8). The paths are localadmin and then `qm guest exec`, which works because
|
||||
the 1008 agent ping is ok. Re-run the §1 lockout check right before the shutdown, as §1 requires.
|
||||
N2. The adm group gives jarvis read access to `/var/log` files. 2 of the 18 sudo uses were `journalctl`.
|
||||
jarvis keeps its own user journal. A Hermes flow that reads system logs would fail, and §8 shows none.
|
||||
N3. Downtime is now the whole boot for the Hermes units and the home automation containers, not only the
|
||||
user units. Record the shutdown time and each unit's `ActiveEnterTimestamp` as 4.3 already does.
|
||||
N4. This moves step 4 ahead of the Case 1 install, where the row 195 brief had it waiting until Case 1 is
|
||||
live. That's Mos's call, and it's the safer order, because `/srv/mosaic` never exists while jarvis can
|
||||
read it. Row 195's design should change P5's evidence to "closed at #423 step 4" when this is done.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Review: ops-01 #423 VM tightening r5, steps 1 to 3, diff-only (duckworth-0035)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. I reviewed the document only and ran nothing on pve, .53 or .54.
|
||||
- Subject: /mnt/storage/scratch/tmp/mosvk/q183-vm-tightening-r5.md, sha256 4cbdc48e…f380 (checked).
|
||||
- Base: q183-vm-tightening-r4.final.md, sha256 03cd1075…cbc05f (checked, the version I reviewed in
|
||||
duckworth-0032).
|
||||
- Diff: q183-r4-to-r5.diff, sha256 15326e32…4e70 (checked). It is identical to my own `diff -u` of the
|
||||
two files, so nothing outside the diff changed.
|
||||
|
||||
## Verdict: approve steps 1 to 3 on 4cbdc48e
|
||||
|
||||
- B1 resolved.
|
||||
- The drop-in is `01-q183.conf`, so it sorts ahead of `60-cloudimg-settings.conf` and any cloud-init
|
||||
drop-in.
|
||||
- §0 records the facts the fix depends on: Include is at line 12, `sshd_config` has no
|
||||
`PermitRootLogin` line, and the effective value is `without-password` today.
|
||||
- The verify reads `sshd -T` for `permitrootlogin no`, and gets a key count of 0 for every
|
||||
`authorizedkeysfile` path under /root. `ssh root@` refused is labelled "not proof".
|
||||
- N1 to N3 and N6 are taken as written. N2 is better than I asked. The key line comes from jarvis's or
|
||||
melanie's file, matched by fingerprint against dragon-lin's public key, with no options. Root's file is
|
||||
never used, because it carries the `command=` prefix.
|
||||
- N4: 0 root key logins in 14 days on both VMs, counted again right before step 2.
|
||||
- N5: 1008 falls back to `qm guest exec`. 1012's fallback is named and untested. See N8.
|
||||
- N7: the conf lines are restored from the root-only copy, without `qm set`.
|
||||
- Answer (a) is in §0.
|
||||
- Recount: 2 + 7 + 9 = 18, and the per-command list also sums to 18, over a fixed UTC window. The cause
|
||||
of the earlier numbers is stated.
|
||||
|
||||
Condition: this approval covers step 1 as written in r5. The gate line says step 1 "follows" the #424
|
||||
credential-store design if that design arrives first. If #424 changes how localadmin's key or sudo is
|
||||
held, the changed step 1 comes back to me as a diff before it runs.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N8. Step 2 doesn't touch jarvis's or melanie's sudo. Until §4 and §5 run, `ssh [email protected] sudo` and
|
||||
`ssh [email protected] sudo` are still a tested way back in. That makes them the first fallback on both VMs,
|
||||
ahead of the guest agent and the rescue boot. Name them in step 2's rollback. If 1012 ever needs more, a
|
||||
better fallback than `init=/bin/bash` is pve root editing the stopped VM's disk offline. That doesn't
|
||||
depend on catching a GRUB menu that cloud images usually hide.
|
||||
N9. Root's key already carries a forced `command=`, so root key login only prints a message today.
|
||||
Step 2 is still right: it removes the key and sets the policy, so a later edit of root's file can't
|
||||
reopen root login.
|
||||
N10. The §6 unit-file rule (webby-0025) is sound. Root renders or copies the unit in, root-owned, with
|
||||
fixed-value checks, and no unit source sits under /srv/mosaic. I'll review it with Gosalyn's design and
|
||||
the Case 2 step that runs it.
|
||||
@@ -0,0 +1,61 @@
|
||||
# #1782 owner thread claims, design r1 (duckworth-0118)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: morgana-0083 (05:11:29Z).
|
||||
- Candidate: 3e9939b7b on morgana/thread-claims-c272, file agents/morgana/work/owner-thread-claims/BUILD.md, base c27207f6e.
|
||||
- Brief: docs/plans/2026-10-22_owner-thread-claims.md (Sage). It is uncommitted, and I read it in the canonical checkout.
|
||||
- Method: git reads only. Nothing ran.
|
||||
|
||||
## Verdict: approve D1 with conditions C1-C3. Q1 yes. On Q2, a follow-up row is fine with the same bind, and Sage rules it.
|
||||
|
||||
## Q1. D1 over the mixed list: accepted
|
||||
|
||||
The consumer table is right at c27207f6e:
|
||||
|
||||
- webui public/bus.js reads `c.role` per row in both held tables (:447, :474).
|
||||
- `held-count` (:462) and `Held roles` (:476) count rows.
|
||||
- The TUI reads `item.role` and `holder_run` (app.mjs:288, :433).
|
||||
|
||||
A mixed list would render blank role cells and wrong counts in the live Console unless webui and the TUI changed in this row. The composer (:298) filters non-strings and would be safe.
|
||||
|
||||
D1 meets brief items 1-3 and 5 without touching any of them. `agents` stays the same for every session, which is stronger than item 2. The brief allows a separate verb.
|
||||
|
||||
- **SQL.** It uses the same latest-op join as `#threadHolders` (broker.mjs:485), minus the instance and owner filters. The `thread_one_holder` trigger (3d-3e.sql:49) keeps it to one held row per thread.
|
||||
- **Fields.** `instance` is operator-written, so it can be in the result. `name` is user-written, so it must stay out (brief item 3, T5/M5).
|
||||
- **Reader access.** Human and reader sessions are already exempt from the processing gate (:402), so a halt doesn't hide claims. The reader allow-list (:2370) is the one list that must gain the verb.
|
||||
|
||||
## Conditions (must hold in the build)
|
||||
|
||||
- **C1. `mosaic agents --tester` must not call `thread.claims`.**
|
||||
- D4 has `mosaic agents` call `agents` and then `thread.claims`.
|
||||
- With `--tester`, `callFor` binds as the tester (cli.mjs:193-198). D6 leaves the verb off TESTER_VERBS, so the second call fails `tester-refused`. `mosaic agents --tester` would then exit 3, which breaks a working command.
|
||||
- It also breaks tester.test.mjs:127-128, which pins exactly one spawn with verb `agents`.
|
||||
- Fix: call `thread.claims` only on the human path, and keep that pin unchanged. Add a T that `agents --tester` still makes exactly one call. The touched-file list for the gate then includes tester.test.mjs (run whole, no change) and other-tree.test.mjs:26/:33.
|
||||
- **C2. State the `--json` shape and pin it.**
|
||||
- `print` (cli.mjs:299) writes the raw value under `--json`. D4 says nothing about JSON.
|
||||
- Pick one and test it in T6:
|
||||
- the `agents` array unchanged, with thread claims only in text, or
|
||||
- an explicit object.
|
||||
- I found no consumer of `mosaic agents --json` in tests or scripts at c27207f6e.
|
||||
- I recommend leaving the array unchanged. The owner's machine read is then `thread.claims` itself, documented in the bus README.
|
||||
- **C3. Record the item 5 deviation.** The brief says the pin moves to the owner's `agents` call. Under D1 it moves to `thread.claims` (D5). Add one line to BUILD.md naming this as the D1 consequence, so the manifest review doesn't read it as a miss.
|
||||
|
||||
## Q2. The Console
|
||||
|
||||
- I agree with a follow-up row for a Console "Thread claims" section, bound the same way: it lands before any user thread goes live.
|
||||
- PRD :1083-1084 promises the owner visibility. The Console is the owner's main surface, and a Console-only owner won't run `mosaic agents`. So the follow-up can't be optional before go-live. That is the same bind #1782 itself carries.
|
||||
- Keeping it out keeps this row's manifest off webui and its browser tests for 10-27.
|
||||
- Sage rules the scope.
|
||||
|
||||
## Notes (don't block)
|
||||
|
||||
- N1. T4 covers role, human and reader. Add the tester session's `agents` to the same before/after equality. It costs one line, and it pins D6 for the tester too.
|
||||
- N2. M3 drops the human/reader check. T3 checks a role session. Also check that a tester gets `tester-refused`, which the TESTER_VERBS check gives before the verb case. That makes D6 a tested claim and not just a code-reading one.
|
||||
- N3. For FED R3, a user principal must not get `thread.claims` for other users' threads (D6 defers it). Name that in the R3/R6 carry when this lands, alongside 205 N1/N3.
|
||||
- N4. The before-in-review grep for new verb-set pins is right. Add the processing-gate list (:403) to that grep, even though human and reader are exempt, so a later edit doesn't miss it.
|
||||
|
||||
## Next
|
||||
|
||||
1. Morgana builds to D1 with C1-C3.
|
||||
2. Sage rules Q2 and creates the row.
|
||||
3. Manifest round on the candidate with the C1 test.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Row 277 (#1782) build, early static look (duckworth-0119)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: morgana-0093 (05:26:15Z).
|
||||
- Candidate: c050bb90b on morgana/row277-23c3, base 23c3d6218 (the origin/refactor tip). Four commits.
|
||||
- Manifest: /mnt/storage/scratch/morgana-r277/manifest-c050bb90b.txt, digest eedc783f…8dcf, 9 paths, none in webui.
|
||||
- Method: git reads only. Nothing ran, and the gate is still to come.
|
||||
|
||||
## Verdict: no blockers. The build matches D1 and meets C1-C3. Approval on this candidate waits on a green gate and the M1-M7 results.
|
||||
|
||||
## Checks
|
||||
|
||||
- **Manifest.**
|
||||
- The file hashes to eedc783f…8dcf.
|
||||
- All nine lines match their blobs at c050bb90b.
|
||||
- `git diff --stat 23c3d6218 c050bb90b` is exactly those nine paths.
|
||||
- No commit has an attribution line.
|
||||
- **Verb (broker.mjs:3425-3432).**
|
||||
- Human or reader only, otherwise `human-required`. No arguments.
|
||||
- The SQL is the `#threadHolders` latest-op join with only the business filter. It returns the five D1 fields, ordered by `t.seq`.
|
||||
- `thread_claims.harness` is NOT NULL (3d-3e.sql), so the text line never prints `undefined`.
|
||||
- **Reader list.** `thread.claims` is added at :2370, the one list D3 named. TESTER_VERBS is unchanged, so the tester refuses at :2371 before the verb.
|
||||
- **C1 (cli.mjs:314-319).** `--tester` returns after the single `agents` call. tester.test.mjs:133-145 pins the tester calls as `["agents"]` and the human calls as `["agents","thread.claims"]`. The single-spawn pin at :127-128 is unchanged.
|
||||
- **C2.**
|
||||
- `--json` prints the `agents` result alone, and the test deepEquals it with the broker's own `agents` answer.
|
||||
- format.test.mjs pins three text cases: mixed, no roles with threads, and the default `threads` argument. Role lines are byte-identical to before.
|
||||
- **C3.** BUILD.md records that the pin moves to `thread.claims` (D5 and C3). message-filter.test.mjs:175-177 reads `holder_run` from the owner's call.
|
||||
- **Tests vs the brief.**
|
||||
- Owner sees, and doesn't see after release or revoke: T1 and T2.
|
||||
- T2 asserts the op history `claim, release, claim, revoke`, which shows the first-op reading would be wrong.
|
||||
- Role session unchanged: T4, across all four sessions.
|
||||
- No user-written text: T5 checks the name and a thread message body. T1 also pins the exact key list.
|
||||
- CLI mixed list: T6.
|
||||
- **Mutants vs tests.** Each one has a test that fails it:
|
||||
|
||||
| Mutant | Change | Test that fails it |
|
||||
| --- | --- | --- |
|
||||
| M1 | `[]` | T1 deepEqual, and T7 `claim?.holder_run` |
|
||||
| M2 | `min(seq)` | T2 'released' |
|
||||
| M3 | the human/reader check dropped | T3 chief and run sessions |
|
||||
| M4 | `agents` appends thread rows | T1 role map and T4 |
|
||||
| M5 | `t.name` added | T5 and the T1 key list |
|
||||
| M6 | thread lines dropped | format.test.mjs |
|
||||
| M7 | the tester path calls `thread.claims` | T8 (the tester throws, or the calls are `["agents","thread.claims"]`) |
|
||||
|
||||
- **Pins.**
|
||||
- A grep of `packages/*/tests/*` and `scripts/*` finds no sha256 pin on the four changed source paths.
|
||||
- `thread.claims` appears only in the manifest paths and the brief.
|
||||
|
||||
## Notes (don't block)
|
||||
|
||||
- N1. Before this row, `mosaic agents` (human, text) made one call. Now a failing `thread.claims` fails the whole command. Human and reader skip the processing gate, and the CLI and broker ship in the same copy, so I see no live path to that failure. Name it if the gate shows otherwise. This is the CLI side of the row 278 brief's "a refused thread.claims call leaves the role rows rendered".
|
||||
- N2. packages/cli/README.md:31 still lists `mosaic agents [--business <id>] [--json]` with no `--tester` and no mention of the thread lines. This is doc-only (decision 137). It can be fixed here, or with row 278.
|
||||
|
||||
## Next
|
||||
|
||||
1. The gate runs on Sage's CLEAR: the touched files whole plus other-tree.test.mjs, the bus and cli suites, every scripts/test-*.sh, and M1-M7.
|
||||
2. Morgana makes the in-review move with the logs.
|
||||
3. Manifest round on the same digest. If the candidate is unchanged, I expect to approve.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Row 277 (#1782) manifest review (duckworth-0127)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: morgana-0129 (11:14Z).
|
||||
- Candidate: morgana/row277-46de, tip 62f53b1e078af20765de72161c9f83927405b1b2, base 46dedf925.
|
||||
- Manifest: /mnt/storage/scratch/morgana-r277/manifest-62f53b1e0.txt, 10 paths, digest e281aa2c7f0dc8f0.
|
||||
- Evidence: /mnt/storage/scratch/morgana-r277/logs/gate-62f53b1e0.out (sha256 95a2d9abd8a1411b, 71 lines) and the per-run logs Morgana named. Method: reads only. I ran nothing.
|
||||
|
||||
## Verdict: approve.
|
||||
|
||||
## Bind
|
||||
|
||||
- The log opens with HEAD 62f53b1e0, the e281aa2c digest, `manifest check ok` and the anchors for M1-M9.
|
||||
- The gate tree, morgana-thread-claims, is at 62f53b1e0 now, and `git status -- packages` is clean.
|
||||
- 0126 checked the digest against the blobs at 62f53b1e0.
|
||||
|
||||
## C1: the gate list ran whole
|
||||
|
||||
- **Files.** Every test file the row touches ran whole, plus the three named readers: thread-claims 5, message-filter 6, user-principal 22, cli format 4, tester 11, other-tree 4, host 43. Each has 0 fails and 0 skips.
|
||||
- **Suites.** bus 793, cli 836, harness 151, business 231, seat 167. Each has 0 fails and 0 skips.
|
||||
- **Scripts.** All 10 `scripts/test-*.sh` at 62f53b1e0 ran with rc=0. The set equals `git ls-tree` scripts/test-*.sh.
|
||||
- **Named skips.**
|
||||
- test-queue has 1 skip. Its verify/render check needs the canonical root, which a worktree isn't.
|
||||
- test-task has 2 skips, the live provider cases held out under row 244. I counted them in script-test-task.log at lines 87 and 93.
|
||||
- Neither skip covers a path in 277's manifest.
|
||||
|
||||
## C2: M1-M9 killed on the committed bytes
|
||||
|
||||
- Each mutant run has 0 passes and 1 fail, on the named test.
|
||||
- M1-M7 and M9 fail with `ERR_ASSERTION`. M9 is the new one, and it fails "Missing expected rejection", which is the outcome 0125 predicted.
|
||||
- M8 makes the catch rethrow, so the stubbed `processing-halted` error escapes main and fails tester.test.mjs:149. That's the behaviour the mutant changes: it isn't a syntax error or a harness crash. So it counts as a kill.
|
||||
- The log ends `restored at 62f53b1e0` and `unexpected: 0`. The clean tree status above confirms the restore.
|
||||
|
||||
## Notes (don't block)
|
||||
|
||||
- N1. M8 is killed by the rethrown error, not by an assertion. A test that pins `main` resolving would make the kill an assertion. Builder's call. It doesn't need another round.
|
||||
- Carry. 0123 N3 binds 264a r7, as sage-2592 ruled: per-session thread.claims plus a two-session test (C4), and the default session in thread-claims.test (C5).
|
||||
|
||||
## Next
|
||||
|
||||
Sage posts this verdict. I record approve in the gap Sage names, with --candidate set to the round's key from queue show.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Row 277 (#1782) post-194 delta review (duckworth-0123)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: morgana-0119 (10:34Z), per sage-2506.
|
||||
- Candidate: morgana/row277-0165, tip e77c9d30c, base 0165be266. The base is the origin/refactor tip, with 194, 236 and 274 landed.
|
||||
- Manifest: /mnt/storage/scratch/morgana-r277/manifest-e77c9d30c.txt, 10 paths, digest e731ae6e407ab019.
|
||||
- Method: git reads only, plus the manifest file Morgana sent by path. Nothing ran. The gate waits on Sage's CLEAR, after Honker's 256 gate.
|
||||
|
||||
## Verdict: no blockers. Ready for the gate. C1-C2 apply at the manifest round.
|
||||
|
||||
## Checks
|
||||
|
||||
- **Range-diff.** 23c3d6218..be2414b64 against 0165be266..e77c9d30c is all `=` across the six commits. e77c9d30c has the same patch-id as be2414b64 (b9ecaa23). No commit has an attribution line.
|
||||
- **Rebase.**
|
||||
- The path set equals 23c3d6218..be2414b64.
|
||||
- In bus README, broker.mjs, cli README and cli.mjs, the changed lines are identical before and after the rebase. Only the surrounding context moved.
|
||||
- Every manifest line matches its blob at e77c9d30c, and the file hashes to e731ae6e.
|
||||
- **N1, the refused call** (cli.mjs:320-329).
|
||||
- `agents` is called first and unchanged.
|
||||
- A thrown error with a bus code (`^[a-z-]{1,64}$`, the same check as cli.mjs:255 and :503) prints the role lines, then `thread claims: refused <code>`, and the command exits 0.
|
||||
- An error without a code is rethrown. That includes the transport's timeout `CliError` (transport.mjs:64), which sets no `.code`.
|
||||
- The `--json` and `--tester` paths make one call, as before (C1/C2 from 0118).
|
||||
- **N1 test** (tester.test.mjs:147-161).
|
||||
- The stub transport throws a `processing-halted` error shaped like `via`'s at :29-32.
|
||||
- The test pins the whole output (role line, then the refusal line) and the human call list `["agents"]`.
|
||||
- A mutant that drops the try/catch fails it.
|
||||
- **N2.** cli README:31 now lists `[--tester]`, and :81-86 describes the thread lines, the refusal line, `--json` and `--tester`. That matches the code.
|
||||
- **The broker on the new base** (broker.mjs:3708, :4951-4957).
|
||||
- The hunk is unchanged.
|
||||
- The thread_claims columns it reads are unchanged on 0165be266. 236's 3j-3k step doesn't touch that table.
|
||||
- **274.**
|
||||
- thread.claims isn't on USER_VERBS, so a user session is refused at the user gate.
|
||||
- It can now return user-thread rows to the owner and readers: thread id, instance, run, harness and since. No name and no messages.
|
||||
- The approved design takes the `#threadHolders` join "without the instance and owner filter" (BUILD.md:55), which I approved in 0118. That's owner-visible run governance, as sage-1368 B1 ruled for row 193, so I accept it (N2).
|
||||
|
||||
## Conditions for the manifest round
|
||||
|
||||
- C1. Morgana's gate list runs whole: the 10 touched files' tests, other-tree.test, host.test and user-principal.test, then the bus and cli suites and every scripts/test-*.sh. Skips must be named.
|
||||
- C2. M1-M8 are killed on the committed bytes, with HEAD and the e731ae6e check printed at the top.
|
||||
|
||||
## Notes (don't block)
|
||||
|
||||
- N1. The rethrow branch has no test.
|
||||
- A catch-all mutant (drop the code check) survives. It would print `refused undefined` on a timeout and exit 0.
|
||||
- Also, `invalid-response` (transport.mjs:66, :74) prints as "refused" though it isn't a bus refusal.
|
||||
- A test adding one codeless throw would close both. Builder's call.
|
||||
- N2. User threads from 274 now appear in the owner's and readers' `thread.claims` (see Checks). Sage can confirm that if they want it on record.
|
||||
- N3. Carry to 264a.
|
||||
- thread.claims takes `max(seq)` per thread (broker.mjs:4955). Under 264a's sessions, one thread can hold several session claims, so the query must go per session.
|
||||
- Whichever of 277 and 264a lands second must make that change and test a thread with two sessions. It must also give thread-claims.test's hand-made threads (:47) and its `message_threads` insert (:111) the default session.
|
||||
- I'll check this in whichever review comes second.
|
||||
|
||||
## Next
|
||||
|
||||
1. Sage's CLEAR after the 256 gate, then Morgana's gate.
|
||||
2. Manifest round. If e731ae6e is unchanged and C1-C2 hold, I approve.
|
||||
3. I record in a gap Sage names. --candidate must be the round's key, so I check queue show first.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Row 277 (#1782) r4 delta review (duckworth-0125)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: morgana-0122 (10:37Z), a reply to duckworth-0123.
|
||||
- Candidate: morgana/row277-0165, tip fa9e7fae6, base 0165be266. It fast-forwards from e77c9d30c.
|
||||
- Manifest: /mnt/storage/scratch/morgana-r277/manifest-fa9e7fae6.txt, 10 paths, digest e281aa2c7f0dc8f0.
|
||||
- Method: git reads only, plus the manifest file Morgana sent by path. Nothing ran.
|
||||
|
||||
## Verdict: no blockers. 0123's N1 is closed by the new test, if the gate shows M9 killed.
|
||||
|
||||
## Checks
|
||||
|
||||
- **Shape.** e77c9d30c is an ancestor of fa9e7fae6, so commits 1-6 are unchanged. Commit 7 has patch-id 66e228cf and touches two files, +38 and no removals. No attribution line.
|
||||
- **Manifest.** All 10 lines match their blobs at fa9e7fae6, and the file hashes to e281aa2c. Against e731ae6e, only BUILD.md and cli tester.test.mjs changed. The path set equals 0165be266..fa9e7fae6.
|
||||
- **The new test** (tester.test.mjs:164-178).
|
||||
- The stub passes every verb except thread.claims to the real broker, so `agents` runs for real first.
|
||||
- Case 1 throws an Error with no `.code`. Case 2 sets `ECONNRESET`, which fails `^[a-z-]{1,64}$` on case. Both reach the rethrow at cli.mjs:326.
|
||||
- main's catch (cli.mjs:677-678) rethrows anything that isn't a CliError unchanged, so `e === error` holds on the unmutated code.
|
||||
- stdout must stay empty, which also proves the role lines aren't printed before the throw.
|
||||
- M9 (drop the code check) would print the refusal line and resolve. Both assertions fail, so the mutant is killed by assertion, not by a crash.
|
||||
- **BUILD.md r4 section.** It matches what the commits do. The gate list (sage-2590) is a superset of my C1.
|
||||
|
||||
## Your question on invalid-response
|
||||
|
||||
Leave it. It prints `refused invalid-response`, which names the real cause, and the command still exits 0 with the role lines. Sage can rule otherwise. It doesn't block.
|
||||
|
||||
## Conditions for the manifest round
|
||||
|
||||
- C1 and C2 from 0123 stand, with M9 added to C2: M1-M9 killed on the committed bytes, with HEAD and the e281aa2c check printed first.
|
||||
- The digest the round checks is now e281aa2c, not e731ae6e.
|
||||
|
||||
## Next
|
||||
|
||||
1. Sage's CLEAR after the 256 gate, then Morgana's gate.
|
||||
2. Manifest round on fa9e7fae6. If e281aa2c is unchanged and C1-C2 hold, I approve.
|
||||
3. I record in a gap Sage names. --candidate must be the round's key, so I check queue show first.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Row 277 (#1782) rebase check onto 46dedf925 (duckworth-0126)
|
||||
|
||||
- Reviewer: Duckworth (Opus 5.5), 2026-10-22.
|
||||
- Request: sage-2600 (11:01Z) and morgana-0126 (11:01Z).
|
||||
- Candidate: morgana/row277-46de, tip 62f53b1e0, base 46dedf925. The base is the origin/refactor tip, with 256 landed.
|
||||
- Method: git reads, plus the manifest file Morgana sent by path. Nothing ran.
|
||||
|
||||
## Verdict: a clean rebase. No path changed. 0125 stands on 62f53b1e0.
|
||||
|
||||
- **Range-diff.** 0165be266..fa9e7fae6 against 46dedf925..62f53b1e0 shows all seven commits as `=`. That's seven, not the six in sage-2600, because r4 added commit 7. The tip's patch-id is 66e228cf, the same as fa9e7fae6. No attribution line.
|
||||
- **Manifest.** manifest-62f53b1e0.txt is byte-identical to manifest-fa9e7fae6.txt and hashes to e281aa2c7f0dc8f0. All 10 lines match their blobs at 62f53b1e0. The path set equals 46dedf925..62f53b1e0.
|
||||
- **What 256 changed.** It touched 14 paths between the two bases, and none of them is in 277's manifest.
|
||||
- Its runtime.mjs change passes the reader to `instances.read` and `instances.checkDelete`. Its agents.js change is a comment on `endProtected`.
|
||||
- Neither one touches `agents`, `thread.claims` or the cli `agents` output, so 277's tests read nothing that 256 changed.
|
||||
|
||||
## Manifest round
|
||||
|
||||
C1 (Morgana's gate list whole, skips named) and C2 (M1-M9 killed on the committed bytes, with HEAD and the e281aa2c check printed first) apply to 62f53b1e0.
|
||||
@@ -0,0 +1,101 @@
|
||||
# #1797 config_checks on any key, round 1: static review (duckworth-0229)
|
||||
|
||||
Reviewer: Duckworth. Author: Gander. Request: gander-0164. Brief: f0e3dcdf4
|
||||
docs/plans/2026-10-22_config-checks-any-key-brief.md (Sage, mos-1353). Queue row: not assigned yet.
|
||||
Candidate: 0585b3213 on 01983cd3f, branch gander/config-checks-any-key. Packet e7371025f.
|
||||
Manifest: agents/gander/work/config-checks-any-key/MANIFEST.sha256, sha256 d6226d281c778677…, 3 paths.
|
||||
Method: git reads only. No runs.
|
||||
|
||||
## Verdict: APPROVE (static), with C1 and C2
|
||||
|
||||
The change does what the brief asks, and each brief test is there. The approval holds for this
|
||||
round only. The post-270 rebase adds the compaction-key cases, and that round needs its own read (C1).
|
||||
|
||||
## Manifest
|
||||
|
||||
- The manifest file hashes to d6226d28…4a97e4a.
|
||||
- All 3 blobs match 0585b3213: TOOLS.md eea06caa, stack-release.test.mjs a35c49c5,
|
||||
stack-release.sh f8fe1348.
|
||||
- The path list equals `git diff --name-only 01983cd3f 0585b3213`. All 3 are in the brief's
|
||||
"Files owned".
|
||||
- origin/refactor was 01983cd3f when I read it. No test pins a hash of stack-release.sh or TOOLS.md.
|
||||
|
||||
## config_checks (stack-release.sh:1076-1104)
|
||||
|
||||
- :1083-1086 classify the file once: missing (rc 1, pass), unparseable (rc 2, refuse), or parsed.
|
||||
`extra` is in the `local` at :1080, and the assignment is bare, so `|| key=$?` keeps node's rc.
|
||||
- With a loader, :1096 runs the target's validate for every parsed config. stdout goes to
|
||||
/dev/null, so the resolved config is never kept. That meets "whenever the config file exists and
|
||||
parses".
|
||||
- `validate` without `--instance` doesn't run checkClaim (mosaic-config.mjs loadSelected), and
|
||||
instance.sh:100 exports a named instance's MOSAIC_CONFIG. So a named-instance op validates its own
|
||||
config and can't refuse on a claim mismatch.
|
||||
- With no loader, :1092-1095 refuse only for a key outside the oldest four, or a non-object value.
|
||||
- The console branch and its text at :1100 are byte-equal to the base, including the
|
||||
"it has no scripts/mosaic-config.mjs" text. The row 200 tests at :1575-1594 still match.
|
||||
- The non-console text at :1102 carries the loader's first line, which names the refused key
|
||||
(`unsupported configuration key: "…"`), and keeps the restore-the-backup instruction.
|
||||
|
||||
## The oldest key set
|
||||
|
||||
- The set is c3d29e796:scripts/mosaic-config.mjs:114, the first loader. It matches the fixture's
|
||||
LOADER_OLD.
|
||||
- It's the right answer to the brief's "name that set from the copies". stack-copy.sh has had
|
||||
`scripts/mosaic-config.mjs` in COPY_PATHS since it was added (305a012b1), and the loader predates
|
||||
stack-release.sh (7e7f8e208). So no copy that stack-copy.sh writes lacks a loader. The no-loader
|
||||
branch only matters for a hand-made or damaged copy, and there it fails closed.
|
||||
|
||||
## Tests (stack-release.test.mjs:2588-2644)
|
||||
|
||||
- :2597 a refusing target, no console key: activate and rollback both refuse before any stop, and
|
||||
`--plan` lists one refusal. That's the brief's first case.
|
||||
- :2612 accepting loaders on C1 and B get called once each with `validate` and the selected
|
||||
MOSAIC_CONFIG, and the canary on stdout never shows up. The fixture's copies run their loader only
|
||||
from config_checks (the copy's scripts/mosaic is mosaicStub), so the exact two-line witness holds.
|
||||
- :2630 a missing loader passes the oldest-key config and refuses each compaction key.
|
||||
- :2639 a missing file passes, and `{` refuses, both without calling the loader.
|
||||
- The helper at :2624 cuts the function at the first `\n}\n` after `config_checks() {`. No line in
|
||||
the body is a bare `}`, so it gets the whole function.
|
||||
- anyConfigLoader overwrites the existing loader in B or C1 and restores the bytes and 0444 in
|
||||
`finally`. The copies are shared across the file, and the file runs its tests in order (no
|
||||
`concurrency` option).
|
||||
|
||||
## Mutants M1-M5 (changes.json)
|
||||
|
||||
- Each anchor appears once in f8fe1348. I counted with python3 -I -B over the git blob, and each
|
||||
mutated file's sha256 matches the "after" value in UNIQUENESS.json.
|
||||
- Kills, as read:
|
||||
- M1 (console-only skip) and M2 (`|| true` on validate): :2597's refusals don't happen.
|
||||
- M3 (no-loader pass): :2630 gets status 0, not 3.
|
||||
- M4 (`key = 99`): `{` falls through to the stand-in loader, which writes the witness and exits 0.
|
||||
:2639 fails on status and on the witness.
|
||||
- M5 (skip the loader for an oldest-key config): :2612's witness is empty.
|
||||
- M1 is the brief's named mutant. All five are sound.
|
||||
|
||||
## Conditions
|
||||
|
||||
- C1. The rebase after 270, 271 and 203 is a new round. It adds the activate and rollback refusal for
|
||||
`compactionReadBytes` and `compactionStreams` against a pre-270 target, as BUILD.md says. After 270,
|
||||
the fixture's LOADER_OLD comes from the 270 loader, which takes both keys through `.concat`. So B no
|
||||
longer stands for a pre-270 copy on those keys, and the case needs its own stand-in. I read that
|
||||
round's manifest and the new test before the gate.
|
||||
- C2. The gate in BUILD.md runs on Sage's CLEAR: RED/GREEN, stack-release.test.mjs whole, the cli
|
||||
suite whole, every scripts/test-*.sh, and M1-M5 with byte restores. A first-run red follows
|
||||
mos-1272.
|
||||
- Every existing activate and rollback against B, C1 and C now runs the real loader, where before
|
||||
only console configs did. stack-release-external.test.mjs and stack-upgrade.test.mjs drive the
|
||||
real script too. The cli suite run covers all of them. A red there is the change's, not
|
||||
pre-existing.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
- N1. A config with `console` and a later key the target refuses takes the console branch (:1086
|
||||
prefers `console`). The message says "holds a console key… remove the key first" while the
|
||||
diagnostic in brackets names the other key. The restore instruction is still right. Doc-level.
|
||||
- N2. With no loader the text reads "refused by OLD's loader (it has no scripts/mosaic-config.mjs to
|
||||
validate configuration key: …)". It's accurate, but it reads oddly. Doc-level.
|
||||
- N3. :2633 sets `compactionStreams` to `['messages']`. 270 makes it an integer. Only the key is
|
||||
checked, so it doesn't matter, but 1024 would match the real shape.
|
||||
- N4. BUILD.md says "No config values are printed." The loader's first line can echo a value (for
|
||||
example `unsupported environment: "x"`) into the refusal and the log. The console branch already did
|
||||
this, and config.json holds no secrets. The BUILD.md wording could say that.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Review: Morgana row 193 design r3 (duckworth-0011)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/morgana/work/queue-193/DESIGN.md, diff 19c015741..76f4605e1 (one commit, morgana-0008).
|
||||
DESIGN.md at 76f4605e1 has sha256 e61500d9…8859, checked. Line numbers are at 76f4605e1. Code read at
|
||||
53bb59462. Against duckworth-0008 (REVIEW.md, aa7f082bf).
|
||||
|
||||
## Verdict: changes, B3 only
|
||||
|
||||
B1 and B2 are resolved and N1 to N4 are done. B3 is a small gap in the new 5.7 table, which B1 asked to
|
||||
list every read: the project file reads are missing, and they carry a user-thread run's output to the owner.
|
||||
|
||||
## r2 codes
|
||||
|
||||
- B1 resolved (5.7, :279-327). Option (a) is taken and stated plainly: messages are private, governance is
|
||||
not. The table covers the reader list at broker.mjs:1303 and the owner's board reads. The governance pin
|
||||
test (:405-407) shows a user-thread decision in the owner's `decisions recent` and `inbox`, with message
|
||||
bodies still hidden. The :1683 route is named as intended. Gizmoduck's FED r4.5 (acd5f1f42, gizmoduck-0022)
|
||||
now keeps it, with an R3 test and an R6 notice, so the two designs agree.
|
||||
- B2 resolved (5.7 :322-324, section 9 :440-451). The message filter is in 193b1 with `#readable`
|
||||
owner-only, and 193b2 adds only the share exception. No order of FED R8 and 193b2 can put a user thread
|
||||
live unfiltered. Mutants 11 and 12 are tested under 193b1 (:408-413).
|
||||
- N1 done (:383-384). N2 done (:6-7). N3 done (5.6 :277, section 8 :393, mutant 5 :429): R4 owns the
|
||||
level-loss test and mutant. N4 done (5.2, :205-207).
|
||||
|
||||
## Blocker
|
||||
|
||||
B3. The table at :289-302 says it lists every read an owner or reader session has, but it leaves out
|
||||
`project.files` and `project.file` (broker.mjs:1441-1450, human and tester). A user-thread run acts with
|
||||
the owner's role authority, so where that role holds record authority it can `record.write` into the
|
||||
project repository (broker.mjs:1395-1407). The owner then reads that record with `project.file`. Under
|
||||
option (a) this is governance and visible, which is fine. But the table omits it, and the user-facing
|
||||
sentence at :306 names only decisions. A user who reads "Decisions your thread's agent raises go to the
|
||||
business owner" would assume records the agent writes stay private.
|
||||
Fix: add a `project.files`, `project.file` row (records a run writes, governance, visible) and widen the
|
||||
sentence to "Decisions your thread's agent raises, and records it writes to the project, go to the business
|
||||
owner." Also list `message.receive` and `message.read` on the `messages` row (:296). They filter on
|
||||
`to_role='human'` too (broker.mjs:1931-1936, :1953), so they're private for the same reason. Gizmoduck's R6
|
||||
notice should match the widened sentence (I'm telling Gizmoduck in duckworth-0012).
|
||||
|
||||
## Note (does not block)
|
||||
|
||||
N5. The paragraph at :308-311 still says FED may add a guard at :1683 and that the `inbox` row would then
|
||||
change. FED r4.5 has settled this: no guard, as Mos ruled (mos-1021, mos-1022). Cite r4.5 and drop the
|
||||
conditional.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Review: Morgana row 193 design r4 (duckworth-0014)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/morgana/work/queue-193/DESIGN.md, diff 76f4605e1..edc794933 (one commit, morgana-0009).
|
||||
DESIGN.md at edc794933 has sha256 c18d27b0…b613a, checked. Against duckworth-0011 (REVIEW-r3.md, 912b1d325).
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
## r3 codes
|
||||
|
||||
- B3 resolved (5.7 table and sentence). The `project.files` and `project.file` row is in, marked
|
||||
governance, visible. `message.receive` and `message.read` are on the `messages` row. The user sentence
|
||||
names records, and the governance pin test checks that a run's `record.write` shows in the owner's
|
||||
`project.file`.
|
||||
- N5 done. The gated-raise paragraph cites FED r4.5 (acd5f1f42) and drops the conditional. It matches what
|
||||
I approved in duckworth-0012.
|
||||
|
||||
FED R6's notice should use this sentence (duckworth-0012 N21).
|
||||
@@ -0,0 +1,11 @@
|
||||
# Review: Morgana row 193 design r5 (duckworth-0018)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/morgana/work/queue-193/DESIGN.md, diff edc794933..e2d161e2e (one commit, morgana-0011).
|
||||
DESIGN.md at e2d161e2e has sha256 dd7fcb41…b523, checked. Doc-only.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
The 5.7 sentence now names every governance read the table marks visible: decisions, inbox, `agents`
|
||||
(claims), `tasks` and `project.file`. The governance pin checks each one, so the sentence and the test
|
||||
can't drift apart. FED r4.6 uses the same sentence word for word (duckworth-0019).
|
||||
@@ -0,0 +1,72 @@
|
||||
# Review: Morgana row 193 design r2, many humans per business (duckworth-0008)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
|
||||
Subject: agents/morgana/work/queue-193/DESIGN.md at 19c015741 on morgana/row193 (sha256 f1283e310d65…5d10,
|
||||
checked), base 53bb59462, Gitea #1691. r2 supersedes 5d15979e0 (morgana-0006), and I reviewed r2 whole.
|
||||
Code read at 53bb59462. Federation design read at 73677b87d (r4.1). gizmoduck-0020 says `covers` is unchanged
|
||||
through r4.4.
|
||||
|
||||
## Verdict: changes, B1 and B2
|
||||
|
||||
The model is sound. There's one store for users and grants, the role is the grant's level, and one
|
||||
verifier (row 196) mints every principal. Shares never widen a grant, and the latest row per
|
||||
`(thread, target)` decides. Two things block. 5.7 promises more privacy from the owner than the bus gives.
|
||||
And the build order can put user threads live before the filters that keep them private.
|
||||
|
||||
## Rulings asked in morgana-0004
|
||||
|
||||
1. `thread_shares` deviation (5.1): approved. F1 to F3 hold. A share is a runtime act by a non-owner
|
||||
principal, and access.json is owner-written config whose loader revokes everything on any error. An
|
||||
append-only bus table gated by `covers` is the right home. N12 is met (5.2, :207-208).
|
||||
2. Roles owner, collaborator, viewer (4.1-4.3): approved. Viewer creates, opens and sends nothing, so it
|
||||
never drives an instance. The per-role verb table refuses everything not listed.
|
||||
3. The R4 predicate: settled in FED. `covers` with `level: 'collaborator'` is FED R4's. See N3 for who owns
|
||||
the downgrade test.
|
||||
4. "Private" stated plainly (5.7): not yet, see B1.
|
||||
5. H1-H5 (section 6): consistent with row 196 as I reviewed it (duckworth-0006). Users have no CLI path,
|
||||
and the verifier returns no role.
|
||||
6. Schema 3j, the `--migrate` window and the split (7, 9): approved, subject to B2.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. 5.7 says a user's thread is private from the owner at the bus boundary (:284-286), and it filters
|
||||
only `trail` and `replies`. A run in a user's thread acts with the owner's role authority (F10), and
|
||||
several owner and reader reads show what it does:
|
||||
- `decisions {list: 'roles' | 'recent'}` (broker.mjs:2174-2192) and `inbox` (:2150-2157) return whole
|
||||
decision rows: `question`, `options` and `recommendation` are free text the run writes (schema.sql:46-61).
|
||||
The `decisions` table has no thread column, so no filter can catch these by `message_threads`.
|
||||
- FED 3.6 item 7 stops a user-thread run from messaging `to: 'human'`, but doesn't name the decision route
|
||||
at :1683. That is the tester's guard for a decision routed to the human (`tester-thread-human`). Without
|
||||
it, a user-thread run's gated raise lands in the owner's inbox. I'm raising this with Gizmoduck as well.
|
||||
- `agents` and `tasks` show the run's claims and the Vikunja work it touches.
|
||||
Fix: pick one, and say it in 5.7.
|
||||
- (a) Keep these owner-visible on purpose. The run spends the owner's authority, so its decisions and task
|
||||
writes are the owner's governance. Then 5.7 says plainly that the user's messages are private, and the
|
||||
run's decisions, tasks and claims are not. Add a test that pins it: a user-thread run's decision shows
|
||||
in the owner's `decisions recent`.
|
||||
- (b) Filter them too. That needs a thread link on decisions, which is more schema, and it hides
|
||||
governance from the owner.
|
||||
I recommend (a). Either way, 5.7 has to list every owner and reader read and say which side of the line
|
||||
it's on.
|
||||
|
||||
B2. The 5.7 filters land in 193b2 with schema 3j (:414). 9 says nothing is exposed before that, because
|
||||
"FED R8 is later" (:420-421). Nothing enforces that order. FED R8 is the 10-27 live window, and 193b2
|
||||
waits for a `--migrate` window Sage hasn't scheduled. If R8 goes first, the owner can read every user
|
||||
message through `trail` and `replies`.
|
||||
Fix: move the hiding filter to 193b1. Without shares, `#readable` is false for anyone but the thread's
|
||||
owner, so b1 hides every message in another user's thread, and needs no schema. 193b2 then only adds the
|
||||
share exception. Mutants 11 and 12 move with it. The alternative is an R8 entry-gate line saying R8 waits
|
||||
for 193b2, and Gizmoduck would have to carry that.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. Section 7 still says "193a and 193c have no schema change ... 193b carries 3j" (:355-356). After the
|
||||
split, 193b1 has no schema change either, and 193b2 carries 3j.
|
||||
N2. The header lists FED r3.1 (a2840a6be) as read (:6-7), but coverage now comes from r4.1 and later.
|
||||
Update the citation so the build seat reads the right FED.
|
||||
N3. The downgrade test and mutant 5 are claimed by both rows. 5.6 and section 8 put them in 193b1
|
||||
(:274-275, :365-366, :399), and FED r4.4 keeps them in R4, which depends on row 193's viewer level (mos-1020,
|
||||
gizmoduck-0020). Name one owner, so the mutant isn't run twice or dropped by both.
|
||||
N4. `thread.share` with `to` set to the business human's name: if the business file's `human` is later
|
||||
renamed, old share rows name a stale target. Nothing renames the human today. One line in 5.2 saying a
|
||||
share to a name that is no longer the human is void would close it.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Scoped check: row 194 b3 N1 delta (duckworth-0074)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in sage-1634 through honker-0059. Subject:
|
||||
`honker/row194-b3`, a61a49a51..86889c59e, one commit, 3 files, +55/-0. Only this delta is in scope. My
|
||||
N1 is from duckworth-0070.
|
||||
|
||||
## Result: N1 met, no findings
|
||||
|
||||
- `business/src/instance-settings.mjs:365` (`commit()`): before `beforeWrite`, the requested revision
|
||||
is compared with `writer.read(business).revision`, and on a difference the write refuses
|
||||
`revision-conflict`.
|
||||
- The code and text are the same as the writer's own check (`organization.mjs:188`), so no caller
|
||||
sees a new refusal.
|
||||
- The check comes after the no-change, validate, route and project checks. A stale request that also
|
||||
fails one of those still gets the same refusal as before.
|
||||
- Every caller passes a checked 64-hex revision. `set` checks it at `:179` and `veto` at `:262`.
|
||||
`hire` and `fire` take theirs from `writer.read` (`:284`, `:311`). No caller relies on a missing
|
||||
revision to skip the check.
|
||||
- For add, change and veto, the only change is that the refusal comes earlier. For delete and fire,
|
||||
a stale revision now refuses before the revoke.
|
||||
- The window that remains is in BUILD.md: a write between this read and the writer's lock still lets
|
||||
the revoke run and then refuses. The `:361-364` comment says so too. Option 2, running `beforeWrite`
|
||||
under the lock after `:188`, is a follow-up. That fits sage-1634.
|
||||
- T21 (`bus/tests/lifecycle-revoke.test.mjs:146`) reproduces the case. A delete is raised on revision
|
||||
R and taken, then an issue for rev-3 writes the file. After that, the delete refuses
|
||||
`revision-conflict` and leaves these unchanged:
|
||||
- the file's bytes
|
||||
- rev-2's credentials entry
|
||||
- the vikunja token file and its bytes
|
||||
- the store listing
|
||||
- status `valid`
|
||||
- Fire has no test, which I accept. It reads its revision at `:311` in the same synchronous call, so
|
||||
it can only go stale through another process, and it runs the same line.
|
||||
- The refusal text is unchanged, which is right. The CLI REASONS and Console INSTANCE_REASONS
|
||||
`revision-conflict` lines say nothing about a revoke, and with this check none has run.
|
||||
- No imports changed. `instance-settings.mjs` isn't a RUNNER_FILES file. No test or script pins its hash
|
||||
(0 hits, control 11).
|
||||
|
||||
My runs, single files in a detached worktree of 86889c59e:
|
||||
- bus: lifecycle-revoke 5/0 (T21 included), reload 8/0.
|
||||
- business: instance-settings 11/0.
|
||||
- cli: instances 14/0.
|
||||
- webui: agents 8/0.
|
||||
- cli `lifecycle.test.mjs` gives 8/8 here, all eight "timed out waiting" on a sandboxed launch. The base
|
||||
a61a49a51 gives the same 8/8, so it's my session, like seat sandbox.test. Honker's 16/0 and Sage's
|
||||
gate cover it.
|
||||
|
||||
I ran no mutants. M76 and M77 are on Sage's slot.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Scoped check: row 194 hunks on row 199 code (duckworth-0070)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Asked in sage-1606 and sage-1607, with hunks listed in
|
||||
honker-0052. Subject: `honker/row194-b3` at b39766112, on cb5fdb064 (row 199's landing). Commits:
|
||||
589ae2c1d (squash), b1db067c5 and b39766112. Webby gives the row verdict. This check covers only the
|
||||
hunks below.
|
||||
|
||||
## Result: no blocker in scope
|
||||
|
||||
1. `packages/business/src/credentials.mjs:73-81, :110-113`: the date skip for a store reference
|
||||
with no file. Holds.
|
||||
- `notIssued` is true only for `ref.store` with `ENOENT`. Every other reference keeps both date
|
||||
checks, and every other lstat error is still `not found`.
|
||||
- The date's format is still checked at parse (`:50-51`) and by the bus `Credentials`
|
||||
constructor (`bus/src/credentials.mjs:51-52`). Only the expiry and rotation comparisons are
|
||||
skipped.
|
||||
- A revoked store reference (`{store:true, date}` with no file) skips them too. That's right,
|
||||
because it describes no token.
|
||||
- `DATE_KEY` is now exported (`:12`), and `instance-settings.mjs:120` reads it. The values are
|
||||
unchanged.
|
||||
|
||||
2. Credential verbs.
|
||||
- `bus/src/credential-verbs.mjs` and `bus/src/credentials.mjs` are byte-identical to cb5fdb064.
|
||||
- `runtime.mjs:109-143`, the revoke callback.
|
||||
- Fire revokes each service of the entry. `credential-unknown` (an agent created since the start)
|
||||
goes on, and any other error stops the verb as `credential-revoke-failed`.
|
||||
- Delete calls `Credentials.remove`. For an instance the bus doesn't know, that's an empty
|
||||
service list, and it clears the directory's token and tmp files (`bus/src/credentials.mjs:211-227`).
|
||||
- Both then refuse if anything is left in the agent's store directory, so a fire with a token on
|
||||
disk that the running bus doesn't know refuses (sage-1592).
|
||||
- `instance-settings.mjs:332-358` runs every check before `beforeWrite` and the write after it,
|
||||
so a refused check revokes nothing and a failed revoke writes nothing.
|
||||
- `broker.mjs:1816-1817` passes `credential-revoke-failed` through for `instance.fire` and
|
||||
`instances.set` only. Any other code still becomes `outcome-unknown`.
|
||||
- Tests: `bus/tests/lifecycle-revoke.test.mjs`, the four T20 cases. They cover the fire and delete
|
||||
failures with the file's bytes unchanged, and the "no entry in the running bus" cases.
|
||||
|
||||
3. `runtime.mjs:154-167`, fixedRole compares kind and path only. Holds.
|
||||
- `fixedRef` keeps `file`, `env` and `store`, so a change from file to env, a path change or a
|
||||
change to store still answers `restart: true`.
|
||||
- Only `rotateBy` and `expires` are dropped. The only writers of those dates are row 199's `issue`
|
||||
(which updates `Credentials` memory in the same verb, `bus/src/credentials.mjs:190-191`), its
|
||||
revoke (date kept) and row 194's create (today's date, a store reference with no file).
|
||||
- `reload.test.mjs` T14 (date-only change still reloads) and T19 cover it.
|
||||
|
||||
N2 from my row 199 review (the restart route, sage-1607). Both required items are present:
|
||||
- (a) The create result says a restart is needed. The broker answers `restart: true` because the new
|
||||
references differ from the boot copy. The CLI prints `RESTART_NEEDED` (`cli/src/instances.mjs:68`,
|
||||
`:252`), and the definitions list says "added with store references and a restart" (`:209`).
|
||||
Tested at `cli/tests/instances.test.mjs:78`, `:100-101`.
|
||||
- (b) `bus/tests/reload.test.mjs:117` (T19) creates a credentialed agent, expects `restart: true`,
|
||||
then runs `issue` and expects `credential-unknown`. It starts a new bus on the same files, after
|
||||
which `issue` works, status reads `valid`, and a later vars change still reloads.
|
||||
|
||||
My runs, single files in a detached worktree of b39766112:
|
||||
- business: credentials 6/0, instance-settings 11/0.
|
||||
- bus: credentials 5/0, credential-store 12/0, credential-verbs 2/0, lifecycle-revoke 4/0, reload
|
||||
8/0, project-runtime 3/0.
|
||||
- cli: instances 14/0.
|
||||
- seat `sandbox.test.mjs`: 7/3, and the same 7/3 on cb5fdb064. The failures are "no answer" from the
|
||||
confined shell in my session, so this environment can't run that test. It isn't evidence either
|
||||
way. Sage's gate and Honker's run cover it.
|
||||
- broker.mjs's only import change adds `unhireableAction` from `vocabulary.mjs`, which is already
|
||||
mounted. RUNNER_FILES isn't affected.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. A fire or delete revokes before the write. If the write then fails (`revision-conflict`, or an
|
||||
`outcome-unknown` from the writer), the credentials are already revoked while the file is
|
||||
unchanged.
|
||||
- A store reference is left in the not-issued state, which matches the file, and a retry is safe
|
||||
because a revoke of a missing store file is ENOENT, not an error.
|
||||
- The human sees `revision-conflict` and nothing says the agent's tokens were revoked.
|
||||
- A one-line mention in that refusal's text, or in the CLI's retry hint, would close it. It's a
|
||||
follow-up at most.
|
||||
|
||||
N2. Because fixedRole ignores dates, a hand edit that extends a vikunja `expires` in the business
|
||||
file reloads with `restart: false` (T14). The running `Credentials` keeps its boot date, so `use`
|
||||
still refuses `credential-expired` until a restart. No writer verb produces that change, only a hand
|
||||
edit, so this is a documentation point at most. It's consistent with the runtime comment at
|
||||
`:151-153`.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Row 195 manifest round 1 (duckworth-0088)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-21. Darkwing asked in darkwing-0100. In-review rev 2756, request
|
||||
#1693 comment 29985, gate sage-1872. Candidate `darkwing/row195-b` 37171f65b on 4ced5e63d. Manifest
|
||||
`~/darkwing-scratch/r195/manifest-37171f65b….sha256`, file sha256
|
||||
c6ba9ac9eab6c87e86846ee231fbd2b0db15532b62c5ebaf614fcc53ea8db8d1, five paths. I approved the design as
|
||||
r7 5425ea2f3 in duckworth-0087 (REVIEW-r7.md).
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
1. The manifest holds.
|
||||
- All five digests match their blobs at 37171f65b, and the path set equals
|
||||
`git diff --name-only 4ced5e63d 37171f65b`.
|
||||
- `scripts/case1-accept.sh` is 100755.
|
||||
- `queue review verify-commit 195 37171f65b…` prints "matches the manifest candidate (5 paths)".
|
||||
- 4ced5e63d..origin/refactor changes only `docs/plans/DEFERRED.md`, which isn't in the manifest.
|
||||
2. DESIGN.md is in the manifest because the branch carries r6 and r7. Its blob changes from the approved
|
||||
17dfbad95 in four one-line hunks: :21, :82, :326 and :340. :82 is my N1 (`broker.mjs:321`). :21 and
|
||||
:326 give row 226's state, and :340 records gosalyn-0085. All four are doc-only, as sage-1817
|
||||
allows. Nothing else in the design moved.
|
||||
3. The script follows §5.
|
||||
- The args file passes `stack-upgrade.sh`'s read_args rules: root-owned, no group or other write, one
|
||||
40-hex `--copy`, unknown pre-`--` args refused. It also refuses a non-factory file and one for
|
||||
another business (BUILD #5). Both are stricter than the design, which is fine.
|
||||
- The `--root` seam is refused as root, and a user needs it (:40-45). The test covers the user side
|
||||
(:453-462). The root side can't be tested without root, the same as the hostcheck.
|
||||
- F3 matches `sent <id> to <role>` (cli.mjs:395). F7's JSON fields `run`, `business`, `instance`
|
||||
and `live` match `launches list --json` (cli.mjs:478) and `launch.by`. F7 accepts only the fixed
|
||||
`ok <instance> run <run>` line from the stack's user.
|
||||
- P9's eight items are the six `^ok N ` hostcheck lines, the jarvis `Permission denied`, the empty
|
||||
member list of group `mosaic`, and no other account on its gid. A missing item gives one unnamed
|
||||
line.
|
||||
- P checks never touch the exit. Mutant 12 and the "every P unnamed" test pin that.
|
||||
4. The three items Darkwing asked me to check:
|
||||
- BUILD #1, P4 `accepted Q182+Q183`. One line carries one mark, and P4 is the only Q182 path, so one
|
||||
token is right. The count list stays consistent.
|
||||
- BUILD #8, F5 names claude by argv0 basename or `comm`. This can only fail closed. A pass still needs
|
||||
both labels exactly, on a `/usr/bin/bwrap` owned by `mosaic` with no bwrap parent. A name that
|
||||
misses gives FAIL, and the window catches it, as BUILD says.
|
||||
- BUILD #12, `@` in the `--claude` regex. That's needed for `node_modules/@anthropic-ai/...`. F6's
|
||||
prefix check and `case2-unit.sh`'s root-only rule still bound the path.
|
||||
5. Tests and mutants. All the cases from §5 are present in the 22 tests, and BUILD names a killing test
|
||||
for each of the 12 mutants. BUILD explains why mutant 1 survives the F1-variants test, and "each F
|
||||
alone" kills it. I read the table. I didn't run the tests.
|
||||
6. Hash and count pins. No test pins a hash of `docs/TOOLS.md` or of the new files. The 64-hex grep over
|
||||
`packages/*/tests/*` and `scripts/test-*.sh` finds pins in other files, which is the positive control,
|
||||
but none on these paths. The only TOOLS.md hit in tests is a string inside
|
||||
`stack-release.test.mjs:76`, not a read. The cli suite (662/0) and every `scripts/test-*.sh` passed on
|
||||
Sage's gate (sage-1870).
|
||||
|
||||
## Notes (no block)
|
||||
|
||||
- N1. F9 runs `$ROOT$NODE` as root (`case1-accept.sh:298`) before F10 checks that `--node` is the
|
||||
`/opt/mosaic-stack/node-<pin>` path (:303-304). The header (:10) and the TOOLS.md entry say root
|
||||
"only reads". BUILD #6 reasons that node is root's own. That holds because `case2-unit.sh:128-133`
|
||||
refused a non-root-owned `--node` when it rendered the units, and the args file is root's. So this is
|
||||
not an escalation path on a host installed as designed. The cheaper shape is to run F9's parse
|
||||
through `AS_USER` (the file is 0644), or to apply F10's path check first. Either one makes the header
|
||||
true. A later row can do it.
|
||||
- N2. The header's "Root itself only reads systemd, ss, /proc..." leaves out the commands root does run:
|
||||
`curl` (F2), `sudo -l` (P1, P3), the jarvis probe (P9), `hostcheck.sh --check` and `systemctl`.
|
||||
Doc-only.
|
||||
- N3. F6's prefix match (:261) accepts a `--claude` with `..` after `claude-<pin>/`, because the path
|
||||
regex allows `.`. That claude runs only as `mosaic`, and the args file is root's, so nothing is
|
||||
reached. F10 uses equality, so it's unaffected.
|
||||
|
||||
## My runs
|
||||
|
||||
None. I read the code, the tests and BUILD.md at 37171f65b. Darkwing ran the single, the mutants and the
|
||||
gate in Sage's slot (sage-1829, sage-1865), and Sage confirmed them in sage-1870.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Review: Darkwing row 195 DESIGN r2, the Case 1 install on 10.1.1.53 (duckworth-0047)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. My first review of this design. Darkwing asked for r2 in place
|
||||
of r1.
|
||||
- Subject: darkwing/row195 at 818b79696, `agents/darkwing/work/queue-195/DESIGN.md`, blob fcbb4aa9b5,
|
||||
sha256 329a5458…a28ea (checked), 196 lines. Its parent is on origin/refactor.
|
||||
- Brief: `docs/plans/2026-10-20_hub-own-uid.md` at f24f2cd40, blob 45be7f09afc1. Rulings sage-1451.
|
||||
- I read the whole design and the brief. I checked these citations at 818b79696:
|
||||
- `launcher.mjs:310`, `sandboxProblem({ bwrap })` before every role launch
|
||||
- `broker.mjs:245`, a human named `tester` refuses
|
||||
- `webui/src/serve.mjs:466-470`, the default host is 127.0.0.1, non-loopback refuses, and the tester
|
||||
listener is 127.0.0.1 only
|
||||
- `serve.mjs:611`, `/healthz`
|
||||
- the control board's default `127.0.0.1:7331`
|
||||
- `stack-release.sh`, the systemctl, backup and gate lines cited in B1
|
||||
- I ran nothing on .53 and nothing on dragon-lin's live state.
|
||||
|
||||
## Verdict: changes, B1 only
|
||||
|
||||
What holds:
|
||||
- §0's host facts are what the rest of the design needs, and they were read only, as the brief allows.
|
||||
- §1 gives a reason for every input: `dev` and its paths, a per-user node so Hermes's node is left alone,
|
||||
claude pinned through `MOSAIC_CLAUDE_BIN`, and the tester ports. The step table names a runner and a path
|
||||
for each step. The root steps go through ops-01. The `mosaic` steps use `sudo -u mosaic -H`, with no key
|
||||
and no login shell for `mosaic`. The root-owned checkout outside `/srv/mosaic` for `case2-unit.sh`
|
||||
matches the §6 unit-file rule (webby-0025, my duckworth-0035 N10).
|
||||
- §2 gives every difference from Case 2 a reason. Keeping the Discord check is right, because one bot token
|
||||
shouldn't feed two connectors. Asking for `--profile` over a copied script follows the brief.
|
||||
- §3 follows Q185 and the Q173 secrets rule. The credential is made in the window, never copied from
|
||||
dragon-lin, never printed, and only its name is counted. pi stays off until #383.
|
||||
- §5 implements the brief's acceptance contract: one line per path with a mark, `unnamed` for "not named
|
||||
in Q182 or Q183", and a non-zero exit only on an F check. Each of F1 to F4 has a check you can run. F2
|
||||
follows sage-1451 Q3. The test plan uses a fake root and fake tools, so nothing reaches .53.
|
||||
- §6 states the rulings as Sage gave them.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. §4 doesn't yet say how a re-pin, a rollback or a backup runs on .53. It names the problem (:108-111)
|
||||
and leaves the fix to the build (:113-115, "I'll size that in the build and name it in the manifest"),
|
||||
but §7 doesn't name it. The brief makes re-pin, rollback and backup, and who runs each, part of the
|
||||
design. Three concrete gaps:
|
||||
|
||||
1. The health gate and the in-run rollback have no owner. `stack-release.sh` activate is health-gated, and
|
||||
it puts the old stack back in the same run when the gate fails (:2-4, `rollback_in_run` :561-583). That
|
||||
all goes through `systemctl --user` (:178, :436-453, :474, :554). §4 splits the activate into a `mosaic`
|
||||
half (copy, pointer, backup) and a root half (`case2-unit.sh`, restart). Nothing then says who runs the
|
||||
`/healthz` gate after root's restart, or who puts the old copy back if the gate fails. It doesn't say
|
||||
whether the activation log gets an `ok` or `gate-failed` line either. That log is what the §4 rollback
|
||||
reads ("the previous distinct copy, from the activation log").
|
||||
2. The backup is of a running store. In §4's order, `mosaic` takes the tar (:122-124) while the unit is
|
||||
still up, because only root can stop it. `backup_store` (:369-392) copies `bus.sqlite` only after the
|
||||
bus has stopped, with no -wal or -shm, and checks `stopped` again afterwards (row 185). A tar of a live
|
||||
SQLite store isn't a consistent backup. The tar also lands in `/srv/mosaic/backups/` rather than
|
||||
`$DATA/backups/stack/<activation>`, so `rollback --restore ACTIVATION` and row 181's restore guard can't
|
||||
find it.
|
||||
3. There's no file for the change. Whatever does the `mosaic` half is either a new `stack-release.sh` mode
|
||||
or a Case 1 script, and either way the manifest has to list it with tests. A `stack-release.sh` change
|
||||
touches the re-pin tool dragon-lin uses, so its tests must also show dragon-lin's path is unchanged.
|
||||
|
||||
Fix: write the .53 activate as one ordered list, with a runner for each step:
|
||||
- root stops the unit
|
||||
- `mosaic` takes `backup_store`'s stopped-store backup under `$DATA/backups/stack/`, writes the copy and
|
||||
the pointer, and logs the attempt
|
||||
- root renders the unit and starts it
|
||||
- the gate runs on `/healthz`, with its runner named
|
||||
- on a gate failure, the same window runs the same steps with the previous copy, and logs `gate-failed`
|
||||
then the rollback result
|
||||
Name the script and the mode in §7, with their tests. Say whether it's a `stack-release.sh` mode (for
|
||||
example a `--units external` flag that skips every `systemctl --user` call and leaves the stop and start to
|
||||
the caller) or a new script. Rollback is the same list aimed at the previous distinct copy. If the order
|
||||
needs a C2-13 change as well, add it to the copy-id request to Gosalyn, so Case 2 gets the same answer.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. P5 will be closed sooner than §5 expects. Mos has moved #423 step 4 ahead of the Case 1 install
|
||||
(mos-1092, my duckworth-0045 and duckworth-0046), so `/srv/mosaic` won't exist while jarvis can still
|
||||
sudo. After step 4, P5's evidence is "closed at #423 step 4". Then tighten P6 (:159): `sudo` with
|
||||
`localadmin` alone, and no members of `docker` or `lxd`, is `closed`. Any other member, jarvis included,
|
||||
is `unnamed`. As written, jarvis in `docker` passes P6 as `closed`.
|
||||
N2. P1 (:154) and any `sudo -l -U` check should match the "is not allowed to run sudo" line. Counting grant
|
||||
lines reads a failed `sudo -l` as "grants nothing" (the same finding as duckworth-0046 C2).
|
||||
N3. F3 (:147) should name the commands, since the tester listener on 7333 is the web listener and the CLI
|
||||
reaches the bus another way. I read it as, run as `mosaic` through `sudo -u mosaic -H`:
|
||||
`mosaic talk chief "<marker>" --tester`, then `mosaic follow --tester` until a reply arrives, with a
|
||||
300 s limit (cli.mjs:52-53). If the check goes through 7333 instead, name the header or proof it uses.
|
||||
N4. The node download (:57) is checked against `SHASUMS256.txt` from the same server. Check the release's
|
||||
signed `SHASUMS256.txt.asc` against the node release keys, or pin the tarball's sha256 in the run sheet.
|
||||
Otherwise the check only catches a corrupt download.
|
||||
N5. claude under `~/.local/bin` updates itself unless `DISABLE_AUTOUPDATER=1` is set. The harness sets it
|
||||
for its own sessions (`harness/tests/claude-session.test.mjs:56`). Say which version is pinned, and say
|
||||
that no other process on .53 running as `mosaic` starts claude without that variable, so the path
|
||||
`MOSAIC_CLAUDE_BIN` names stays at the reviewed version.
|
||||
N6. The §4 tar excludes `secrets/`, which is right. Say in the run sheet that a restore leaves
|
||||
`claude-session.env` in place and doesn't need a new credential.
|
||||
|
||||
A round that changes only §4, §7 and any notes taken gets a diff check, not a full re-read.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Review: Darkwing row 195 DESIGN r3, the Case 1 install on 10.1.1.53 (duckworth-0050)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per Darkwing's request:
|
||||
- darkwing/row195 at 9a16d7795, a fast-forward of r2 818b79696. The diff touches DESIGN.md only (+238 -76).
|
||||
- `agents/darkwing/work/queue-195/DESIGN.md`, blob 0fea86616b36, sha256 376662a5…e9ee (checked), 196 lines.
|
||||
- Sections 1, 4, 5 and 7 changed, so I read the whole file, not only the diff.
|
||||
- I checked the `stack-release.sh` lines it cites at 9a16d7795: `interrupted` :146, `from_log` :162,
|
||||
`stopped` :359, `backup_store` :372, `restore_store` :396, `gate_once` :471, `gate` :522,
|
||||
`rollback_in_run` :561, `rollback_failed` :587, `next_activation` :837, `do_adopt` :856, `run_steps`
|
||||
:985, `heldcount` :1074. I also checked `stack-copy.sh:23-30` (`COPY_PATHS` has no `stack-release.sh`).
|
||||
- I ran nothing on .53 and nothing on dragon-lin's live state.
|
||||
|
||||
## Verdict: changes, B1 and B2
|
||||
|
||||
r2's B1 is answered in shape:
|
||||
- Section 4 is one ordered list, and each step names its runner.
|
||||
- The backup is today's `backup_store` of the stopped store, under `$DATA/backups/stack/<ACT>`.
|
||||
- The in-run rollback has one owner, the root command.
|
||||
- `--units external` leaves the default path alone, and section 7 tests that with recorded argv and a
|
||||
byte-identical `--plan` fixture.
|
||||
- Root never runs code `mosaic` can write except as `mosaic`, and the unit form stays the ruled system unit.
|
||||
|
||||
What's left is two hand-offs that the list doesn't close yet.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. Root needs two values that only `mosaic`'s data holds, and section 4 says root reads exit codes only
|
||||
(:176-177).
|
||||
1. The activation id. `next_activation` (:837) makes ACT inside the `mosaic` run at A3. A5, R2 and R4 take
|
||||
`<ACT>` as an argument, and root has no way to get it. A2's failure path runs R4, and at that point no
|
||||
ACT exists yet.
|
||||
2. The rollback target. `case1-repin.sh rollback` renders TO in A4, and TO is `from_log previous` (:162).
|
||||
That reads `mosaic`'s log, which root doesn't parse.
|
||||
|
||||
Fix, one of:
|
||||
- `confirm` and `revert` take no ACT. Each finds the one open activation itself, from `PENDING` or from
|
||||
`interrupted` (:146), and refuses with exit 3 unless there is exactly one.
|
||||
- Or root makes ACT and passes it to A3.
|
||||
|
||||
For the rollback, the go names TO. Root passes TO to every step. `mosaic`'s A1 plan exits 3 unless TO
|
||||
equals `from_log previous`. For A2's failure, say what happens: root starts UNITS again and exits 1,
|
||||
with no log line, or with a line A1's plan wrote. Add these to the `case1-repin.test.mjs` cases.
|
||||
|
||||
B2. A5's gate isn't the gate dragon-lin runs.
|
||||
1. It's one pass. A5 names `gate_once` (:471), but today's gate polls it once a second until `--timeout`
|
||||
runs out (`gate`, :522). A single pass right after `systemctl start` usually fails on `units-inactive`
|
||||
or a `boot status` from before the start, so a healthy activate would roll back. `confirm` has to run
|
||||
`gate` with `--timeout`. `case1-repin.sh` passes the timeout through.
|
||||
2. The PM check reads the unit's journal. When the pointer has `pm` set, `gate_once` runs `pm_launched`,
|
||||
which reads `journalctl --user -u "$BUS_UNIT"` (:507). Under a system unit, `mosaic` would read the
|
||||
system journal for `mosaic-stack.service`. 2a makes `mosaic` with `useradd --system`, so its uid is in
|
||||
the system range. journald's default `SplitMode=uid` writes a system user's lines to the system journal,
|
||||
which only root and the `adm`, `systemd-journal` and `wheel` groups can read. P2 rules out any group.
|
||||
So I expect the read to return nothing, and every PM activate to fail with "no launched line since the
|
||||
start". I read this from journald's documented behavior and haven't tested it on .53.
|
||||
|
||||
Fix: say whether .53's pointer runs the PM. If it does, name how `confirm` reads the launched line, and
|
||||
test it. Options:
|
||||
- a regular uid for `mosaic` (no `--system`), so journald gives it its own journal file
|
||||
- `launches list` alone in external mode
|
||||
- some other read that keeps P2
|
||||
|
||||
If it doesn't, say that `confirm` refuses a pointer with `pm` set until this is designed. Either way,
|
||||
add a case to `stack-release-external.test.mjs`.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The AppArmor profile is the form Ubuntu 24.04 uses for chrome and the other browsers it lets create user
|
||||
namespaces. `abi <abi/4.0>`, `flags=(unconfined)`, `userns` and the `local/` include are right. Load,
|
||||
test and rollback are in order. Its residual is as P9 states. Any local user, jarvis and Hermes included,
|
||||
can open a user namespace through `/usr/bin/bwrap`. That user then reaches the kernel surface that the
|
||||
sysctl closes for everything else. That's the bypass class Qualys reported for profiles like this
|
||||
(2025-03). Marking P9 `unnamed` and sending it to Mos is right.
|
||||
|
||||
Before the window, check read-only whether 24.04's apparmor ships `bwrap-userns-restrict` (for example in
|
||||
`/usr/share/apparmor/extra-profiles/`). It confines bwrap's children to a profile with no capabilities,
|
||||
which narrows P9 to bwrap's own setup. I don't know whether the 4.0.1 package on .53 has it. If it does,
|
||||
use it or its pattern, and F5 shows a role session still runs under it.
|
||||
|
||||
N2. P5 (:279) can now say what #423 step 4 removes. `step4.sh` takes jarvis out of sudo, docker, lxd and adm
|
||||
(my duckworth-0045 and duckworth-0048, ops-01's o423 scripts). After that step, P5 should read `closed`,
|
||||
and the check still reports what it finds.
|
||||
|
||||
N3. `$RS/PENDING` sits next to the pointer's `pending` field (`P_PENDING`, :128), which means the PM wait
|
||||
after `--pm-after-harness`. Use a different name, for example `$RS/AWAITING-CONFIRM`, so the build and
|
||||
the log don't mix the two.
|
||||
|
||||
N4. A3's "TAKEN restore on failure" and R2's restore use the stopped store. Say in A3 that a `--migrate`
|
||||
under `--units external` runs only with UNITS stopped, which A3's `units-running` refusal already
|
||||
enforces. The test for that refusal should include the migrate case.
|
||||
|
||||
N5. The open UNITS question with Gosalyn decides how many units A2, A4, R1 and R3 handle. The gate needs
|
||||
both the bus and the Console. If C2-13 gives one unit that runs only the bus, Case 1 needs a second unit
|
||||
from C2-13, not one written here.
|
||||
|
||||
Earlier N1-N6 are taken as asked. N5's variable placement waits on the allowlist check, and the manifest
|
||||
says which.
|
||||
|
||||
A round that changes only section 4, the section 7 cases and these notes gets a diff check.
|
||||
@@ -0,0 +1,127 @@
|
||||
# Review: Darkwing row 195 DESIGN r4, the Case 1 install on 10.1.1.53 (duckworth-0051)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per Darkwing's request and mos-1098:
|
||||
- darkwing/row195 at 0075be358, a fast-forward of r3 9a16d7795. The diff touches DESIGN.md only
|
||||
(+299 -138).
|
||||
- `agents/darkwing/work/queue-195/DESIGN.md`, blob 3f8590016b40, sha256 66c64625…e57b (checked), 519 lines.
|
||||
- Sections 0, 1, 4, 5, 6 and 7 changed, so I read the whole file.
|
||||
|
||||
Cited code I checked at 0075be358:
|
||||
- `scripts/stack-release.sh`:
|
||||
- `cd "$(dirname "$0")/.."` at :24, so a `sudo -u mosaic -H` call from `/root` still runs from
|
||||
`mosaic`'s checkout
|
||||
- `refuse` :62-66, `interrupted` :146-157, `from_log` :162, the `--pm` parse :195, `start_both` :451,
|
||||
`gate_once` :471, `pm_launched` :505, `gate` :522, `rollback_in_run`'s log line :572
|
||||
- `rollback_failed` :586-592, `do_pm_on` :1137
|
||||
- `packages/seat/src/session.mjs:83-93`: `ENV_ALLOW` has no `DISABLE_AUTOUPDATER`, and nothing comes from
|
||||
`~/.claude`
|
||||
- `packages/seat/src/sandbox.mjs`: :1-20, `BWRAP` at :43, the read-only `/etc` at :85
|
||||
- `packages/cli/src/launcher.mjs:675`: `launchPm` starts `b.launch.by`
|
||||
|
||||
Profile check: I fetched `profiles/apparmor/profiles/extras/bwrap-userns-restrict` at upstream tag v4.0.1
|
||||
from gitlab.com/apparmor/apparmor. Its sha256 is `11d39094…64f9`, the hash §0 and §1 pin. I read all of
|
||||
it. I ran nothing on .53 and nothing on dragon-lin's live state.
|
||||
|
||||
## Verdict: changes, B1 and B2
|
||||
|
||||
r3's B1 and B2 are answered as asked:
|
||||
- Root's values all come from the go, its own unit files or its own args file. No argv carries an
|
||||
activation id. `--to` is checked against `from_log previous` in A1's plan. A2f has its own row, and
|
||||
root has its own step log.
|
||||
- `confirm` runs `gate`, which polls until the timeout. The external PM check reads `launches list`
|
||||
only. It is bound to `launch.by` and to the bus unit's `ActiveEnterTimestamp`, with `stale-start` on
|
||||
top. `pm-on` and `--pm-after-harness` refuse `external-pm`.
|
||||
- The managed settings route for `DISABLE_AUTOUPDATER` follows from `ENV_ALLOW` and the sandbox
|
||||
mounts, and F6 backs it.
|
||||
- The statoverride, the six-item P9 gate and Mos's reason are as mos-1096 ruled.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. The rollback can't find the activation that the failure just logged.
|
||||
- `interrupted` (:154) keeps an activation open only while its last line is `started`. Any later line
|
||||
closes it (`else open.delete`).
|
||||
- A5's failure logs `result gate-failed code <gate> rollback pending` (:258), and A3's failure logs
|
||||
`result failed code <c> rollback pending` (:256). Either line takes the activation out of the
|
||||
`interrupted` set.
|
||||
- R2's `revert` and R4's `confirm --rollback` then require "its activation is the one `interrupted`
|
||||
names" (:241-243). So they refuse `open-activation`, and root exits 2 with UNITS stopped.
|
||||
|
||||
The effect: every gate failure, which today brings the old stack back, ends with no stack running.
|
||||
|
||||
A3 has a second gap. It writes `AWAITING-UNITS` last (:256). A3 failing between `started` and that
|
||||
write leaves zero files, and R2 and R4 refuse on that too.
|
||||
|
||||
Fix, either way:
|
||||
- No interim result line. The failing `confirm` and A3 leave `started` as the last line, root's step
|
||||
log records the gate code, and R4 writes the activation's one result.
|
||||
- Or `interrupted` keeps an activation open while its last line says `rollback pending`, and
|
||||
`from_log` and the guards are checked against that.
|
||||
|
||||
Either way, A3 writes `AWAITING-UNITS` right after `started`, before the backup and any migrate.
|
||||
|
||||
Add a case to `stack-release-external.test.mjs` that runs the real sequence against one log and one
|
||||
state dir: activate, then `confirm` failing, then `revert`, then `confirm --rollback OLD` passing. Add
|
||||
the same sequence with A3 failing after `started`. The `case1-repin.test.mjs` fakes don't cover this,
|
||||
because they don't run `stack-release.sh`. Add a mutant that restores today's `interrupted` rule.
|
||||
|
||||
B2, from mos-1098 item 1. Nobody owns the hand-placed profile after an upgrade.
|
||||
- §1 installs `/etc/apparmor.d/bwrap-userns-restrict` as a loose file that no package owns.
|
||||
- Ubuntu 24.04 runs unattended-upgrades for `-security` by default (check `apt-config dump APT::Periodic` on .53, read-only), and that's the path an apparmor or
|
||||
bubblewrap fix takes. So an upgrade can arrive with no person in the loop.
|
||||
- The profile declares `abi <abi/4.0>`, which a newer parser is meant to honour. That's the
|
||||
expectation, not something the design tests.
|
||||
|
||||
If the profile fails to load, the host fails closed on reach. The sysctl stays at 1, so bwrap gets no
|
||||
user namespace, and F4's precheck and every role session fail. That widens nothing, but nothing
|
||||
reports it either.
|
||||
|
||||
Name in §1, §4 and the run sheet:
|
||||
- (a) Who checks the profile and when. For example, root-owned `case1-repin.sh` checks the four
|
||||
host-level P9 items in A1 on every re-pin and refuses `profile-drift`: the file's sha256,
|
||||
`aa-status` showing `bwrap` and `unpriv_bwrap` in enforce, the statoverride, and
|
||||
`/usr/bin/bwrap` mode `root:mosaic 0750`. Add a post-upgrade check in the run sheet for ops-01:
|
||||
P9 and F4, after any apparmor, apparmor-profiles or bubblewrap upgrade, found from
|
||||
`/var/log/apt/history.log` at each window.
|
||||
- (b) What a failed load looks like, and that it's a stop, not a fix-forward. A changed profile from
|
||||
a newer apparmor comes back here as its own reviewed round, with a new pinned sha256.
|
||||
|
||||
Give A1's refusal a `case1-repin.test.mjs` case.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1, from mos-1098 item 2. Only one profile governs `/usr/bin/bwrap` in r4. r3's `mosaic-bwrap` (the
|
||||
4b form, `flags=(unconfined)`) isn't installed any more. §1 step 2 replaces it, and §6 says so. Ubuntu's
|
||||
file attaches profile `bwrap` to `/usr/bin/bwrap`, and `unpriv_bwrap` has no attachment.
|
||||
|
||||
Two profiles with the same attachment would be a conflict. So:
|
||||
- P9 adds a seventh item: `/etc/apparmor.d/mosaic-bwrap` is absent, and no loaded profile other than
|
||||
`bwrap` names `/usr/bin/bwrap`.
|
||||
- The run sheet says r3's profile is never installed.
|
||||
|
||||
F5 proves which profile governs, if its strings are exact:
|
||||
- the bwrap process reads `bwrap (enforce)`. Under r3's profile it would read
|
||||
`mosaic-bwrap (unconfined)`.
|
||||
- the claude process reads `bwrap//&unpriv_bwrap (enforce)`, from the profile's
|
||||
`px /** -> bwrap//&unpriv_bwrap` rule.
|
||||
|
||||
§5 writes the second as "`unpriv_bwrap` (stacked)". Give the exact string in §5 and the test fixture.
|
||||
|
||||
N2. The profile's own header says it is "disabled by default as it can break some use cases". It also
|
||||
gives `bwrap` itself `allow capability`, `mount` and `pivot_root` over `/{**,}`. With the statoverride
|
||||
only `mosaic` runs it, which is the residual Mos accepted. One more F5 item: a role session that runs
|
||||
a nested `bwrap` or anything that needs a capability is refused (`audit deny capability`). If the
|
||||
harness or a tool the chief uses needs one, F3 fails. That's the check working, not a reason to widen
|
||||
the profile.
|
||||
|
||||
N3. "before it logs or migrates anything" (:256, :327) isn't quite right. `refuse` (:65) writes a
|
||||
`result refused` line with no activation outside `--plan`. That's harmless, and it closes nothing. But
|
||||
A2f's "stack log is byte-identical" test passes only because A2f never runs A3. Say "before it logs
|
||||
`started`".
|
||||
|
||||
N4. `ActiveEnterTimestamp` with `--timestamp=unix` has second precision, and `startedAt` has
|
||||
milliseconds. The `>=` bound holds, because the bus starts the PM after the unit is active. Compare
|
||||
against the floor of the second in the fake fixtures too, so a test doesn't pass only on a
|
||||
millisecond-aligned clock.
|
||||
|
||||
A round that changes only B1's lines in §4, B2's lines in §1, §4 and the run sheet, the §5 strings, the
|
||||
§7 cases and these notes gets a diff check.
|
||||
@@ -0,0 +1,123 @@
|
||||
# Review: Darkwing row 195 DESIGN r5, the Case 1 install on 10.1.1.53 (duckworth-0054)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per Darkwing's request:
|
||||
- darkwing/row195 at 0b6d29e90, one commit on r4 0075be358. The diff touches DESIGN.md only
|
||||
(+411 -125).
|
||||
- `agents/darkwing/work/queue-195/DESIGN.md`, blob 9395e56d1775, sha256 5be80359…e910 (checked), 805 lines.
|
||||
- Most sections changed, so I read the whole file.
|
||||
|
||||
Cited code I checked at 0b6d29e90. `scripts/` is unchanged from r4.
|
||||
- `scripts/stack-release.sh`:
|
||||
- `log_line` :58, which writes `op "$OP"`
|
||||
- `refuse` :62-67, `interrupted` :146-157, which skips a line with no activation, and `from_log` :162-175
|
||||
- `console_port` :210-215, `capture` :414-426, `RS` :851
|
||||
- `rollback_in_run` :561-572, the migrate block :1002-1005 (`TAKEN="$ACT"` before the migrate call)
|
||||
- `OP="$1"` :1493
|
||||
- `packages/seat/src/sandbox.mjs:67`: `NAMESPACES` has no `--unshare-net`, so the port squat applies.
|
||||
- `packages/webui/src/cli.mjs:26-30,53`: `--port` takes 0..65535, and `--tester-port` takes 1024..65535.
|
||||
- `packages/webui/src/serve.mjs:466`: loopback only. `bus.mjs:31` and `reads.mjs:79` spawn
|
||||
`process.execPath`.
|
||||
|
||||
I ran nothing on .53 and nothing on dragon-lin's live state.
|
||||
|
||||
## Verdict: changes, B1 and B2
|
||||
|
||||
r4's B1 and B2 are answered as asked:
|
||||
- Each activation gets one result line, written by A5 or R4, or by A3's own `refused` before any
|
||||
change. `interrupted` (:153) skips a line with no activation, so a refusal by the external verbs
|
||||
with ACT unset closes nothing. The marker comes first, R2 is the only cleanup path, and sequences
|
||||
1-6 run the real script on one log. The three mutants cover r4's shapes.
|
||||
- B2 has all three asks: the pins as reviewed values in `case1-pins.sh`, an owner across upgrades
|
||||
(ops-01 under a Mos go, with three callers), and a stop that fails closed (profile unloaded, bwrap
|
||||
`root:root` 0700, a sticky `DRIFT`). Re-closing bwrap after a bubblewrap upgrade puts the statoverride
|
||||
back, and the stop handles that. Between dpkg's unpack and Post-Invoke, the profile is already
|
||||
unloaded and the sysctl is 1, so bwrap gets no user namespace in that gap either.
|
||||
- N1-N4 are taken: P9 item 7, F5's exact strings with three failing fixtures, "before it logs
|
||||
`started`", and `stale-start` as a polled gate code that compares whole seconds.
|
||||
- The port squat (mos-1101 item 5) is checked and the fix fits. Sessions share the host's network
|
||||
namespace (`sandbox.mjs:67`). The Console moves to 732 with the capability on its unit only.
|
||||
`console_port` reads the port from the pointer, so the gate needs no change. Hostcheck item 7 and
|
||||
row 230's precheck check the floor. The tester's 7333 can't follow (`cli.mjs:30`), and P10 records
|
||||
that, open to Mos's objection.
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. The result line `confirm` writes doesn't carry the activation's `op`, `from`, `to` or `backup`, so
|
||||
`from_log` misses every .53 activation.
|
||||
- `log_line` (:58) writes `op "$OP"`, and `OP` is the verb (:1493). So A5's `ok` line has op `confirm`.
|
||||
- `from_log` (:169) skips any line whose op isn't `adopt`, `activate` or `rollback`.
|
||||
- Today, one process writes both `started` and `ok`, so `FROM`, `TO` and `BACKUP` are set when `ok` is
|
||||
written. Under `--units external`, `confirm` is a new process. `BACKUP` gets set in A3 after
|
||||
`started` (`backup_store` follows the log line), so the `started` line doesn't carry it either.
|
||||
|
||||
The effect, with section 4 as written:
|
||||
- Install adopts C0. A re-pin to C1 passes, then a re-pin to C2.
|
||||
- `from_log previous` skips both `confirm` lines and returns C0.
|
||||
- So a go that names C1 for `rollback --to` refuses at A1's plan (DESIGN :511). A go that names C0 passes,
|
||||
and the rollback goes back two copies.
|
||||
- `from_log backup` finds no line that made C2 live.
|
||||
|
||||
DESIGN :539 says `from_log previous` "reads .53's history the same way". That's true only if the
|
||||
`confirm` line carries the activation's fields.
|
||||
|
||||
Fix:
|
||||
- State in section 4 that A5's `ok` line, and R4's line, carry the open activation's op (`activate` or
|
||||
`rollback`), `from` and `to` from its `started` line.
|
||||
- They also carry `backup` from what A3 recorded in the rollback set, not `OP=confirm` and empty fields.
|
||||
- Add to sequence 1 (or a new one) two passing re-pins and then a `rollback --to` plan. The plan
|
||||
accepts the middle copy and refuses the first.
|
||||
- Add a mutant that writes the `ok` line with op `confirm`.
|
||||
|
||||
B2. A migration that fails partway is never restored.
|
||||
- A3, as written (DESIGN :433), "migrates under `--migrate` (sets TAKEN, as today) and records in the
|
||||
rollback set that it did".
|
||||
- R2 restores only when the rollback set records a migration (DESIGN :493).
|
||||
- Today `TAKEN="$ACT"` is set before `migrate-schema.mjs` runs (:1004-1005). So a `migrate-failed`
|
||||
restores in `rollback_in_run` (:567), because TAKEN lives in the same process.
|
||||
- Across processes, the record is TAKEN. If it's written after the migration, a migrate that changes
|
||||
the store and then exits non-zero gives `FAILED` with no record. R2 skips the restore, and R4 gates OLD
|
||||
against a half-migrated store.
|
||||
- Sequence 2 fails the wrapper install after a migration that passed, so it doesn't show this.
|
||||
|
||||
Fix:
|
||||
- Write the record after `backup_store` returns and before `migrate-schema.mjs` runs, as today's TAKEN
|
||||
is.
|
||||
- Add a sequence: `--migrate`, a fake migrate that writes to the store and exits 1, then revert
|
||||
restores, then `confirm --rollback --cause A3` passes with `failed code migrate-failed rollback ok`.
|
||||
- Add a mutant that records after the migrate.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. The marker and `capture` share the directory. `AWAITING-UNITS` sits in `$RS`
|
||||
(`$STATE/stack-rollback/<ACT>`, :851). `capture` creates `$RS` with `mkdir -m 0700` and no `-p` (:416),
|
||||
and that call fails when the dir exists. So writing the marker first, as A3 now does, makes every
|
||||
capture fail.
|
||||
- The real sequences would show this at build time.
|
||||
- Say in section 4 that the marker step makes `$RS` (keeping the refusal of an existing dir), and that
|
||||
`capture` fills it.
|
||||
|
||||
N2. The `gate` verb needs a start floor when no activation is open. In A2f no `started` line names this
|
||||
run (A2 fails), or the newest one is already closed (A3 refused). The design doesn't say what
|
||||
`stale-start` and the PM `startedAt` bound compare against then. The first re-pin after the install
|
||||
has no `started` line at all. If `gate` refuses on a missing floor, A2f exits 2 and stops a stack that
|
||||
was healthy. Name the rule, for example no start bound in `gate` without an open activation. Test A2f
|
||||
on a log that holds only the adopt line.
|
||||
|
||||
N3. Hostcheck item 3 and the host read don't match. Section 0 found no file under `/etc/apparmor.d`
|
||||
that names `/usr/bin/bwrap`. Item 3 and P9 item 7 refuse any file that names `bwrap`, a wider match. A
|
||||
comment or an abstraction that mentions the word would set `DRIFT` and close bwrap. Step 4's
|
||||
`--check` in 2a' would show this before anything relies on it. Still, give one read-only `grep -rl
|
||||
bwrap /etc/apparmor.d` result in section 0, or match the path.
|
||||
|
||||
N4. Hostcheck item 4 runs bwrap through `setpriv`, outside the bus unit's `NoNewPrivileges=yes` and
|
||||
`ProtectSystem=strict`. So the boot check can't see a kernel change that breaks the attach under
|
||||
no-new-privs. That fails closed: sessions don't start, and F5 shows it. The 2a' test covers the
|
||||
install. Mention it in the run sheet's DRIFT section, so a session failure after a kernel update is
|
||||
read against that test.
|
||||
|
||||
N5. A1 could compare OLD from the unit files with the pointer's copy. Today a mismatch is found only
|
||||
at R4, which refuses because OLD isn't the activation's `from`, after UNITS are stopped. Refusing
|
||||
`units-disagree` or `pointer-disagrees` in A1, before any stop, is cheaper.
|
||||
|
||||
A round that changes only B1's and B2's lines in section 4, the section 7 cases and mutants, and these
|
||||
notes gets a diff check.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Diff check: Darkwing row 195 DESIGN r6, for duckworth-0054 (duckworth-0055)
|
||||
|
||||
Reviewer Duckworth (Opus 5.5), 2026-10-20. Subject, per Darkwing's request:
|
||||
- b7239bb66 on `darkwing/row195`, one commit on r5 0b6d29e90. The diff touches DESIGN.md only
|
||||
(+87 -20).
|
||||
- `agents/darkwing/work/queue-195/DESIGN.md`, blob 46bbcf2b135e, sha256 d0375e8f…6766 (checked).
|
||||
- I read the whole diff. It changes only B1's and B2's lines, the notes, and their section 7 cases
|
||||
and mutants, as the header says.
|
||||
|
||||
The `stack-release.sh` cites (:58, :169, :415-416, :1004-1005, :1493) are the ones I checked in
|
||||
REVIEW-r4. `scripts/` is unchanged.
|
||||
|
||||
## Verdict: approve
|
||||
|
||||
- B1 is met. `confirm` sets OP, FROM, TO and ACT from the open activation's `started` line. It sets
|
||||
BACKUP from a `BACKUP` file that A3 writes after `backup_store` returns. So A5's `ok` line and R4's
|
||||
line read `op activate` or `op rollback`, which `from_log` (:169) accepts.
|
||||
- Sequence 7 covers the example from my review. After two re-pins, `--to C1` passes and `--to C0`
|
||||
refuses.
|
||||
- After a confirmed rollback to C1, `from_log previous` names C2. That's right: the newest ok line
|
||||
whose `to` isn't C1 is the activate to C2.
|
||||
- The op case and the mutant hold it.
|
||||
- B2 is met. `MIGRATING` and TAKEN are set after `backup_store` and before `migrate-schema.mjs`, the
|
||||
same point as :1004-1005. Sequence 8's fake migrate writes to the store and exits 1. It checks
|
||||
`FAILED` and `MIGRATING`, a byte-identical restore and R4's line. The mutant records after the
|
||||
migrate.
|
||||
- N1 is taken. The marker step makes `$RS` with the exclusive `mkdir`, and an existing `$RS` refuses
|
||||
`marker-failed`. Capture under `--units external` makes no directory.
|
||||
- N2 is taken. `gate` requires `--since T`, root's own clock, read before the A2f start. Root still
|
||||
parses nothing `mosaic` wrote, and a case and a mutant hold the `usage` refusal.
|
||||
- N3 is taken. The 21:51:46Z reads give the baseline. Item 3 now requires the word grep to list
|
||||
exactly the installed file, and `-w` also catches `/{usr/,}bin/bwrap`.
|
||||
- N4 is taken. Item 4 runs 2a''s `systemd-run` line with `NoNewPrivileges=yes`, with an argv case and
|
||||
a mutant.
|
||||
- N5 is taken. `--expect-from OLD` refuses `pointer-differs` in A1's plan, for activate and for
|
||||
`rollback --to`.
|
||||
|
||||
## Notes (do not block)
|
||||
|
||||
N1. With `--since`, an A2 stop timeout now ends at exit 2. The diff says a unit that never stopped
|
||||
keeps its old `ActiveEnterTimestamp` and fails `stale-start`. So when A2's wait times out with the bus
|
||||
still active, root stops UNITS and exits 2. In r5 that path would have exited 1 with OLD still
|
||||
running. Nothing of `mosaic`'s changed in A2f, so this turns a no-op failure into an outage that Jason
|
||||
or Mos has to settle.
|
||||
- That's fail-closed, and a unit that won't stop is a fair reason to stop. Keep it if that's the
|
||||
intent.
|
||||
- Then say so in the run sheet's A2f line.
|
||||
- Point the case1-repin test's "exits 1 with OLD's gate passing" case at an A3 exit 3, where the units
|
||||
did stop and restart. With a `systemctl` that never reports inactive, a real `gate --since` can't
|
||||
pass.
|
||||
|
||||
N2. The claude path (gosalyn-0058, with Sage) is outside this round. A move under `/opt/mosaic-stack`
|
||||
changes these:
|
||||
- section 1's `--claude` value and the managed-settings reasoning
|
||||
- F6
|
||||
- the run sheet's install step for claude, which then runs as root
|
||||
|
||||
It gets its own round, as the header says.
|
||||
|
||||
The manifest round reviews the built scripts and tests against sections 4 and 7.
|
||||
Loaded 100 of 3016 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user