diff --git a/agents/megavolt/work/queue-281/BUILD.md b/agents/megavolt/work/queue-281/BUILD.md index dab420e49..7f0dfb2a9 100644 --- a/agents/megavolt/work/queue-281/BUILD.md +++ b/agents/megavolt/work/queue-281/BUILD.md @@ -1,7 +1,7 @@ # Row 281 build notes: a fire or a delete revokes the instance's grants (#1786) -Builder Megavolt, reviewer Dewey, gate owner Sage. Design draft d2 for Dewey's approval. d2 adds Sage's -Q1/Q2 rulings (sage-2573) to d1 (dfebd16cc). No code until it is +Builder Megavolt, reviewer Dewey, gate owner Sage. Design draft d3 for Dewey's approval. d2 added Sage's +Q1/Q2 rulings (sage-2573) to d1 (dfebd16cc). d3 adds the approved-before-fire tests T6/T6b/T6c (sage-2574). No code until it is approved. The results sections are filled in after the runs on Sage's gate. ## Sources @@ -136,6 +136,13 @@ A host named only by an open decision has no record. Step 3a still runs `remove` `#revokedSince` then ends that decision `revoked`. The Gitea side is the same through `#giteaRevokedSince`. +This is the case where a decision is approved before the fire and executed after it (sage-2574). Both checks +look for a `grant.revoked` of the agent recorded after the decision was raised (broker.mjs:1156 and :1678 at +47a35bf43). A fire's `grant.revoked` is always later than the approval, so the executor ends the decision +whenever it runs. The fired agent keeps its roles entry, so `#hostDefinition` and `#giteaRole` still pass, and +this check is the only thing that stops the decision. T6 and T6b test it. A delete also removes the roles +entry, so the executor refuses there as well. + ## 4. A refusal (D4) Step G stops at the first grant that can't be revoked. Grants already revoked in this step G stay revoked and @@ -221,8 +228,14 @@ Bus, with the fake installer and the fake Gitea the row 235 and 236 tests use: unrevoked. A repeat with B reachable completes and revokes only B and Gitea. - T5: Gitea's admin token can't be checked. The fire refuses with the Gitea grant intact. - T5b: a token DELETE fails. The fire refuses `gitea-left`. A repeat with Gitea back sweeps and completes. -- T6: an approved host grant decision that hasn't run yet, for the fired agent: the fire records - `grant.revoked {granted: false}`, and the executor then ends that decision `revoked`. +- T6 (sage-2574): a host grant decision approved before the fire, executed after it. The fire records + `grant.revoked {granted: false}` for (agent, host). The executor runs after `fire.done` and ends the decision + `revoked`: no `host_grants` row, no key file, no `add` sent to the host. +- T6b (sage-2574): the same for a Gitea grant decision. The executor ends it `revoked` before any Gitea call: + no `gitea_grants` row, no token file, and the fake Gitea saw no token create. +- T6c: a host or Gitea executor already in flight when step G starts. The host side aborts its claim (step + 3a.1) and ends `revoked`. The Gitea side ends `revoked` at its step 8 check. Build-time check: a token the + in-flight executor created before that check must be deleted, or named in `serviceLeft`, and not left live. - T7: an instance with no grant fires exactly as today. Same result keys, and the same events, counted. - T8: hosts.revoke and grants.revoke keep their records, their order and their tester refusals. @@ -237,7 +250,7 @@ G closes the request with `grant-revoke-failed`, if the launcher's tests stub `l - M4: `lifecycleDelete` skips step G. Killed by T3. - M5: the fire's host revoke goes local first (`lineFirst: false`). Killed by T4, because B's row is gone. - M6: a Gitea revoke with something in `serviceLeft` counts as done. Killed by T5b. -- M7: open decisions left out of the set. Killed by T6. +- M7: open decisions left out of the set. Killed by T6 and T6b. - M8: step G skips the leftover-line retry. Killed by T4b. - M9: a failed leftover-line retry refuses the fire. Killed by T4b.