Compare commits
202
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af7c5bb094 | ||
|
|
6850e43908 | ||
|
|
9093999650 | ||
|
|
5c472db38d | ||
|
|
067c60ac0f | ||
|
|
cf5966469e | ||
|
|
66df77d113 | ||
|
|
023a600431 | ||
|
|
0006f03f47 | ||
|
|
10aa996fb0 | ||
|
|
97dc3fa1ea | ||
|
|
3405d8cae5 | ||
|
|
53f0445bbe | ||
|
|
c6ea27aaba | ||
|
|
61c17c3978 | ||
|
|
e1853039f5 | ||
|
|
9ffd417c70 | ||
|
|
439c3c5d35 | ||
|
|
192049c985 | ||
|
|
74d2d6da15 | ||
|
|
3c69ad053b | ||
|
|
14137ff639 | ||
|
|
0939c06827 | ||
|
|
37732a9bd2 | ||
|
|
333e34a484 | ||
|
|
d715fc7784 | ||
|
|
a019741396 | ||
|
|
f597910fa9 | ||
|
|
c1a13ab26a | ||
|
|
15cc360db0 | ||
|
|
914ac06855 | ||
|
|
13d066f75a | ||
|
|
9609c9e1ea | ||
|
|
c59b5652c6 | ||
|
|
63c351b284 | ||
|
|
1cd1bf31be | ||
|
|
0371fa9a09 | ||
|
|
da32114698 | ||
|
|
a2691a4b54 | ||
|
|
b7b518d6c3 | ||
|
|
761f3a3144 | ||
|
|
bc9a27981b | ||
|
|
05231348fb | ||
|
|
a989a70c1b | ||
|
|
03ee29bfcf | ||
|
|
d1154708ef | ||
|
|
6a8478a2d8 | ||
|
|
2c1fae5bee | ||
|
|
8306d6cd2b | ||
|
|
8978559b32 | ||
|
|
1f3b83f604 | ||
|
|
f2e66f1e29 | ||
|
|
1005f99b6a | ||
|
|
2364c521b2 | ||
|
|
23d43a930d | ||
|
|
c708442c4c | ||
|
|
cbf142b7f0 | ||
|
|
36842190b7 | ||
|
|
6b632d4ec9 | ||
|
|
0b3db9c22a | ||
|
|
ea022163d5 | ||
|
|
203d666f8c | ||
|
|
5fe8a03dc0 | ||
|
|
f12b790b4c | ||
|
|
7d550703e2 | ||
|
|
d83af73634 | ||
|
|
b6d604e96b | ||
|
|
dce214677f | ||
|
|
7e1b374d46 | ||
|
|
49ade06654 | ||
|
|
fb986ab2a9 | ||
|
|
b24c0f2ddb | ||
|
|
7d88908c53 | ||
|
|
daea42820a | ||
|
|
e906040957 | ||
|
|
911872a5ee | ||
|
|
16542b141e | ||
|
|
c18d267879 | ||
|
|
849e5bfb30 | ||
|
|
73ee45fa91 | ||
|
|
a6275ca3a8 | ||
|
|
6b4202fb6d | ||
|
|
7a2504991f | ||
|
|
0f1ebbab55 | ||
|
|
5fb0f88a23 | ||
|
|
43faeff461 | ||
|
|
dac7482bb7 | ||
|
|
00502a740d | ||
|
|
923c88e7dc | ||
|
|
9d3c9a3162 | ||
|
|
0603896bc6 | ||
|
|
edf7f504ef | ||
|
|
c226abfac7 | ||
|
|
a800944bce | ||
|
|
e08062d7c5 | ||
|
|
171480e6e8 | ||
|
|
32f05027cc | ||
|
|
6ef7be7330 | ||
|
|
cde575d283 | ||
|
|
471be47e5b | ||
|
|
e826c6dac8 | ||
|
|
78bd0d404b | ||
|
|
873992a9e2 | ||
|
|
39c821e7c0 | ||
|
|
34e1ab2bf5 | ||
|
|
057eebc46d | ||
|
|
55760b938b | ||
|
|
3cf210d00e | ||
|
|
1741bf720c | ||
|
|
d774c2626a | ||
|
|
de018df873 | ||
|
|
3dee8c4a73 | ||
|
|
0242780bac | ||
|
|
ad2be8ae45 | ||
|
|
a06e8f000a | ||
|
|
e2e7c60e56 | ||
|
|
14ad3baadc | ||
|
|
7703865349 | ||
|
|
8ca09fedf7 | ||
|
|
ef7ce90895 | ||
|
|
5c67029398 | ||
|
|
f0f9ff89c4 | ||
|
|
d3fa3a0ada | ||
|
|
fb8cabc649 | ||
|
|
c80230196d | ||
|
|
860e2012f5 | ||
|
|
9f3d724dff | ||
|
|
cb5700b617 | ||
|
|
2e91ddef65 | ||
|
|
88f7ebe0b6 | ||
|
|
e44598b48d | ||
|
|
7c00c83450 | ||
|
|
35130c26bb | ||
|
|
2f488b4ea7 | ||
|
|
660d25e656 | ||
|
|
15c09fd7b5 | ||
|
|
ebb3d83bf6 | ||
|
|
a11a9926cd | ||
|
|
5d8f0f4b54 | ||
|
|
ef2098c967 | ||
|
|
bb7c028852 | ||
|
|
de135fc029 | ||
|
|
ef63393ee4 | ||
|
|
bbca2935ba | ||
|
|
191048fbec | ||
|
|
4a3dc25763 | ||
|
|
bef9a62f4c | ||
|
|
a0a00630ea | ||
|
|
efd6c1f028 | ||
|
|
02bb0afb3a | ||
|
|
387f04dfda | ||
|
|
7a542c0ea2 | ||
|
|
858f773625 | ||
|
|
e10a6f1166 | ||
|
|
bba870ee9c | ||
|
|
2fb6815963 | ||
|
|
57da6fda51 | ||
|
|
361bd753fa | ||
|
|
69d5b39600 | ||
|
|
4e931c4f1f | ||
|
|
d2344ff8bf | ||
|
|
3ab3c010b1 | ||
|
|
deda9f48e8 | ||
|
|
433a95292e | ||
|
|
7a9a90a7a2 | ||
|
|
2f00563249 | ||
|
|
2a216fd169 | ||
|
|
928405649c | ||
|
|
2dfc1b9ad6 | ||
|
|
5bd27e804b | ||
|
|
449c3458ff | ||
|
|
82f50d1273 | ||
|
|
2beb49daeb | ||
|
|
d37be6ff29 | ||
|
|
a5643f9bd7 | ||
|
|
9705a6aaca | ||
|
|
1f404c973b | ||
|
|
b2793f2aa1 | ||
|
|
f942bd7d43 | ||
|
|
425900b036 | ||
|
|
85a72842c3 | ||
|
|
19bb3ae364 | ||
|
|
216302d65f | ||
|
|
0d0df1ecee | ||
|
|
dba33aaf60 | ||
|
|
0597cc1988 | ||
|
|
c7a672d622 | ||
|
|
04e78354a6 | ||
|
|
fcac66f7bf | ||
|
|
ca6b32d668 | ||
|
|
5a02ee3954 | ||
|
|
e1e64ff011 | ||
|
|
6d2e502728 | ||
|
|
c9d65664af | ||
|
|
18f5daaaef | ||
|
|
822bf1aa04 | ||
|
|
162ce46dbc | ||
|
|
dada066753 | ||
|
|
9afb947c73 | ||
|
|
8d53b93c78 | ||
|
|
365a763122 | ||
|
|
f9d218ab1e |
No files matched your search
@@ -5174,13 +5174,3 @@ Before: managed project/thread verbs had no Console conversation area. After: fi
|
||||
2026-10-20 04:44:54 UTC | Row 178 round 2 preparation | Before: Dewey C1 five render guards lacked killing tests. Added independent refusal fixtures, a temporary uid+1 id stub with cleanup, and empty loopback proof case; activation ordering recorded in r2 BUILD. Product unchanged; syntax/text preflight only, no test or mutant execution. Pending row 181 rebase and Sage slot.
|
||||
|
||||
2026-10-20 05:10:34 UTC | Row 178 #1676 round 2 verified | Before: C1 render refusals lacked killing tests and row 181 base was pending. Rebased onto 931651f2d, kept all COUNTED/count code and tests, appended T178 tail; added independent owner/collision/absolute/charset/header and empty-loopback proof tests. Four files singly 156/156, all 24 mutants killed, metadata and sandbox fixture probes pass; scratch sweep empty. Packet agents/rocko/work/queue-178/r2/, candidate 9300a670, inventory d0dcde7b (59/59), scan 58 files no findings; in-review revs 2166-2167, expected no-token request failure. No live actions or commits.
|
||||
|
||||
2026-10-20 19:09:44 UTC | Gander row 196 #1694 design phase | Before: tester TCP reaches every local uid and human proofs do not bind the socket peer. After: design draft at agents/gander/work/queue-196/DESIGN.md inventories listeners, proposes private tester HTTP and SO_PEERCRED binding, aligns the per-request principal contract with Morgana and Gizmoduck, and lists migration, tests, mutants and same-uid limits. Python versus other credential options and Console provenance require Duckworth ruling. Documentation only. No product code, suite, live probe, secret access or deployment.
|
||||
|
||||
2026-10-20 19:16:20 UTC | Gander row 196 #1694 design r2 | Before: Duckworth r1 requested concurrent helper-fd evidence (B1) and selected O1 with interpreter, proof-only and image conditions. After: design adds eight concurrent genuine proof processes plus a racing copier, M20 swapped socket and post-helper socket survival, root-owned system ELF pinning and activation evidence, safe gid validation, cap traffic without helpers, and unchanged P19 R2 text. Mos accepted socket-only tester access (mos-1015 via sage-1363); any authenticated browser relay is a separate row. Documentation coverage checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:21:19 UTC | Gander row 196 #1694 design approval and N5 | Before: Duckworth approved r2 (duckworth-0009, cb6d840df) with a nonblocking interpreter-upgrade note. After: r3 chooses startup safety revalidation, appends actual interpreter identity to bus/peercred-starts.jsonl and retains old activation evidence. Operator re-pin uses the next reviewed copy, respecting already-active refusal. Added task-by-task PLAN.md for inline implementation and independent manifest review. Documentation checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:34:28 UTC | Gander row 196 build paused at design mismatch | Before: approved design promised Unix tester cleanup only for the owned inode. After: scratch test shows Node/libuv close unlinks a replacement inode before JS close handlers. Evidence agents/gander/work/queue-196/evidence/tester-cleanup-blocker.txt. Sent Sage/Duckworth gander-0016/0017, recommending explicit same-uid residual with private 0700 parent retained. Interpreter, concurrent peer, principal, human, peer-transport, transport, tester and process single files passed. Partial code uncommitted, tester transport not wired. No suites, mutant batches or live actions.
|
||||
|
||||
2026-10-21 18:45 UTC | Gander row 196 preservation before rebase | Sage-2051, Mos order mos-1193, explicitly authorizes commit and fast-forward push of the current build before deferred full verification. Product and evidence preserved together; prior focused checks and stopped gates are recorded in the row196 packet, with no claim of full acceptance. Next: rebase onto a36523713 on new gander/row196-r2, report range-diff/patch-id/conflicts, then await Sage's clear for M1-M29 and all requested suites. No live action, force push, branch deletion or tests during hold.
|
||||
@@ -16,12 +16,6 @@ not fleet seats. Keep changes in `/mnt/storage/src/mosaic-stack`; do not modify
|
||||
| Rocko | General development, investigation, testing, and review | T3 session (Codex, gpt-6.1-sol, thread d99326cc, lead decision 122; Claude thread b84bb264 retired 2026-10-12); launcher Claude Code, Sonnet model | `agents/rocko/launch.sh` |
|
||||
| Filbert | General development, investigation, testing, and review | T3 session (Claude, Opus 5.5, thread 048f3c63, non-sandbox reviews only, lead decisions 130 and 134; Codex threads f712d8af and 50342f6a retired 2026-10-13, Claude thread 9cb9731e retired 2026-10-12); launcher Pi, `openai-codex/gpt-6-astra:low`, not to be used under R26 until it changes (lead decision 69) | `agents/filbert/launch.sh` |
|
||||
| Researcher | Source-grounded technical research and evidence | T3 session (Claude, Sonnet 5.5, thread 9bac0794, rows 97 and 98, lead decision 124); Haiku rows (71, 80, 84, 88, later 96) run as subagents Sage dispatches with model haiku, effort high and a worktree (lead decision 129); Haiku thread c98aaf4a retired 2026-10-13 unused; launcher Pi, configured Mosaic model | `agents/researcher/launch.sh` |
|
||||
| Launchpad | Builder, MVP T1 row 192 (Q175 SSO phase 1) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Morgana | Builder, MVP T1 row 193 (human users and RBAC) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Honker | Builder, MVP T1 row 194 (agent lifecycle) | T3 session (Claude, Opus 5.5), started by Sage under mos-0974/0977 | none, T3 only |
|
||||
| Gander | Builder, the human boundary across uids (P19 333, #1694) | T3 session (Codex, gpt-6.1-sol), started by Sage under mos-0977 | none, T3 only |
|
||||
| Webby | Reviewer: Case 2 designs, rows 192 and 194 | T3 session (Claude, Opus 5.5, thread 03086006), started by Mos (mos-0981) | none, T3 only |
|
||||
| Duckworth | Reviewer: federation designs, row 193 | T3 session (Claude, Opus 5.5, thread ffceafec), started by Mos (mos-0977) | none, T3 only |
|
||||
| Sage | Project lead: coordination, review, integration; earlier DYOR strategy records retained | T3 session (Claude Code); Pi launcher `zai/glm-5.3:high` retained | `agents/sage/launch.sh` |
|
||||
|
||||
Each script supports `--check` and `--fresh`. Normal launches resume the agent's
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Duckworth
|
||||
|
||||
Reviewer seat, Claude Opus 5.5, started by Mos (mos-0977, thread ffceafec). Reviews federation designs and MVP T1 row 193.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `duckworth/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,8 +0,0 @@
|
||||
# Gander
|
||||
|
||||
Builder seat, Codex gpt-6.1-sol, started by Sage under mos-0977 item 5 (more Codex Sol rows) for the human
|
||||
boundary row (P19 333, #1694, mos-0989).
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `gander/*`
|
||||
branches through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Row 196 build evidence
|
||||
|
||||
Base 4d26003f7. Build authorized sage-1371, design approved duckworth-0009/0015.
|
||||
No live units, live ports, credentials, real second uid or live state touched.
|
||||
Single test files only so far. Suites and mutant batches require Sage's slot.
|
||||
|
||||
## Cleanup ruling, sage-1382 (2026-10-20T19:39:30Z)
|
||||
|
||||
The first scratch test proved Node/libuv unlinks a final-path replacement before
|
||||
JavaScript close callbacks. Evidence: evidence/tester-cleanup-blocker.txt.
|
||||
Duckworth's option 1 (523a6bddf) was then verified in a standalone scratch test:
|
||||
bind temporary.sock, record dev/ino, rename to tester.sock, connect successfully,
|
||||
replace final inode, close. Temporary name absent and replacement survived.
|
||||
|
||||
Sage chose option 1. The code binds a unique temporary name in the checked 0700
|
||||
parent, chmods/verifies the socket, records its dev/ino and renames to tester.sock.
|
||||
The temporary basename has the same length as tester.sock. libuv remembers the
|
||||
removed temporary name. The close callback lstat/checks the final dev/ino before
|
||||
unlink. The final-path replacement test was RED with direct bind and GREEN after
|
||||
rename. It also asserts no temporary entry after bind or close. M24 will bind the
|
||||
final path directly and must fail this test.
|
||||
|
||||
Residual: a hostile same-uid process can replace a path between lstat and unlink
|
||||
(or between the final-path check and rename), or recreate the temporary name
|
||||
before libuv closes it. The checked private parent excludes other uids, not the
|
||||
instance's own uid. These same-uid races are outside this row's guarantees.
|
||||
|
||||
## Development checks
|
||||
|
||||
Evidence files retain the red/green and diagnostic output. The initial concurrent
|
||||
peer test exposed that Node pauses a Socket passed through spawn stdio. The caller
|
||||
now resumes it after helper exit, with bidirectional post-helper traffic asserted.
|
||||
No product commits until applicable suites pass. Final candidate and counts follow
|
||||
after implementation and authorized verification.
|
||||
|
||||
## Gate stop, 2026-10-20T19:57:01Z
|
||||
|
||||
Gate run 1 stopped at bus 463 pass/1 failure. The row164 reviewed byte pins needed
|
||||
the intentional row196 human-walk/nonce and Console initial-spawn nonce changes.
|
||||
Only those two pins were updated, retaining human-cli and CLI transport pins,
|
||||
literal path recovery and channel plug-in import bans. Focused test passed.
|
||||
|
||||
Run 2: bus 464 pass/0 fail. WebUI stopped at two failures in tester-browser.test.mjs:
|
||||
it still formed TCP URLs from the now-Unix listener address. Script counted 269
|
||||
passing top-level cases before interruption and 2 failures. No final WebUI summary.
|
||||
CLI and mutants did not run. Node24 peer 3/3 and principal 4/4 passed separately.
|
||||
Sage received clear (gander-0023). The failures are fixture migration omissions,
|
||||
not timing failures from Launchpad's overlapping single T56 run. No product commit.
|
||||
|
||||
Focused repairs: the browser fixture fulfills synthetic-origin requests through
|
||||
CDP from the scratch Unix listener, with no TCP listener, proxy or product relay.
|
||||
The two tester browser tests pass. Self-review also found a FIFO receipt could
|
||||
block open before fstat; O_NONBLOCK now makes it refuse promptly (RED timeout,
|
||||
then interpreter 5/5 GREEN). Missing/non-regular helper now refuses construction
|
||||
before opening broker.sock (RED missing refusal, then peer reader 4/4 GREEN).
|
||||
Renewed slot requested in gander-0024; no suite or mutant rerun yet.
|
||||
|
||||
## Manifest preparation while the slot is occupied
|
||||
|
||||
sage-1409 accepts the CDP fixture repair and startup refusals. MANIFEST.md gives
|
||||
each its own item, including the old/new human.mjs and WebUI bus.mjs sha256 values
|
||||
and rationale for the row164 re-pin. PRODUCT-FILES.json records current product,
|
||||
test and operator-document hashes, explicitly as an uncommitted draft.
|
||||
|
||||
Self-review also found helper shutdown returned before child close. The focused
|
||||
test was RED (evidence/peer-shutdown-red.txt), then GREEN after reader.close awaits
|
||||
all killed helper close events and server.close awaits reader.close. M27 removes
|
||||
the wait. M25 removes FIFO nonblocking open and M26 removes helper preflight.
|
||||
All three are prepared but unexecuted, reported to Sage in gander-0025.
|
||||
|
||||
After this change, individual Node 24 files pass: peercred 5/5 and principal 4/4.
|
||||
Node 26 peer-transport is 3/3. Their logs retain the full results. No suite or
|
||||
mutant batch ran during the hold. sage-1416 puts Gander after Launchpad, Darkwing,
|
||||
Rocko and row 192; Sage must send go before the renewed batch starts.
|
||||
|
||||
The consumer audit found one additional conversion defect in the files already
|
||||
changed: proof insertion stopped at --tester-port but not --tester-socket.
|
||||
Pointer argv therefore differed from rendered unit argv when conversion also
|
||||
supplied a proof pair. The extended row196 release case reproduced the exact
|
||||
order mismatch, then passed after adding the socket flag to that insertion
|
||||
boundary. The proof-file no-read check still passes. M28 restores the defect.
|
||||
No batch ran. Shell syntax and diff whitespace checks pass. Final prepared
|
||||
mutant count is 28, pending Sage's go.
|
||||
|
||||
## Static review completion, sage-1425
|
||||
|
||||
The changed verifier path now has a real Client refusal test. A completed proof
|
||||
refusal reaches the client as human-required; helper failure is peercred-required.
|
||||
No broker request dispatches. peer-transport is 4/4. Client code is unchanged:
|
||||
an abrupt close, timeout or transport error without a response is outcome-unknown.
|
||||
The scratch base/candidate comparison returns human-required for both. It does
|
||||
not reproduce Quackerjack's exact failing instance; the manifest names this limit.
|
||||
|
||||
Review also found release did not check tester path length until after service
|
||||
stop. The focused long-path activation fixture reproduced it, then passed with
|
||||
the new preflight. Existing unsafe parent type/uid/mode checks also precede stop;
|
||||
the fixture verifies no start/stop calls for unsafe mode. M29 removes the check.
|
||||
Legacy tester-port-invalid wording is restored to avoid an unrelated contract
|
||||
change. Review covered all changed trust paths and consumers; no further code
|
||||
changes are planned before the batch. M1-M29 remain prepared and unexecuted.
|
||||
@@ -1,332 +0,0 @@
|
||||
# Row 196: the human boundary across uids and humans
|
||||
|
||||
Gander, 2026-10-20. Revision 3, recording approved r2 and N5. Gitea #1694, P19 333.
|
||||
Base: `96f243faf`, branch `gander/row196`. Design approval latest-by 2026-10-22, landing
|
||||
latest-by 2026-10-24 and before federation R3. This document authorizes no live action.
|
||||
|
||||
## 1. Outcome and authority
|
||||
|
||||
Other Linux users must not reach the tester's data or writes. A bus human proof must describe
|
||||
the process that actually connected. Console user calls must also pass the human walk and
|
||||
come directly from this bus tree's Console. Row 193 and federation R3 use that one verifier.
|
||||
|
||||
This is not isolation from a hostile process on the instance's own uid. Keeping the human
|
||||
terminal CLI means that an unmarked process on that uid still has the owner's authority.
|
||||
Process ancestry cannot prove that a process was never an agent. Sections 4 and 8 state this
|
||||
limit without treating it as an enforcement guarantee.
|
||||
|
||||
Inputs: COMMON at `53bb59462`, the row brief at `96f243faf`, Q175 sections 1, 6 and 8,
|
||||
federation r2 at `60b9b112a` section 3.6, Duckworth's B1-B5 at `b402197ff`, row 190's
|
||||
DESIGN.md and the row 190b brief. Q175-Q178 and the MVP plan set the order. Row 190b is on
|
||||
hold pending the host decision. This row does not assume that a separate uid is already live.
|
||||
|
||||
Sage's `sage-1343` accepts the design direction and requires Duckworth to rule on the peer
|
||||
credential implementation. `sage-1345` keeps the control board inventory-only. Morgana's
|
||||
`morgana-0003` supplies the principal contract in section 4. Sage registers this session.
|
||||
Mos accepted removal of direct tester browser access in mos-1015, relayed by sage-1363.
|
||||
Agents test through the private socket. Any later browser view is an authenticated relay
|
||||
behind SSO, designed and reviewed in its own row. This row adds no relay.
|
||||
|
||||
## 2. Inventory from code, not a live probe
|
||||
|
||||
"Same uid" includes untrusted local processes. "Other uid" excludes root, which is outside
|
||||
this boundary. SSO below means Q175 is active on that instance, not merely installed.
|
||||
|
||||
| Entry | Same uid today | Other uid today | Effect of SSO and this row |
|
||||
|---|---|---|---|
|
||||
| Main Console, default 7332, bare serve default 7330 | Loopback reads. With preview/send, matching Host, header and origin can write as owner | Same TCP reach and header forgery | Q175 SSO verifies login and refuses non-public Host except GET /healthz. This row does not change public ingress |
|
||||
| Tester TCP, default installation 7333, optional per instance | Reads and the tester instance, route, name and chat writes | Identical reach, no uid check | Q175 deliberately leaves tester separate. This row replaces TCP with a private filesystem Unix socket |
|
||||
| `<dataRoot>/bus/broker.sock` | Connects, then needs cap or proof. Claimed human PID is not the socket peer | Normally refused by bus/ 0700 and socket 0600 | SSO changes nothing. Add peer uid and PID binding, retain file checks |
|
||||
| Human wrapper to tree's human-cli.mjs | Owner if CLI path, nonce and ancestry pass. Agent ancestors refuse | Cannot connect to protected socket, and proof uid refuses | Preserve terminal owner path, add peer binding. SSO does not authenticate terminal users |
|
||||
| Tester CLI to broker.sock | Tester if its proof passes and bus.tester is exactly true. T3 agents are allowed, managed runs are not | Filesystem modes normally refuse | Same tester policy, plus peer binding. No owner/user upgrade |
|
||||
| `<dataRoot>/bus-host/launch.sock` | Requires run cap and launcher checks | bus-host/ 0700, socket 0600 | No human proof endpoint. Unchanged |
|
||||
| `<dataRoot>/bus-host/git.sock` | Git capability protocol and broker authorization | bus-host/ 0700, socket 0600 | No human proof endpoint. Unchanged |
|
||||
| Discord connector | Outbound Discord gateway/REST, no inbound local listener. Local process could tamper with same-uid files/process | No local port to call. File separation depends on uid and modes | Existing guild, user, channel, bot and webhook checks remain. Discord is not a new bus human transport |
|
||||
| Row 190 additions | Separate configs, roots, unit names, wrappers and Console ports, on one uid | Console/tester TCP still reachable | Adds no new class of listener. Row 196 derives its socket per dataRoot. Row 190 alone supplies no hostile-process isolation |
|
||||
| Control board, default 7331 | Unauthenticated transcript/board reads, POST /api/reply and /api/seen after request checks | Same loopback reach. A local client can supply matching request headers | Independent development service, outside instance SSO. Inventory only per sage-1345. Its fix needs another row |
|
||||
|
||||
Evidence: `packages/webui/src/{cli,serve,bus}.mjs`, `packages/bus/src/{server,runtime,human,
|
||||
tester,store}.mjs`, `packages/cli/src/{launcher,git-socket,transport}.mjs`,
|
||||
`packages/discord/src/{gateway,authorize}.mjs`, `packages/control-board/src/serve.mjs`.
|
||||
Store checks bus/ with `safe(directory,true)`, including owner, symlink and mode checks.
|
||||
The dataRoot check itself does not require 0700 today. Do not describe that as a checked mode.
|
||||
|
||||
## 3. D1: private tester HTTP socket
|
||||
|
||||
Add `serve --tester-socket`, a boolean selecting `<dataRoot>/console/tester.sock`. No arbitrary
|
||||
path or new system config field. Create console/ as 0700. Refuse a symlink, wrong owner,
|
||||
non-directory or any group/other bits on an existing console/. Refuse a pre-existing socket
|
||||
or other entry rather than deleting it blindly. A stale socket needs an operator's diagnosis.
|
||||
Refuse a path longer than Linux's 107-byte pathname capacity before binding.
|
||||
|
||||
Bind HTTP on the Unix path, set socket mode 0600, then verify type, owner and mode before
|
||||
declaring ready. The private parent covers the bind-to-chmod interval. Cleanup only the owned
|
||||
device/inode, as broker.sock does. Never open a TCP compatibility bridge or an abstract socket.
|
||||
Readiness failure closes the main listener too, rather than leaving a partially started Console.
|
||||
|
||||
The tester handler keeps its fixed principal, header/cookie refusals and exact write list.
|
||||
Use a fixed logical origin `http://mosaic-tester.invalid` for URL and same-origin checks. It
|
||||
does not resolve that name. Clients send that Host and Origin plus the existing Fetch Metadata
|
||||
when required. A Host string is not authentication. Unix socket access supplies that boundary.
|
||||
The main listener still refuses x-mosaic-tester. No tester /auth route exists.
|
||||
|
||||
`bus.tester` remains Jason's switch. Socket reach never enables tester authority by itself.
|
||||
Turning it off must refuse every tester bus call. This row adds no user or owner capability
|
||||
to the tester. Tester reads retain today's scope, including any local Console reads that are
|
||||
not bus calls. This row does not turn bus.tester into a separate HTTP read switch.
|
||||
|
||||
### Consumers and migration paths
|
||||
|
||||
| Consumer of 7333 | New path |
|
||||
|---|---|
|
||||
| Agent tester HTTP tools and curl probes in authorized window packets | `curl --unix-socket <dataRoot>/console/tester.sock http://mosaic-tester.invalid/...`, with the fixed Origin for writes. Add one repository helper for bounded tester HTTP requests so callers do not copy path/origin logic |
|
||||
| `scripts/mosaic ... --tester` | Already uses TESTER_CLI and broker.sock, not TCP. Keep its verbs, output and switch |
|
||||
| Browser tester page in row 176 and later window checks | Ordinary browsers cannot navigate a Unix HTTP socket. Replace automated tester HTTP checks with the socket helper. Browser visual QA uses scratch fixtures only. No local TCP relay is added |
|
||||
| `webui-service.sh` render/install and installed unit | Carry --tester-socket for new copies. Refuse a target without support. Units running old copies retain their old flags until a window |
|
||||
| `stack-release.sh` activate, plan, rehearsal and rollback | Add --tester-socket. Replace inherited --tester-port N only when explicitly requested. Refuse incompatible inherited flags before stopping units. Plan names the transport and every dropped pair. Rehearsal uses a scratch socket |
|
||||
| Gate/window Console checks, including agents' console-check scripts | Extend checks to use scratch Unix HTTP and expect no tester TCP bind. Sage owns live packet updates, including Q175 phase 2's old --tester-port 7333 instructions |
|
||||
| `docs/TOOLS.md`, webui/cli/bus READMEs and examples | Document socket invocation, fixed origin, loss of direct browser navigation and per-instance path. Historical window receipts stay unchanged |
|
||||
|
||||
Repository search found no non-test hardcoded 7333 caller in tools/ or the harness code. The
|
||||
active windows and external seat commands still need Sage's consumer audit before re-pin.
|
||||
The build manifest will record the full search, rather than treating this draft as that audit.
|
||||
|
||||
## 4. D2: one verified principal per bus request
|
||||
|
||||
Internal interface: `verifyPrincipal(request, connection) -> {kind,business,id}` or refusal.
|
||||
Kinds are owner, user and tester. The connection metadata is supplied by the trusted server,
|
||||
never deserialized from JSON. No role or authority is returned. The broker looks up live policy.
|
||||
|
||||
1. Read and validate the bounded envelope first. For human, tester and user proofs, read Linux
|
||||
SO_PEERCRED on that accepted Unix connection before dispatch. Require a valid peer PID
|
||||
and the bus uid. Refuse unavailable credentials, unsupported platform and errors. Cap
|
||||
traffic keeps its capability protocol behind the validated 0700 directory and 0600 socket.
|
||||
2. Require exactly one cap, human, tester or future user envelope. Human/tester/user proof.pid
|
||||
must equal the kernel peer PID. Verify startTime, nonce and CLI path against that process.
|
||||
Keep the second process read after the walk. Missing, changed or exited processes refuse.
|
||||
3. Owner terminal: run the full existing human walk. Map the business to businesses[b].human.
|
||||
No supplied human id, role or uid selects the principal. Keep today's terminal invocation.
|
||||
4. Tester: run verifyTester, retaining its deliberate difference from the human walk. An agent
|
||||
seat may be tester. A managed-run marker or registered managed ancestor refuses. Then check
|
||||
bus.tester. The full human walk must never be weakened to admit a session user.
|
||||
5. Session user: use the full human walk with the path-bound user-cli entry and its own nonce
|
||||
variable, MOSAIC_BUS_USER_NONCE. Parameterize the nonce reader without admitting a human
|
||||
or tester nonce as a user proof. Require its immediate parent to be the same tree's resolved webui/src/cli.mjs,
|
||||
argv[2] `serve`. Check parent uid, startTime and ppid again before binding. Both child and
|
||||
parent remain alive for the call. A different checkout or wrapper parent refuses.
|
||||
6. For user calls, the Console puts its mapped access-file user id in the verified proof's
|
||||
user field, as federation 3.6 specifies. The id comes only from claims Q175 verified for
|
||||
this request's live session, never from an identity cached past that session. It never
|
||||
takes that id from a form field or verb
|
||||
args. Return kind user, proof.business and proof.user as id only after the provenance
|
||||
checks. R3 then validates that mapping and the live grant. Bad access.json denies users,
|
||||
not the terminal owner. The verifier does not implement access-file policy.
|
||||
7. Owner Console calls keep the verified Q175 owner subject path. Under SSO only a subject
|
||||
in console.sso.subjects reaches owner bus calls. They use human-cli and the same peer/walk
|
||||
checks. A terminal owner still needs no SSO token. No anonymous main-listener fallback.
|
||||
|
||||
The bus verifies which process supplied the session identity. It relies on that Console's
|
||||
Q175 implementation to have verified OIDC. It does not independently verify an ID token in
|
||||
this option. A provider/sub field alone, from another process, is not enough.
|
||||
|
||||
The Console must spawn user-cli directly with its nonce already in the launch environment.
|
||||
It must not use human-cli's existing self-respawn pattern, which makes the CLI's parent a CLI.
|
||||
Apply the same initial-nonce spawn to Console human calls for consistent tests. Terminal
|
||||
human-cli can retain its self-respawn since no Console-parent condition applies there.
|
||||
Never strip agent markers from inherited environments to make a walk pass.
|
||||
|
||||
The server binds an internal cap for this request, dispatches, and disconnects in every
|
||||
success/error/timeout path, including asynchronous task and web calls. It never returns the
|
||||
cap or a reusable principal handle. A proof copied by another process has a different kernel
|
||||
peer PID and fails. One verified CLI may make only its one request on its one connection.
|
||||
The session may hold s.user for that request. No PID or principal cache survives the connection.
|
||||
|
||||
### Fit with rows 193 and R3
|
||||
|
||||
Row 196 owns peer metadata, proof/walk verification and the internal principal interface.
|
||||
It supplies the Console provenance verifier and tests before R3 adds user-cli and user sessions.
|
||||
R3 owns access.json mapping and the bindUser integration, using this interface. It must not
|
||||
add a second ancestry implementation. Row 193 adds viewer and share policy to the same live
|
||||
grant check. An owner id is the business human. A user id is the access-file id, with stored
|
||||
owner string `user:<id>`. Neither row takes role from the proof.
|
||||
|
||||
No live grant, unknown subject, duplicate owner/user mapping and invalid access file all
|
||||
deny user binding through R3's policy. Per-request authorization, access.reload and the
|
||||
30-second recheck remain R3's responsibility. Share visibility uses that check in row 193.
|
||||
Binding provenance is not a grant and never widens one. R3's user dispatch must run before
|
||||
names falls through to #agent. R3 must not land before this contract and its tests are green.
|
||||
Gizmoduck's gizmoduck-0015 and Gander's gander-0007 agree on this split. R3 keeps the reserved
|
||||
user-name mutant. Row 196 tests kind separation even if a user id resembles the owner name.
|
||||
|
||||
### OIDC alternative
|
||||
|
||||
The bus could verify the signed ID token itself and map its issuer/sub. That reduces reliance
|
||||
on the Console's subject assertion, but requires JWKS/provider handling, nonce or challenge
|
||||
binding, replay handling, login and logout/session lifetimes in the bus. Reusing an ID token
|
||||
on every request either creates a replay credential or expires a Console session with the
|
||||
token. A bus-issued session needs a separate lifecycle design. It also leaves the terminal
|
||||
owner and hostile same-uid limit intact. Recommend the Console provenance contract for this
|
||||
slice. Duckworth approved this option in duckworth-0006, with the live-session condition above
|
||||
and the requirement to carry section 8 R2 into P19 333 word for word.
|
||||
|
||||
## 5. D3: obtaining kernel credentials, O1 approved with conditions
|
||||
|
||||
All options must bind the accepted connection, not a PID a caller picked. None gets a silent
|
||||
fallback to the current proof. Sage required this comparison. Duckworth selected O1 in
|
||||
duckworth-0006, with the interpreter, proof-only and image conditions below. O4 remains the
|
||||
fallback if Python is unavailable, and needs a revised build manifest before use.
|
||||
|
||||
| Option | What it proves and costs | Recommendation |
|
||||
|---|---|---|
|
||||
| O1 Python stdlib helper on inherited accepted socket | Node passes the accepted net.Socket as fd 3 using documented spawn stdio stream passing. Python socket(fileno=3) reads SO_PEERCRED and returns only pid,uid,gid on stdout. No socket reads, no shell, no credential values. Adds Python to bus hosts and any future bus image, with a process per admitted proof connection. Copies carry the helper and depend on the system interpreter | Selected by Duckworth, with conditions below |
|
||||
| O2 /proc/net/unix plus inode and /proc/PID/fd scans | The table identifies Unix socket entries, not an authenticated peer PID for each accepted connection. Finding a process holding an inode is racy, may have several holders and can be unreadable. The server endpoint's inode is not the client's inode | Reject as a replacement for SO_PEERCRED. No claimed proof PID may choose the scan target |
|
||||
| O3 plain Node fd passing or abstract sockets | A private inherited IPC channel can authenticate a child that this parent spawned. The bus did not spawn the independently started Console or terminal CLI. Moving the bus into the Console's child tree changes lifecycle and ownership. An abstract name supplies no filesystem mode boundary and no peer uid check by itself. Node's public net API supplies no SO_PEERCRED accessor | Reject as a drop-in. A larger bus-spawned transport design could use fd capabilities, but is outside this slice |
|
||||
| O4 small compiled helper or Node-API addon | Calls getsockopt directly, with no Python runtime. A helper can use the same inherited fd. Adds compiler/build closure and platform artifacts to stack-copy and containers. An addon also needs a supported way to obtain the accepted socket fd and Node version coverage | Viable alternative if Python is refused. Prefer a small helper over undocumented Node handles |
|
||||
|
||||
For O1, pin the system interpreter `/usr/bin/python3`, resolving its system symlink to an
|
||||
absolute ELF binary. Require root ownership and no group/other write on that executable and
|
||||
its path components. Refuse scripts, PATH shims and any resolved path under an OS account's
|
||||
home, including /home and /root. A pyenv shim can choose an interpreter before -I -S applies.
|
||||
No PATH lookup, caller-selected interpreter or helper. Invoke the resolved binary with
|
||||
`-I -S`, a minimal environment and the absolute helper path in the same pinned copy.
|
||||
|
||||
Activation preflight records the interpreter's resolved path, version and file identity/hash
|
||||
in its gate evidence. The bus validates and pins the resolved executable at each startup
|
||||
before opening broker.sock. The activation gate must report the same interpreter used by
|
||||
that bus. Missing, unsafe or changed resolution during that activation refuses it, never
|
||||
falling back to PATH. No secret values are recorded. Test system-owned interpreter checks
|
||||
through injected metadata.
|
||||
|
||||
N5 decision: a later bus restart repeats the safety/version/support checks and accepts a
|
||||
safe system interpreter installed by an OS upgrade. It does not compare against an old
|
||||
activation hash. Before opening the listener, append a startup receipt with timestamp,
|
||||
copy path, resolved interpreter path, version and file identity/hash to
|
||||
`<dataRoot>/bus/peercred-starts.jsonl` (0600 in the checked 0700 bus directory). Refuse an
|
||||
unsafe receipt file or failed append. Never edit the old activation evidence. The startup
|
||||
receipt identifies the binary now in use, so the old receipt remains a historical fact.
|
||||
If an upgraded interpreter fails the checks, startup fails closed until a safe supported
|
||||
system interpreter is restored by the operator. There is no shim or O4 auto-fallback.
|
||||
|
||||
Operator re-pin after an interpreter change: in the next authorized window, prepare the
|
||||
next reviewed copy with `scripts/stack-copy.sh copy NEW`, then run
|
||||
`scripts/stack-release.sh activate NEW BUSINESS --tester-socket` with that instance's existing
|
||||
flags and selector, first with --plan. NEW must differ from the active copy because activate
|
||||
refuses already-active. Record the interpreter in the new activation evidence and compare it
|
||||
with the bus startup receipt. A routine OS update alone does not authorize a re-pin or unit
|
||||
restart. A bus restart does not rewrite config, pointer or activation history.
|
||||
|
||||
Preflight helper and SO_PEERCRED support too. The helper lives under packages/bus/src so
|
||||
stack-copy's closure includes it. Copies remain immutable. Document Python >=3.10 on bus
|
||||
hosts. Today's Containerfile builds the worker image and runs run-agent.sh, not the bus.
|
||||
It needs no Python change for this row. A future bus-host image must explicitly include
|
||||
the pinned system Python or use reviewed O4. No install through the node_modules symlink.
|
||||
|
||||
Pause each proof connection while reading credentials. Limit outstanding helpers
|
||||
to 16, timeout to one second, output to one strict JSON record of at most 256 bytes. Refuse
|
||||
nonzero exit, extra output, malformed integers, missing fd and timeout. Validate pid, uid and
|
||||
gid as safe integers, pid >=2 and uid/gid >=0. Only pid and uid select the principal. gid is
|
||||
validated but grants no authority. Kill and reap helpers on shutdown. Cap envelopes never
|
||||
spawn this helper. Reading the proof envelope first is sound because SO_PEERCRED describes
|
||||
the peer at connect time. Keep the accepted Socket object owned by its connection closure,
|
||||
not a shared current-fd variable. Only the helper's duplicate fd is closed when it exits.
|
||||
|
||||
References: [Node stdio stream passing](https://nodejs.org/api/child_process.html#optionsstdio),
|
||||
[Node Unix socket support](https://nodejs.org/api/net.html#ipc-support),
|
||||
[Python socket and getsockopt](https://docs.python.org/3/library/socket.html),
|
||||
[Linux Unix credentials and permissions](https://man7.org/linux/man-pages/man7/unix.7.html),
|
||||
[Linux proc network table fields](https://man7.org/linux/man-pages/man5/proc_pid_net.5.html).
|
||||
These support the APIs. The connection-binding behavior still needs T2 on supported Node versions.
|
||||
|
||||
## 6. Migration and rollback
|
||||
|
||||
No config schema or store schema changes. Old copies and units keep working until each
|
||||
instance re-pins. No new verifier is loaded into a running old broker. Do not pair a new
|
||||
Console with an old bus and call it protected. Same-tree paths intentionally refuse mixtures.
|
||||
|
||||
The authorized window must pin bus, Console and human wrapper to one candidate. New tools
|
||||
offer `activate ID BUSINESS --tester-socket`. Plan must show the old tester-port pair removed
|
||||
and the new socket flag added, and tell Jason that direct tester browser navigation is lost.
|
||||
Mos accepted that change in mos-1015 (sage-1363). Agents test through the socket. A later
|
||||
authenticated browser relay behind SSO belongs to its own row. Without the explicit
|
||||
conversion, a candidate that no longer
|
||||
serves tester TCP refuses inherited --tester-port before stopping anything. New serve refuses
|
||||
--tester-port with a migration error. It never silently changes that flag's meaning.
|
||||
|
||||
Rollback to a copy lacking --tester-socket drops tester enablement and reports it, as today's
|
||||
tools drop unsupported flags. It does not turn the socket back into TCP. The old main Console
|
||||
and owner CLI can run with their known old limits. A failed activation's recovery may restore
|
||||
the old saved unit, including its TCP listener. Report that recovery as restoration of the old
|
||||
security state, never as a hardened deployment. After rollback, R3 and second-human access are
|
||||
not accepted until prerequisites are restored. Older operator tools cannot manage the new
|
||||
flag safely. The window must use the reviewed candidate tool blob for rollback as well.
|
||||
|
||||
The Q173 window supplies real uid-denial evidence and the final consumer audit. No second uid,
|
||||
live socket, unit, config or secret is touched in this row's tests. Row 190b or a separate host
|
||||
still owns cross-instance file isolation. It is not replaced by SSO or this socket change.
|
||||
|
||||
## 7. Acceptance tests and mutants
|
||||
|
||||
All fixtures use scratch roots and ephemeral sockets, no live 7332/7333, public origin or
|
||||
provider. No real second uid. Foreign uid checks use injected metadata and name that limit.
|
||||
Use short TMPDIR paths so the longest Unix socket path stays under 108 bytes.
|
||||
|
||||
| Test | Required evidence | Mutant killed |
|
||||
|---|---|---|
|
||||
| T1 private tester | HTTP over scratch socket reads and writes as tester only. parent 0700, socket 0600. No tester TCP listen. Wrong Host/origin and auth cookies refuse | M1 bind loopback TCP as well, M2 remove socket mode check, M3 accept owner cookie |
|
||||
| T2 real peer binding | At least 8 concurrent proof connections from distinct scratch client processes, each with its own live proof, plus a copier racing one live CLI. Barrier the helpers so connections overlap. Each genuine call must bind its own peer PID and pass, the copier must refuse. Repeat on Node 24 and 26. A lower-level helper fixture exchanges bytes both ways on the accepted socket after the helper exits, and each real bus call receives its response after helper exit. Helper failure cases all refuse | M4 use proof.pid as peer PID, M5 continue after helper error, M20 give the helper another pending connection's socket |
|
||||
| T3 uid/path refusal | Inject foreign peer uid, wrong file owner/modes, symlink parent, existing socket and long path. Inject interpreter script, user-owned ELF, home path, writable ancestor and missing executable. Refuse before dispatch, no repaired paths. Unsafe pid/uid/gid and extra helper output refuse | M6 ignore uid, M7 accept unsafe parent |
|
||||
| T4 ancestry | Agent env, binary and registered launch above owner/user refuse. Missing/reused PID, changed parent and cycles refuse. Clean terminal owner works | M8 omit walk, M9 omit identity re-read |
|
||||
| T5 Console parent | Direct CLI child from exact tree Console fixture works. Same CLI under shell, other copy, wrapper, agent ancestor or CLI respawn refuses | M10 remove direct-parent check, M11 accept any Console basename |
|
||||
| T6 principal shape | No human id or role from request body. Exactly one proof. No returned cap. Disconnect on synchronous and async failure, timeout and close. Tester agent allowed, managed tester refused | M12 accept extra identity, M13 omit disconnect, M14 use tester walk for user |
|
||||
| T7 consumer migration | Render/plan/activate/rehearse fixture carries socket flag in unit and pointer. Explicit conversion removes port. Unsupported/inherited mismatch refuses before unit calls. Rollback disables unsupported tester, never opens TCP | M15 keep inherited tester port, M16 restore TCP on downgrade |
|
||||
| T8 gates and identity | bus.tester false refuses tester calls. SSO owner/user wiring uses only claims verified for that request's live session. Expired/logged-out or replaced session cannot reuse an earlier mapped id. User kind never becomes owner from its id. R3 integration maps subject, denies missing/invalid grants and owner overlap, broken access file leaves terminal owner working | M17 bypass tester switch, M18 turn user kind into owner by id |
|
||||
| T9 isolation limits | Two scratch instances have different sockets and matching tree paths. Foreign-uid stand-in refuses. Record that no real uid boundary was exercised | M19 choose default instance socket |
|
||||
| T10 interpreter and cap path | Activation evidence records the same pinned root-owned system ELF that the bus uses. Scripts, home paths and unsafe owners/modes refuse. Simulated OS replacement followed by startup accepts a safe replacement and appends its actual identity without changing the old receipt, refuses an unsafe replacement or unsafe/unwritable receipt. Cap calls complete without invoking the helper, proven with a helper spy that fails if called | M21 bypass interpreter safety, M22 invoke helper for cap traffic, M23 retain stale interpreter identity after restart |
|
||||
|
||||
Tests for new policy stay in the bus, WebUI and CLI packages. R3 provides the real user/grant
|
||||
half of T8 in its candidate, with its own gate. This row tests the shared verifier's user
|
||||
provenance without shipping placeholder user access. Mutants run on scratch copies,
|
||||
restore the original bytes and verify hashes. Two failures in a gate run stop and are reported.
|
||||
Single test files run freely. Ask Sage for the slot before any suite. Final acceptance is
|
||||
Duckworth's approved manifest plus Sage's bus, WebUI, CLI and scripts/test-*.sh gate.
|
||||
|
||||
## 8. Closure, limits and review decisions
|
||||
|
||||
R1. Same uid remains trusted OS authority. A hostile process may detach, clear its markers,
|
||||
modify argv, read same-uid pipes or capabilities, edit policy files, or interfere with the
|
||||
Console where host policy permits. SO_PEERCRED proves the connecting process identity at
|
||||
connection time. It does not prove human intent, protect memory, or track a socket passed to
|
||||
another process. Ordinary agent children fail the walk. Hostile detached agents are not
|
||||
claimed contained. Separate uids narrow cross-instance reach, not reach within one instance.
|
||||
|
||||
R2. The Console provenance option trusts verified code and process metadata, not an independent
|
||||
bus-side OIDC exchange. That trade must be approved, not hidden behind the phrase zero trust.
|
||||
|
||||
R3. A Unix tester endpoint removes direct browser navigation. No bridge is in this design.
|
||||
The 7331 development board remains a separately reported reachability and impersonation risk.
|
||||
|
||||
P19 333 can record the following after the implementation gate and authorized re-pin:
|
||||
"Tester ingress is private to the instance uid. Bus human proofs are tied to the actual
|
||||
Unix peer and retain the human walk. Session-user calls require the same-tree Console as
|
||||
direct parent. Q175 phase 2 separately protects main Console ingress with verified OIDC.
|
||||
Hostile processes on the instance uid, root, and the independent development control board
|
||||
remain outside these guarantees. Cross-instance file isolation requires the uid/host gate."
|
||||
|
||||
Duckworth requires that P19 333 also carry R2 word for word:
|
||||
"The Console provenance option trusts verified code and process metadata, not an independent
|
||||
bus-side OIDC exchange. That trade must be approved, not hidden behind the phrase zero trust."
|
||||
|
||||
Do not mark the historical task fully closed on a design or commit. Closure needs live
|
||||
Q175 phase 2, this row's re-pin and the recorded residual accepted through Mos. A promise of
|
||||
hostile same-uid isolation would require separate OS authority for agents and a new design.
|
||||
|
||||
Review receipt: duckworth-0006, notes at `86be181a2:agents/duckworth/work/row196/REVIEW.md`.
|
||||
Q1 selects O1 with the conditions incorporated in section 5. Q2 approves Console provenance
|
||||
with the live-session and unchanged P19 R2 conditions. B1 adds concurrent T2, M20 and socket
|
||||
survival checks. N2's detached-Console limit remains in R1. N3's gid validation and N4's
|
||||
operator window warning are included. Mos's tester-access ruling is recorded above. No
|
||||
product code begins before design approval.
|
||||
Duckworth approved revision 2 in duckworth-0009, review at
|
||||
`cb6d840df:agents/duckworth/work/row196/REVIEW-r2.md`. N5 permits either restart policy.
|
||||
Revision 3 selects safety revalidation with a new append-only startup receipt, and gives the
|
||||
operator a re-pin path that respects the existing already-active refusal.
|
||||
@@ -1,121 +0,0 @@
|
||||
# Row 196 review manifest
|
||||
|
||||
Status: draft, not an acceptance claim. Candidate product files are uncommitted.
|
||||
Base: 4d26003f731a6eec439df6a48cacc3ef482a59c8. Design r3 approved by
|
||||
duckworth-0009/0015 and sage-1371. No merge, deployment or live re-pin requested.
|
||||
Current path hashes are in PRODUCT-FILES.json, marked as an uncommitted draft.
|
||||
Final candidate commit, exact changed-file hashes and gate totals follow after
|
||||
the renewed suite slot. Sage owns that slot and the final gate. Duckworth reviews
|
||||
this manifest before Sage's gate.
|
||||
|
||||
## Review items
|
||||
|
||||
| Item | Change and acceptance evidence | Mutants |
|
||||
|---|---|---|
|
||||
| Kernel peer proof | `bus/src/peercred.py` obtains SO_PEERCRED from inherited fd 3. `peercred.mjs` owns the accepted Socket in each connection closure, bounds helpers and validates all three safe integers. `peercred.test.mjs` overlaps eight distinct callers plus a copier and checks traffic after helper exit. | M4, M5, M20 |
|
||||
| Interpreter trust | `bus/src/interpreter.mjs` resolves fixed system Python, checks root ownership, safe ancestors, ELF and version, and refuses home paths or shims. Startup appends actual identity; activation compares the fresh receipt. `interpreter.test.mjs` covers safe replacement and unchanged older receipt. | M21, M23 |
|
||||
| Startup FIFO refusal | Receipt opens include O_NONBLOCK before fstat, avoiding an indefinite FIFO open. `interpreter.test.mjs`, `non-regular startup receipt refuses promptly without waiting on a FIFO peer`, was RED by timeout, then GREEN. Evidence: `evidence/interpreter-fifo-red.txt`, `evidence/interpreter-green.txt`. No live receipt was used. | M25 removes O_NONBLOCK |
|
||||
| Missing helper refusal | Reader construction refuses missing, symlink or non-regular peer helper before opening the broker socket. `peercred.test.mjs`, `missing or non-regular peer helper refuses at construction before opening the bus`, was RED, then GREEN. Evidence: `evidence/peer-helper-red.txt`, `evidence/peercred-green.txt`. | M26 removes helper preflight |
|
||||
| Helper shutdown | Reader close kills and awaits helper close events; server close awaits it. `peercred.test.mjs`, `shutdown waits for killed helper close before returning`, was RED, then GREEN. Evidence: `evidence/peer-shutdown-red.txt`, `evidence/peercred-green.txt`. | M27 returns without waiting |
|
||||
| Shared principal | `bus/src/principal.mjs` checks kernel uid/PID, retains the human walk, separates nonce kinds and requires the exact same-tree Console serve direct parent for users. `principal.test.mjs` supplies process-table refusals. User id is provenance only; R3 owns mapping, grants and user dispatch. | M6, M8-M12, M14, M18 |
|
||||
| Request lifetime | `bus/src/server.mjs` runs the helper only for proof envelopes and releases transient caps for sync/async success, refusal, timeout and close. `peer-transport.test.mjs` tests cleanup and fails if a cap request invokes the helper. | M13, M22 |
|
||||
| Private tester ingress | `webui/src/tester-socket.mjs` owns 0700 console directory and 0600 socket; `serve.mjs` preserves tester policy and refuses sign-in headers/cookies. `tester-socket.test.mjs` covers unsafe paths, origin/Host, instance separation and HTTP access. | M1-M3, M7, M19 |
|
||||
| Owned inode cleanup | sage-1382 selects temporary bind, rename to final, conditional final dev/ino cleanup. `tester-socket.test.mjs` asserts temporary name absent after bind/close and replacement final inode survives close. Scratch proof and remaining same-uid race are in BUILD.md. | M24 binds final directly |
|
||||
| Tester policy | `bus.tester` remains Jason's switch. Existing `tester.test.mjs` verifies refusals. No user widening, new browser relay or control-board change. | M17 |
|
||||
| Consumer migration | WebUI CLI, service rendering, release plan/activation/rehearsal and CLI Unix HTTP helper carry explicit --tester-socket. Inherited TCP on a new target refuses before stop without explicit conversion. Unsupported rollback disables tester and reports it. Focused service/release/tester HTTP files cover these paths. | M15, M16 |
|
||||
| Proof pair ordering | Conversion plus an explicit proof pair keeps pointer and unit argv in the same order: preview, proof pair, tester socket. The focused row196 release test was RED with the old insertion scan, then GREEN after including --tester-socket in its stop list. The proof-file no-read check also passes. Evidence: `evidence/release-proof-order-red.txt` and `evidence/release-proof-order-green.txt`. | M28 omits socket from the insertion boundary |
|
||||
| Path preflight before stop | Activation checks the final Unix pathname byte length and any existing parent directory's type, owner and mode before stopping units. The row196 release case reproduces a too-long path reaching service stops, then verifies length and unsafe-parent refusals leave start/stop calls unchanged. Evidence: `evidence/release-path-red.txt` and `evidence/release-path-green.txt`. | M29 removes the preflight |
|
||||
| Browser fixture migration | Test-only CDP interception fulfills synthetic-origin requests from the scratch Unix listener. No TCP listener or product relay. `tester-browser.test.mjs` is 2/2 after the fixture repair, accepted in sage-1409. Evidence: `evidence/tester-browser-focused.txt`. | Product transport removal is covered by M1; browser fixture itself is tested by its two cases. |
|
||||
|
||||
All package paths above are under `packages/`. Mutants are prepared in
|
||||
`mutants.mjs` but none have run as of this draft. Their kill and restoration
|
||||
receipts must be attached before acceptance. M25-M29 supplement design M1-M24.
|
||||
|
||||
## Separate row164 proof baseline update
|
||||
|
||||
Duckworth should review this hunk separately, as requested in sage-1409.
|
||||
Only two baselines in `packages/bus/tests/channel-proof.test.mjs` change.
|
||||
|
||||
| File | Previous sha256 | Candidate sha256 | Why |
|
||||
|---|---|---|---|
|
||||
| `packages/bus/src/human.mjs` | `b995fc5cff3af8ade4c990d0e3bebf9d83829e00f034ebc9c3dcfd05f252b573` | `0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079` | Read the distinct user nonce and parameterize the existing path-bound human walk for the shared verifier. |
|
||||
| `packages/webui/src/bus.mjs` | `76a1b57858c4aa41071746713a196299264851c18322a6f9869d553f0362d421` | `934ab2f9b6bde72aafe7814758550c283eb25a4f3a23b284021b13718d67178f` | Supply the initial owner CLI nonce at spawn and map peercred-required to HTTP refusal. |
|
||||
|
||||
The human-cli and CLI transport hashes stay fixed. Literal shim path recovery,
|
||||
forged-proof refusal and the channel plug-in proof-caller import ban remain tested.
|
||||
Focused evidence: `evidence/channel-proof.txt`. These hashes must be checked again
|
||||
against the final candidate before review.
|
||||
|
||||
## Consumer inventory and window
|
||||
|
||||
Tester tools use `packages/cli/src/tester-http.mjs` and the selected instance's
|
||||
console/tester.sock. WebUI tester test fixtures and browser fixtures use scratch
|
||||
Unix sockets. The stack-release gate and rehearsal carry --tester-socket and
|
||||
compare the bus startup interpreter receipt. Package READMEs and docs/TOOLS.md
|
||||
show the Unix path and explicit migration. Direct browser access is lost by
|
||||
Mos's accepted ruling mos-1015; a future SSO relay needs its own row. Name that
|
||||
loss to Jason in the authorized window. No candidate is live from this work.
|
||||
|
||||
The 7331 development control board is inventory only. Its reads, /api/reply
|
||||
writes and reachability remain the separate boundary described in DESIGN.md.
|
||||
Row 190b remains on hold; this work does not isolate hostile same-uid processes.
|
||||
|
||||
## Gate history and remaining evidence
|
||||
|
||||
Run 1: bus 463 pass, one byte-baseline failure. Run 2: bus 464 pass; WebUI
|
||||
stopped at its second fixture migration failure, with 269 observed top-level
|
||||
passes and no final summary. CLI and mutants did not start. Sage received clear
|
||||
in gander-0023. No timing failures were attributed to Launchpad's T56 overlap.
|
||||
The two browser failures were repaired and verified in a single file.
|
||||
|
||||
sage-1416 delays the renewed slot until Launchpad, Darkwing, Rocko and row 192
|
||||
finish. No suite or mutant batch starts before Sage's go. Remaining: final
|
||||
bus/WebUI/CLI suites, M1-M29 receipts, final changed-file hashes,
|
||||
verified candidate commit/push, Duckworth
|
||||
manifest review and Sage's gate. No approval is implied by this draft.
|
||||
|
||||
Node 24 focused reruns after the shutdown change are complete: peercred 5/5 and
|
||||
principal 4/4. Node 26 peer-transport now passes 4/4, including client-visible
|
||||
refusal codes. Full logs are in evidence/.
|
||||
|
||||
## Refused caller behavior and static review
|
||||
|
||||
The verifier path is changed by this row. A completed proof refusal returns an
|
||||
explicit protocol error before closing the connection: `human-required` for a
|
||||
rejected human proof and `peercred-required` for helper failure. The real Client
|
||||
receives those codes in `peer-transport.test.mjs`, without broker dispatch. No
|
||||
client code is changed. Abrupt close, transport error or timeout without a valid
|
||||
response still reports `outcome-unknown`; this row does not convert that case.
|
||||
|
||||
Quackerjack's reported failed-proof close was not reproduced against this branch's
|
||||
base. A scratch comparison loads HEAD's server and the candidate with the same
|
||||
refusing authenticator and unchanged Client. Both return `human-required`.
|
||||
Evidence: `evidence/refusal-baseline-comparison.txt`. This comparison does not
|
||||
claim to reproduce Quackerjack's exact process ancestry or instance. The manifest
|
||||
therefore claims the explicit codes only for the paths verified here.
|
||||
|
||||
Static review covered peer reader and shutdown, runtime startup and failure
|
||||
cleanup, principal/walk dispatch, tester bind/close, Console spawn, Unix client,
|
||||
release conversion/recovery/preflight, unit rendering and test fixture changes.
|
||||
It found the shutdown, proof ordering and preflight defects recorded above.
|
||||
Each has a focused test and prepared mutant. An unrelated change to legacy
|
||||
tester-port-invalid text was also removed to preserve the existing contract.
|
||||
No further implementation changes are planned before the authorized batch.
|
||||
|
||||
## Limits carried to acceptance
|
||||
|
||||
No real second uid or live instance is exercised. Foreign uid metadata is a
|
||||
stand-in. The authorized window still owns real uid-denial and consumer audit.
|
||||
Python is a new bus-host dependency; the worker Containerfile is unchanged.
|
||||
Restarts validate the current interpreter and append a receipt; old receipts
|
||||
are preserved. Interpreter changes use the next reviewed copy and preserve
|
||||
the already-active refusal.
|
||||
|
||||
Hostile same-uid processes can read or alter same-uid state, detach or interfere
|
||||
with pathname checks and Console provenance. Separate uids narrow cross-instance
|
||||
reach, not reach within one instance. R3 must source the proof id only from
|
||||
claims verified for that request's live session and reread grants per request.
|
||||
P19 333 must retain this text unchanged:
|
||||
|
||||
The Console provenance option trusts verified code and process metadata, not an independent
|
||||
bus-side OIDC exchange. That trade must be approved, not hidden behind the phrase zero trust.
|
||||
@@ -1,121 +0,0 @@
|
||||
# Row 196 Human Boundary Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox syntax for tracking.
|
||||
|
||||
**Goal:** Bind human proofs to their actual Unix peers and make tester HTTP private to the instance uid.
|
||||
|
||||
**Architecture:** The bus reads kernel credentials through a pinned system Python helper only for proof envelopes. One principal verifier keeps the human walk and supplies Console provenance for federation R3. Tester HTTP moves to a private filesystem socket. Release tools migrate the flags explicitly.
|
||||
|
||||
**Tech Stack:** Linux, Node 24 and 26, Python >=3.10 standard library, existing Node test runner and shell release tools.
|
||||
|
||||
**Spec:** `agents/gander/work/queue-196/DESIGN.md`, approved r2 at 5fe259f16, with N5 restart policy added in r3.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Work in the existing gander/row196 worktree. Plain node_modules symlink. No new package dependency.
|
||||
- No live files, credentials, listeners, units, second uid or external authentication service.
|
||||
- Helpers: proof envelopes only, at most 16 concurrent, one second timeout, 256-byte strict output.
|
||||
- Root-owned system Python ELF at resolved /usr/bin/python3, never PATH or a home/shim path.
|
||||
- console/ 0700, tester.sock 0600, Unix pathname at most 107 bytes.
|
||||
- One principal and transient cap per request. R3 owns user access policy and user-cli integration.
|
||||
- Mos accepted socket-only tester access. No TCP relay or control-board change.
|
||||
- Single test files run freely. Ask Sage before any suite. Stop and report two failures in a gate run.
|
||||
- Commit code only after applicable suites pass. Sage owns the final gate and manifest queue moves.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- Connection overlap and disconnect: helpers must retain the correct Socket object and release capacity on every exit.
|
||||
- OS interpreter replacement: restart records the actual safe binary, while old receipts remain unchanged.
|
||||
- Long or unsafe instance paths: refuse before binding or stopping any service.
|
||||
- Rollback/recovery: distinguish intentional tester disablement from restoration of an old saved TCP unit.
|
||||
- CLI nonce respawn and session expiry: direct-parent provenance must survive correct Console spawning and refuse stale identity.
|
||||
|
||||
## Task 1: kernel credentials and interpreter evidence
|
||||
|
||||
Files: create `packages/bus/src/peercred.mjs`, `packages/bus/src/peercred.py`,
|
||||
`packages/bus/src/interpreter.mjs`; tests in `packages/bus/tests/peercred.test.mjs` and
|
||||
`packages/bus/tests/interpreter.test.mjs`, with subprocess fixtures under `packages/bus/fixtures/`.
|
||||
|
||||
Interfaces: `inspectInterpreter()` returns a validated immutable record of path, version,
|
||||
device, inode and sha256. `recordInterpreterStart(directory, record)` appends the actual startup
|
||||
identity safely. `createPeerReader({interpreter})` returns `{read(socket),close()}`. read resolves
|
||||
to `{pid,uid,gid}` from that exact socket, or refuses. Test-only injected OS/process operations
|
||||
do not become product flags or environment overrides.
|
||||
|
||||
- [ ] Write interpreter safety, receipt replacement and cap-independent helper tests (T3/T10).
|
||||
- [ ] Run each new test file alone and record the expected missing-module failures.
|
||||
- [ ] Implement system interpreter validation and startup receipt append with no-follow, owner/type/mode checks.
|
||||
- [ ] Write T2's eight-process barrier plus racing copier, wrong-fd case and post-helper socket exchange.
|
||||
- [ ] Implement fd-3 helper, strict result validation, bounded async reader and helper shutdown.
|
||||
- [ ] Run `node --test packages/bus/tests/interpreter.test.mjs` and separately `node --test packages/bus/tests/peercred.test.mjs`.
|
||||
- [ ] Confirm process/socket cleanup, eight distinct kernel PIDs, copier refusal and original receipt preservation.
|
||||
|
||||
## Task 2: principal provenance and bus dispatch
|
||||
|
||||
Files: modify `packages/bus/src/{human,tester,server,runtime}.mjs`, create
|
||||
`packages/bus/src/principal.mjs`, add `packages/bus/tests/principal.test.mjs`, and extend
|
||||
`packages/bus/tests/human.test.mjs`. Extend server/runtime test files after inspecting their fixtures.
|
||||
|
||||
Interfaces: `verifyPrincipal(request, {peer,...trustedPathsAndProcessReaders})` returns
|
||||
`{kind,business,id}`. Human maps to the business human, tester to tester, user to its verified
|
||||
Console proof id. No roles or grants. Server obtains peer metadata from Task 1 only after
|
||||
envelope validation and only for proofs. Existing cap dispatch never invokes the helper.
|
||||
Future user binding stays refused without R3's trusted integration.
|
||||
|
||||
- [ ] Write T4/T5/T6/T8 process-table tests, including changed ancestry, distinct nonce namespaces and kind confusion.
|
||||
- [ ] Run the focused new file and confirm expected failure before implementing the verifier.
|
||||
- [ ] Parameterize the nonce/path portion of the existing human walk and retain all marker/binary/launch checks.
|
||||
- [ ] Wire peer metadata, interpreter startup receipt and one-request cleanup into runtime/server.
|
||||
- [ ] Test cap requests with a failing helper spy (M22), and test async refusal/close cleanup.
|
||||
- [ ] Run each affected bus test file alone. Record any base fixture changes needed for real peer identity.
|
||||
|
||||
## Task 3: tester Unix HTTP and Console nonce spawning
|
||||
|
||||
Files: modify `packages/webui/src/{cli,serve,bus}.mjs`, create
|
||||
`packages/webui/src/tester-socket.mjs` and `packages/cli/src/tester-http.mjs`; add focused
|
||||
`packages/webui/tests/tester-socket.test.mjs` and `packages/cli/tests/tester-http.test.mjs`.
|
||||
Extend existing WebUI transport tests.
|
||||
|
||||
Interfaces: `startTesterSocket({dataRoot,...handlerOptions})` owns the private listener and
|
||||
cleanup. `testerRequest({dataRoot,method,path,body})` uses the fixed logical origin and bounded
|
||||
Unix HTTP. Console human child spawning supplies its initial nonce, preserving agent markers.
|
||||
R3 reuses that spawn pattern for its user-cli.
|
||||
|
||||
- [ ] Write T1/T3/T9 socket permission, unsafe-path, wrong-origin and instance separation tests.
|
||||
- [ ] Confirm the new focused files fail, then implement --tester-socket and refusal of --tester-port.
|
||||
- [ ] Keep tester principal, write allowlist and cookie/header refusals intact. Add no browser relay.
|
||||
- [ ] Write and implement the initial-nonce Console spawn test and the helper request timeout test.
|
||||
- [ ] Run each new or changed test file alone, verifying cleanup and no tester TCP bind.
|
||||
|
||||
## Task 4: release migration and operator evidence
|
||||
|
||||
Files: modify `scripts/{webui-service,stack-release}.sh`, inspect
|
||||
`scripts/stack-copy.sh` closure, extend `packages/cli/tests/stack-release.test.mjs` and its
|
||||
render fixtures. Update `docs/TOOLS.md` and package READMEs with the implementation.
|
||||
|
||||
Interfaces: `activate ID BUSINESS --tester-socket` explicitly replaces inherited tester-port
|
||||
flags. Pointer and unit argv agree. Candidate support and interpreter preflight run before
|
||||
any unit stop. Activation gate evidence records the same interpreter as the bus startup receipt.
|
||||
|
||||
- [ ] Write T7 explicit conversion, inherited mismatch and unsupported-target refusal fixtures.
|
||||
- [ ] Run only the affected test file, then implement flag rendering, plan output and rollback disabling unsupported tester.
|
||||
- [ ] Preserve old-copy recovery behavior and report restored old security state accurately.
|
||||
- [ ] Add rehearsal scratch socket and T10 interpreter gate identity comparison.
|
||||
- [ ] Verify same-copy activation refusal is preserved. Document N5's next-reviewed-copy re-pin path.
|
||||
- [ ] Run the affected file alone, shell syntax checks, and inspect remaining tester-port references by purpose.
|
||||
|
||||
## Task 5: mutants, approved suites and manifest
|
||||
|
||||
Files: add the row's mutation runner and receipts under `agents/gander/work/queue-196/`.
|
||||
Append BUILD-LOG phase receipts. No SESSIONS or queue file edits.
|
||||
|
||||
- [ ] Exercise M1-M23 against isolated scratch copies. Record killer test, restored hash and cleanup for each.
|
||||
- [ ] Run focused tests on Node 24 and 26, including the concurrent T2 case and socket lifetime checks.
|
||||
- [ ] Send Sage the scope and estimated duration, obtain the suite slot, then run approved bus/WebUI/CLI suites.
|
||||
- [ ] Let Sage own the scripts/test-*.sh gate. Stop on the repository's two-failure rule.
|
||||
- [ ] Build the exact changed-path manifest with acceptance evidence, search results, limits and consumer audit.
|
||||
- [ ] Commit/push the verified candidate only after applicable suites pass. Send Duckworth and Sage path, commit and hashes for manifest review.
|
||||
|
||||
Execution stays with Gander in this worktree. No new agent delegation. Duckworth reviews
|
||||
the manifest independently. Push is only to gander/row196. No merge, re-pin or deployment
|
||||
is authorized by this plan.
|
||||
@@ -1,186 +0,0 @@
|
||||
{
|
||||
"status": "uncommitted draft; regenerate after final tests",
|
||||
"base": "4d26003f731a6eec439df6a48cacc3ef482a59c8",
|
||||
"files": [
|
||||
{
|
||||
"path": "docs/TOOLS.md",
|
||||
"baselineSha256": "5bd1bc697cd56e57b00e60c0e80be72cd1043a55a6d2fc0b9e2ccdd0c5cf9802",
|
||||
"candidateSha256": "7eba3f1ac2e5a159c9c764f1c3e4f6fdfaf06d2904cdc36338723d5ddd9044c8"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/README.md",
|
||||
"baselineSha256": "4377cc32aed37fa1bb645c0bbc3340eed4ec01740e67327449949474e35d3fdb",
|
||||
"candidateSha256": "becff52b647f28079d2756b26a7e46e28f14b695f57b2dede779e7cb43af3d96"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/fixtures/peer-client.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "60720461a7994011a9a30594715eb2c992be699cd61c656e77cbd609ad05328e"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/human.mjs",
|
||||
"baselineSha256": "b995fc5cff3af8ade4c990d0e3bebf9d83829e00f034ebc9c3dcfd05f252b573",
|
||||
"candidateSha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/interpreter.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/peercred.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/peercred.py",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "d1536a8f472fa5b3773adb10555529ed049ad63b647c0b44c00ce5f25ce09f85"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/principal.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/runtime.mjs",
|
||||
"baselineSha256": "fe70f4b6568fbfec85e58d34f3cb8c3624daba65487d73ad24e981f2121cb27a",
|
||||
"candidateSha256": "e001bdba127fc6e7a6e95ef1864e661b2842f3c797169ab0a6ccaa5e2cd7da00"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/src/server.mjs",
|
||||
"baselineSha256": "8a1b8eac60a5885bbfd36f939b63443de303ed298bf9e1ff653a5a7bcbae76d2",
|
||||
"candidateSha256": "a5788c0a0b2b78de5eb2111a47c475e100cbb0e2eca4b601e7f027cac2c56d7e"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/tests/channel-proof.test.mjs",
|
||||
"baselineSha256": "98f12f509f0e292fa0b0e3924834f9d0f285c08ea5c4d22057566c49e2bd6c68",
|
||||
"candidateSha256": "e99133c3c957a1e856e404343721add2430e4a0d8c75452517aef04942af56ab"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/tests/interpreter.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "d19779b97e80ed3d62f58d0e798df46c23f45fa02b14fb4ac7a2ff4690a734e2"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/tests/peer-transport.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "a39af877a2d9143cc8d0584af0d603ca7ab651db8787e5816579762f17295887"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/tests/peercred.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "2c4c0bed41e546ce94535d8c4547b1cf5552a181183525dd1d52e2af1d64ccb6"
|
||||
},
|
||||
{
|
||||
"path": "packages/bus/tests/principal.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "cefbc0688d823c69196d94bb71467884acd639070721286d7e8e4e986c8240c1"
|
||||
},
|
||||
{
|
||||
"path": "packages/cli/README.md",
|
||||
"baselineSha256": "2c9f8fe7143aa80e59826ab53bd046de2b5e0a8f3e81fe4e3df77726bb54d50b",
|
||||
"candidateSha256": "23f4c767a67b358025f0d035d2a676e14e335478c480bc44a5c303066c40709c"
|
||||
},
|
||||
{
|
||||
"path": "packages/cli/src/tester-http.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "4bd7bdf0898844684ebf9444b110d2c5b6fa874b2681183e54f1f62f97823192"
|
||||
},
|
||||
{
|
||||
"path": "packages/cli/tests/stack-release.test.mjs",
|
||||
"baselineSha256": "b01ff656962c6fdb5bc3554cfbb13645b7f25adae5f6cd9608af8de6d6091c6c",
|
||||
"candidateSha256": "4c92b7cc7c4b942a1bbb9a9e24c7c1ef10e23d3f9d166bee98ed1b7f6b8de4b0"
|
||||
},
|
||||
{
|
||||
"path": "packages/cli/tests/tester-http.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "fbaa36e64129aee324341364bde831c379c098b97932cceadb7fcaa7909d4b20"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/README.md",
|
||||
"baselineSha256": "78c7b60d23cf20863a1cb66e16af2f9363d08acc79c5b4a344cd8af4fbe5bd44",
|
||||
"candidateSha256": "8bf41140a0c013a3d77757b66e442d46c278cf8eb0f54ac26b4e477e8265279b"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/src/bus.mjs",
|
||||
"baselineSha256": "76a1b57858c4aa41071746713a196299264851c18322a6f9869d553f0362d421",
|
||||
"candidateSha256": "934ab2f9b6bde72aafe7814758550c283eb25a4f3a23b284021b13718d67178f"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/src/cli.mjs",
|
||||
"baselineSha256": "4f988333ec6af225a023323dda85a159b81a77f408a8dd7bff798fe9a1f4b24b",
|
||||
"candidateSha256": "fbe9db9352300fb969aa889e40323c61b1a51d01f63434964be0a8c530240e50"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/src/serve.mjs",
|
||||
"baselineSha256": "ba7d3e35b225cb4e17a4d2530a0f34d0a3734641e3982229a38207a36b3cd433",
|
||||
"candidateSha256": "894bdb260af418bef7ca9dac5dd19541ca898d6f946ed12fa832bc3475234709"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/src/tester-socket.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/browser.mjs",
|
||||
"baselineSha256": "1e70a0175f1d660f6b71449a1423ece83fbe8332108960fa9aff6f7a5e48022a",
|
||||
"candidateSha256": "2f3ea92cbe1ca115301ee1e3b8fc55a206842606237446b700881ee57a1b18cb"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/proof-cli.test.mjs",
|
||||
"baselineSha256": "c23975b140ed977f3b98dbbec5de289e9f930cc42089193c4aa7c106338a68ba",
|
||||
"candidateSha256": "105cf4936db0498d0c0025698c7ee33ddce9530b43f6c829b77afdbcb3075207"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/proof.test.mjs",
|
||||
"baselineSha256": "c06a1e673ef6f3df8f0473692c828aeba853369d2b668e821f1d41e6340b6503",
|
||||
"candidateSha256": "5f34d699a1bce6f46f286def1a93fdd4d863d06bec6839677fae1041be7153dd"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/service.test.mjs",
|
||||
"baselineSha256": "424be2837a18a41eab56b0deb576e35b460ac16b8c371762ad44896ffc3aad7d",
|
||||
"candidateSha256": "2304ced2473c25b0369b4bb9b1433c1be3ee9ab8a6f75c971487abcd48cb1d1b"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-browser-fixture.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "9215ff92df774ba1e83819b87f758b53c8f3d19c2824c916490165914f1cb9e8"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-browser.test.mjs",
|
||||
"baselineSha256": "3cc6def0c8cfb6cf7bb91a52df6e57f18071549588b35394e0083f7dc938dacf",
|
||||
"candidateSha256": "89ffc5cbed1630a523080e8561d3678c1f781fb657199a5cd6cdb8189f082c73"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-cli.test.mjs",
|
||||
"baselineSha256": "dd74517b336e1670a5910481920ba875ffb004d52d8c6bee7cc5efc81e43215d",
|
||||
"candidateSha256": "a5c6c95dbda75dce37d38d3ce4761d3ad576babd9304a046e9a62b1b5dc2fdfd"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-fixture.mjs",
|
||||
"baselineSha256": "2169803c50815d214cab120eaa9d1542aa3b96bfe4738d68307eb0b92c1213f3",
|
||||
"candidateSha256": "7d4f3a59d21675807b1bfb92d40b7378134df450590bc6d42fa4103ab1343348"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-listener.test.mjs",
|
||||
"baselineSha256": "aed4cec37401bc7953b1a872349ed77c70210fcfb75ebf6d4e881a393f92c7e9",
|
||||
"candidateSha256": "7f0fc2daead11048cb86e0a74b6a223314785b1740a2927461d012b876a69f18"
|
||||
},
|
||||
{
|
||||
"path": "packages/webui/tests/tester-socket.test.mjs",
|
||||
"baselineSha256": null,
|
||||
"candidateSha256": "c01a942da1ad66a08a4e7c92d3c7e64f517530acfe5517f1e9895767d5ea3c0b"
|
||||
},
|
||||
{
|
||||
"path": "scripts/stack-release.sh",
|
||||
"baselineSha256": "5bb3ac40102ce515f1acdcbc5670feb161ce40a193e94e2f8b104ebf98eddc38",
|
||||
"candidateSha256": "59b39d63feb77f22abb4b7152c18feff53715cf3a10dde6374c69615329868c7"
|
||||
},
|
||||
{
|
||||
"path": "scripts/webui-service.sh",
|
||||
"baselineSha256": "d514caf757c03e5d02ef39b19359669bd2476dca1877f5d237c65bd62c33d2bf",
|
||||
"candidateSha256": "67cc347231d4410acf0fd845bde58aeabf099d1d6cf3e36efda4db66c5438c10"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
# Row 196 execution ledger
|
||||
Plan: agents/gander/work/queue-196/PLAN.md
|
||||
Base: 4d26003f7. Build authorized by sage-1371, mos-1021 item 2 and mos-1023 item 3.
|
||||
Pre-flight: Task 1 supplies peer reader and interpreter evidence to Task 2 and release gate in Task 4. Task 2 supplies principal provenance; R3 supplies user policy. Task 3 supplies tester transport to Task 4. Interfaces match approved design.
|
||||
Ruling: retain this repository packet as the ledger instead of adding a root .superpowers directory, honoring bootstrap-only root. No code commits before applicable suites pass; Duckworth is the independent manifest reviewer. Suites and mutant batches require Sage slot.
|
||||
Task 1: in progress.
|
||||
Task 1: interpreter tests RED missing module -> GREEN 3/3; peer reader RED missing module -> timeout diagnosed: Node spawn stdio pauses Socket -> explicit caller resume -> GREEN 3/3. Concurrency test observed nine distinct peers, eight accepted, copier refused, post-helper bidirectional traffic. No suite/commit yet.
|
||||
Task 2: principal RED missing module -> GREEN 3/3; existing human 4/4, peer-transport RED -> GREEN 2/2, transport 5/5, tester 14/14, process 8/8, all individual files. Runtime records interpreter and supplies kernel peer to shared verifier; cap traffic does not spawn helper.
|
||||
Task 3: BLOCKED by design contradiction, reported gander-0016/0017 at 2026-10-20T19:34:28Z. Node/libuv closes Unix listener by unlinking its pathname even after replacement inode. Scratch cleanup assertion reproduces; conditional JavaScript cleanup is too late. Tester module not wired into serve. Recommend explicitly accepting same-uid pathname replacement as residual under private-parent boundary. No product commit, suite or mutant batch.
|
||||
Task 3 resumed: sage-1382 selected Duckworth option 1. Temporary bind -> rename -> conditional final inode cleanup implemented. Replacement survival and absence of temporary name RED -> GREEN. M24 is bind-final-directly. Remaining same-uid lstat/unlink race recorded in BUILD.md. No design weakening.
|
||||
Gate run 1 bus: 463 pass, 1 fail, no other packages started. Failure was row164 byte baseline, which already records intentional re-pins in later rows. Ruling: update only human.mjs and webui bus.mjs pins because their approved row196 changes are intentional, preserving the other two pins, literal entry-path tests and plug-in import ban. This does not bypass a policy refusal or weaken the proof walk.
|
||||
Gate run 2: bus 464/464 pass. WebUI stopped on second failure: tester-browser.test.mjs still constructed TCP URLs using Unix address().port. 269 top-level passing cases observed, 2 failed, final suite summary unavailable due interruption. CLI and M1-M24 not started. Not timing failures. Sage notified clear in gander-0023. Node24 peer 3/3 and principal 4/4 passed. No suite/mutant rerun without renewed slot. Next: migrate scratch browser fixture without adding a product relay.
|
||||
Focused repair after gate stop: tester-browser 2/2 GREEN using CDP request interception and real scratch Unix HTTP, no TCP listener or product relay. Self-review added FIFO startup-receipt refusal (RED timeout -> GREEN) and helper file preflight (RED no refusal -> GREEN). interpreter 5/5 and peer 4/4. Renewed slot requested gander-0024; suites and mutant batch still stopped.
|
||||
sage-1409 accepts the fixture repair and requires separate manifest items for the row164 hashes and startup refusals, each with tests and mutants. sage-1416 delays the slot about an hour; no suite or mutant batch before go. Draft MANIFEST.md and PRODUCT-FILES.json prepared. M25 FIFO, M26 helper preflight and M27 helper shutdown are prepared, not executed. Gander-0025 reports the planned M1-M27 batch.
|
||||
Focused shutdown check: peer reader now awaits killed helper close events and server close awaits the reader. peer-shutdown-red.txt records the failing assertion, peercred-green.txt the 5/5 result. Final focused Node24 reruns: peercred 5/5, principal 4/4. Node26 peer-transport 3/3 after async reader close. git diff --check clean. These are single-file checks, not renewed suite acceptance.
|
||||
Consumer audit found proof insertion missed the new socket flag, making pointer and rendered unit argument order disagree. Focused row196 release fixture extended with preview + explicit proof: RED exact-order mismatch -> GREEN 1/1 after adding --tester-socket to the insertion boundary. Existing proof-file no-read check passes. Evidence release-proof-order-{red,green}.txt. M28 prepared for that boundary. Final planned mutant batch M1-M28; none executed. Shell syntax and diff whitespace checks pass.
|
||||
sage-1425: complete static self-review before slot, document refused caller outcome. Real Client test now verifies human-required for proof refusal and peercred-required for helper failure, without dispatch; peer-transport 4/4. Scratch HEAD-vs-candidate comparison returns human-required in both. Quackerjack's exact failing instance is not reproduced; client.mjs unchanged and abrupt close still outcome-unknown. Manifest states the limit.
|
||||
Static review complete: peer/helper lifecycle, runtime, verifier walk, tester socket, Console spawn, Unix client, release/recovery/preflight and fixtures inspected. Found release path preflight missing before stop: long-path fixture RED -> GREEN with preflight; unsafe parent also refuses before any stop. release-path-{red,green}.txt. M29 prepared. Restored unchanged legacy tester-port-invalid wording. Prepared batch now M1-M29, still unexecuted. No further implementation changes planned before Sage's go.
|
||||
sage-1464 (2026-10-20T20:40Z): renewed bus/WebUI/CLI and M1-M29 slot is fifth, after rows 192, 201, 204 and 200. Continue holding until Sage's go. Fleet notice #426(a), mos-1086, live 20:32:55Z: no branch deletion; any rebase must use a new branch name; leave pr-merge.sh -d off. No rebase or deletion is planned for this row.
|
||||
sage-2051 / mos-1193 (2026-10-21T18:45Z) explicitly orders committing and fast-forward pushing the current row196 build before its deferred full verification, then rebasing onto a36523713 on new gander/row196-r2. This supersedes the earlier no-product-commit-before-suite gate for this preservation commit only. Full acceptance remains pending. No tests until Sage says clear. Subsequent batch order: M1-M29, bus, WebUI, CLI, every whole scripts/test-*.sh; TMPDIR must be created with mktemp under /mnt/storage/scratch/tmp, never /tmp. Preserve the old branch. Row252 design may proceed while waiting; no row252 code before Webby approves.
|
||||
@@ -1,10 +0,0 @@
|
||||
✔ channel conversion preserves the four proof/transport byte baselines and modules cannot import a proof caller (0.85226ms)
|
||||
✔ channel recovery still requires the same shim path and rejects an absent or forged proof (308.49278ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 389.108211
|
||||
@@ -1,12 +0,0 @@
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.430093ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.499236ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.651712ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.268904ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 60.373844
|
||||
@@ -1,28 +0,0 @@
|
||||
✖ non-regular startup receipt refuses promptly without waiting on a FIFO peer (1008.229518ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1059.700302
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/interpreter.test.mjs:69:1
|
||||
✖ non-regular startup receipt refuses promptly without waiting on a FIFO peer (1008.229518ms)
|
||||
AssertionError [ERR_ASSERTION]: unsafe FIFO must refuse without blocking startup
|
||||
|
||||
null !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/interpreter.test.mjs:75:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: null,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
✔ system Python is a resolved root-owned ELF, never a shim or home interpreter (1.874163ms)
|
||||
✔ real system interpreter is available and safely inspected (16.54588ms)
|
||||
✔ restart receipt appends actual replacement identity and refuses unsafe files (3.340596ms)
|
||||
✖ activation gate compares fresh startup receipt to the current interpreter identity (1.116553ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 70.983973
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/interpreter.test.mjs:59:1
|
||||
✖ activation gate compares fresh startup receipt to the current interpreter identity (1.116553ms)
|
||||
TypeError: verifyInterpreterReceipt is not a function
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/interpreter.test.mjs:64:15)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
@@ -1,13 +0,0 @@
|
||||
✔ system Python is a resolved root-owned ELF, never a shim or home interpreter (1.992922ms)
|
||||
✔ real system interpreter is available and safely inspected (18.869225ms)
|
||||
✔ restart receipt appends actual replacement identity and refuses unsafe files (3.111417ms)
|
||||
✔ activation gate compares fresh startup receipt to the current interpreter identity (1.357978ms)
|
||||
✔ non-regular startup receipt refuses promptly without waiting on a FIFO peer (32.332597ms)
|
||||
ℹ tests 5
|
||||
ℹ suites 0
|
||||
ℹ pass 5
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 105.511835
|
||||
@@ -1,35 +0,0 @@
|
||||
node:internal/modules/esm/resolve:272
|
||||
throw new ERR_MODULE_NOT_FOUND(
|
||||
^
|
||||
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/interpreter.mjs' imported from /home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/interpreter.test.mjs
|
||||
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
|
||||
at moduleResolve (node:internal/modules/esm/resolve:879:10)
|
||||
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
|
||||
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
|
||||
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
|
||||
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
|
||||
at #resolve (node:internal/modules/esm/loader:690:17)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
|
||||
code: 'ERR_MODULE_NOT_FOUND',
|
||||
url: 'file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/interpreter.mjs'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
✖ packages/bus/tests/interpreter.test.mjs (29.295027ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 34.780279
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/interpreter.test.mjs:1:1
|
||||
✖ packages/bus/tests/interpreter.test.mjs (29.295027ms)
|
||||
'test failed'
|
||||
@@ -1,13 +0,0 @@
|
||||
✔ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (244.921933ms)
|
||||
✔ helper output and failures fail closed including unsafe gid (2.053177ms)
|
||||
✔ helper timeout, capacity and shutdown refuse without leaving children (26.359085ms)
|
||||
✔ missing or non-regular peer helper refuses at construction before opening the bus (0.377385ms)
|
||||
✔ shutdown waits for killed helper close before returning (5.980511ms)
|
||||
ℹ tests 5
|
||||
ℹ suites 0
|
||||
ℹ pass 5
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 352.965488
|
||||
@@ -1,12 +0,0 @@
|
||||
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (2.18658ms)
|
||||
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.652569ms)
|
||||
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.382801ms)
|
||||
✔ human leaf identity is reread after walking the parents (0.23585ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 69.614066
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"version": "v24.22.0",
|
||||
"path": "/mnt/storage/scratch/tmp/g196-node24-gzFqsW/bin/node",
|
||||
"sha256": "cc2f601cee82d9a5a233302fe307abc600727035fd7d2c3a3571755989e95008",
|
||||
"source": "https://nodejs.org/dist/v24.22.0/node-v24.22.0-linux-x64.tar.xz"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
✖ missing or non-regular peer helper refuses at construction before opening the bus (0.992412ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 73.967525
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/peercred.test.mjs:93:1
|
||||
✖ missing or non-regular peer helper refuses at construction before opening the bus (0.992412ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/peercred.test.mjs:94:9)
|
||||
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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /peercred-required/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
✖ shutdown waits for killed helper close before returning (1.761026ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 78.151245
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/peercred.test.mjs:98:1
|
||||
✖ shutdown waits for killed helper close before returning (1.761026ms)
|
||||
AssertionError [ERR_ASSERTION]: shutdown must await the helper close event
|
||||
|
||||
0 !== 1
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/peercred.test.mjs:106:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
✔ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (7.225507ms)
|
||||
✔ proof helper error and disconnect during verification never dispatch or bind (1.683542ms)
|
||||
✔ Client receives explicit proof refusal codes without dispatch, not outcome-unknown (50.186199ms)
|
||||
✔ asynchronous proof calls release their transient session on success, failure and timeout (28.028729ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 148.350042
|
||||
@@ -1,50 +0,0 @@
|
||||
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (5.927716ms)
|
||||
✖ proof helper error and disconnect during verification never dispatch or bind (1.959972ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57.64878
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/peer-transport.test.mjs:9:1
|
||||
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (5.927716ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/peer-transport.test.mjs:15:9)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/peer-transport.test.mjs:20:1
|
||||
✖ proof helper error and disconnect during verification never dispatch or bind (1.959972ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- 'peercred-required'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/peer-transport.test.mjs:25:9)
|
||||
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: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: 'peercred-required',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
✖ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (10044.708285ms)
|
||||
ℹ started 9 clients
|
||||
ℹ received 9 envelopes
|
||||
ℹ read 9 peers
|
||||
ℹ received 9 replies; after 0
|
||||
✔ helper output and failures fail closed including unsafe gid (2.230205ms)
|
||||
✔ helper timeout, capacity and shutdown refuse without leaving children (25.395463ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 1
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10137.502565
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/peercred.test.mjs:15:1
|
||||
✖ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (10044.708285ms)
|
||||
'test timed out after 10000ms'
|
||||
@@ -1,13 +0,0 @@
|
||||
✔ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (274.013965ms)
|
||||
✔ helper output and failures fail closed including unsafe gid (1.948532ms)
|
||||
✔ helper timeout, capacity and shutdown refuse without leaving children (26.176111ms)
|
||||
✔ missing or non-regular peer helper refuses at construction before opening the bus (0.331892ms)
|
||||
✔ shutdown waits for killed helper close before returning (5.788665ms)
|
||||
ℹ tests 5
|
||||
ℹ suites 0
|
||||
ℹ pass 5
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 396.110405
|
||||
@@ -1,35 +0,0 @@
|
||||
node:internal/modules/esm/resolve:272
|
||||
throw new ERR_MODULE_NOT_FOUND(
|
||||
^
|
||||
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/peercred.mjs' imported from /home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/peercred.test.mjs
|
||||
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
|
||||
at moduleResolve (node:internal/modules/esm/resolve:879:10)
|
||||
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
|
||||
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
|
||||
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
|
||||
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
|
||||
at #resolve (node:internal/modules/esm/loader:690:17)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
|
||||
code: 'ERR_MODULE_NOT_FOUND',
|
||||
url: 'file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/peercred.mjs'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
✖ packages/bus/tests/peercred.test.mjs (31.978232ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 37.606365
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/peercred.test.mjs:1:1
|
||||
✖ packages/bus/tests/peercred.test.mjs (31.978232ms)
|
||||
'test failed'
|
||||
@@ -1,15 +0,0 @@
|
||||
✔ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (259.164726ms)
|
||||
ℹ started 9 clients
|
||||
ℹ received 9 envelopes
|
||||
ℹ read 9 peers; states false/true/false,false/true/false,false/true/false,false/true/false,false/true/false,false/true/false,false/true/false,false/true/false,false/true/false
|
||||
ℹ received 9 replies; after 9
|
||||
✔ helper output and failures fail closed including unsafe gid (1.460068ms)
|
||||
✔ helper timeout, capacity and shutdown refuse without leaving children (26.09001ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 355.364452
|
||||
@@ -1,12 +0,0 @@
|
||||
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.70707ms)
|
||||
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.585886ms)
|
||||
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.289109ms)
|
||||
✔ human leaf identity is reread after walking the parents (0.170627ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53.605151
|
||||
@@ -1,35 +0,0 @@
|
||||
node:internal/modules/esm/resolve:272
|
||||
throw new ERR_MODULE_NOT_FOUND(
|
||||
^
|
||||
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/principal.mjs' imported from /home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/tests/principal.test.mjs
|
||||
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
|
||||
at moduleResolve (node:internal/modules/esm/resolve:879:10)
|
||||
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
|
||||
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
|
||||
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
|
||||
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
|
||||
at #resolve (node:internal/modules/esm/loader:690:17)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
|
||||
code: 'ERR_MODULE_NOT_FOUND',
|
||||
url: 'file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/bus/src/principal.mjs'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
✖ packages/bus/tests/principal.test.mjs (28.063196ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 33.574982
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/principal.test.mjs:1:1
|
||||
✖ packages/bus/tests/principal.test.mjs (28.063196ms)
|
||||
'test failed'
|
||||
@@ -1,16 +0,0 @@
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (135.66144ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (72.874581ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (105.550828ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (164.1127ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (109.156675ms)
|
||||
✔ REQ-LIMIT-4 the broker process answers the launcher's mayLaunch op with the limits' awaited decision (100.062279ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (70.91343ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (105.059528ms)
|
||||
ℹ tests 8
|
||||
ℹ suites 0
|
||||
ℹ pass 8
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 913.679719
|
||||
@@ -1,11 +0,0 @@
|
||||
✔ proof startup refuses partial flags non-preview and invalid fixture file metadata or bytes (1039.716731ms)
|
||||
✔ serve argv carries only fixture proof path and header; startup explicitly reports proof on or off (241.707876ms)
|
||||
✔ CLI applies proof on the main listener and rejects that header on its tester listener (232.216606ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1631.223267
|
||||
@@ -1,12 +0,0 @@
|
||||
✔ proxy proof admits named reads and sends, reads its fixture once, and never exposes the value (33.64167ms)
|
||||
✔ proxy proof refuses missing wrong short or repeated proof before auth and before any read (9.334211ms)
|
||||
✔ proxy proof header is refused on other Hosts and on the tester listener (4.637787ms)
|
||||
✔ proof options reject unsafe or colliding header names without disclosing file contents (0.742812ms)
|
||||
ℹ tests 4
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 170.24723
|
||||
@@ -1,2 +0,0 @@
|
||||
baseline human-required
|
||||
candidate human-required
|
||||
@@ -1,9 +0,0 @@
|
||||
✔ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (5883.382797ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 6639.610757
|
||||
@@ -1,36 +0,0 @@
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (6495.905564ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 7217.097801
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/stack-release.test.mjs:2088:1
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (6495.905564ms)
|
||||
AssertionError [ERR_ASSERTION]: stack-release: interpreter: {"path":"/usr/bin/python3","sha256":"fixture"}
|
||||
stack-release: captured the rollback set: /mnt/storage/scratch/tmp/sr-eLxVuy/data-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/state/stack-rollback/20261020T201023Z-bb03a6447dc9
|
||||
stack-release: stopped both units
|
||||
stack-release: stopped store schema: 3g
|
||||
stack-release: backup: /mnt/storage/scratch/tmp/sr-eLxVuy/data-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/backups/stack/20261020T201023Z-bb03a6447dc9
|
||||
stack-release: installed bb03a6447dc98f172cbd28c2fbab126b37ff69c3
|
||||
stack-release: gate passed on bb03a6447dc98f172cbd28c2fbab126b37ff69c3
|
||||
stack-release: activate ok: bb03a6447dc98f172cbd28c2fbab126b37ff69c3 is live for acme
|
||||
|
||||
|
||||
0 !== 3
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/cli/tests/stack-release.test.mjs:2108:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 3,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
✔ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (5160.650984ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 6043.67883
|
||||
@@ -1,43 +0,0 @@
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (3046.881963ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3691.652388
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/stack-release.test.mjs:2088:1
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (3046.881963ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
... Skipped lines
|
||||
|
||||
[
|
||||
'--port',
|
||||
'43495',
|
||||
'--business',
|
||||
'acme',
|
||||
...
|
||||
'jason',
|
||||
+ '--tester-socket',
|
||||
'--proof-header',
|
||||
'x-console-proof',
|
||||
'--proof-file',
|
||||
'/mnt/storage/scratch/tmp/sr-fLVpjD/proof.187',
|
||||
- '--tester-socket'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/cli/tests/stack-release.test.mjs:2102:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ '--port', '43495', '--business', 'acme', '--public-host', 'console.example', '--auth-header', 'x-user', '--allow-user', 'jason', '--tester-socket', '--proof-header', 'x-console-proof', '--proof-file', '/mnt/storage/scratch/tmp/sr-fLVpjD/proof.187' ],
|
||||
expected: [ '--port', '43495', '--business', 'acme', '--public-host', 'console.example', '--auth-header', 'x-user', '--allow-user', 'jason', '--proof-header', 'x-console-proof', '--proof-file', '/mnt/storage/scratch/tmp/sr-fLVpjD/proof.187', '--tester-socket' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
✔ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (4452.603661ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 5183.817431
|
||||
@@ -1,36 +0,0 @@
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (2938.271119ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3279.087225
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/stack-release.test.mjs:2088:1
|
||||
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (2938.271119ms)
|
||||
AssertionError [ERR_ASSERTION]: stack-release: captured the rollback set: /mnt/storage/scratch/tmp/sr-QgMOBP/data/state/stack-rollback/20261020T194438Z-6e9ce64cf63b
|
||||
stack-release: drop --tester-port 7333; the target Console does not support it
|
||||
stack-release: stopped both units
|
||||
stack-release: stopped store schema: 3g
|
||||
stack-release: backup: /mnt/storage/scratch/tmp/sr-QgMOBP/data/backups/stack/20261020T194438Z-6e9ce64cf63b
|
||||
stack-release: installed 6e9ce64cf63b125d007476844c4d695a308514d8
|
||||
stack-release: gate passed on 6e9ce64cf63b125d007476844c4d695a308514d8
|
||||
stack-release: activate ok: 6e9ce64cf63b125d007476844c4d695a308514d8 is live for acme
|
||||
|
||||
|
||||
0 !== 3
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/cli/tests/stack-release.test.mjs:2095:45)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 3,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
✔ row196 socket flag renders only for supporting targets and refuses mixed transports (16.910871ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 77.189844
|
||||
@@ -1,32 +0,0 @@
|
||||
✖ row196 socket flag renders only for supporting targets and refuses mixed transports (7.489228ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 75.646616
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/service.test.mjs:812:1
|
||||
✖ row196 socket flag renders only for supporting targets and refuses mixed transports (7.489228ms)
|
||||
AssertionError [ERR_ASSERTION]: usage: scripts/webui-service.sh copy COMMIT [--copies DIR] [--allow-worktree] | render [--copy ID [--copies DIR]] [FLAGS] | install --copy ID [--copies DIR] [--dir DIR] [--no-reload] [--allow-worktree] [FLAGS] | uninstall [--dir DIR] [--no-reload] | status [--dir DIR]
|
||||
FLAGS: [--port N] [--business ID] [--board URL] [--public-host NAME --auth-header HEADER --allow-user ID [--send]] [--tester-port N] [--proof-header NAME --proof-file PATH]
|
||||
--tester-port: 1024..65535, different from --port and never 7332; loopback tester listener.
|
||||
--allow-worktree is for the tests only: copy and install otherwise refuse outside the canonical checkout.
|
||||
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/tests/service.test.mjs:815:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
✔ tester service render carries the tester port alongside existing flags (24.94035ms)
|
||||
✔ tester service refuses forwarded 7332 even with another main port (9.82912ms)
|
||||
✔ tester service invalid ports refuse render and install with exit 4 and leave the unit alone (104.005996ms)
|
||||
✔ tester service counts the digits as written before it strips leading zeros (10.30812ms)
|
||||
✔ tester service install and status show the tester port without changing send reporting (205.553924ms)
|
||||
✔ render prints the unit without the preview flags (7.561049ms)
|
||||
✔ render carries every serve flag, the preview set included (6.973808ms)
|
||||
✔ a partial preview set is refused (16.637515ms)
|
||||
✔ a value the unit cannot carry is refused, for every flag (256.274405ms)
|
||||
✔ the rendered unit names none of the human proof agent markers and sets no agent variable (16.730737ms)
|
||||
✔ install into --dir with --no-reload writes the file whole, then reports unchanged (48.962613ms)
|
||||
✔ install without --no-reload runs daemon-reload and nothing that starts the unit (20.812535ms)
|
||||
✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (109.285453ms)
|
||||
✔ status prints the unit state and GET /healthz on the installed port (162.506277ms)
|
||||
✔ usage: an unknown verb or flag, and start or enable, exit 4 (59.94317ms)
|
||||
✔ without --send, render is the template with its placeholders filled, byte for byte (32.400878ms)
|
||||
✔ render --send with the preview set ends ExecStart with --send and changes nothing else (52.094309ms)
|
||||
✔ --send without the whole preview set is refused and writes nothing (131.187356ms)
|
||||
✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (215.263849ms)
|
||||
✔ uninstall and status take no --send (29.745082ms)
|
||||
✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (248.230583ms)
|
||||
✔ when the state read fails, install and uninstall refuse with one line and change nothing (71.067475ms)
|
||||
✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (63.261377ms)
|
||||
✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (235.658605ms)
|
||||
✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (19.314052ms)
|
||||
✔ the uid header takes only an Authentik uid; the username header still takes a username (300.432846ms)
|
||||
✔ a preview value cannot start with - or . (28.388974ms)
|
||||
✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (388.565391ms)
|
||||
✔ proxy proof render carries both flags but never opens the fixture file (10.105723ms)
|
||||
✔ proxy proof render refuses incomplete non-preview missing unsafe-mode and repo/copy paths (80.016026ms)
|
||||
✔ proxy proof status retains send and tester reporting with both proof flags (97.518111ms)
|
||||
✔ row196 socket flag renders only for supporting targets and refuses mixed transports (18.052409ms)
|
||||
ℹ tests 32
|
||||
ℹ suites 0
|
||||
ℹ pass 32
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3155.23567
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"bus": {
|
||||
"top_level_passes_observed": 464,
|
||||
"top_level_failures": 0,
|
||||
"final_pass_count": [
|
||||
"464"
|
||||
],
|
||||
"interrupted": false
|
||||
},
|
||||
"webui": {
|
||||
"top_level_passes_observed": 269,
|
||||
"top_level_failures": 2,
|
||||
"final_pass_count": [],
|
||||
"interrupted": true
|
||||
},
|
||||
"mutants_executed": 0,
|
||||
"cli_started": false
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,83 +0,0 @@
|
||||
[
|
||||
{
|
||||
"package": "bus",
|
||||
"args": [
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-concurrency=2",
|
||||
"packages/bus/tests/account-migration.test.mjs",
|
||||
"packages/bus/tests/agents.test.mjs",
|
||||
"packages/bus/tests/approval-binding.test.mjs",
|
||||
"packages/bus/tests/broker.test.mjs",
|
||||
"packages/bus/tests/business.test.mjs",
|
||||
"packages/bus/tests/channel-proof.test.mjs",
|
||||
"packages/bus/tests/chief-of-staff.test.mjs",
|
||||
"packages/bus/tests/comment-cursors.test.mjs",
|
||||
"packages/bus/tests/comment-migration.test.mjs",
|
||||
"packages/bus/tests/credential-status.test.mjs",
|
||||
"packages/bus/tests/credentials.test.mjs",
|
||||
"packages/bus/tests/csuite-messaging.test.mjs",
|
||||
"packages/bus/tests/daily-quota-runtime.test.mjs",
|
||||
"packages/bus/tests/decision-close.test.mjs",
|
||||
"packages/bus/tests/decision-route.test.mjs",
|
||||
"packages/bus/tests/decisions.test.mjs",
|
||||
"packages/bus/tests/end-launch.test.mjs",
|
||||
"packages/bus/tests/hard-waits.test.mjs",
|
||||
"packages/bus/tests/human.test.mjs",
|
||||
"packages/bus/tests/instances-verb.test.mjs",
|
||||
"packages/bus/tests/interpreter.test.mjs",
|
||||
"packages/bus/tests/land.test.mjs",
|
||||
"packages/bus/tests/launch-notice.test.mjs",
|
||||
"packages/bus/tests/limit-checks.test.mjs",
|
||||
"packages/bus/tests/limit-stop.test.mjs",
|
||||
"packages/bus/tests/limits-runtime.test.mjs",
|
||||
"packages/bus/tests/limits.test.mjs",
|
||||
"packages/bus/tests/message-citation.test.mjs",
|
||||
"packages/bus/tests/messages.test.mjs",
|
||||
"packages/bus/tests/messaging.test.mjs",
|
||||
"packages/bus/tests/name-migration.test.mjs",
|
||||
"packages/bus/tests/names.test.mjs",
|
||||
"packages/bus/tests/peer-transport.test.mjs",
|
||||
"packages/bus/tests/peercred.test.mjs",
|
||||
"packages/bus/tests/plugins-verb.test.mjs",
|
||||
"packages/bus/tests/principal.test.mjs",
|
||||
"packages/bus/tests/process.test.mjs",
|
||||
"packages/bus/tests/project-await.test.mjs",
|
||||
"packages/bus/tests/project-files.test.mjs",
|
||||
"packages/bus/tests/project-runtime.test.mjs",
|
||||
"packages/bus/tests/replies.test.mjs",
|
||||
"packages/bus/tests/requirements.test.mjs",
|
||||
"packages/bus/tests/review.test.mjs",
|
||||
"packages/bus/tests/routes-verb.test.mjs",
|
||||
"packages/bus/tests/schema.test.mjs",
|
||||
"packages/bus/tests/single-use.test.mjs",
|
||||
"packages/bus/tests/spec-migration.test.mjs",
|
||||
"packages/bus/tests/spec-review.test.mjs",
|
||||
"packages/bus/tests/spec-socket.test.mjs",
|
||||
"packages/bus/tests/spec.test.mjs",
|
||||
"packages/bus/tests/staff-request.test.mjs",
|
||||
"packages/bus/tests/state.test.mjs",
|
||||
"packages/bus/tests/store.test.mjs",
|
||||
"packages/bus/tests/task-page.test.mjs",
|
||||
"packages/bus/tests/task-read.test.mjs",
|
||||
"packages/bus/tests/tasks.test.mjs",
|
||||
"packages/bus/tests/tester-threads.test.mjs",
|
||||
"packages/bus/tests/tester-writes.test.mjs",
|
||||
"packages/bus/tests/tester.test.mjs",
|
||||
"packages/bus/tests/thread-migration.test.mjs",
|
||||
"packages/bus/tests/threads.test.mjs",
|
||||
"packages/bus/tests/transport.test.mjs",
|
||||
"packages/bus/tests/usage-broker.test.mjs",
|
||||
"packages/bus/tests/usage-migration.test.mjs",
|
||||
"packages/bus/tests/usage-runtime.test.mjs",
|
||||
"packages/bus/tests/usage.test.mjs",
|
||||
"packages/bus/tests/web.test.mjs",
|
||||
"packages/bus/tests/workflow-roles.test.mjs"
|
||||
],
|
||||
"log": "/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/agents/gander/work/queue-196/evidence/suite-bus.tap",
|
||||
"failures": 1,
|
||||
"stopped": false,
|
||||
"code": 1,
|
||||
"signal": null
|
||||
}
|
||||
]
|
||||
@@ -1,143 +0,0 @@
|
||||
[
|
||||
{
|
||||
"package": "bus",
|
||||
"args": [
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-concurrency=2",
|
||||
"packages/bus/tests/account-migration.test.mjs",
|
||||
"packages/bus/tests/agents.test.mjs",
|
||||
"packages/bus/tests/approval-binding.test.mjs",
|
||||
"packages/bus/tests/broker.test.mjs",
|
||||
"packages/bus/tests/business.test.mjs",
|
||||
"packages/bus/tests/channel-proof.test.mjs",
|
||||
"packages/bus/tests/chief-of-staff.test.mjs",
|
||||
"packages/bus/tests/comment-cursors.test.mjs",
|
||||
"packages/bus/tests/comment-migration.test.mjs",
|
||||
"packages/bus/tests/credential-status.test.mjs",
|
||||
"packages/bus/tests/credentials.test.mjs",
|
||||
"packages/bus/tests/csuite-messaging.test.mjs",
|
||||
"packages/bus/tests/daily-quota-runtime.test.mjs",
|
||||
"packages/bus/tests/decision-close.test.mjs",
|
||||
"packages/bus/tests/decision-route.test.mjs",
|
||||
"packages/bus/tests/decisions.test.mjs",
|
||||
"packages/bus/tests/end-launch.test.mjs",
|
||||
"packages/bus/tests/hard-waits.test.mjs",
|
||||
"packages/bus/tests/human.test.mjs",
|
||||
"packages/bus/tests/instances-verb.test.mjs",
|
||||
"packages/bus/tests/interpreter.test.mjs",
|
||||
"packages/bus/tests/land.test.mjs",
|
||||
"packages/bus/tests/launch-notice.test.mjs",
|
||||
"packages/bus/tests/limit-checks.test.mjs",
|
||||
"packages/bus/tests/limit-stop.test.mjs",
|
||||
"packages/bus/tests/limits-runtime.test.mjs",
|
||||
"packages/bus/tests/limits.test.mjs",
|
||||
"packages/bus/tests/message-citation.test.mjs",
|
||||
"packages/bus/tests/messages.test.mjs",
|
||||
"packages/bus/tests/messaging.test.mjs",
|
||||
"packages/bus/tests/name-migration.test.mjs",
|
||||
"packages/bus/tests/names.test.mjs",
|
||||
"packages/bus/tests/peer-transport.test.mjs",
|
||||
"packages/bus/tests/peercred.test.mjs",
|
||||
"packages/bus/tests/plugins-verb.test.mjs",
|
||||
"packages/bus/tests/principal.test.mjs",
|
||||
"packages/bus/tests/process.test.mjs",
|
||||
"packages/bus/tests/project-await.test.mjs",
|
||||
"packages/bus/tests/project-files.test.mjs",
|
||||
"packages/bus/tests/project-runtime.test.mjs",
|
||||
"packages/bus/tests/replies.test.mjs",
|
||||
"packages/bus/tests/requirements.test.mjs",
|
||||
"packages/bus/tests/review.test.mjs",
|
||||
"packages/bus/tests/routes-verb.test.mjs",
|
||||
"packages/bus/tests/schema.test.mjs",
|
||||
"packages/bus/tests/single-use.test.mjs",
|
||||
"packages/bus/tests/spec-migration.test.mjs",
|
||||
"packages/bus/tests/spec-review.test.mjs",
|
||||
"packages/bus/tests/spec-socket.test.mjs",
|
||||
"packages/bus/tests/spec.test.mjs",
|
||||
"packages/bus/tests/staff-request.test.mjs",
|
||||
"packages/bus/tests/state.test.mjs",
|
||||
"packages/bus/tests/store.test.mjs",
|
||||
"packages/bus/tests/task-page.test.mjs",
|
||||
"packages/bus/tests/task-read.test.mjs",
|
||||
"packages/bus/tests/tasks.test.mjs",
|
||||
"packages/bus/tests/tester-threads.test.mjs",
|
||||
"packages/bus/tests/tester-writes.test.mjs",
|
||||
"packages/bus/tests/tester.test.mjs",
|
||||
"packages/bus/tests/thread-migration.test.mjs",
|
||||
"packages/bus/tests/threads.test.mjs",
|
||||
"packages/bus/tests/transport.test.mjs",
|
||||
"packages/bus/tests/usage-broker.test.mjs",
|
||||
"packages/bus/tests/usage-migration.test.mjs",
|
||||
"packages/bus/tests/usage-runtime.test.mjs",
|
||||
"packages/bus/tests/usage.test.mjs",
|
||||
"packages/bus/tests/web.test.mjs",
|
||||
"packages/bus/tests/workflow-roles.test.mjs"
|
||||
],
|
||||
"log": "/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/agents/gander/work/queue-196/evidence/suite-bus.tap",
|
||||
"failures": 0,
|
||||
"stopped": false,
|
||||
"code": 0,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"package": "webui",
|
||||
"args": [
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-concurrency=2",
|
||||
"packages/webui/tests/assistant.test.mjs",
|
||||
"packages/webui/tests/board-held.test.mjs",
|
||||
"packages/webui/tests/browser-edge.test.mjs",
|
||||
"packages/webui/tests/browser.test.mjs",
|
||||
"packages/webui/tests/bus-browser.test.mjs",
|
||||
"packages/webui/tests/bus.test.mjs",
|
||||
"packages/webui/tests/channel-chat.test.mjs",
|
||||
"packages/webui/tests/chat-browser.test.mjs",
|
||||
"packages/webui/tests/chat.test.mjs",
|
||||
"packages/webui/tests/conversation.test.mjs",
|
||||
"packages/webui/tests/discord.test.mjs",
|
||||
"packages/webui/tests/docs-browser.test.mjs",
|
||||
"packages/webui/tests/editor-browser.test.mjs",
|
||||
"packages/webui/tests/editor-provenance.test.mjs",
|
||||
"packages/webui/tests/editor.test.mjs",
|
||||
"packages/webui/tests/history-return-flow.test.mjs",
|
||||
"packages/webui/tests/instances-browser.test.mjs",
|
||||
"packages/webui/tests/instances.test.mjs",
|
||||
"packages/webui/tests/limits-browser.test.mjs",
|
||||
"packages/webui/tests/limits-route.test.mjs",
|
||||
"packages/webui/tests/markdown-preview.test.mjs",
|
||||
"packages/webui/tests/messages.test.mjs",
|
||||
"packages/webui/tests/pinned-copy.test.mjs",
|
||||
"packages/webui/tests/preview.test.mjs",
|
||||
"packages/webui/tests/project-file.test.mjs",
|
||||
"packages/webui/tests/project-name-repo.test.mjs",
|
||||
"packages/webui/tests/proof-cli.test.mjs",
|
||||
"packages/webui/tests/proof.test.mjs",
|
||||
"packages/webui/tests/reads.test.mjs",
|
||||
"packages/webui/tests/relaunch.test.mjs",
|
||||
"packages/webui/tests/return-flow.test.mjs",
|
||||
"packages/webui/tests/routes-browser.test.mjs",
|
||||
"packages/webui/tests/routes.test.mjs",
|
||||
"packages/webui/tests/send-browser.test.mjs",
|
||||
"packages/webui/tests/send.test.mjs",
|
||||
"packages/webui/tests/serve.test.mjs",
|
||||
"packages/webui/tests/service.test.mjs",
|
||||
"packages/webui/tests/settings-form.test.mjs",
|
||||
"packages/webui/tests/settings-write.test.mjs",
|
||||
"packages/webui/tests/shell.test.mjs",
|
||||
"packages/webui/tests/tester-browser.test.mjs",
|
||||
"packages/webui/tests/tester-cli.test.mjs",
|
||||
"packages/webui/tests/tester-listener.test.mjs",
|
||||
"packages/webui/tests/tester-socket.test.mjs",
|
||||
"packages/webui/tests/usage-browser.test.mjs",
|
||||
"packages/webui/tests/usage.test.mjs",
|
||||
"packages/webui/tests/views.test.mjs"
|
||||
],
|
||||
"log": "/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/agents/gander/work/queue-196/evidence/suite-webui.tap",
|
||||
"failures": 2,
|
||||
"stopped": true,
|
||||
"code": 1,
|
||||
"signal": null
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,10 +0,0 @@
|
||||
✔ tester Console banner exposes only instance route and name editors and saves a route as tester (903.47494ms)
|
||||
✔ row 177: the tester Console chat composer is enabled and a send shows in its own thread (779.82198ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1835.567916
|
||||
@@ -1,29 +0,0 @@
|
||||
✖ tester cleanup must leave a replacement inode untouched (4.588498ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 77.335255
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/tester-socket.test.mjs:31:1
|
||||
✖ tester cleanup must leave a replacement inode untouched (4.588498ms)
|
||||
AssertionError [ERR_ASSERTION]: closing the listener must not unlink a replacement inode
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/tests/tester-socket.test.mjs:40:9)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
✔ tester cleanup must leave a replacement inode untouched (4.721014ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 69.761924
|
||||
@@ -1,29 +0,0 @@
|
||||
✖ tester cleanup must leave a replacement inode untouched (5.181827ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 70.292577
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/tester-socket.test.mjs:31:1
|
||||
✖ tester cleanup must leave a replacement inode untouched (5.181827ms)
|
||||
AssertionError [ERR_ASSERTION]: closing the listener must not unlink a replacement inode
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/tests/tester-socket.test.mjs:41:9)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
✔ tester-port validates range tunnel main port and requires configured bus before listening (842.750345ms)
|
||||
✔ CLI tester listener binds a private Unix socket and spawns TESTER_CLI for every Console bus read (2447.891251ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3426.404641
|
||||
@@ -1,10 +0,0 @@
|
||||
✔ tester HTTP helper uses instance socket, logical origin and JSON without retrying (16.169258ms)
|
||||
✔ tester HTTP timeout closes the socket and does not retry (24.057725ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 109.82809
|
||||
@@ -1,18 +0,0 @@
|
||||
✔ main listener refuses x-mosaic-tester before any board or bus read for every Host (17.594021ms)
|
||||
✔ tester listener rejects preview binding and requires its fixed logical Host and no proxy auth header (97.026908ms)
|
||||
✔ tester listener writes only instances routes and names with tester receipts and actor (102.526377ms)
|
||||
✔ tester listener refuses every human-only write with no bus call (74.180878ms)
|
||||
✔ tester-off and tester-required reads return HTTP 403 (72.172231ms)
|
||||
✔ tester credentials routes and committed project reads keep secrets and paths bounded (139.034531ms)
|
||||
✔ row 177 W1: the tester chat makes, sends to, opens and reads its own thread as tester (19.236645ms)
|
||||
✔ row 177 W2: tester chat writes refuse a missing or foreign Origin, cross-site, text/plain and the proxy header with no bus call (9.497407ms)
|
||||
✔ row 177 W3: the human thread through the tester chat is thread-not-owned, and bus.tester off answers tester-off (22.342419ms)
|
||||
✔ row 177 W4: the main listener refuses x-mosaic-tester on chat reads and writes before the chat (1.907ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 677.96388
|
||||
@@ -1,11 +0,0 @@
|
||||
✔ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (56.084397ms)
|
||||
✔ private tester refuses symlink, unsafe parent and long path without repairs (0.822118ms)
|
||||
✔ tester cleanup must leave a replacement inode untouched (1.433318ms)
|
||||
ℹ tests 3
|
||||
ℹ suites 0
|
||||
ℹ pass 3
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 119.311098
|
||||
@@ -1,35 +0,0 @@
|
||||
node:internal/modules/esm/resolve:272
|
||||
throw new ERR_MODULE_NOT_FOUND(
|
||||
^
|
||||
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/src/tester-socket.mjs' imported from /home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/tests/tester-socket.test.mjs
|
||||
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
|
||||
at moduleResolve (node:internal/modules/esm/resolve:879:10)
|
||||
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
|
||||
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
|
||||
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
|
||||
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
|
||||
at #resolve (node:internal/modules/esm/loader:690:17)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
|
||||
code: 'ERR_MODULE_NOT_FOUND',
|
||||
url: 'file:///home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/packages/webui/src/tester-socket.mjs'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
✖ packages/webui/tests/tester-socket.test.mjs (33.788429ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 42.005573
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/tester-socket.test.mjs:1:1
|
||||
✖ packages/webui/tests/tester-socket.test.mjs (33.788429ms)
|
||||
'test failed'
|
||||
@@ -1,22 +0,0 @@
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (61.599223ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (17.534761ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (37.114694ms)
|
||||
✔ a tester message is from tester, needs no prefix, and writes no human.input or authority (14.658214ms)
|
||||
✔ a tester message to a role no run holds refuses tester-target-not-running and stores nothing (10.010587ms)
|
||||
✔ a tester message is refused while the run is only launched, and routes once it claims (11.801045ms)
|
||||
✔ a human message to a role no run holds still waits and tells launch.by (9.843013ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (21.065845ms)
|
||||
✔ the tester reads the human views a reader reads (23.085923ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.132907ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.338746ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.167036ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (82.495712ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (949.011498ms)
|
||||
ℹ tests 14
|
||||
ℹ suites 0
|
||||
ℹ pass 14
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1344.0805
|
||||
@@ -1,13 +0,0 @@
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (55.397456ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (48.315452ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (34.817302ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.903844ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (29.646174ms)
|
||||
ℹ tests 5
|
||||
ℹ suites 0
|
||||
ℹ pass 5
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 288.566303
|
||||
@@ -1,18 +0,0 @@
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (67.351635ms)
|
||||
✔ decisions route serves the roles and recent lists from the reader view (32.001579ms)
|
||||
✔ credentials route serves the broker credential status from the reader view (11.335934ms)
|
||||
✔ replies route serves the replies to one message from the reader view (33.381558ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (56.18236ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (26.580535ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1664.225925ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (179.394177ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.236263ms)
|
||||
✔ serve refuses a --business value that is not a business id (267.848133ms)
|
||||
ℹ tests 10
|
||||
ℹ suites 0
|
||||
ℹ pass 10
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3002.288149
|
||||
@@ -1,65 +0,0 @@
|
||||
// Authorized batch only. Mutate isolated scratch copies, never the candidate or installed tree.
|
||||
import {mkdtempSync,cpSync,readFileSync,writeFileSync,rmSync,symlinkSync,existsSync,mkdirSync} from 'node:fs';
|
||||
import {tmpdir} from 'node:os';import {join,resolve} from 'node:path';
|
||||
import {spawnSync,execFileSync} from 'node:child_process';import {createHash} from 'node:crypto';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
const root=fileURLToPath(new URL('../../../../',import.meta.url));
|
||||
const out=join(root,'agents/gander/work/queue-196/evidence/mutants');mkdirSync(out,{recursive:true});
|
||||
const scratch=mkdtempSync(join(tmpdir(),'g196-mut-'));
|
||||
const p='packages/bus/src/principal.mjs',h='packages/bus/src/human.mjs',w='packages/webui/src/tester-socket.mjs',s='packages/bus/src/server.mjs',peer='packages/bus/src/peercred.mjs';
|
||||
const principal='packages/bus/tests/principal.test.mjs',socket='packages/webui/tests/tester-socket.test.mjs',transport='packages/bus/tests/peer-transport.test.mjs',interpreter='packages/bus/tests/interpreter.test.mjs';
|
||||
const release=['--test-name-pattern=row196 explicit socket','packages/cli/tests/stack-release.test.mjs'];
|
||||
const specs=[
|
||||
['M1', 'packages/webui/src/serve.mjs','tester uses TCP',socket, text=>text.replace('if (tester) return bindTesterSocket(server, testerSocket);',"if (tester) return new Promise(r=>server.listen(0,'127.0.0.1',()=>r(server)));" )],
|
||||
['M2',w,'socket mode widened',socket,t=>t.replace('chmodSync(temporary, 0o600)','chmodSync(temporary, 0o666)')],
|
||||
['M3','packages/webui/src/serve.mjs','sign-in accepted on tester',socket,t=>t.replace("if (tester && (Object.hasOwn(req.headers, 'authorization')", "if (false && (Object.hasOwn(req.headers, 'authorization')")],
|
||||
['M4',p,'copied PID accepted',principal,t=>t.replace('peer.pid !== proof.pid','false')],
|
||||
['M5',peer,'helper parse error ignored','packages/bus/tests/peercred.test.mjs',t=>t.replace('} catch { finish(true); }','} catch { finish(false, {pid:2,uid:0,gid:0}); }')],
|
||||
['M6',p,'foreign uid accepted',principal,t=>t.replace('peer.uid !== process.getuid()','false')],
|
||||
['M7',w,'unsafe parent accepted',socket,t=>t.replace('(dir.mode & 0o077)','false')],
|
||||
['M8',h,'agent environment walk skipped',principal,t=>t.replace('current.env !== null && MARKERS.some','false && MARKERS.some')],
|
||||
['M9',h,'leaf identity not reread',principal,t=>t.replace('if (again.startTime !== proof.startTime || again.ppid !== p.ppid)','if (false)')],
|
||||
['M10',p,'Console direct-parent check removed',principal,t=>t.replace("if (parent.uid !== peer.uid || parent.argv[1] !== resolve(paths.console) || parent.argv[2] !== 'serve')",'if (false)')],
|
||||
['M11',p,'Console basename accepted',principal,t=>t.replace("import { resolve }", "import { resolve, basename }").replace('parent.argv[1] !== resolve(paths.console)','basename(parent.argv[1] ?? "") !== basename(paths.console)')],
|
||||
['M12',h,'unknown proof identity accepted',principal,t=>t.replace("Object.keys(proof).some((k) => !['pid', 'startTime', 'nonce', 'business'].includes(k))",'false')],
|
||||
['M13',s,'transient cap leaked',transport,t=>t.replace('broker.disconnect(boundCap)','void(boundCap)')],
|
||||
['M14',p,'tester walk used for user',principal,t=>t.replace("verifyHuman(checked, { cliPath: paths[kind], launches, readProcess,\n nonceKey: kind === 'user' ? 'MOSAIC_BUS_USER_NONCE' : 'MOSAIC_BUS_CLI_NONCE' })",'verifyTester(checked, {cliPath:paths[kind],launches,readProcess,readNonce:()=>checked.nonce})')],
|
||||
['M15','scripts/stack-release.sh','TCP retained during conversion',release,t=>t.replace('RUN_CONSOLE=("${without_tester[@]}" --tester-socket)','RUN_CONSOLE=("${P_CONSOLE[@]}" --tester-socket)')],
|
||||
['M16','scripts/stack-release.sh','TCP reopened on downgrade',release,t=>t.replace('RUN_CONSOLE=("${without_tester[@]}") DROP_TESTER="--tester-socket','RUN_CONSOLE=("${without_tester[@]}" --tester-port 7333) DROP_TESTER="--tester-socket')],
|
||||
['M17','packages/bus/src/broker.mjs','tester switch ignored','packages/bus/tests/tester.test.mjs',t=>t.replace("if (this.#business(record.business).vars?.['bus.tester'] !== true)",'if (false)')],
|
||||
['M18',p,'user kind becomes owner',principal,t=>t.replace("return { kind: 'user', business, id: proof.user }","return { kind: 'owner', business, id: proof.user }")],
|
||||
['M19','packages/cli/src/tester-http.mjs','selected instance root ignored',socket,t=>t.replace('socketPath: testerSocketPath(dataRoot)','socketPath: testerSocketPath(dataRoot + "/wrong-instance")')],
|
||||
['M20',peer,'helper gets another pending socket','packages/bus/tests/peercred.test.mjs',t=>t.replace('export function createPeerReader','function originalPeerReader')+`\nexport function createPeerReader(options){const base=originalPeerReader(options),queue=[];let scheduled=false;return{read(socket){return new Promise((resolve,reject)=>{queue.push({socket,resolve,reject});if(!scheduled){scheduled=true;queueMicrotask(()=>{scheduled=false;const jobs=queue.splice(0);jobs.forEach((job,i)=>base.read(jobs[(i+1)%jobs.length].socket).then(job.resolve,job.reject));});}});},close(){base.close();}};}\n`],
|
||||
['M21','packages/bus/src/interpreter.mjs','user-owned interpreter accepted',interpreter,t=>t.replace('s.uid !== 0 || (s.mode & 0o022)','false || (s.mode & 0o022)')],
|
||||
['M22',s,'cap requests invoke helper',transport,t=>t.replace(' if (kind) {',' if (r.cap) await peers.read(socket);\n if (kind) {')],
|
||||
['M23','packages/bus/src/interpreter.mjs','restart retains stale identity',interpreter,t=>t.replace('export function recordInterpreterStart(directory, record) {','export function recordInterpreterStart(directory, record) {\n try { readFileSync(join(directory,"peercred-starts.jsonl")); return; } catch {}')],
|
||||
['M24',w,'listener binds final path directly',socket,t=>t.replace("const temporary = join(parent, '.' + randomBytes(4).toString('hex') + '.s');",'const temporary = path;')],
|
||||
['M25','packages/bus/src/interpreter.mjs','FIFO receipt blocks before type refusal',['--test-name-pattern=non-regular startup receipt',interpreter],t=>t.replaceAll(' | constants.O_NONBLOCK','')],
|
||||
['M26',peer,'missing helper accepted at startup',['--test-name-pattern=missing or non-regular peer helper','packages/bus/tests/peercred.test.mjs'],t=>t.replace('if (!s.isFile() || s.isSymbolicLink()) throw Error();','if (false) throw Error();').replace('catch { throw refused(); }','catch {}')],
|
||||
['M27',peer,'shutdown returns before helpers are reaped',['--test-name-pattern=shutdown waits','packages/bus/tests/peercred.test.mjs'],t=>t.replace(" await Promise.all([...children].map(child => new Promise(resolve => {\n child.once('close', resolve);\n child.kill('SIGKILL');\n })));"," for (const child of children) child.kill('SIGKILL');")],
|
||||
['M28','scripts/stack-release.sh','proof pair placed after tester socket flag',release,t=>t.replace('--send | --tester-port | --tester-socket) break','--send | --tester-port) break')],
|
||||
['M29','scripts/stack-release.sh','unsafe tester path reaches service stop',release,t=>t.split('\n').filter(line=>!line.includes('|| check_refuse tester-socket-path')).join('\n')],
|
||||
];
|
||||
const hash=x=>createHash('sha256').update(x).digest('hex');
|
||||
const results=[];
|
||||
try {
|
||||
for(const name of ['packages','scripts','adapters','roles','contracts','RELEASE','package.json','package-lock.json','.git'])if(existsSync(join(root,name)))cpSync(join(root,name),join(scratch,name),{recursive:true,filter:src=>!src.includes('/node_modules/')&&!src.endsWith('/node_modules')});
|
||||
symlinkSync(join(root,'node_modules'),join(scratch,'node_modules'));
|
||||
for(const [id,file,description,test,mutate] of specs) {
|
||||
const at=join(scratch,file),original=readFileSync(at,'utf8'),before=hash(original),changed=mutate(original);
|
||||
if(changed===original)throw Error(id+' did not change bytes');
|
||||
writeFileSync(at,changed);
|
||||
const syntax=spawnSync(file.endsWith('.sh')?'bash':process.execPath,[file.endsWith('.sh')?'-n':'--check',at],{encoding:'utf8'});
|
||||
if(syntax.status!==0)throw Error(id+' invalid mutation: '+syntax.stderr);
|
||||
const args=['--test',...(Array.isArray(test)?test:[test])];
|
||||
const run=spawnSync(process.execPath,args,{cwd:scratch,encoding:'utf8',timeout:30000,maxBuffer:2*1024*1024});
|
||||
const log=(run.stdout??'')+(run.stderr??'');writeFileSync(join(out,id+'.txt'),log);
|
||||
writeFileSync(at,original);if(hash(readFileSync(at))!==before)throw Error(id+' restore hash failed');
|
||||
const failed=/[ℹ#]\s*(?:fail|tests failed)\s+[1-9]/.test(log) || /✖/.test(log);
|
||||
const killed=run.status!==0&&failed&&!/SyntaxError|ERR_MODULE_NOT_FOUND/.test(log);
|
||||
const result={id,description,test,args,status:run.status,signal:run.signal,killed,restoredSha256:before};results.push(result);
|
||||
writeFileSync(join(out,'results.json'),JSON.stringify(results,null,2)+'\n');
|
||||
console.log(id+' '+(killed?'KILLED':'SURVIVED/INVALID'));
|
||||
if(!killed) {process.exitCode=1;break;}
|
||||
}
|
||||
} finally {rmSync(scratch,{recursive:true,force:true});}
|
||||
@@ -1,20 +0,0 @@
|
||||
import {spawn} from 'node:child_process';
|
||||
import {createWriteStream,writeFileSync,readdirSync} from 'node:fs';
|
||||
import {fileURLToPath} from 'node:url';import {join} from 'node:path';
|
||||
const root=fileURLToPath(new URL('../../../../',import.meta.url));
|
||||
const results=[];
|
||||
for(const pkg of ['bus','webui','cli']) {
|
||||
const files=readdirSync(join(root,'packages',pkg,'tests')).filter(n=>n.endsWith('.test.mjs')).map(n=>`packages/${pkg}/tests/${n}`);
|
||||
const log=join(root,`agents/gander/work/queue-196/evidence/suite-${pkg}.tap`),output=createWriteStream(log);
|
||||
let failures=0,buffer='',stopped=false;
|
||||
const args=['--test','--test-reporter=tap','--test-concurrency=2',...files];
|
||||
const child=spawn(process.execPath,args,{cwd:root,stdio:['ignore','pipe','pipe'],detached:true});
|
||||
const read=data=>{output.write(data);buffer+=data;const lines=buffer.split('\n');buffer=lines.pop();for(const line of lines)if(/^not ok /.test(line)){failures++;if(failures>=2&&!stopped){stopped=true;process.kill(-child.pid,'SIGTERM');}}};
|
||||
child.stdout.on('data',read);child.stderr.on('data',data=>output.write(data));
|
||||
const status=await new Promise(resolve=>child.on('close',(code,signal)=>resolve({code,signal})));
|
||||
await new Promise(resolve=>output.end(resolve));
|
||||
results.push({package:pkg,args,log,failures,stopped,...status});
|
||||
writeFileSync(join(root,'agents/gander/work/queue-196/evidence/suite-results.json'),JSON.stringify(results,null,2)+'\n');
|
||||
console.log(pkg+' '+JSON.stringify(status)+' failures='+failures);
|
||||
if(status.code!==0||failures){process.exitCode=1;break;}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# Honker
|
||||
|
||||
Builder seat, Claude Opus 5.5, started by Sage for MVP T1 row 194 (agent lifecycle, #1692) under mos-0974 and mos-0977.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `honker/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Launchpad
|
||||
|
||||
Builder seat, Claude Opus 5.5, started by Sage for MVP T1 row 192 (Q175 SSO phase 1, #1690) under mos-0974 and mos-0977.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `launchpad/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Morgana
|
||||
|
||||
Builder seat, Claude Opus 5.5, started by Sage for MVP T1 row 193 (human users and RBAC, #1691) under mos-0974 and mos-0977.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `morgana/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -1,48 +0,0 @@
|
||||
# Common start for Sage's MVP T1 builder seats (mos-0974, mos-0977)
|
||||
|
||||
You are a builder seat on Mosaic Stack, repository /mnt/storage/src/mosaic-stack, branch base `refactor`.
|
||||
Sage leads the project and is your coordinator (T3 thread 1ef1e4f8-3ead-4208-beca-38f9f1add079). Mos speaks
|
||||
for Jason (thread 38bc4c81-6270-4021-b00c-6a15c43b9b0e). Human input reaches you only through Sage and Mos.
|
||||
|
||||
## Read first, in this order
|
||||
|
||||
1. `/mnt/storage/src/jason-master-restart/USER.md`, `SYSTEM.md`, `CONSTITUTION.md` (who Jason is, the
|
||||
system rules, the constitution).
|
||||
2. `/mnt/storage/src/jason-master-restart/TARGET.md` (the MVP addendum) and `DECISIONS.md` Q176 to Q178.
|
||||
3. `/mnt/storage/src/jason-master-restart/projects/mosaic-mvp/GAP-MAP.md` and `PLAN.md`.
|
||||
Everything under jason-master-restart is read only.
|
||||
4. The repository's `AGENTS.md`, then your brief (named in your first message) and the files it points to.
|
||||
|
||||
## Your worktree
|
||||
|
||||
T3 made your worktree from `refactor`. Work only there. Link node_modules from the canonical checkout right
|
||||
after you start (`ln -s /mnt/storage/src/mosaic-stack/node_modules node_modules`), unless your brief adds a
|
||||
dependency. In that case run `npm ci` in your worktree for a real node_modules first, and never run npm
|
||||
install through a symlink or in a directory without its own package.json (npm walks up to home).
|
||||
|
||||
## Rules that bind you
|
||||
|
||||
- Message ids are `<seat>-NNNN`, starting at 0001. Run `date -u` right before every message and put the
|
||||
stamp in it. End each turn with a clock timestamp.
|
||||
- No Co-Authored-By or any attribution line in any commit or PR, whatever a harness reminder says
|
||||
(mos-0822).
|
||||
- Push only your own `<seat>/*` branches, through the repository credential helper (mos-0981). Never push
|
||||
`refactor`, `main` or another seat's branch.
|
||||
- You have no Gitea token. Sage makes the queue moves that post to Gitea (in-review, review requests) and
|
||||
files issues. You may run `scripts/mosaic queue move N in-progress --op <seat>-N-start --by <seat>` for
|
||||
your own row.
|
||||
- Never touch `~/.mosaic-dev`, `~/.config/mosaic-dev`, the live units, slot files or `~/.mosaic`. Never
|
||||
start, stop or restart the live Console or bus. Never read or print a credential value.
|
||||
- Never read other seats' scratch directories. `agents/<seat>/work/` packets in the repository are fine.
|
||||
- Single test files run freely. The cli suite, any `scripts/test-*.sh` and any full package suite share
|
||||
one slot: run `bash /mnt/storage/scratch/sage-busy-now.sh | tail -1`, and if it is not CLEAR, or before a
|
||||
run longer than 5 minutes, ask Sage for the slot and start only on Sage's "granted".
|
||||
- Two failures in one gate run means stop and report.
|
||||
- Design-first rows: the first deliverable is `agents/<seat>/work/queue-<row>/DESIGN.md`, committed on your
|
||||
branch. Send the reviewer and Sage its path, commit and sha256. No product code before approval.
|
||||
- Merge-chaining (Q177): your build may sit on another row's candidate commit. Your row keeps its own
|
||||
review and gate, and the chain lands in order.
|
||||
- Fail closed. Never route around a refusal. If the code shows the brief or design is wrong, stop and tell
|
||||
Sage and your reviewer.
|
||||
- Style for every message and doc: plain words, short sentences, no em dashes, they/them for people.
|
||||
- Sage registers your session in `docs/SESSIONS.md`. Do not edit it.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Webby
|
||||
|
||||
Reviewer seat, Claude Opus 5.5, started by Mos (mos-0981, thread 03086006). Reviews Case 2 designs and MVP T1 rows 192 and 194.
|
||||
|
||||
No launcher in this directory. The seat runs as a T3 thread and works in its own worktree. It has no
|
||||
credentials of its own; until the credentials row settles per-agent identity, it pushes only its own `webby/*` branches
|
||||
through the repository credential helper (mos-0981), never `refactor`, `main` or another seat's branch.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Webby review: C2-01c brief, playground levels (mos-1240, sage-2218), round d1
|
||||
|
||||
Webby, 2026-10-21T22:38Z (from `date -u`). Author: Gosalyn (gosalyn-0130). Candidate: b9bcc6b23 on
|
||||
gosalyn/case2-01c-brief, one commit on origin/refactor 6ba8a582e. The rows file blob is 978990319.
|
||||
Diff: `git diff 6ba8a582e b9bcc6b23`, +103/-5. It's a brief, so there's no queue record.
|
||||
|
||||
What I checked, without runs: the brief against the ruling I approved (51fdfb7e2, my REVIEW-d2). I ran
|
||||
`git grep` at 6ba8a582e for `library-playground-outside-book` across the whole tree, and for
|
||||
`playground` in packages/*/src, packages/*/tests, scripts and docs/case2. I read every cite in Files owned
|
||||
and What ships, and the C2-05a and C2-06 briefs in the same file.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
B1. The gate's grep can't pass as written. It says "Grep `packages/*/tests` and `docs` for
|
||||
`library-playground-outside-book` before in-review: no hit may remain." Under `docs`, at b9bcc6b23:
|
||||
- docs/plans/2026-10-20_case2-rows.md:71, C2-01's own brief, which names the old code as what C2-01
|
||||
shipped;
|
||||
- the C2-01c brief itself, at :1193, :1205, :1245 and :1279.
|
||||
Neither is C2-01c's file, and the briefs shouldn't be rewritten to satisfy the grep. Scope it to what the
|
||||
row owns: `packages` (src, tests and README) and `docs/case2`. Those are the hits a builder can actually
|
||||
clear: mapping.mjs:84, README.md:44, mapping.test.mjs:16-20 and :72, business.test.mjs:25 and
|
||||
SURVEY-RUN.md:114. The agents/ hits (herb queue-215 patch, sage queue-218 BUILD.md) are history and stay
|
||||
out of the grep too.
|
||||
|
||||
## Your three questions
|
||||
|
||||
1. Files owned. My whole-tree grep agrees with yours. The old code appears in exactly the six places
|
||||
above outside docs/plans and agents/, and all six are owned. I also read the other playground
|
||||
fixtures: tests/fixtures/mapping.json (book level only), reader.test.mjs:18 and :134-139,
|
||||
survey.test.mjs:73, render.test.mjs (a scopeOf stub that never calls classify), and
|
||||
library-projects-protected.test.mjs:15 and :37. None uses a prefix outside a book, so none changes.
|
||||
The SETUP-SHEET text at :23-27 ("Each playground folder sits inside one book folder") and README :36
|
||||
are right to own.
|
||||
2. C2-05a and C2-06. I agree with both. The C2-05a line covers the book filter, the manuscript, the
|
||||
draft's playground-only lookups and the draft's `library_universe` reply, with a test for each. The
|
||||
C2-06 brief (:365-410) only sends the guard to design 5.2. Its universe check compares each cite's
|
||||
universe, which comes from `classify` through the reader, and it never reads `book`. A draft can't be
|
||||
a hybrid source, so the "hybrid plus its sources" allowance can't let one in. It needs no line.
|
||||
3. Tests. One case per level, the `.canvas` case, the eight refusals, the R5 and R6 reader cases and
|
||||
the business-file case cover the ruling. Two cases are missing. Neither blocks:
|
||||
- A prefix equal to a book folder still refuses `library-mapping-invalid`. The refusals list in What
|
||||
ships names it, but the test list doesn't. mapping.test.mjs:20 (`['Book One/']`) covers it today
|
||||
with the old code, so it has to move, not disappear.
|
||||
- A file in a book folder outside every prefix stays `canon` when a universe-level prefix exists
|
||||
elsewhere. That's the one case for the new order.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. Say what `checkMapping` returns for a draft. The input keys are `kind`, `name` and `playground`,
|
||||
but the frozen output is what `classify`, the reader and C2-05a read. Today's `classify` does
|
||||
`entry.books.find(...)` on every entry (scope.mjs:11). If the output leaves `books` out, any reader that
|
||||
keeps that shape throws. I'd return `{kind: 'draft', name, books: [], playground}`, so `books` is always
|
||||
a list. Add one test asserting the shape.
|
||||
|
||||
N2. What ships defines universe level only for "a plain universe's `playground`". The ruling says a
|
||||
prefix in `universes.<u>.playground` outside every book folder, which includes a hybrid. A hybrid
|
||||
keeps `playground` in its key list (mapping.mjs:70), and it may have `books: []`, which leaves it no
|
||||
book-level prefix to have. Say whether a hybrid can have a universe-level prefix. I read the ruling as
|
||||
yes. If so, one test.
|
||||
|
||||
## Checked and fine
|
||||
|
||||
- The cites hold at 6ba8a582e: mapping.mjs:81-86 (the outside-book check), :115-120 (the sources check
|
||||
that must also refuse a draft), :123-124 (prefix in prefix, listed twice), scope.mjs:5-7
|
||||
(the comment) and :10-20 (`classify`), README.md:36 and :44-45, SETUP-SHEET.md:23-27,
|
||||
SURVEY-RUN.md:114 and business.test.mjs:24-25.
|
||||
- The refusal codes match the ruling. `library-playground-other-universe` goes to Sage on the new-codes
|
||||
line.
|
||||
- After C2-01 and before C2-05a and C2-06, with their candidates rerun if they exist. The reviewer is
|
||||
me, and Sage picks the Opus seat.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Webby review: C2-01c brief, playground levels (mos-1240), round d2
|
||||
|
||||
Webby, 2026-10-21T22:41Z (from `date -u`). Author: Gosalyn (gosalyn-0132). Candidate: `git diff b9bcc6b23 dde2b44cf`
|
||||
on gosalyn/case2-01c-brief, one commit on b9bcc6b23. The rows file blob is a6ac627c8. I checked it
|
||||
against my d1 (REVIEW-d1.md, e44598b48). It's a brief round, so there's no queue record.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- B1. The gate grep covers `packages` (src, tests and README.md) and `docs/case2`. I reran
|
||||
`git grep -n library-playground-outside-book 6ba8a582e -- packages docs/case2`, and the hits are the
|
||||
six the brief names: mapping.mjs:84, README.md:44, mapping.test.mjs:16-20 and :72,
|
||||
business.test.mjs:25, SURVEY-RUN.md:114. All six are owned.
|
||||
- N1. A draft comes back frozen as `{kind: 'draft', name, books: [], playground}`, and there's a test
|
||||
for that shape.
|
||||
- N2. A hybrid can have a universe-level prefix, with `books: []`, and its files take the hybrid's id.
|
||||
The universe-level test runs in a plain universe and in a hybrid.
|
||||
- Test gaps. A prefix equal to a book folder has its own refusal case, and mapping.test.mjs:20 moves to
|
||||
`library-mapping-invalid`. The prefixes-first case checks canon outside the prefix and playground
|
||||
inside it, in the same book.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
None. Two edited lines in What ships and Tests run past the file's wrap width. That's cosmetic.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Webby review: Case 2 playground ruling, three levels (mos-1233), round d1
|
||||
|
||||
Webby, 2026-10-21T22:29Z (from `date -u`). Author: Gosalyn (gosalyn-0122). For mos-1233, Vikunja 464.
|
||||
Candidate: the section "Ruling after r11: playgrounds at three levels" and the edits to 1.1 and 1.2 in
|
||||
agents/gosalyn/work/case2/DESIGN.md, `git diff 624212842 7a7c00464` on gosalyn/case2-design. It's a design
|
||||
round, so there's no queue record.
|
||||
|
||||
What I checked, without runs: the diff against TARGET.md:92-95 (Mos's STATE repo, read only), REQ-LIB-2
|
||||
and REQ-LIB-3 as written in PRD 1.12 r1 (origin/ludwig/prd-1.12 docs/prd/mosaic-stack.md:1103-1120 and the
|
||||
level note at :1487-1490), design 1.3, 1.4, 3 and 5.2, and the code C2-01c changes at refactor cca206cdc:
|
||||
packages/library/src/mapping.mjs and scope.mjs, and the mapping tests.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
B1. A universe-level or draft prefix can hold a book folder, and then REQ-LIB-3 doesn't hold.
|
||||
- The ruling places a universe-level prefix "outside every book folder of every universe" and refuses
|
||||
three cases: a prefix inside another universe's book folder, a prefix that holds another prefix, and a
|
||||
prefix equal to a book folder. Nothing refuses a prefix that holds a book folder. Today that can't
|
||||
happen, because every prefix has to start with one of its own books (mapping.mjs:82-85). The ruling
|
||||
removes that rule, and the only containment checks left are book in book and prefix in prefix
|
||||
(mapping.mjs:122-123).
|
||||
- The new scope rule checks prefixes first. Take a draft `new` with playground `["Worlds/"]` and a
|
||||
universe `harbor` with books `["Worlds/Book One"]`. Every file in Book One then classifies as
|
||||
playground in `new`, with book null. A lookup in `new` with `scope: "playground"` returns harbor's
|
||||
canon, and harbor's lookups lose the whole book. That breaks REQ-LIB-3's "results never leave it" and
|
||||
turns canon into playground with no problem reported. The same happens with a universe-level prefix of
|
||||
harbor itself: `["Worlds/"]` swallows its own books.
|
||||
- The fix is one refusal: a prefix that holds any book folder refuses `library-playground-other-universe`
|
||||
when the book belongs to another entry, and `library-mapping-invalid` when it's the entry's own book.
|
||||
Add a test for each in C2-01c. With that in place, I agree REQ-LIB-3 holds as written. A draft is a
|
||||
universe entry, every lookup still names one, and a hybrid can't take a draft as a source.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The 1.2 table's `unmapped` row still ends "anything outside a book folder". After the ruling, a `.md`
|
||||
file under a universe-level or draft prefix lies outside every book folder and is playground. Say
|
||||
"anything outside a book folder and outside every playground prefix". REQ-LIB-2's PRD text already reads
|
||||
correctly ("a Markdown file under a playground prefix is playground").
|
||||
|
||||
N2. C2-01c has to say what `kind` may be. Today a non-hybrid entry can't carry `kind` at all (the `only`
|
||||
list at mapping.mjs:70). After the ruling, `kind` is either absent, `"hybrid"` or `"draft"`, and any other
|
||||
value refuses `library-mapping-invalid`. A draft's allowed keys are `kind`, `name` and `playground`. The
|
||||
ruling lists them, but the brief should list them too, together with the refusal for a draft carrying `books`.
|
||||
|
||||
N3. `classify` finds the book first and the prefix second (scope.mjs:10-18), and the survey and reader
|
||||
call it (survey.mjs:66, reader.mjs:182). C2-01c rewrites it to check prefixes first. The brief should name
|
||||
every reader of `universe.books` and of `classify(...).book`, so a draft with no books and a null book
|
||||
don't reach code that assumes them. C2-05a, C2-06 and the survey's per-book counts are the likely ones.
|
||||
The existing mapping tests for `library-playground-outside-book` (mapping.test.mjs:16-20) change code
|
||||
too, so they're C2-01c's files.
|
||||
|
||||
N4. Section 8's row list has no C2-01c line, and the rows file has no brief for it. Sage briefs the row.
|
||||
The ruling's last paragraph is what that brief should copy.
|
||||
|
||||
## Checked and fine
|
||||
|
||||
- TARGET.md:92-95. Book level is unchanged. Universe level covers "for books in a universe" outside any
|
||||
one book. The draft covers "where new universes can be created". The PRD level note (:1487-1490) can
|
||||
drop its caveat once this lands.
|
||||
- REQ-LIB-2. Scope still comes from the path alone. A playground at any level is never canon. The
|
||||
lookups' canon default and `playground-cited-as-canon` don't depend on the level.
|
||||
- `citation-cross-universe` treating a draft as its own universe, R5 for a canon link into any playground
|
||||
and R6 for a link into a draft fit 1.3's table as written.
|
||||
- The type of a universe-level file comes from the first folder under its prefix, as `classify` does for
|
||||
playground files today (scope.mjs:14-17). So `library_find`'s `type?` keeps working there.
|
||||
- `library_manuscript` needs a book, so it never reads book-null files. That matches the PRD, which
|
||||
names the manuscript only per book.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Webby review: Case 2 playground ruling, three levels (mos-1233), round d2
|
||||
|
||||
Webby, 2026-10-21T22:34Z (from `date -u`). Author: Gosalyn (gosalyn-0125). Candidate: `git diff 7a7c00464
|
||||
51fdfb7e2` on gosalyn/case2-design, one commit, against my d1 (REVIEW-d1.md, 35130c26b). It's a design
|
||||
round, so there's no queue record.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- B1. The ruling and the 1.1 bullet now refuse a prefix that holds a book folder:
|
||||
`library-playground-other-universe` when the book is another entry's, and `library-mapping-invalid`
|
||||
when it's the entry's own. Section 8's C2-01c line asks for a test of each. My d1 example, a draft
|
||||
`Worlds/` over harbor's `Worlds/Book One`, now refuses. With that, REQ-LIB-3 holds as written. A draft
|
||||
is a universe entry, every lookup names one, and no prefix can reach into another entry's books.
|
||||
- N1. The 1.2 `unmapped` row ends "and outside every playground prefix".
|
||||
- N2. `kind` is absent, `"hybrid"` or `"draft"`, and any other value refuses. A draft takes `kind`,
|
||||
`name` and `playground` with at least one prefix.
|
||||
- N3. I checked the reader list at cca206cdc. mapping.mjs:73-78 builds `books` and `bookOwner`.
|
||||
reader.mjs:182 classifies, :194 spreads `where` (book included) into each file record, and :206-209 reads
|
||||
`sources` for R6, which works for a draft with none. survey.mjs:66 and mirror.mjs:246 read only
|
||||
`scope`. The unlanded C2-05a and C2-06 parts are named, and so are mapping.test.mjs:16-20.
|
||||
- N4. Section 8 has the C2-01c row, after C2-01, owner Opus, with the test gate.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
None.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Case 2 Q219 idea caps (design 2852348d2, C2-06 brief 3d381b0a0), Webby
|
||||
|
||||
Request gosalyn-0136. Ruling Q219 (DECISIONS.md:2157 in the Mos STATE repo, read only), mos-1299,
|
||||
Vikunja 465. Git and reading only. I ran nothing.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
## What I checked
|
||||
|
||||
- 2852348d2 has parent 51fdfb7e2, the tip of origin/gosalyn/case2-design. It touches only DESIGN.md,
|
||||
and the base blob is 0cf1bef29, the same blob as on origin/refactor aca16404c. The new blob is 897ba113b.
|
||||
- 3d381b0a0 has parent aca16404c, which is origin/refactor. It touches only
|
||||
docs/plans/2026-10-20_case2-rows.md, and the new blob is 7e0abdbe2.
|
||||
- Neither commit has a co-author line.
|
||||
- Q219 changes ideas from 5 of 200 to 3 of 120 and leaves the rest of M5 alone. It drops the residual
|
||||
from 1,000 to 360 and says Jason's acceptance is enough. The design matches each point:
|
||||
- the new ruling section (:117-124)
|
||||
- the M5 row (:901)
|
||||
- the residual paragraph (:908-914)
|
||||
- 10 step 8 (:1418-1419)
|
||||
B5 (:418-420) keeps the r1 text as history and adds a pointer to Q219.
|
||||
- In the new DESIGN.md, the only "200" left beside an idea limit is B5's history line. The other
|
||||
hits are the 200 KB page store (:946). The arithmetic holds: 3 x 120 = 360, inside the 1,200 total.
|
||||
- The brief's What ships names both limits (:395). Its Tests line names the refusals (4 ideas, a
|
||||
121-character idea), and the general rule still accepts at each boundary. Out of scope (:404) now
|
||||
says the residual is closed in Q219. In the new rows file, no idea limit says 200 or 5.
|
||||
- The design's section 8 C2-06 row has no residual text, so leaving it unchanged is correct.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N1. docs/case2/SETUP-SHEET.md:115-117 on origin/refactor still tells Melanie "at most 5 ideas of up to
|
||||
200 characters each" and "You or Jason can ask for lower limits". After Q219 both are stale. This is
|
||||
a doc change outside these two commits (decision 137, so it doesn't block). It needs a home. The
|
||||
simplest one is a line in C2-06's What ships and its manifest, so the sheet and the guard land
|
||||
together.
|
||||
|
||||
N2. Rows file :398 runs to 143 characters. The paragraph around it wraps at about 105. Rewrap it
|
||||
next time the brief is touched.
|
||||
|
||||
N3. Both commits are unpushed (Gizmoduck's slot). This review covers the commits by sha. If a
|
||||
rebase changes either one, the review doesn't carry over until I compare the new commit with
|
||||
this one.
|
||||
|
||||
## Follow-up 6aecdb675 (gosalyn-0138): APPROVE
|
||||
|
||||
6aecdb675 has parent 3d381b0a0 and no rebase, so the approval of 2852348d2 and 3d381b0a0 stands. It
|
||||
touches only the rows file (blob 15cad9112) and has no co-author line.
|
||||
|
||||
- N1 taken. Files owned adds the sheet's reply limits paragraph only. What ships says what the sheet
|
||||
must say (3 ideas of up to 120 characters) and drops the lower-limits offer. No test in packages or
|
||||
scripts reads SETUP-SHEET.md.
|
||||
- N2 taken. Lines 379-412 are all 110 characters or shorter.
|
||||
|
||||
N4 (non-blocking). The new cite says `:114-117` at aca16404c. At aca16404c, line 114 is blank and the
|
||||
paragraph runs from :115 to :118, ending with "No cited answer" instead. "The reply limits paragraph
|
||||
only" still names it without doubt, so this doesn't block. Fix it next time the brief is touched.
|
||||
|
||||
## Cite fix 33ac28398 (gosalyn-0140): APPROVE
|
||||
|
||||
33ac28398 has parent 6aecdb675 and no rebase. It changes one line, the sheet cite `:114-117` to
|
||||
`:115-118` (rows blob 4b25e76ec). N4 is closed, and the approvals above stand.
|
||||
@@ -0,0 +1,131 @@
|
||||
# Case 2 Q222 reply limits as settings (design df29550c9, C2-06 brief 467be26e3), Webby
|
||||
|
||||
Request gosalyn-0143. Ruling Q222 (mos-1302): "Things should NOT be hardcoded". Mos asked for two
|
||||
checks: no path lets an agent session change a limit, and no bad value switches the guard off. I read
|
||||
with git against origin/refactor aca16404c. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES (R1-R3), design and brief
|
||||
|
||||
The structure is right. The settings live in a top-level key outside `projects`. A human-only verb
|
||||
writes them, outside `ORGANIZATION_KEYS` and `TESTER_ORGANIZATION_VERBS`. A writer fence covers
|
||||
every other path, and each key falls back on its own. The three gaps below are in the text the build
|
||||
will follow.
|
||||
|
||||
## What I checked
|
||||
|
||||
- df29550c9 is a child of 2852348d2 and touches only DESIGN.md (blob 05de030ad). 467be26e3 is a child
|
||||
of 33ac28398 and touches only the rows file (blob 10a818f44). Neither has a co-author line.
|
||||
- Agent paths to a write, at aca16404c:
|
||||
- `organization.set` maps to `limits.set` with actor `<definition>.<role>`, keys limited to
|
||||
`ORGANIZATION_KEYS` (broker.mjs:219, :4205-4213).
|
||||
- The chief's hire and fire use actor `<definition>.<role>` (:4306).
|
||||
- The tester uses actor `"tester"` (:4201), gated by `TESTER_ORGANIZATION_VERBS` (:139, :4158).
|
||||
- Credential verbs (credential-verbs.mjs:27-50) clone the file document.
|
||||
- The launcher's delete uses actor `r.by`, the human who asked (:3892).
|
||||
- Every setter builds `next` with `structuredClone` of the file's document: instance-settings.mjs
|
||||
:226/:291/:327, launch-settings.mjs:180/:288, plugin-settings.mjs:57, credential-verbs.mjs:27/:47.
|
||||
So none of them changes `replyLimits` today, and the M11 fence won't refuse an ordinary agent write.
|
||||
The design's claim holds.
|
||||
- M9 is already in `organizationWriter.write` (organization.mjs:191-201). It compares `document` and
|
||||
then `written`, the JSON round-trip, against `old.projects`. `old` is the on-disk file.
|
||||
- `validateBusinessDocument` (business.mjs:229) rejects unknown top keys, so `replyLimits` has to join
|
||||
`TOP_KEYS`. With any value accepted, a bad hand edit can't stop her business from loading. I agree
|
||||
with that choice.
|
||||
- The ceilings make sense as validity bounds. Every key resolves to an integer within a range, so no
|
||||
value reads as "no limit". Replacing the whole object, with a missing key meaning its default, is
|
||||
fine. It matches how `plugins.set` takes its whole value.
|
||||
|
||||
## Required changes
|
||||
|
||||
R1. Say which `human` the fence compares against: the on-disk file's (`old.human` in `write`), never
|
||||
the incoming document's. The design says "unless `actor` equals the file's `human`". A direct `write`
|
||||
call is in M11's scope. Compared against `document.human`, a write that sets `human` to its own actor
|
||||
and changes `replyLimits` would pass. Add a test: a direct `write` that changes both `human` (to the
|
||||
actor) and `replyLimits` refuses `organization-reply-limits-human-only`, and the file and
|
||||
`changes.jsonl` are unchanged. Put the same test in the brief's M11 list.
|
||||
|
||||
R2. Say what the guard does when the business file can't be read or parsed at check time: missing,
|
||||
unreadable, not JSON, or a document that isn't an object. The fallback paragraph covers a bad
|
||||
`replyLimits` value, not a failed read. The guard must refuse the reply in that case, or check it at
|
||||
the defaults. It must never skip the check. Name the outcome and its code, and add a test to the
|
||||
brief: with the file unreadable, an over-limit reply refuses and `messages` is unchanged.
|
||||
|
||||
R3. Name booleans, arrays and objects in the fallback list next to `null`, a string and a fraction,
|
||||
and test them. JavaScript comparisons coerce: `true >= 1`, and `[7] <= 1000` with `[7]` read as 7. An
|
||||
implementation that checks ranges with `<` and `>` and no type check would let these through. Say
|
||||
"an integer by `Number.isInteger`" or something equally exact, and add `true`, `[7]`, `{}` and `"5"`
|
||||
to the brief's invalid-value tests.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N1. M9 checks both `document` and `written` (organization.mjs:193 and :200). Write M11 the same way
|
||||
and say so, so a value with a `toJSON` from a direct caller can't differ between the check and the
|
||||
disk. Agent sessions can't send such a value through the broker, so this doesn't block.
|
||||
|
||||
N2. The guard should read each key with `Object.hasOwn`. `library.limits.set` should refuse unknown
|
||||
keys with `invalid-request`, the way `keys()` does for the other verbs. The design says unknown keys
|
||||
are ignored when read. For the verb it says only that a value outside the table refuses.
|
||||
|
||||
N3. Line cites drift against aca16404c:
|
||||
- `TOP_KEYS` is business.mjs:24, not :21.
|
||||
- `write` is organization.mjs:180, not :177.
|
||||
- `ORGANIZATION_KEYS` is broker.mjs:219 and `TESTER_ORGANIZATION_VERBS` is :139, not :103 and :145.
|
||||
Fix these in the design and the brief.
|
||||
|
||||
N4. "After: C2-04". M9 is already in `write` at aca16404c, so C2-04's writer part has landed. If
|
||||
C2-04 is done, the dependency is met. If parts of it are still open, say which.
|
||||
|
||||
N5. At the ceilings, a human-set value can make the residual very large (up to 100,000 characters
|
||||
of item text) and can make the correction check permissive (`correctionPercent` 100). The guard
|
||||
stays on, since cites are still required, and Q222 gives the choice to the human. It's worth one line
|
||||
in the setup sheet and in `library.status`: the residual is `ideasPerReply` times `ideaLength`.
|
||||
|
||||
N6. The tester's actor is the literal `"tester"` and a role's is `<definition>.<role>`.
|
||||
`ID_PATTERN` (vocabulary.mjs:199) allows both `tester` and dotted ids as the business `human`. So a
|
||||
human id of `tester` or `assistant.bean` would equal an agent actor, and the M11 compare would pass
|
||||
for that agent. The human picks the id, so this is unlikely. Still, the fence could also require
|
||||
that the write came from a human verb, or the validator could refuse a `human` id that collides
|
||||
with those actor shapes.
|
||||
|
||||
## After mos-1305
|
||||
|
||||
Mos accepted four of Gosalyn's choices: the ceilings as validity bounds, 0 as the strict value for
|
||||
`ideasPerReply` and both correction keys, After C2-04, and the verb in limits.mjs. None of R1-R3
|
||||
conflicts with them. All three are about the settings mechanism (the M11 fence, the read at check
|
||||
time, the per-key fallback), which carries into the rework after Q30, so they stand. N4 is withdrawn,
|
||||
since Mos accepted After C2-04. Findings on the item format itself wait for the Q30 rework.
|
||||
|
||||
## Round 2 (gosalyn-0150): APPROVE, design f164dcef8 and brief 51cc0445c
|
||||
|
||||
f164dcef8 is a child of df29550c9 and touches only DESIGN.md (blob 6c5a36eb7). 51cc0445c is a child
|
||||
of 467be26e3 and touches only the rows file (blob 8f350730f, +26/-14). Neither has a co-author line.
|
||||
Both are local, so this approval binds to these two shas. A rebase needs a compare before it carries.
|
||||
|
||||
- R1 met. The fence compares against the on-disk `old.replyLimits` and `old.human`, never
|
||||
`document.human`. It also refuses `tester` and `<definition>.<role>` for any role in `old.roles`.
|
||||
The brief adds the test for a write that changes both `human` and `replyLimits`, plus a mutant that
|
||||
compares `document.human`.
|
||||
- R2 met. A missing, unreadable, non-JSON or non-object file checks at the defaults and
|
||||
`library.status` shows `reply-limits-unreadable`. The brief tests an over-limit reply with the file
|
||||
unreadable, `messages` unchanged. R2 allowed refuse or defaults; defaults is fine.
|
||||
- R3 met. Keys are read by `Object.hasOwn` and used only if `Number.isInteger` and in range. The list
|
||||
names booleans, strings, arrays, objects, fractions and `NaN`, and the brief tests `true`, `[7]`, `{}`,
|
||||
`"5"` and an array as `replyLimits`.
|
||||
- N1 (both `document` and `written`), N2 (unknown key refuses `invalid-request`), N5 (setup sheet line
|
||||
and the residual in `library.status`) and N6 (agent actor shapes) are taken.
|
||||
- N3 cites check at aca16404c: business.mjs:24, organization.mjs:180 and :191-193/:199-200, broker.mjs
|
||||
:139, :219, :419 and :570.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N7. broker.mjs:327 (`checkBusiness`, row 177) already refuses a business whose `human` is `tester`, so
|
||||
that half of N6 was closed before this round. A dotted human id equal to a role actor is still legal.
|
||||
With the new rule, that human can't change the limits at all, since their own verb's actor matches
|
||||
the refused shape. That fails closed, so it doesn't block. The build should name the code that verb
|
||||
returns in that case, and the cleaner fix is still a validator refusal of such an id.
|
||||
|
||||
N8. The brief's dotted-human test says "that role's write refuses". `organization.set` can't carry
|
||||
`replyLimits` (it isn't in `ORGANIZATION_KEYS`), so the test has to be a direct `write` with the role's
|
||||
actor. Say so when the brief is next touched, or the test may pass for the wrong reason.
|
||||
|
||||
N9. Rows file :383 runs to 115 characters. Rewrap when the brief is next touched.
|
||||
@@ -0,0 +1,305 @@
|
||||
# PRD 1.17 draft d2, Case 2 story files are DOCX (Vikunja 570), Webby
|
||||
|
||||
Request: Sage sage-3098 (00:50:47Z 10-23), reviewer named in mos-1367 B5.
|
||||
Draft: agents/sage/work/prd-1-17/DRAFT-d2.md, sha256 02af5bbfd27d54bf7afc917fe573c310e9059e1bf50af2fa7d7ac1c893788b1d
|
||||
(matched before reading). Base: docs/prd/mosaic-stack.md blob a6db9b4a13d4 at 482e865ac.
|
||||
Sources: /mnt/storage/src/jason-master-restart/DECISIONS.md Q235-Q237 (lines 2355-2400), read only.
|
||||
|
||||
## Verdict: CHANGES (B1, B2, B3)
|
||||
|
||||
## What I checked
|
||||
|
||||
- Quotes. Every "Replace" and "Add after/before" anchor appears exactly once in the 1.16 text with lines
|
||||
joined: REQ-LIB-2 scope sentence (base :1225-1230), "Lookups return canon..." (:1230-1231), "so an agent
|
||||
can't cite..." (:1231-1232), REQ-LIB-4 "The user commits and pushes..." (:1252-1253), REQ-LIB-5 "Each
|
||||
answer names the commit it read." (:1262-1263), REQ-LIB-6 "A reply may cite lines..." (:1283-1284),
|
||||
"the broker refuses such a write before any git call." (:1276), "(Design 1.1" (:1208), and the 1.11 MVP
|
||||
line quoted in the notes.
|
||||
- Ids. requirements.json at 482e865ac has 85 ids and requirements.test.mjs:21 pins 85. One new id makes 86.
|
||||
The test's version pin (:24, '1.16') also moves, and the draft's file list covers it.
|
||||
- B1 condition (mos-1367). REQ-LIB-8 bullet 1 (draft :132-134) says it plainly: the converted file leaves
|
||||
the Gitea-mirrored repository and its DOCX lives in the stack's writing store, not in the repository
|
||||
the user pushes or opens in Obsidian. REQ-LIB-4's replacement (:100-104) says the same. Met.
|
||||
- B2 condition. Draft :135-140: each import has its own fidelity report, the human's confirmation of it, a
|
||||
manifest naming the source file and commit, and one commit. Met, apart from B3 below.
|
||||
- P1-P6 against REQ-LIB-8:
|
||||
- P1: bullet text :127-129. P2: :135-140 (convert, mark, move to the archive folder), with O4's
|
||||
archived scope in REQ-LIB-2 keeping it out of canon. P3: :143-145. P4: :129-131 and the format map
|
||||
:146-147. P5: :141-142 and :151. P6: :148-151, and the 1.17 note :161-164 for the 10-27 release.
|
||||
- All six are present.
|
||||
- Text beyond P1-P6. These trace to the landed O2 design D10/D12 or to Mos's O1-O4 rulings, not to
|
||||
P1-P6. Each is cited in the draft, so I don't count them as overreach, but Mos and Jason should know
|
||||
they are in the text Jason says yes to:
|
||||
- REQ-LIB-4: each accepted editor save is one commit by the human in the writing store (D12).
|
||||
- REQ-LIB-5: a DOCX is read only from a committed version, never unsaved text.
|
||||
- REQ-LIB-6: "A DOCX citation never calls a paragraph a line."
|
||||
- REQ-LIB-8 :148-150: the import opens only once agents can read committed DOCX "and cite them".
|
||||
P6 says only "once agents can read DOCX chapters". Citing is a stricter gate than P6.
|
||||
- The Risks line on zip diffs.
|
||||
- REQ-LIB-8 vs REQ-LIB-6 supporting writes. No conflict in the writes themselves. The archive folder
|
||||
may not overlap another prefix (draft :85-87), so it can't sit inside a supporting folder, and REQ-LIB-6
|
||||
gains "the archive folder is read-only to it". Supporting files stay Markdown in the user's repository.
|
||||
- REQ-LIB-8 vs REQ-WAIT-2/3. No conflict. The import is a human verb with no role grant and no agent
|
||||
request path (O3), so it isn't a vocabulary action and REQ-WAIT-3 doesn't reach it. REQ-WAIT-3's
|
||||
`library.write` paragraph (base :719-724) already says it "never writes a story file", which still
|
||||
holds for a DOCX in the writing store. If 571 later makes the import a vocabulary action, REQ-WAIT-3
|
||||
makes it a hard wait and REQ-WAIT-2 keeps it out of every role file, as O3 says.
|
||||
|
||||
## Required changes
|
||||
|
||||
- B1. A playground file can become canon. REQ-LIB-2's replacement (draft :83-84) makes "a story file's
|
||||
DOCX document in the writing store" canon for its book, with no other scope for a DOCX. REQ-LIB-8
|
||||
(:135) lets the import take any "Markdown story file", and P1 makes "chapters and other story files"
|
||||
DOCX. Nothing stops the import from taking a Markdown file under a playground prefix. Its DOCX would
|
||||
then come back as canon, which breaks REQ-LIB-2's own title, "Playgrounds are never confused with
|
||||
canon" (base :1211). The fix can be either of these:
|
||||
- The import takes only a canon file, a Markdown file in a book folder outside every playground and
|
||||
supporting prefix, and playground text stays Markdown for now.
|
||||
- A DOCX keeps the scope, universe and book of the file it came from.
|
||||
The first is smaller and leaves the playground question to 571.
|
||||
- B2. Archived files have no universe. REQ-LIB-1's addition (draft :69-70) names "one archive folder" for
|
||||
the whole mapping, and REQ-LIB-2's addition (:85-87) says it may not overlap a book folder. A file
|
||||
moved there loses the path that gave it a universe and book. REQ-LIB-3 (base :1237) says "Every lookup
|
||||
names one universe, and its results never leave it". An archived lookup then either returns nothing or
|
||||
returns files from every universe, and the second breaks REQ-LIB-3. Two ways to fix it:
|
||||
- The archive folder is set per universe (or per book).
|
||||
- The converted mark records the source path, and an archived file takes its universe and book from
|
||||
that mark.
|
||||
Either way the text should say which, since O4's continuity use (citing history) needs the book.
|
||||
- B3. The archive move has no named committer. The import makes two changes in two places:
|
||||
- the DOCX commit in the writing store;
|
||||
- the mark and move of the Markdown file in the user's repository.
|
||||
REQ-LIB-4 today says the user commits with their own tools and the bus only mirrors that repository
|
||||
with a read-only token, fast-forward only (base :1252-1259). REQ-LIB-6 ties every agent write to the
|
||||
agent's own account. The draft doesn't say whose account commits the archive move in the user's
|
||||
repository, or how "changes nothing when any step fails" (:140) holds when one step is a commit to a
|
||||
repository the stack doesn't own. Say who commits it. The human's own account, as with REQ-LIB-4's
|
||||
saves, fits P5. Also say what "changes nothing" covers. For example, the writing-store commit happens
|
||||
only after the move is on the remote and in the mirror, or the reverse. The mechanics can stay with 571.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
- N1. REQ-LIB-2's new rule (draft :80-81) says "a file under the archive folder is archived". Every other
|
||||
scope in the same sentence is "a Markdown file". A non-Markdown file the user drops in the archive
|
||||
folder would become archived and returnable. "a Markdown file under the archive folder" matches the
|
||||
other scopes.
|
||||
- N2. The Settled points intro (:16) says Mos took the first choice in each, but only O1 and O2 carry
|
||||
"Ruled yes in B1/B2". O3 and O4 still read as recommendations. Add "Ruled yes in B3" and "in B4" so the
|
||||
record doesn't need mos-1367 to read.
|
||||
- N3. REQ-LIB-1's version tag (:70-71) doesn't say where it goes. The 1.15 tag sits inside the closing
|
||||
parenthesis, "Version 1.15: supporting folders.)", so the 1.17 tag presumably follows it there.
|
||||
- N4. P5 names "Melanie's real books", and REQ-LIB-8 :150-151 says "a real user's books". That reads
|
||||
broader, which is the safer direction. I'm flagging it only so Mos sees the wording changed.
|
||||
- N5. If mos-1368's impact map changes the text, I'll review the delta against d2.
|
||||
- N6. The 1.15 and 1.16 notes keep the old text of every requirement they rewrote, for example "The 1.14
|
||||
text of REQ-LIB-6 was: ..." (base :1735). The d2 notes don't keep the 1.16 text of the REQ-LIB-2, -4, -5
|
||||
and -6 sentences they replace. Adding those lines follows the convention and lets Jason read the change
|
||||
without the diff.
|
||||
|
||||
## Round 2: draft d3 (sage-3099, 00:54:40Z 10-23): APPROVE
|
||||
|
||||
Draft: DRAFT-d3.md, sha256 f6dd3c28743bb34c5cde10a2d7db7a51d684f8ad2c91a95fe6440835f631cf4d (matched). I read
|
||||
the full d2-to-d3 diff. Lines below are d3 lines.
|
||||
|
||||
- B1 met. REQ-LIB-8 :162-165: only a canon Markdown file converts. The import refuses playground, supporting
|
||||
and archived files and a converted one. REQ-LIB-2 :111-112 makes a DOCX canon only for the universe and
|
||||
book of the canon file it came from. A playground file can no longer come back as canon.
|
||||
- B2 met. REQ-LIB-2 :110-111 and REQ-LIB-8 :170-171: the move keeps the old path under the archive folder, and
|
||||
the archived file takes its universe and book from that path. Scope still comes from the path alone, and
|
||||
REQ-LIB-3 holds. The 1.17 note :219-220 says so.
|
||||
- B3 met. :172-176: the mark and move are one commit authored by the human in the user's repository. The
|
||||
DOCX is canon only once both commits exist, and the Markdown stays canon at its old path until then. A
|
||||
failed import leaves no returned DOCX and no moved Markdown.
|
||||
- N1, N2, N3 and N6 are met. The three version-tag anchors are each unique in the 1.16 text: "Version 1.15:
|
||||
supporting folders.)" with lines joined, "Version 1.15: supporting.)" at base :1236, and "Version 1.15.)"
|
||||
at base :1293. The quoted 1.16 sentences in the notes (:205-215) match the text they replace.
|
||||
- N4 is kept with a reason in C-section. That's Mos's call.
|
||||
|
||||
### Notes (non-blocking, round 2)
|
||||
|
||||
- N7. "Pushed the way REQ-LIB-4's Console saves are" (:172-173). In d3, the only Console saves REQ-LIB-4
|
||||
mentions are the DOCX saves to the writing store, which are never pushed to the user's repository. The
|
||||
path meant is presumably REQ-LIB-4's existing "Bus writes and pushes come in later rows (C2-02,
|
||||
C2-03b)" (base :1259, and the 1.15 note at base :1762-1763 on the user's Console saves). Naming those
|
||||
rows would remove the ambiguity.
|
||||
- N8. "No Markdown moved" on failure (:175-176) only holds if the store commit comes before the push of the
|
||||
move. Once the move is pushed it can't be withdrawn without a new commit, because history isn't
|
||||
rewritten. That ordering is 571's to state.
|
||||
- N9. C1 narrows P1. Jason's P1 makes "chapters and other story files" DOCX, and d3 keeps playground story
|
||||
text Markdown. The 1.17 note :217-218 says this openly, so Jason's yes covers it. Mos should make sure it
|
||||
is visible when the text goes to him.
|
||||
|
||||
## Round 3: draft d5 (sage-3117, 01:31:00Z 10-23): CHANGES (B1, B2, B3)
|
||||
|
||||
Draft: DRAFT-d5.md, sha256 0dc49e5b4b84de0e43d365c1ef87d3e6300dba7e986d5dc0bb4acc50ad4e900b (matched; DRAFT.md is
|
||||
the same blob), 458 lines. Lines below are d5 lines. Base anchors are from 482e865ac with lines joined. Sources read
|
||||
only: DECISIONS Q226 (d183246, :2253-2273) and the impact map (d183246, projects/mosaic-mvp/case2-docx-impact-map.md).
|
||||
|
||||
### Anchors and titles
|
||||
|
||||
- Each new "Replace" and "becomes" anchor appears once in the 1.16 text, with one exception, N10.
|
||||
- The three changed titles (:348-353) are 120, 116 and 117 characters, one line each, inside the
|
||||
requirements.test.mjs title rules.
|
||||
- The six roles/*.md contracts each hold "Every item cites the lines of the user's material it is about" (:311-313).
|
||||
|
||||
### Sage's five checks
|
||||
|
||||
1. REQ-LIB-2 opening "A Markdown file's scope comes from its path alone" (:95). Right call. A DOCX takes its scope
|
||||
from its source (:103-107), so "a file's" would be false. A non-Markdown file in the archive folder stays
|
||||
unmapped, which closes my old N1.
|
||||
2. Exclusion by id (:232-233). Needed: a DOCX started in the editor has no old path to exclude. REQ-LIB-3's own text
|
||||
(base :1246-1247) says "an exclusion keeps a source path out", so see N11.
|
||||
3. Autosave (:263-265). F41 says "All changes are instantly committed and available to the agents", and Mos's
|
||||
reading is "Each save is a commit at once, and agents read it at their next lookup" (DECISIONS :2257, :2268).
|
||||
Saves while the human types fit "instantly". The human-set intervals are Sage's addition (:418) and appear in
|
||||
the text Jason approves, but the J list (:426-432) doesn't carry them. See N12. REQ-LIB-5's "a lookup that
|
||||
starts after a save is acknowledged returns that save or a later one" (:148-149) matches "available to the
|
||||
agents".
|
||||
4. Unmarked folders not taken (:421). Agreed. REQ-LIB-2 leaves an unmapped file unmapped and never returned, so a
|
||||
DOCX made from one would have no scope to keep. The impact map's Q18 put the narrow or wide reading to Jason, and
|
||||
d5 marks the choice as item 4 (:443), so he sees it.
|
||||
5. A6's view not taken (:422). Acceptable only with Jason's yes. The impact map says "Jason: yes only if the
|
||||
amendment drops that view" (A6). J-b (:427) asks it. See N13.
|
||||
|
||||
## Required changes
|
||||
|
||||
- B1. REQ-LIB-8 and REQ-LIB-9 have no titles. requirements.test.mjs:20 needs one entry per `**REQ-` id, each with a
|
||||
title of at most 120 characters, and Jason says yes to the exact text, so the titles belong in it. Fix text for
|
||||
the :346-354 note, after the REQ-LIB-4 title:
|
||||
"REQ-LIB-8 is titled 'Story files become DOCX through a human-only, one-way import that archives the Markdown
|
||||
under its old path' and REQ-LIB-9 'The Console's DOCX editor: only a human saves, each save is one commit, and
|
||||
the editor code is vendored and pinned'." These are 106 and 114 characters. Also add "and titles of REQ-LIB-8
|
||||
and REQ-LIB-9" to the file list at :451.
|
||||
- B2. The old-path rule covers canon only, but d5 converts playground files too. REQ-LIB-2 (:97-99) excepts a
|
||||
converted file's old path only inside the canon clause, and REQ-LIB-8 (:226-227) says only "is not canon". D1
|
||||
makes playground files convertible (:189, :201). After one is converted, a Markdown file the user puts back at
|
||||
its old path under the playground prefix is returned as playground, next to the playground DOCX that replaced
|
||||
it. Agents then see two current versions of one story file, which is what D5's rule exists to stop. Fix text:
|
||||
- REQ-LIB-2, replace "one under a playground prefix at any level is playground, any other Markdown file in a
|
||||
book folder is canon unless its path is a converted file's old path (REQ-LIB-8), and everything else" with
|
||||
"a Markdown file at a converted file's old path is unmapped (REQ-LIB-8), one under a playground prefix at any
|
||||
level is playground, any other Markdown file in a book folder is canon, and everything else".
|
||||
- REQ-LIB-8 :226, "is not canon" becomes "is neither canon nor playground".
|
||||
- B3. The 1.12 note misstates Q226 (:285-289). It says "Q226 later brought C2-02, C2-03b, C2-11a and C2-11b into
|
||||
the 10-27 slice". Q226's F42 reading names three: "C2-02, C2-03b and C2-11b move into the 10-27 slice"
|
||||
(DECISIONS :2269), and its F41 reading says "The editor stays an add-on that doesn't gate 10-27 (Q195)"
|
||||
(:2268). C2-11a isn't in Q226. The impact map's A57 says of C2-11a and C2-11b "Neither gates 10-27,
|
||||
ROWS:1655", which disagrees with F42 for C2-11b. Fix: name only C2-02, C2-03b and C2-11b with Q226 F42 as the
|
||||
source, and either cite the ruling that added C2-11a or drop it. If the queue's current gating differs from
|
||||
Q226, say which record wins. Jason reads this note as history, so it has to match his record.
|
||||
|
||||
## Notes (non-blocking, round 3)
|
||||
|
||||
- N10. "The Console shows each cite's scope." appears twice in the 1.16 text: in REQ-LIB-2 (base :1233) and in the
|
||||
1.14 note quoting the old REQ-LIB-2 (base :1706). The replacement at :117 should say "in REQ-LIB-2", so the 1.14
|
||||
note, which is history, stays unchanged.
|
||||
- N11. :342 says REQ-LIB-3 is unchanged, but REQ-LIB-8's by-id exclusion adds a form that REQ-LIB-3's "an
|
||||
exclusion keeps a source path out" doesn't have. Say "REQ-LIB-3's text is unchanged; REQ-LIB-8 adds an
|
||||
exclusion by DOCX id", or add the clause to REQ-LIB-3 itself.
|
||||
- N12. Add a J item for the autosave intervals. F41 says "instantly", and "at intervals only a human changes" is
|
||||
new text. J-e covers versioning, not timing.
|
||||
- N13. J-b should quote what it narrows. REQ-LIB-5 keeps its "Connections are always visible" tag (:161-162), while
|
||||
its new text says no graph shows a DOCX file's links (:158-159). Q226 asked for "another way to handle the
|
||||
visual brain map". J-b would be clearer to Jason if it named both.
|
||||
- N14. REQ-LIB-4's new title "agents read Markdown only there" can be read as "agents read only Markdown there".
|
||||
"agents read the user's Markdown only there" makes the title 128 characters, over the limit. "and agents read
|
||||
Markdown from it alone" makes it exactly 120. Sage's call.
|
||||
- N7-N9 of round 2: N8 is settled by D3's order bullet (:218-223). N7 is met at :215-216 (C2-02, C2-03b). N9 no
|
||||
longer applies, since d5 converts playground story files too (:189).
|
||||
|
||||
## Round 4: draft d6 (sage-3119, 01:36:38Z 10-23): APPROVE
|
||||
|
||||
Draft: DRAFT-d6.md, sha256 886b93c571f87bfbb2f27ee3113b6800a32d8f89196d95ace33566031bee380a (matched; DRAFT.md is
|
||||
the same blob), 475 lines. I read the whole d5-to-d6 diff. Nothing outside the listed fixes changed.
|
||||
|
||||
- B1 met. Both titles are in the note (:359) and the file list (:468). Sage's REQ-LIB-8 title is 113 characters,
|
||||
and I accept it over mine: it covers D2's story files started in the editor, which mine left out.
|
||||
- B2 met. REQ-LIB-2 (:97-100) puts the old-path rule ahead of the playground and canon clauses, so it covers both.
|
||||
REQ-LIB-8 (:227-228) says "neither canon nor playground".
|
||||
- B3 met. The note (:287-291) names F42's three rows, matching DECISIONS :2269, and says F42 wins over records
|
||||
that differ. The 1.12 note it cites does list C2-11a among rows that don't gate 10-27 (base :1637).
|
||||
- N10 met (:118). The sentence still appears twice in the base, and "In REQ-LIB-2 only" scopes it.
|
||||
- N11, N12 (J-h, :449) and N13 (J-b, :443) met.
|
||||
- N14 not taken, and Sage cites mos-1372 D11. That's Mos's call.
|
||||
|
||||
## Round 5: draft d8 (sage-3378, 11:13:06Z 10-23)
|
||||
|
||||
Read: DRAFT-d8.md first as sha e9b01d44e32fe63b (634 lines), then as regenerated under mos-1446 (sage-3383, 11:17:01Z), sha 343cff6ff8fdfdb8 (638 lines, same as DRAFT.md). d7-to-d8.diff sha d8be352ad71e8172. Its body equals `diff -u` of d7 and d8, and only the header's file names and times differ. Mos's fix list mos-1443 sha 4c1b30d4. Line numbers below are for 343cff6f. Doc-only, no slot used, no node run.
|
||||
|
||||
Verdict: APPROVE. No B items.
|
||||
|
||||
Checked:
|
||||
- Placement. Every Replace and Delete block of the fix list is gone from d8 or kept inside its With text. Every With and Insert block appears once. J-i to J-l follow J-h in order. M1's sentences sit after "Bus writes and pushes come in later rows (C2-02, C2-03b)." The M30 placement notes are in for REQ-LIB-8, REQ-LIB-9 and the Risks lines. The inline fixes M36a (D9), M45a ("Review order (mos-1367 B5):") and M45c (D10) are in, and their old forms are gone.
|
||||
- The d8 changes list has one entry for each of R1 to R14 and M1 to M45, plus the landing swap line and the file list.
|
||||
- Landing swap. b99ca86b9 is on origin/refactor ("docs: align editor conversion boundary and host restore with PRD d8"). M15 (source line) and M16 (571 list lead) cite it. J-b keeps 767bb62c7. The Connections view text in writing-editor.md is the same at both commits, so J-b's line numbers hold.
|
||||
- mos-1446. The source line (:70-87) quotes Q235 in full. I checked it word for word against jason-master-restart DECISIONS.md:2357, read only. It keeps "creating writing" and drops only the leading "Q36:", which the line's "answering Q36" covers. The quote rewraps to 82 columns. The M15 line (:500) says "in full (mos-1446)". Rerunning my check on 343cff6f, every With block is found except M15 (changed by this ruling) and M16 (found once 767bb62c7 is swapped for b99ca86b9).
|
||||
- Open defaults with Mos: M10 has no J-m, M15 quotes Q235 in full under mos-1446, and M40 has both sentences in REQ-LIB-6 ("No creative agent starts or undoes a conversion ..." and "An agent may still suggest a conversion to the human in chat.").
|
||||
- R3's five d9 edits are not applied. REQ-LIB-5's notes still say "until a Console view of their links exists", and the Risks line says the same.
|
||||
- Section 5.1 against origin/refactor. packages/tasks/tests/requirements.test.mjs:21 pins 85 ids and :77 expects `requirements[85].id: REQ-ROLE-1 twice`. packages/bus/tests/requirements.test.mjs:48 pins length 85. REQ-LIB-7 and REQ-INST-1 are adjacent in packages/tasks/requirements.json:86-87, so the new ids fit between them. `GATED_ONLY` is at packages/business/src/vocabulary.mjs:101, as the 571 list item names it.
|
||||
- Stale d7 text against the new text. No "agents read only", "W4b" (outside the M4 history line), "three things", "acknowledged" or "WebUI view" is left in the PRD text. "path alone" appears only in the Replace instruction (DRAFT-d8.md:106) and the kept 1.16 quote (:402). REQ-LIB-4, REQ-LIB-5, REQ-LIB-8, J-h and J-j agree on read timing: a save is read at the next lookup after the editor reports it committed.
|
||||
|
||||
Non-blocking:
|
||||
- N15. DRAFT-d8.md:559, the D7 landing line, still says the impact map's item 2 adds "no agent credential reaches it". R8 changed Item 2 (:620) and REQ-LIB-4 (:156) to "no agent can reach it with any credential". The pointer quotes text that no longer exists. Suggest quoting the R8 form.
|
||||
- N16. DRAFT-d8.md:538, the d7 changes line, says "d6's 'no agent credential reaches it' holds". That was true of d7. As history it can stay, but a short "(R8 replaces it in d8)" saves Jason reading two answers.
|
||||
- N17. Rewrap. The PRD text still has three lines over 82 columns: :107 (83, REQ-LIB-2), :276 (83) and :277 (95, the REQ-LIB-8 old-path bullet). All three came in with d6 B2 and are in d7 at :91, :230 and :231. d8 fixed the other d7 overlong lines. Not blocking because landing rewraps the quoted text anyway.
|
||||
- N18. No round of mine covers d6 to d7 (mos-1379 on Fenton's 571 B2). I read its text as carried in d8: REQ-LIB-5's tracked-changes, comments and tracked-deletions sentences with the Q222 tag (:179-181, :197), and the J-d sentence (:603). They agree with the 571 design's settings and with J-d's "Markdown has neither". I accept them.
|
||||
|
||||
## Round 6: draft d9 (sage-3498, 15:25:43Z 10-23)
|
||||
|
||||
Read with git show from Sage's local commit 4863e2caf, which is unpushed. DRAFT-d9.md is sha 7dfc52fd602405a1, 647 lines, and DRAFT.md is the same file. d8-to-d9.diff is sha 7cf3431492251a1b, and its body equals `diff -u` of d8 (343cff6f) and d9. Also read: Q239 at jason-master-restart 7ee5ab7 DECISIONS.md:2406 (read only), and R3 and M22 in Mos's fix list mos-1443 (d8-fixes.md:13-20 and :393-410). The 1.16 base is docs/prd/mosaic-stack.md blob a6db9b4a at 482e865ac. Line numbers below are for d9. Doc-only, no slot used, no node run.
|
||||
|
||||
Verdict: CHANGES, one B item.
|
||||
|
||||
Sage's three questions:
|
||||
- Items 2 and 3 stay inside Q239's "nothing else changes". Without item 2, REQ-LIB-5's note (:417-418) and the graph Risks line (:456-457) would still say the view is later work, which contradicts the new REQ-LIB-5 sentence (:194-195). Item 3 only records the answer. R3 itself lists the note, the Risks line and the A6 line as d9 edits, so none of them is new scope. None should come out. B1 is about their wording, not their presence.
|
||||
- Line widths. No PRD-text line is over 82 columns. :137 (101) and :192 (83) are instruction lines, both already in d8. N17's two rewraps keep the words: the whitespace-normalized text is unchanged.
|
||||
- Replace quotes. Every Replace, "Add, after" and "becomes" quote in the Text changes section occurs once in the 1.16 base, normalized for whitespace. The one exception is "The Console shows each cite's scope.", which occurs twice (REQ-LIB-2 and the 1.14 note at base :1706). d9 scopes it with "In REQ-LIB-2 only", as N10 asked. The insertion anchors "Trusted mode lets an agent", "### Installer" and "## Milestones" each occur once.
|
||||
|
||||
Checked as well: N15 and N16 are in at :568 and :547, worded as I asked. Nothing outside the listed places changed.
|
||||
|
||||
Blocking:
|
||||
- B1. d9 doesn't use the edits Mos already ruled for a yes to J-b, and doesn't say why. mos-1443 R3 (d8-fixes.md:13-20) says "If Jason says yes, d9 takes five edits", with exact text. d9 departs from three of them:
|
||||
1. REQ-LIB-5 (:194-195). R3.1 adds "The Console's Connections view lists the current links and backlinks of each story file the user may see, in both formats, with each one's scope and format." d9 instead replaces "and no graph shows them yet" with "The Console shows a converted chapter's links out and in, in a Connections view." That is a narrower requirement. It covers converted chapters only, and it drops each link's scope and format and the "story file the user may see" bound. Q239's reading ("a Console view that shows a converted chapter's connections") fits either text. A builder would get a different view from each, so the choice of text belongs to Mos, not to the draft.
|
||||
2. The Risks line (:456-457). R3.2 is "...show in the Console's editor and its Connections view, and no graph shows them yet." d9 ends "..., not in Obsidian." That's close in meaning, but it is still not the ruled text.
|
||||
3. The A6 line. R3.3 moves it from "Not taken" up to A6 under "What d5 adds". d9 leaves it under Not taken (:605) and adds a sentence, so a line about a view that is now taken still sits under "Not taken".
|
||||
R3.5 matches in substance at :418. Fix: either take R3.1 to R3.3 as written, or keep d9's text and add a line under "d9 changes" saying it departs from R3 and why, so Mos rules on it at his J-b check. R3.4 is design work (Gyro, writing-editor.md and docx-citations.md at 767bb62c7). A note in "d9 changes" that it follows would keep it from being lost.
|
||||
|
||||
Non-blocking:
|
||||
- N19. J-b (:610) still says "His call whether 1.17 requires that view. As written, it does not." and "no graph or list shows them" ahead of the answer. Both are false of d9. Suggest putting them in the past ("As d8 was written, it did not") or ending J-b's question before the answer.
|
||||
- N20. J-b's "the import does not wait for the view (mos-1443 R3)" states as settled what R3 calls "my default for Mos to confirm". Q239 doesn't address it. Either cite it as Mos's default, or have Mos confirm it at the J-b check and cite that.
|
||||
- N21. The new REQ-LIB-5 sentence rests on Q239, but REQ-LIB-5's 1.17 tag (:198) cites only Q222, and the Status and source lines name Q235 to Q237. Adding "Q239" to the tag would give the requirement its source. It's Mos's call, given Q239's "nothing else changes".
|
||||
|
||||
## Round 7: draft d9 r2 (sage-3502, 15:36:19Z 10-23)
|
||||
|
||||
Read with git show from Sage's local commit 8b4201055, which is unpushed and sits on 4863e2caf. DRAFT-d9.md is sha b17ce3867c4dfea7, 650 lines, and DRAFT.md is the same file. d8-to-d9.diff is sha 130ac61133f12324, and its body equals `diff -u` of d8 and d9 r2. I diffed d9 r1 (7dfc52fd) against r2: the changes are the header, :194-197, :420-421, :459, the d9 changes lines (:476-477), the A6 move (:588 in, old :605 out) and J-b (:613). Line numbers are r2's.
|
||||
|
||||
Verdict: APPROVE. No B items.
|
||||
|
||||
- B1 met. R3.1, R3.2 and R3.5's sentences each occur once in d9 r2, word for word with d8-fixes.md:15-19, normalized for whitespace. R3.3: the A6 line now sits as a sub-bullet under A6 in "What d5 adds" (:588), and "Not taken" keeps only the item 4 line. The d9 changes line names R3.4 as Gyro's design edit, with its anchors.
|
||||
- N19 met. J-b's question is in the past tense ("As d8 was written, it did not.").
|
||||
- N20 met. J-b cites the import not waiting for the view as Mos's R3 default, for him to confirm.
|
||||
- N21 goes to Mos with the sha, which I accept.
|
||||
- No contradiction found. REQ-LIB-8's four opening conditions don't name the view, which fits the R3 default. REQ-LIB-5's Markdown sentence (Obsidian graph) and the view's "in both formats" don't conflict.
|
||||
- Widths. No PRD-text line is over 82 columns. :137 and :192 are the same instruction lines as before.
|
||||
- The Replace quotes didn't change between r1 and r2, so round 6's result holds: each quote occurs once in base a6db9b4a, apart from the scoped REQ-LIB-2 one.
|
||||
|
||||
## Round 8, d9 r3 (4607e15b8, DRAFT-d9.md sha 02f28decad7f946f)
|
||||
|
||||
Scope under mos-1470: Mos's two edits only. Static read with `git show`.
|
||||
|
||||
- Parent of 4607e15b8 is 8b4201055, so round 7's record keeps its sha.
|
||||
`git diff 8b4201055 4607e15b8` touches DRAFT-d9.md, DRAFT.md and d8-to-d9.diff.
|
||||
DRAFT.md has the same content as DRAFT-d9.md (02f28dec). DRAFT-d8.md doesn't change.
|
||||
- Edit 1 (N20): the end of J-b reads "Q239 doesn't address it, and Mos confirmed it
|
||||
at his J-b check (mos-1470)." That is the only change to J-b.
|
||||
- Edit 2 (N21): the REQ-LIB-5 version tag at :199-201 adds "and Q239 for the
|
||||
Connections view". Line widths are 79, 78 and 23, all under 82. The old quote is
|
||||
unchanged from round 7.
|
||||
- d9 changes: the J-b yes line now ends "Beyond these and N21, nothing else in the
|
||||
PRD text changes.", the N19/N20 line records the confirmation, and a new N21 line
|
||||
follows. These are not PRD text.
|
||||
- d8-to-d9.diff (ca6c40f9eb077ce6): with the two header lines skipped, its body
|
||||
equals `diff -u DRAFT-d8.md DRAFT-d9.md` at 4607e15b8.
|
||||
- Mos's text comes from Sage's relay in sage-3505. I haven't read mos-1470 itself.
|
||||
|
||||
Verdict: APPROVE. No new N items.
|
||||
@@ -0,0 +1,143 @@
|
||||
# PRD 1.15, REQ-LIB-6 rewrite (594add438), Webby
|
||||
|
||||
Request gosalyn-0168 (17:23Z 10-22), for mos-1320 item 8. Commit 594add438 on gosalyn/prd-req-lib-6,
|
||||
parent aca16404c, local, no co-author line. Three files: docs/prd/mosaic-stack.md (+105/-35 with the
|
||||
others), packages/tasks/requirements.json and the version pin in requirements.test.mjs. Mos accepted the
|
||||
REQ-LIB-1 and REQ-LIB-2 widening in mos-1322 and named this review the gate. Read with git and against
|
||||
Mos's STATE repo (read only). I ran nothing.
|
||||
|
||||
## Verdict: CHANGES (B1)
|
||||
|
||||
One sentence in REQ-LIB-6 says more than the design does. Everything else is right.
|
||||
|
||||
## What I checked
|
||||
|
||||
- The quotes. Q223's two passages are verbatim from DECISIONS.md:2212. "Q30: yes." and the F43 line
|
||||
"citations should be requireable. Tunable setting." are Jason's words in the Q226 record (:2253,
|
||||
:2261). Mos's reading of F43 is verbatim from the bullet at :2270. TARGET.md:101 is "Agents NEVER write
|
||||
prose for the user".
|
||||
- REQ-LIB-6 against design b68d79663 and Q223/Q226: agents never write story, they write only `.md`
|
||||
files under a supporting prefix a human marks (design :215-218, :797-802), every other path refuses
|
||||
before git, each write is one commit by the agent's own Gitea account with a trailer, she sees a
|
||||
notice and can revert, chat is free text, every present citation is checked, `requireCitation` is
|
||||
human-only and off by default.
|
||||
- REQ-LIB-2. The scope order matches the design's table (:823): supporting first, so a folder in both
|
||||
lists is supporting, then playground, then canon. Lookups default to canon. The canon-cite rule is the
|
||||
only claim left once Q223 removed the item kinds, which is Mos's reading too.
|
||||
- The residual replacement matches the design's: a supporting doc can hold any text, the path check in
|
||||
the bus is the only path limit, and the agent's token can write any path.
|
||||
- The Version 1.15 note keeps the old REQ-LIB-6 and residual texts verbatim and lists REQ-LIB-3, 4, 5
|
||||
and 7 as unchanged. LIB-4's reading (writes in C2-02 and C2-03b, the fetch read-only through the
|
||||
chief's grant) is right.
|
||||
- requirements.json: version 1.15, three titles changed, all within 120 characters (117, 112, 119). The
|
||||
count stays 85. The test's pin at :24 moves to 1.15, and the status regex at :23 matches the new
|
||||
status line.
|
||||
|
||||
## Required change
|
||||
|
||||
B1. REQ-LIB-6 says `requireCitation` "makes every reply carry a citation". The design keeps the
|
||||
runner's fixed texts passing when it's true (design :245 and :1286-1287, and webby-0151 R1 added the
|
||||
too-long text): no answer, the turn failed, and the answer was too long to send. A test written from
|
||||
this sentence would refuse those notices. Say "makes every reply carry a citation, except the runner's
|
||||
fixed notices", or name them. The 1.14 text had the same exception, so the 1.15 text shouldn't lose it.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N1. REQ-LIB-1's rewrap leaves "organization file" alone on a line (prd.md, the second line of the
|
||||
item). Refill the paragraph.
|
||||
|
||||
N2. REQ-LIB-2's "follow the same placement rules" is fine at PRD level. The design also refuses a
|
||||
supporting prefix that holds another supporting prefix, and names `library-supporting-other-universe`.
|
||||
The design is where those live, and the parenthetical already points to it.
|
||||
|
||||
## Round 2: 6742cf5ad (gosalyn-0173, 17:33Z, parent 594add438)
|
||||
|
||||
Only docs/prd/mosaic-stack.md, +6/-4, local, no co-author line. Read with git, nothing run.
|
||||
|
||||
Verdict: APPROVE.
|
||||
|
||||
- B1 met. `requireCitation` now excepts the runner's fixed notices and names all four: no answer, a
|
||||
failed turn, no answer passing the citation check, and the too-long text. Those are the four bodies the
|
||||
design's guard accepts after webby-0151 R1.
|
||||
- N1 met. "organization file" rejoins its sentence. The refill leaves "is refused. So" short on its line,
|
||||
which is cosmetic.
|
||||
|
||||
The gate is `packages/tasks/tests/requirements.test.mjs` and the `scripts/test-*.sh` that read the PRD or
|
||||
requirements.json, on Sage's run.
|
||||
|
||||
## Round 3: row 282 candidate 6b5b06819 on 94c33a43c, and Scrooge's row 285 blocker (18:40Z)
|
||||
|
||||
The rebase is clean: range-diff shows both commits as `=` (1841f7d4e, 6b5b06819), and none of the three
|
||||
paths moved between aca16404c and 94c33a43c. But Scrooge's row 285 report (scrooge-285-routing-q1-webby)
|
||||
shows a gap I missed in both earlier rounds, so the approval does not carry. Read with git, nothing run.
|
||||
|
||||
Verdict on 6b5b06819: CHANGES (B2).
|
||||
|
||||
B2. 1.15 gives creative agents a write (REQ-LIB-6, prd.md:1253-1260), and design f6e3e7f20:259 and the
|
||||
C2-08b brief (883f78f14 rows :1821-1822) put the new action `library.write` in each creative role's
|
||||
`withinRole`. PRD 1.15 never says that action is routed:
|
||||
- REQ-WAIT-3 (prd.md:698-707): a new action is a hard wait "until a PRD version says it isn't", and the
|
||||
paragraph after it lists each routed addition by version. 1.15 adds no line there.
|
||||
- REQ-WAIT-2 (:692-697): no role file may grant a hard-wait action. The list changes only "under a new
|
||||
PRD version that Jason approves".
|
||||
- Code agrees. vocabulary.mjs:62-66 and broker.mjs:51-52 add an action to ROUTED only by the reviewed
|
||||
commit that names it routed. Scrooge cites role.mjs:43 as the refusal.
|
||||
So C2-08b cannot be built to its brief under 1.15 as written. The fix belongs in this PRD row, the
|
||||
version that creates the write. Add a REQ-WAIT-3 line in the existing pattern, for example:
|
||||
"`library.write` was added as a hard wait and routes under REQ-LIB-6 since version 1.15." Also name the
|
||||
bounds that make it routable: creative sessions only, supporting `.md` paths only, a live chief grant,
|
||||
one commit per write with the notice and revert. Then record it in the 1.15 version note.
|
||||
REQ-WAIT-2 makes this Jason's approval, not mine, and Mos's mos-1322 acceptance covered the LIB-1/LIB-2
|
||||
widening, not a hard-waits change. Mos or Jason has to say yes to the routing sentence before it lands.
|
||||
|
||||
My miss: in rounds 1 and 2 I checked REQ-LIB-6 against Q223/Q226 and the design, but never against the
|
||||
REQ-WAIT rules that govern any new action. The design and brief rounds (webby-0151, -0155) missed it too.
|
||||
|
||||
## Round 4: Mos ruling mos-1328 (18:42Z), and the routing text in 759a18d85 (gosalyn-0181)
|
||||
|
||||
Mos ruled that routing `library.write` needs Jason's explicit yes. Q223 and Q226 say what agents may do,
|
||||
but they don't approve a version text. So 1.15 lands as I approved it, and a separate 1.16 carries only
|
||||
the routing. Row 285 goes `--after` the 1.16 row.
|
||||
|
||||
- Row 282 on 6b5b06819: APPROVE again. webby-0160 carries, and B2 moves to 1.16 by Mos's ruling.
|
||||
The rebase checks in round 3 still hold.
|
||||
- 759a18d85 (parent 6b5b06819, PRD only, +22/-3, no co-author line, every new line within 75
|
||||
characters, `git diff --check` clean): it does not go into 282. The text is right, and I'd approve it
|
||||
as the 1.16 content. The REQ-WAIT-3 paragraph (prd.md:711) matches design f6e3e7f20:259-260: creative
|
||||
definitions holding the action within-role, supporting `.md` paths only, a live Gitea grant on the
|
||||
library project, one commit per write with the trailer, the notice and revert. The REQ-LIB-6 sentence
|
||||
points to those bounds without restating them.
|
||||
- For the 1.16 row: say "since version 1.16" in both places, add a 1.16 header and note in place of the
|
||||
1.15 edits, bump requirements.json to 1.16, and move the test pin at requirements.test.mjs:24 to 1.16.
|
||||
I review that text, then Mos puts it to Jason.
|
||||
|
||||
## PRD 1.16 (row #1796), 482e865ac on 6b5b06819 (gosalyn-0184): APPROVE
|
||||
|
||||
I reviewed the diff 6b5b06819..482e865ac only. It touches three files (+35/-6): the PRD (blob a6db9b4a1),
|
||||
requirements.json (version only) and requirements.test.mjs:24 (pin only). No co-author line,
|
||||
`git diff --check` is clean, and the only added line over 75 characters is the test pin. Read with git,
|
||||
nothing run.
|
||||
|
||||
- The REQ-WAIT-3 paragraph (prd.md:719-725) follows the 1.7 and 1.8 routing paragraphs and gives the
|
||||
bounds of design f6e3e7f20:259-260. "Six creative roles" matches `CREATIVE_DEFINITIONS`
|
||||
(packages/business/src/vocabulary.mjs:207 at 94c33a43c).
|
||||
- Whose grant gates a write: Gosalyn's text says the writing agent's own grant. That matches design
|
||||
:259-260 (`library-write-no-grant`), :1396 and :1702, and REQ-LIB-6's "by that agent's own Gitea
|
||||
account". The chief's grant covers only the fetch. If Mos meant the chief's grant, that changes the
|
||||
approved design, so it's Mos's call, not a PRD edit. I agree with the text as written.
|
||||
- The REQ-LIB-6 sentence (:1280-1282) names the action and points back to REQ-WAIT-3. It doesn't restate
|
||||
the bounds.
|
||||
- The header, the 1.16 source line and the 1.16 note are accurate. The note says ROUTED is added only by
|
||||
C2-08b (row 285), after this version, which matches mos-1328.
|
||||
- The gate is the same three files as 282, because the version pin moved.
|
||||
|
||||
### Approved blob check (mos-1354, 22:25Z 10-22)
|
||||
|
||||
Jason approved PRD 1.16 ("Q32: yes", DECISIONS Q229) as blob a6db9b4a13d42c816371afa60c90efae74bbc0c1. `git rev-parse 482e865ac:docs/prd/mosaic-stack.md` returns exactly that blob, so the one I approved is the one Jason approved. mos-1354 calls the file docs/prd/prd.md, but at 482e865ac the PRD is docs/prd/mosaic-stack.md. The blob id is what binds. 482e865ac isn't on origin/refactor yet, which still has blob 717c66fc. At landing, check that the landed docs/prd/mosaic-stack.md blob is a6db9b4a. A mismatch goes to Sage and Mos.
|
||||
|
||||
### Round 1 verdict on 482e865acb7a8d881e1e2cc0a80141fb872cd688 (sage-3049, request c30846, rev 3354): APPROVE
|
||||
|
||||
- The candidate is 482e865ac, the commit I approved earlier in this file. Its parent is 6b5b06819 and its tree is unchanged.
|
||||
- `482e865ac:docs/prd/mosaic-stack.md` is blob a6db9b4a13d42c816371afa60c90efae74bbc0c1, which is the text Jason approved (Q229).
|
||||
- No base drift. The three paths have the same blobs at 6b5b06819 and on origin/refactor f0e3dcdf4: PRD 717c66fc, requirements.json a079888e, requirements.test.mjs 759b011f. A landing on refactor therefore produces blob a6db9b4a. Row 282 landed as 647424442, not 6b5b06819, but it carries the same three blobs.
|
||||
- No co-author line, and `git diff --check` is clean. Read with git; nothing run. The gate (requirements.test and the tasks suite) runs on Sage's CLEAR.
|
||||
@@ -0,0 +1,97 @@
|
||||
# Case 2 Q226 round 2 (design b68d79663, briefs d4d503db9), Webby
|
||||
|
||||
Request gosalyn-0165 (17:19Z 10-22), for webby-0151. Design b68d79663 on gosalyn/case2-design (parent
|
||||
f164dcef8, DESIGN.md blob 61394054e) and briefs d4d503db9 on gosalyn/case2-06-idea-caps (parent
|
||||
51cc0445c, rows blob 5254e360c). Both are amended and local. I read the deltas from af3349f00 and
|
||||
965dec0ee with git, against my last-fetched origin/refactor aca16404c. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES (R4)
|
||||
|
||||
R1, R2, N1-N5 and the mos-1320 items are met. R3 is met in its wording, but the "ever existed" check
|
||||
has no named source, and the obvious one loses the record on revoke. That's R4, one paragraph and one
|
||||
test line in C2-03b and design 7.2. The PRD REQ-LIB-6 row brief is approved as written.
|
||||
|
||||
## Round 1 items
|
||||
|
||||
- R1 met. A `request-too-large` on a JSON body gets one retry turn naming the limit, then the fixed text
|
||||
`(the answer was too long to send)`. The guard accepts four bodies when `requireCitation` is true.
|
||||
Design 5.2 and the C2-06 brief agree, and the runner test stores the fixed text for a cited reply over
|
||||
the limit.
|
||||
- R2 met. An unreadable business file resolves `requireCitation` to true, `library.status` shows
|
||||
`reply-limits-unreadable`, and the fixed texts still pass. It's in the Q222 paragraph, the ruling
|
||||
fallback, M5 and C2-06's settings and tests. A bad hand-edited value still reads as off, which is right.
|
||||
- R3 wording met. The fallback applies only while no chief grant record has ever existed, in any state.
|
||||
After that a revoked or `held` grant, or a fired chief, refuses `library-fetch-no-grant`. A write, a
|
||||
revert and a save never fall back. The tests cover a valid slot and a mode-0000 slot. Step 4b deletes
|
||||
the slot and revokes its token, and design 7.2, 10 and the brief agree. See R4 for the source.
|
||||
- N1 met. A revert and her save are authored by `editor` and push with `library-human`.
|
||||
`library-mapping-invalid` and `library-human-missing` refuse before any git call.
|
||||
- N2 met. Design 5.2 cites runner.mjs:571-574, :572 and :577-583.
|
||||
- N3 met. M3 names packages/harness/src/tools.mjs and the `raise_decision` skip at :241.
|
||||
- N4 and mos-1320 item 5. C2-21 has both record-only Gitea checks.
|
||||
- N5 met. One line in C2-08b.
|
||||
|
||||
## mos-1320 and sage-2833
|
||||
|
||||
- Item 2. The C2-21 brief and the design keep the `#giteaRole` comment citing mos-1320
|
||||
(broker.mjs:2118-2121 at aca16404c). The `host-creative-role` refusals stay: the grant writer at
|
||||
broker.mjs:1096, `host.run` at :1244, and the step 1 recheck. Each has a named existing test:
|
||||
grant-host.test.mjs:125 and grant-executor.test.mjs:1035 and :1046. I checked all five lines.
|
||||
- sage-2833. C2-05b is back as row 224, built at 23816cf28. The new C2-05c (reviewer Dewey) owns the
|
||||
supporting scope and blob, and C2-02 now runs after C2-05c. The scope enum pin at
|
||||
library-tools.test.mjs:49 in 23816cf28 is `['canon', 'playground']`, so C2-05c changing it is right.
|
||||
- Item 8. The PRD row brief is below.
|
||||
|
||||
## Required change
|
||||
|
||||
R4. Name what the "ever existed" check reads, and make it survive a revoke. At aca16404c a revoke
|
||||
deletes the `gitea_grants` row (broker.mjs:2785), and so does `grant-lost` (:2609). The table has no
|
||||
state column (schema.sql:469-480). A check written as "is there a `gitea_grants` row for the chief"
|
||||
finds nothing after a revoke or a fire, and the fetch falls back to slot `library-gitea`. That's the hole
|
||||
R3 closed. The `events` table is append-only (schema.sql:131-132), and grant events carry `method:
|
||||
'gitea'` and `agent` under `GRANT_EVIDENCE` (broker.mjs:246, :2366, :2469). Say:
|
||||
|
||||
- The check reads grant evidence events, not `gitea_grants`. Any `GRANT_EVIDENCE` event with `method`
|
||||
`gitea` and `agent` = the chief ends the fallback for good.
|
||||
- Drop "on the library project", or name where the project is read after a revoke. `grant.applied` and
|
||||
`grant.revoked` carry no `projects` (broker.mjs:2469, :2786-2789), and the row that has them is gone.
|
||||
Dropping it fails safe: a chief granted on any project loses the slot.
|
||||
- The revoked and fired test cases go through the broker's real revoke and fire paths, so the row is
|
||||
actually deleted when the fetch runs. A fixture that leaves the row in place passes either way.
|
||||
|
||||
## PRD REQ-LIB-6 row brief: approved
|
||||
|
||||
The cites hold at aca16404c: REQ-LIB-6 at docs/prd/mosaic-stack.md:1230, the title at
|
||||
packages/tasks/requirements.json:85, and the `['docs/prd/mosaic-stack.md', '1.14']` pin at
|
||||
requirements.test.mjs:24. The count pin at :21 stays 85 because the id stays. The owned files cover the
|
||||
PRD's `Status: approved, version` line, which :23 matches. Landing before C2-01d and C2-02 is right,
|
||||
since both cite the new text. When the PRD commit comes, I check the new REQ-LIB-6 against Q223 and
|
||||
Q226, the `:1594` residual, the list of unchanged REQ-LIB ids, and the version against whatever landed
|
||||
last.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N6. The briefs now order a docs row first (item 0) and C2-05c before C2-02. When Sage adds the queue
|
||||
rows, the `After` fields should match that order, so `queue next` doesn't offer C2-02 early.
|
||||
|
||||
## Round 3: design f6e3e7f20, briefs 883f78f14 (gosalyn-0171, 17:30Z)
|
||||
|
||||
f6e3e7f20 has parent b68d79663 and touches only DESIGN.md (+13/-8). 883f78f14 has parent d4d503db9 and
|
||||
touches only the rows file (+21/-12). Neither has a co-author line. Both are local. Read with git, nothing
|
||||
run.
|
||||
|
||||
Verdict: APPROVE. R4 is met.
|
||||
|
||||
- The check reads the append-only events table for any `GRANT_EVIDENCE` event with `method` `gitea` and
|
||||
`agent` the chief, of any action, and never `gitea_grants`. The design's ruling paragraph, 7.2 and
|
||||
steps 4 and 4b say the same thing as the C2-03b bullet. The cites hold at aca16404c: broker.mjs:246,
|
||||
:2469, :2609, :2785 and :2787, and schema.sql:131-132 and :469-480.
|
||||
- "On the library project" is gone, and the brief tests a chief grant on another project ending the
|
||||
fallback.
|
||||
- The revoked and fired cases go through `grants.revoke` and `instance.fire`, and each asserts the row is
|
||||
gone before the fetch.
|
||||
- The `held` case has an event to find. `held` is a credential-file state (gitea-grant.mjs:400, :414-425)
|
||||
that follows a commit `grant.step` or an unknown token. Both come after gitea grant events (broker.mjs
|
||||
:2366, :2236), so a held chief never falls back to the slot.
|
||||
|
||||
No new notes. N6 (the After order for Sage) still stands.
|
||||
@@ -0,0 +1,115 @@
|
||||
# Case 2 Q226 rework (design af3349f00, briefs 965dec0ee), Webby
|
||||
|
||||
Request gosalyn-0161 (17:07Z 10-22), for mos-1309. Design af3349f00 on gosalyn/case2-design and briefs
|
||||
965dec0ee on gosalyn/case2-06-idea-caps, both local. I read with git against my last-fetched
|
||||
origin/refactor aca16404c. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES (R1-R3)
|
||||
|
||||
The rework follows mos-1309. The shared bot is gone from the write path, the Q222 fence carries over to
|
||||
the two new keys, every write and save keeps the stale-base refusal, and M12 runs before any git call.
|
||||
The three gaps are a silent drop in the runner plan, one setting that fails open, and the fetch fallback
|
||||
Gosalyn asked me to question.
|
||||
|
||||
## What I checked
|
||||
|
||||
- af3349f00 has parent f164dcef8 and touches only DESIGN.md (blob 83bef1bd1, +387/-150). 965dec0ee has
|
||||
parent 51cc0445c and touches only the rows file (blob f3ee8ce29, +677/-126). Neither has a co-author
|
||||
line. Both are local, so this review binds to these shas, and a rebase needs a compare.
|
||||
- Gosalyn's three code findings hold at aca16404c:
|
||||
1. `#giteaRole` is broker.mjs:2122-2135. :2126 refuses every `CREATIVE_DEFINITIONS` role with
|
||||
`gitea-not-declared`, and the comment at :2121 cites mos-1163.
|
||||
2. `clip` is runner.mjs:130-131 (`BODY_MAX` 32000 at :56). :572 holds both fixed texts and :573 sends
|
||||
`clip(body) + unslop.note()`. A JSON body would lose its shape either way, so the library-business
|
||||
rule is needed.
|
||||
3. The runner's tool socket refuses past 65536 at :294. The bus server's `LIMIT` is 65536 (:6), with
|
||||
the save-verb exception at :9, :47-48 and :62. The client checks at client.mjs:19.
|
||||
- The Q222 fence carries. M11 is unchanged: the on-disk `old.human`, the actor-shape refusal, both
|
||||
`document` and `written`, and the `document.human` mutant. Keys are read by `Object.hasOwn`,
|
||||
`requireCitation` only as a `boolean`, and `citationsPerReply` only as an integer from 1 to 1,000. The
|
||||
invalid-value list names `"true"`, `1`, `null`, `[true]` and `{}` for the boolean.
|
||||
- My Q222 notes: N7 is taken (the writer's code is named, no validator change, said why). N8 is taken (a
|
||||
direct `write` with the role as actor). N9 is taken (C2-06's section is within 110 characters).
|
||||
- Writes have no shared-credential path. A write needs a live, not `held`, seat grant
|
||||
(`library-write-no-grant`), the helper answers one named seat's user and file, and it refuses another
|
||||
seat's user. `identity` and `publish` aren't in `ORGANIZATION_KEYS` (broker.mjs:219) or `INSTANCE_KEYS`
|
||||
(:222), so no agent can give itself a grant target.
|
||||
- Stale-base holds on all three commit paths: the agent write, the revert (base is the commit's own
|
||||
blob) and her save. The race retry re-applies only when the blob at the new tip still equals `base`.
|
||||
- The commit message carries only the path and the trailer. M12 refuses control characters, so a path
|
||||
can't inject a second trailer line.
|
||||
- The revert choice is right. A revert is her act, and authoring it as her `editor` with her own token
|
||||
means it still works after the agent's grant is revoked or the seat is fired.
|
||||
|
||||
## Required changes
|
||||
|
||||
R1. Say what she sees when a cited reply is too long to send. The brief removes the clip for a JSON body
|
||||
and says a body over the 64 KiB request limit "fails the send as any failed send does today". Today that
|
||||
is the catch at runner.mjs:577-583: it logs and skips `message.read`. She gets no answer and no notice.
|
||||
The retry doesn't help, since it runs only on a code from the 5.2 table, and `request-too-large` comes
|
||||
from the client (client.mjs:19-20) before the broker. Removing the clip makes this more likely: 32,000
|
||||
characters of non-ASCII text with JSON escaping can already pass 64 KiB. Pick one outcome and test it.
|
||||
The simplest is to treat `request-too-large` on a JSON body like a table refusal: one retry turn naming
|
||||
the limit, then a fixed text such as `(the answer was too long to send)`. That text then joins the bodies
|
||||
the guard accepts when `requireCitation` is true. Put it in 5.2 and the C2-06 brief, with a runner test
|
||||
that a cited reply over the limit stores the fixed text.
|
||||
|
||||
R2. An unreadable business file turns `requireCitation` off. Under Q222 the defaults were the strict
|
||||
values, so "check at the defaults" failed safe. `requireCitation` defaults to `false`, the loose value. If
|
||||
she sets it to `true` and the file can't be read at check time, an uncited reply passes. Mos's check for
|
||||
this round was that an unreadable file fails safe. For the unreadable case only, resolve
|
||||
`requireCitation` to `true` (or refuse the reply) and keep `library.status` showing
|
||||
`reply-limits-unreadable`. The fixed texts still pass, so she still hears that something went wrong. Add
|
||||
the test to C2-06: with the file unreadable, an uncited reply refuses `citation-missing` and `messages` is
|
||||
unchanged. A bad hand-edited value reading as off is fine. That's a human edit, Jason's default, and
|
||||
`library.status` names the key.
|
||||
|
||||
R3. Narrow the C2-03b fetch fallback to slot `library-gitea`. The brief falls back "only while the chief
|
||||
has no live read grant". That also covers a chief whose grant was revoked or `held`, or a fired chief. A
|
||||
revoke would then quietly move the fetch onto the old shared token instead of stopping it, and the
|
||||
shared credential the ruling removes stays live with no end date. The design (10 step 4) says C2-03b
|
||||
retires the slot, and the brief keeps it, so the two disagree. Say:
|
||||
- The fallback applies only when no chief grant record exists at all. Once one has existed, a revoked or
|
||||
`held` grant refuses the fetch with a named code and `library.status` shows it.
|
||||
- The fallback never applies to a push. Add a test: a write with no live seat grant refuses
|
||||
`library-write-no-grant`, and the slot file is never opened (a read trace, or the slot at mode 0000).
|
||||
- Name the step that removes the slot after C2-21 lands, and make the two documents agree.
|
||||
|
||||
## Notes (non-blocking)
|
||||
|
||||
N1. The design's write steps say "The author and committer are the seat's `identity`", and the revert
|
||||
and her save follow "the same steps". Only the C2-02 brief says a revert is authored by her `editor` and
|
||||
pushes with `library-human`. Say it in the design too. Also name the code for a revert or save when
|
||||
`editor` or slot `library-human` is missing. The brief names `library-mapping-invalid` for a save
|
||||
without `editor`, and nothing for a missing slot.
|
||||
|
||||
N2. Design 5.2 still cites `runner.mjs:449` for the fixed texts and `:451-456` for the dropped answer. At
|
||||
aca16404c those lines are `founderCheck` and `role.claim`. The texts are at :572 and the catch is at
|
||||
:577-583. The C2-06 brief has the right lines. Fix the design.
|
||||
|
||||
N3. M3 says `tools.mjs:225-280` "adds those to every session". At aca16404c `raise_decision` is already
|
||||
skipped for creative definitions (packages/harness/src/tools.mjs:241). The rest of the list is right.
|
||||
Also name the path, `packages/harness/src/tools.mjs`.
|
||||
|
||||
N4. Gitea branch protection can block force-push for collaborators. That would back D6 at the remote
|
||||
for the seat tokens, which "can write every path". It doesn't limit paths, so the residual stands. Worth
|
||||
one line next to the protected-file-patterns sentence for C2-21's live check.
|
||||
|
||||
N5. Adding the gitea credential to `roles/chief-of-staff.json` reaches every business that uses the
|
||||
role, Case 1 included. A grant still needs `identity` and `publish` in the business file, which only the
|
||||
human sets, and a human-approved `credential.mint`. So nothing widens without her or Jason. C2-08b's
|
||||
brief could say that in one line, so the reviewer of that row doesn't have to rebuild the argument.
|
||||
|
||||
## After mos-1320 (cc, 17:09Z)
|
||||
|
||||
Mos ruled on Gosalyn's points in gosalyn-0162. None of R1-R3 conflicts with those rulings. R1 and R2
|
||||
are about the runner and the guard. R3 is about the fetch's slot `library-gitea`, which mos-1320 doesn't
|
||||
address. Item 3 covers slot `library-human` for her saves, and I agree with it.
|
||||
|
||||
- Item 5 asks for a record-only check of protected file patterns on the scratch Gitea. N4 fits the same
|
||||
run: whether branch protection refuses a seat user's force-push while her normal push passes.
|
||||
Record-only, like item 5.
|
||||
- Next round I check item 2 in the C2-21 brief: the comment at broker.mjs:2120 cites mos-1320, and the
|
||||
host-grant refusals (`host-creative-role` in the grant writer, the step 1 recheck and `host.run`) stay
|
||||
as they are, each with a test or a named existing one.
|
||||
- Item 8, the REQ-LIB-6 docs row, I review when Gosalyn sends it.
|
||||
@@ -0,0 +1,173 @@
|
||||
# Webby review: Case 2 design r1 (#416)
|
||||
|
||||
Webby, 2026-10-20 (from `date -u`). Independent reviewer, replacing Dewey (Mos, mos-0980).
|
||||
Subject: `agents/gosalyn/work/case2/DESIGN.md`, blob fba567f71dc1, at e997f3b76 and on
|
||||
origin/gosalyn/case2-design 88222f3f3 (same blob). Author: Gosalyn. Static review only. I ran no
|
||||
tests and changed no product code.
|
||||
|
||||
Code base: `git archive 5d019d876`. `git diff --stat 5d019d876 9ae8c73ac -- packages adapters roles scripts`
|
||||
is empty, so every line below also holds at origin/refactor 9ae8c73ac. Paths are relative to `packages/`
|
||||
unless they start with `adapters/` or `roles/`.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
Twelve blockers. Section 6's no-schema claim holds. Section 5's write-side guardrails hold. The
|
||||
message-side guardrails have two channels the design misses (B3, B4), and the smallest slice can't
|
||||
run as written (B1, B2).
|
||||
|
||||
## Citations checked
|
||||
|
||||
These hold as cited: git.mjs:8 (`protocol.allow=never` in SAFE), git.mjs:44 (any `remote.*` key
|
||||
refuses), git.mjs:55-56; project index.mjs:12, :31-84, :97, :195-234, :209, :248-261, :256-258;
|
||||
paths.mjs:7, :14-15, :34-48 (`absolute(project.root)` at :41 throws on a missing root);
|
||||
business.mjs:186-187; gate.mjs:130-167 (typed by name at :134, built-ins at :137-138);
|
||||
claude-gate.mjs:13; tools.mjs:6-7, :95-102, :186, :211-220, :271-280; vocabulary.mjs:92-97, :131;
|
||||
role.mjs:19-28, :55-66; broker.mjs:290-291, :642-650, :1378-1430, :1434-1435, :1867, :2331-2347,
|
||||
:2374-2399; schema.sql:240-250, :296-306, :310; runner.mjs:446-456; seat session.mjs:59-64;
|
||||
adapters/claude/adapter.sh:47-53; launcher.mjs:135, :236-238, :353; webui bus.js:885, :919.
|
||||
|
||||
One miss: the `ls-tree` and `cat-file` reads that 1.3 cites at read.mjs:67-86 are in `blob()` at
|
||||
read.mjs:19-26 (N1).
|
||||
|
||||
## Section 6: "no schema or trigger change"
|
||||
|
||||
It holds. `threads` has no definition column or check (schema.sql:240-250).
|
||||
`message_threads_match` (:296-299) and `delivery_same_thread` (:300-306) check one thread's
|
||||
instance, and a room is one thread per instance, so each insert in `room.send` passes as written.
|
||||
`thread_one_per_run` allows one held thread per run, which matches one session per agent. The only
|
||||
constraint the design must name is `UNIQUE (project, name)` (:249). `room.open` names each thread
|
||||
after its instance, so an existing thread with that name refuses (N9).
|
||||
|
||||
The single-instance rule is code, not schema: broker.mjs:291, :2332-2334, launcher.mjs:135 and
|
||||
:236-237. Changing it is C2-09's job, which is why B1 matters.
|
||||
|
||||
## Jason's three guardrails
|
||||
|
||||
| Guardrail | Enforced in code? |
|
||||
|---|---|
|
||||
| Agents never write prose for the user | Into the library: yes. Gate (gate.mjs:137-138), typed tools (no `write_record` without `record.write`, tools.mjs:211) and human-only verbs make an agent write impossible. In messages: only the form is bounded (M5), and two channels skip M5 (B3, B4). The 400-character limit is stated in 5.1, but the stated residual is too small (B5) |
|
||||
| Agents always refer to the source material | Structural once B6 and B7 are fixed. The quote-in-lines check at a reachable commit is real verification |
|
||||
| Tool use is guarded | Yes: gate, typed tool list, broker re-check (tools.mjs:6-7) and `#checkMessaging` (broker.mjs:1867). N6 closes a role-file gap |
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. The smallest slice gives Melanie no way to chat with a creative agent. `#threaded` is true only
|
||||
for definition `assistant` (broker.mjs:291). `thread.create` refuses `thread-instance-ambiguous`
|
||||
with more than one (:2334). The launcher refuses `thread-not-allowed` for any other definition
|
||||
(launcher.mjs:135, :237). C2-08 adds the `THREAD_DEFINITIONS` constant, but C2-09 is the row that
|
||||
makes the broker and launcher read it and adds `instance` to `thread.create`, and C2-09 is outside
|
||||
the slice (DESIGN 9). The Console's thread creation must pass `instance` too. Fix: put C2-09's
|
||||
broker, launcher and `thread.create` parts in the slice. The Console chat must pick the instance.
|
||||
`room.*` can stay out.
|
||||
|
||||
B2. In the slice, Melanie reads raw JSON and can't see sync failures. Replies are JSON bodies (5.2),
|
||||
and only C2-10 renders them. 7.2 step 2 says "The Console shows them with file and line", but the
|
||||
library pages are C2-11b, outside the slice. A bad Obsidian push silently stops agents seeing new
|
||||
work. Fix: the slice needs a minimal cited-item renderer in the existing chat view and a sync status
|
||||
line (last valid commit, problems, `unpushed`).
|
||||
|
||||
B3. `raise_decision` is a second prose channel to the human. Every agent gets it with free-text
|
||||
`question` and option `text` (tools.mjs:221, comment at :5-6). An uncited raise goes to the human.
|
||||
M3's "no write tool exists" is true, but M5 covers only `message.send`. Fix: either refuse
|
||||
`decision.raise` from creative definitions in the broker and drop the tool from their typed set, or
|
||||
put the raise text through the same guard. Test both paths.
|
||||
|
||||
B4. The Console conversation view shows the model's raw text, including text M5 refused.
|
||||
webui serve.mjs:559-569 proxies `/api/conversation` to the control board, and the reader emits
|
||||
assistant text blocks (conversation/src/events.mjs:58, :82). A refused prose answer stays in the
|
||||
session file and is readable. Fix: leave creative sessions out of the catalogue on a library
|
||||
instance, or show stored messages only. Test that a creative session isn't listed.
|
||||
|
||||
B5. The stated limit understates what M5 lets through. A reply holds 20 items of 400 characters,
|
||||
which is 8,000 characters. An `idea` item's text is free, and any real quote of any line satisfies
|
||||
the cite. That is a full scene per reply, not "a one-sentence suggestion" (5.1). Jason wrote
|
||||
"NEVER", and Q176 says the system enforces it. Fix: add a total-characters cap per reply and a lower
|
||||
cap for `idea` items (for example 200 characters and at most 5 items). State the residual with
|
||||
those numbers. Mos takes the residual to Jason or Melanie for acceptance, because it is their rule.
|
||||
|
||||
B6. A `correction` isn't bound to the source. M5 limits the edit distance between `from` and `to`,
|
||||
but nothing requires `from` to be text in the cited lines. An agent can invent both. Fix: `from`
|
||||
must equal one cite's `quote`, after the same whitespace normalization, inside that cite's lines.
|
||||
Test that an invented `from` is refused.
|
||||
|
||||
B7. `citation-unfetched` has nothing to check against. The web trail stores the URL
|
||||
(broker.mjs:1521) and a summary of status, bytes and redirects (bus/src/web.mjs:46), not the page
|
||||
text. "The quote occurs in the fetched text" needs that text. Also, Claude's built-in WebSearch and
|
||||
WebFetch are on when `policy.webBuiltin` is set (gate.mjs:28-32), and those fetches never reach the
|
||||
broker. Fix: say where fetched text is kept (bounded, per run, or a new table, and if a table then
|
||||
say so). M1 must refuse the built-in web source for creative definitions, so `research` uses only
|
||||
the broker's tools.
|
||||
|
||||
B8. Remote content is a new untrusted input, and the S-rules don't check tree entries. Today only
|
||||
the bus writes record repos. Under 7.2, Obsidian or anyone with push to the Gitea repo writes too.
|
||||
A fast-forward checks out whatever the commit holds. No rule refuses symlinks (120000), gitlinks
|
||||
(160000), executables or nested `.gitattributes` and `.gitmodules`. Separately, S1 refuses the
|
||||
`.gitignore` that C2-12 commits, and Obsidian puts new notes and `.trash/` at the vault root by
|
||||
default. Fix: add a rule that every entry is a 100644 blob or a tree, and refuse those names at any
|
||||
depth. Allow `.gitignore` at the top. Commit Obsidian's new-file location and trash settings in
|
||||
`app.json`. Test each case.
|
||||
|
||||
B9. The fail-closed state in 7.2 strands Melanie's writes. If remote main is ahead and invalid, the
|
||||
bus stays on the old commit (step 2). The next bus write commits on that base. The push can't
|
||||
fast-forward, so the commit is `unpushed`, then `library-diverged`, with no merge until C2-17.
|
||||
S5 makes this likely: Obsidian users create `[[stub]]` links to notes that don't exist yet, so one
|
||||
dangling link in the playground blocks the whole sync. Fix: refuse library writes while remote main
|
||||
is ahead and invalid (`library-remote-invalid`). Apply S5 to canon only, or allow unresolved links
|
||||
in `playground/`. Test both.
|
||||
|
||||
B10. The 7.2 gate can't catch the obvious mutant. Tests use the file protocol through a test-only
|
||||
argument, so the https path, `GIT_ASKPASS` and the protocol override are never run. A mutant that
|
||||
sets `protocol.allow=always` passes them all. `git()` passes only `gitEnv()` plus the identity
|
||||
argument (git.mjs:10-12, seat checkout.mjs:79-89), so sync needs its own env entry for the helper.
|
||||
`GIT_TERMINAL_PROMPT=0` (checkout.mjs:85) doesn't stop askpass, which is fine. Fix: one function
|
||||
builds the production fetch and push argv and env. A test asserts that the argv holds SAFE, then
|
||||
exactly `-c protocol.https.allow=always`, and no other `protocol.*` key. Another asserts that
|
||||
`GIT_ASKPASS` names a helper outside the repo that reads the slot file. Keep the file-protocol
|
||||
switch out of any path that a business file or a bus verb can reach.
|
||||
|
||||
B11. An optional `root` breaks more than the overlap loop. `ensureCheckouts` clones every entry's
|
||||
root (seat checkout.mjs:310, :319). With no root it calls `clone(project, undefined, ...)`, so "a
|
||||
project with no root gives agents no clone" (2.1) is false today. `projectFilePath(entry.root)`
|
||||
(business/src/project.mjs:9-10) throws on undefined, and it is called at business/src/cli.mjs:72,
|
||||
:78, :118, resolve.mjs:44 and instance-settings.mjs:142. Fix: C2-04 lists these consumers, each
|
||||
skips a library project explicitly, and each has a test.
|
||||
|
||||
B12. Scope and crossovers can also come through front-matter fields, and in the slice a hybrid
|
||||
rests on convention. S2 and S3 speak of links only. `extends:`, `sources`, and `exclude` and `pin`
|
||||
paths are references too, and a canon book entity with `extends: playground/...` would bring
|
||||
playground text into canon lookups labelled canon. `library_manuscript` has no `scope`, so it must
|
||||
label or refuse a playground book. In the slice there is no crossover dialog (C2-11b), so a hybrid
|
||||
is a hand-written `universe.json` pushed from Obsidian, and S6 doesn't require `sanctioned`. Fix:
|
||||
put every path field under S2 and S3. Make S6 require `sanctioned`. State that in the slice a hybrid
|
||||
is Melanie's hand edit, and that only her account and the bus account can write to the Gitea repo.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. Cite read.mjs:19-26 for the blob reads, not :67-86.
|
||||
N2. `END_PROTECTED` is `[THREAD_DEFINITION]` (vocabulary.mjs:135). Say whether creative
|
||||
definitions become end-protected when `THREAD_DEFINITIONS` replaces it.
|
||||
N3. The runner sends `(the session gave no answer)` and `The turn failed: ...` (runner.mjs:449).
|
||||
The guard will refuse both, so Melanie never learns a turn failed. Add a fixed turn-failed notice,
|
||||
and test that any notice other than the fixed bodies is refused.
|
||||
N4. Promotion moves several files. `commitFile` commits one path (index.mjs:219). C2-02 needs a
|
||||
multi-path commit.
|
||||
N5. Ids allow capitals (paths.mjs:7). Two universes that differ only by case collide on a
|
||||
case-insensitive file system. Enforce lowercase in `checkLibrary`.
|
||||
N6. A role with no `messaging` key may message anyone (role.mjs:50). M1 must require `messaging`
|
||||
for creative definitions.
|
||||
N7. C2-05b's sync-before-read needs C2-03, which its row doesn't list. C2-03 reads the remote URL
|
||||
that C2-04 adds to the business file, so either pass the URL in or order C2-04 first.
|
||||
N8. C2-06 must run the guard through both the runner's RESULT path and `send_message`, with a
|
||||
non-creative sender in a library business. Otherwise a mutant keyed on `CREATIVE_DEFINITIONS`
|
||||
passes, against 5.2's "role choice doesn't matter".
|
||||
N9. Name the refusal when `room.open` meets an existing thread with an instance's name
|
||||
(schema.sql:249).
|
||||
|
||||
## Rows and the date
|
||||
|
||||
The rows are small and most gates name the right refusal. B6, B10 and N8 are the gates that miss
|
||||
their obvious mutant. The listed slice (C2-01 to C2-08, C2-12, C2-13) does not let Melanie start.
|
||||
She can write in Obsidian, but she can't reach an agent (B1) or read its answers (B2). With
|
||||
C2-09's broker part and a minimal renderer added, it can be done by 10-27 only if T1 row 1 (row
|
||||
190) lands by about 10-24, as section 9 says. Reaching the Console from her machine also needs SSO
|
||||
(Q175).
|
||||
@@ -0,0 +1,74 @@
|
||||
# Webby review: Case 2 design r10 (#416), two system units
|
||||
|
||||
Webby, 2026-10-20T21:01Z (from `date -u`). Author: Gosalyn (gosalyn-0045). Commit 4281b0064 on
|
||||
gosalyn/case2-design, DESIGN blob 4a1860e698e0. Diff `git diff 51006710a 4281b0064`, one commit. I
|
||||
read both unit templates, scripts/webui-service.sh, webui `cli.mjs serve`, scripts/mosaic and decisions 62,
|
||||
94 and 102 on origin/refactor. Static review.
|
||||
|
||||
## Verdict: CHANGES (B1, B2, B3)
|
||||
|
||||
The fix is the right one. Two units from the existing templates match how the stack runs today, and C2-10b
|
||||
needs the Console. One copy for both units keeps the human proof working, since the bus accepts only its
|
||||
own human-cli.mjs path (webui-service.sh:266-273). Making `%h` literal is correct for a system unit. The new
|
||||
refusal cases are good.
|
||||
|
||||
B1. The Console's boot start is Jason's call, not mine or Mos's, and the design should default to the strict
|
||||
reading until he rules. Decision 94 (lead-decisions:2016-2023) says the process that starts the Console
|
||||
holds human authority, and that a start through systemd gives a clean ancestry the proof accepts, "the
|
||||
detachment gap decision 62 names". Decision 102 lets one named seat, Mos, start it. It calls that a
|
||||
tradeoff Jason accepted, because "the human proof now rests on Jason's grant to one named seat" (:2253). A
|
||||
boot-enabled system unit hands that start to PID 1 on every reboot, with no named starter. That extends a
|
||||
human-authority boundary, so under AGENTS.md it needs an owner decision. Decision 94 also accepts the
|
||||
local-forgery limit only "for this PoC on dragon-lin under Jason's user ... this decision doesn't carry the
|
||||
limit there" (:2058-2062). On .54 any process that reaches the Console port can send as Melanie, and
|
||||
`melanie`'s Hermes processes and sudo are on that host until #423 step 5. Nobody has accepted that limit
|
||||
for .54.
|
||||
|
||||
Fix: render the Console unit with no `[Install]` section, so a human or Mos starts it at step 9 and after
|
||||
each boot. That fails closed and is cheap to reverse, because adding `[Install]` later is one re-render.
|
||||
Put two questions to Jason through Mos: may a boot start stand in for the human's start on .54, and does he
|
||||
accept the local-forgery limit there? The bus unit can stay boot-enabled, since it holds no human
|
||||
capability.
|
||||
|
||||
B2. `@PATH@`, `@NODE@` and `@FLAGS@` have no render rule. Both templates set
|
||||
`Environment=PATH=@PATH@` (mosaic-bus.service.in:15, mosaic-webui.service.in:25). The bus `ExecStart` is
|
||||
scripts/mosaic, a bash script that runs `exec node` from PATH (scripts/mosaic:22-40). So the bus's node is
|
||||
whatever PATH the render writes, not `--node`. Row 195 found v22 on .53, and the precheck checks
|
||||
`node --version` on `<user>`'s shell PATH, not the unit's. Fix: say `@PATH@` is `dirname(--node)` followed
|
||||
by `/usr/local/bin:/usr/bin:/bin`, as webui-service.sh:246-248 does, and `@NODE@` is `--node`. Say
|
||||
`@FLAGS@` is the checked serve flags, and refuse any `@[A-Z_]+@` left in either render, not only `%h`. Add
|
||||
a test that the bus unit's PATH starts with the `--node` directory.
|
||||
|
||||
B3. The hardening lines are never listed. In r9 they lived in the new template that C2-13 owned. r10 drops
|
||||
that template, and the render "adds ... the hardening lines", but neither the design nor the brief says
|
||||
which ones. The test "a template with a hardening line removed" has nothing to remove: the templates carry
|
||||
only `NoNewPrivileges=yes`. Some common lines break sessions. `RestrictNamespaces=`, `PrivateUsers=` or a
|
||||
`SystemCallFilter=` without the namespace calls stops bwrap, and then no role starts. The bwrap precheck
|
||||
runs in `<user>`'s shell, outside the unit's sandbox, so it can't catch that. Fix: list the exact lines in
|
||||
the brief (for example `ProtectHome=yes`, `ProtectSystem=strict` with `ReadWritePaths=<home>`,
|
||||
`PrivateTmp=yes`, `NoNewPrivileges=yes`), say none of them restricts user namespaces, and add a step 9
|
||||
check that one session starts under the bus unit.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. Both templates already end with `[Install]` `WantedBy=default.target` (bus :41-42, webui :39-40). The
|
||||
render should replace that line with `multi-user.target`, not add a second one. Under B1 it removes the
|
||||
Console's section.
|
||||
|
||||
N2. The render adds no `After=` or `Wants=`, so at boot the two units start in parallel. That is harmless
|
||||
today, because serve calls the bus per request (webui cli.mjs, `busCaller`) and starts without it. But the
|
||||
stated order applies only to a manual step 9. Either add `After=mosaic-bus@<business>.service` to the
|
||||
Console unit, or say that boot order doesn't matter.
|
||||
|
||||
N3. "The serve flags pass scripts/webui-service.sh's own flag checks" can't be done by calling that
|
||||
script. `serve_flags` (:146-207) is internal. Its proof-file check compares the owner to `id -u` (:201),
|
||||
which is root under case2-unit.sh. `render` finds node with `command -v` (:237), not `--node`. And
|
||||
webui-service.sh isn't in the files owned. Say whether case2-unit.sh copies the checks, with tests, or
|
||||
whether the row may move `serve_flags` into a sourced file. Also state which serve flags Case 2 uses.
|
||||
|
||||
N4. With a copy, webui-service.sh passes `--checkout <canonical>` (:251). Her host has no canonical
|
||||
checkout, so the queue read runs from the copy, and the queue store refuses that. Say that the Console unit
|
||||
gets no `--checkout`, and that the queue panel isn't part of Case 2.
|
||||
|
||||
N5. A re-pin must move both units to the same copy, or the human proof fails (webui-service.sh:266-273).
|
||||
Have case2-unit.sh refuse a render whose two units name different copy ids, and test it.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Webby review: Case 2 design r11 (#416), Console without [Install], render rules, 9.3
|
||||
|
||||
Webby, 2026-10-20T21:14Z (from `date -u`). Author: Gosalyn (gosalyn-0048, gosalyn-0051). Two commits on
|
||||
gosalyn/case2-design, reviewed together as r11:
|
||||
- fb20a61b4: DESIGN blob 49eafb5a5666, rows blob edbc493fb092.
|
||||
- eac92d956: rows only, rows blob 3a86d5f048dc. This is the one to re-pin.
|
||||
|
||||
Diffs: `git diff 4281b0064 fb20a61b4` and `git diff fb20a61b4 eac92d956`. I read B1 against mos-1097 and
|
||||
the port squat against mos-1100. I read the cited code at fb20a61b4. Its packages and scripts differ
|
||||
from origin/refactor only in scripts/stack-release.sh, which none of these citations use. This is a
|
||||
static review. I ran nothing on any host.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
B1 is fixed, per mos-1097. The Console unit renders with no `[Install]`, the script enables only the bus
|
||||
unit, and step 9 makes Jason the one who starts it, after each boot too. The tradeoff after an unplanned
|
||||
reboot is written down. Q185 (jason-master-restart DECISIONS.md:1573) does put him in the homelab for
|
||||
1012 reboots. Mos asked for two facts, and 9.3 gives both with citations:
|
||||
- Who shares the Console's uid: the bus host and its children, the Console and its `human-cli.mjs`
|
||||
children (webui bus.mjs:1-3), every session inside bwrap, and the admin steps.
|
||||
- What a bwrap session can reach. The port, yes. sandbox.mjs:67 has no `--unshare-net`, and :29-31 says
|
||||
the network isn't confined. The proof file, no. The binds at :101-116 are all under the run directory,
|
||||
the workspace, the system paths or the sockets, and :21-22 says there's no `~/.config`. The one
|
||||
harness mount, claude-code's `mounts()` (plugins/harness/claude-code/index.mjs:73-77), is only the
|
||||
pinned binary.
|
||||
|
||||
The 401/403 reading of serve.mjs holds. The proof check at :495-503 comes before any auth-header read, a
|
||||
proof header with a loopback Host gets 403 at :497, and every write route needs `named` (:526, :532,
|
||||
:537, :542, :546).
|
||||
|
||||
B2 is fixed. I listed every token in both templates. The bus has `@REPO@`, `@PATH@`, `@CLAUDE_ENV@`,
|
||||
`@PM_FLAGS@`, `%h` and `%i`. The Console has `@REPO@`, `@PATH@`, `@NODE@` and `@FLAGS@`. Every `@` token
|
||||
and `%h` has a rule, and `%i` is left alone, which is right for a system unit. The leftover refusal is
|
||||
`@[A-Z_]+@` or `%h`, so it doesn't catch `%i`. The `@PATH@` rule fixes the case I missed in r10. With
|
||||
`--node /usr/bin/node`, webui-service.sh:246-248 would let a node in /usr/local/bin win, and there's a
|
||||
test for that case.
|
||||
|
||||
B3 is fixed. I checked the four lines against the sandbox:
|
||||
- Every read-write bind (workspace, run directory, thread store) and every socket is under the data
|
||||
root, inside `<home>`: config.mjs:19, git-socket.mjs:1, launcher.mjs:5. So `ReadWritePaths=<home>`
|
||||
covers them.
|
||||
- Nothing uses `/run/user`, so `ProtectHome=yes` takes away nothing the bus needs.
|
||||
- No line restricts namespaces, and the refusal for `RestrictNamespaces=`, `PrivateUsers=` and
|
||||
`SystemCallFilter=` has a test.
|
||||
- The step 9 session start is the real check, and it waits on #383 as stated.
|
||||
|
||||
N1-N5 from r10 are all in the C2-13a brief, with tests.
|
||||
|
||||
The port squat and its fix are right. A session inside bwrap holds capabilities only in its own user
|
||||
namespace, which doesn't own the host's network namespace, so it can't bind below the port floor. cli.mjs
|
||||
:27 takes ports 0..65535, so serve needs no change. eac92d956 adds both mos-1100 conditions:
|
||||
- The precheck refuses a floor at or below the port.
|
||||
- The bind test builds a real session argv with `binds()` (sandbox.mjs:101) and `sandboxArgv()` (:162).
|
||||
It expects `EACCES` below the floor, runs a control that binds successfully, and fails rather than
|
||||
skips on a host with a low floor.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. A correction to my own r10 B1. I cited the dragon-lin forgery acceptance as decision 94. It is
|
||||
decision 95 (lead-decisions.md:2048, the text at :2058-2062). r11 9.3 has it right. Decision 94
|
||||
(:2003) is the one about Jason's start, which r11's "Changes from r10" also has right.
|
||||
|
||||
N2. The bind test's control binds "a free port of 1024 or above". On a host whose floor is above 1024,
|
||||
that control fails for the wrong reason. Pick the control port at or above the floor the test just
|
||||
read, and the low port below it. It's one line in the brief.
|
||||
|
||||
N3. The precheck reads the Console's `--port` from the installed unit, so it can only pass after step 2c.
|
||||
Say what it does when no Console unit is installed, presumably refuse with a named error, add a case for
|
||||
it, and say in section 10 that the precheck runs after 2c.
|
||||
|
||||
N4. `AmbientCapabilities=` carries across exec, so each `human-cli.mjs` child the Console starts also
|
||||
holds `CAP_NET_BIND_SERVICE`. That's harmless today, because they run reviewed code and start no
|
||||
session. Add one sentence to 9.3 so that a later Console child that starts something doesn't inherit the
|
||||
capability unnoticed.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Webby review: Case 2 design r2 (#416, creative writing hub)
|
||||
|
||||
Webby, 2026-10-20T19:28Z (from `date -u`). Independent reviewer. Author: Gosalyn.
|
||||
Subject: `agents/gosalyn/work/case2/DESIGN.md` at c1913fb07 on gosalyn/case2-design, blob
|
||||
05a79cbca624, sha256 bda7a717...4055 (verified). Code read at 9ae8c73ac, where the citations point.
|
||||
Static review. I ran nothing. I read the whole file. The r1 items I don't name below read as fixed.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
Four blockers. Three are in the persona path, which is new in r2. The data model rewrite onto her
|
||||
real repo is the right call, and the 1.3 claim mostly holds (see "The three questions").
|
||||
|
||||
## Blockers
|
||||
|
||||
B1. A top-level playground has no universe. 1.1 (:169-170) suggests "one at the top", but 1.2
|
||||
(:191) gives a file the universe whose `books` list holds its book folder. A file under a top-level
|
||||
prefix has no book folder, so `library_find` (universe required) can't return it, and the cite
|
||||
check's one-universe rule (:464) has nothing to compare. Fix: put `playground` inside each universe
|
||||
entry, or refuse a prefix that isn't inside a book folder. Also give playground the `.md` condition
|
||||
canon has (:187). As written, a `.canvas` under a playground prefix is playground and citable.
|
||||
|
||||
B2. Where `persona.text` lives, and who can read it. Instance entries take inline `vars` only
|
||||
(business.mjs:89 closes the keys). Nothing lets an entry point at a file, so "a 0600 file that the
|
||||
business file entry takes" (:665, :722-723) has no mechanism. Inline, the text is read by more than
|
||||
the design says:
|
||||
- The tester's `instances` read is allowed (broker.mjs:103) and returns every instance's vars
|
||||
whole (instance-settings.mjs:58-63). That breaks "No agent sees the persona text" (:724) on any
|
||||
host with `bus.tester` on (vars.mjs, `bus.tester`).
|
||||
- Every organization write logs old and new values of each changed path in
|
||||
`businesses/.history/<id>/changes.jsonl` (organization.mjs:70-80, :201-204).
|
||||
Fix: say the text sits inline in the business file, or add a file-reference var with its own
|
||||
checks. Then either refuse persona keys in the tester's reads, or make C2-13's precheck refuse
|
||||
`bus.tester` on `<host>`. State the mode of the business file and `.history`. Test it in C2-08.
|
||||
|
||||
B3. Name the places `THREAD_DEFINITION` must stay `assistant`. Section 4 (:404-406) replaces it
|
||||
"wherever it means may hold a thread" and keeps only tools.mjs:186. launch-settings.mjs:140, :186
|
||||
and :191 also use it. They let `organization.set`'s `assistants` key set an assistant's whole vars,
|
||||
which the chief of staff holds (`ORGANIZATION_KEYS`, broker.mjs:145, :1266-1271, Q169). Read as "may hold a thread", that
|
||||
lets the chief of staff rewrite Bean's `persona.text` and model. Fix: list those three lines as
|
||||
staying `assistant`, and add a C2-08 test that `assistants` with `bean` refuses
|
||||
`organization-not-assistant`.
|
||||
The same change has a second effect the design doesn't state. With `END_PROTECTED` set to
|
||||
`THREAD_DEFINITIONS` (:407-408), `instances.set` refuses to add or change a creative instance
|
||||
(instance-settings.mjs:23, :109), and `definitions()` marks all six not accepted (:52). So her model
|
||||
choice for Bean, a persona edit and adding a specialist later are all hand edits of the business
|
||||
file. 4 (:394) and 10 step 7 read as if she can pick specialists later. Say how that happens.
|
||||
|
||||
B4. Name the persona block's exact place. `prompt()` is the contract (bundle.mjs:177), then
|
||||
`## This session` (:179) with the session lines. The last of those says "end each turn with the
|
||||
answer itself" (:188), then the end_run and review-loop lines follow. "After the contract" can
|
||||
mean before `## This session` or at the very end, and the C2-08 snapshot depends on which. I'd put
|
||||
it between the contract and `## This session`, so the broker's facts come after the persona. Fence
|
||||
it the way skills are (`SKILL_END`, :107, :170), and refuse persona text that holds the end marker
|
||||
or a line starting `## `. Hermes text is 8,000 characters that nobody here has read, so it can't be
|
||||
allowed to forge a section. The snapshot then pins the order: contract, persona, session.
|
||||
|
||||
## The three questions
|
||||
|
||||
1.3, reporting is enough. For the agent-side guarantee, yes. Scope comes from the path alone, and
|
||||
the cite check (:463) refuses a playground cite on `fact`, `finding` and `correction` whatever her
|
||||
files contain. None of R1-R7 changes a scope. Holding back her head would add nothing to that
|
||||
check. Two limits belong in the text (N1, N2).
|
||||
|
||||
M9. It holds today. Every organization setter builds its document from the file and changes only
|
||||
its own keys (broker.mjs:1261-1269, instance-settings.mjs:93-121, launch-settings.mjs:182-191), so
|
||||
no verb can change `projects` now. M9 guards against a future setter. Place it where that setter
|
||||
can't skip it (N3).
|
||||
|
||||
Persona order. See B4.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. `library_get` takes only `path` (:346), so its `links_out` and `links_in` are the one place a
|
||||
lookup can name a file in another universe or an unmapped file. Say both lists drop those targets
|
||||
(R6, R2), and add a C2-05a test.
|
||||
N2. Scope is the current mapping applied to the path. A note she moves into a book folder is canon
|
||||
on her next push, and a mapping edit re-scopes old commits for the cite check. Both are right, and
|
||||
both are hers. Put the first in the setup sheet (C2-12).
|
||||
N3. Put M9 in `organizationWriter.write` (organization.mjs:177), comparing `canonicalJson` of old
|
||||
and new `projects`, hybrid entries included. Since no verb can build the change, the C2-04 test
|
||||
calls `writer.write` directly with a changed playground prefix, and one with a changed hybrid.
|
||||
N4. Obsidian resolves a shortest-path link across the whole vault, which is `~/src` with her other
|
||||
repos in it. The reader sees one repo. A base name unique in the books repo but not in the vault
|
||||
can resolve to a different file than Obsidian shows her. Count those in the survey's link-form
|
||||
counts if the survey can see it, or note it in the setup sheet.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Webby review: Case 2 design r3 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T19:43Z (from `date -u`). Author: Gosalyn. Subject: `agents/gosalyn/work/case2/DESIGN.md` at
|
||||
2958acb81 on gosalyn/case2-design, blob 60bc7d682e0d, sha256 f368c27d...c325 (verified). Read as the diff from
|
||||
c1913fb07 (r2, my REVIEW at 162ce46db), with the code at 9ae8c73ac. Static review. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
My r2 B1, B2 and B4 are fixed, and so are N1 to N4. B3 is fixed for the three paths it names, but it misses a
|
||||
fourth (B1 below).
|
||||
|
||||
- r2 B1. Playground prefixes now sit inside a universe and must start with one of its book folders, or the
|
||||
file refuses with `library-playground-outside-book`. A `.canvas` under a prefix is unmapped. 1.2's universe
|
||||
rule then holds for playground files.
|
||||
- r2 B2. The text moves to `<configDir>/personas/<business>/<instance>.md`, and the vars hold only the name and
|
||||
the digest. I checked the modes against the code. `durable` (organization.mjs:59-61) and `log` (:138-141)
|
||||
open with 0600 and O_NOFOLLOW. `log` refuses a group or other bit. `directory` (:39, :45) makes 0700 and
|
||||
refuses a loose one. The `instances` read (instance-settings.mjs:61-63) copies vars whole, so it now carries
|
||||
only the digest. `previous.json` (organization.mjs:197-198) holds the old business file, which has no text
|
||||
either.
|
||||
- r2 B3. launch-settings.mjs:140, :186 and :191 stay `THREAD_DEFINITION`. `organization.set`'s `instances` key
|
||||
refuses a creative instance for every actor. `instances.set` gets the human-only creative branch.
|
||||
- r2 B4. The fence follows `SKILL_END` (bundle.mjs:107, :170). The block sits between `contract.trimEnd()`
|
||||
(:177) and "## This session" (:179). The snapshot pins that order.
|
||||
- The correction is right. broker.mjs:147-149 says the chief of staff or a human-resolved decision ends an
|
||||
`END_PROTECTED` instance.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. `routes.set` lets the tester change a creative instance's harness, model and thinking. It is in
|
||||
`TESTER_ORGANIZATION_VERBS` (broker.mjs:103), and the gate at :1259 lets a tester through. `setRoutes`
|
||||
(launch-settings.mjs:240-272) has no protected-definition check. It replaces the route vars of any named
|
||||
instance, then writes at :277. So the design's 4 is wrong in two places:
|
||||
- DESIGN.md:474-493 lists every path to a creative instance and leaves this one out.
|
||||
- DESIGN.md:493 says that with `bus.tester` on, the tester "could change no creative instance".
|
||||
|
||||
M10 (DESIGN.md:563) has no test for it. `bus.tester` is off on her business and C2-13 refuses it, so this is
|
||||
not reachable there today. But the design claims the rule holds even if testing were on, and on any library
|
||||
business that turns testing on, it doesn't.
|
||||
|
||||
Fix: in C2-08, have `setRoutes` refuse a `CREATIVE_DEFINITIONS` instance from actor `tester` with
|
||||
`organization-creative-human-only`, as the `instances.set` branch does. The human keeps `routes.set`, which is
|
||||
the Console's routing table. Add the path to the list at 474, fix 493, and add the case to M10's tests: the
|
||||
tester's `routes.set` on `bean` refuses, the file is unchanged, and the human's still works.
|
||||
|
||||
## Your two questions
|
||||
|
||||
Keying the creative branch on actor `"tester"` is enough. broker.mjs:1272 sets the actor to `"tester"` only
|
||||
for a tester session. The business load check refuses a human id of `tester` (broker.mjs:245) and an instance
|
||||
of that name (:248). instance-settings.mjs:86 refuses one too. So the string can't name the human, and the
|
||||
writer is the only place that knows the definition, because it reads the document. A broker-side check would
|
||||
have to read the file a second time to learn the definition. The one thing to add: make C2-08's tester case go
|
||||
through the broker with a bound tester session, not just `set(..., "tester")`, so a change to the actor line at
|
||||
:1272 fails the test. The same goes for B1's case.
|
||||
|
||||
The `# `/`## ` refusal is safe, because it fails closed, but it may refuse her real personas. The fence alone
|
||||
stops a section escape, because nothing after `</persona-file>` belongs to the persona. The heading rule
|
||||
guards against a model reading a `## ` line inside the fence as a new section, which is reasonable. I don't
|
||||
know what her Hermes persona files look like. If they use Markdown headings, C2-20 stops at the fence check
|
||||
(DESIGN.md row C2-20) and the design names no next step. See N2.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The chief of staff can change which model a creative instance runs without touching the instance.
|
||||
`organization.set` takes `models` (broker.mjs:145, ORGANIZATION_KEYS), and launch-settings.mjs:169 replaces
|
||||
the whole `launch.models` table. If `bean`'s `model` var is a key the chief of staff rewrites, Bean runs the new
|
||||
model. That matches what 4 promises, since the instance and the persona stay unchanged, but mos-1033's "only
|
||||
the human" reads wider. Either say in 4 that the models table is shared and the chief of staff may retune it,
|
||||
or refuse a `models` change to a key a creative instance names. Mos's call, so I'd put it to him.
|
||||
N2. For C2-20, count the lines starting `# ` or `## ` in each persona and print the count with the byte count,
|
||||
before Jason's go. Then state the remedy in 9.1. That could be Melanie or Jason editing the text, or a recorded
|
||||
rewrite to `### `. Any rewrite needs her say, because the text is hers.
|
||||
N3. "Only Bean's or Chet's own session sees it" (DESIGN.md:866) needs a qualifier. `buildBundle` writes
|
||||
prompt.md, persona included, once per run into the run directory at 0600 (bundle.mjs:241-244, :265). Run
|
||||
directories are write-once and stay until `prune`. The claude adapter also passes prompt.md as one argument
|
||||
(bundle.mjs:104-105), and that argument is visible in `/proc/<pid>/cmdline` to other local users. No verb reads
|
||||
either copy, and on her VM the only other user is `melanie`, whose personas they are. So this doesn't change the
|
||||
design's safety. The text should name both copies, and the C2-13 precheck should check the run root's mode
|
||||
with the rest.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Webby review: Case 2 design r4 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T19:58Z (from `date -u`). Author: Gosalyn. Subject: `agents/gosalyn/work/case2/DESIGN.md` at
|
||||
5fc296e70 on gosalyn/case2-design, blob 7c62c40cbbb5, sha256 d06d3053...ab0a (verified). Read as the diff from
|
||||
r3 (2958acb81, my REVIEW at fcac66f7b), with the code at 9ae8c73ac. Static review. I ran nothing, apart from
|
||||
reading pi 0.85.1's resource-loader.js in the canonical checkout's node_modules.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
My r3 B1 is fixed, and so are N1, N2 and N3.
|
||||
|
||||
- r3 B1. `routes.set` is now on the list (DESIGN.md:549-552), and :493 is fixed (now :565). The one rule keys
|
||||
on the business file's `human`. I checked the actor lines: broker.mjs:1272 sets `tester` or the human id,
|
||||
and :1278 sets `<definition>.<instance>` for a role. M10 has the tester's `routes.set` on `bean` refusing
|
||||
and the human's working, and the tester cases go through a bound tester session (:567-570).
|
||||
- N3. The adapter lines are right (claude adapter.sh:79, :82; pi :106, :121). I read pi 0.85.1's
|
||||
`resolvePromptInput` (dist/core/resource-loader.js:17-31). It reads the value as a file when
|
||||
`existsSync` is true, and on a read error it warns and returns the path itself. See N1 below for the case
|
||||
where the file doesn't exist.
|
||||
- N2. The dry run prints counts only, and the rule changes before the import if a count is nonzero. That's
|
||||
what I asked for.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. The human does reach the `models` write, through `limits.set`. DESIGN.md:29-30 and :545-548 say that
|
||||
`organization.set` takes only an agent session, "so no verb writes `launch.models` for the human today", and
|
||||
that Jason edits it by hand. The first half is true. The conclusion isn't:
|
||||
|
||||
- `limits.set` is a human verb (broker.mjs:1248). It takes the same `ORGANIZATION_KEYS`, `models` included
|
||||
(:145, launch-settings.mjs:16).
|
||||
- `organization.set` is `limits.set` for a role (broker.mjs:1249-1251). Both reach the same `set()`
|
||||
(:1284, launch-settings.mjs:150-169), and the `models` replace is at :169.
|
||||
- So the human's `limits.set { models }` replaces `launch.models` today, with actor = her id. The tester
|
||||
can't, because `limits.set` isn't in `TESTER_ORGANIZATION_VERBS` (broker.mjs:103).
|
||||
|
||||
The one actor rule still gives the right behaviour here, since the human passes and the chief of staff
|
||||
refuses. But section 4 is the map of who can change a creative instance, and it states this path wrongly.
|
||||
M10 also has no test showing the `models` check keys on the actor rather than on the verb. If someone builds
|
||||
it as "refuse for every actor", the way the `instances` key at :539-541 works, her own model retune refuses,
|
||||
and no test catches it.
|
||||
|
||||
Fix:
|
||||
- In both places, say that the human writes `launch.models` with `limits.set` and the chief of staff with
|
||||
`organization.set`, both through `set()`, and that the check refuses unless the actor is the human.
|
||||
- Add to M10 and C2-08: the human's `limits.set` change to Bean's `launch.models` key works, next to the chief
|
||||
of staff's change that refuses.
|
||||
- :541 already says the human's `instances: {bean: ...}` refuses. That case also goes through `limits.set`.
|
||||
Name the verb there too, so the test is written against it.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. Pi also uses the path as the prompt when the file doesn't exist. Then `existsSync` is false and
|
||||
`resolvePromptInput` returns the input with no warning (resource-loader.js:21, :30). So the adapter's check
|
||||
has to cover existence as well as readability. `[ -r "$f" ]` does both. Add a missing file to C2-08's pi
|
||||
adapter test, next to the unreadable one.
|
||||
|
||||
N2. The claude adapter test can only show what the adapter puts in argv. Whether claude 2.1.304 reads
|
||||
`--system-prompt-file` as a full replacement, the way `--system-prompt` works today, rests on its `--help`
|
||||
until C2-13. Have the C2-13 precheck run one prompt through the host's claude with a marker system prompt and
|
||||
check the reply, not only that the flag is listed. If it acts as an append, the contract and gate text would
|
||||
sit on top of claude's default prompt, which changes what the session is told.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Webby review: Case 2 design r5 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T19:54Z (from `date -u`). Author: Gosalyn. Subject: `agents/gosalyn/work/case2/DESIGN.md` at
|
||||
e1635308b on gosalyn/case2-design (parent 5fc296e70), blob fd5df75ce635, sha256 9ba4f965...2d22d (verified).
|
||||
Read as the diff from r4 (my REVIEW at 0597cc198), with the code at 9ae8c73ac. Static review. I ran nothing.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- r4 B1 is fixed. Section 4 (DESIGN.md:555-557) says `limits.set` and `organization.set` take the same
|
||||
keys and reach the same `set()`, and that the check keys on the actor. I re-read
|
||||
packages/bus/src/broker.mjs:1248-1251 and :1284 at 9ae8c73ac, and they match. The `instances` case
|
||||
(:565-566) names both verbs. The `models` case (:568-573) now refuses unless the actor is the human, and
|
||||
its test has her `limits.set` change to Bean's key working next to the chief's refusal. M10 (:665) and
|
||||
C2-08 (:899) carry the same case. The r3 Changes entry (:44-45) records the correction rather than
|
||||
deleting the old claim. No stale "agent-only" or "by hand" claim is left (grep).
|
||||
- r4 N1 is fixed. :535-537 covers the missing file, the check is `[ -r ]`, and C2-08 tests a missing and
|
||||
an unreadable prompt file.
|
||||
- r4 N2 is fixed. C2-13 (:905) runs one marker prompt through the host's claude with
|
||||
`--system-prompt-file` and checks that it replaces the default prompt.
|
||||
|
||||
No new findings. Section 10 step 1 stays on hold per mos-1050. That isn't mine to clear.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Webby review: Case 2 design r6, section 10 step 1 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T19:59Z (from `date -u`). Author: Gosalyn. Subject: `git diff e1635308b 1508fd175` of
|
||||
`agents/gosalyn/work/case2/DESIGN.md` (r5 blob fd5df75ce635, approved in webby-0021, to r6 blob 80893dd4cff1).
|
||||
Static review. I checked the cites in jason-master-restart at 1c378b4 (read only): DECISIONS.md Q183 and
|
||||
projects/mosaic-mvp/MELANIE-HOST-RECON.md:27-39. Both say what r6 says they say.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
The Q183 reading is right. #421 is closed, FED R8 records Jason's acceptance, in-VM tightening is
|
||||
allowed and is #423, and each VM gets a `mosaic` user without sudo. Section 9's isolation paragraph
|
||||
(DESIGN.md:961-965) states the residual as it is now, with the recon cite.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. Step 1 has lost the owner of the root steps on .54. Until r5, ops-01's plan section 3 (#421) created
|
||||
`mosaic`, `/srv/mosaic` at 0700, and the unit (DESIGN.md:947-950 still cites it). That plan stopped. r6
|
||||
step 1 (:1017) says "the install creates the `mosaic` user". But step 2 (:1023) runs the install "as
|
||||
`<user>`", and C2-13 runs as `<user>`. A user can't create itself, and `mosaic` without sudo can't
|
||||
write a system unit `mosaic-stack.service` (:948). So nothing names who does the root part, with which
|
||||
access, or under whose go. The only root paths into .54 today are pve root and `melanie`'s sudo
|
||||
(MELANIE-HOST-RECON.md:31, :35-36), and using either one is an act on Melanie's machine.
|
||||
|
||||
Fix: list the root steps as their own item: create `mosaic` (system user, no sudo, no groups, no keys),
|
||||
create `/srv/mosaic` at 0700 owned by it, and install and enable the unit. Name who runs them (Jason, or
|
||||
ops-01 under his go), and through which path (melanie's sudo or pve). Say that this is step 2's go and not
|
||||
step 1's. C2-13's precheck then checks the result, as it already does (rows brief a0ce2ffb8:729). Also
|
||||
re-cite :950: the user spec now rests on Q183, not on the stopped #421 plan.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. :1018 says the precheck "refuses if it has sudo or any group". A system user still has its own
|
||||
primary group. Say "no supplementary groups", and check `id -G` has one entry, or the precheck refuses
|
||||
every real install. The rows brief (:729) uses the same words, so fix both.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Webby review: Case 2 design r7, sections 9 and 10 and C2-13 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T20:02Z (from `date -u`). Author: Gosalyn. Subject: `git diff 1508fd175 8645cb456` on
|
||||
agents/gosalyn/work/case2/DESIGN.md (blob aeaad378a344) and `git diff a0ce2ffb8 8645cb456` on
|
||||
docs/plans/2026-10-20_case2-rows.md (the rows file arrived in a0ce2ffb8, between r6 and r7). Static
|
||||
review. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
r6 B1 is fixed, and so is N1.
|
||||
- Section 10 step 2 (DESIGN.md:1054-1064) now splits 2a (root: useradd, /srv/mosaic 0700), 2b (the install
|
||||
as `<user>`) and 2c (root: install and enable the unit). It names ops-01 as owner, the localadmin
|
||||
path from #423 step 1, and Jason's go. Step 1 (:1045-1053) is right as written. Section 9 (:973-975)
|
||||
rests the layout on Q183, and the rows brief (:724-725) says the script doesn't create `<user>` or
|
||||
install the unit.
|
||||
- N1. "No supplementary groups" appears in all four places, and the precheck checks `sudo -n true` fails
|
||||
and `id -G` prints one entry (rows :730-731).
|
||||
- Mos's item 3. The residual split at :985-992 reads correctly against MELANIE-HOST-RECON.md:31, :35-36.
|
||||
|
||||
My one blocker is in step 2c and C2-13, not in step 1. I have no objection to the mos-1050 hold on step 1
|
||||
lifting now. Mos decides that.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. Root installs a unit file that the unprivileged user wrote. C2-13's script runs as `mosaic` and
|
||||
writes the `mosaic-stack.service` file (rows :724-725, DESIGN.md :940). 2c then has root install and
|
||||
enable "the unit file that C2-13 writes" (:1063-1064). By 2c, 2b has already run `bootstrap.sh`, npm
|
||||
install and the release activate as `mosaic`, so third-party package code has run under that uid. Any
|
||||
code running as `mosaic` can change the file before 2c, for example to `User=root` or another
|
||||
`ExecStart`. Root then installs a root service. The same holds on every later re-install. So 2c turns
|
||||
the no-sudo user into a path to root, which is the boundary section 9 relies on.
|
||||
|
||||
Fix: 2c must not trust the content. Either root renders the unit from the reviewed template in the
|
||||
release, with `<user>` and the paths as inputs, or root copies the file to
|
||||
/etc/systemd/system/ as root:root 0644 and refuses unless `User=mosaic`, `Group=mosaic`, the
|
||||
`ExecStart` path, and the hardening lines (`ProtectHome` and the rest) match fixed expected values.
|
||||
Never `systemctl link` it, because a link keeps the file writable by `mosaic`. Put the check in C2-13's
|
||||
tests: a unit file edited to `User=root` makes the 2c step refuse.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. 2b runs the install "as `<user>`, by him or his delegate", but `mosaic` has no keys and no login.
|
||||
The only way in is `sudo -u mosaic` from `localadmin`. Say so, so that nobody adds a key to `mosaic`
|
||||
to make 2b work.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Webby review: Case 2 design r8, step 2c and C2-13 (Vikunja #416)
|
||||
|
||||
Webby, 2026-10-20T20:04Z (from `date -u`). Author: Gosalyn. Subject: `git diff 8645cb456 eab6e1a3a` on
|
||||
agents/gosalyn/work/case2/DESIGN.md (blob 5dd7a9c9b4c0) and docs/plans/2026-10-20_case2-rows.md. Static
|
||||
review. I ran nothing.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
r7 B1 is fixed, and so is N1.
|
||||
- `mosaic` writes no unit. In 2c (DESIGN.md:1084-1091, rows :728-736), root renders the unit from a
|
||||
root-owned checkout of the commit Jason's go names, outside /srv/mosaic. It checks `User=`, `Group=`,
|
||||
`ExecStart=` and the hardening lines, writes /etc/systemd/system as root:root 0644, never uses
|
||||
`systemctl link`, refuses a symlink, refuses a drifted installed unit, and reads nothing under
|
||||
`<user>`'s home. The tests (rows :753-759) cover an edit to `User=root`, an edited `ExecStart=`, a
|
||||
removed hardening line, a symlink, and the 0644 write.
|
||||
- Gosalyn's change from my fix is right, and my suggestion was wrong. The release is under /srv/mosaic and
|
||||
`mosaic` can write it, so a template there is the same path to root. The root-owned checkout closes it.
|
||||
- N1. 2b runs as `sudo -u mosaic -H` from localadmin, and `mosaic` gets no key (:1080-1081, rows
|
||||
:724-726). `sudo -u` with a command doesn't need a login shell.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The check covers named lines, not the whole file. A template line such as `ExecStartPre=+...`,
|
||||
`ExecStartPost=!...` or `PermissionsStartOnly=` runs as root even with `User=mosaic`. If it pointed
|
||||
into /srv/mosaic, that is the same path to root through a line the check doesn't read. The template is
|
||||
reviewed, so this is defence in depth. Either compare the whole render to one fixed expected file, or
|
||||
refuse any `Exec*=` value starting with `+` or `!` and any `PermissionsStartOnly`. Add one test case
|
||||
for it. The existing packages/cli/systemd/mosaic-bus.service.in has a single `ExecStart=` (:30), so
|
||||
the new template can follow it.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Webby review: Case 2 design r9 (#416)
|
||||
|
||||
Webby, 2026-10-20T20:48Z (from `date -u`). Author: Gosalyn (gosalyn-0035). Commit 29e85196b on
|
||||
gosalyn/case2-design, DESIGN blob 60a2d6cb751e. Round 8: case2-r8/REVIEW.md, approve on eab6e1a3a.
|
||||
|
||||
Scope: `git diff eab6e1a3a 29e85196b` on DESIGN.md and docs/plans/2026-10-20_case2-rows.md, including
|
||||
13a98bfd0 and 29404f996 as asked. Row A's and row B's briefs (f325657c7) are not part of this round. I
|
||||
read their citations only where the design shares them. This was a static review. I ran nothing.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
Model path (section 9). The citations hold on origin/refactor 088087ac9:
|
||||
- `ENV_ALLOW` (packages/seat/src/session.mjs:83-88) has no `ZAI_API_KEY`, so today the key is dropped.
|
||||
- pi-ai reads `zai` from `ZAI_API_KEY` (env-api-keys.js:89), and `glm-5.3` is a zai model id (see N1 for
|
||||
the file path).
|
||||
- pi's harness sets `countsAccount` to false (pi/index.mjs:42), so the account check at launcher.mjs:296-308
|
||||
never runs for pi. `preflight()` (:26) checks only the binary. `credentialStatus` (launcher.mjs:343) is
|
||||
metadata passed into the launch object (:385), not a refusal. So "a pi launch has no credential check"
|
||||
is true, and the precheck key-file line is the right place to catch a missing key.
|
||||
- `PI_PROVIDER` comes from `session.provider` (pi/index.mjs:57).
|
||||
|
||||
2d. The key file is the path the bus unit already loads (mosaic-bus.service.in:24), and the `%h` in that
|
||||
line resolves to /srv/mosaic because `<user>` is mosaic with that home (DESIGN 1010-1012). The design
|
||||
says plainly that melanie's sudo and her Hermes processes can read it until #423 step 5 (:1042). On
|
||||
Gosalyn's naming question, I'd keep `claude-session.env`. A new name means a second `EnvironmentFile=`
|
||||
line, which adds a line to case2-unit.sh's fixed set and departs from the bus unit. The cost of keeping
|
||||
it is a misleading name. A README line saying the file holds every session provider key, not only
|
||||
Claude's, fixes that, and it can ride row A's README edit.
|
||||
|
||||
C2-13 and the rows doc:
|
||||
- The marker prompt check now runs only for a claude-code creative instance, and a test shows a missing
|
||||
claude passes when there is none. That's right, since Bean and Chet run pi.
|
||||
- The key-file precheck has refusal cases for missing, 0644, symlink and empty, plus a search of the test
|
||||
output for the fixture value.
|
||||
- `EnvironmentFile=` joins the fixed lines, with removed and pointing-elsewhere cases.
|
||||
- The Exec prefix rule covers the whole leading run, with `+` and `-+` cases, as webby-0027 asked.
|
||||
- 13a98bfd0, `--profile factory|library`. `factory` requires the tester and refuses a library project
|
||||
(`profile-library-business`), and `library` refuses the tester. Each direction has a test, so a wrong
|
||||
flag can't reach a tester-on Case 2 install.
|
||||
- 13a98bfd0, node >= 24 and bwrap prechecks. Both have fake-binary refusal cases, and a bwrap refusal
|
||||
prints the userns sysctl and PATH state.
|
||||
- 29404f996, the copy id. The rule matches `copy_root` (scripts/stack-copy.sh:20): 40 lowercase hex and
|
||||
`scripts/mosaic` present. The tests cover short, uppercase, `/`, `..` and missing.
|
||||
|
||||
C2-20. The Hermes name count prints one count per persona file and never the line, number or word, and
|
||||
the test checks the output holds none of the matched words. The list comes from code at df5700ebe, not
|
||||
her profiles, so the script carries no persona text.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The model data file is `dist/providers/data/zai.json`, not `data/zai.json` (DESIGN:1023, and row A's
|
||||
brief). The id `glm-5.3` is there. Fix the path when the file is next touched.
|
||||
|
||||
N2. `copy_root` reads `COPIES` as `${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-stack/copies`
|
||||
(stack-copy.sh:6), while case2-unit.sh fixes `<home>/.local/share/mosaic-stack/copies`. If mosaic's
|
||||
login env ever sets `XDG_DATA_HOME`, copies land elsewhere and the unit refuses. That fails closed, so it
|
||||
doesn't block. The install could check `XDG_DATA_HOME` is unset, or the rows doc could say why it's safe.
|
||||
|
||||
N3. "Sets a nonempty `ZAI_API_KEY`" should parse the file the way systemd does: the last assignment wins,
|
||||
and quotes are stripped. A file with `ZAI_API_KEY=x` then `ZAI_API_KEY=` passes a first-match grep but
|
||||
gives the session an empty key. Add that case to the refusal list.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Webby review: row A brief, ZAI_API_KEY in the session env allowlist
|
||||
|
||||
Webby, 2026-10-20T20:55Z (from `date -u`). Requested by Sage (sage-1477). Brief text: the "Brief: row A"
|
||||
section of docs/plans/2026-10-20_case2-rows.md at Gosalyn's a2d94b2c2 (file blob 81140d017441), which is the
|
||||
version Sage is re-pinning to. Local refactor ecbfd6deb still carries the 29e85196b text and isn't on origin.
|
||||
I read the cited code on origin/refactor. Static review.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
Citations hold on origin/refactor:
|
||||
- `ENV_ALLOW` is session.mjs:83-88, with the provider line at :86 (`OPENAI_API_KEY`, `OPENROUTER_API_KEY`).
|
||||
- `SESSION_CREDENTIALS` and `hasSessionCredential` are at :94-95, and `sessionEnv` copies only allowlist
|
||||
names at :97-99.
|
||||
- launcher.mjs:299 runs the account check only when `countsAccount` is true. pi returns false
|
||||
(pi/index.mjs:42).
|
||||
- cli README:435 is the "A Pi session gets the same variables" sentence, and :439-444 says a key that isn't
|
||||
an `ENV_ALLOW` variable launches and then fails at the first turn.
|
||||
- session.test.mjs:51-59 is the existing allowlist test. Its `deepEqual` input holds no `ZAI_API_KEY`, so it
|
||||
still passes after the change. :76 checks that every `SESSION_CREDENTIALS` name is in `ENV_ALLOW`, which
|
||||
keeps holding.
|
||||
- No test under packages/*/tests or scripts/test-*.sh pins a hash of session.mjs.
|
||||
|
||||
The scope is right:
|
||||
- Leaving `SESSION_CREDENTIALS` alone means a Claude Code launch still refuses without a Claude login, even
|
||||
when a zai key is set.
|
||||
- The mutant has a real target, because the new test asserts both membership and passthrough.
|
||||
- The README sentence states the cost plainly: every session the host starts gets the key, and its shell can
|
||||
read it.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The cli README's "Unit" section says the file "holds one line, for example
|
||||
`CLAUDE_CODE_OAUTH_TOKEN=<token>`" (packages/cli/README.md:711). The brief adds a second sentence there. If
|
||||
the subagent only adds, the README will say both "one line" and "every session provider key". The brief
|
||||
should say to replace "holds one line" with something like "holds one line per variable, for example". This
|
||||
is a doc-only fix. Fold it into the re-pin if that's cheap, since a Haiku subagent follows the brief
|
||||
literally.
|
||||
|
||||
N2. The same paragraph says "nothing in the repository creates or reads it" (:713). That stays true for row
|
||||
A. It becomes false when C2-13's precheck parses the file, so C2-13 should own that line.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Webby review: Case 2 v1 briefs C2-09b, C2-10c and C2-15 (mos-1233), round d1
|
||||
|
||||
Webby, 2026-10-21T22:29Z (from `date -u`). Author: Gosalyn (gosalyn-0122). For mos-1233, Vikunja 464.
|
||||
Candidate: docs/plans/2026-10-20_case2-rows.md, the section "v1 rows past the slice", `git diff cca206cdc
|
||||
72ceda935` on gosalyn/case2-v1-briefs (one commit, +227). These are briefs, so there's no queue record.
|
||||
|
||||
What I checked, without runs: each brief against design 1.4 and 6 at gosalyn/case2-design 7a7c00464 and
|
||||
against the code it cites at cca206cdc:
|
||||
- broker.mjs: `thread.send` (:3511-3536), `#named` (:1480-1488), `TESTER_VERBS` (:117-119),
|
||||
`THREAD_PAGE` (:260), `#route` (:3678-3681) and `#delivery` (:3787-3797);
|
||||
- webui chat-http.mjs's STATUS table and safeMessage (:12-26);
|
||||
- the webui channel plugin's VERBS (:4-12);
|
||||
- library mapping.mjs's hybrid branch and mapping.test.mjs.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
B1. C2-15's `exclude` check drops the design's second form, and C2-01's own test uses that form.
|
||||
- Design 1.4 allows `{"exclude": "<source path or source/type>"}`.
|
||||
- mapping.test.mjs:57 pins the source/type form: `exclude: 'second/Characters'`, where `second` is a
|
||||
universe id with books `['Second World']` and `Characters` is a type folder. mapping.mjs:98-100 accepts
|
||||
it today.
|
||||
- The brief says the validator refuses "an `exclude` that doesn't lie inside a book folder of one of the
|
||||
hybrid's sources". `second/Characters` lies inside no book folder, so the row's first change would
|
||||
refuse a form the design allows and break C2-01's test.
|
||||
- The brief has to keep both forms and say how to tell them apart. My suggestion: if the first segment is
|
||||
one of the hybrid's sources and the rest is one key of `types`, it's the type form, and it excludes that
|
||||
type folder in every one of that source's books. Otherwise it must lie inside a source's book folder. If
|
||||
both readings fit, it refuses. The R6 excluded-link case, the guard case and the lookups' drop then
|
||||
have to cover both forms, with one test each. Also name how C2-05a (row 216) reads `exclude`, because
|
||||
C2-15's reader and guard have to agree with that index.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. Your question, R8's second case. I agree with the reading in principle. A hybrid can't edit a
|
||||
source's file, so "a hybrid file changes it" (design 1.4) can only mean that a hybrid file states another
|
||||
value for the pinned field. Linking to the entity is a fair test of "about that entity". But as written
|
||||
it gives false positives. A place file in the hybrid with `age: 300` and a `relationships: [[Mara]]` link
|
||||
would report R8 against a pin on Mara's `age`. Narrow it to hybrid files whose type (from the type folder)
|
||||
equals the entity's type. Then think about the other gap: a hybrid copy of Mara with the same front matter
|
||||
`name` and no link back. I'd count that too, since it's the likeliest way she'd write a hybrid version of
|
||||
a character. Either way it's report-only, so it doesn't block. Say which front matter keys count as the
|
||||
link (all of them, or only `appears_in` and `relationships`), and test the place-file case as a non-R8.
|
||||
|
||||
N2. C2-15's validator should also refuse a `pin.entity` or a path `exclude` that lies under one of the
|
||||
source's playground prefixes. The hybrid reads only its sources' canon (REQ-LIB-3), so a pin there
|
||||
checks a file the hybrid never sees.
|
||||
|
||||
N3. C2-09b's `room.read` groups by shared `at`, body and sender. Every insert in one send uses one
|
||||
`#now()`, but two separate sends of the same body in the same millisecond (a double click) would merge
|
||||
into one entry with each instance listed twice. Since one send's rows go in one transaction, their
|
||||
`messages.seq` values are consecutive. Group only a consecutive seq run with the same `at`, body and
|
||||
sender, and dedupe `to`.
|
||||
|
||||
N4. C2-09b resolves a tag through `#named` or as an instance key. A tag can match one instance's display
|
||||
name and another instance's key. The brief should rule which wins, or refuse it as ambiguous, with a test.
|
||||
|
||||
N5. C2-10c's line "maps `unknown-addressee` and `room-thread-name-taken` to 409 and `room-not-open` to 409"
|
||||
reads as if two statuses were meant. If all three are 409, say "maps all three to 409".
|
||||
|
||||
N6. Pinning cost, as you said. Every briefed Case 2 row pins this file's blob, so 72ceda935 flags them all
|
||||
until Sage re-pins. Landing both of Sage's edits under one re-pin makes sense.
|
||||
|
||||
## Checked and fine
|
||||
|
||||
- C2-09b reuses what `thread.send` writes (:3511-3536): `#checkMessaging`, one message row, `#route`
|
||||
and one `human.input`. `#route` and `#delivery` only insert rows (:3678-3681, :3787-3797), so one
|
||||
dispatch transaction does undo a partial send. Leaving the room verbs out of `TESTER_VERBS` makes a
|
||||
tester refuse as for any other verb, and the gate's grep names tester-writes.test.mjs.
|
||||
- `room.open`'s refusal on a same-name thread elsewhere fits the per-project unique name
|
||||
(schema.sql:249), and "no schema change" holds.
|
||||
- C2-10c keeps the broker as the authority for addressing. The parity test reuses C2-09b's table. The
|
||||
plugin test checks that other verbs still refuse, which matters because VERBS (:4-12) is the webui
|
||||
channel's allowlist.
|
||||
- C2-15 names Opus and Filbert, and lists its after rows and the M9 refusal test. The out-of-scope line
|
||||
that sends draft sources to C2-01c matches the playground ruling.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Webby review: Case 2 v1 briefs C2-09b, C2-10c and C2-15 (mos-1233), round d2
|
||||
|
||||
Webby, 2026-10-21T22:34Z (from `date -u`). Author: Gosalyn (gosalyn-0125). Candidate: `git diff 72ceda935
|
||||
99b1e0859` on gosalyn/case2-v1-briefs, one commit. The rows file blob is c45816509, as Gosalyn said. I
|
||||
reviewed it against my d1 (REVIEW-d1.md, 35130c26b). These are briefs, so there's no queue record.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- B1. C2-15 keeps both exclude forms of design 1.4.
|
||||
- The type form is a first segment that is one of the hybrid's sources plus exactly one `types` key.
|
||||
`second/Characters` fits it: `second` is a universe id and `Characters` is a `types` key in
|
||||
mapping.test.mjs.
|
||||
- Anything else is the path form, inside or equal to a source book folder.
|
||||
- When both readings fit, it refuses.
|
||||
- One `excluded(mapping, hybrid, path)` helper in scope.mjs serves C2-05a's index, R6 and the guard.
|
||||
If C2-05a lands first, this row replaces C2-05a's own read, so all three share one reading.
|
||||
- Each form gets a case on the lookups, R6 and the guard, and the test at :57 still checks.
|
||||
- N1. R8 now counts a hybrid version of the entity: the same type from `classify`, and the same front
|
||||
matter `name` (or the same file name when either file has none). A link alone doesn't count. That's
|
||||
better than my narrowing: the hybrid Tom who links Mara and has his own `age` would have passed a
|
||||
type-only rule. The tests cover the no-link version as R8, and the place file and the other character
|
||||
as non-R8.
|
||||
- N2. A pin entity or path exclude under a source playground prefix refuses.
|
||||
- N3. `room.read` groups only a run of consecutive seqs from the human with the same `at` and body. The
|
||||
double-click case has a test.
|
||||
- N4. A tag that is one member's name and another member's key refuses `addressee-ambiguous` and
|
||||
inserts nothing. A tag that names one member both ways resolves to that member. The new code is on the
|
||||
line for Sage.
|
||||
- N5. C2-10c maps all four room codes to 409.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. R8's version rule says "a hybrid file" without a scope. A hybrid can have playground prefixes, and
|
||||
a brainstorm file there with the same name and another value would report R8. It's report-only, so the
|
||||
cost is noise in `library.status`, not a wrong lookup. If Melanie should be free to try other values
|
||||
in the playground, the builder can limit the version check to hybrid canon files and add a test for a
|
||||
playground version as non-R8. That's the builder's and Filbert's call, not a change to this brief.
|
||||
@@ -0,0 +1,360 @@
|
||||
# Webby static look: row 191 Console layout fix (sage-2364, mos-1248 condition 3)
|
||||
|
||||
Asked by Sage at 2026-10-22T02:50:06Z. Git reads only, no runs. Base: Rocko's 0f8235277
|
||||
(origin/rocko/row191-stoprun-ddfb-20261022).
|
||||
|
||||
## The failure, read from the base
|
||||
|
||||
- shell.test.mjs:380 asserts `scrollHeight === innerHeight` on the Board at 1440x1000. Rocko's gate
|
||||
reports 1062 against 1000.
|
||||
- index.html loads processing.css after shell.css (:4 and :6), and both use `.frame` (0,1,0). So
|
||||
processing.css:14 `min-height:0` wins over shell.css:135 `min-height:calc(100vh - var(--bar-h) -
|
||||
var(--foot-h))` at 760 px and wider. The frame has no floor of its own any more.
|
||||
- processing.css:12 makes body a flex column with `min-height:100vh`. Its in-flow children are the
|
||||
cmdbar (53 px, `--bar-h`), #processing-controls, the hidden banner, .frame, and the sticky footer (33 px,
|
||||
`--foot-h`). The tree is `position:fixed` (processing.css:16), so it takes no height.
|
||||
- Nothing caps the body at 100vh, so the page is cmdbar + bar + frame content + footer whenever that sum
|
||||
is over 1000. 1062 - 1000 = 62, and 62 is the bar's height from Sage's report. 1000 - 53 - 33 = 914 is
|
||||
the frame height shell.css:135 used to set. So the Board's content is at most 914 px and over 852 px.
|
||||
Before 191 it fit, and the bar now takes its share.
|
||||
- That means no rule on the frame alone can fix it while the window scrolls. The content needs 914 px,
|
||||
and the bar leaves 852. A fix has to do one of three things:
|
||||
1. take the bar out of flow, for example sticky or fixed over the frame's top, and pad the content by
|
||||
the bar's height only where the tree already sits;
|
||||
2. scroll the frame inside a body capped at 100vh, which changes :382-385 because the window would
|
||||
no longer scroll at 420 px;
|
||||
3. make the Board shorter.
|
||||
I'll judge Rocko's diff against these.
|
||||
|
||||
## Checks for Rocko's diff
|
||||
|
||||
1. **Layout.** At 1440x1000 the bar's box lies inside 0..1000, and scrollHeight equals innerHeight
|
||||
because the page fits, not because the window can't scroll. The same holds at 760-959 px (nav 200 px,
|
||||
shell.css:142-151) and at 1900 px and up (nav 280/320 px).
|
||||
2. **Tall page.** At 1440x420, scrolled to the end, :382-385 still see `scrollHeight > innerHeight`,
|
||||
with the tree between the cmdbar bottom and the footer top. The bar must not cover the tree or
|
||||
the first Board row.
|
||||
3. **:380 unchanged.** `git diff 0f8235277 <fix> -- packages/webui/tests/shell.test.mjs` adds lines and
|
||||
changes none at :373-385.
|
||||
4. **New assertion.** It must fail when the fix is reverted. For that it needs the bar's own box,
|
||||
`#processing-controls` getBoundingClientRect, against innerHeight and the cmdbar, at the size that
|
||||
failed. A check on scrollHeight alone at a size where the Board fits anyway would pass with the bug.
|
||||
5. **Mutant.** It must revert the CSS fix and nothing else, and it must name the killing test.
|
||||
|
||||
## Verdict on 5cd32f4c1 (rocko 02:57:02Z): CHANGES, B1
|
||||
|
||||
Read at 2026-10-22T02:59Z (from `date -u`). It's one commit on 0f8235277: processing.css (+12/-2), shell.test.mjs (+11/-0)
|
||||
and BUILD.md. Nothing has run, mine included.
|
||||
|
||||
**B1. As written, the CSS delta doesn't change the height of the page that failed.**
|
||||
(processing.css:12-24 at 5cd32f4c1)
|
||||
- Old body: a flex column with `min-height:100vh`, and the frame `flex:1; min-height:0`. The body's
|
||||
height is auto, so the frame's 0% basis resolves to its content, and the body comes to
|
||||
max(100vh, cmdbar + bar + C + footer), with C the frame's in-flow content height.
|
||||
- New body: a grid with `min-height:100vh` and the frame on row 4 as `minmax(min-content,1fr)`.
|
||||
min-content of the frame is the same C. The track can't go below it, and 1fr only spends space that is
|
||||
left over. So the body comes to max(100vh, cmdbar + bar + C + footer) again.
|
||||
- The two give the same number. For 1062 to come out of the old one, C has to be over 852
|
||||
(1000 - 53 - 62 - 33). In that case the new one gives 1062 too, and :380 fails exactly as before.
|
||||
1062 is exactly 1000 plus the bar, so C is likely 914 = `100vh - var(--bar-h) - var(--foot-h)`. That
|
||||
points at a box in the frame's flow that is sized to the viewport, and the delta doesn't touch it.
|
||||
I couldn't find which box it is from the stylesheets. processing.css:22 `min-height:0` does beat
|
||||
shell.css:135 on load order, and `.frame > .tree` is fixed. So it needs a measurement, not more CSS.
|
||||
- **Before the gate.** On a slot, run one probe at 1440x1000 on the Board (the shell.test fixture) and
|
||||
report:
|
||||
- the `.frame` rect height and `#main` scrollHeight;
|
||||
- the rect of every in-flow child of body;
|
||||
- the computed `min-height` and `position` of `.frame` and `.tree`.
|
||||
|
||||
Run it on 0f8235277 and on 5cd32f4c1. If the two match at 1062, this delta isn't the fix.
|
||||
Then there are three ways out:
|
||||
1. remove the box sized to 100vh - 86;
|
||||
2. take the bar out of the frame's height budget, for example by subtracting a `--processing-h`
|
||||
from the shell's calc() values, or by placing the bar in a row the shell already counts;
|
||||
3. if the Board really needs 914 px, it isn't a short page with the bar on it. Changing :374's
|
||||
viewport would be a test change for Sage and Mos to rule on, not for Rocko or me.
|
||||
- Rocko's BUILD.md says a second red stops the window and goes to Mos. That's one more reason not to
|
||||
open the gate on an unmeasured fix.
|
||||
|
||||
**What holds.**
|
||||
- **:380 is unchanged.** The diff removes no line in shell.test.mjs, and :373-380 and :392-397 (the old
|
||||
:381-386) are byte-identical.
|
||||
- **The new assertions (:381-391) help.** The visible and positive-height check catches a fix that hides
|
||||
the bar, which :380 alone would pass. The check that the bar sits under the cmdbar and inside the
|
||||
viewport is the bar's own box, as I asked. :391 repeats :380, which runs first, so it can never be
|
||||
the one that fails. N1: drop it, or leave it as documentation.
|
||||
- **KS59** reverts processing.css to 0f8235277's bytes. :380 would kill it, not the new assertions. That's
|
||||
fine, since a revert is the failing state. I'd add a second mutant that sets `#processing-controls`
|
||||
to `display:none` with the fix in place, so :388 has a kill of its own.
|
||||
- **The grid body's auto-placed children.** a.skip is `position:absolute` (app.css:24), #announce is
|
||||
.sr-only absolute, #toast is fixed, the dialog is closed, and a hidden banner leaves row 3 at 0. None
|
||||
of them takes a row, so the explicit rows hold. Sticky still works for the cmdbar and the footer as
|
||||
grid items. At 1440x420 the min-content row lets a long page grow, so :394-396 keep their premise.
|
||||
At 760-959 px the same rows apply with the 200 px nav. I see no regression there, but none of
|
||||
this matters until B1 is settled.
|
||||
|
||||
## Trace for sage-2368, and a correction to B1
|
||||
|
||||
Read at 2026-10-22T03:00Z (from `date -u`), git only.
|
||||
|
||||
**Correction.** Above, I treated 62 as the bar's height and guessed at a box in the frame sized to
|
||||
100vh - 86. Both were inferences, and the second is wrong. 62 is only the overflow:
|
||||
53 + barH + C + 33 - 1000. Neither the gate log nor the diff gives barH.
|
||||
|
||||
**What set the frame's height on the 1440x1000 Board under 0f8235277.** The frame's only in-flow child
|
||||
is #main, the Board's own content.
|
||||
- body children (index.html): a.skip, header.cmdbar, #processing-controls, #processing-banner (hidden),
|
||||
.frame, footer.foot, #announce, #toast, dialog#cmdk-dialog.
|
||||
- .frame children: nav.tree, main#main.content, aside#inspector (hidden).
|
||||
- **nav.tree.** processing.css:16 `.frame > .tree { position:fixed }` is (0,2,0). It beats `.tree`
|
||||
sticky at console.css:14 and shell.css:144, and static at console.css:39. So the tree is out of flow.
|
||||
- **aside#inspector.** shell.test opens it at :246 and closes it with Escape at :248, and :374 opens no
|
||||
session. app.js:88 sets `hidden = !selected`, and close() (app.js:402) clears `selected`. No
|
||||
.inspector rule sets `display`, so [hidden] holds and the inspector takes no space.
|
||||
- **min-height.** processing.css:14 `min-height:0` beats shell.css:135 and console.css:12 on load order
|
||||
(index.html:4 then :6), as Sage says. Rocko's cause, shell.css:135, isn't the cause.
|
||||
- That leaves #main's content C. The old page was max(1000, 86 + barH + C). Before 191 there was no bar,
|
||||
and shell.css:135 lifted the frame to 914, so the page fit whenever C <= 914. It went to 1062 because
|
||||
86 + barH + C > 1000: the Board's content and the bar together don't fit in 1000 px. shell.test:373
|
||||
already says the Board grew at row 95 ("the short page is taller").
|
||||
|
||||
**Does 5cd32f4c1 remove it?** No. Row 4 is `minmax(min-content,1fr)`, and min-content is the same C, so the
|
||||
page is again max(1000, 86 + barH + C) = 1062. B1 stands, with this cause.
|
||||
- The probe I asked for still settles it. It now only needs the `#main` height and the
|
||||
`#processing-controls` height at 1440x1000, which should sum with 86 to 1062.
|
||||
- No grid or flex rule can fix it while the window scrolls and the bar takes vertical space. The ways out
|
||||
change design or test:
|
||||
1. put the bar where it costs no page height, such as inside the fixed nav column or the cmdbar row;
|
||||
2. make the Board shorter;
|
||||
3. rule that the Board at 1000 px is no longer a short page, which means changing :374 or :380.
|
||||
|
||||
These are calls for Sage and Mos, not something to fix in CSS.
|
||||
|
||||
**Small checks.**
|
||||
- **No implicit row 6.** a.skip is `position:absolute` (app.css:24), and #announce is .sr-only absolute
|
||||
(app.css:23, shell.css:139). #toast is fixed (shell.css:104). The dialog is display:none while closed,
|
||||
and in the top layer while open. Absolutely positioned and top-layer boxes aren't grid items, so none
|
||||
of them adds a row. The hidden banner leaves row 3 at 0.
|
||||
- **760-959 px behaves as before.** console.css:37-38 drops the frame to one column at <=959. shell.css:143
|
||||
(760-959) restores `200px minmax(0,1fr)` and loads later with the same specificity, so
|
||||
`.frame > .content { grid-column:2 }` still has a column 2. The tree stays fixed at
|
||||
`--processing-nav:200px` (processing.css:28). 5cd32f4c1 changes none of that, only the body rows.
|
||||
|
||||
## Structure ruling for sage-2382 and Rocko 03:04:20Z
|
||||
|
||||
Read at 2026-10-22T03:06Z (from `date -u`), git only, on 5cd32f4c1. No runs.
|
||||
|
||||
**Ruling: (i), the labeled region as the first child of nav.tree, with conditions R1-R6. Drop sticky.**
|
||||
|
||||
Why (i) and not (ii).
|
||||
- **:380 edges.** In (i) nav.tree stays the same element, class and fixed box (processing.css:26,
|
||||
shell.css:136). A child can't move the nav's rect, so :396 and the 420-tall check hold as before.
|
||||
In (ii) the nav keeps its box too, but the controls sit over it.
|
||||
- **Fragility.** The controls have no fixed height. #processing-receipt is aria-live text that
|
||||
changes, and #processing-unavailable wraps to several lines at 200 or 240 px. (ii) has to reserve
|
||||
that space without a measurement. A fixed reservation goes wrong as soon as the receipt changes or the
|
||||
text zooms, and then the overlay covers nav links. Subgrid plus a wrapper is a larger change than the
|
||||
fix needs. (i) gets the real auto height from normal flow.
|
||||
- **Landmark.** ARIA allows a region inside navigation, and axe's top-level rules cover only
|
||||
banner, main, contentinfo and complementary. A landmark list then shows "Instance processing" under
|
||||
"Console". That's a cost, since Stop isn't navigation, but the name and role still say what it is.
|
||||
Tab order is unchanged: cmdbar, then Stop, then the nav links, the same as the body-child layout
|
||||
today. I accept the nesting for this row.
|
||||
- **Height.** With the controls out of body flow, the page is max(innerHeight, 53 + C + 33), C being
|
||||
#main's content. That's the pre-191 arithmetic, and it fits whenever C <= 914 at 1000 tall. If the
|
||||
Board's C is over 914 on its own, no layout fixes :380. The debug run settles that.
|
||||
|
||||
Why no sticky.
|
||||
- nav.tree scrolls itself (console.css:14 `overflow:auto`). A sticky region at its top covers a
|
||||
focused nav link whenever the nav is scrolled and the user tabs back up. That's WCAG 2.4.11 (focus
|
||||
not obscured), and nothing sets scroll-padding to stop it.
|
||||
- At 1440x420 the nav is 334 px tall (420 - 53 - 33). A sticky region with the note shown can take
|
||||
over half of it.
|
||||
- Without sticky, Stop is the nav's first content. It's in view at the nav's scroll origin, and
|
||||
focus scrolls it into view.
|
||||
|
||||
Conditions.
|
||||
- **R1. Markup.** Move the existing div as is: id, class, role=region and aria-label "Instance
|
||||
processing". It goes before `<h2 class="tree-title">Console</h2>` in index.html:22.
|
||||
#processing-state keeps role=status, and the receipt keeps aria-live. #processing-banner stays
|
||||
where it is.
|
||||
- **R2. Descendant rules.** As a child of .tree, the controls match rules written for nav content:
|
||||
- console.css:21 `.tree .btn{margin-top:auto}` hits Stop and Resume;
|
||||
- console.css:40 `.tree .btn{margin:0;order:1}` at <=959 px. The region is `display:flex`
|
||||
(processing.css:1), so order:1 moves Stop and Resume after the state text, the note and the
|
||||
receipt. The visual order then differs from the DOM order at 400 px;
|
||||
- live.css:5 `.tree .small{margin-top:auto}`, live.css:10 `.tree .small{flex-basis:100%;margin:0}`
|
||||
and shell.css:147 `.tree .small{flex-basis:auto;margin-top:auto}` hit the note and the receipt.
|
||||
|
||||
Add rules that put them back, with `.tree .processing-controls .btn` and `.small` at (0,3,0). Don't
|
||||
edit the shell rules. The new test asserts that Stop's computed `order` is 0 at 400 px, and a mutant
|
||||
that removes the reset must fail it.
|
||||
- **R3. Below 760 px.** .tree is a wrapping flex row (console.css:39). The region needs
|
||||
`flex-basis:100%`, or it shares a line with the "Console" title (`flex:1`, console.css:40). Then
|
||||
Stop sits above the title and the Projects list. Test at 400 px that Stop's rect top is above
|
||||
#projects' rect top, and that Stop is visible.
|
||||
- **R4. Remove dead layout.** Drop the body grid and the `.processing-controls` grid-row and
|
||||
margin-left rules from 5cd32f4c1. They apply to nothing once the controls are in the nav. Keep
|
||||
`.frame > .tree { position:fixed }` and the `--processing-nav` widths. The `.frame`
|
||||
`min-height:0` that beats shell.css:135 then goes back to what it did on 0f8235277. That's fine,
|
||||
because the frame's height is C either way.
|
||||
- **R5. Tests.**
|
||||
- shell.test :373-380 and :392-397 stay byte-identical.
|
||||
- :381-390 still hold. The bar's top sits under the cmdbar and its bottom inside the viewport at
|
||||
1440x1000. Add a check that the bar's rect lies inside the tree's rect.
|
||||
- :67 becomes the mos-1250 text (sage-2384): not inside #main or aside.inspector, and before #main
|
||||
in source. The bar is in nav.tree, which comes before #main, so it passes. The move-into-#main
|
||||
mutant must fail it.
|
||||
- Every shipped-strings assertion is unchanged.
|
||||
- My N2 still applies: a `display:none` mutant has to have :388 as its own kill.
|
||||
- **R6. Debug run (Sage's slot).** Report the height of #processing-controls and nav.tree at
|
||||
1440x1000, 1440x420 and 800x420, and #main's height at 1440x1000. If the region is over half the
|
||||
nav at 1440x420, tell me before the window. It isn't a blocker, but it's a usability call for
|
||||
Sage.
|
||||
|
||||
Not a condition. When the halt banner is shown it's still a body row, so a halted Board can scroll at
|
||||
1000 tall. :380 runs with it hidden. I'd file it as a follow-up and not widen this row.
|
||||
|
||||
## Sticky versus sage-2372 (Rocko 03:06:49Z)
|
||||
|
||||
Read at 2026-10-22T03:08Z (from `date -u`), git only. No runs.
|
||||
|
||||
**Ruling: the separate scroller. The controls don't scroll and the links scroll under them inside the
|
||||
same nav.tree box.** It meets both sage-2372 (the controls never scroll out) and 2.4.11 (nothing overlays
|
||||
a focused link). The controls aren't positioned over the scroller. They take their own flex row above it.
|
||||
|
||||
Conditions S1-S5, on top of R1-R6 (R1's "before the Console h2" still holds):
|
||||
- **S1. Markup.** nav.tree's children become the controls region, then one plain div with no role or
|
||||
label, for example `.tree-scroll`. That div holds the existing children in their existing order: the
|
||||
Console h2, #sections, #board-tree and the rest. Ids and classes don't change. No test or script
|
||||
reads nav children by position. Only ids are used: shell.test :272/:280/:282 #sections,
|
||||
board-held.test :110 #board-tree.
|
||||
- **S2. 760 px and up.** nav.tree keeps its fixed box (top --bar-h, height 100vh - bar - foot) and
|
||||
goes to `overflow:hidden`. The controls are `flex:none`. `.tree-scroll` is
|
||||
`flex:1 1 auto; min-height:0; overflow:auto`, and it repeats console.css:14's column flex and
|
||||
`gap:10px`. Then `.tree .btn{margin-top:auto}` (console.css:21) and `.tree .small{margin-top:auto}`
|
||||
(live.css:5, shell.css:147) still push the nav's own button and note to the bottom. The same applies
|
||||
at 760-959 under shell.css:144.
|
||||
- **S3. Below 760 px.** `.tree-scroll{display:contents}`, so its children stay flex items of the
|
||||
wrapping row (console.css:39-40, live.css:10). The `order:1`/`order:2` layout and shell :280 "one
|
||||
row" keep working without edits. The region gets flex-basis:100% (R3), and nav.tree stays static
|
||||
with no scroll.
|
||||
- **S4. Tests.** Everything in R5 still applies. Add a check at 1440x420: scroll `.tree-scroll` to its
|
||||
end, and Stop's rect is still inside the viewport, under the cmdbar. A mutant that sets
|
||||
`.tree-scroll{overflow:visible}` and nav.tree back to `overflow:auto` must fail it.
|
||||
- **S5. R6 still applies.** If the region is over half of nav.tree at 1440x420, the scroller gets
|
||||
small. That's Sage's usability call before the window.
|
||||
|
||||
## webby-0107 withdrawn (sage-2386)
|
||||
|
||||
Read at 2026-10-22T03:09Z (from `date -u`). Sage's sage-2386 (03:07:50Z) crossed with webby-0107. It accepts
|
||||
non-sticky, withdraws the sage-2372 long-nav visibility requirement and rules out a separate links
|
||||
scroller. The scroller existed only to meet that requirement, so S1-S5 are withdrawn. R1-R6 stand as
|
||||
written, with ordinary nav.tree overflow:auto. Under R6, I still want the region and nav heights at
|
||||
1440x420 on the debug run.
|
||||
|
||||
## Static look at da4334806 (rocko 03:10:31Z): no blockers
|
||||
|
||||
Read at 2026-10-22T03:12Z (from `date -u`), git only. One commit on 5cd32f4c1 touching index.html,
|
||||
processing.css, three test files and BUILD.md. Nothing has run. Manifest digest 245dceb1 (88 paths), as
|
||||
Rocko reported. I haven't recomputed it, so that's for the manifest round.
|
||||
|
||||
**R1 markup.** The region div moves byte-for-byte to nav.tree's first child, before the Console h2.
|
||||
Its id, class, role=region, aria-label, role=status and aria-live are all unchanged. The banner keeps its
|
||||
place in body, before .frame. processing.js is unchanged.
|
||||
|
||||
**R2 descendant rules.** `.tree .processing-controls .btn {margin:0; order:0}` and `.small {margin:0;
|
||||
flex-basis:100%}` are (0,3,0) and sit outside any media query, so they beat console.css:21/:40, live.css:5/:10 and shell.css:147 (all (0,2,0))
|
||||
at every width. The base `.btn` (shared/app.css:60) has no margin, so `margin:0` changes nothing on its
|
||||
own. `.processing-controls p {margin:0}` and the note/receipt `flex-basis:100%` already held, so the
|
||||
`.small` reset only cancels the tree rules. console.css:21 `justify-content:center` still reaches the
|
||||
buttons. That's harmless. No other `.tree` or `nav` descendant rule matches the controls.
|
||||
|
||||
**R3 below 760.** `.tree > .processing-controls {flex-basis:100%}` at <=759 puts the region on its own
|
||||
line ahead of the Console title. The new browser test checks Stop's top against #projects' top at 400 px.
|
||||
|
||||
**R4.** The body grid, the grid-row rules, the controls' margin-left and `.frame {min-height:0}` are all
|
||||
gone. With no body rule left in processing.css, shell.css:135 lifts the frame to 100vh - 86 again.
|
||||
`.frame > .tree {position:fixed}`, the content column and the nav widths stay.
|
||||
|
||||
**Height.** The body is block flow again: cmdbar 53, then the hidden banner at 0, then
|
||||
.frame = max(914, C), then the footer 33. So the page is max(1000, 86 + C), with no bar term. #main's
|
||||
content doesn't change in 191, so C is the pre-191 value that passed :380. The debug run confirms it.
|
||||
|
||||
**R5 tests.**
|
||||
- shell.test is additions only, at :389-399. :373-388 and every later old line are byte-identical, :380
|
||||
included. The new check puts the bar inside `.tree`'s rect, and the diagnostic logs heights.
|
||||
- processing-ui :67 has the mos-1250 text, quoted old and new in BUILD.md. `<main id="main"` and
|
||||
`<aside class="inspector"` match the literal markup, and there's exactly one `</aside>`. KS61 (into
|
||||
#main) moves controlsAt past mainAt, so it fails. If the aside markup is ever renamed, inspectorAt is
|
||||
-1, and `indexOf('</aside>', -1)` searches from 0. The check then fails closed, not open.
|
||||
- processing-browser: the new test covers 400x1000, 1440x1000, 1440x420 and 800x420. Order is
|
||||
checked at 800 and 400, inside the <=959 band where console.css:40 applies order:1, so KS62 is killed
|
||||
there. Visibility and the cmdbar/viewport bounds are checked on load.
|
||||
- Mutants: KS59 is a full revert of HTML and CSS, KS60 hides the controls (N2 met), KS61 moves them into
|
||||
#main, KS62 drops the order reset. The total is 77.
|
||||
|
||||
**sage-2378 checks.**
|
||||
1. Ids, role and label are kept.
|
||||
2. Nested in the nav landmark, which I accepted in webby-0106.
|
||||
3. Stop is visible at 400 and 1440x1000, and above Projects at 400.
|
||||
4. Every existing assertion is unchanged, other than :67 under mos-1250, and the shipped strings are
|
||||
untouched.
|
||||
5. The height arithmetic is above.
|
||||
|
||||
**Notes, none blocking.**
|
||||
- N1 (carried): :402 (the old :391) still repeats :380.
|
||||
- N3: `focus()` then activeElement only proves Stop is focusable and enabled when that line runs. It
|
||||
doesn't prove Tab order. Stop is `disabled` in the markup until processing.js reads the state. If the
|
||||
fixture leaves it disabled, this line fails on the run, so it's a fixture question, not a test hole.
|
||||
DOM order already gives cmdbar, Stop, nav links.
|
||||
- N4: R6 reporting is in place (ROW191_LAYOUT lines). Sage's half-nav call at 1440x420 waits on those
|
||||
numbers.
|
||||
|
||||
### Addendum for sage-2392: full 0f8235277..da4334806 range, Sage's two items, KS59-KS62
|
||||
|
||||
Read at 2026-10-22T03:13Z (from `date -u`), git only.
|
||||
- **Range.** `git diff --stat 0f8235277 da4334806 -- packages/` has five files: index.html 8, processing.css
|
||||
14, processing-browser +27, processing-ui +5/-1, and shell.test +22/-0 (0 removed lines). The body flex,
|
||||
the controls' margin-left and `.frame{flex:1;min-height:0}` from 0f8235277 are all gone, as is
|
||||
5cd32f4c1's grid. The verdict above holds for the whole range.
|
||||
- **Item 1, flex containers below 760.** .tree is `display:flex` at every width (console.css:14), and
|
||||
it's a wrapping row at <=959 (console.css:39). shell.css:158-171 (<=759) doesn't touch .tree's display.
|
||||
The region is `display:flex; flex-wrap:wrap` at every width (processing.css:1), and nothing overrides
|
||||
that. So `.tree > .processing-controls{flex-basis:100%}` works in the tree's row, and the `.small`
|
||||
flex-basis works in the region's row.
|
||||
- **Item 2, order.** `.tree .processing-controls .btn` is (0,3,0) against console.css:40's `.tree .btn`
|
||||
at (0,2,0). A media query adds no specificity, so the reset wins whatever the load order.
|
||||
- **KS59-KS62**, read from all-mutants.json in Rocko's packet (77 entries):
|
||||
- **KS59.** The HTML edit plus an extra_edits entry on processing.css restore 0f8235277's exact markup
|
||||
and CSS. That's the state the gate failed at 1062, so :380 kills it.
|
||||
- **KS60.** Appends `#processing-controls{display:none}` at (1,0,0), which beats the class rule. :380
|
||||
can't see it any more, so the visibility check at shell :388 is its kill. N2 is met.
|
||||
- **KS61.** Moves the region to the head of #main, so processing-ui :67's controlsAt < mainAt fails.
|
||||
- **KS62.** Deletes the `.btn` reset line, and the order assertion at 800 and 400 kills it.
|
||||
- All four are unrun.
|
||||
|
||||
N3 closed (Rocko 03:13:09Z, checked at 2026-10-22T03:14Z). processing.js:41-52 fetches /api/config with the
|
||||
state, sets `canWrite` (:46) and then calls draw(). The status text reads "Processing is enabled." only
|
||||
when that draw runs with a snapshot, and :29 sets `halt.disabled` in the same call. The test's setup
|
||||
waits for "enabled" (processing-browser:39) with config `send:true` (:27). So Stop is enabled before
|
||||
focus() runs.
|
||||
|
||||
## Debug run on da4334806 (Rocko, 03:14:37-03:14:53Z)
|
||||
|
||||
Read at 2026-10-22T03:16Z (from `date -u`), from Rocko's debug-da4334806 logs. I ran nothing.
|
||||
- **Counts.** shell.test reports `# tests 5 / pass 5 / fail 0 / skipped 0`, and processing-browser
|
||||
reports 9/9/0/0. The total is 14/0/0, as Rocko said.
|
||||
- **ROW191_LAYOUT matches the report.**
|
||||
- Writable controls are 127 px, and the tree is 914 at 1000 tall and 334 at 420.
|
||||
- Read-only controls are 212.5 at 1440x1000.
|
||||
- At 400x1000, Stop's top is 181.5, the cmdbar bottom is 129 and #projects starts at 352.6, with
|
||||
order 0.
|
||||
- **R6.** The writable controls are 127 of 334 at 1440x420 (38%), under half. The read-only ones were
|
||||
measured only at 1000 tall, but their height depends on width, not viewport height (wrapping at
|
||||
240 px). So at 1440x420 they would be 212.5 of 334, about 64%. That's over half.
|
||||
- **My read for Sage's call.** It's acceptable. The region isn't sticky and scrolls away with the nav, so
|
||||
the 64% applies only at the nav's scroll origin, and every nav link is still one scroll away.
|
||||
#projects already began below the fold at 1440x420 before 191: 636.6 - 127 = 509.6 is past 420. A
|
||||
420-tall desktop window isn't the normal case. If Sage wants it smaller, the fix is a shorter
|
||||
read-only note, not layout.
|
||||
@@ -0,0 +1,103 @@
|
||||
# Webby review: row 192 r1, Q175 phase 1 SSO (#1690)
|
||||
|
||||
Webby, 2026-10-20T19:49Z (from `date -u`). Author: Launchpad. Candidate 83b32d8d8 on launchpad/row192, base
|
||||
53bb59462. Manifest `candidate-manifest.sha256` digest 3ad2a73d...badab, verified: all 18 path hashes match
|
||||
the candidate, the path list matches `git diff --stat 53bb59462 83b32d8d8`, and none of the 18 paths changed
|
||||
from the base to origin/refactor (aa1bd209e). Design: `agents/sage/work/q175-sso/DESIGN.md`. Static review
|
||||
plus one node probe of `returnPath` (below). I ran no suite and no mutant. I did not read
|
||||
/mnt/storage/scratch/launchpad-r192.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
The build follows the design closely. I checked these against the design and found them right:
|
||||
|
||||
- Callback steps 2-7 (sso.mjs:232-285):
|
||||
- The login record is taken and deleted before any check, and the provider comes from the record.
|
||||
- `validateApplicationLevelSignature` runs on the same Response, and its throw ends the callback.
|
||||
- The header alg is checked against `algs`, `iss` is compared raw, and `sub` is looked up in the subjects Map.
|
||||
- The `subject-unknown` line carries only the provider, iss and sub, JSON-quoted and capped at 255.
|
||||
- Sessions:
|
||||
- The store is keyed by sha256 and the cookie strings are exact.
|
||||
- The caps are 64 sessions and 32 logins.
|
||||
- Idle and absolute expiry are server-side.
|
||||
- `touch` happens only on a POST or a GET outside `/api/` (serve.mjs, the `sso.session` call).
|
||||
- Host (F4). Under `--sso`, anything but the public Host is 403, except GET `/healthz`. `/auth/*` routes go
|
||||
to `sso.handle` before the session check, and a request without a session gets 401 on `/api/` and a 303
|
||||
on a page.
|
||||
- Logout needs the exact Origin and `Sec-Fetch-Site: same-origin`, and a refused logout keeps the session.
|
||||
- Discovery (Q-B, sso.mjs:92-121, :151-182):
|
||||
- Unreachable and Refused are split as the design says.
|
||||
- A Refused provider stops the start. An unreachable one retries with doubling, capped at 300 s.
|
||||
- A Refused result on a retry stops the retries.
|
||||
- `readSlot` (sso.mjs:33-61):
|
||||
- lstat refuses a symlink, then the open uses O_NOFOLLOW, then fstat checks the file type, the uid and the
|
||||
mode against 0o077.
|
||||
- The realpath checks cover the repo, the checkout and the copies root.
|
||||
- The default copies root matches `stack-copy.sh:6`. I read the owner branch. It is right, and as the
|
||||
design says, no test covers it.
|
||||
- Tester listener (section 6, F8):
|
||||
- `startServer` throws on `tester` with `sso`.
|
||||
- Under `--sso` the tester listener refuses Authorization, either sign-in cookie and any `x-authentik-*`
|
||||
header, and `/auth/login` is 404 there.
|
||||
- The tester writer is unchanged.
|
||||
- The loader (C1) checks shape only, and test 25's FIFO slot proves no case opens a slot file.
|
||||
`mosaic-config.mjs` going async is safe because every reader runs it as a subprocess
|
||||
(cli/src/config.mjs:22, business/src/cli.mjs:32).
|
||||
- `webui-service.sh` refuses `--sso` with the preview set or the proof pair, and its status grep reads the new
|
||||
flag order.
|
||||
- The page: both app.js fetch paths, plus the two bus.js reads (F1), sign out on 401 `sign-in required` only.
|
||||
The link goes to `/auth/login?return=<path and query>`.
|
||||
- Every design mutant is in `mutants.py`, plus 10 more. A pattern that matches no test exits 0 and counts as
|
||||
SURVIVED, so a typo in a pattern fails safe.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. Open redirect after sign-in. `returnPath` (sso.mjs:200-206) accepts any URL whose origin is
|
||||
`publicOrigin` and returns `u.pathname + u.search`. The callback puts that string in `Location` (:284). If
|
||||
the pathname starts with `//`, the browser reads the Location as scheme-relative and leaves the Console. I
|
||||
ran the function as written in node, with `publicOrigin = https://mosaic.example.com`:
|
||||
|
||||
| `return=` | `returnPath` | Browser goes to |
|
||||
| --- | --- | --- |
|
||||
| `/.//evil.example` | `//evil.example` | `https://evil.example/` |
|
||||
| `/x/..//evil.example` | `//evil.example` | `https://evil.example/` |
|
||||
| `https://mosaic.example.com//evil.example` | `//evil.example` | `https://evil.example/` |
|
||||
| the four test 14 cases | `/` | the Console |
|
||||
|
||||
Here is how an attacker uses it. They send Jason `https://<public host>/auth/login?return=/.//evil.example`.
|
||||
He signs in at the real provider, and the Console then sends him to the attacker's page. The `.`/`..` segments
|
||||
are why. Test 14's `//evil.example` parses with evil.example as the host, so the origin check catches it.
|
||||
`/.//evil.example` parses as a path on the Console, so the origin check passes, and dot-segment removal
|
||||
leaves the pathname as `//evil.example`. Design B2 and test 14 exist to stop exactly this.
|
||||
|
||||
Fix: return an absolute URL on the Console, `publicOrigin + u.pathname + u.search`. Then `//evil.example`
|
||||
becomes `https://<host>//evil.example`, which stays on the Console. Or refuse a pathname that starts with
|
||||
`//`. Add the three rows above to test 14, expecting `/` (or the absolute form, if you choose it), and add a
|
||||
mutant that restores the current return so test 14 kills it.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. The brief and BUILD.md:4 cite design blob eba861f5d2bb (d55d86bb0). Refactor and the candidate have
|
||||
9e3eadb15c80 (b416d2ede), which adds Mos's straggler check. The two differ only in the phase 2 window, so
|
||||
the build isn't affected. Cite the current blob in the next round.
|
||||
|
||||
N2. Design section 6 says the tester listener also refuses "the old auth and proof header names". Under
|
||||
`--sso`, cli.mjs:108 passes `testerProofHeader: proof?.header ?? null`, and that is always null because
|
||||
`--sso` refuses `--proof-*`. So the old proof header name isn't refused on 7333. The `x-authentik-*` prefix
|
||||
covers the old auth header. Nothing on 7333 reads the proof header, so this is harmless. Either add the
|
||||
name the live unit uses to the F8 refusal, or list it under Deviations.
|
||||
|
||||
N3. serve.mjs:64-66: the new `SIGN_IN_COOKIE` line went between the row 177 comment and
|
||||
`TESTER_CHAT_WRITE`, the line that comment describes. Move it above the comment, with its own comment.
|
||||
|
||||
N4. For the phase 2 window and row 200, not this row. Before 192, `mosaic-config.mjs` refuses any unknown
|
||||
top-level key (53bb59462 scripts/mosaic-config.mjs:112-116). Once `console` is in the live config.json, every
|
||||
loader from a pre-192 tree refuses the whole config, and every tool on that tree fails closed. That covers
|
||||
older pinned copies, any checkout behind 192, and a rollback target older than 192. A 192+ tree without
|
||||
`packages/webui/src/sso-config.mjs` refuses too (the import fail in `validateConsole`). The
|
||||
stack-release.test.mjs:1793 fixture copies `scripts/mosaic-config.mjs` without it, which is fine while the
|
||||
fixture config has no `console`. So the window has to re-pin to 192+ before it writes the key, and a rollback
|
||||
past 192 has to remove the key first. Put both in the window plan.
|
||||
|
||||
N5. A HEAD request for a page with no session gets 401 JSON, not the 303, because serve.mjs checks
|
||||
`req.method !== 'GET'`. Browsers don't send that, so no change is needed.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Webby review: row 192 r2, Q175 phase 1 SSO (#1690)
|
||||
|
||||
Webby, 2026-10-20T19:58Z (from `date -u`). Author: Launchpad. Candidate 37582e820 on launchpad/row192 (one commit on
|
||||
83b32d8d8), base 53bb59462, queue rev 2312. Manifest `candidate-manifest.sha256` digest 6f8cee14...7787,
|
||||
verified. All 18 path hashes match the candidate. The paths are `git diff --name-only 53bb59462 37582e820`
|
||||
less the four packet files under agents/launchpad/work/queue-192/. None of the 18 paths changed from the base
|
||||
to origin/refactor (ae3c36f24). Static review plus one node probe of `returnURL` (below). I ran no suite and
|
||||
no mutant. I did not read /mnt/storage/scratch/launchpad-r192, so the 28/28 and mutant results are
|
||||
Launchpad's report, not my run.
|
||||
|
||||
## Verdict: APPROVE
|
||||
|
||||
- r1 B1 is fixed. `returnURL` (sso.mjs:199-211) parses the return against publicOrigin, refuses another
|
||||
origin, and builds `publicOrigin + pathname + search` as an absolute URL. The callback's Location (:289)
|
||||
is now always absolute. Only the callback uses it; the export at :341 follows the rename. Test 14 has my
|
||||
three rows, `/board/./x`, and an origin assert on every case. The positive test expects the absolute
|
||||
form. Mutant 22 restores the r1 return under test 14.
|
||||
- Probe. I ran the function as written in node with publicOrigin `https://console.example.test` over 18
|
||||
inputs: my three rows, `/%2e%2e//evil`, `/.\\/evil`, `///evil`, `//evil`, `/\\evil`,
|
||||
`/@evil.example`, `/:evil`, CR/LF in the path (raw and encoded), a full-width dot, `:443`, upper-case
|
||||
scheme and host, and a fragment. Every result stayed on the Console's origin. Raw CR/LF are stripped by
|
||||
the URL parser, so no header split.
|
||||
- Mutant 23 (drop the second origin check). I agree it is equivalent. sso-config.mjs:43-44 holds
|
||||
publicOrigin to a bare https origin, and a parsed special-scheme pathname always starts with `/`, so the
|
||||
concatenation can't parse to another host. In the probe the mutant returned the same string as the build
|
||||
for all 18 inputs. Keeping the check as an unreachable guard is fine with me. Whether to keep it is
|
||||
Sage's call, as BUILD.md says.
|
||||
- N1 (design blob 9e3eadb15c80), N2 (under Deviations), N3 (serve.mjs:64-66, comment order) are done. N4
|
||||
moves to row 200 (#1698) and the window plan through Sage (mos-1058). N5 needs no change.
|
||||
|
||||
No new findings.
|
||||
@@ -0,0 +1,179 @@
|
||||
# Webby review: row 194 (#1692, agent lifecycle), build b4 as cut on b5, static
|
||||
|
||||
Webby, 2026-10-21 (times from `date -u` in the session line). Author: Honker (honker-0073, honker-0075).
|
||||
Request by sage-2230 and honker-0075: review b5, not f482890dc.
|
||||
|
||||
- **Candidate.** honker/row194-b5 at dfabbbe8a, six commits on origin/refactor da9555ca2.
|
||||
- **Manifest.** Honker gives 51 paths. `git diff --name-only da9555ca2 dfabbbe8a` gives 52. The extra one is
|
||||
agents/honker/work/queue-194/PRD-1.8-DRAFT.md, which stays out (sage-1626).
|
||||
- **The row.** It is in-progress, so I record nothing until Sage posts the request.
|
||||
|
||||
What I did, static only:
|
||||
- **b5 against b4.** On the 52 paths, `git diff f482890dc dfabbbe8a` touches only docs/plans/DEFERRED.md.
|
||||
So I read b4's code once, at f482890dc, and every line cite below holds at dfabbbe8a.
|
||||
- **Drift.** da9555ca2 is an ancestor of origin/refactor, and `da9555ca2..origin/refactor` touches none of
|
||||
the 52 paths.
|
||||
- **DEFERRED.md.** The diff keeps refactor's entries and adds the creative-delete entry after them.
|
||||
- **The read.** Four read-only subagents split the diff: bus, business, cli, and webui with harness. I
|
||||
re-read the code behind every finding below myself. Findings I couldn't confirm aren't listed.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
### B1. After a skipped reload, veto, hire and fire answer `restart: false`
|
||||
|
||||
- **Where.** packages/bus/src/runtime.mjs:280-282:
|
||||
|
||||
```
|
||||
const live=reload(id);
|
||||
...
|
||||
return verb==='instances.set'?{...result,restart:!live}:result;
|
||||
```
|
||||
|
||||
The writer's `commit()` always answers `restart: false` (instance-settings.mjs:386). So for
|
||||
`veto.set`, `instance.hire` and `instance.fire`, a reload that `sameFixed` refused (runtime.mjs:225-227)
|
||||
still answers `restart: false`.
|
||||
- **How a business gets there.** Brief item 7 lets the human add a credentialed definition. That add
|
||||
correctly answers `restart: true`. Its new `credentials` entry differs from `boot.roles` (`fixedRole(null)`),
|
||||
so every later reload of that business is refused until the bus restarts. A hand edit of a fixed key does
|
||||
the same.
|
||||
- **Failing scenario.**
|
||||
1. The human adds a credentialed agent and gets `restart: true`.
|
||||
2. Before restarting, the human runs `veto keep X`. The file gets X in `launch.keep`, the reload is
|
||||
refused, and the answer says `restart: false`.
|
||||
3. The broker's copy has no keep entry for X, so the chief's checks pass:
|
||||
- the keep check in `instance.fire` (broker.mjs:2570);
|
||||
- the `role.revoke` reason branch;
|
||||
- the B7 model check (broker.mjs:2478-2479).
|
||||
4. A chief fire of X runs step A: it revokes the claims, writes `lifecycle.end` with `kill: true`, and
|
||||
writes `fire.stopped` (broker.mjs:2595-2614).
|
||||
5. Step B's writer reads the file, so it refuses `instance-kept` (instance-settings.mjs:332), and
|
||||
`fire.done` is never written.
|
||||
6. X is left `instance-firing`. That refuses X's launch at the verb (:534) and at the take (:552). The
|
||||
veto the human set is what keeps the fire from finishing.
|
||||
- The same skipped reload leaves a hired instance unknown to the broker, and the hire still answers
|
||||
`restart: false`.
|
||||
- **What it breaks.**
|
||||
- DESIGN.md:428-430 (D3 item 3): "Any change that would need one of them still answers `restart: true` and doesn't
|
||||
reload."
|
||||
- DESIGN.md:408 (D2.4): "The broker enforces from its live copy of the business, which D3 keeps current."
|
||||
- The answer tells the human the veto is live when it isn't.
|
||||
- **Tests.** reload.test.mjs pins `restart: true` only for `instances.set` (:90-98, :157-163). No case
|
||||
covers a veto, hire or fire after a refused reload.
|
||||
- **Fix.**
|
||||
- Answer `restart: !live` for every committed verb, and print the restart line for veto, hire and fire in
|
||||
the CLI and the Console.
|
||||
- Add a reload test: a refused reload, then `veto.set`, which must answer `restart: true`.
|
||||
- Add a mutant that restores the `instances.set`-only condition.
|
||||
- Optional: have broker fire re-check keep against the file before step A. The writer already reads the
|
||||
file, so the check costs one read.
|
||||
|
||||
### B2. Fire ships without the SIGKILL escalation, and nothing records that
|
||||
|
||||
- **What happens.** Fire step A writes `lifecycle.end` with `kill: true` (broker.mjs:2611), and the
|
||||
launcher never reads it:
|
||||
- launcher.mjs:704 and :708 send every `lifecycle.end` to `terminate()`;
|
||||
- `terminate()` (:729-739) sends SIGTERM only.
|
||||
- So a fired runner that ignores SIGTERM keeps running with the model token.
|
||||
- No `lifecycle.end.killed` or `lifecycle.end.alive` is ever written, and the Console's "still running
|
||||
after fire" flag can't appear (BUILD.md:466-467).
|
||||
- T9a, T9b, T15 and M49-M55 are missing, and the halted checks (D1.1 item 2, D6) are missing too.
|
||||
- **What the design says.**
|
||||
- DESIGN.md D2.2: "After the grace, SIGKILL to any runner still alive ... and `lifecycle.end.killed`".
|
||||
- DESIGN.md D2.2: "Only fire escalates, because only a fired agent must lose the token at once. If row
|
||||
191 hasn't landed when this row builds, the build chains on its candidate (Q177)."
|
||||
- **What the build says.** BUILD.md:29 marks S6 "waits on row 191". Row 191 is in-progress in queue.json.
|
||||
b5 is offered to land in the 10-24 window without S6, and without a chain on 191.
|
||||
- **What I couldn't find.** A ruling that lets 194 land without S6. The DEFERRED.md diff adds only the
|
||||
creative-delete entry.
|
||||
- **Fix, either of:**
|
||||
- Build S6 on 191's candidate as the design says.
|
||||
- Get a Sage ruling that 194 lands without it, plus a DEFERRED entry. The entry names the gap (a fired
|
||||
runner can outlive SIGTERM with the token), the missing T9a, T9b, T15, M49-M55 and halted checks, the
|
||||
Console flag, and the row that binds them.
|
||||
- Since b4 lands before 191 either way, the ruling route is the cheaper one. It's Sage's call. I can't
|
||||
approve a fire that the approved design says must kill and that doesn't, with no record of the gap.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. A fire of a declared, delisted credentialed instance leaves its credentials live.
|
||||
- **Where.** instance-settings.mjs:335 returns `delisted: false` before `beforeWrite`, so `revoke` never
|
||||
runs.
|
||||
- **Scenario.**
|
||||
1. The human adds a credentialed coder-2, restarts, and issues its tokens.
|
||||
2. The human delists it with `launch: false`.
|
||||
3. The chief fires it. Fire answers done, and the tokens stay valid.
|
||||
4. The human can still launch an unlisted instance (V2).
|
||||
- **Design.** DESIGN.md D2.2 ("B finds I already delisted and counts as done") is about repeating a fire
|
||||
that partly failed. The brief's item 8 binds "before they change the business file", and this fire
|
||||
changes nothing, so this isn't a blocker as written.
|
||||
- **Fix.** Call `revoke` on that path when the entry has credentials. A revoke of an already revoked
|
||||
service should be a no-op. Add one test.
|
||||
|
||||
N2. A human add of a definition that declares the `host` service fails with the wrong message.
|
||||
- **Where.** `createdCredentials` (instance-settings.mjs:100, :127) maps every declared service, `host`
|
||||
included. `DATE_KEY` (credentials.mjs:12) has no `host`, so the entry gets a literal `"undefined"` key.
|
||||
Then `checkRoles` (business.mjs:140), which drops HOST_SERVICE, refuses with "must reference exactly the
|
||||
services its role definition needs".
|
||||
- **Effect.** It fails closed, and no shipped role declares `host`. The fix is to filter HOST_SERVICE
|
||||
first, or refuse the add by name.
|
||||
|
||||
N3. The B7 check (broker.mjs:2478-2479) looks only at `args.instances`. A chief `organization.set` that
|
||||
re-points the `models` entry a kept instance's `vars.model` names changes that instance's model at its
|
||||
next launch. DESIGN.md D2.4 says "that instance's model key". Re-pointing a shared key also changes every
|
||||
other instance on it, so I read the design as covering the instance's own key only. Say so in BUILD.md,
|
||||
or extend the check the way row 213 did for creative instances.
|
||||
|
||||
N4. A fire whose step B keeps refusing has no human escape. `#firing` (broker.mjs:537-543) clears only on a
|
||||
later `fire.done`, and only a chief fire writes one. Today only B1 makes this reachable. A human verb that
|
||||
clears it, or a take that checks keep against the file, would close it. Record it as a follow-up if B1's
|
||||
fix lands without one.
|
||||
|
||||
N5. Webui: `busStatus` (webui bus.mjs) maps none of the lifecycle refusal codes. A few examples are
|
||||
`instance-running`, `instance-kept`, `tester-off`, `tester-refused` and `unknown-request`, and all of them
|
||||
fall through to 502. The body is right. Separately, agents.js keeps polling an `unknown-request` for up to
|
||||
90 reads. Neither is harmful.
|
||||
|
||||
N6. CLI terminate (launcher.mjs:731-733) signals every registry run of a non-thread instance. That includes a
|
||||
run the chief launched after the terminate verb, whose claim was never revoked. DESIGN.md:118-120 (V3) names the
|
||||
verb's runs plus spawned-but-unbound ones. If this is meant, say so in the design's terms. The CLI text
|
||||
already hints at it.
|
||||
|
||||
N7. A delete that commits and then closes `outcome-unknown` (launcher.mjs:745-749) can't be retried. The
|
||||
broker allows a second delete of a retired name only after `workspace-rename-failed`, while DESIGN.md:173
|
||||
says a second delete "retries step 3 only" with no code limit. The window is narrow.
|
||||
|
||||
N8. DESIGN.md:625 still lists "SIGKILL escalation" under "Not in the row". That's r2 text that D2.2 r4
|
||||
replaced. It contradicts B2's design text. Don't edit the approved design. Note it in BUILD.md.
|
||||
|
||||
## Checked and fine
|
||||
|
||||
- **My design r4 notes.**
|
||||
- N1 is in: `#firing` (broker.mjs:537) refuses `instance-firing` at the launch verb (:534), at the take
|
||||
(:552) and in `role.launch` authority (:1770).
|
||||
- N2 is in: hire refuses unless roleVersion is 2 (broker.mjs:2647), and `checkHireable` refuses a version
|
||||
1 name (role.mjs:94).
|
||||
- **Creative hire.**
|
||||
- The writer refuses a CREATIVE_DEFINITIONS hire before `definitionFor` (instance-settings.mjs:304-308).
|
||||
- `checkHireable` refuses a creative name in a hireable list.
|
||||
- **endProtected.** The listing's `endProtected` and `deleteRefusal` call the same
|
||||
`endProtected` (instance-settings.mjs:32, :36, :170). agents.js:44 hides Delete on it.
|
||||
- **Reload.** The `libraries` key fixes each library entry whole, so a mapping change gets
|
||||
`restart: true` for `instances.set`.
|
||||
- **C1.** `testerNoSignIn` is gone from serve.mjs, webui cli.mjs and sso.test.mjs, with no reference left.
|
||||
- **Order and revision.** Fire runs A, B, C, and the consume happens only in C. `commit()` checks the
|
||||
revision before the revoke.
|
||||
- **Credential failures.** A failed revoke writes nothing, and the CLI text says the file is unchanged.
|
||||
- **Human tracker add.** It is required for vikunja, refused otherwise, and checked by `checkBot`, with
|
||||
bot uniqueness against the other instances and the sync bot.
|
||||
- **Human-only routes.** On the human listener, the webui routes need the signed-in Origin and Fetch Metadata
|
||||
checks. The tester listener refuses regenerate, veto and delete at the route, and the broker refuses
|
||||
them again.
|
||||
- **Escaping.** agents.js escapes every name and attribute it renders.
|
||||
|
||||
## Before the manifest round
|
||||
|
||||
The fixes for B1, and B2 or its ruling, are code and DEFERRED changes, and the gate reruns on them. I'll
|
||||
review the delta and then the manifest record. When I get a CLEAR, I'll run:
|
||||
- reload.test, lifecycle-verbs.test and chief-of-staff.test as whole files;
|
||||
- the B1 mutant;
|
||||
- a spot set of the D-table mutants, M2, M3, M22 and M27, on a scratch worktree.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Webby review: row 194 (#1692, agent lifecycle), build b6, static
|
||||
|
||||
Webby, 2026-10-21T23:52Z (from `date -u`). Author: Honker (honker-0083). Rulings in force: sage-2241,
|
||||
sage-2243, sage-2245.
|
||||
|
||||
- **Candidate.** honker/row194-b6 at af8d58c7d, one commit on b5 dfabbbe8a. 17 files, +280/-29.
|
||||
- **What I read.** The whole `git diff dfabbbe8a af8d58c7d`: source, tests, DEFERRED.md and BUILD.md "b6".
|
||||
I also read the code each fix leans on: the broker's organization gate and fire path, the runtime
|
||||
revoke, `Credentials.revoke` and `setRoutes`.
|
||||
- **What I didn't do.** No test or mutant has run. They wait for Sage's CLEAR. The row has no posted
|
||||
request for this round, so I record nothing.
|
||||
|
||||
## Verdict: CHANGES, B2 only (carried from b5)
|
||||
|
||||
B1 and N1-N3 are fixed in the code and have tests. N4 is deferred with a named test. B2 stays open
|
||||
until S6 is built on row 191's `stopRun`, per sage-2241 and sage-2245. Nothing new blocks.
|
||||
|
||||
### B1. Closed statically, runs pending
|
||||
|
||||
- **Restart.** runtime.mjs:33 and :290 answer `restart: !live` for `instances.set`, `veto.set`,
|
||||
`instance.hire` and `instance.fire`. Limits, routes and plugins writes keep their old answers, and
|
||||
reload.test pins that limits.set has no `restart` key.
|
||||
- **The fire check.** broker.mjs:2596 calls `write('instance.fireCheck')` before step A.
|
||||
- runtime.mjs:272 sends it to `checkFire` (instance-settings.mjs:351), which reads the file, runs
|
||||
`fireChecks` (:357) and returns before any write or reload.
|
||||
- `fire` runs the same `fireChecks` at step B.
|
||||
- `instance.fireCheck` appears only at broker.mjs:2596. A client that sends it gets no route to the
|
||||
writer.
|
||||
- **Printing.** The CLI veto prints RESTART_NEEDED or NO_RESTART. agents.js prints the matching
|
||||
receipt text. serve.mjs `/api/veto` needs a boolean `restart`, or it answers `outcome-unknown`.
|
||||
- **Tests.**
|
||||
- reload.test.mjs:150 runs my b5 sequence with the real writer. A credentialed add refuses the reload.
|
||||
Then hire, keep, unkeep and fire each answer `restart: true`. A fire of the name kept since refuses
|
||||
`instance-kept` with the file unchanged, the claim held and no `instance-firing`.
|
||||
- chief-of-staff T10 (new) has the writer refuse at fireCheck. The organization log holds only
|
||||
`instance.fireCheck`, the claim stays and `instance.launch` goes through.
|
||||
- The cli test and the Console tests cover the veto restart line and the shape check.
|
||||
- **What the CLEAR run must show.** Both of BUILD.md's mutants killed:
|
||||
- (a) the `instances.set`-only restart line. reload.test :70 and :150 should kill it.
|
||||
- (b) no `instance.fireCheck` call. reload.test :150 and chief-of-staff T10 should kill it.
|
||||
|
||||
### B2. Open
|
||||
|
||||
S6 isn't in b6. BUILD.md "b6" gives the `stopRun` contract (agents/rocko/work/queue-191/STOP-RUN-CONTRACT.md)
|
||||
and says 194 lands with or after row 191 in the 10-24 window. That matches sage-2241. I'll review S6
|
||||
when it's wired onto Rocko's commit. T9a, T9b, T15, M49-M55 and the halted checks come with it.
|
||||
|
||||
## The b5 notes
|
||||
|
||||
- **N1. Closed.** A fire of a delisted name now revokes with no write (instance-settings.mjs:334-339).
|
||||
- A repeat is safe. `Credentials.revoke` takes ENOENT (credentials.mjs:333-337), and the runtime
|
||||
revoke skips `credential-unknown` (runtime.mjs:151-155).
|
||||
- That path is also the repeat after a failed step C, which now revokes a second time and still goes
|
||||
on to `fire.done`.
|
||||
- The lifecycle-revoke.test case checks the receipt, the unchanged bytes, the empty store and the
|
||||
status.
|
||||
- **N2. Closed.** instance-settings.mjs:101 drops HOST_SERVICE. The new instance-settings test checks the
|
||||
references.
|
||||
- **N3. Closed for the chief.** `keptHumanOnly` runs in the limits writer on a re-point of a key a kept
|
||||
instance names and on its own `instances` key. The broker passes `instance-kept` for every verb. The
|
||||
"B7 kept instance" test (launch-settings.test.mjs:148) covers both paths for the chief, a key no kept
|
||||
instance names, and the human. The comparison `actor !== document.human` is the same one
|
||||
`creativeHumanOnly` already relies on.
|
||||
- **N4. Deferred.** The DEFERRED.md entry names the gap, a human-only verb and its test.
|
||||
- **N5, N7.** Follow-ups, as BUILD.md says.
|
||||
- **N6.** With Sage, tied to S6.
|
||||
- **N8.** Noted in BUILD.md, and the design isn't edited.
|
||||
|
||||
## New notes, non-blocking
|
||||
|
||||
N1. The tester half of N3 doesn't match what the tester can reach.
|
||||
- `limits.set` isn't in TESTER_ORGANIZATION_VERBS (broker.mjs:130). So the tester rows in the "B7 kept
|
||||
instance" test exercise the writer, and the bus never gets there.
|
||||
- `routes.set` is a tester verb. `setRoutes` (launch-settings.mjs:291-293) runs `creativeHumanOnly`
|
||||
and not `keptHumanOnly`. So the tester can change a kept instance's model through a route.
|
||||
- I didn't check `instances.set` change with `vars.model` for the tester.
|
||||
- D2.4 binds the chief, so this isn't a blocker. Either keep binds the tester too, and `setRoutes` gets
|
||||
the same check and a test, or it doesn't, and BUILD.md drops "the tester included". Sage's call.
|
||||
|
||||
N2. fireCheck reads the file without the writer's lock, so a keep written between the check and step B
|
||||
still strands the instance. The new DEFERRED entry names that case. I'm noting it only so the CLEAR
|
||||
run doesn't treat it as new.
|
||||
|
||||
## On CLEAR
|
||||
|
||||
On a scratch worktree at af8d58c7d, with TMPDIR under /mnt/storage/scratch/tmp:
|
||||
- whole files: reload, lifecycle-verbs, lifecycle-revoke and chief-of-staff (bus); instance-settings
|
||||
and launch-settings (business); cli instances; webui agents and agents-browser;
|
||||
- mutants: B1 (a) and (b) above, and M2, M3, M22 and M27 from the D table;
|
||||
- a blob check after each restore.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Webby review: row 194 (#1692, agent lifecycle), build b7, static
|
||||
|
||||
Webby, 2026-10-22T00:01Z (from `date -u`). Author: Honker (honker-0085). Rulings in force: sage-2241,
|
||||
sage-2245, sage-2246, sage-2248, sage-2249.
|
||||
|
||||
- **Candidate.** honker/row194-b7 at 0f0e2c7ef, nine commits on dcd8d4dda. dcd8d4dda is origin/refactor
|
||||
after row 254 landed.
|
||||
- **What I didn't do.** Nothing has run yet. Runs wait for Sage's CLEAR, after Rocko's row 191 slot. No
|
||||
request is posted for this round, so I record nothing.
|
||||
|
||||
## Verdict: CHANGES, B2 only (carried)
|
||||
|
||||
The rebase is clean, and the routes.set fix closes my b6 N1. B2 (S6) is still open, as ruled in sage-2241.
|
||||
|
||||
### The rebase
|
||||
|
||||
- **Commits.** `git range-diff da9555ca2..af8d58c7d dcd8d4dda..b0aed7527` gives `=` for six of the seven
|
||||
commits. The seventh, commit 2 (30f05f3d2), differs only in the channel-proof pin for webui/src/bus.mjs
|
||||
and in the STATUS context lines next to row 254's entries.
|
||||
- **channel-proof.** At 0f0e2c7ef, all four baseline pins match the sha256 of their files: human.mjs,
|
||||
human-cli.mjs, cli transport.mjs and webui bus.mjs (15e8123f). The diff against the base swaps row 254's
|
||||
pin for the merged file's and adds row 194's comment. Row 254 had no comment line of its own to lose.
|
||||
- **webui/src/bus.mjs.** Against dcd8d4dda, the diff is the two row 194 tracker entries. STATUS has no
|
||||
duplicate key.
|
||||
- **DEFERRED.md.** Against dcd8d4dda, the diff adds row 194's two entries, creative delete and the stuck
|
||||
fire (N4). It removes nothing.
|
||||
- **Drift.** da9555ca2..dcd8d4dda reaches the manifest on only those three paths: DEFERRED.md,
|
||||
channel-proof.test.mjs and webui bus.mjs. The b7 manifest is the same 53 paths as b6, with
|
||||
PRD-1.8-DRAFT left out.
|
||||
|
||||
### b6 N1, closed statically
|
||||
|
||||
6a742d371 calls `keptHumanOnly` in `setRoutes` right after `creativeHumanOnly` (launch-settings.mjs:294-295).
|
||||
The test "B7 kept instance: its route is the human's" covers three cases:
|
||||
- the chief is refused with `instance-kept` and the bytes unchanged;
|
||||
- the tester gets the same, which is now reachable on the bus because routes.set is a tester verb;
|
||||
- the human re-routes the instance.
|
||||
|
||||
M-next, which drops the call, has to be killed on the CLEAR run.
|
||||
|
||||
### The behaviour change (sage-2246)
|
||||
|
||||
An agent can no longer re-point a `launch.models` key that a kept instance names, and a shared key
|
||||
counts. I agree. Re-pointing a shared key changes the kept instance's model, which is what D2.4 B7
|
||||
protects. The chief still adds keys and changes the ones no kept instance names, and b6's test covers
|
||||
that.
|
||||
|
||||
### The mutant (a) correction
|
||||
|
||||
Right. The CLI test and the Console tests stub the bus, so only reload.test can kill (a).
|
||||
|
||||
## On CLEAR
|
||||
|
||||
Honker's runb7.sh list covers the whole files and mutants (a), (b) and M-next. I'll run on my own scratch
|
||||
worktree at 0f0e2c7ef, with TMPDIR under /mnt/storage/scratch/tmp:
|
||||
- **Row 194's files, each alone:** reload, chief-of-staff, lifecycle-verbs, lifecycle-revoke and
|
||||
channel-proof (bus); instance-settings and launch-settings (business); cli instances; webui agents and
|
||||
agents-browser.
|
||||
- **Rebase files:** webui library-status.test and bus library-sync.test. Row 254's tests ran against
|
||||
its own bus.mjs, and this merge changes that file.
|
||||
- **Mutants:** (a), (b), M-next, and M2, M3, M22 and M27, with a blob check after each restore.
|
||||
|
||||
B2 stays open until the S6 commit, with N6 narrowed as sage-2246 rules. I'll review it when it arrives.
|
||||
@@ -0,0 +1,307 @@
|
||||
# Webby review: row 194 (#1692, agent lifecycle), build b8 r2, static
|
||||
|
||||
Webby, 2026-10-22T00:41Z (from `date -u`). Author: Honker (honker-0092, honker-0094). Rulings in force: sage-2241,
|
||||
sage-2246, sage-2260, sage-2261, sage-2289.
|
||||
|
||||
- **Candidate.** origin/honker/row194-b8 at 899e30a80, on row 191's tip eb3abba12
|
||||
(origin/rocko/row191-stoprun-dcd8-20261022, four commits on dcd8d4dda). b8 r2 is b8 (0583569b7) plus
|
||||
one commit, and 0583569b7 is its parent.
|
||||
- **Manifest.** `git diff --name-only eb3abba12 899e30a80` gives 54 paths. Leaving out
|
||||
agents/honker/work/queue-194/PRD-1.8-DRAFT.md gives 53. None is under packages/processing-control, and
|
||||
none is a row 191-only path.
|
||||
- **What I read.**
|
||||
- The b7 to b8 range-diff, `git range-diff dcd8d4dda..0f0e2c7ef eb3abba12..0583569b7`, and every
|
||||
interdiff of the three `!` commits.
|
||||
- The whole S6 commit b3341c1cb and the r2 commit 899e30a80.
|
||||
- BUILD.md "b8 notes" and "b8 r2: sage-2289".
|
||||
- Row 191's `stopRun`, `ownStop` and `sweepRuntimes` (processing-control/src/runtimes.mjs at
|
||||
eb3abba12).
|
||||
- DESIGN.md V3 (:112-124), D2.2 (:276-312), D6 (:485-496), D7 (:498-503) and the T9a, T9b, T15 and
|
||||
M49-M55 rows.
|
||||
- **What I didn't do.** No test or mutant has run. They wait for Sage's CLEAR. The row has no posted
|
||||
request for this round, so I record nothing.
|
||||
|
||||
## Verdict: no blockers. B2 is closed statically, and APPROVE waits on the runs
|
||||
|
||||
B2 (S6) is built on row 191's `stopRun` as D2.2 asks. Terminate stays SIGTERM-only, and the halted
|
||||
checks match D6. The unknown outcome follows sage-2289. I found nothing new that blocks. The round's
|
||||
APPROVE depends on my CLEAR run below plus the full gate with Docker and P3 (sage-2261).
|
||||
|
||||
## The rebase onto 191
|
||||
|
||||
- Six commits are `=`. Commits 1, b4 and b6 are `!`, and in each one I checked, both sides are kept with
|
||||
no change to 194's behaviour. BUILD.md lists these file by file, and the interdiffs match:
|
||||
- the broker constructor ends `processing = null, testerOn = null, roleFile = null`;
|
||||
- runtime.mjs keeps 191's `broker = new Broker(...)`, `broker.reconcileProcessing(); processing.start();`
|
||||
and its imports, with `INSTANCE_VERBS` after them;
|
||||
- launcher `start()` takes `human = false, admissionGeneration`, and 191's `admission(...)` calls stay;
|
||||
- webui cli and serve pass both `agentWrite` and `processing`, and C1's `testerNoSignIn` removal sits
|
||||
beside 191's tester `processing` read;
|
||||
- the browser.test script pin is `['/editor.js','/processing.js','/brand.js','/app.js',
|
||||
'/markdown-preview.js','/chat.js','/agents.js','/bus.js']`.
|
||||
- 191 also changed 10 of the 194 paths. That's why these resolutions get a full run and not a carried
|
||||
verdict.
|
||||
|
||||
## B2 (S6) against the design
|
||||
|
||||
- **Fire goes through 191's stop.** `act` (launcher.mjs:763-770) sends a `kill: true` end to `fireStop`
|
||||
(:819). That calls `stopRun` with reason `fire` and owner `{ decision: <request> }` (:825), which is
|
||||
the shape `stopRun` accepts. `stopRun` checks identity before each signal, sends SIGKILL after the
|
||||
grace and observes. D2.2 items 3a-3c are 191's code, not a second copy.
|
||||
- **Terminate is SIGTERM only (V3).** `terminate` (:804) sends SIGTERM and nothing else. The
|
||||
T9a terminate test (lifecycle.test.mjs:670) pins one SIGTERM and no fire-stop record. M54 targets it.
|
||||
- **Outcomes.** `lifecycleDone` (broker.mjs:2322) accepts `stops` only on a `kill: true` end (:2343). It
|
||||
writes `lifecycle.end.killed` or `.alive` against the instance with run and pid. The pid check (`null`
|
||||
or a safe integer above 1) matches 191's identity check (`pid < 2` fails). The test at
|
||||
chief-of-staff.test.mjs:899 covers the refusals.
|
||||
- **Unknown, r2.** `fireStop` (launcher.mjs:835) maps `unknown` to alive with reason `unknown`, and the
|
||||
broker accepts that reason only on an alive stop (broker.mjs:2335). That closes more than the
|
||||
no-record case. `ownStop` returns a stored result before it signals (runtimes.mjs:267). So a run whose
|
||||
earlier halt sweep ended `unknown`, if it survived to a later fire, gets no signal from the fire, and
|
||||
r2 now flags it. Before r2 it would have vanished from the fire's report. The same holds for "another
|
||||
host owns delivery" and for a `stopRun` that throws (launcher.mjs:828). The new T9a test (:597) and
|
||||
M56 cover it.
|
||||
- **Console (D7).** `#aliveAfterFire` (broker.mjs:2351) lists alive runs with no `session.ended`, and the
|
||||
instances read adds them as `alive` (:2633). agents.js escapes each run with `txt`. The
|
||||
agents-browser test puts a hostile run name through it.
|
||||
- **Halted (D6).**
|
||||
- `#launchable` (broker.mjs:617) is used only by `instance.launch` and `instance.regenerate`. So
|
||||
`instance.end` keeps no halted check, and terminate works while halted (V3 item 5).
|
||||
- The take re-checks for every action except end and delete (:636-647).
|
||||
- Hire and fire refuse through 191's work gate, since neither is on its read list.
|
||||
- The tick (launcher.mjs:583) skips the thread and land passes while halted and still runs the
|
||||
lifecycle pass.
|
||||
- T15 covers the bus (lifecycle-verbs.test.mjs:150) and the cli (lifecycle.test.mjs:718).
|
||||
- **Halt joining a fire (T9b).** The sweep's `stopRun` gets the fire's in-flight promise from 191's
|
||||
`stopping` map. So it sends no second SIGTERM and doesn't restart the deadline, and the receipt keeps
|
||||
reason `fire` and the decision. lifecycle.test.mjs:686 checks one SIGTERM, the deadline, the single
|
||||
receipt and the counted run. M55 and M55-deadline target it.
|
||||
- **N6 (sage-2246).** `ending` (launcher.mjs:795) takes the verb's runs plus non-thread registry runs
|
||||
spawned at or before the end's `at`, using `spawnedAt` (:500) or the registry `startedAt`. That
|
||||
matches DESIGN.md:118-120. lifecycle.test.mjs:751 checks that a later run survives and the next end
|
||||
takes it. Fire uses the same selection. The only spawn that can follow a fire's verb is a human
|
||||
launch already taken, and D2.2 (:300-302) leaves that one to the human's terminate. So the narrowing
|
||||
doesn't cut into F4.
|
||||
- **Regenerate** now signals every registry run of the instance (launcher.mjs:772), with no N6 cut.
|
||||
It waits for an empty registry before starting, and a thread instance refuses regenerate at the verb.
|
||||
That's b7's behaviour, and it's right.
|
||||
- **Tests and mutants present.** T9a has four cli tests (:562, :597, :619, :670) and one bus test. T9b,
|
||||
T15 (bus and cli), N6 and the agents-browser flag are all there. BUILD.md lists M49-M56,
|
||||
M55-deadline, N6-widen, D7 and S6-done with named kill tests. The DESIGN.md test table names
|
||||
`launcher.test.mjs`, and the tests are in `lifecycle.test.mjs`. The b8 notes record that deviation,
|
||||
and I agree with it.
|
||||
|
||||
## Notes, non-blocking
|
||||
|
||||
N1. The clock caveat is narrower than BUILD.md says. The bus's `#now` (broker.mjs:524-526) is monotonic:
|
||||
`max(Date.now(), clock + 1)`. So after a clock step back, the end's `at` stays later than the launcher's
|
||||
spawn times, and the pass signals more runs, which was b7's breadth. It doesn't signal fewer. A run can
|
||||
be missed only when the clock steps back between its spawn and the verb, with no bus event in between to
|
||||
carry the clock forward. That's fine as it stands. BUILD.md could say which direction the error goes.
|
||||
|
||||
N2. `stops` is capped at 64 (broker.mjs:2330). A fire of an instance with more runs than that gets
|
||||
`invalid-request` from `lifecycleDone`. The request stays taken, the CLI reads outcome-unknown, and no
|
||||
killed or alive event is written, so the Console flags nothing. No business is near 64 runs of one
|
||||
instance today. Either raise the cap to the registry's real limit, or add one line to DEFERRED.
|
||||
|
||||
N3. The CLI `instances` output (instances.mjs:201) prints `running` and not `alive`. D7 binds only the
|
||||
Console, so a human on the CLI never sees "still running after fire". A follow-up row, not this one.
|
||||
|
||||
N4. An alive flag clears only on the run's `session.ended`. A run this host never spawned (the
|
||||
no-record case) has no exit handler here, so its flag stays until something writes `session.ended`. A
|
||||
false flag is the safe side per sage-2289. I'm noting it so nobody reads a lingering flag as a bug in
|
||||
the round.
|
||||
|
||||
N5. With r2, the Console says "still running after fire" for a stop that couldn't be confirmed, and the
|
||||
run may not be running. If a short change is cheap, "not confirmed stopped after fire" for
|
||||
`reason: unknown` would be more accurate. Cosmetic.
|
||||
|
||||
## On CLEAR
|
||||
|
||||
On my own scratch worktree at 899e30a80, with TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/...`:
|
||||
- **Whole files, one at a time.**
|
||||
- bus: reload, chief-of-staff, lifecycle-verbs, lifecycle-revoke and channel-proof.
|
||||
- business: instance-settings and launch-settings.
|
||||
- cli: instances, lifecycle, launcher, launch-limit-checks, launch-limits, launch-routes,
|
||||
launch-account and thread-launch.
|
||||
- webui: agents, agents-browser and browser.
|
||||
- library: status and sync.
|
||||
- **Skips.** lifecycle.test skips when `sandboxProblem()` reports one (:33). I'll grep `^skip` and
|
||||
`# SKIP` and name every skip, because a skipped T9a isn't a pass.
|
||||
- **Mutants, with a blob check after each restore.**
|
||||
- B1 (a) and (b), and M-next.
|
||||
- From the D table: M2, M3, M16, M22 and M27.
|
||||
- S6: M49-M56 and M55-deadline (M52, M55 and M55-deadline edit 191's runtimes.mjs), plus N6-widen,
|
||||
D7 and S6-done.
|
||||
- The anchors are mine. I don't use Honker's runb8.sh.
|
||||
|
||||
## Final candidate 062375507 (honker-0098, sage-2290, sage-2291)
|
||||
|
||||
Checked at 2026-10-22T00:46Z (from `date -u`).
|
||||
- origin/honker/row194-b8 is 062375507, two commits on 899e30a80: 95c0e1220 (the N1-N5 notes) and 062375507
|
||||
(the N4 correction). Both are doc-only.
|
||||
- `git diff 899e30a80 062375507 -- packages` is empty. The changed paths are BUILD.md and
|
||||
docs/plans/DEFERRED.md. With the PRD draft left out, the manifest is still 53 paths.
|
||||
- **The DEFERRED line names what sage-2291 asked for.** The cap is 64 stops in `lifecycleDone`, and
|
||||
the refusal is `invalid-request`. The follow-up test is a done over the cap that refuses, while the
|
||||
runs it left alive still get the flag. Its consequence is right too. fireStop signals before the done,
|
||||
and `fire.done` is written in the verb, so only the events and the flag are lost.
|
||||
- **N1 as corrected in BUILD.md is still too strong.** "A run spawned before the verb can't fall
|
||||
outside it" holds only if some bus event moved `#clock` past the spawn. Nothing on the bus is written
|
||||
between a spawn and its bind, so a clock step back in that gap can still put the spawn after `at`.
|
||||
That's the narrow case from my N1. It's doc-only, so it doesn't block (decision 137), and it needs no
|
||||
new commit.
|
||||
- The static review carries to 062375507. All runs use 062375507.
|
||||
|
||||
## Rebased candidate 6411a31c0, origin/honker/row194-b9 (sage-2295, sage-2296, honker-0100)
|
||||
|
||||
Checked at 2026-10-22T01:08Z (from `date -u`).
|
||||
- 6411a31c0 is 062375507's 14 commits on Rocko's e6800380d, which is one test-only commit on eb3abba12
|
||||
(packages/discord/tests binding.test.mjs and recover.test.mjs, +2 each).
|
||||
- `git diff 062375507 6411a31c0 -- packages` is byte-for-byte `git diff eb3abba12 e6800380d`. Nothing
|
||||
outside packages changed.
|
||||
- `git range-diff eb3abba12..062375507 e6800380d..6411a31c0`: all 14 commits are `=`.
|
||||
- Against e6800380d the manifest is 53 paths without the PRD draft. None is under processing-control or
|
||||
packages/discord.
|
||||
- The static review carries to 6411a31c0. All runs use 6411a31c0.
|
||||
|
||||
## Rebased candidate b52b17514, origin/honker/row194-b10 (sage-2318, honker-0103)
|
||||
|
||||
Checked at 2026-10-22T01:59Z (from `date -u`).
|
||||
- b52b17514 is 6411a31c0's 14 commits on Rocko's 8e1f55c1d. 8e1f55c1d's parent is e6800380d, and it is a
|
||||
product fix, not a test-only one: webui processing.css and processing.js, plus processing-browser.test.mjs
|
||||
and processing-ui.test.mjs.
|
||||
- `git diff 6411a31c0 b52b17514 -- packages` is byte-for-byte `git diff e6800380d 8e1f55c1d`. Nothing
|
||||
outside packages changed.
|
||||
- `git range-diff e6800380d..6411a31c0 8e1f55c1d..b52b17514`: all 14 commits are `=`.
|
||||
- Against 8e1f55c1d the manifest is 53 paths without the PRD draft. None is under processing-control, and
|
||||
194 touches neither processing.js nor processing.css nor Rocko's two tests.
|
||||
- **Where the two meet.** 194 changes webui src/public/index.html (it adds `/agents.js` to the script
|
||||
line), and Rocko's processing-ui.test.mjs:66 reads index.html for the processing controls. Its asserts
|
||||
are on the processing nodes and the processing.js and processing.css tags, which 194 leaves alone, so I
|
||||
expect a pass. shell.test.mjs:40 also reads index.html with app.js and bus.js, and it wasn't on my list.
|
||||
- **Run list change.** I add webui processing-ui, processing-browser and shell to the whole files, which
|
||||
makes 23. The rest of "On CLEAR" stands.
|
||||
- The static review carries to b52b17514. All runs use b52b17514.
|
||||
|
||||
## Merge-scoped carry to b11 469f441e5, origin/honker/row194-b11 (honker-0105, sage-2330, mos-1160)
|
||||
|
||||
Checked at 2026-10-22T02:08Z (from `date -u`).
|
||||
- **Base.** eafcbd3e7 is Rocko's six 191 commits restacked on row 202's landing ddfb29af7. It is not a
|
||||
descendant of 8e1f55c1d. `git range-diff dcd8d4dda..8e1f55c1d ddfb29af7..eafcbd3e7` gives five `=` and one
|
||||
`!`. The `!` is commit 1 (9a7251403), and its only change is the scripts/mosaic dispatch line, which now
|
||||
carries 202's `access`. `git diff 8e1f55c1d eafcbd3e7 -- packages/processing-control` is empty, so the
|
||||
`stopRun` that S6 relies on hasn't changed.
|
||||
- **The 14 commits.** `git range-diff 8e1f55c1d..b52b17514 eafcbd3e7..469f441e5`: all 14 are `=`.
|
||||
- **b10 to b11.** `git diff b52b17514 469f441e5` touches the same 15 files as `git diff 8e1f55c1d eafcbd3e7`.
|
||||
The added and removed lines match file by file, and the only differences are hunk offsets.
|
||||
- **Tree.** `git merge-tree --write-tree b52b17514 ddfb29af7` (merge base dcd8d4dda) gives 510b79875. That
|
||||
tree differs from b11 only in scripts/mosaic. It has one conflict there, and b11 resolves it to
|
||||
`halt|resume|processing|...|thread|access`, which keeps both sides. 194's `instances|instance|veto` line
|
||||
(:32) is unchanged.
|
||||
- **Overlap with 202.** Only two paths are in both 202 and the 194 manifest: packages/cli/src/cli.mjs and
|
||||
scripts/mosaic. In b11's cli.mjs, 202's import is at :23 and its `if (verb === "access")` block is at :619.
|
||||
- **Manifest.** Against eafcbd3e7 it's still 53 paths without the PRD draft. None is under
|
||||
processing-control or access.
|
||||
- **Run list change.** 202's only test is packages/cli/tests/access.test.mjs. It reads cli.mjs and
|
||||
runtime.mjs, and 194 changes both, so I'm adding it. That makes 24 whole files. The mutant list stands.
|
||||
- The static review carries to 469f441e5. All runs use 469f441e5.
|
||||
|
||||
## Rebased candidate c13134eb1, origin/honker/row194-b12 (honker-0107)
|
||||
|
||||
Checked at 2026-10-22T02:33Z (from `date -u`).
|
||||
- 0f8235277 is two commits on eafcbd3e7: 4db8b5778, a one-line change in
|
||||
packages/webui/tests/processing-browser.test.mjs, and Rocko's packet BUILD.md.
|
||||
- `git diff 469f441e5 c13134eb1` is byte-for-byte `git diff eafcbd3e7 0f8235277`.
|
||||
- `git range-diff eafcbd3e7..469f441e5 0f8235277..c13134eb1`: all 14 commits are `=`.
|
||||
- Against 0f8235277 the manifest is 53 paths without the PRD draft. Neither of Rocko's two paths is in it.
|
||||
- The static review carries to c13134eb1. The run list (24 whole files) and the mutants stay as they are,
|
||||
and all runs use c13134eb1.
|
||||
|
||||
## b13 7609210a3 carry check (honker-0110, sage-2458)
|
||||
|
||||
Git reads only. No tests have run yet.
|
||||
|
||||
- The 14 commits sit on refactor 0fa7bf421, the landed 191. The base of b12 c13134eb1 was 0f8235277.
|
||||
- range-diff 0f8235277..c13134eb1 against 0fa7bf421..7609210a3 shows 14 of 14 `=`.
|
||||
- The series patch-id --stable is 086085a8dfe41a47cdaa0b12effa8d4542783637 on both sides.
|
||||
- The path list is identical, 54 paths.
|
||||
- The drift 0f8235277..0fa7bf421 covers 14 files. The only one 194 also touches is packages/webui/src/public/index.html. 191 changes lines 14 and 20-22, and 194 changes line 7 (adds agents.js to the script line), so the hunks are separate.
|
||||
- Tests that read index.html: agents.test (in 194, row 194 N5 at :214), processing-ui.test:66 and shell.test:40. None of them pins the script list. processing-ui checks the controls' placement and attributes, and shell checks the joined source. stack-release.sh drift is read only by the cli stack-* tests, and 194 touches none of those.
|
||||
- My run list stays at 24 files. processing-ui, processing-browser and shell are already in it since b10. Runs go on 7609210a3 at Sage's CLEAR, after Honker's runb8.
|
||||
|
||||
## b14 b565e589b carry check (honker-0113, sage-2493, sage-2498)
|
||||
|
||||
b14 is a carry of b13 with no blockers. These are git reads only. I did no runs, because the slot is Honker's.
|
||||
- 14 commits on refactor 23c3d6218.
|
||||
- range-diff 0fa7bf421..7609210a3 against 23c3d6218..b565e589b shows 14 of 14 `=`.
|
||||
- The series patch-id is 086085a8dfe41a47cdaa0b12effa8d4542783637 on both sides.
|
||||
- The path list is identical.
|
||||
- The drift 0fa7bf421..23c3d6218 (205 and 268, 24 files) meets 194 at two paths, broker.mjs and business.test.mjs. For both files, the +/- lines of 194's own diff are identical on the old and new bases. So 194's hunks are unchanged, and git applied them without touching 205's or 268's lines.
|
||||
- Whether the merged files run together is a matter for Honker's runs on b14, which Sage is gating.
|
||||
|
||||
## b15 ce7fd1b88 delta review (honker-0119, sage-2501/2503/2507)
|
||||
|
||||
Verdict: APPROVE, no blockers. These are git reads only, and nothing has run on b15.
|
||||
- b15 is b14 b565e589b plus two commits.
|
||||
- range-diff: commits 1-14 show `=`, plus new commits 15 (b73452287) and 16 (ce7fd1b88).
|
||||
- The series patch-id is 64303253f8e6. The path list is the same as b14's.
|
||||
- Neither new commit has an attribution line.
|
||||
|
||||
(a) b73452287, broker.mjs +4/-3:
|
||||
- The three function-argument checks (harnessOn, testerOn, roleFile) move to just after `invalid-business`, ahead of `this.#store` and every store read. Each check's text is unchanged. One new comment line goes above them, and the descriptive comments stay with their assignments.
|
||||
- Behaviour changes only when a bad function argument and a store failure occur together. The argument error now wins, and that is what T8b expects (chief-of-staff.test:868, `store: null`).
|
||||
- The only other pin on these messages is routes-verb.test:89, which uses a real store and is unaffected.
|
||||
|
||||
(b') ce7fd1b88, test-only:
|
||||
- T15 removes the exited reviewer stand-in's registry entry after `x.exited === 143`.
|
||||
- The refusal it avoids comes from business/src/launch.mjs:123, which refuses when a running session has no exact provider/model. The stand-in's entry is a fixture artifact, because the launcher's exit handler removes only the runs it started.
|
||||
- This is the same convention as N6 at lifecycle.test:763-764. standIn is unchanged.
|
||||
- I accept Sage's ruling of (b') over (b). Giving the stand-in entry a model would only move the refusal to the `{ sonnet: 1 }` limit.
|
||||
- The second remove in standIn's t.after is a no-op.
|
||||
|
||||
N1, non-blocking: no test pins the `testerOn must be a function` message. This is old, not new in b15.
|
||||
|
||||
Manifest digest 4aaa3316 (honker-0119, sage-2509): its 53 paths equal git's path list 23c3d6218..ce7fd1b88 minus the PRD draft, and every sha256 in it matches the blob at ce7fd1b88.
|
||||
|
||||
## b16 9cdc47d04 carry check (honker-0122, sage-2521)
|
||||
|
||||
b16 is a carry of b15 with no blockers. These are git reads only, and nothing has run.
|
||||
- 16 commits on ceadad993, which is origin sage/row279-carry-343b.
|
||||
- range-diff 23c3d6218..ce7fd1b88 against ceadad993..9cdc47d04 shows 16 of 16 `=`.
|
||||
- The series patch-id is 64303253f8e6, the same as b15. The path list is identical.
|
||||
- No commit has an attribution line.
|
||||
- The drift 23c3d6218..ceadad993 is 26 files, and none of them is a 194 path.
|
||||
|
||||
The drift does reach code that 194 runs. 279 changes processing-control/src/runtimes.mjs:333 in ownStop, from `while (now() <= deadline)` to `for (;;)`. Now the loop's only exit at the deadline is the final status check, which returns alive, killed, stopped or identity-changed. Before, the loop could fall through to `finish('unknown')`.
|
||||
- 194's S6 fire stop calls stopRun (lifecycle T9a/T9b, and the launcher's fireStop maps `unknown`). So the outcomes 194 sees at the deadline can change.
|
||||
- The whole runb8 rerun on b16 settles that. It is required, and a carry from b15's runs would not cover it.
|
||||
- M52, M55 and M55-deadline mutate this file. Before the mutants run, each anchor must still match runtimes.mjs at ceadad993 exactly once. The broker.mjs anchors were checked the same way at b15. 279 changed only line 333, so the anchors are probably intact, but the runner has to confirm it, not assume it.
|
||||
|
||||
Anchors (honker-0124): I spot-checked them on 9cdc47d04, whose runtimes.mjs is the same as ceadad993's. M52's line occurs once, M55/M55-deadline's ` let problem = null;` once, and the unknown return at :294 is there. Before the mutants run, runb8 asserts count==1 for each anchor. My b16 condition is met.
|
||||
|
||||
## b17 3f451772d carry (honker-0130), APPROVE
|
||||
|
||||
b16 9cdc47d04 was rebased onto b0fd28b6f (row 280 landed). I checked this from the canonical checkout.
|
||||
|
||||
- 3f451772d is honker/row194-b17. b0fd28b6f is its ancestor, with 16 commits on top.
|
||||
- `git range-diff ceadad993..9cdc47d04 b0fd28b6f..3f451772d` shows all 16 commits `=`.
|
||||
- The series patch-id is 64303253f8e6 on both bases.
|
||||
- The changed-path list is the same on both bases. The packages diff from b16 to b17 is only 280's three bus test files, and none is in 194's manifest. No 194 test or script mentions them.
|
||||
- DEFERRED.md is the only path where 280 and 194 overlap. 194's +/- lines there are byte-identical on both bases (same md5 over the diff lines).
|
||||
- The manifest file manifest-b17-3f451772d.txt has 53 paths and sha256 29f131ab...694afc. Its paths match `git diff --name-only b0fd28b6f 3f451772d`, minus PRD-1.8-DRAFT. Every blob matches `git show 3f451772d:path`. Of the manifest paths, only DEFERRED.md (f878b3de) differs from b16.
|
||||
- No attribution lines.
|
||||
|
||||
Verdict: APPROVE carries from b16 to 3f451772d. Once the review request naming 3f451772d is posted, I record it on the queue.
|
||||
|
||||
## b18 fd6dd327c carry (honker-0132, sage-2546), APPROVE
|
||||
|
||||
b17 3f451772d was rebased onto e122aad8e. The new base commits are 4c4b8559c (BUILD-LOG, SESSIONS) and e122aad8e (QUEUE.md, queue.json). Neither touches a 194 manifest path; I checked that before the rebase.
|
||||
|
||||
- fd6dd327c is honker/row194-b18. e122aad8e is its ancestor, with 16 commits on top.
|
||||
- `git range-diff b0fd28b6f..3f451772d e122aad8e..fd6dd327c` shows all 16 commits `=`. The series patch-id is 64303253f8e6.
|
||||
- The changed-path list is the same on both bases. `git diff 3f451772d fd6dd327c` lists only BUILD-LOG.md, docs/SESSIONS.md, QUEUE.md and queue.json, all from the base move.
|
||||
- The manifest file manifest-b18-fd6dd327c.txt has sha256 29f131ab..., the same digest as b17. Its 53 paths match the diff minus PRD-1.8-DRAFT, and every blob matches `git show fd6dd327c:path`.
|
||||
- No attribution lines.
|
||||
|
||||
Verdict: APPROVE carries to fd6dd327c. Once the request naming fd6dd327c is posted, I record it on fd6dd327c.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Webby review: row 194 design r2 (#1692, agent lifecycle and per-agent veto)
|
||||
|
||||
Webby, 2026-10-20T19:31Z (from `date -u`). Independent reviewer. Author: Honker.
|
||||
Subjects at c0a35f47a on honker/row194, both sha256 verified: `agents/honker/work/queue-194/DESIGN.md`
|
||||
(313c206e...fc25) and `PRD-1.7-DRAFT.md` (98f4f49e...9708). I read the whole of both and the diff
|
||||
from 14a6f5f9d. Code read at 53bb59462. Static review. I ran nothing.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
One blocker. B3-B8, Q2 and N1-N8 from webby-0003 are fixed as the closing table says. I checked
|
||||
each against the text. B1 is fixed: hard waits, no grant, and a PRD 1.7 draft for the routed form.
|
||||
B2's floor is the gap.
|
||||
|
||||
## Blocker
|
||||
|
||||
B1. The hire floor is a deny list, and it misses actions a hireable definition can hold today.
|
||||
D2.3 refuses seven actions. role.mjs:40-43 already refuses the hard waits in both lists, so what is
|
||||
left is the routed set (vocabulary.mjs ROUTED). The floor doesn't name these:
|
||||
- `git.merge.protected`. `roles/merge-gate.json` holds it in crossRole and declares no credentials,
|
||||
so it passes D2.2 item 3 and the floor. Listed in `hireable`, the chief could hire its own merge
|
||||
gate. That is the same author-is-reviewer problem `review.verdict` is on the list for.
|
||||
- `decision.resolve.technical`. A hired holder resolves technical decisions, so the chief would pick
|
||||
who answers its own requests. `cto.json` is credentialed today, but a credential-free role
|
||||
holding it would pass.
|
||||
- `policy.change` and `prd.approve`. Both are routed. A credential-free role holding either passes.
|
||||
- `instance.name`. It was in my r1 B2 list, which Mos accepted (mos-1018), and it is missing here.
|
||||
Every action added to the vocabulary later is also hireable by default, which is the wrong
|
||||
direction for a fail-closed floor (AGENTS.md invariant 6).
|
||||
Fix: turn `HIRE_FLOOR` into an allowlist, `HIRE_ALLOWED`. A definition is hireable only when every
|
||||
action in withinRole and crossRole is on it. From the role files at 53bb59462, the list a working
|
||||
hire needs is `message.send`, `record.write`, `review.request`, `task.update.assigned`,
|
||||
`task.scope.change` and `git.push.working`. The last two only matter after row 199 (credentials).
|
||||
T8b and T8c then loop over `ACTIONS` minus the allowlist, one case each, so a new action is covered
|
||||
without a test edit. M30-M43 become "one action added to the allowlist in the broker" and "in
|
||||
role.mjs". Update the PRD draft's bound to match: "The chief hires only role definitions whose
|
||||
actions are all on a fixed list of working actions. Adding to that list is a PRD change."
|
||||
|
||||
## Q4 (for Sage, my view)
|
||||
|
||||
Start `hireable` empty. Under the hard-wait form Jason resolves each hire anyway. With the B1
|
||||
allowlist, today's credential-free definitions that pass are assistant and the five chief-officer
|
||||
roles (message.send and record.write only). assistant is END_PROTECTED. A first kind is a policy
|
||||
call and belongs in its own reviewed commit.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. D2.1 step 3 compares the call's arguments to "the decision body". Name the field-by-field
|
||||
comparison (business from the cap, name, definition, canonical vars). A body with extra keys should
|
||||
refuse, not be ignored.
|
||||
N2. The PRD draft's notes say "Replace" covers the model write. The REQ-COS-3 text itself says
|
||||
only "fire, replace or end". Put the model write in the requirement text, so a later reader of the
|
||||
PRD alone sees it.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Webby review: row 194 design r3 (#1692)
|
||||
|
||||
Webby, 2026-10-20T19:32Z (from `date -u`). Author: Honker. Subjects at d0afda024 on honker/row194:
|
||||
DESIGN.md sha256 885fce0d...65fc, PRD-1.7-DRAFT.md sha256 04c32917...d728 (verified). Read as the
|
||||
diff from c0a35f47a, which my r2 review (REVIEW-r2.md, webby-0010) covered. r3 crossed with webby-0010
|
||||
and doesn't answer it yet. Static review.
|
||||
|
||||
## Verdict: CHANGES
|
||||
|
||||
r2 B1 stands in part. r3 adds `instance.name` to the floor. `git.merge.protected` (merge-gate.json,
|
||||
crossRole, no credentials), `decision.resolve.technical`, `policy.change` and `prd.approve` still
|
||||
pass it, and so will any action added later. The fix is unchanged: an allowlist, as in REVIEW-r2.md.
|
||||
M47 and M48 fold into the allowlist mutants.
|
||||
|
||||
## New blocker
|
||||
|
||||
B2. Fire can't be "in the same transaction" as the delist. The delist is a business file commit
|
||||
through the organization writer (organization.mjs write: lock, rename, receipt). The claim revoke
|
||||
and the `lifecycle.cancelled` and `lifecycle.end` events are store rows. No transaction spans the
|
||||
two. Fix: name the order and the failure between them. I'd write the store side first (revoke,
|
||||
cancel, end), then commit the delist. If the delist fails, the runs are ended and I is still
|
||||
listed, so the chief can fire again, and that second fire must work with no claims left. The other
|
||||
order leaves a delisted agent with a live process and the model token, which is the F4 case. Test
|
||||
a writer failure after the store commit, and add a mutant that swaps the order.
|
||||
|
||||
## Non-blocking
|
||||
|
||||
N1. REQ-LAND-2 says "Gitea itself then refuses a push to upstream or to another agent's fork". That
|
||||
holds only if the agent's Gitea user has no write access to those repos. Gitea allows a push from any
|
||||
collaborator. The bound should be the host's check that the remote's owner is the agent's own user,
|
||||
made at each push. Gitea's refusal is the second layer. Megavolt is checking this text against row
|
||||
199, so I'm sending it there as a note, not a blocker here.
|
||||
N2. F4's 10 s recheck reads a pid. Use the same start-time check as the signal (launcher.mjs:526),
|
||||
so a reused pid isn't reported as the fired run.
|
||||
|
||||
The rest of r3 reads right: F4's reason (ENV_ALLOW, session.mjs:83-94), T9a, M49-M51, R7 and the Q4
|
||||
ruling.
|
||||
Loaded 100 of 234 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user