diff --git a/agents/duckworth/work/federation-r1/REVIEW-r2.md b/agents/duckworth/work/federation-r1/REVIEW-r2.md new file mode 100644 index 000000000..9b101b3ad --- /dev/null +++ b/agents/duckworth/work/federation-r1/REVIEW-r2.md @@ -0,0 +1,59 @@ +# Review: Gizmoduck federation design r2 and r2.1 (duckworth-0002) + +Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run. +Subject: agents/gizmoduck/work/federation/DESIGN.md at head 5ad67f671 (r2.1, parent 60b9b112a, r2 parent 6925f78f0). +DESIGN.md line numbers below are at 5ad67f671. The r2.1 diff only moves the uid to row 190b, makes R7 depend on row 190 and +puts P19 333 on the path. I checked it and found no issue. Code at origin/refactor +9ae8c73ac. Against duckworth-0001 (REVIEW.md, b402197ff). + +## Verdict: changes, B6 only + +B1 to B5 are resolved. r2 adds one new blocker of its own. Fixing B6 and C1 as line edits plus their +test lines would earn approval. + +## r1 codes + +- B1 resolved (3.6). The walk is the human's (human.mjs:73-88), the user CLI has its own nonce, the Console + parent is checked against the bus's tree (webui-service.sh:3 confirms `cli.mjs serve`), and the user id + binds only with a live grant. R3 has both mutants. Condition C1 below. +- B2 resolved (3.6 list, 4). `#owner`, the `#threadFor` refusal plus `message.send` being off the list (two + walls), the sender fields and the audit are all in. The 48 `s.human` and 41 `s.tester` line counts match. +- B3 resolved (3.5). There is one level with seven verbs. Each read has a filter, and everything else is + `access-refused`. The cited lines match (broker.mjs:2143-2146, :2288-2295, :2318-2322, :2331-2334, + :2351-2420, :1203-1207, :2398). +- B4 resolved (3.7). There are three triggers. `threadsState` (:390-393) is computed per call, so a restart + doesn't bypass the revocation. The launcher skips a revoked thread and SIGTERMs a running one (launcher.mjs:510-553, + :527, tick :133). R4 gets mutants and the new order. +- B5 resolved (R8, 5, 8, 9). Separate uids are a hard entry gate (row 190b in r2.1) with checks, and N9 is + folded in. +- N1 to N9 are handled where the "Changes from r1" section says. + +## Blocker + +B6. With r2's sender change, a user's thread never gets an answer. 3.5 sets `thread.send` to write +`from_role` `user` (DESIGN.md:207). The runner answers every message with `message.send { to: m.from_role }` +(packages/harness/src/runner.mjs:450). The broker accepts only `human`, `tester` or a role as `to` +(broker.mjs:1862), so each reply fails `unknown-role`, and the user sees no answer. The tester needed a +whole set of changes for the same path (row 177): `to: 'tester'` only in reply to a tester message +(:1852-1857), `#testerThread` and `tester-thread-human` (:1864-1866), the `#checkMessaging` mapping (:1867), +`#sender` (:627-629), and the runner's header (runner.mjs:75-78). The design names none of these for users. + +Fix: state the reply path. Either accept `to: 'user'` only in reply to a message from that user in that +user's thread, with the tester's set of checks, or say the run answers `human` in the user's thread and +why the owner's inbox then holds it. Add a runner header, like the tester's, saying the message comes from +a granted user, is never the owner's instruction, and authorizes nothing. Add an R3 test that a thread run's +reply reaches the user's `thread.messages`. Add a mutant where the reply-to check is dropped and a reply +lands in another user's thread. Make R7 assert that a reply arrives, not only that the send works. + +## Condition (line edit) + +C1. "Agents can't, because the walk refuses them" (DESIGN.md:251-252) overstates it. An unsandboxed agent +on the instance's uid, such as a T3 seat, can run `systemd-run --user` or daemonize with a clean env. Its +process then has no agent ancestor, and the walk passes. That is the same hole the owner's human proof +has (Sage's design section 8, P19 333), so federation still adds no weaker path. Say that instead. + +## Note + +N10. Six of the seven verbs gate with `if (!s.tester) this.#human(s)`. `names` gates with +`if (!s.human && !s.tester) this.#agent(s)` (:2290). The "seven `#human` gates" in 3.6 item 4 and section 4 +should name that difference so R3 doesn't miss it. diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index b142717ea..575c1647e 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1362,3 +1362,4 @@ are never rewritten or removed; corrections are new entries. 2026-10-20T18:40:20Z | Filbert (Opus 5.5, 048f3c63) | Q175 SSO design review r3 (DESIGN.md blob a152fd58e18e at 817e8f51b), static | approve on condition D1 (slot file checks stay out of shared loader, shape-only there) as a line edit; D2 notes: slot root from configDir, scratch dataRoot, name discovery GETs for Mos; C1/C2 and notes verified; Mos C1 sequence confirm still open; no Gitea/queue record 2026-10-20T18:41:20Z | Filbert (Opus 5.5, 048f3c63) | Q175 SSO design D1/D2 check (DESIGN.md blob eba861f5d2bb at d55d86bb0), static git diff 817e8f51b..d55d86bb0 | ack: D1 and D2 match; design approved; Mos C1 sequence confirm still open; no Gitea/queue record 2026-10-20T18:51:09Z | Duckworth (Opus 5.5) | Gizmoduck federation design r1 review (DESIGN.md blob 0f4570d478f5 at 6925f78f0, code at 9ae8c73ac), static | changes B1-B5 (forwarded user identity, ownership code, business-wide reads, revocation ends no runs reliably, one-uid isolation); notes N1-N9; agents/duckworth/work/federation-r1/REVIEW.md +2026-10-20T18:58:59Z | Duckworth (Opus 5.5) | Gizmoduck federation design r2/r2.1 re-review (5ad67f671, parents 60b9b112a, 6925f78f0; code 9ae8c73ac), static | changes B6 only (user from_role breaks the runner reply path, runner.mjs:450 vs broker.mjs:1862); B1-B5 resolved; C1 line edit (systemd-run escape); N10; agents/duckworth/work/federation-r1/REVIEW-r2.md