Compare commits

..
Author SHA1 Message Date
jason.woltje d858c9f8b7 Detect binaries despite git attributes and reject unconfirmed reviews (#1825) 2026-10-24 01:40:16 -05:00
jason.woltje 704910ca3b Fix push-check binary checks from repository subdirectories (#1825) 2026-10-24 01:12:15 -05:00
jason.woltje cceea407e2 Add reviewed-binary push check and scratch regression suite (#1825) 2026-10-24 01:02:29 -05:00
jason.woltje b617b80f68 agents: register Codex seat Bradford for the push check row (#1825, mos-1572); doc only 2026-10-24 00:52:34 -05:00
jason.woltje 93200bb446 docs: brief for scripts/push-check.sh, the repo push check that passes a binary only when an approved review candidate lists its path and sha256 (mos-1572 option A); doc only 2026-10-24 00:52:13 -05:00
jason.woltje d16958bcfd docs: W brief D2 lets O2 rows build before 10-27 and land only on an idle slot, Opus 5.5 review once at landing (mos-1571); register Codex seats Glomgold (row 309, W1) and Gyro (row 310, W2); doc only 2026-10-24 00:26:11 -05:00
jason.woltje 48f1627be2 queue: revs 3789-3844 (sage briefs/reviewer moves 157/208/265, requests, notes and done for 208/326/265; goldie 265 and 326; poe 208; bentina 157 r1/r2; gladstone 208 approve; boyd 157 r1 changes, r2 approve, 265 approve; duckworth 208 approve; darkwing 157 r2 approve) 2026-10-24 00:19:05 -05:00
jason.woltje 17f056787f docs: brief template, landed evidence is plain text with no gzip or binary waiver (mos-1570); doc only 2026-10-24 00:16:26 -05:00
jason.woltje 200dd396c4 feat(bus): add human project rooms with addressed sends
(cherry picked from commit ef4c938532)
2026-10-23 23:57:31 -05:00
jason.woltje 537902d261 docs: DEFERRED closes ops-0282 B2-2 (row 326 landed) and carries row 208 N1, the 5 s user.js re-render; doc only 2026-10-23 23:56:43 -05:00
jason.woltje f58049de02 duckworth: row 208 (193d) second review approve (static, guards) on 4a4bef6c1, no bus change, every hidden control has a broker refusal, grant hint per-grant and self-only, N1 (duckworth-0302)
(cherry picked from commit c36a0dc0d0a9f762bc74078dd53cce53df75642a)
2026-10-23 23:30:38 -05:00
jason.woltje 33f49f7d5a docs(host): close five first-install run-sheet gaps (#1822)
(cherry picked from commit a1b13c60e1)
2026-10-23 23:30:30 -05:00
jason.woltje 4a4bef6c13 feat(webui): shared threads and per-grant viewer read-only (193d) 2026-10-23 23:22:19 -05:00
133 changed files with 4257 additions and 2546 deletions

No files matched your search

+13 -5
View File
@@ -5458,12 +5458,20 @@ Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault r
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 — Poe, row 208 / 193d (#1706), before: building Console shared list,
share controls and per-grant viewer read-only against approved design e2d161e2e.
Base ec86f2c84. Isolated tests and UI-guard mutants pending Sage CLEAR; no live action.
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 — Poe, row 208 / 193d (#1706), after: Console shared list, share/unshare
controls and live per-grant viewer UI implemented. Webui 418/418 and plugins
119/119 green under sage-3925 CLEAR; four UI guard mutants killed with viewer
bus bypass refusals. Prior old-test draft regression fixed under sage-3918;
missing root oauth4webapi corrected by pinned npm ci under sage-3924. Evidence:
agents/poe/work/queue-208/BUILD.md. Independent reviews and Sage's global gate
remain; no live state, schema or bus policy change.
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 | Goldie | Row265 / #1770 before, recorded during authorized verification | C2-09b has no project room verbs. On goldie/row265 at ec86f2c84, add only room.open/send/read, pure exported leading-tag parser, README verb rows and new bus tests. Sage-3921 fixes EOF-tag and message-only timestamp readings; tester list/helpers/schema stay unchanged. Claim rev3814; Sage-3932 CLEAR for serialized new files, whole bus and twelve refusal mutants. No old-test edits authorized; stop rules apply.
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 | Goldie | Row265 / #1770 first slot STOP (sage-3932) | New parser14/14, rooms18/18 green; serialized bus stops at old project-runtime.test.mjs:14 import-closure pin, partial619 ok/1 not ok. New pure address.mjs import is absent from expected imports. Static trace also needs address.mjs in seat RUNNER_FILES. No old-test/product correction, no mutant/restored run, no green commit/push; old-pin and out-of-owned-files mount change sent to Sage for ruling. Audit prose missed the direct source pin despite raw grep hit; correction recorded in BUILD.md.
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.
2026-10-24 | Goldie | Row265 / #1770 verified (mos-1567, sage-3942) | Approved correction adds solely address import pin + Row265 comment to project-runtime.test and one pure-source RUNNER_FILES mount; new block checks parser has no imports. Serialized fresh-short-TMP timeout-k10: unchanged sandbox10/10, parser15/15, rooms18/18, full bus957/957, twelve named refusal mutants killed (each Node1, driver0), restored parser15/15 + rooms18/18. All positives zero fail/skip/cancel; every mutation restored full broker SHA71b32ee4. Old first-red evidence retained, no other old-test change. Slot returned goldie-0034; Boyd packet explicitly includes mount-list review, candidate push next.
-10
View File
@@ -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).
@@ -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
1 warning found (use docker --debug to expand):
 - 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
1 warning found (use docker --debug to expand):
 - 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
1 warning found (use docker --debug to expand):
 - 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
-363
View File
@@ -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 ]
+10
View File
@@ -0,0 +1,10 @@
# Bradford
Builder seat, Codex gpt-6.1-sol, started by Sage under Q246 and mos-1572. Owns the push check row:
`scripts/push-check.sh`, which passes a binary only when an approved review candidate lists its path and sha256
(docs/plans/2026-10-24_push-guard-binary-manifest.md, #1825).
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 `bradford/*` branches through the repository credential helper (mos-0981), never `refactor`,
`main` or another seat's branch. Poe gives the first Codex review and Dewey the Opus 5.5 landing review (mos-1542,
mos-1571). Registered by Sage on 2026-10-24 so the queue can name the seat as row owner (store.mjs isSeatDir).
+162
View File
@@ -0,0 +1,162 @@
# Row 327 — reviewed binary push check (#1825)
2026-10-24 Bradford, branch bradford/row327, base b617b80f6.
## Plan and boundaries
Implement the scratch check's ordered tokens in scripts/push-check.sh, using an
embedded Node program for byte-safe git paths and SHA-256 blob hashing. Bash is
the public entry point; Node is already a repository requirement. Git commands
use explicit resolved object IDs, disable external diff/textconv, and inspect
committed bytes. No queue writes, hook changes, binary evidence, or live state.
Fetch is opt-in. Read reviewed rows with the queue package rows() API rooted in
the caller's repository; this preserves canonical/witness/replay checks.
Only the latest round counts; any changes receipt blocks that round. A matching
approval authorizes exactly the manifest path/hash, or a path changed from the
commit candidate's first parent with matching committed blob hash and mode.
Deleted binaries need no candidate. Multiple --row values form a union.
Add scratch-only integration coverage in scripts/test-push-check.sh. Reuse queue
test fixture helpers to bootstrap real scratch queues, then use queue verbs to
open rounds and record receipts. All git writes, bare remotes and queues remain
under TMPDIR. Test fail-closed ordering and five required mutants, generated as
text copies in scratch. No old tests are modified. Add the TOOLS entry.
Every run needs Sage CLEAR; run timeout -k 10. Record plain-text results here or
in this packet. After green checks, commit and push only bradford/row327 through
the repository helper, send Sage and Poe tip/base/path sha256 manifest, and move
327 in-review. Sage owns request posting and landing; Dewey reviews once there.
## Recovery and choices
Initial worktree queue next refused linked-worktree access as designed. Queue
claim is performed from the canonical checkout; production reads in the guard
retain the same refusal rather than invent a worktree override.
Requested Sage ruling on AGENTS log appends outside the brief's owned paths.
Sage sage-3989: lead handles BUILD-LOG and SESSIONS at landing. Session entry:
2026-10-24 Bradford — row 327 / #1825 — implementation and scratch verification
in this packet; outcome to be appended after acceptance.
Implementation note: commit candidates use an explicit first-parent diff (empty
tree for a root commit), with rename detection disabled, like verify-commit.
The changes-veto case has both an approval and a changes receipt; a veto mutant
would otherwise still fail for lack of approval and provide false confidence.
A receipt for another candidate cannot be produced through queue verbs: the
suite corrupts only a scratch queue and expects the production reader to refuse.
Diff color, external diff and textconv are disabled so scans inspect plain git
output. Paths are read from NUL-delimited records (including spaces/tabs).
## Verification (Sage CLEAR sage-3991)
First new-suite run red: my whitespace-path fixture attempted a manifest path
that the queue intentionally forbids. Corrected the fixture to a commit
candidate, which can contain that git path. Production queue code and old tests
remain unchanged. Raw first-run output is test-run-1.txt.
Second run green: PASS push-check 22 checks; KILLED skip-hash,
accept-changes, earlier-round, working-tree, no-row. test-run-2.txt holds the
plain-text receipt. bash -n both scripts and git diff --check passed. Runs used
TMPDIR recorded in tmpdir.txt and timeout -k 10 240; all generated binary bytes
were in disposable scratch repositories, never in this checkout or evidence.
The slot was used for only the authorized new suite and syntax/diff checks.
No claim of the lead's full scripts/test-*.sh landing gate is made here.
Final inspection after the first green run found a path-decoding concern: lossy
UTF-8 decoding can conflate distinct git filenames. Hardened NUL-delimited path
decoding to reject invalid UTF-8, and quote newline-containing paths in refusal
output to preserve one STOP line. Added scratch cases for both. Requested a new
CLEAR (bradford-0005) because the previous slot had already been released.
Final run (sage-3994 CLEAR) green on final code: PASS push-check 24 checks,
all five mutants KILLED again, bash -n both scripts and git diff --check passed.
Receipt: test-run-3.txt. The guard rejects non-UTF-8 git paths as environment
errors rather than comparing replacement characters; newline paths remain
checkable through commit candidates and refusals quote them on one line.
Acceptance for the builder stage: ordered tokens and stop behavior, explicit
base/fetch, exact blob manifest/commit checks, latest-round/veto checks, deletion,
log/FF/secrets/attribution refusals, and all required mutants are covered. Queue
reads use rows() with production validation; tests use only scratch queues.
Candidate-mismatched receipts are impossible through the queue API and are
covered by a corrupt scratch queue refusing its read. The lead's full-suite gate,
Poe first review, Dewey landing review, and adoption for Sage's next push remain
landing stages. No hooks, role policy, credentials or other seats' paths changed.
## Poe first review R1 (poe-0010) — changes
cceea407e has a blocking subdirectory bypass: ls-tree without --full-tree can
omit an existing binary outside the caller cwd, making it look deleted.
Accepted the finding. Git now anchors all invocations at the repository root,
ls-tree uses --full-tree, and diff/diff-tree use --no-relative. EXPECTED_LOG still
resolves from the original caller cwd. Added outside-cwd and nested-binary cases
under diff.relative=true: unreviewed stops, reviewed manifest/commit passes.
No old test changed. New run requires Sage CLEAR before execution.
R1 verification under sage-3999 CLEAR: test-run-4.txt records PASS 29 checks
and KILLED all five required mutants. Syntax and diff whitespace checks passed.
A second bounded run used the new suite against cceea407e's script copied under
TMPDIR: r1-regression-red.txt records the outside-cwd unreviewed binary reaching
NO_BINARY/PUSH_READY (actual exit 0, expected 1). Thus the new regression detects
the reviewed bug, while the revised guard refuses it. No code changed between
the green run and this old-candidate reproduction. All runs used timeout -k 10.
Holding claimant queue writes per sage-3999 until Boyd finishes row 309's record.
Last acknowledged row 327 state is in-progress (bradford-0010, revision 3868).
## Dewey landing review B1 (dewey-0956) — changes
Read the invited review body at /mnt/storage/scratch/tmp/dew327vWXloCT/body.md.
B1 identifies a brief-level gap: numstat follows candidate/host git attributes,
so *.bin diff makes a NUL-containing blob disappear from the binary-only set.
The production script indeed filters numstat before reading any blob, consistent
with that data flow. Proposed correction: enumerate every changed numstat path,
skip absent/non-blob entries at SHA, classify binary when numstat says binary OR
committed blob has NUL among first 8000 bytes. Read with cat-file, never worktree.
Test committed .gitattributes, info/attributes, and core.attributesFile overrides
in scratch; reject with no row, allow exact reviewed bytes; add NUL-scan mutant.
Sage owns the brief amendment and next CLEAR/queue sequencing.
N1 acknowledged: candidate-equality check is defense in depth; invalid receipt
cannot pass queue replay, so the corrupt-queue case does not isolate that check.
N2/N3 recorded as described in review, without optional secrets/reader changes.
N4 planned hardening: refuse unconfirmed durable tail notes from rows() with
exit 2, with a real scratch witness-prefix case rather than a test-only reader.
No test run or queue write started for B1/N4 before Sage CLEAR.
Sage sage-4013/4015 authorizes B1 and N4 within the brief; amendment committed on
refactor at 9042754c4 (read with git show, no branch change). Fix and tests written
before requesting CLEAR as directed. Tests cover all three attribute sources,
approved manifest success under each override, exact 7999/8000 offset boundary,
and a real prefix-witness rollback to yield an unconfirmed approval tail.
Added only the requested sixth mutant, skip-nul-scan; the original five remain.
No new production configuration knobs, no secrets-diff change, no queue writes.
Will demonstrate red against the prior candidate using the new tests, then green
against revised code, under the approved slot. Full landing gate remains Sage's.
After Dewey's round-2 CHANGES record (comment 31522, rev3876), Sage sage-4016
released only the claimant return to in-progress. Acknowledged:
`ok bradford-0017 rev 3877 row 327 in-review→in-progress`.
No other queue write before the next in-review candidate move.
Test slot remains pending Sage CLEAR after Gyro's build.
## Amendment 1 verification (sage-4017 CLEAR)
b1-regression-red.txt: new tests against 704910ca3 reproduce attributes-hidden
asset.bin reaching NO_BINARY/PUSH_READY with exit0 instead of exit1.
n4-regression-red.txt: revised code with only the N4 check removed lets the real
scratch unconfirmed approval tail reach BINARY_REVIEWED/PUSH_READY, exit0 rather
than exit2. These are expected negative controls; neither changed committed code.
Final full new-suite run is green: test-run-5.txt records PASS 38 checks and
KILLED skip-hash, accept-changes, earlier-round, working-tree, no-row, skip-nul-scan.
All runs used timeout -k 10 240 with the same scratch TMPDIR; no old tests were
changed or run. bash -n both scripts and git diff --check also passed. No source
changes followed this green run. Sage's full landing gate is still separate.
Scope in the final delta: classify all changed present blobs using numstat OR
first-8000-byte NUL; fail closed on queue unconfirmed-tail notes; regression cases
and requested sixth mutant; TOOLS entry and this plain-text packet. B1's brief
amendment is Sage's 9042754c4; this branch does not edit the brief or queue files.
@@ -0,0 +1,27 @@
node:internal/modules/run_main:107
triggerUncaughtException(
^
AssertionError [ERR_ASSERTION]: LOG_OK
FF_OK
NO_BINARY
SECRETS_CLEAN
NO_ATTRIBUTION
PUSH_READY ee394f31d1e52751716e2413049f41b2f3072e65..76acc948149e718091fcd3f3807257e752a6616e
0 !== 1
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:100:7
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 0,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
Node.js v26.8.1
@@ -0,0 +1,27 @@
node:internal/modules/run_main:107
triggerUncaughtException(
^
AssertionError [ERR_ASSERTION]: LOG_OK
FF_OK
BINARY_REVIEWED 1
SECRETS_CLEAN
NO_ATTRIBUTION
PUSH_READY c1a8a1e9b8c3988874a8f7a35afab7c06aea94a5..325cd3671a7bf70b9a3aee60f4446bc93594a12a
0 !== 2
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:121:7
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 0,
expected: 2,
operator: 'strictEqual',
diff: 'simple'
}
Node.js v26.8.1
@@ -0,0 +1,27 @@
node:internal/modules/run_main:107
triggerUncaughtException(
^
AssertionError [ERR_ASSERTION]: LOG_OK
FF_OK
NO_BINARY
SECRETS_CLEAN
NO_ATTRIBUTION
PUSH_READY 896bdf0ef56596c67d2dc427275743c46026e832..ad9f9c86112fadafef4d4854f46bd9994fe2fe5d
0 !== 1
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:130:7
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 0,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
Node.js v26.8.1
@@ -0,0 +1,21 @@
node:internal/modules/run_main:107
triggerUncaughtException(
^
AssertionError [ERR_ASSERTION]: queue: candidate manifest path must be a repo-relative path: "space tab\tasset.bin"
2 !== 1
at Object.round (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:61:12)
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:116:7
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 2,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
Node.js v26.8.1
@@ -0,0 +1,6 @@
PASS push-check 22 checks
KILLED skip-hash
KILLED accept-changes
KILLED earlier-round
KILLED working-tree
KILLED no-row
@@ -0,0 +1,6 @@
PASS push-check 24 checks
KILLED skip-hash
KILLED accept-changes
KILLED earlier-round
KILLED working-tree
KILLED no-row
@@ -0,0 +1,6 @@
PASS push-check 29 checks
KILLED skip-hash
KILLED accept-changes
KILLED earlier-round
KILLED working-tree
KILLED no-row
@@ -0,0 +1,7 @@
PASS push-check 38 checks
KILLED skip-hash
KILLED accept-changes
KILLED earlier-round
KILLED working-tree
KILLED no-row
KILLED skip-nul-scan
@@ -0,0 +1 @@
/mnt/storage/scratch/tmp/YPrPwR
@@ -0,0 +1,78 @@
# Row 208 (193d) second review: poe/row208 4a4bef6c1 (duckworth-0302)
Request: sage-3934 (sage-3931 request, c31492 on #1706). Candidate: 4a4bef6c1 on ec86f2c84, one
commit. Design: e2d161e2e:agents/morgana/work/queue-193/DESIGN.md section 8, sha256 dd7fcb41
(matches). Scope under mos-1542: the guards only. The question is whether a viewer can widen
authority.
Method: static. Git reads, plus the mutant summary Sage sent by path. No node, no tests.
## Verdict: APPROVE (static)
The diff has no bus or schema change. Every write is still decided by the broker as it stood at the
base. The webui adds routes and hints, and no authority.
- **chat-http.mjs.**
- share and unshare are POST only. They come after the same write fences as every other write:
send and named mode, the SSO session or auth header, Origin, and sec-fetch-site.
- The body must be exactly `{to}`: a string of 1-200 characters with no CR, LF or NUL. Extra keys
(owner, verb) refuse 400, and the test covers that.
- shares is GET only and takes no query.
- The reply must echo thread, to and op exactly. Anything else is 503 outcome-unknown.
- The new STATUS codes map share-not-covered to 403, share-exists to 409 and share-not-found to 404.
- `?shared=` accepts only `true`.
- **chat.mjs and the adapter.**
- The three verbs go over `createChannelCaller` to the webui adapter (index.mjs:12-14). The adapter
is an allowlist passthrough to the session's own `call`.
- A granted user's chat is rebound per session (serve.mjs:701), so share calls carry the user's
principal, never the owner's.
- The test fixture runs the real adapter and the real Broker.
- **The broker decides each new path, unchanged from the base.**
- thread.share, thread.unshare and thread.shares are in USER_VERBS and not in VIEWER_VERBS, so
userLevel gives collaborator. A viewer gets access-refused at #grantedThread (broker.mjs:950).
- Each of the three goes through `#thread`'s owner check (broker.mjs:909-913). A sharee gets
thread-not-owned and can't reshare.
- share-not-covered applies to a `user:` target (broker.mjs:6559).
- Only thread.messages takes `#threadRead`, so a share gives read access and nothing else.
- **The grant-hint predicate.**
- access.mjs `chatGrants` re-runs `lookup`, so the subject must still sign in as session.user with a
live grant. It then keeps only that user's grants in this business that cover at collaborator,
which is the same `covers` call as the broker's `#granted`.
- It returns only `{projects, instances}`. No ids, receipts, subjects or other users escape. Any
failure gives [].
- The client's `scopeWrites` requires the project and the instance to match within one grant
(`some(g => project && instance)`). It never pairs a project from one grant with an instance from
another, so mixed grants can't combine into a scope that no single grant has.
- `includes('*')` on the client is equivalent to covers' `[0] === '*'`, because checkScope
(business access.mjs:97-103) refuses `*` unless it is alone.
- The hint is never carried over: chatGrants starts at [] and is re-read from /api/config on each
user render. It is in /api/config only when `granted`.
- **No client-only hiding stands in for a broker refusal.** Each hidden control maps to a refusal:
| Hidden control | Who | Broker refusal |
| --- | --- | --- |
| composer and resume | viewer | thread.send/open: access-refused |
| composer and resume | sharee | thread.send/open: thread-not-owned |
| share form | viewer | access-refused |
| share form | sharee | thread-not-owned |
| create thread | out of scope | collaborator check at create |
| create project | user | project.create isn't in USER_VERBS |
| session controls, clear, cancel | user | session.* isn't in USER_VERBS (tester-only) |
Test :136 POSTs create, send, open and share from the page with the UI bypassed, and checks four
403 access-refused responses, unchanged table counts, and that each verb reached the bus. Test :19
adds unshare and the shares read over HTTP. Test :154 covers the sharee's thread-not-owned on send,
open and share.
- **Mutants, as reported (sage-3920 CLEAR, /mnt/storage/scratch/tmp/8UlAhS/poe-208-mutants.json, not
rerun).**
- There are four served-copy mutants: composer, open, share and create. Each one's anchor must exist,
or the fixture throws.
- Each mutant fails the UI-absence test with ERR_ASSERTION, and the bypass test passes under each
one, so the broker still refuses. That meets design section 8: "With each UI check removed, the
bus still refuses."
- The full webui suite passed 418/0 and plugins 119/0 (sage-3925), as reported.
## Notes
- N1 (outside guard scope). user.js:16 re-renders every 5 s while the tab is visible. Each render
re-reads /api/config, projects, threads and the shared list. This doesn't affect authority, but it
is steady load per open user tab.
+9
View File
@@ -0,0 +1,9 @@
# Glomgold
Builder seat, Codex gpt-6.1-sol, started by Sage under Q246 and mos-1571. Owns row 309, O2 W1: the reviewed local
thesaurus data (docs/plans/2026-10-21_q195-writing-editor.md, "W1 reviewed local thesaurus data").
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 `glomgold/*` branches through the repository credential helper (mos-0981), never `refactor`,
`main` or another seat's branch. Claude seats orchestrate and give the Opus 5.5 landing review (mos-1571). Registered by
Sage on 2026-10-24 so the queue can name the seat as row owner (store.mjs isSeatDir).
+134
View File
@@ -0,0 +1,134 @@
# Row 265 / #1770 — project rooms
Current result: verified under sage-3942; slot returned in goldie-0034. Full bus 957/957, sandbox 10/10, parser 15/15, rooms 18/18, twelve mutants killed, restored files green. Review/push packet below includes mos-1567 closure scope.
Goldie, 2026-10-24. Branch goldie/row265, base ec86f2c847f6154a0677c6ed23c87dabc2c665d0. Brief docs/plans/2026-10-20_case2-rows.md § C2-09b rooms at blob3a5ed91a4fb3462df67e6939ae6f18290ac9b453; design agents/gosalyn/work/case2/DESIGN.md §6. Sage-3915 transfers ownership from Rocko to Goldie and review from Dewey to Boyd. No old test edit authorized. All tests/mutants wait for Sage CLEAR; queued behind Poe, Bentina and Sage's326 gate.
## Design and clarified readings
A room is a bus project. Members are all loaded business instances whose definitions are in THREAD_DEFINITIONS, sorted by key. Canonical room threads have those keys as names and belong to the requesting human. open checks all name/instance/owner collisions before creating anything; it delegates missing inserts to existing thread.create dispatch, including default session metadata. send/read require the complete canonical set. No schema or migration change.
Sage-3921 settles two brief readings: the single captured timestamp applies to the room.send MESSAGE rows; existing route/delivery/event helpers keep their own timestamps in the same dispatch transaction. They are unchanged. A final EOF-terminated @name is a tag, so bare @continuity is invalid-request rather than a broadcast. Leading whitespace is accepted. Pure parseAddress returns {tags,body}, where body is the unaddressed remainder; room.send stores the ORIGINAL input unchanged. Export it from the bus index and expose a separate reusable test table for C2-10c parity.
send resolves tags against current #named results and member instance keys, deduplicates recipients, and refuses multiple member matches. It checks each target's current default-session fence and messaging rule, then inserts one normal INFO message, route and human.input per target under the existing request transaction. All message rows share one at. No tag broadcasts; tags after the first non-tag token remain plain text.
read includes all sessions of the caller's canonical threads, globally ordered by messages.seq. Each entry retains first-message fields plus thread/session/instance and adds ids (every grouped message id) and to (each instance once). Only consecutive human rows with identical at/body/class group, and a repeated instance starts a new entry, so same-millisecond identical sends remain separate. Paging limit counts merged entries (THREAD_PAGE defaults/max); clients continue after the last ids element. Cursor must belong to a room thread. Bounded raw scan limit*memberCount+1 completes requested groups without reading unbounded history. Sage-3921 accepts these routine choices as proposed in goldie-0030.
## Scope / steps
1. Read current thread.create/send/messages, transaction wrapper, naming and messaging helpers; audit source readers/pins. Claim265 via queue after next. Done/read, claim recorded at rev 3814 with op goldie-0265-start-1 (same-op replay confirmed).
2. Add parser + index export, only three room cases in broker, README verb rows and new bus tests/fixtures. Do not modify existing thread/messaging/ACL/helper behavior.
3. New tests cover all brief cases, atomicity across message/route/input rows, ownership/projects, permissions, paging/grouping and session fences. No suites or test runs before CLEAR.
4. Record exact refusal→test→mutant matrix here before requesting the slot; one targeted killed mutant per named refusal, restoration byte-checks. New files whole then bus suite under CLEAR, serial timeout-k10, fresh31-byte mktemp TMPDIR plus UTC/load/busy header.
5. Before in-review, grep packages/*/tests for broker.mjs/TESTER_VERBS and changed-file pins. Push own branch and hand tip/base/manifest directly to Sage/Boyd. Sage owns request posting/landing.
No Console/Discord/CLI room surface or cross-agent reply visibility added. No tester verb, role, schema or secret change. No host action or credential access.
## New tests and refusal / mutant matrix
Only new test files and a new reusable vector fixture are added. No pre-existing test changes. Parser table: address.test.mjs consumes address-cases.mjs, including final EOF tags, leading whitespace, tags after the first word, repetition and blank/tag-only remainder. Broker cases: rooms.test.mjs covers sorted/reused canonical threads, other-instance and other-owner collisions, all three verbs' human/tester gates, missing projects/member set, partial/foreign/wrong-instance rooms, key and current-name addressing, ambiguity/same-member name+key, unknown/non-member/old-name tags, tags-only bodies, one/many/broadcast targets, original bytes, shared message timestamp, routes/deliveries/human.input, denied last recipient, clearing last session, all-session merged reads, agent reply metadata, merged paging/default/max, foreign/noncanonical/absent cursors, and same-at repeated sends. Synthetic historical rows append valid rows; they do not rewrite evidence or clocks. The same-at batches repeat the first recipient at each boundary, proving identical sends remain distinct.
Each named refusal has one mutation of its enforcement, run against the entire new rooms.test.mjs file. The driver mutants.py stores the exact original→mutant diff in logs/Mxx-*.diff, logs UTC/load/busy/TMPDIR, accepts only Node exit 1 with the named target test failing, and restores/verifies full broker bytes and SHA256 in finally. No mutation is retained. Generic pre-existing request/schema/argument policies are exercised through existing code; the matrix names the refusals added or deliberately inherited by these room cases.
| Mutant | Refusal | Guard relaxed | Required failing new test |
| --- | --- | --- | --- |
| M01-human | human-required | remove common room human check | room verbs require the human |
| M02-tester | tester-refused | add the three room verbs to TESTER_VERBS | room verbs require the human (explicit absence pin) |
| M03-project | project-not-found | remove room project lookup check | room verbs refuse missing projects |
| M04-members | thread-instance-missing | allow empty member set | room verbs refuse missing projects |
| M05-collision | room-thread-name-taken | remove canonical owner/instance collision check | room.open preflights instance collisions |
| M06-open | room-not-open | remove missing canonical thread check | send/read refuse a missing canonical member |
| M07-body | invalid-request | allow empty parsed remainder | unknown or non-member addressees and tags-only bodies |
| M08-unknown | unknown-addressee | allow zero member matches | unknown or non-member addressees and tags-only bodies |
| M09-ambiguous | addressee-ambiguous | allow multiple member matches | a legacy name colliding with another member key |
| M10-messaging | message-sender-refused | omit room target messaging check | messaging denial on the last target |
| M11-cursor | message-not-found | unknown room cursor falls back to seq zero | room.read groups one send, pages |
| M12-fence | session-clearing | omit room target session fence | a clearing last member fences the whole send |
For M02, the existing human check remains a second barrier: the mutant deliberately relaxes only the tester allowlist; the new explicit absence assertion kills it without loosening or running the old pin. Existing helpers keep their full session-state behavior. No helper or schema mutation ships.
## Reader / hash audit
`rg -n 'broker\.mjs|TESTER_VERBS' packages/*/tests --glob '!fixtures/*'` produced reader-grep.txt (133 hits at audit time). These are imports except the concrete source read at bus/tests/limit-checks.test.mjs:323-326 (forbidden limit-check/token-limit imports) and the tester set assertions at bus/tests/tester-writes.test.mjs:18-32. The latter stays byte-for-byte unchanged. bus/tests/viewer.test.mjs:116 builds its coverage set from exported verb lists; rooms do not widen those lists. No old test needs an edit.
Pin check: recursively inspect packages/*/tests plus scripts/test-*.sh for changed-file mentions (broker.mjs, address.mjs, packages/bus/README.md, packages/bus/src/index.mjs) with literal 64-hex values within eight lines: pin-context-grep.txt records no hits. A direct same-line hash/digest/sha256 grep finds registrySha256 imports only, which hash host registry data rather than broker bytes. The independent channel-proof.test.mjs baselines cover proof/transport files, none changed here. Positive control: pin-positive-control.txt records actual schema.sql / SCHEMA_SHA256 / createHash references in bus/tests/schema.test.mjs and bus/src/store.mjs. Schema bytes are unchanged.
## Requested serialized slot plan (not run)
At each run: fresh `TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)`, date -u, uptime, and `bash /mnt/storage/scratch/sage-busy-now.sh 2>/dev/null | tail -1` in the log. No concurrent test runs. Ignored root node_modules symlink targets the canonical checkout; it will not be committed.
1. `timeout -k 10 120 node --test packages/bus/tests/address.test.mjs` whole.
2. `timeout -k 10 120 node --test packages/bus/tests/rooms.test.mjs` whole.
3. `timeout -k 10 600 node --test --test-concurrency=1 packages/bus/tests/*.test.mjs` bus suite whole.
4. `python3 agents/goldie/work/queue-265/mutants.py`: M01 through M12 serially, each runs `timeout -k 10 120 node --test packages/bus/tests/rooms.test.mjs` whole, fresh TMPDIR/header; stop on a live mutant or wrong failure. Record each diff and restoration digest.
5. Restored address.test.mjs whole and rooms.test.mjs whole separately, same commands as 1/2 and fresh directories/header each.
Estimate 5–10 minutes; bus timeout ceiling 10 minutes, individual new/mutant ceilings 2 minutes each. On first new-test red, record/report it. On any old-test red or need for old-test change, stop and ask Sage. Static node --check for new modules/tests and git diff --check passed; no tests or mutants run yet.
## First slot STOP — sage-3932
2026-10-24T04:27Z: stopped on an old-test red. No old test changed or rerun, no mutant executed, no product adjustment after the failure, no commit/push claiming verification.
| Step | Exit | Counts / disposition | Log |
| --- | --- | --- | --- |
| 1 address whole | 0 | 14 pass, 0 fail/skip/cancel; 58.559 ms | logs/01-address.log |
| 2 rooms whole | 0 | 18 pass, 0 fail/skip/cancel; 1067.524 ms | logs/02-rooms.log |
| 3 bus whole | 1 (stopped) | partial 619 top-level ok, 1 not ok; not a completed-suite result | logs/03-bus.log |
| 4 twelve mutants | not run | stop rule | none |
| 5 restored files | not run | stop rule; no mutations were applied | none |
All three run headers use UTC/date, uptime, helper CLEAR and distinct 31-byte scratch TMPDIRs. Bus began04:25:22Z at load2.80/2.91/2.61, TMPDIR /mnt/storage/scratch/tmp/LaZAaK. Parser used /mnt/storage/scratch/tmp/rqcTH7; rooms has its own header. Explicit TAP reporter was selected for rooms/bus and the mutant driver so failure detection/counts are stable across Node26's default spec reporter; this changes reporting only.
The old red is project-runtime.test.mjs:12, assertion:14, test620, “Broker keeps the sandbox client import closure: only the listed imports, no lazy project import”. Actual imports include the new `import { parseAddress } from './address.mjs';`; the pinned array omits it. ERR_ASSERTION deepStrictEqual; individual duration2.469251ms. The full expected/actual/stack is in 03-bus.log. No other red was observed before stopping.
Reader-audit correction: reader-grep.txt:100 already contains project-runtime.test.mjs:13, a direct broker source read. The earlier prose incorrectly described the import-closure assertion as a normal import; I missed this old source pin during inspection. This is not a hash pin. Follow-up static trace finds seat/src/sandbox.mjs:50-60 RUNNER_FILES includes broker and landing but not address; seat/tests/sandbox.test.mjs:45-53 compares this mount list to the recursively imported source closure. A parser dependency therefore also needs the one new RUNNER_FILES path. Both an old-test pin change and this out-of-owned-files product change require Sage/Mos approval before edits. Recommended bounded change: add only the parser import to the project-runtime expected imports, add only packages/bus/src/address.mjs beside broker in RUNNER_FILES, and leave the generic sandbox closure test unchanged.
Broker bytes remain SHA256 71b32ee4afa322793aad81231441ee7badc3e5aff65ff008c6cc0f7f63b72781. git diff --check still passes. Slot returned in goldie-0032, pending ruling/new CLEAR; no reruns.
## mos-1567 approved closure correction / second slot request
Sage-3940 relays Mos's mos-1567 and widened row scope (sage-3939). Applied exactly the approved pre-existing-file edits: project-runtime.test.mjs adds two lines at17-18, one Row265 comment and the parser import in the pinned array, no other line changed (numstat2 added/0 deleted); seat/src/sandbox.mjs adds only `packages/bus/src/address.mjs` immediately after broker in RUNNER_FILES. seat/tests/sandbox.test.mjs stays byte-for-byte unchanged. The parser remains a separate pure module. A NEW block in address.test.mjs asserts `doesNotMatch(/\bimport\b/)` on its source, with a node:fs import in that new test file; no existing assertion is moved or weakened.
Review packet must explicitly call out the RUNNER_FILES mount-list entry for Boyd's review. This is a read-only source-file mount needed by the broker's static dependency, with no new module dependency or role/verb authority. The first-run import pin failure remains recorded above and in logs/03-bus.log; new logs will have rerun-prefixed names, preserving first-slot bytes.
Requested rerun order, awaiting CLEAR after Bentina: first `timeout -k 10 180 node --test --test-reporter=tap packages/seat/tests/sandbox.test.mjs` whole, then the original five steps again from step1: address whole (now15 cases), rooms whole, bus whole with concurrency1 (includes project-runtime whole), M01–M12 whole-room-file mutants, then restored address and rooms files separately. Explicit TAP reporter is used throughout for stable logs/kill matching; timeouts otherwise120 per new/mutant run and600 for bus. Each run still gets fresh short mktemp TMPDIR, date/uptime/busy header, serialized with the same stop rule. Estimate5–10minutes. Only static node --check and git diff --check run since the STOP; all pass. No new test run yet.
## Verified result — sage-3942 CLEAR, slot returned goldie-0034
All rerun positives exit 0 with zero failures, skips or cancellations. Full bus suite passed 957/957, including the approved project-runtime import pin. The unchanged sandbox test passed 10/10, including its recursive mount/import-closure assertion. New parser cases passed 15/15, rooms 18/18, and both passed again after restoration. No tests run after slot return.
| Run | Exit | Pass / fail | Duration ms | UTC / load / helper | Log |
| --- | --- | --- | --- | --- | --- |
| rerun-00-sandbox | 0 | 10 / 0 | 5526.027699 | Sat Oct 24 04:31:35 UTC 2026; 23:31:35 up 10 days, 14:51, 2 users, load average: 2.83, 2.91, 2.73; CLEAR | [log](logs/rerun-00-sandbox.log.gz) |
| rerun-01-address | 0 | 15 / 0 | 54.634817 | Sat Oct 24 04:31:45 UTC 2026; 23:31:45 up 10 days, 14:51, 2 users, load average: 2.54, 2.84, 2.71; CLEAR | [log](logs/rerun-01-address.log.gz) |
| rerun-02-rooms | 0 | 18 / 0 | 921.788987 | Sat Oct 24 04:31:52 UTC 2026; 23:31:52 up 10 days, 14:51, 2 users, load average: 2.42, 2.81, 2.70; CLEAR | [log](logs/rerun-02-rooms.log.gz) |
| rerun-03-bus | 0 | 957 / 0 | 118397.418425 | Sat Oct 24 04:32:01 UTC 2026; 23:32:01 up 10 days, 14:51, 2 users, load average: 2.28, 2.77, 2.68; CLEAR | [log](logs/rerun-03-bus.log.gz) |
| rerun-05-restored-address | 0 | 15 / 0 | 54.587943 | Sat Oct 24 04:35:02 UTC 2026; 23:35:02 up 10 days, 14:54, 2 users, load average: 3.10, 3.07, 2.83; CLEAR | [log](logs/rerun-05-restored-address.log.gz) |
| rerun-05-restored-rooms | 0 | 18 / 0 | 991.877991 | Sat Oct 24 04:35:11 UTC 2026; 23:35:11 up 10 days, 14:55, 2 users, load average: 3.17, 3.09, 2.84; CLEAR | [log](logs/rerun-05-restored-rooms.log.gz) |
Mutant driver exit 0; all twelve target tests killed their guard relaxation with Node exit 1. Every mutant log records exact bytes restored to broker SHA256 `71b32ee4afa322793aad81231441ee7badc3e5aff65ff008c6cc0f7f63b72781`. Driver receipt: [rerun-04-mutants-driver.log](logs/rerun-04-mutants-driver.log.gz).
| Mutant | Node exit | Pass / fail | Exact diff | Raw log |
| --- | --- | --- | --- | --- |
| M01-human | 1, killed | 16 / 2 | [diff](logs/M01-human.diff) | [log](logs/M01-human.log.gz) |
| M02-tester | 1, killed | 17 / 1 | [diff](logs/M02-tester.diff) | [log](logs/M02-tester.log.gz) |
| M03-project | 1, killed | 17 / 1 | [diff](logs/M03-project.diff) | [log](logs/M03-project.log.gz) |
| M04-members | 1, killed | 17 / 1 | [diff](logs/M04-members.diff) | [log](logs/M04-members.log.gz) |
| M05-collision | 1, killed | 14 / 4 | [diff](logs/M05-collision.diff) | [log](logs/M05-collision.log.gz) |
| M06-open | 1, killed | 17 / 1 | [diff](logs/M06-open.diff) | [log](logs/M06-open.log.gz) |
| M07-body | 1, killed | 17 / 1 | [diff](logs/M07-body.diff) | [log](logs/M07-body.log.gz) |
| M08-unknown | 1, killed | 16 / 2 | [diff](logs/M08-unknown.diff) | [log](logs/M08-unknown.log.gz) |
| M09-ambiguous | 1, killed | 17 / 1 | [diff](logs/M09-ambiguous.diff) | [log](logs/M09-ambiguous.log.gz) |
| M10-messaging | 1, killed | 17 / 1 | [diff](logs/M10-messaging.diff) | [log](logs/M10-messaging.log.gz) |
| M11-cursor | 1, killed | 17 / 1 | [diff](logs/M11-cursor.diff) | [log](logs/M11-cursor.log.gz) |
| M12-fence | 1, killed | 17 / 1 | [diff](logs/M12-fence.diff) | [log](logs/M12-fence.log.gz) |
## Final review scope and packet
Boyd reviews the three room cases, pure parser/export, README rows, new parser/room tests, and **the added read-only RUNNER_FILES mount for packages/bus/src/address.mjs**. mos-1567 authorizes that one sandbox source line and the sole old-test delta: project-runtime.test.mjs:17-18 adds exactly the Row265 comment and parser import pin (numstat 2 added / 0 deleted). Byte comparison against base after removing those additions proves all other old-test bytes unchanged. Removing the single RUNNER_FILES addition likewise restores sandbox.mjs to base. sandbox.test.mjs has no byte change. New address.test.mjs block proves the mounted parser source has no import word.
The widened changed-file hash-context audit includes sandbox.mjs: [pin-context-with-sandbox.txt](pin-context-with-sandbox.txt), zero 64-hex-near-reference hits. The original reader and positive-control receipts remain alongside it; the missed source pin and first STOP remain recorded, not concealed. No role/schema/tester allowlist or helper change ships. BUILD-LOG and SESSIONS only append tracking entries.
Base: `ec86f2c847f6154a0677c6ed23c87dabc2c665d0`. Branch: `goldie/row265`. MANIFEST.sha256 lists every changed source/test/tracking/evidence file except itself, with repository-relative paths, and is verified before commit/push. Raw first-red logs are retained separately from green reruns.
Evidence packaging: logs are stored losslessly as `.log.gz`; decompress with `gzip -cd`. Decompression was byte-compared with each raw run log before packing. This preserves the original failure/Node whitespace while keeping the committed diff whitespace-clean. The run driver recreates raw `.log` files when used. Mutant busy-check headers identify only their own Python driver PID3686656 as BUSY; positive runs report CLEAR. The driver held the assigned slot throughout, with serialized child tests.
@@ -0,0 +1,51 @@
996e9d0c1e7a8436691ce5d5d89b4eb940b07aafb3386f63a9847d203f43d998 BUILD-LOG.md
26b5821895a152ddac66a26f16585c6c48ec71d99ca7e7c2b9a4d5a71943c5b4 agents/goldie/work/queue-265/BUILD.md
1289c0f9f4e857590490a0972b5300cc8fb1a86e3ef79fe63e47472709e982f6 agents/goldie/work/queue-265/logs/01-address.log.gz
bc0ac95454ca4a4478641832e68caeb82bfe8b1fdc9abc0b80f1126ef42b7be5 agents/goldie/work/queue-265/logs/02-rooms.log.gz
459a45bd772576847338a438b27f4f578a2a660be744e1404fd239d192c6957c agents/goldie/work/queue-265/logs/03-bus.log.gz
c30cecb149e98d64f285e81a82d07a0a89dad5ea223f9031b543230615663bb5 agents/goldie/work/queue-265/logs/M01-human.diff
a75e02d91ff46fbfaf627beea943183b769e417bcb572bf11f778f0fca7fcf29 agents/goldie/work/queue-265/logs/M01-human.log.gz
b78b1c5b205272411710f56a981acf58dc09c46333369924b98c4c8657dc949c agents/goldie/work/queue-265/logs/M02-tester.diff
6a680dcb1db461cce3a3a2e066aa1245e4f84aa6754354058009bc0f6ebf4185 agents/goldie/work/queue-265/logs/M02-tester.log.gz
02a6cfab404405099fc39eb83413614c67a0c4c3f87cd23c3849a35d1055ddb0 agents/goldie/work/queue-265/logs/M03-project.diff
8b7cd0c568510e49b61fa14fce7b2680025b773eda884e0a84163d240a124e39 agents/goldie/work/queue-265/logs/M03-project.log.gz
18b2cb43f2799301cf934e4c327bc1a03e08ff8d11ff590cb9677c86dfd43ff0 agents/goldie/work/queue-265/logs/M04-members.diff
195f9ec57c9494552566d2f8b87f3b17a210b811f8b556dd65db89814e237816 agents/goldie/work/queue-265/logs/M04-members.log.gz
a773e214c76acf5285ac4ca17cdd90dd552ddc79157abe75ef6de7f67a0b6a70 agents/goldie/work/queue-265/logs/M05-collision.diff
1148cda3f6aaa7f6aeaab2f95c932114ddeac5bfd9318a6b77a4148079d486ed agents/goldie/work/queue-265/logs/M05-collision.log.gz
56400065c9cdb6305763b2bff8c0e06e9ae1223fa89b4467634a9951622d3fbe agents/goldie/work/queue-265/logs/M06-open.diff
b8f9bd9a5c488e604d0afe3feda4a2602359a94a49c7fdbd2f0f6a421b0ca1ce agents/goldie/work/queue-265/logs/M06-open.log.gz
2c1b745a15602cdcaf194131e4d6e0f8fa349e9712ce2e8cb6e695699e4ec19d agents/goldie/work/queue-265/logs/M07-body.diff
4857461e7a2ceb01a6446acb4708a732949a0d8d1a85fa2f6d10bedde6b9e11b agents/goldie/work/queue-265/logs/M07-body.log.gz
e3480e69d016caea6c1fbcdfa022b18214a10e8deca4409d0dc2f72261d2ee92 agents/goldie/work/queue-265/logs/M08-unknown.diff
3d0f347f21df3c388a4f39679d4694aadc9684ae8b8ad2d30bf0bb316e48dcb0 agents/goldie/work/queue-265/logs/M08-unknown.log.gz
62420ed20affcab256facee05414b8b760a4c79a60f60be0c06c88abb61ec6ae agents/goldie/work/queue-265/logs/M09-ambiguous.diff
14a18d61344df592e885c46ace34e0c59914f4fbe591fa99eeb3d81fa8ef8bf1 agents/goldie/work/queue-265/logs/M09-ambiguous.log.gz
0671bf77394f82df92252275171fd9edea2e5b2e7143387840d03bda544676bf agents/goldie/work/queue-265/logs/M10-messaging.diff
4202215d40999e0d1cc98ebfb5445f0c8fd4688e6ca968e85d225723076ca8a7 agents/goldie/work/queue-265/logs/M10-messaging.log.gz
042598ecb551e761d37af898f69e17ec137d2f39687dbf81ce5e1eeb98a38202 agents/goldie/work/queue-265/logs/M11-cursor.diff
deff3d0514c379bd3d9c6f6bbc60db59b9dce2baaafd6fa611711c98cc8365ce agents/goldie/work/queue-265/logs/M11-cursor.log.gz
780e70fbcb2328006c8d9ace75aa62fcd0d935b4d758b50b0f5c5d5c55604122 agents/goldie/work/queue-265/logs/M12-fence.diff
b629b41a39c028b70d4b847c351d248d6d70e8175d6d27f563b0ed8817f89642 agents/goldie/work/queue-265/logs/M12-fence.log.gz
1b8c3cec31b44ff018a1bc74fef95996bcbdd21d340e2d2605154ba645648040 agents/goldie/work/queue-265/logs/rerun-00-sandbox.log.gz
d18c5a9432de06ebec36c937da7dfb1a3e79dc397b33ff624058af351eee24dd agents/goldie/work/queue-265/logs/rerun-01-address.log.gz
d10b83ccec56e47d8c7c74d8304fc4fc87b8ea92a50098380cd02b8012c32d09 agents/goldie/work/queue-265/logs/rerun-02-rooms.log.gz
6ff5955fe5c85f51c1cf4daeb8eddd61ef1a30e170221f99bfb08da8304d7e4a agents/goldie/work/queue-265/logs/rerun-03-bus.log.gz
eeba0182f06cee0c06cd15ecf7ef43aaeffd7149b2202cc0653078c20bb46198 agents/goldie/work/queue-265/logs/rerun-04-mutants-driver.log.gz
5fe9fe35af60ef951c5c68b040aa9bb6067d9c746e143bb5782eb428acd4e4fe agents/goldie/work/queue-265/logs/rerun-05-restored-address.log.gz
2b3f3305ff5f6926a0269bd6c88d5f2e4a89727eefcf99625bebb51a70abb649 agents/goldie/work/queue-265/logs/rerun-05-restored-rooms.log.gz
f11ae40d923affad1936fe51d05981ac201a1051bc9f877d75fc1d544c6e38f1 agents/goldie/work/queue-265/mutants.py
e9cdf16670a16a14f3dd722a69c63a1785e4deb3608ee05d519b0ec8c1a9ca0b agents/goldie/work/queue-265/pin-context-grep.txt
491ba3bb25e918049249178f3c6a67b2b4f56a197f0a67059acb2420cd03b73c agents/goldie/work/queue-265/pin-context-with-sandbox.txt
784fb42bff187c0eebcf7756942ec3cf56f8faa7b54ce4ad96181b81fe46f0e9 agents/goldie/work/queue-265/pin-positive-control.txt
766ead519eefbde958bbdbf77bf858b10dd1e3d9dee8ab5d356fe821ce7602fe agents/goldie/work/queue-265/reader-grep.txt
6c3c44eb943f33d5bb547f542c77da4d6f04e738056466bc491c2f6fd5f67946 docs/SESSIONS.md
1ba33af1f42c88a5346d25fe915a44ee70a27fbc6b9ab880db0866cbdad5d45d packages/bus/README.md
232a1c77c2f17011c53b55fc6052f70985598210769d41d7c2c0b535328aad23 packages/bus/src/address.mjs
71b32ee4afa322793aad81231441ee7badc3e5aff65ff008c6cc0f7f63b72781 packages/bus/src/broker.mjs
ed57d48d2af4a225259e0a44c86a30b12792ec9f725dd9c89251569b7dc89e5a packages/bus/src/index.mjs
a6725ffe30d44981a9b9d3a3c319f7e96a46ec7bd74f0c8d869d9a798eca1a81 packages/bus/tests/address-cases.mjs
fae431bc3743b224f69da33c4cebe376b47257d67f52d25d3ab0cb59f224a607 packages/bus/tests/address.test.mjs
8594e94384c1c8b7234cfa8bbc17456cff86d9adef936cd1059a0d5a60e4546b packages/bus/tests/project-runtime.test.mjs
892c3709393fa8962710bc07565d1097bbd79ea1fbfda34af5f9385db34ed0fa packages/bus/tests/rooms.test.mjs
83019fa6b39ad9fd79b84627856b4dec82683d8261b8cba9b9ac840e0c489268 packages/seat/src/sandbox.mjs
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M01-human
@@ -6423,7 +6423,6 @@
case 'room.open':
case 'room.send':
case 'room.read': {
- this.#human(s);
const opening = verb === 'room.open';
const sending = verb === 'room.send';
keys(a, opening ? ['project'] : sending ? ['project', 'body'] : ['project', 'after', 'limit'],
Binary file not shown.
@@ -0,0 +1,12 @@
--- broker.original
+++ M02-tester
@@ -122,7 +122,8 @@
// Row 175 (#1673, Q173): the test system's reads and two writes, name.set and project.create,
// each recorded with `tester` as its actor. The organization branch keeps its own list below.
// Row 177 (#1675, Q173): the tester's own threads, owner `tester`, and a stop of their runs.
-export const TESTER_VERBS = Object.freeze(['message.send', 'message.receive', 'message.read', 'inbox', 'agents', 'tasks', 'trail', 'decisions', 'messages', 'replies',
+export const TESTER_VERBS = Object.freeze([
+ 'room.open', 'room.send', 'room.read','message.send', 'message.receive', 'message.read', 'inbox', 'agents', 'tasks', 'trail', 'decisions', 'messages', 'replies',
'usage', 'names', 'limits', 'plugins', 'instances', 'project.list', 'thread.list', 'name.set', 'project.create', 'credentials',
'thread.create', 'thread.open', 'thread.send', 'thread.messages', 'assistant.stop',
// Row 194 (#1692, D4.1): launch and terminate, and the read of the requests they make.
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M03-project
@@ -6429,7 +6429,6 @@
keys(a, opening ? ['project'] : sending ? ['project', 'body'] : ['project', 'after', 'limit'],
sending ? ['project', 'body'] : ['project']);
const project = identifier(a.project);
- if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', project, s.business)) fail('project-not-found');
const members = Object.keys(b.roles).filter((instance) => this.#threaded(s.business, instance)).sort();
if (!members.length) fail('thread-instance-missing');
const owner = this.#owner(s);
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M04-members
@@ -6431,7 +6431,6 @@
const project = identifier(a.project);
if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', project, s.business)) fail('project-not-found');
const members = Object.keys(b.roles).filter((instance) => this.#threaded(s.business, instance)).sort();
- if (!members.length) fail('thread-instance-missing');
const owner = this.#owner(s);
// Preflight all collisions/missing threads before open creates or send writes anything.
const threads = members.map((instance) => {
Binary file not shown.
@@ -0,0 +1,11 @@
--- broker.original
+++ M05-collision
@@ -6436,8 +6436,6 @@
// Preflight all collisions/missing threads before open creates or send writes anything.
const threads = members.map((instance) => {
const t = this.#store.get('SELECT * FROM threads WHERE project=? AND name=?', project, instance);
- if (t && (t.instance !== instance || t.owner !== owner))
- fail(opening ? 'room-thread-name-taken' : 'room-not-open');
if (!t && !opening) fail('room-not-open');
return t ?? { instance, name: instance };
});
@@ -0,0 +1,10 @@
--- broker.original
+++ M06-open
@@ -6438,7 +6438,6 @@
const t = this.#store.get('SELECT * FROM threads WHERE project=? AND name=?', project, instance);
if (t && (t.instance !== instance || t.owner !== owner))
fail(opening ? 'room-thread-name-taken' : 'room-not-open');
- if (!t && !opening) fail('room-not-open');
return t ?? { instance, name: instance };
});
if (opening) {
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M07-body
@@ -6450,7 +6450,6 @@
if (sending) {
string(a.body, 32768);
const addressed = parseAddress(a.body);
- if (!addressed.body.trim()) fail('invalid-request');
const selected = new Set();
for (const tag of addressed.tags) {
const candidates = new Set([this.#named(s.business, tag), tag].filter((instance) => members.includes(instance)));
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M08-unknown
@@ -6454,7 +6454,6 @@
const selected = new Set();
for (const tag of addressed.tags) {
const candidates = new Set([this.#named(s.business, tag), tag].filter((instance) => members.includes(instance)));
- if (!candidates.size) fail('unknown-addressee');
if (candidates.size > 1) fail('addressee-ambiguous');
selected.add([...candidates][0]);
}
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M09-ambiguous
@@ -6455,7 +6455,6 @@
for (const tag of addressed.tags) {
const candidates = new Set([this.#named(s.business, tag), tag].filter((instance) => members.includes(instance)));
if (!candidates.size) fail('unknown-addressee');
- if (candidates.size > 1) fail('addressee-ambiguous');
selected.add([...candidates][0]);
}
const targets = threads.filter((t) => !addressed.tags.length || selected.has(t.instance));
@@ -0,0 +1,10 @@
--- broker.original
+++ M10-messaging
@@ -6462,7 +6462,6 @@
const sessions = targets.map((t) => {
const session = this.#defaultSession(t.id);
this.#sessionFence(session);
- this.#checkMessaging(s.business, 'human', t.instance);
return session;
});
const at = this.#now();
@@ -0,0 +1,11 @@
--- broker.original
+++ M11-cursor
@@ -6488,7 +6488,7 @@
`SELECT m.seq FROM messages m JOIN message_threads mt ON mt.message=m.id WHERE m.id=? AND m.business=? AND mt.thread IN (${placeholders})`,
identifier(a.after), s.business, ...tids,
)?.seq;
- if (after === undefined) fail('message-not-found');
+ if (after === undefined) after = 0;
}
// At most one row per instance joins a group; one lookahead row completes the last page.
const rows = this.#store.all(
Binary file not shown.
@@ -0,0 +1,10 @@
--- broker.original
+++ M12-fence
@@ -6461,7 +6461,6 @@
const targets = threads.filter((t) => !addressed.tags.length || selected.has(t.instance));
const sessions = targets.map((t) => {
const session = this.#defaultSession(t.id);
- this.#sessionFence(session);
this.#checkMessaging(s.business, 'human', t.instance);
return session;
});
Binary file not shown.
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Slot-only runner. Every mutation restores and verifies the complete broker bytes."""
from pathlib import Path
import hashlib, os, re, subprocess, sys
ROOT = Path(__file__).resolve().parents[4]
SOURCE = ROOT / 'packages/bus/src/broker.mjs'
OUT = Path(__file__).resolve().parent / 'logs'
MUTANTS = [
('M01-human', 'human-required', 'room verbs require the human', ' this.#human(s);\n', ''),
('M02-tester', 'tester-refused', 'room verbs require the human', None, None),
('M03-project', 'project-not-found', 'room verbs refuse missing projects', " if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', project, s.business)) fail('project-not-found');\n", ''),
('M04-members', 'thread-instance-missing', 'room verbs refuse missing projects', " if (!members.length) fail('thread-instance-missing');\n", ''),
('M05-collision', 'room-thread-name-taken', 'room.open preflights instance collisions', " if (t && (t.instance !== instance || t.owner !== owner))\n fail(opening ? 'room-thread-name-taken' : 'room-not-open');\n", ''),
('M06-open', 'room-not-open', 'send/read refuse a missing canonical member', " if (!t && !opening) fail('room-not-open');\n", ''),
('M07-body', 'invalid-request', 'unknown or non-member addressees and tags-only bodies', " if (!addressed.body.trim()) fail('invalid-request');\n", ''),
('M08-unknown', 'unknown-addressee', 'unknown or non-member addressees and tags-only bodies', " if (!candidates.size) fail('unknown-addressee');\n", ''),
('M09-ambiguous', 'addressee-ambiguous', 'a legacy name colliding with another member key', " if (candidates.size > 1) fail('addressee-ambiguous');\n", ''),
('M10-messaging', 'message-sender-refused', 'messaging denial on the last target', " this.#checkMessaging(s.business, 'human', t.instance);\n", ''),
('M11-cursor', 'message-not-found', 'room.read groups one send, pages', " if (after === undefined) fail('message-not-found');\n", ' if (after === undefined) after = 0;\n'),
('M12-fence', 'session-clearing', 'a clearing last member fences the whole send', ' this.#sessionFence(session);\n', ''),
]
def sha(data): return hashlib.sha256(data).hexdigest()
def main():
OUT.mkdir(exist_ok=True)
original = SOURCE.read_bytes()
text = original.decode()
start = text.index(" case 'room.open':")
end = text.index(" // A thread belongs", start)
block = text[start:end]
for name, refusal, expected, old, new in MUTANTS:
if old is None:
needle = 'export const TESTER_VERBS = Object.freeze(['
assert text.count(needle) == 1
changed = text.replace(needle, needle + "\n 'room.open', 'room.send', 'room.read',", 1)
else:
assert block.count(old) == 1, (name, block.count(old))
changed = text[:start] + block.replace(old, new, 1) + text[end:]
# Save conventional original→mutant diff without relying on shell interpolation.
import difflib
(OUT / f'{name}.diff').write_text(''.join(difflib.unified_diff(text.splitlines(True), changed.splitlines(True), fromfile='broker.original', tofile=name)))
tmp = subprocess.check_output(['mktemp', '-d', '/mnt/storage/scratch/tmp/XXXXXX'], text=True).strip()
env = {**os.environ, 'TMPDIR': tmp}
log = OUT / f'{name}.log'
try:
SOURCE.write_text(changed)
with log.open('w') as stream:
for command in [['date', '-u'], ['uptime'], ['bash', '-c', 'bash /mnt/storage/scratch/sage-busy-now.sh 2>/dev/null | tail -1']]:
subprocess.run(command, cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT, check=True)
stream.write(f'TMPDIR={tmp}\nmutant={name} refusal={refusal} original_sha256={sha(original)}\n')
stream.flush()
result = subprocess.run(['timeout', '-k', '10', '120', 'node', '--test', '--test-reporter=tap', 'packages/bus/tests/rooms.test.mjs'],
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT)
output = log.read_text()
killed = result.returncode == 1 and re.search(r'(?m)^not ok \d+ - ' + re.escape(expected), output)
print(f'{name}: exit={result.returncode} target_killed={bool(killed)} log={log}', flush=True)
if not killed: return 1
finally:
SOURCE.write_bytes(original)
assert SOURCE.read_bytes() == original
assert sha(SOURCE.read_bytes()) == sha(original)
with log.open('a') as stream: stream.write(f'RESTORED sha256={sha(original)}\n')
return 0
if __name__ == '__main__': sys.exit(main())
@@ -0,0 +1 @@
No changed-file mention has a literal 64-hex digest within eight lines.
@@ -0,0 +1 @@
No changed-product-file mention has a literal 64-hex digest within eight lines.
@@ -0,0 +1,6 @@
packages/bus/src/store.mjs:15:const ddl = readFileSync(new URL('../schema.sql', import.meta.url), 'utf8');
packages/bus/src/store.mjs:16:export const SCHEMA_SHA256 = 'a2ba3a46f12e56139ecae9e55227d00847842aedec50c1d92aaae28844ca48e7';
packages/bus/src/store.mjs:106:const hex = (value) => createHash('sha256').update(value).digest('hex');
packages/bus/src/store.mjs:107:if (hex(ddl) !== SCHEMA_SHA256) throw Error('schema-source-mismatch');
packages/bus/tests/schema.test.mjs:17: db.exec(readFileSync(new URL('../schema.sql', import.meta.url), 'utf8'));
packages/bus/tests/schema.test.mjs:90: const hex = (s) => createHash('sha256').update(s).digest('hex');
@@ -0,0 +1,133 @@
packages/bus/tests/session-compactions.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/bus/tests/console-decide.test.mjs:14:import { Broker, CONSOLE_VERBS } from '../src/broker.mjs';
packages/harness/tests/runner.test.mjs:14:import { Broker, BusError } from "../../bus/src/broker.mjs";
packages/bus/tests/project-files.test.mjs:8:import { PROJECT_FILE_VERBS } from '../src/broker.mjs';
packages/cli/tests/lifecycle.test.mjs:19:import { BusError } from "../../bus/src/broker.mjs";
packages/bus/tests/lifecycle-verbs.test.mjs:11:import { Broker, LIFECYCLE_REQUEST_MS, TESTER_VERBS } from '../src/broker.mjs';
packages/bus/tests/lifecycle-verbs.test.mjs:74: for (const verb of ['instance.launch', 'instance.end', 'instance.request']) assert.ok(TESTER_VERBS.includes(verb), verb);
packages/bus/tests/lifecycle-verbs.test.mjs:75: assert.equal(TESTER_VERBS.includes('instance.regenerate'), false);
packages/bus/tests/tasks.test.mjs:9:import { Broker, BusError, TASK_VERBS } from '../src/broker.mjs';
packages/harness/tests/tools.test.mjs:4:import { ORGANIZATION_KEYS as BROKER_ORGANIZATION_KEYS, TASK_PAGE, TASK_PAGE_MAX } from "../../bus/src/broker.mjs";
packages/seat/tests/session.test.mjs:11:import { Broker } from "../../bus/src/broker.mjs";
packages/bus/tests/decision-route.test.mjs:12:import { Broker } from '../src/broker.mjs';
packages/cli/tests/thread-launch.test.mjs:19:import { BusError } from "../../bus/src/broker.mjs";
packages/seat/tests/sandbox.test.mjs:15:import { Broker } from "../../bus/src/broker.mjs";
packages/bus/tests/host-run.test.mjs:16:import { Broker, ORGANIZATION_KEYS } from '../src/broker.mjs';
packages/bus/tests/host-run.test.mjs:181: // organization.set reaches the writer as limits.set (broker.mjs:1379).
packages/cli/tests/helpers.mjs:10:import { BusError, Broker } from "../../bus/src/broker.mjs";
packages/tasks/tests/fields.test.mjs:10:import { TASK_VERBS } from '../../bus/src/broker.mjs';
packages/tasks/tests/fields.test.mjs:226: // decision back to that role (broker.mjs, decision.raise).
packages/webui/tests/instances.test.mjs:15:import { BusError } from '../../bus/src/broker.mjs';
packages/cli/tests/tester-threads.test.mjs:14:import { BusError, Broker } from "../../bus/src/broker.mjs";
packages/bus/tests/staff-request.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/tasks/tests/world.mjs:5:import { Broker } from '../../bus/src/broker.mjs';
packages/cli/tests/processing-cli.test.mjs:7:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/grant-host.test.mjs:10:import { Broker, registrySha256 } from '../src/broker.mjs';
packages/business/tests/workflow-roles.test.mjs:115: // Q169 (broker.mjs END_PROTECTED, #hardWait): revoking the assistant is a hard wait as well.
packages/business/tests/workflow-roles.test.mjs:136: // sage-0709: only recordLand spends a decision (broker.mjs #spent). checkLand refuses decision-consumed after
packages/cli/tests/thread-revoke.test.mjs:18:import { BusError } from "../../bus/src/broker.mjs";
packages/bus/tests/tester-writes.test.mjs:10:import { Broker, TESTER_VERBS, TESTER_ORGANIZATION_VERBS } from '../src/broker.mjs';
packages/bus/tests/tester-writes.test.mjs:18:test('row 175: TESTER_VERBS and the organization branch tester set are pinned', () => {
packages/bus/tests/tester-writes.test.mjs:19: assert.deepEqual([...TESTER_VERBS], [
packages/bus/tests/tester-writes.test.mjs:32: assert.ok(Object.isFrozen(TESTER_VERBS) && Object.isFrozen(TESTER_ORGANIZATION_VERBS));
packages/cli/tests/stack-release.test.mjs:1993: for(const [f,text] of [['packages/cli/src/cli.mjs',id===A?'// fixture host from before row 163\nprocess.exit(1);\n':script(rHost)],['packages/webui/src/cli.mjs',script(rConsole)],['packages/bus/src/store.mjs',rStore(v,froms)],['packages/bus/src/migrate-schema.mjs',script(rMigrate)],['packages/bus/src/broker.mjs','export class BusError extends Error { constructor(code) { super(code); this.code = code; } }\n']]){mkdirSync(dirname(join(d,f)),{recursive:true});writeFileSync(join(d,f),text)}
packages/cli/tests/session-cli.test.mjs:94: const { Broker } = await import('../../bus/src/broker.mjs');
packages/cli/tests/session-launch.test.mjs:21:import { BusError } from "../../bus/src/broker.mjs";
packages/cli/tests/revoke-window.test.mjs:19:import { BusError } from "../../bus/src/broker.mjs";
packages/bus/tests/chief-of-staff.test.mjs:10:import { Broker, ACTIONS } from '../src/broker.mjs';
packages/cli/tests/instances.test.mjs:13:import { BusError } from "../../bus/src/broker.mjs";
packages/cli/tests/thread-cli.test.mjs:14:import { BusError, Broker } from "../../bus/src/broker.mjs";
packages/bus/tests/processing.test.mjs:6:import { Broker } from '../src/broker.mjs';
packages/cli/tests/idle-stop-race.test.mjs:17:import { BusError } from "../../bus/src/broker.mjs";
packages/cli/tests/tester-reads.test.mjs:12:import { BusError } from "../../bus/src/broker.mjs";
packages/cli/tests/session-clear.test.mjs:18:import { BusError } from "../../bus/src/broker.mjs";
packages/bus/tests/names.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/cli/tests/compactions-launch.test.mjs:24:import { BusError } from "../../bus/src/broker.mjs";
packages/cli/tests/tester.test.mjs:7:import { BusError, Broker } from "../../bus/src/broker.mjs";
packages/bus/tests/reload.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/bus/tests/agents.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/bus/tests/end-launch.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/webui/tests/decide-browser.test.mjs:14:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/credential-verbs.test.mjs:11:import { Broker, CREDENTIAL_VERBS } from '../src/broker.mjs';
packages/webui/tests/send-browser.test.mjs:13:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/launch-notice.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/bus/tests/messages.test.mjs:3:import { Broker } from '../src/broker.mjs';
packages/bus/tests/workflow-roles.test.mjs:11:import { Broker } from '../src/broker.mjs';
packages/bus/tests/library-sync.test.mjs:11:import { Broker, LIBRARY_VERBS } from '../src/broker.mjs';
packages/bus/tests/credential-status.test.mjs:12:import { Broker } from '../src/broker.mjs';
packages/bus/tests/gitea-grant-docker.test.mjs:15:import { Broker, GRANT_EVIDENCE } from '../src/broker.mjs';
packages/bus/tests/thread-migration.test.mjs:11:import { Broker } from '../src/broker.mjs';
packages/bus/tests/comment-cursors.test.mjs:7:import { Broker } from '../src/broker.mjs';
packages/bus/tests/thread-claims.test.mjs:11:import { Broker } from '../src/broker.mjs';
packages/bus/tests/state.test.mjs:7:import { Broker, TASK_PAGE } from '../src/broker.mjs';
packages/bus/tests/access-revoke.test.mjs:14:import { Broker } from '../src/broker.mjs';
packages/bus/tests/plugins-verb.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/bus/tests/transport.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/bus/tests/publish.test.mjs:14:import { Broker } from '../src/broker.mjs';
packages/bus/tests/publish.test.mjs:221: const { ACTIONS, HARD_WAITS } = await import('../src/broker.mjs');
packages/bus/tests/tester-threads.test.mjs:11:import { Broker } from '../src/broker.mjs';
packages/bus/tests/tester-threads.test.mjs:284: // The row #specNotice (broker.mjs) writes for a human save of the spec in project stack. The save
packages/bus/tests/approval-binding.test.mjs:7:import { Broker } from '../src/broker.mjs';
packages/bus/tests/fire-revokes-grants.test.mjs:15:import { Broker, GRANT_EVIDENCE, registrySha256 } from '../src/broker.mjs';
packages/bus/tests/daily-quota-runtime.test.mjs:10:import { Broker } from '../src/broker.mjs';
packages/bus/tests/limit-checks.test.mjs:323: for (const file of ['../src/runtime.mjs', '../src/broker.mjs', '../src/process.mjs']) {
packages/bus/tests/spec-fixtures.mjs:6:import { Broker } from '../src/broker.mjs';
packages/bus/tests/decisions.test.mjs:9:import { Broker, RECENT } from '../src/broker.mjs';
packages/bus/tests/peer-transport.test.mjs:9:import { BusError } from '../src/broker.mjs';
packages/bus/tests/peer-transport.test.mjs:47: const {TASK_VERBS,BusError}=await import('../src/broker.mjs');
packages/bus/tests/messaging.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/bus/tests/rooms.test.mjs:8:import { Broker, THREAD_PAGE, THREAD_PAGE_MAX, TESTER_VERBS } from '../src/broker.mjs';
packages/bus/tests/rooms.test.mjs:80: assert.ok(!TESTER_VERBS.includes(verb));
packages/bus/tests/web.test.mjs:8:import { Broker, BusError, WEB_UNTRUSTED } from '../src/broker.mjs';
packages/bus/tests/replies.test.mjs:11:import { Broker, REPLIES } from '../src/broker.mjs';
packages/bus/tests/thread-shares.test.mjs:14:import { Broker } from '../src/broker.mjs';
packages/bus/tests/message-citation.test.mjs:6:import { Broker } from '../src/broker.mjs';
packages/bus/tests/limits.test.mjs:7:import {Broker} from '../src/broker.mjs';
packages/bus/tests/host-fixtures.mjs:6:import { registrySha256, writeGrantEvent } from '../src/broker.mjs';
packages/bus/tests/gitea-grant.test.mjs:12:import { Broker, GRANT_EVIDENCE, writeGrantEvent } from '../src/broker.mjs';
packages/bus/tests/csuite-messaging.test.mjs:12:import { Broker } from '../src/broker.mjs';
packages/bus/tests/decision-close.test.mjs:9:import { Broker } from '../src/broker.mjs';
packages/bus/tests/grant-scope.test.mjs:14:import { Broker, GRANT_EVIDENCE, writeGrantEvent } from '../src/broker.mjs';
packages/bus/tests/project-await.test.mjs:7:import { Broker, BusError } from '../src/broker.mjs';
packages/bus/tests/launcher-released.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/bus/tests/limit-stop.test.mjs:12:import { Broker } from '../src/broker.mjs';
packages/bus/tests/message-filter.test.mjs:12:import { Broker, REPLIES } from '../src/broker.mjs';
packages/bus/tests/land.test.mjs:18:import { Broker } from '../src/broker.mjs';
packages/bus/tests/single-use.test.mjs:7:import { Broker } from '../src/broker.mjs';
packages/bus/tests/session-clear.test.mjs:10:import { Broker } from '../src/broker.mjs';
packages/bus/tests/grant-migration.test.mjs:10:import { Broker } from '../src/broker.mjs';
packages/bus/tests/usage-broker.test.mjs:7:import { Broker } from '../src/broker.mjs';
packages/bus/tests/project-runtime.test.mjs:13: const source = readFileSync(new URL('../src/broker.mjs', import.meta.url), 'utf8');
packages/bus/tests/task-read.test.mjs:7:import { Broker } from '../src/broker.mjs';
packages/bus/tests/library.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/bus/tests/instances-verb.test.mjs:11:import { Broker, LIFECYCLE_HUMAN_VERBS, ORGANIZATION_KEYS, TESTER_ORGANIZATION_VERBS } from '../src/broker.mjs';
packages/bus/tests/routes-verb.test.mjs:13:import { Broker, ORGANIZATION_KEYS } from '../src/broker.mjs';
packages/bus/tests/broker.test.mjs:10:const { Broker } = await import('../src/broker.mjs').catch((e) => {
packages/bus/tests/grant-enroll.test.mjs:10:import { Broker, BusError, writeGrantEvent, registrySha256 } from '../src/broker.mjs';
packages/bus/tests/hard-waits.test.mjs:7:import { Broker, ACTIONS, HARD_WAITS } from '../src/broker.mjs';
packages/bus/tests/requirements.test.mjs:10:import { Broker } from '../src/broker.mjs';
packages/bus/tests/grant-executor.test.mjs:15:import { Broker, registrySha256, writeGrantEvent } from '../src/broker.mjs';
packages/bus/tests/sessions.test.mjs:10:import { Broker } from '../src/broker.mjs';
packages/bus/tests/user-principal.test.mjs:14: Broker, ACTIONS, USER_VERBS, TESTER_VERBS, TASK_VERBS, WEB_VERBS, HOST_VERBS, HOSTS_VERBS, CREDENTIAL_VERBS,
packages/bus/tests/user-principal.test.mjs:16:} from '../src/broker.mjs';
packages/bus/tests/user-principal.test.mjs:137: ...ACTIONS, ...TESTER_VERBS, ...TESTER_ORGANIZATION_VERBS, ...PROJECT_FILE_VERBS, ...LIBRARY_VERBS, ...CREDENTIAL_VERBS, ...HOSTS_VERBS,
packages/bus/tests/tester.test.mjs:10:import { Broker, TASK_VERBS } from '../src/broker.mjs';
packages/bus/tests/threads.test.mjs:9:import { Broker, THREAD_PAGE } from '../src/broker.mjs';
packages/webui/tests/routes-browser.test.mjs:15:import {BusError} from '../../bus/src/broker.mjs';
packages/bus/tests/creative.test.mjs:8:import { Broker } from '../src/broker.mjs';
packages/webui/tests/granted-user.test.mjs:16:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/task-page.test.mjs:7:import { Broker, TASK_PAGE, TASK_PAGE_MAX } from '../src/broker.mjs';
packages/webui/tests/user-option-swap.test.mjs:14:import { Broker } from '../../bus/src/broker.mjs';
packages/webui/tests/board-held.test.mjs:20:import { Broker } from '../../bus/src/broker.mjs';
packages/webui/tests/chat-sessions-access.test.mjs:10:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/review.test.mjs:14:import { Broker } from '../src/broker.mjs';
packages/webui/tests/sso-browser.test.mjs:14:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/revoke-window.test.mjs:13:import { Broker } from '../src/broker.mjs';
packages/webui/tests/bus.test.mjs:13:import { Broker } from '../../bus/src/broker.mjs';
packages/bus/tests/viewer.test.mjs:13: Broker, ACTIONS, USER_VERBS, VIEWER_VERBS, BOARD_VERBS, TESTER_VERBS, TESTER_ORGANIZATION_VERBS, PROJECT_FILE_VERBS,
packages/bus/tests/viewer.test.mjs:14:} from '../src/broker.mjs';
packages/bus/tests/viewer.test.mjs:116: const others = new Set([...ACTIONS, ...TESTER_VERBS, ...TESTER_ORGANIZATION_VERBS, ...PROJECT_FILE_VERBS, ...BOARD_VERBS, 'no.such.verb']);
packages/webui/tests/tester-fixture.mjs:12:import { Broker, BusError } from '../../bus/src/broker.mjs';
packages/webui/tests/routes.test.mjs:12:import { BusError } from '../../bus/src/broker.mjs';
packages/webui/tests/chat.test.mjs:191: const { Broker } = await import('../../bus/src/broker.mjs');
packages/webui/tests/bus-browser.test.mjs:11:import { Broker } from '../../bus/src/broker.mjs';
+56
View File
@@ -0,0 +1,56 @@
# Row 326 / #1822 — five run-sheet gaps
Goldie, 2026-10-24. Branch goldie/row-run-sheet in /home/jwoltje/.t3/worktrees/mosaic-stack/goldie-row-run-sheet. Base local refactor4ec279dc43922b338a94718a2afdaba87ef89c15, explicitly assigned by sage-3893. Brief docs/plans/2026-10-24_run-sheet-doc-brief.md at4ec279dc4, blobce3e24fa, section Brief: five run-sheet gaps. Reviewer Gladstone; Sage posts review and owns landing/full gate.
## Before / after and choices
Before: the sheet omitted proof bytes/newline handling, treated render refusal without distinguishing metadata/content, omitted missing-project warning consequences and a pre-install key check, offered no first-install retry form, and omitted the bus-relative backup checksum form.
After: the sheet covers all five cases in their existing sections6/9. Numbered section headings are unchanged. Only this BUILD.md and docs/guides/host-run-sheet.md are edited, per the brief's explicit two-file boundary. Session/build-phase registration and the manifest are recorded here rather than adding changes to SESSIONS.md, BUILD-LOG.md or a third manifest file. No host action or credential read. No suite or product command executed, and no CLEAR needed for this doc-only row (decision137).
| Gap / claim | Source read and how it supports the text |
| --- | --- |
| Proof format, optional LF | webui/src/serve.mjs:257-267 opens no-follow, checks regular/uid/mode600-or400, strips exactly one trailing LF at263, then requires32+ ASCII base64url chars at264. Table states the allowed alphabet and one optional trailing LF. Extra newline/CR is outside that alphabet. |
| Render versus start | case2-unit.sh:397-408 explicitly stats/realpaths the proof and never opens it. webui/src/serve.mjs:267 throws the content error; webui/src/cli.mjs:138-140 catches and sets exit2. Table now distinguishes metadata refusal at render from bad bytes at Console start. |
| Missing project warning | business/src/cli.mjs:74-78 reports absent plus the no-project-variables warning, then continues. case2-install.sh:217-221 captures validate output and prints business-validates on success. Text makes that incomplete install possible and tells operators to stop on the missing-file warning, per Duckworth row323 N2. |
| Pre-install key check | business/src/cli.mjs:71-82 checks every file-backed project, skips kind library, and requires loaded id==business key at80. Read-only inline Node uses only builtins and section2's pinned node, as mosaic. It reads the exact intended business input and files under each project's root, prints a match, and exits nonzero with STOP for read/JSON/id failure. Expected .53 output comes from .mosaic/project.json:3 and case1-business.mosaic-stack.json:10-11. Fixing business rather than project id is mos-1563. |
| Why check before install | case2-install.sh:178 makes the copy before validation at217. New check is explicitly before case2-install.sh and needs no existing Mosaic config, npm modules or installed wrapper. |
| Refused first-install residue | case2-install.sh:172/174 creates/bootstraps the config; bootstrap.sh:13 dispatches to mosaic-config.mjs, whose412-424 creates config.json or validates an existing one without rewriting it. case2-install.sh:200-211 may create businesses/file and library slots. case2-persona-import.mjs:300-308 may create library personas. Validation cleanup at case2-install.sh:217-219 removes only this run's new business file, retaining dirs. Copy may already exist under the path at install:80/178; stack-copy.sh:68 reuses an existing copy. |
| Retry paths / refs | Mos-1565/ops-0291, as pinned in the brief, names exactly root checkout, mosaic clone and default config dir to move aside. UTC-suffixed mv is root-only documentation; no delete, chown or chmod. Destination collision/move failure prints STOP. Retry starts section6's first-install block again. The existing upgrade fetch targets only refs/remotes/origin/refactor; stack-copy.sh:44-48 requires reachability from both it and refs/heads/refactor. |
| Credential re-placement | Existing sheet section9 names claude-session.env, library proof and library-gitea with owner/mode. Moving config aside preserves them but the new config needs Jason to re-place any already-filled files, under mos-1565. Existing per-agent slots still require credentials issue/Approve, never hand placement. No value or command to read a credential added. |
| Backup hand check | stack-release.sh:475-482 creates activation/bus and writes SHA256SUMS from inside bus. restore at502 and rehearsal at2244 cd bus and check ../SHA256SUMS. New hand command does the same as mosaic, without --quiet so the expected bus.sqlite:OK is visible; ops-0292 records why checking in activation itself fails. |
## Read-only grep gate
Executed after editing, with no test/suite run:
```
git grep -n host-run-sheet -- 'packages/*/tests/*' 'scripts/*'
# no output, exit1
git grep -n case1-business.mosaic-stack.json -- 'packages/*/tests/*' 'scripts/*'
packages/business/tests/case1-business-file.test.mjs:13:const DOCUMENT = join(REPO, "docs/guides/case1-business.mosaic-stack.json");
packages/business/tests/case1-project-key.test.mjs:9:const CASE1 = join(REPO, "docs/guides/case1-business.mosaic-stack.json");
# exit0: positive control, same command form, both tests read this document
git grep -n -E '(host-run-sheet.*[0-9a-f]{64}|[0-9a-f]{64}.*host-run-sheet)' -- 'packages/*/tests/*'
# no output, exit1: no 64-hex test pin beside the sheet name
```
An earlier positive control with case2-install.sh also found the real installer spawn in case1-business-file.test.mjs:39 and the CLI installer tests. The sheet-name search finds no reference at all in tests/scripts, stronger than just finding no same-line pin. No read/pin blocker.
Static syntax only: extracted the three new shell snippets, replaced only the version placeholder for parsing, and ran bash-n3/3. Extracted Node heredoc parsed with node --check --input-type=module. No snippet body executed. git diff --check passes. Source citations checked against this base; no new em dash and section numbering unchanged.
## Session / claimant receipt
2026-10-24 | Goldie a13dd6d5-5e73-4b7d-8318-02abeff24940 | Row326/#1822 doc-only build | Five gaps documented with source lines and grep control; no suites/host/credential action. Claim goldie-0326-start-1 rev3793, briefed→in-progress. In-review follows the pushed candidate; expected tokenless pre-send failure will be handed to Sage.
## Inline manifest
Only the sheet needs a content pin; BUILD.md is this receipt in the same candidate commit. An inline manifest honors the brief's two-file limit:
```
e4c14516edff3bfbc81ea553a5a6ccebb6154942473540659d4681c2d56c9711 docs/guides/host-run-sheet.md
```
Reviewer validates the five source chains and operational order. Sage's landing gate owns suites. No code row265 work started; its brief comes separately.
+10
View File
@@ -0,0 +1,10 @@
# Gyro
Builder seat, Codex gpt-6.1-sol, started by Sage under Q246 and mos-1571, as the W2 brief names. Owns row 310, O2 W2:
the WordCraft source import and reproducible restricted web build (docs/plans/2026-10-21_q195-writing-editor.md,
"W2 WordCraft source import and reproducible restricted web build").
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 `gyro/*` branches through the repository credential helper (mos-0981), never `refactor`,
`main` or another seat's branch. Claude seats orchestrate and give the Opus 5.5 landing review (mos-1571). Registered by
Sage on 2026-10-24 so the queue can name the seat as row owner (store.mjs isSeatDir).
+122
View File
@@ -0,0 +1,122 @@
# Row 208 / 193d — Console shared threads and viewer reads
Base ec86f2c84, branch poe/row208; Gitea #1706. Approved design: e2d161e2e,
sha256 dd7fcb419ed8510e9d8d74b569183386d0942af8b68eecc0bbb5e182e677b523.
## Before
FED R6 supplies per-session user transport and the user-only page, row 192 SSO,
and row 206 supplies schema 3l shares and bus refusals. No bus/schema changes needed.
The webui channel adapter must forward the three existing share verbs.
## Choices and acceptance
Use the existing chat HTTP write fences and per-session transport for shares.
Expose only live collaborator grant project/instance scopes as UI hints, never
identity or bus authority. Re-read hints when rendering a user page; mixed grants
must hide writes on viewer-only threads even when another project allows writes.
Shared threads always read without session management, composer, resume or sharing.
Keep the exact governance notice on user thread/share pages. Targets are typed
owner/user principal names or *, avoiding a new user directory read API.
Add new isolated HTTP/broker and browser coverage. Remove each UI guard in mutants
and show the real broker still refuses viewer create, send, open and share writes.
Every test execution requires Sage CLEAR; use scratch TMPDIR, deadlines, npm ci,
and MOSAIC_REQUIRE_EDITOR_BUILD=1. No live state or network destinations.
## Tracking
Queue next from linked tree refused as designed; canonical next matched pinned brief.
Claim poe-0001 initially refused on Sage's queue lock; retry the same operation id.
Claim poe-0001 succeeded at canonical queue revision 3807 (in-progress).
During implementation, three string replacements briefly targeted canonical
packages/webui/src/public/{bus.js,user.js,chat.js} due to an incorrect command cwd.
Checked the complete diffs, surgically reversed exactly those replacements, and
applied them to this worktree. Sage independently confirmed canonical packages/
has no remaining diff (sage-3914). No tests or live actions occurred there.
poe-0002 requested the test slot. sage-3914 approved the exact serialized plan,
but CLEAR is pending Bentina returning her slot. No node/npm/browser commands
before CLEAR. New tests are thread-sharing.test.mjs with thread-sharing-fixture.mjs;
mutants.mjs runs four served-source mutants with assertion kills plus bus bypass.
## First verification (sage-3917 CLEAR)
Scratch logs: /mnt/storage/scratch/tmp/YU1G5B/.
Syntax 12/12 exit 0; npm-ci.log exit 0; editor-build.log exit 0;
thread-sharing.tap 7/7 exit 0. mutants.log and poe-208-mutants.json: four
absence-test assertion kills (exit 1 each), four bus bypass proofs (exit 0 each),
runner exit 0. Each mutant's individual log is poe-208-NAME-kill.tap or -bypass.tap.
Full serialized webui.tap stopped on an existing-test red: 44 pass, 1 fail before
termination; runner exit 1. Plugins not run. Slot returned to Sage via poe-0003.
Existing failure: chat-browser.test.mjs:123, uncertain writes/read failure draft
preservation, line 133 expects #chat-body after no-bus-host. The new current.thread
is not retained before list reads succeed, unlike old current.threads. A transient
read failure therefore hides the existing owner conversation. Proposed fix retains
same-thread metadata across refresh, never stale user grant scopes. No old tests
changed and no further execution pending Sage's ruling/CLEAR.
## Approved correction (sage-3918)
The old test is correct; this change broke its transient-failure draft contract.
Retain current.thread and shared only when the old id equals the requested id.
User chatGrants still start empty and are read fresh, so a stale hint is never
carried forward. A real 403/404 clears thread/shared metadata plus cached history,
drafts and operation forms. chat-browser.test.mjs remains byte-identical.
Fix applied statically; Sage will CLEAR the four mutants, full webui and plugins
suites after Bentina returns the slot. No node/npm/browser commands before then.
Clarification during static review: retain the existing distinction between a
thread-level refusal and a local bad-session selector (old row 272 B1 test).
All 403/404 responses clear visible thread/shared metadata, as before; thread-level
thread-not-owned/thread-not-found/access-refused also erase sibling cached history
and drafts. A bad-session selector never erases valid sibling drafts/recovery keys.
This preserves the existing invalidation contract rather than broadening the fix.
sage-3920 CLEAR: first mutant rerun stopped on a new-test failure, not a valid
mutant kill. /mnt/storage/scratch/tmp/8UlAhS/poe-208-composer-kill.tap shows null
old.thread on initial /chat: old?.id and undefined route id compared equal.
Fixed by requiring old to exist before either same-id metadata carry. This is a
new defect in the approved correction, no old test change. Rerunning the plan.
## Second slot outcome (sage-3920)
Scratch /mnt/storage/scratch/tmp/8UlAhS/.
Initial mutants.log exit 1 was the new null-previous-state defect above. Fixed;
mutants-r2.log exit 0: all four assertion kills exit 1, all four bypass proofs
exit 0 (individual logs overwritten by the successful rerun; summary JSON kept).
Full serialized webui.tap stopped on old tests: 114 top-level pass, 5 fail,
runner exit 1. The original draft-preservation failure and row 272 B1 both pass.
The new old-test failure is environmental: decide-sso.test.mjs cannot import
root dependency oauth4webapi (ERR_MODULE_NOT_FOUND); all five failures share it.
packages/webui npm ci installs its editor tooling, not root runtime dependencies.
No tests changed; plugins not run. Request root npm ci from pinned lockfile in
next slot before rerunning webui and plugins. Slot returned via poe-0004.
sage-3924 ruled the second red an environment gap, not a product defect. Approved
root npm ci --ignore-scripts from the pinned lock, full webui then plugins,
serialized. sage-3925 CLEAR starts that sequence. Push only after green; send
base/tip and every changed path's SHA-256 to Sage and Gladstone.
## Final builder verification (sage-3925 CLEAR)
Root npm ci --ignore-scripts exit 0 from pinned lock.
Full webui: exit 0, 418 pass, 0 fail, 0 skipped (271964 ms).
Plugins: exit 0, 119 pass, 0 fail, 0 skipped (823 ms).
Logs /mnt/storage/scratch/tmp/Et9xAa/{root-npm-ci.log,webui.tap,plugins.tap}.
Slot returned immediately after plugins via poe-0005. All new acceptance cases
are included in the full webui run. Four latest UI mutants and bus bypass proofs
remain green at /mnt/storage/scratch/tmp/8UlAhS/mutants-r2.log and
poe-208-mutants.json. No old tests changed. No bus authority/schema changes.
Acceptance: owner and collaborator can share/unshare own writable threads;
shared lists preserve the plain-list shape and show owner names safely as text;
shared threads cannot compose, resume, mutate sessions or reshare. Viewer old
own threads retain read history with no write controls. Mixed grants and live
downgrades hide writes only where coverage requires; revoked shares clear the
view. Direct browser POSTs bypass all UI checks and the real broker still
refuses viewer create/send/open/share. Existing exact governance notice retained.
Global scripts/test-*.sh gate and independent reviewer approval remain Sage's
landing gate; this builder does not claim those or any live deployment.
+28
View File
@@ -0,0 +1,28 @@
// Run only under Sage's CLEAR, with scratch TMPDIR and MOSAIC_REQUIRE_EDITOR_BUILD=1.
// Mutants alter only the fixture-served copy of chat.js; repository source is untouched.
import { spawnSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
const file = 'packages/webui/tests/thread-sharing.test.mjs';
const results = [];
for (const mutant of ['composer', 'open', 'share', 'create']) {
for (const [stage, pattern, expected] of [
['kill', '193d viewer UI has no', 1],
['bypass', '193d bypassed UI checks', 0],
]) {
const run = spawnSync(process.execPath, ['--test', '--test-concurrency=1', '--test-name-pattern=' + pattern, file], {
env: { ...process.env, POE_UI_MUTANT: mutant }, encoding: 'utf8', timeout: 90000,
});
const log = join(process.env.TMPDIR, `poe-208-${mutant}-${stage}.tap`);
writeFileSync(log, (run.stdout ?? '') + (run.stderr ?? ''));
const killedByAssertion = /ERR_ASSERTION/.test(run.stdout ?? '');
const passed = run.status === expected && (stage !== 'kill' || killedByAssertion);
results.push({ mutant, stage, exit: run.status, signal: run.signal, passed, log });
console.log(JSON.stringify(results.at(-1)));
if (!passed) {
writeFileSync(join(process.env.TMPDIR, 'poe-208-mutants.json'), JSON.stringify(results, null, 2));
process.exit(1);
}
}
}
writeFileSync(join(process.env.TMPDIR, 'poe-208-mutants.json'), JSON.stringify(results, null, 2));
-55
View File
@@ -1,55 +0,0 @@
{
"version": 1,
"agents": [
{
"host": "api.anthropic.com",
"port": 443,
"path": "adapters/claude-code; adapters/pi (anthropic)",
"note": "Candidate from S6; real-token provider-host measurement by Mos pending. Claude nonessential traffic disabled."
},
{
"host": "api.z.ai",
"port": 443,
"path": "adapters/pi (zai)",
"note": "Candidate for the configured zai provider; unmeasured until Mos runs provider-hosts.sh. No authority to widen on failure."
},
{
"host": "api.openai.com",
"port": 443,
"path": "adapters/pi (openai)",
"note": "S6 dummy-key CONNECT measured; real-token provider-host measurement by Mos pending."
}
],
"services": [
{
"host": "tasks.woltje.com",
"port": 443,
"path": "packages/tasks/src/vikunja.mjs:41",
"note": "fetch; requires NODE_USE_ENV_PROXY=1 and HTTPS_PROXY in container startup (I6)."
},
{
"host": "git.mosaicstack.dev",
"port": 443,
"path": "packages/bus/src/gitea-grant.mjs:115",
"note": "NOT PROXIED today: https.request agent:false with custom CA; transport ruling owned by Sage before broker container rollout."
},
{
"host": "git.mosaicstack.dev",
"port": 443,
"path": "packages/seat/src/working.mjs:471",
"note": "NOT VERIFIED through proxy: broker publishRemote spawns git with a scrubbed environment and credential on fd 3; core design excludes git. Sage transport ruling required."
},
{
"host": "discord.com",
"port": 443,
"path": "packages/cli/src/notifier-process.mjs:45 -> packages/discord/src/rest.mjs:45",
"note": "Notifier REST fetch only; requires NODE_USE_ENV_PROXY=1 and HTTPS_PROXY (I6)."
},
{
"host": "gateway.discord.gg",
"port": 443,
"path": "packages/discord/src/gateway.mjs:193",
"note": "NOT PROXIED today: native WebSocket in separate Discord connector, not notifier. Design names gateway; retained pending Sage transport ruling."
}
]
}
+5 -8
View File
@@ -1620,11 +1620,8 @@ are never rewritten or removed; corrections are new entries.
2026-10-24T00:41Z | Duckworth (Opus 5.5) | row 323 round 1 static review on 6a0f0f4f8 (sage-3865, sage-3870) | APPROVE (static) C1 Sage full gate cli, manifest 903ef022 matches, fields sourced, tests read shipped bytes, tester mutant real, N1-N2 doc (duckworth-0300) |
2026-10-24T01:13Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row325 / #1821 Case1 project key | New goldie/row325 worktree at99930362e; pinned brief60d78e44e. Draft JSON key correction, new real-project-byte tests, sole old line70 key edit under mos-1564. Static checks only; requesting Sage CLEAR, no queue writes.
2026-10-24T01:14Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row325 / #1821 verified | sage-3882: new3/3, business264/264, one key mutant killed/restored, new3/3 again, per-run fresh shortTMP/load/helperCLEAR. Four manifest entries verify; sole old assertion key change, no queue writes. Candidate push/report to Sage and Gladstone next.
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 I2 / #1652 pre-build recovery at ec86f2c84 | Blocked: canonical queue stale brief pin plus non-fetch broker outbound stop condition; sent Sage bentina-0001, recorded source drift in agents/bentina/work/queue-157/BUILD.md. No tests, Docker, credential reads, queue writes, commit or push.
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 first CLEAR stop | sage-3910 tests31/31 helper/config,11/11 proxy. Own output regression stops old config run3 (55 pass/1 fail); run4 not run, no Docker. Slot returned Sage bentina-0004; source-only correction applied under sage-3916, no rerun until next CLEAR. Logs in agents/bentina/work/queue-157.
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 candidate verified | sage-3919 config56/56/helper31/31; sage-3923 final proxy12/12 and install exit0 (rootless builds/pins, internal network, services fetch). Source-only transport gaps recorded under sage-3907. Slot returned bentina-0007; preparing own-branch candidate for Boyd then Darkwing, with Mos measurement/Sage full gate still pending. No protected branch/live-state/real-token action.
2026-10-24 | Bentina (d1ae468e-51ba-40c4-9513-b39ebbb40450) | Row157 / #1652 round2 B1/B2 | sage-3933 scoped corrections; sage-3936 archived red proves7defect assertions/5controls, fixed12/12, helper/config33/33, egress22/22, rootless install exit0. No old assertion changes. Slot returned bentina-0014; preparing followup commit/push for Boyd then Darkwing, provider measurement/Sage gate still pending.
2026-10-24 | Poe | Q246 / mos-1566, row 208 / 193d (#1706), poe/row208 | Started; approved design hash verified; implementation and isolated acceptance pending.
2026-10-24 | Poe | Row 208 / 193d (#1706), poe/row208 | Builder acceptance green: webui 418/418, plugins 119/119, four UI mutants killed with bus bypass proofs; candidate prepared for Gladstone then Duckworth guard review.
2026-10-24T04:29Z | Duckworth (Opus 5.5) | row 208 (193d) second review, guards only, on 4a4bef6c1 (sage-3934) | APPROVE (static), no bus change, every hidden control has a broker refusal, grant hint per-grant and self-only, N1 5 s poll (duckworth-0302) |
2026-10-24T04:27Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row265 / #1770 C2-09b rooms | Goldie owns build, Boyd reviews under sage-3915. New goldie/row265 worktree at ec86f2c84, claim rev3814; room verbs/parser/new tests prepared, no old tests changed. Sage-3932 slot: new parser14/14 and rooms18/18 passed; whole bus in progress before refusal mutants, stop rule active.
2026-10-24T04:35Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row265 / #1770 verified | First slot stopped at project-runtime import pin. mos-1567 narrowly authorizes pin and pure-source mount correction. sage-3942 rerun: sandbox10/10, parser15/15, rooms18/18, bus957/957, twelve refusal mutants killed/restored SHA71b32ee4, parser15/15 + rooms18/18 again. Slot returned goldie-0034; no further test runs. Boyd reviews RUNNER_FILES alongside rooms; own-branch push/claimant in-review next.
+21 -35
View File
@@ -1366,41 +1366,27 @@ before middleware. Same-uid callers can still read the file; separate service
uid is Jason's question on #385. The Authentik allowed-uid file remains an
identity input and is never reused for proof.
## Containerized image preparation (I2)
## `scripts/push-check.sh` — verify a prospective push
`scripts/images.sh build|list` uses RELEASE tags mosaic-core, mosaic-session
and mosaic-egress. It requires install.mode containerized and an explicit
Unix endpoint in the selected config. Build prepares an isolated runtime
source context and uses only the configured rootless daemon; list inspects
the three exact tags. It does not start the stack. See images/README.md.
`scripts/push-check.sh SHA EXPECTED_LOG [--row N]... [--base-ref REF] [--fetch]`
checks the repository at the caller's cwd; it never pushes or writes the queue.
Git checks cover the full repository even from a subdirectory; EXPECTED_LOG is
resolved relative to the original caller. Non-UTF-8 git paths refuse with exit 2.
EXPECTED_LOG must contain exactly `git log --oneline BASE..SHA` (including its
newlines). BASE defaults to `refs/remotes/origin/refactor`; `--base-ref` overrides
it. Only `--fetch` fetches origin, before resolving BASE.
`scripts/lib/docker.sh` is a sourceable Bash helper exposing
`mosaic_docker <arguments...>`. It uses common.sh's load_config (which reads
mosaic-config.mjs env), caches one successful rootless check in that Bash
process, and adds --host to every Docker call. The first call checks the
exit status of info before parsing .SecurityOptions as a JSON array for
exactly name=rootless. Failure prints endpoint-not-rootless, exits the
operation with status 2, and never forwards the requested command. Local
mode prints containerized-install-required. No ambient context, DOCKER_HOST
or caller-supplied mode/endpoint variable selects the target.
Success prints `LOG_OK`, `FF_OK`, `NO_BINARY` or `BINARY_REVIEWED n`,
`SECRETS_CLEAN`, `NO_ATTRIBUTION`, then `PUSH_READY BASE..SHA` as the last line.
A changed present blob counts as binary when numstat marks it binary or its
first 8000 committed bytes contain NUL, regardless of git attributes. Each
present binary needs its committed blob sha256 and exact path in an approved
latest-round candidate from a supplied row. A `changes` receipt blocks that round.
Commit candidates authorize only changed paths with matching bytes and mode.
Deleted binaries need no review. Queue reads retain canonical-checkout checks
and refuse unconfirmed durable tails with exit 2.
The config loader accepts optional install with mode local/containerized.
Absent defaults to local without adding a validation-output key; local forbids
endpoint, containerized requires
`^unix:///[^\s]+\.sock$`. Unknown keys, mode or bad type exit 2. validate
checks shape only and never changes config. env adds MOSAIC_INSTALL_MODE
and MOSAIC_DOCKER_ENDPOINT for the helper; no configVersion bump.
`scripts/test-install.sh` runs the I2 config/helper/proxy cases, then builds
and probes the images on MOSAIC_TEST_DOCKER_ENDPOINT (default explicitly
unix:///run/user/1000/docker.sock). It creates only i2-prefixed containers
and networks, cleans only those names, and refuses an unavailable or
non-rootless endpoint. It neither installs nor starts a daemon. It uses
scratch MOSAIC_CONFIG and dummy/no tokens. All test execution requires the
assigned test slot. Run with fresh short scratch TMPDIR and timeout -k 10.
Mos's real-provider measurement script and expected output are in
agents/bentina/work/queue-157/provider-hosts.sh and PROVIDER-EXPECTED.md.
The baked agents list is provisional until matching evidence appears on
#1652. The services contract notes transport gaps; I2 starts no broker behind
the proxy. Sage owns the ruling on those gaps before later install rows.
Exit 0 means ready; 1 means refusal (`STOP reason`, always the last line); 2 means
usage or environment error. A refusal never prints later success tokens.
`timeout -k 10 240 bash scripts/test-push-check.sh` exercises isolated scratch
repositories and the six required mutants; set TMPDIR to a scratch filesystem.
+92 -1
View File
@@ -207,6 +207,43 @@ install -o mosaic -g mosaic -m 0600 /root/<sha>.bundle /srv/mosaic/incoming/<sha
sudo -u mosaic -H git clone -q -b refactor /srv/mosaic/incoming/<sha>.bundle /srv/mosaic/src/mosaic-stack
```
If the first install refuses, use Mos's retry path (mos-1565, ops-0291). Bootstrap can already have
written `/srv/mosaic/.config/mosaic-dev/config.json` (`scripts/case2-install.sh:174`,
`scripts/bootstrap.sh:13`, `scripts/mosaic-config.mjs:412-424`). The config dir can also hold
`businesses/`, the business file, and library persona files and slot dirs
(`scripts/case2-install.sh:200-211`, `scripts/case2-persona-import.mjs:300-308`). A validation refusal
removes only the business file this run created; its directory remains (`scripts/case2-install.sh:217-219`).
The copy may already exist under `/srv/mosaic/.local/share/mosaic-stack/copies/<sha>`
(`scripts/case2-install.sh:80`, `scripts/stack-copy.sh:68`).
As root, move these three paths aside with a UTC suffix. Move, never delete, so their owner and mode
stay with them (mos-1565). This is the default-instance retry for the paths in this sheet:
```sh
(
cd / || exit 1
retry_stamp="$(date -u +%Y%m%dT%H%M%SZ)" || exit 1
for retry_path in /root/mosaic-stack /srv/mosaic/src/mosaic-stack /srv/mosaic/.config/mosaic-dev; do
if [ -e "$retry_path" ] || [ -L "$retry_path" ]; then
retry_saved="$retry_path.$retry_stamp"
if [ -e "$retry_saved" ] || [ -L "$retry_saved" ]; then
echo "STOP: retry destination already exists: $retry_saved" >&2
exit 1
fi
mv -T -- "$retry_path" "$retry_saved" || exit 1
fi
done
) || echo "STOP: could not move the first-install paths aside; do not continue"
```
Then rerun the first-install block from its first line. Never use the upgrade fetch below on clones
whose first install has not finished: it updates `refs/remotes/origin/refactor`, while copy requires
the commit to be reachable from both that ref and `refs/heads/refactor` (`scripts/stack-copy.sh:44-48`; mos-1565).
Recheck section 9 before starting units. If Jason already filled `claude-session.env`, the library
proof file or `library-gitea`, have him re-place those files under the new config dir with section 9's
owner and mode. Per-agent slots still use `mosaic credentials issue` or Approve, never a hand copy
(section 9; mos-1565). The moved config dir preserves the earlier files.
After a first install, once both units are running (section 10), adopt the installed copy before
any activate or upgrade. This step runs only on Mos's go, with Jason or Mos running it
(`scripts/stack-release.sh:14`):
@@ -254,6 +291,19 @@ When a run doesn't exit 0, send Mos the exit and the last lines of `/var/log/mos
the old copy. The next run refuses `args-differ`, and a render from that file would put the old copy back.
Move the file the result line names into place with `mv -f`. The two differ only in the `--copy` value.
To check a backup by hand before rollback or restore, run as the stack's user from its `bus/` directory:
```sh
sudo -u mosaic -H /bin/sh -c \
'cd "<dataRoot>/backups/stack/<activation>/bus" && sha256sum -c ../SHA256SUMS'
```
Expect `bus.sqlite: OK`; stop on a failed check. The set holds `bus/bus.sqlite`, and `SHA256SUMS` names
`bus.sqlite` relative to `bus/` (`scripts/stack-release.sh:475-482`). Running `sha256sum -c SHA256SUMS`
from the backup set's directory looks for `bus.sqlite` one directory too high (ops-0292). Restore and
rehearsal use the same `bus/` form
(`scripts/stack-release.sh:502`, `scripts/stack-release.sh:2244`).
After `case2-install.sh` succeeds, create the human CLI wrapper before running
`stack-copy.sh check --units external`. Creating this wrapper is a separate live
step requiring Mos's go (`scripts/stack-copy.sh:3`); the install does not create it. Run only on that go:
@@ -275,11 +325,52 @@ it. The wrapper must name the same `<sha>` copy as the units.
Precheck lines: none. Root's checkout at `<sha>` and the stack's user's clone: precheck line missing.
After install, `stack-copy.sh check --units external` reads the copy the units run.
Before running `case2-install.sh`, check the exact file you will pass as `--business-file`. Each
non-library `projects` key must equal the `id` in `<root>/.mosaic/project.json`, and that file must exist
in the stack's user's clone. Library entries are skipped by validate (`packages/business/src/cli.mjs:72`).
Run this read-only check as `mosaic` with section 2's pinned node:
```sh
sudo -u mosaic -H /opt/mosaic-stack/node-<v>/bin/node --input-type=module - '<business-file>' <<'MOSAIC_PROJECT_KEYS'
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
try {
const business = JSON.parse(readFileSync(process.argv[2], 'utf8'));
const projects = Object.entries(business.projects);
if (!projects.length) throw new Error('business file declares no projects');
for (const [id, entry] of projects) {
if (entry.kind === 'library') continue;
const file = join(entry.root, '.mosaic', 'project.json');
const project = JSON.parse(readFileSync(file, 'utf8'));
if (project.id !== id)
throw new Error(`project file ${file} has id ${project.id}, but business ${business.id} declares it as ${id}`);
console.log(`ok project ${id}: ${file} has id ${project.id}`);
}
} catch (error) {
console.error(`STOP: ${error.message}; do not install`);
process.exitCode = 1;
}
MOSAIC_PROJECT_KEYS
```
For .53 expect `ok project stack: /srv/mosaic/src/mosaic-stack/.mosaic/project.json has id stack`
(`.mosaic/project.json:3`, `docs/guides/case1-business.mosaic-stack.json:10-11`). Any STOP or nonzero exit
stops the install. Fix a mismatched business key, not the project id (mos-1563). The install otherwise
reaches that refusal at step 7, after making the copy (`packages/business/src/cli.mjs:80`,
`scripts/case2-install.sh:178`, `scripts/case2-install.sh:217`).
The stack's user's `scripts/case2-install.sh` (C2-13b, design 10 step 2b) makes the first copy with
`stack-copy.sh copy <sha>`. The copy runs `npm ci` from `package-lock.json`, which installs the pinned
harness, so the host needs to reach the npm registry during the install. The install runs no
`release.sh package` or `activate`.
A missing `.mosaic/project.json` only warns: `project <id>: no project file at <path>; it has no project
variables`. Validate reports that project `absent` and continues (`packages/business/src/cli.mjs:74-78`).
An install can therefore pass step 7 without the project's variables. It captures validate's output
and prints `business <id> validates` (`scripts/case2-install.sh:217-221`). Treat the missing-file warning as a stop,
place the correct project file and repeat the check above before continuing (Duckworth 323 N2;
`scripts/case2-install.sh:217-221`).
## 7. The args file
`/etc/mosaic-stack/dev.args` holds the `case2-unit.sh` arguments, one per line, with the serve flags after a
@@ -318,7 +409,7 @@ file with `stat -c '%U %a %n' PATH`, never `cat`. `<business>` is the host's bus
| Path | File | Mode | Owner | Profile | Precheck line |
|---|---|---|---|---|---|
| `/srv/mosaic/.config/mosaic-dev/secrets/` | `claude-session.env`, one line `ZAI_API_KEY=...` | 0600 | mosaic | library | `<path> sets ZAI_API_KEY` |
| `/srv/mosaic/.config/mosaic-dev/secrets/` | the Console's proof file, the path the args file's `--proof-file` names | 0600 or 0400 | mosaic | library | none. `case2-unit.sh` refuses at render. Precheck line missing. |
| `/srv/mosaic/.config/mosaic-dev/secrets/` | the Console's proof file, the path the args file's `--proof-file` names: one base64url line, at least 32 `[A-Za-z0-9_-]` characters, with one optional trailing LF (`packages/webui/src/serve.mjs:263-264`) | 0600 or 0400 | mosaic | library | none. Render checks metadata only and never opens the file (`scripts/case2-unit.sh:397-408`). Bad bytes refuse at Console start with exit 2 (`packages/webui/src/serve.mjs:267`, `packages/webui/src/cli.mjs:138-140`). Precheck line missing. |
| `/srv/mosaic/.config/mosaic-dev/secrets/<business>/` | `library-gitea` | 0600 | mosaic | library | After a unit start, wait for loopback `/healthz` with the bounded retry in section 10 before `case2-precheck.sh --business <business> --library-status`: `library-last-error library-credential-missing` until it's placed, then `library <project> last fetched ...` |
| `/srv/mosaic/.config/mosaic-dev/secrets/agents/<business>/<agent>/` | one file per service | 0600, dirs 0700 | mosaic | both | none. Filled only by `mosaic credentials issue`, or Approve after #436. Never by hand. |
+6 -4
View File
@@ -27,8 +27,9 @@ pane, persistent chat in the right, a draggable divider, document navigation, pi
right-click thesaurus. It is not a reskinned row 154 editor. The first useful release, opened at W7 after the required gates, must
pass writing, footnote, comment, tracked-change and page-layout save/reopen tests together.
D2. Q210/Q220 allow design and review now. O2 builds remain after the October 27 MVP work
and never take its gate slot. S0 baseline clearance is conditional on M1–M14, Fenton/Filbert
D2. Q210/Q220 allow design, review and builds ahead of the October 27 MVP work (mos-1571).
O2 rows never take the gate slot ahead of a v1 row; they land only when the slot would
otherwise sit idle. S0 baseline clearance is conditional on M1–M14, Fenton/Filbert
at `origin/fenton/q195-s0` 74ec32013 (#1798, Sage PASS comment 30917). Q234 approves the
exact measured frame CSP; the shell stays unchanged. mos-1363 requires W2 to remeasure zero
violations with style-src 'self'; no rfd style hash is approved. These gates are satisfied
@@ -585,14 +586,15 @@ Their implementation gates remain separate. No code row W1/W2/W6/W3a/W3b/W3/W4b/
before S0 PASS (recorded for this baseline, with M1–M14 still binding). Each also observes its explicit dependencies, source ownership, the CSP gate
and the MVP gate-slot priority. Q177 permits chaining reviewed candidates, not overlapping
owners. Sage assigns model-only seats. Each implementation reviewer is Opus 5.5 and did not
author the code. Fenton reviews this design and can review code only when not its author.
author the code. A Codex-built row gets its first reviews from a separate Codex thread (Q246);
the Opus 5.5 review runs once, on the landing candidate (mos-1571). Fenton reviews this design and can review code only when not its author.
Build order (mos-1470, Vikunja 571 comment 961): W7 first supplies reviewed conversion/format
routing code with production import still closed, then W8 lands the Connections view, then
W7's human opening step may open the first real chapter import. W8 is after W3/W4b and
W7's closed routing candidate, before that W7 opening step. This is Mos's build-order rule,
not a new REQ-LIB-8 condition or an addition to import acceptance. No converted real chapter
exists without the view. O2 code remains after 10-27 and never ahead of MVP rows.
exists without the view. O2 code may be built before 10-27 (mos-1571) but never lands ahead of MVP rows.
G1. Future code acceptance runs each touched test file independently and affected package
suites on Sage's slot after MVP priority, plus required repository checks. Source builds need
@@ -0,0 +1,103 @@
# Push check in the repository, with reviewed binaries
Written 2026-10-24 by Sage for Mos's mos-1572 ("Go with A for the guard"), after rows 265 and 310
raised the binary question (mos-1570, sage-3978).
## Brief: push-check.sh passes a binary only when the reviewed candidate lists its path and sha256
**Why.** The lead's push check is a scratch script, `/mnt/storage/scratch/sage-r64/pushcheck.sh`
(mos-0856/0857), so nobody has reviewed it and no test covers it. Its `NO_BINARY` step refuses any
path that `git diff --numstat BASE SHA` reports as binary (`-` in the first column). Row 265 had to
waive that step by hand for 22 gzip logs. Row 310 (W2) will need committed WASM, fonts and test blobs.
Mos chose option A: keep the binary check, and let a binary through only when its path and sha256
appear in the candidate a reviewer approved. Whether any large binary lands at all is a separate
question for Jason (Vikunja 618). This row builds the guard, not that answer.
**Read first.**
- The scratch script, copied under "The scratch script today" at the end of this brief. Don't read
`/mnt/storage/scratch/sage-r64` itself; it's Sage's scratch.
- `packages/queue/README.md`, sections on `move ID in-review` (manifest and commit candidates) and
`review verify-commit`. `packages/queue/src/review.mjs` for how a round's candidate and its receipts
are stored. Running `scripts/mosaic queue show N` prints them.
- `docs/plans/2026-10-20_force-push-guard.md`, row 211 (Darkwing, in progress). That row owns
`scripts/git-hooks/pre-push`, the hook install and `scripts/slot-busy.sh`. Don't touch those files,
and don't wire this check into a hook.
**What ships.**
1. `scripts/push-check.sh SHA EXPECTED_LOG [--row N]...` (new). It runs the scratch script's steps in
the same order and prints the same tokens, so the lead's push sequence only changes its path:
`LOG_OK`, `FF_OK`, `NO_BINARY` or `BINARY_REVIEWED <n>`, `SECRETS_CLEAN`, `NO_ATTRIBUTION`, then
`PUSH_READY BASE..SHA` as the last line. Every refusal prints one `STOP <reason>` line and exits 1.
A usage or environment error exits 2. It works in the repository the caller's cwd is in. The base
ref defaults to `refs/remotes/origin/refactor`, and `--base-ref REF` overrides it for tests. A fetch
happens only when `--fetch` is given, and the lead's sequence passes it.
2. The binary step. For each path that `git diff --numstat BASE SHA` marks binary and that exists at
SHA (a deleted binary needs no review):
- compute the sha256 of the blob at `SHA:path` (`git cat-file blob`, not the working tree);
- look for an approved candidate among the `--row` rows. A round counts only when it is the row's
latest round and has a receipt with verdict `approve` whose candidate equals that round's
candidate. A `changes` receipt in the same round blocks it;
- for a manifest candidate, the manifest must hold the line `<sha256> <path>` exactly;
- for a commit candidate, use the same rule as `queue review verify-commit`. The path must be one the
candidate changed from its first parent, and the sha256 of the candidate's blob must equal SHA's;
- no match prints `STOP binary <path> <sha256> not in a reviewed candidate`. With no `--row`, any
binary stops, which is today's behaviour.
Read the queue only through `scripts/mosaic queue show N` or the queue package's read functions.
Never write the queue or post anything.
3. The secret scan and the attribution check stay as they are. A binary passes the secret scan only
through the review above. Decompressing gzip to scan it is a separate row if anyone wants it
(mos-1570). Don't add it here.
4. `scripts/test-push-check.sh` (new), run by `timeout -k 10` like the other suites. It builds a
scratch bare remote, a scratch clone and a scratch queue. It never touches origin, the canonical
`docs/plans/queue.json` or a seat's queue lock. If the queue CLI can't read a scratch queue without a
full scratch copy of the repository, use a scratch copy. If that is impractical too, stop and ask
Sage before inventing a test-only override. Cases:
- a text-only range prints `NO_BINARY` and `PUSH_READY`;
- an unlisted binary stops, naming the path;
- a listed path with a different sha256 stops;
- an approved manifest candidate passes and prints `BINARY_REVIEWED 1`;
- an approved commit candidate passes. One whose blob for that path differs from SHA's stops;
- a round with a `changes` receipt, a superseded earlier round, and an approve receipt for a
different candidate each stop;
- a deleted binary passes with no `--row`;
- a non-fast-forward SHA, a log mismatch, an attribution line and a planted fake secret each stop,
and nothing after the stop prints;
- the last line is `PUSH_READY` only on success, so `| tail -1 | grep -q '^PUSH_READY '` is safe.
5. Mutants, each killed by the test: skip the sha256 comparison; accept a `changes` round; accept an
earlier round; read the working tree instead of the blob; let a binary through when `--row` is absent.
6. `docs/TOOLS.md`: one entry for `push-check.sh` with usage, the tokens and the exit codes.
**Files owned.** `scripts/push-check.sh`, `scripts/test-push-check.sh`, the new `docs/TOOLS.md` entry,
and your `agents/<seat>/work/queue-<row>/` packet (BUILD.md, plain-text evidence). Anything else needs a
brief amendment.
**Out of scope.** The pre-push hook and slot check (row 211). Deciding whether W2's WASM or W1's data
lands (Jason, Vikunja 618). Gzip scanning. Retiring the scratch script, which Sage does after landing.
**Gate.** A separate Codex thread does the first review. Dewey reviews once on the landing candidate
(guards go to a Claude reviewer, mos-1542). `scripts/test-push-check.sh` and every other
`scripts/test-*.sh` are green on Sage's landing gate, and the mutants are killed. Done when Sage's next
push runs through `scripts/push-check.sh` instead of the scratch script. Evidence is plain text
(BRIEF-TEMPLATE, mos-1570). Use a TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/XXXXXX`.
### The scratch script today
```bash
#!/bin/bash
# Push checks for an explicit sha (mos-0856/0857). Usage: pushcheck.sh SHA EXPECTED_LIST_FILE
# EXPECTED_LIST_FILE holds the exact `git log --oneline origin/refactor..SHA` lines, newest first.
set -o pipefail
cd /mnt/storage/src/mosaic-stack || exit 9
SHA=$(git rev-parse --verify "$1^{commit}") || { echo "STOP sha"; exit 9; }
git fetch -q origin || { echo "STOP fetch"; exit 9; }
BASE=$(git rev-parse refs/remotes/origin/refactor) || exit 9
diff <(git log --oneline "$BASE..$SHA") "$2" && echo LOG_OK || { echo "STOP log"; exit 1; }
git merge-base --is-ancestor "$BASE" "$SHA" && echo FF_OK || { echo "STOP ff"; exit 1; }
[ "$(git diff --numstat "$BASE" "$SHA" | awk '$1=="-"' | wc -l)" = 0 ] && echo NO_BINARY || { echo "STOP binary"; exit 1; }
if git diff "$BASE" "$SHA" | grep -E '^\+' | grep -iqE 'BEGIN [A-Z ]*PRIVATE KEY|ghp_[A-Za-z0-9]{20}|sk-ant-[A-Za-z0-9-]{10}|xox[bp]-|AKIA[0-9A-Z]{16}|(token|secret|password)["'"'"' ]*[:=] *["'"'"'][A-Za-z0-9_\-]{24,}'; then echo "STOP secrets"; exit 1; fi
echo SECRETS_CLEAN
if git log --format=%B "$BASE..$SHA" | grep -iqE 'co-authored-by|generated with'; then echo "STOP attribution"; exit 1; fi
echo NO_ATTRIBUTION
echo "PUSH_READY $BASE..$SHA"
```
+7
View File
@@ -59,6 +59,13 @@ fails it on any commit (row 323, mos-1561). A builder whose cli suite
can't run says so in the row, and the cli suite counts from the lead's
landing gate.
Evidence that lands with a row is plain text: logs, diffs, manifests. No
gzip or other binary file, so the lead's push guard runs its binary and
secret checks with no waiver. If a log is too big, cut it to the lines
that prove the claim. Or keep it off refactor, in the seat's scratch, and
cite its path and sha256 in BUILD.md (mos-1570, after row 265's 22 gzip
logs).
### Handoffs
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner
+12
View File
@@ -756,3 +756,15 @@ The guard is right. The test is the weak part: its prefix leaves no margin, and
timeout. Fix: shorten the prefix and fail on a launch-failed log line instead of waiting 20 s. That's an
old-test change, so it needs Mos's go (mos-1272). Bound to the next row that edits revoke-window.test.mjs.
Until then, docs/plans/BRIEF-TEMPLATE.md's Gate section sets TMPDIR to `mktemp -d /mnt/storage/scratch/tmp/XXXXXX`.
## ops-0282 B2-2 closed by row 326
Sage. Row 326 (#1822) landed in f58049de0 (33f49f7d5). docs/guides/host-run-sheet.md now gives the Console
proof file format in the .54 library table, so the B2-2 entry above is closed. Row226 N1-N4 stay open.
## Row 208 N1: user.js re-renders every 5 seconds
Sage, for Duckworth's N1 on row 208 (c31496, landed f58049de0). packages/webui/src/public/user.js:16
re-renders the user view on a 5 s timer while the tab is visible, whether or not anything changed. That's a
steady client and server load per open tab, not a correctness or guard issue. Fix: render on change, or poll the bus only for a change
marker. Bound to the next row that edits user.js.
+6 -5
View File
@@ -33,7 +33,7 @@ Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
<!-- mosaic-queue:begin -->
Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
Generated from `docs/plans/queue.json` revision 3844 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|---|---|---|---|---|---|---|---|---|
@@ -193,7 +193,7 @@ Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not e
| 154 | A WYSIWYG Console editor built from reviewable source (Q168) | researcher; reviewers dewey, filbert | #1648 | done | 148 done | Mos's decision 123 go on the license and candidate before any code is imported, and later on the public/ diff; Dewey and Filbert approve naming the candidate manifest; webui, bus, cli, project suites and every scripts/test-*.sh green. Needs row 148 done. (sage) | `docs/plans/2026-10-16_q168-wysiwyg-editor.md` § A WYSIWYG Console editor built from reviewable source | — |
| 155 | The human and the chief of staff configure the assistant (Q169) | darkwing; reviewers dewey, filbert | #1649 | done | 139 done, 141 done | Dewey and Filbert approve on #1649 naming the candidate manifest; Mos's decision 123 go on the public/ diff; bus, business, harness, cli, webui suites and every scripts/test-*.sh green; own window and tested migration if schema changes. Needs rows 139 and 141 done. (sage) | `docs/plans/2026-10-16_q169-assistant-config.md` § The human and the chief of staff configure the assistant | — |
| 156 | The host starts the landing run once a merge decision is approved | dewey; reviewers filbert | #1651 | done | 125 done, 149 done | Filbert approves on #1651 naming the candidate manifest; Sage's full gate rerun at the landing target; the re-pin needs Mos's go (sage) | `docs/plans/2026-10-18_landing-run-launch.md` § The host starts the landing run once a merge decision is approved | — |
| 157 | I2 install images, the install config key, the endpoint helper and the egress proxy | researcher; reviewers darkwing, filbert | #1652 | queued | 87 done, 163 done | Filbert and Darkwing approve on #1652 naming the candidate manifest, and Mos's provider-host output is on the issue; Sage's full gate green on the landing tree (sage) | `docs/plans/2026-10-18_containerized-install-rows.md` § I2 install images, the install config key, the endpoint helper and the egress proxy | Decision 96 orders I2-I6 after D1 and D2, which have no rows yet; the goal order sets priority |
| 157 | I2 install images, the install config key, the endpoint helper and the egress proxy | bentina; reviewers boyd, darkwing | #1652 | in-review, round 2, request posted | 87 done, 163 done | Boyd (Codex, first review) and Darkwing approve on #1652 naming the candidate manifest, and Mos's provider-host output is on the issue; Sage's full gate green on the landing tree (sage) | `docs/plans/2026-10-18_containerized-install-rows.md` § I2 install images, the install config key, the endpoint helper and the egress proxy | Decision 96 orders I2-I6 after D1 and D2, which have no rows yet; the goal order sets priority |
| 158 | I3 two broker listeners, the human-socket via value and the host relay | dewey; reviewers darkwing, filbert | #1653 | queued | 157 done | Filbert and Darkwing approve on #1653 naming the candidate manifest; Sage's full gate green on the landing tree (sage) | `docs/plans/2026-10-18_containerized-install-rows.md` § I3 two broker listeners, the human-socket via value and the host relay | Decision 96 orders I2-I6 after D1 and D2, which have no rows yet; the goal order sets priority |
| 159 | I4 the Console in a container with signed-in human authority | darkwing; reviewers dewey, filbert | #1654 | queued | 158 done | Filbert and Dewey approve on #1654 naming the candidate manifest; Sage's full gate green on the landing tree; lands before M3 (sage) | `docs/plans/2026-10-18_containerized-install-rows.md` § I4 the Console in a container with signed-in human authority | Decision 96 orders I2-I6 after D1 and D2, which have no rows yet; the goal order sets priority. B1 is decision 147 |
| 160 | I5 managed sessions as containers | researcher; reviewers dewey, filbert | #1655 | queued | 158 done | Filbert and Dewey approve on #1655 naming the candidate manifest; Sage's full gate green on the landing tree (sage) | `docs/plans/2026-10-18_containerized-install-rows.md` § I5 managed sessions as containers | Decision 96 orders I2-I6 after D1 and D2, which have no rows yet; the goal order sets priority |
@@ -244,7 +244,7 @@ Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not e
| 205 | 193b1a: the message filter (#1703; FED R3 and 193b1b build on it) | morgana; reviewers duckworth | #1703 | done | 196 done | Duckworth approves the manifest; bus suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193b1a, the message filter | Landed ce6a96ad6 fast-forward on 0fa7bf421 (3 commits, 4 paths, manifest 858463b1). Duckworth APPROVE r1 c30375 (signed :124 test diff, mos-1262). Gate: Morgana slot205 10 files, bus 652/0, M1 M11-M14 killed, 0 skips; Sage test-*.sh 10/10, 3 by-design skips. N1/N3 to 205b or R3, N2 to 193b2 brief, N4 = #1782 before any user thread. Rebase next: 268 r4, 194, 236, 253, 274. |
| 206 | 193b2: thread shares, schema 3k-3l after the #436b build (#1691; live needs a --migrate window with Jason) | launchpad; reviewers duckworth | #1704 | done | 205 done, 234 done, 236 done, 275 done | Duckworth approves the manifest; bus suite, cli suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193b2, thread shares (schema 3l) | Landed 0cbe2e9b3 fast-forward on 17f415792 (10 commits, 22 paths), pushed 19:47Z. Duckworth APPROVE rev 3322 c30795 on 0cbe2e9b3; request rev 3320/3321 c30793 posted as jarvis. Gate (Launchpad, 9538e0fc8, tip differs in BUILD.md only): bus 850/0, cli 869/0, ten scripts/test-*.sh rc=0, touched files whole green, M2-M4 M7-M9 and Q2 killed. One placed fixture red (mos-1272). Schema 3l to 3m; live --migrate is Mos's go under Q173. |
| 207 | 193c: mosaic access user verbs and --level viewer (#1691) | megavolt; reviewers duckworth | #1705 | done | 202 done | Duckworth approves the manifest; cli suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193c, mosaic access user verbs and --level viewer | LANDED c4f77373f 22:49Z 10-22 (fast-forward from f0e3dcdf4). Rebuild of gated 9bb406c6c: range-diff 4/4 =, patch-ids equal, 4 manifest blobs equal. Duckworth APPROVE c30849 rev 3356. Gate on 712f79c0a under sage-3041: access.test 31/31, M1-M8 killed, cli 894/894, ten scripts green. N1 DEFERRED (next access.test row). No live change. |
| 208 | 193d: Console shared list, share controls and viewer read-only (#1691; also after FED R6 when filed) | morgana; reviewers duckworth | #1706 | queued | 192 done, 206 done, 294 done | Duckworth approves the manifest; webui suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193d, the Console's shared list, share controls and viewer read-only | — |
| 208 | 193d: Console shared list, share controls and viewer read-only (#1691; also after FED R6 when filed) | poe; reviewers duckworth, gladstone | #1706 | done | 192 done, 206 done, 294 done | Gladstone (Codex, first review) and Duckworth approve the manifest; webui suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193d, the Console's shared list, share controls and viewer read-only | LANDED f58049de0 (build 4a4bef6c1, Duckworth note f58049de0), combined with row 326 on one full gate 04:36-04:51Z: all package suites except queue 0 fail 0 skip (webui 418, plugins 119, cli 986), every scripts/test-*.sh green. Gladstone c31495, Duckworth c31496. #1706 closed c31504. N1 user.js 5 s re-render to DEFERRED. Bundle 9fc7754a. |
| 209 | FED R8: the hub window on 10.1.1.54, a recorded acceptance (#414, r4.11 b93f098f0; live window run by Jason and Mos, after the Case 2 install #416, Bean and Chet live and both Hermes gateways stopped) | sage; reviewers duckworth | — | queued | — | Every entry check's output is in the R8 record with who ran it; Jason signs in to the hub and sends one message, Melanie revokes, his next request is refused; Duckworth checks the record (sage) | `docs/plans/2026-10-20_federation-r1-r2-r5.md` § Brief: FED R8, the hub window on 10.1.1.54 | Stays queued until its entry gates near: Case 2 install on .54 (#416, by 2026-10-27), Bean and Chet live, both Hermes gateways on .54 stopped, R7, #412 phase 2, #419 answer. Filed per mos-1073 after duckworth-0034 (Sage) |
| 210 | Agent host access with sudo through the credential store: grant per agent and host, the stack does ssh and sudo, the agent never sees the key (#1707, Vikunja #424, Q183/Q184, due 10-27; chained on row 199's candidate) | gizmoduck; reviewers duckworth | #1707 | done | — | Duckworth approves DESIGN.md, then the manifest; bus, business, cli suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_host-credentials.md` § Brief: agent host access with sudo through the credential store (#424) | Landed f5753c0e8 on refactor (base 9d165be8c), pushed. Patch-id 723b1c83 matches git diff ad95c888c..0485384e9 on the 23 manifest paths; landed files pass r5 manifest 688d38b9. Duckworth approve r2 rev 2592, r3 29851. Mutants 52/53, M22b equivalent (rev 2584). Gate on ad95c888c+patch: packages 2796/0, scripts 429/0; landed blobs equal the gate tree. |
| 211 | Force-push guard: scripts/git-hooks/pre-push refuses non-fast-forward and delete pushes from every worktree, and scripts/slot-busy.sh exits 1 on BUSY (#1709, Vikunja #426, mos-1078/1083) | darkwing; reviewers dewey | #1709 | in-progress | — | Dewey approves the manifest; the new or changed scripts/test-*.sh and every other scripts/test-*.sh green on Sage's gate; the mutants killed; hook installed in the canonical checkout with a scripted refused force-push (sage) | `docs/plans/2026-10-20_force-push-guard.md` § Brief: a pre-push hook that refuses force-pushes, and a busy check that fails on BUSY (#426) | — |
@@ -301,7 +301,7 @@ Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not e
| 262 | Email E06: IMAP source module (post-v1) | unassigned | #1767 | queued | 261 done | Reviewer approves the manifest; the row's named tests whole plus the mail suite and touched suites green on Sage's slot; reviewer-named mutants killed (post-v1, MVP keeps slot priority) (sage) | `docs/plans/2026-10-21_post-v1-row-briefs.md` § Brief: E06 IMAP source module | — |
| 263 | Email E07: address and rule evaluator (post-v1) | unassigned | #1768 | queued | 258 done, 259 done, 260 done | Reviewer approves the manifest; the row's named tests whole plus the mail suite and touched suites green on Sage's slot; reviewer-named mutants killed (post-v1, MVP keeps slot priority) (sage) | `docs/plans/2026-10-21_post-v1-row-briefs.md` § Brief: E07 address and rule evaluator | — |
| 264 | T1 item 7: independent sessions, compaction counts and clear context | gizmoduck; reviewers duckworth | #1769 | done | — | The named reviewer approves the manifest. The design's T1-T7 acceptance tests run whole plus every touched suite on Sage's slot; T3 waits on Vikunja 466 (N2, Claude's compaction count format and the managed dataRoot reader). The design's section 6 mutants are killed. (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | LANDED 84f53540d ff on e479521d3 14:18:23Z, 21 commits = gizmoduck/row264-r13 tip (no rewrite, range-diff identity). Manifest 257408e9 approved Duckworth rev 3214 (c30640). Gate r13 2cc34aef8: 20 files, bus 827, cli 837, webui 345/1 skip, 10 shells; M1-M4 assertion kills (mos-1279 B, M2 4 files). Overlap none (13, 257). N1 actor_run binds row 269; N2 esbuild skip later gate (sage-2695). |
| 265 | Case 2 C2-09b rooms: room.open, room.send with @tags, room.read | rocko; reviewers dewey | #1770 | queued | 219 done | Dewey approves the manifest. The brief's tests run whole plus the bus suite and every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-09b rooms: room.open, room.send with @tags, room.read | — |
| 265 | Case 2 C2-09b rooms: room.open, room.send with @tags, room.read | goldie; reviewers boyd | #1770 | done | 219 done | Boyd approves the manifest. The brief's tests run whole plus the bus suite and every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-09b rooms: room.open, room.send with @tags, room.read | LANDED 200dd396c (cherry-pick of ef4c93853, 50 blobs equal, log appends equal) 05:13Z on full gate 04:57-05:12Z: all package suites except queue 0 fail 0 skip (bus 957, cli 986, seat 218), every scripts/test-*.sh green. Boyd c31501 incl mos-1567 mount read. 22 gzip logs scanned decompressed, clean. #1770 closed c31510. Bundle 02c3c286. |
| 266 | Case 2 C2-10c Console room view: merged timeline and @name completion | researcher; reviewers dewey | #1771 | queued | 225 done, 265 done | Dewey approves the manifest. The webui suite is green on Sage's gate, along with every touched suite. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-10c Console room view: merged timeline and @name completion | Haiku high subagent under decision 129. |
| 267 | Case 2 C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | morgana; reviewers filbert | #1772 | queued | 216 done, 217 done, 224 done | Filbert approves the manifest. The brief's tests and the end-to-end crossover run whole plus every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | R8 accepted (sage-2218). Webby's R8 noise note (gosalyn-0127) is for the builder. |
| 268 | Case 2 C2-01c playground levels: universe-level prefixes and draft universes | gizmoduck; reviewers webby | #1773 | done | — | Webby approves the manifest. The brief's tests run whole plus the library suite and every touched suite on Sage's slot; the brief's gate grep over packages and docs/case2 finds no library-playground-outside-book. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01c playground levels: universe-level prefixes and draft universes | Landed c27207f6e fast-forward on ce6a96ad6 (1 commit, 9 paths, digest caa0fa4e). r4 is a carry of r3 aca53c4f0: range-diff =, patch-id 2942acc8 equal, blobs equal. Webby APPROVE round 2 on ecd9b35eb stands; carries webby-0112 (r3) and webby-0113 (r4, notes 34e1ab2bf). Gate: Gizmoduck r4 rerun brief 14/14, library 83/83, business 209/209, 0 skips; gate grep empty. 194 rebases next (business.test.mjs). |
@@ -361,7 +361,8 @@ Generated from `docs/plans/queue.json` revision 3788 by `queue render`. Do not e
| 322 | F8/F9: case2-install.sh runs the library persona import from the stack copy, and its refusal carries the error line (433 Run B, ops-0273) | goldie; reviewers morgana | #1818 | required; done | — | Morgana approves (install path). The new case2-install test fails on 094124b4e and passes on the candidate; a mutant restoring REPO at :185 is killed; Sage's full landing gate runs every package suite and scripts/test-*.sh. Then ops-01 reruns Run B P1. (sage) | `docs/plans/2026-10-23_host-fixes-1816-brief.md` § Brief: F8 and F9, the library persona import runs without the clone's node_modules | LANDED 467b05fe2, cherry-pick of 7b4ea4217 on 860afc2b6; added and removed lines identical, patch-id differs only by BUILD-LOG/SESSIONS append context (both sides kept). Pushed with tip 3569bf54f 23:32:54Z 10-23. Morgana APPROVE c31451 rev 3758, N1-N3 non-blocking. Sage full gate GREEN 23:32:28Z on 3569bf54f, all suites 0 fail, every scripts/test-*.sh rc 0. Next: ops-01 Run B P1 rerun on 467b05fe2 (mos-1553). Live unchanged 45dca69f6. |
| 323 | Case 1 factory business file for .53: docs/guides/case1-business.mosaic-stack.json plus a loader and factory-check test (#414 stage 2, ops-0277) | goldie; reviewers duckworth | #1819 | done | — | Sage full gate: all package suites except queue plus every scripts/test-*.sh (mos-1455); Duckworth review; new test file and one bus.tester mutant on CLEAR (sage) | `docs/plans/2026-10-23_case1-business-file-brief.md` § Brief: the Case 1 factory business file | LANDED 571376c30 + evidence 6a0f0f4f8 (ff from dd6cb5872), tip 99930362e. Duckworth approve c31477 rev 3778 on 6a0f0f4f8. Sage gate green 00:54Z on 6a0f0f4f8: all package suites 0 fail, every scripts/test-*.sh rc 0. Root cause of the revoke-window red: TMPDIR length, tools.sock over 107 bytes (session.mjs:278). Brief template rule and DEFERRED entry in c25799928 (mos-1561). N1/N2 doc only. |
| 324 | Tracker optional in the business file (Q247) | goldie; reviewers duckworth | #1820 | done | — | New tests only. Absent tracker: loader and validate pass, the bus boots with no credential-file error and no @sync. Present tracker unchanged. A mutant that skips the presence check is killed. Then whole touched test files, then the full landing gate. (sage) | `docs/plans/2026-10-23_optional-tracker-brief.md` § Brief: tracker becomes optional in the business file | LANDED 7e59ba8b3 + evidence dbdfcfd52 (ff from 3569bf54f), pushed with tip dd6cb5872 00:06:58Z 10-24. Duckworth APPROVE c31470 rev 3770 (static, N1 doc only). Goldie: new 5/5 and 4/4, business.test 21/21, presence mutant killed. Sage full gate GREEN 00:05:49Z on 64ea54083: every package suite 0 fail 0 skip (business 254, cli 986, bus 924, plugins 119, webui 411), every scripts/test-*.sh rc 0. Live unchanged 45dca69f6. Run B N1 for ops-01 P2/P3 (mos-1558). |
| 325 | Case 1 business file: re-key project to stack to match .mosaic/project.json, plus a project-file test (#414 stage 2, ops-0288, mos-1563) | goldie; reviewers gladstone | #1821 | in-review, round 1, request posted | — | New test file and packages/business whole on CLEAR; one key mutant killed; Gladstone review; Sage full landing gate (mos-1455) (sage) | `docs/plans/2026-10-24_case1-project-key-brief.md` § Brief: re-key the Case 1 project to stack | — |
| 325 | Case 1 business file: re-key project to stack to match .mosaic/project.json, plus a project-file test (#414 stage 2, ops-0288, mos-1563) | goldie; reviewers gladstone | #1821 | done | — | New test file and packages/business whole on CLEAR; one key mutant killed; Gladstone review; Sage full landing gate (mos-1455) (sage) | `docs/plans/2026-10-24_case1-project-key-brief.md` § Brief: re-key the Case 1 project to stack | LANDED c5f96836d (ff from 99930362e), tip 3d2569ce2. Gladstone approve c31484 rev 3788 on c5f96836d. Sage gate green 01:31Z on c5f96836d: all package suites 0 fail (business 264, cli 986), every scripts/test-*.sh rc 0. Project key is now stack, matching .mosaic/project.json; new case1-project-key test checks the real project file. Old-test line :70 under mos-1564. |
| 326 | Host run sheet: five operator gaps (B2-2 proof format, 323 N2, pre-install key check, failed-first-install retry, backup SHA256SUMS form) | goldie; reviewers gladstone | #1822 | done | — | Gladstone approves the manifest; every new claim cites its line; no test reads the sheet; Sage's full landing gate green (sage) | `docs/plans/2026-10-24_run-sheet-doc-brief.md` § Brief: five run-sheet gaps | LANDED 33f49f7d5 (cherry-pick of a1b13c60e, patch-id match, sheet e4c14516) in f58049de0 with row 208 on one full gate 04:36-04:51Z, all suites green. Gladstone c31489. #1822 closed c31507. B2-2 closes in DEFERRED. Bundle 9fc7754a. |
<!-- mosaic-queue:end -->
+1707 -33
View File
File diff suppressed because it is too large. Load diff
-34
View File
@@ -1,34 +0,0 @@
# Containerized install images (I2)
All images use the I1 base digest
`node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20`
(Node 24.21.0). A base or contract change requires a reviewed commit and
new build. Tags are mosaic-core:RELEASE, mosaic-session:RELEASE and
mosaic-egress:RELEASE, where RELEASE is the repository file's value.
- core: packages, scripts, roles, contracts, production npm dependencies and
RELEASE, under /opt/mosaic. No git is installed. NODE_USE_ENV_PROXY=1
enables fetch proxy support; startup in I6 supplies HTTPS_PROXY.
- session: core plus global Claude Code 2.1.296 (decision 91; harness README)
and pi at package.json's exact pin, currently 0.85.1. Claude nonessential
traffic is disabled. No host MOSAIC_CLAUDE_BIN is used.
- egress: Node base, dependency-free packages/egress and baked egress contract.
Its required argument selects agents or services. No engine socket mount.
`scripts/images.sh build` and `list` require a valid scratch/operator
containerized config and rootless endpoint. Both use scripts/lib/docker.sh;
local mode refuses before Docker. Build first verifies the endpoint, then
copies only versioned runtime source plus I2's explicit new runtime paths
into an ephemeral context. No .git, host credentials, agent work directories,
or host node_modules enter the context. npm never receives a credential.
These are image definitions, not a deployment: I4-I6 own mounting, hardening,
networks, process entry points and actual stack launch. All run as container
uid 0; the later launchers must set read-only rootfs, dropped caps, no new
privileges, resource limits and their exact allowed mount lists.
Verification is scripts/test-install.sh with a scratch config and explicit
rootless endpoint. It builds images, checks pins, runs an internal-network
CONNECT test, and exercises the Vikunja and notifier REST clients through
the services proxy without a real credential. No broker is started behind
that proxy by I2. The provider-host measurement is a separate Mos gate.
-12
View File
@@ -1,12 +0,0 @@
FROM node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
WORKDIR /opt/mosaic
COPY package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund && npm cache clean --force
COPY packages/ packages/
COPY scripts/ scripts/
COPY contracts/ contracts/
COPY roles/ roles/
COPY RELEASE ./
ENV NODE_USE_ENV_PROXY=1
USER 0
CMD ["node", "packages/bus/src/process.mjs"]
-7
View File
@@ -1,7 +0,0 @@
FROM node@sha256:d6aa754f16b3197301076f047b5def2f02ea1dbbc2ca920407d46d7ec7f87b20
WORKDIR /opt/mosaic
COPY packages/egress/ packages/egress/
COPY contracts/egress.json contracts/egress.json
USER 0
EXPOSE 3128
ENTRYPOINT ["node", "packages/egress/src/cli.mjs"]
-9
View File
@@ -1,9 +0,0 @@
ARG CORE_IMAGE
FROM ${CORE_IMAGE}
ARG PI_VERSION
# Decision 91 and packages/harness/README.md pin Claude Code to this version.
RUN npm install -g --no-audit --no-fund @anthropic-ai/[email protected] @earendil-works/pi-coding-agent@${PI_VERSION} \
&& npm cache clean --force && rm -rf /root/.npm /tmp/*
ENV CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
USER 0
CMD ["node", "--version"]
+3
View File
@@ -242,6 +242,9 @@ a receipt with empty `changes`. A refusal is `action.refused` with its code alon
| `replies` | `{id}`: a message UUID; human and reader sessions only. The messages whose `in_reply_to` is that id, oldest first, at most 50. Writes nothing. |
| `project.create` | `{name,repo?}`; human sessions only (row 121, #1615). `repo` must name one of the business's `projects` (`unknown-repo`); a second project with the same name refuses `project-exists`. Returns `{id}`. |
| `project.list` | No args; human sessions only. The business's projects, oldest first. |
| `room.open` | `{project}`; human only. Creates/reuses the caller's canonical member threads, named by sorted `THREAD_DEFINITIONS` instance keys. A taken name/owner/instance refuses `room-thread-name-taken` atomically. Returns `{project,threads:[{id,instance,name}]}`. |
| `room.send` | `{project,body}`; human only, room must be fully open (`room-not-open`). Leading whitespace and `@name` tokens (including a final EOF tag) address current display names or member keys; no tags broadcasts, repeats send once. Unknown/ambiguous members refuse `unknown-addressee`/`addressee-ambiguous`; tags-only refuses `invalid-request`. Stores the original body, one normal message/route/human.input per target in one transaction, with one shared message timestamp. Returns `{ids,to}` in member-key order. |
| `room.read` | `{project,after?,limit?}`; human only, complete owned room. Oldest first across all sessions of member threads. Returns entries with first-message fields plus `thread`, `session`, `instance`, `ids` and `to`. Only consecutive human rows with equal at/body/class and distinct instances merge; repeated instances split identical sends. `limit` counts merged entries (default 50, 1–200). Continue after the last entry's last `ids` element; an `after` outside the room refuses `message-not-found`. No tester access or schema change. |
| `thread.create` | `{project,name}`; human sessions only. The thread belongs to the caller and to the business's one thread instance (Projects and threads, below). Returns `{id,instance}`. Refuses `project-not-found`, `thread-exists`, `thread-instance-missing` or `thread-instance-ambiguous`. |
| `thread.list` | `{project?,shared?}`; human sessions only. The caller's own threads, oldest first. With `shared: true` (row 206), the threads others own that the caller may read (Thread shares, below), each row adding `owner`. |
| `thread.open` | `{thread,session?}`; human sessions only. Records a `thread_opens` row, which asks the launcher to start the thread's session. Returns `{opened}`. |
+14
View File
@@ -0,0 +1,14 @@
// C2-09b: a pure leading-address parser, shared with the room UI's parity test.
// The remainder is for empty-body checks; the broker always stores the original input.
export function parseAddress(input) {
if (typeof input !== 'string') throw new TypeError('body must be a string');
const tags = [];
let body = input.trimStart();
while (true) {
const token = /^@(\S+)(?:\s+|$)/u.exec(body);
if (!token) break;
tags.push(token[1]);
body = body.slice(token[0].length);
}
return { tags, body };
}
+94
View File
@@ -1,4 +1,5 @@
import { randomUUID, randomBytes, createHash } from 'node:crypto';
import { parseAddress } from './address.mjs';
import { isAbsolute, dirname } from 'node:path';
import { initializeControl, processingStatus } from '../../processing-control/src/state.mjs';
import { NAME as HARNESS_NAME } from '../../plugins/src/kinds.mjs';
@@ -6418,6 +6419,99 @@ export class Broker {
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
}
// C2-09b: canonical per-instance threads form one human-owned project room.
case 'room.open':
case 'room.send':
case 'room.read': {
this.#human(s);
const opening = verb === 'room.open';
const sending = verb === 'room.send';
keys(a, opening ? ['project'] : sending ? ['project', 'body'] : ['project', 'after', 'limit'],
sending ? ['project', 'body'] : ['project']);
const project = identifier(a.project);
if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', project, s.business)) fail('project-not-found');
const members = Object.keys(b.roles).filter((instance) => this.#threaded(s.business, instance)).sort();
if (!members.length) fail('thread-instance-missing');
const owner = this.#owner(s);
// Preflight all collisions/missing threads before open creates or send writes anything.
const threads = members.map((instance) => {
const t = this.#store.get('SELECT * FROM threads WHERE project=? AND name=?', project, instance);
if (t && (t.instance !== instance || t.owner !== owner))
fail(opening ? 'room-thread-name-taken' : 'room-not-open');
if (!t && !opening) fail('room-not-open');
return t ?? { instance, name: instance };
});
if (opening) {
return { project, threads: threads.map((t) => ({
id: t.id ?? this.#dispatch(s, 'thread.create', { project, name: t.name, instance: t.instance }).id,
instance: t.instance, name: t.name,
})) };
}
if (sending) {
string(a.body, 32768);
const addressed = parseAddress(a.body);
if (!addressed.body.trim()) fail('invalid-request');
const selected = new Set();
for (const tag of addressed.tags) {
const candidates = new Set([this.#named(s.business, tag), tag].filter((instance) => members.includes(instance)));
if (!candidates.size) fail('unknown-addressee');
if (candidates.size > 1) fail('addressee-ambiguous');
selected.add([...candidates][0]);
}
const targets = threads.filter((t) => !addressed.tags.length || selected.has(t.instance));
const sessions = targets.map((t) => {
const session = this.#defaultSession(t.id);
this.#sessionFence(session);
this.#checkMessaging(s.business, 'human', t.instance);
return session;
});
const at = this.#now();
const ids = targets.map((t, i) => {
const mid = randomUUID();
this.#store.run(
'INSERT INTO messages(id,at,business,from_role,from_run,to_role,class,in_reply_to,decision,corrects,body) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
mid, at, s.business, 'human', 'human-cli', t.instance, 'INFO', null, null, null, a.body,
);
this.#route(s.business, mid, t.instance, sessions[i]);
this.#event(s, 'human.input', { kind: 'instruction', message: mid }, mid);
return mid;
});
return { ids, to: targets.map((t) => t.instance) };
}
const limit = a.limit ?? THREAD_PAGE;
if (!Number.isInteger(limit) || limit < 1 || limit > THREAD_PAGE_MAX) fail('invalid-request');
const tids = threads.map((t) => t.id);
const placeholders = tids.map(() => '?').join(',');
let after = 0;
if (a.after !== undefined) {
after = this.#store.get(
`SELECT m.seq FROM messages m JOIN message_threads mt ON mt.message=m.id WHERE m.id=? AND m.business=? AND mt.thread IN (${placeholders})`,
identifier(a.after), s.business, ...tids,
)?.seq;
if (after === undefined) fail('message-not-found');
}
// At most one row per instance joins a group; one lookahead row completes the last page.
const rows = this.#store.all(
`SELECT m.*,mt.thread,mt.session,t.instance FROM messages m JOIN message_threads mt ON mt.message=m.id JOIN threads t ON t.id=mt.thread
WHERE m.business=? AND mt.thread IN (${placeholders}) AND m.seq>? ORDER BY m.seq LIMIT ${limit * members.length + 1}`,
s.business, ...tids, after,
);
const entries = [];
let previous;
for (const m of rows) {
const entry = entries.at(-1);
if (entry && m.from_role === 'human' && previous.from_role === 'human' && m.seq === previous.seq + 1
&& m.at === previous.at && m.body === previous.body && m.class === previous.class && !entry.to.includes(m.instance)) {
entry.ids.push(m.id);
entry.to.push(m.instance);
} else {
if (entries.length === limit) break;
entries.push({ ...m, ids: [m.id], to: [m.instance] });
}
previous = m;
}
return entries;
}
// A thread belongs to the user who made it and to one declared thread instance.
// Row 177 (#1675): a tester's thread is owned by `tester`, so neither reaches the other's.
case 'thread.create': {
+1
View File
@@ -3,3 +3,4 @@ export { startBroker } from './runtime.mjs';
export { Client } from './client.mjs';
export { views } from './views.mjs';
export { busBusiness } from './business.mjs';
export { parseAddress } from './address.mjs';
+16
View File
@@ -0,0 +1,16 @@
// C2-09b / C2-10c: reusable parser vectors. Keep the expected result independent of the parser.
export const addressCases = [
['one', '@continuity x', { tags: ['continuity'], body: 'x' }],
['many', '@bean @chet hello', { tags: ['bean', 'chet'], body: 'hello' }],
['repeat', '@bean @bean hi', { tags: ['bean', 'bean'], body: 'hi' }],
['plain', 'hello @bean', { tags: [], body: 'hello @bean' }],
['stop', '@bean hello @chet', { tags: ['bean'], body: 'hello @chet' }],
['EOF tag', '@continuity', { tags: ['continuity'], body: '' }],
['EOF last tag', '@bean @chet', { tags: ['bean', 'chet'], body: '' }],
['leading whitespace', ' \t\n@bean\t@chet\nhello ', { tags: ['bean', 'chet'], body: 'hello ' }],
['tags only', '@bean \t\n', { tags: ['bean'], body: '' }],
['empty', '', { tags: [], body: '' }],
['whitespace', ' \t', { tags: [], body: '' }],
['bare at', '@ hello', { tags: [], body: '@ hello' }],
['punctuation is a name', '@unknown, hi', { tags: ['unknown,'], body: 'hi' }],
];
+17
View File
@@ -0,0 +1,17 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { parseAddress } from '../src/address.mjs';
import { addressCases } from './address-cases.mjs';
for (const [label, input, expected] of addressCases) {
test(`parseAddress: ${label}`, () => assert.deepEqual(parseAddress(input), expected));
}
test('parseAddress rejects non-text without coercion', () => {
for (const input of [null, undefined, {}, 1]) assert.throws(() => parseAddress(input), TypeError);
});
// mos-1567: new coverage, separate from the pre-existing broker import pin.
test('the mounted address parser has no import dependency', () => {
assert.doesNotMatch(readFileSync(new URL('../src/address.mjs', import.meta.url), 'utf8'), /\bimport\b/);
});
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more