Fix the destroy rule: it was written against an invented claim shape
approval-engine flagged the class one message earlier -- a contract whose examples contradict its prose gets implemented as its examples -- and yesterday's package was a fresh instance of it, committed while flagging it. The published rule required context.approval.status == "approved" and counted context.approval.approvals[].subject_id. Neither field exists. approval-engine's approval_claim.schema.json has `state` (whose operative value is `valid`, not `approved`) and carries no approver list at all. The rule was unsatisfiable: every live destroy would have denied dual_control_required no matter how good the approval was. It failed closed, so it was never a hole, but it was policy written against a shape of our own devising rather than a published one. The rule now consumes valid_now from the real claim, guarded on kind and issuer. valid_now is the summary predicate that already folds in the distinct-approver threshold, with reason_code insufficient_approvers for a claim that failed it -- so this is also the correct layering, not just the correct shape. Counting approvers here is exactly the duplication GH-DEC-2026-005 removes; the compensating property is reconstructability at the issuer under 9.6, which is approval-engine's. Recorded as a correction section in the package and the vocabulary doc rather than quietly rewritten. 25 Rego tests and 29 fixtures pass, covering insufficient_approvers, consumed, revoked, approved-but-not-yet- valid, foreign issuer, and wrong kind. Two things the package deliberately does not do, both now written down: it does not compare binding.pdp_digest, because the request digest is computed after policy evaluation and a Rego rule cannot see it; and it makes no cross-check that the claim was approved for this action and target, because the claim's binding uses approval-engine's vocabulary and no mapping between the two is published. Inventing one would silently accept a claim approved for something else. Both belong to the PEP until a mapping exists, and that is worth closing before SECRETS-WP-0007-T04 makes destroy reachable. Also swept the other published fixtures on approval-engine's reasoning. One more instance: the inner decision in examples/caring/action_authorization.json declared contract_version flex-auth.decision-record.v1 while its provenance omitted policy_package_digest, registry_snapshot_digest, and input_claim_digests -- all published contract fields since 2026-09-02. Completed. The remaining example context vocabularies are consumer-owned and match their integrations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTbVXpEiXA7mNJVpDnEPcB Assistant: claude-code Assistant-Model: opus Assistant-Process: 412054@bnt-lap001 Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
This commit is contained in:
parent
6bf5eb7ff9
commit
68ad039a3f
6 changed files with 311 additions and 99 deletions
|
|
@ -19,15 +19,14 @@
|
|||
},
|
||||
"context": {
|
||||
"approval": {
|
||||
"status": "approved",
|
||||
"approvals": [
|
||||
{
|
||||
"subject_id": "human:alice"
|
||||
},
|
||||
{
|
||||
"subject_id": "human:bob"
|
||||
}
|
||||
]
|
||||
"schema_version": "0.1",
|
||||
"kind": "approval-claim",
|
||||
"issuer": "approval-engine",
|
||||
"approval_id": "3d1c0a8e-6b7f-4c21-9a0e-1f2b3c4d5e6f",
|
||||
"state": "valid",
|
||||
"valid_now": true,
|
||||
"consumed": false,
|
||||
"reason_code": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue