Explicit configured claude-fable-5 design review completed: APPROVE, no blockers. Frozen design SHA 4e7b3ffd915b76b623aec3f70f672ff6baf08164. Review used inline authoritative source excerpts, with no code, live seed, provisioning or deployment. Local evidence: /tmp/oficina-roster-design-review/review.ndjson and final.txt. Full final review follows; nonblocking advisories belong in the implementation checks.
APPROVE
I reviewed the frozen design at 4e7b3ff against the inline evidence under the requested lenses. No blockers. The gated posture — operator delivery, live seed, generation fill, chooser go-live — is correctly held as production gates rather than implemented paths, and nothing in the design quietly substitutes for them.
Why each lens passes
- Authority boundaries. The provisioning record is verified against a pinned operator key outside call 8, returns an opaque validated type, and no request input, browser assertion, first visitor or sibling token can construct it or seed owner/generation/consumer. The consumer row binds one team/app/origin; foreign and unknown teams answer an identical 404; other teams' epochs never appear (suite ruling 1). Assertion wire v1 correctly carries no roster claim, and future hints are bounded to one refetch — consistent with the §5.4 roster_epoch exception's actual argument. The home learns nothing new; no address, and person refs travel only inside AEAD cursors and signed payloads, never logs.
- Restore/generation. Directory generation is persisted once by audited bootstrap, never guessed at startup, and an empty generation table refuses call 8 — this closes the "migration mints a valid generation" hole. An unfamiliar generation in a valid signed response fences but cannot approve; only the record update referencing the fresh-owner confirmation can, matching ruling 23. Instance restart starts Unknown; durable bytes are evidence and floors, never a lease.
- Replay. The per-refresh client challenge, requestcursorhash, page_index progression, scope binding (directory/consumer/team/audience), strict envelope parsing, purpose-bound short-lived AEAD cursors, strictly increasing record sequence, and idempotent operation IDs together close cross-refresh, cross-consumer, cross-team and cross-generation replay. The signed 409 cannot publish an empty roster and shares the same bindings. No 304 shortcut removes the "replayed unchanged extends age" class entirely.
- Age/revocation. 30/60/5 with the fetch inside the 60, monotonic deadlines, wall-clock backward fence, no age from partials/hints/failures/restart, lease recheck immediately before every commit and SSE event, a deadline timer on otherwise-silent streams, and removal bounded by the 60-second ceiling even under all-failure refresh — including already-signed-in sessions, which is where revision 3 of the doctrine failed. The design correctly marks the deadline as measured, not claimed.
- Concurrency. One flight, private staging, atomic mutex publish, staging discarded on record change, single producer read transaction so authorization and membership come from one state, epoch bumped in the write transaction, changed epoch → authenticated 409, never a mixed page. The busy-roster starvation cost is named and expires into Unknown rather than staleness.
- Guest permissions. The grant-only query mode is the important catch here: without disabling the OwnerID shortcut under NotMember/Unknown, one stale guest grant reopens every owned document — the design saw it and tests it. Self-grant as an escape is already closed (Grant refuses self, share requires a member lease). Guests keep working during a home outage on per-document rechecked grants, addresses never inherit to Home-only principals, capability links confer nothing, and the linking path is mounted outside the standing it establishes.
- Mechanical feasibility. The nested module, canonical crypto reuse with distinct contexts, handoff/seal reuse, existing directorydata validation (person_ref shape matches the migration CHECK), the principalstore contract mapping (Unknown→ErrUnavailable, fresh nonmember→ErrRefused, no network under writer), and the four-piece sequencing with unmounted seams are all buildable as written. Bounds are internally consistent (100×100=10000, 64KiB page vs 1MiB retained, 10s cursor vs 5s refresh).
Advisories (non-blocking)
- Pin
request_cursor_hashfor page 0 to one spelling — SHA256 of the empty string vs an empty/omitted field — and test it; "empty on page 0" currently reads both ways. - State explicitly that the signature is over the decoded payload bytes, not the base64url text. The Verify call implies it; a builder deserves the sentence.
- Define a tolerance for the backward wall-clock fence so routine NTP slew doesn't fence standing continuously, and test the threshold.
- Implement grant-only mode as one scope constructor (a single choke point), not per-call-site flags — the mutation test for "unchecked OwnerID fallback" should break any query path that skips the mode, including
getDocumentForUpdatesand the?since=fetch path. - Validate the client-supplied
fetchchallenge as exactly canonical 43-char base64url before database work. "Alternate encodings" implies it; making it explicit also bounds the signing-oracle surface of echoing caller-shaped strings under the page context. - Extend the AGENTS.md gate-1/gate-2 corpus with seeded bearer tokens and cursor plaintext contents in pieces 1–2, not at the end — a guard written after the code it guards is written to pass.
- Name the deliberate divergence from doctrine §5.3's wire sketch (no
epochquery param, no unchanged shortcut) in the A.1 note, so a later builder doesn't "restore" the sketch and reintroduce an age-extension path. - Pin the SSE deadline-timer behavior on lease renewal (re-arm vs close-and-reopen); both are safe, but the test must know which one it's pinning.
- Rollback to an older signed provisioning record after durable-state loss is defended only by operator delivery and the fresh-challenge protocol; say so in the record verifier's comment as the residual threat rather than leaving it implied.
The two absolutes this design had to hold — a removed member's standing dies within the ceiling even when everything fails, and no unauthenticated or replayed byte can extend it — both hold as specified.