docs(plans): S2b brief, gated approvals are single-use

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-05 16:44:10 -05:00
co-authored by Claude Opus 5.5
parent 85e8f97dbf
commit 623d1b6cc4
@@ -0,0 +1,60 @@
# Slice 1 S2b: gated approvals are single-use (2026-10-05)
Status: written by Sage, lead, under lead decision 63. It follows
`docs/plans/BRIEF-TEMPLATE.md`, and it amends no section of the slice 1
brief.
## S2b: gated approvals are single-use (authorize records consumption)
### Problem
In S2 round 2 (`agents/rocko/work/slice1-s2-r2/`, candidate manifest
61519059…), `broker.authorize(cap, action, {decision, target})` checks
that the decision is resolved and approved, then appends an
`action.allowed` event. Nothing stops a second call with the same
decision. Darkwing's probe P3 (`agents/darkwing/work/slice1-s2-review/`)
deploys three times on one approval. A gated decision is Jason saying yes
to one action, so one approval must authorize one action.
### Owner and reviewer
- Owner: rocko.
- Reviewer: darkwing.
### Files owned
- `packages/bus/src/broker.mjs` and `packages/bus/src/store.mjs`, for the
check.
- `packages/bus/tests/broker.test.mjs` and, if a new test file is
cleaner, one new file under `packages/bus/tests/`.
- `packages/bus/README.md`, the paragraph on `authorize`.
### What ships
- For a gated decision, `authorize` refuses with `decision-consumed` if
an `action.allowed` event already names that decision. The check and
the new event run in the same transaction, so two concurrent calls
can't both pass.
- Routine, within-role and cross-role decisions keep their current
behavior unless Darkwing's review shows the same hole matters there.
If it does, the review names it and I rule on it.
- No schema change is expected, because the `action.allowed` event
already carries the decision. If one is needed, Darkwing writes it as
schema v3c and S2b pins it.
- Tests: the second use refuses, a use in another run refuses, a use
after the broker restarts refuses, and a fresh approval of the same
action works. Add a mutant that drops the check.
- Suites: `packages/bus` with the glob form on Node 24 and 26, plus
every `scripts/test-*.sh`.
### Out of scope
- Who calls `authorize` and when. That's S3's tasks and S6's launches.
- Expiring an approval that is never used.
### Gate
Darkwing approves on the row's issue. Sage runs the integration gate
(every suite, live-provider cases included) on an index export before
committing. This has to land before S3 or S6 calls `authorize` for a
gated action.