diff --git a/decisions/decisions.md b/decisions/decisions.md index 4edbf83..eee5a94 100644 --- a/decisions/decisions.md +++ b/decisions/decisions.md @@ -550,9 +550,10 @@ object — remains coherent and is recorded here so it is not rediscovered as ne is not adopted now because it has no named issuer or lifecycle owner and the sequence is already settled without one. Revisit on any of: a third or fourth PEP-shaped consumer; a requirement for a single signed forwardable artifact (offline -verification, or an audit that must replay one object rather than a join); resolution -of flex-auth's open G3 finding by composition rather than by adding a lifetime to the -envelope; or assent of the statute §17 Taxonomy request-claim schema +verification, or an audit that must replay one object rather than a join); +~~resolution of flex-auth's open G3 finding by composition rather than by adding a +lifetime to the envelope~~ — **struck, see the amendment below**; or assent of the +statute §17 Taxonomy request-claim schema (`APPROVAL-IN-0001`) — in which case converge there and do **not** revive this separately. @@ -562,6 +563,64 @@ endpoint (structurally impossible for step 1, and it would assert an authority or `entries` (publishing approver identities is a deliberate least-disclosure non-goal, and it would still not satisfy the validator). +### Amendment, 2026-09-06 — the G3 trigger is struck + +Recorded the day after this decision, on corrections volunteered independently by +`approval-engine` and `access-engine`, and verified here against +`flex-auth/schemas/decision_envelope.schema.json`. + +`FLEX-WP-0019` closed G3 on 2026-09-02 by **adding** the field: the schema requires +`lifetime` whenever `effect` is `allow`, sourced from the policy package's +`allow_ttl` with a 15-minute engine default, and `allow_ttl: none` renders a DENY +with reason `allow_lifetime_unstated` rather than a standing grant. + +The trigger was therefore already spent when this decision was written, and it +resolved on the branch that removes the argument rather than the one that supports +it. This is not bookkeeping. Carrying its own end was the one structural thing the +composed bundle did that the split does not, so the strongest available case for +ratifying option D is not merely unspent — **it is answered, and the answer is no.** +A `DecisionEnvelope` now states its own lifetime without borrowing an +`ActionAuthorizationValidity`. + +Three revisit triggers remain: a third or fourth PEP-shaped consumer; a requirement +for a single signed forwardable artifact; and assent of the §17 request-claim schema, +where the instruction is to converge rather than revive. + +The error came from sourcing the trigger from a 2026-08-29 alignment record rather +than from current state — the same class of mistake as reading a change log for what +the body says, which the v0.6 findings audit hit on the same day +(`docs/conformance/2026-09-06-v06-findings-audit.md`). Both repositories reported it +against their own interest, which is why it was caught within a day. + +### Stated consequence, 2026-09-06 — what the PEP stopped checking + +Raised by `secrets-engine` on implementing the split, against its own position, and +recorded here rather than left in a commit message. + +The distinct-approver threshold is no longer verified independently at the PEP. The +claim exposes no approver entries — publishing approver identities is the deliberate +least-disclosure non-goal this decision upheld — so `approval-engine` folds the +threshold into `valid_now`. That is the correct layer under this doctrine and it is +also **a genuine reduction in what the PEP checks on its own**, and the second thing +does not stop being true because the first one is. + +The estate should hold this consciously. `valid_now` is a summary predicate, and a +consumer of a summary trusts the issuer's evaluation of everything folded into it. +The compensating property is not a second check at the PEP — that is the duplication +the split removes — but reconstructability at the issuer: `approval-engine`'s state +transitions and its `use` outbox row must make the threshold evaluation recoverable +after the fact under §9.6. Detection, not prevention, and it should be named as such +in the same register as §9.6's other residual. + +A second consequence, also from `secrets-engine` and also correct: two digests now +cover the same proposed action and are never compared — `approval-engine`'s binding +digest over `{action, actor, principal, purpose, target}` and the flex-auth +`CheckRequest` digest, with `claim.binding.pdp_digest` preferred where the issuer +recorded one. They answer different questions at different layers and collapsing them +would be a layer violation wearing the costume of a refactor. `secrets-engine` holds +a test asserting the two functions disagree, which is the right way to defend a +distinction that looks like duplication. + ## Reversal Revert this record and the amendment to `docs/contracts/approval-consumption.md`. The