diff --git a/agents/gizmoduck/work/queue-234/BUILD.md b/agents/gizmoduck/work/queue-234/BUILD.md index 1a79e6183..71e71462b 100644 --- a/agents/gizmoduck/work/queue-234/BUILD.md +++ b/agents/gizmoduck/work/queue-234/BUILD.md @@ -58,6 +58,7 @@ Row a builds: 8. **Reader filter** (duckworth-0067 N1): every grant-evidence reader in this row (status, the start reconcile, `openGrantDecisions`) filters `kind = 'action.allowed' AND actor_role IS NULL AND actor_run = 'grant'`, with a test that an `action.refused` look-alike and a session-written one are ignored. + `hosts.remove`'s `hostLineLeft` reader is one of them (duckworth-0079 M4). Row b keeps the `host` method's raise binding (R4), the grant executor (X2), the grant rows of X3, X5, `Credentials.register`/`claim`/`abortClaim`, revoke (section 6) and `sweep`. So row a has no way to make a @@ -69,3 +70,83 @@ executor, G11's enrollment cases, G14, G15, G16, G17, G18, G20, G21 (its `host-l event fixture until row b writes `hostLineLeft`), G25, the migration test, the trigger cases, the reader filter test, and row 210's T-tests on store fixtures. Row b: G2 to G4, G7 to G9, G11's grant cases, G12, G13, G19, G22 to G24, G26 to G28. + +## Early check items (duckworth-0079, REVIEW-build-split.md 10672772e on `duckworth/reviews`) + +Moves into row a: +- M1. G4 for the `host-enroll` block (r6:240-242): a question can't forge the block, and the 4096-character + limit holds with the block appended. G4 for the `host` block stays in row b. +- M2. The tag function (r6:265-266), because E2 step 2 tags the installer line. G24's first sentence moves + here: names of 64 characters with `.` and `_` give distinct tags of the fixed length. The `sweep` half + stays in row b. +- M3. A store-fixture test that `host.run` refuses `host-not-granted` for no record, an unresolved decision, + a decision resolved by an arbiter, the tester or an agent or not via cli, a consume record with operation + `grant.host-enroll` in place of `grant.host`, and an unspent decision (X4 first bullet, r6:593-596). A + restart test that a `host_grants` fixture gives `Credentials` a reference (X4 third bullet). + +G21's `host-line-left` case: +- F1. The fixture goes through this row's private grant writer in the real shape: `action.allowed`, actor + (NULL, 'grant'), `body.action` 'grant.failed', `body.host`, `body.hostLineLeft`. No raw SQL around the + trigger. +- F2. `hosts.remove` reads `body.hostLineLeft` from any guarded body, not only `grant.failed`. +- F3. Decided: a guarded body that carries `hostLineLeft` also carries `host`. Row b adds `host` to + `grant.revoke.closed`, so its body is `{tag, host, hostLineLeft}` (r6:716 has `{tag, hostLineLeft}`). + The reader matches on `body.host`, and doesn't map tags back to hosts through `grant.step`. A direct + field is one equality in SQL, and a tag lookup would add a second reader that the N1 filter also has to + cover. +- F4. Row b carry: "no later `sweep` cleared it" (r6:403) needs `grant.sweep`, which only row b writes. Row + b adds the sweep clear to `hosts.remove` with a G21 case, and its G8 or G13 shows `hosts.remove` + refusing after a real `hostLineLeft` write, not only after the fixture. Until then row a's check is "any + guarded `hostLineLeft` for this host refuses". + +## Mutants (r6:838-883, numbered here) + +Row a, killed on this row's tree: + +| # | Mutant | +|---|---| +| A1 | drop the chief check | +| A2 | start the executor on a `credential.mint` with no `grant` | +| A3 | start it on an arbiter resolve | +| A4 | start it on `no` | +| A5 | skip the keyscan compare | +| A6 | compare only one key | +| A7 | write the private key into the event body | +| A8 | drop the "not yet enrolled" check in E2 step 1 | +| A9 | drop the `hosts` primary key | +| A10 | skip the start check for a resolved, unspent enrollment decision | +| A11 | `host.run` reads the business file instead of the grant record | +| A12 | build the tag without the business | +| A13 | drop `from=` in the writer | +| A14 | let the writer touch a line outside the tag pattern | +| A15 | drop `O_NOFOLLOW` on the user file | +| A16 | drop one group from the writer's refused list | +| A17 | drop the user pattern check before the path use | +| A18 | treat a `privileged` entry as enough without `users` | +| A19 | let the writer accept a sudo user not in `privileged` | +| A20 | trigger: drop the `body.operation` arm, so the consume record passes from a session | +| A21 | trigger: accept actor_role NULL with any actor_run | +| A22 | a grant reader drops the `kind = 'action.allowed'` or actor filter | +| A23 | `host.run` skips the Approve record check (any `host_grants` record is enough) | + +Row b keeps the rest of r6's list: the `ready` check, the three hash compares, the definition check, the +install and record order, `remove` on a verify failure, both start sweeps, the reconcile's remote `remove`, +the `grant.step` before install, revoke leaving the record, `register` before `issue`, the abort check and +the `await` before `issue`, revoke and an unreferenced claim, `sweep` and any `mosaic-` line, revoke's host +step order, claim before consume, the resolved unspent grant decision, an async `commit`, `hosts.revoke` +through `credential-verbs.mjs`, the revoke marker reconcile, the upsert, the undo past `grant.revoked`, the +abort after step 5 and a missing `bus.tester` as dev. + +## Broker functions this row touches (sage-1677, planned, final list with the candidate) + +For row 236 (Megavolt), which rebuilds on this landing. Cites are `packages/bus/src/broker.mjs` at 8fede5fc6. +- `recordEvent` (:1243-1251): one more refusal, `reserved-event` for a `grant.*` action or operation. +- New private `#grantEvent`, the session-less writer for guarded events. `#event` (:585-606) is unchanged. +- `decision.raise` (:1880-2029): the `grant` frame check and the rendered block, before the raise + transaction. The keyscan runs before it, as an async step in `request()` (:1362-1450) the way + `requestHost` does. +- `decision.resolve` (:2030-2107): when the human's cli `yes` resolves a `grant` mint, it starts the executor + after the transaction commits and returns `{grant: 'started'}`. +- New private `#consumeGrant` next to `#consumeAuthority` (:855-865). `#spent` (:846-852) is unchanged. +- `requestHost` (:1712-1795): reads the store records and the Approve record check in place of `b.hosts`. +- New `hosts.status|check|remove` handling in `request()`, async like `HOST_VERBS` (:95).