Close the binding-correspondence gap as GH-DEC-2026-008
access-engine raised, and declined to solve locally, a hole in the split GH-DEC-2026-005 ruled on. approval-claim verification item 4 is a disjunction and neither limb delivers "approved for THIS request" on the PDP path: limb one requires translating between two engines' vocabularies and no mapping is published, limb two (pdp_digest) is optional. Where the digest is absent a consumer can hold valid_now true, receive an ALLOW, consume and act with nothing establishing that approval and decision concern the same action and target. Ruled: the PDP digest is the correspondence and is required on that path; a claim without one fails closed; the native limb survives only for consumers already in approval-engine's vocabulary, including T-06. No mapping is published — a translation can be wrong while still producing a confident answer, it fails open, it would be owned by neither engine, and recomputing another layer's binding is the re-derivation GH-DEC-2026-005 already forbids. The cost is stated: an approval issued without a bound CheckRequest is unusable on this path, which is correct behaviour. Also: adopted hub row b606e8ce as canonical for GH-DEC-2026-005 rather than registering a duplicate; recorded approval-engine's narrowing of the approver-threshold consequence (distinctness is a UNIQUE storage invariant, so the PEP stopped checking that the engine applied its own invariant, not whether dual control could be forged); and drafted A7/T08, a §11 marking obligation and §12 consumer rule for derived summaries, after four instances in one week across four repositories. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtJBr77gMFLrN93iEevqQJ Assistant: claude-code Assistant-Model: opus Assistant-Process: 425128@bnt-lap001 Assistant-Session: f5944d8b-dac4-4e1a-87eb-8b3d8f314a63
This commit is contained in:
parent
865bab3955
commit
f0f888ca7e
4 changed files with 196 additions and 4 deletions
|
|
@ -978,3 +978,97 @@ decided_by: Bernd Worsch
|
|||
created: '2026-09-06T07:30:42.213794Z'
|
||||
updated: '2026-09-06T07:30:42.213794Z'
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
Raised by `access-engine` while implementing `GH-DEC-2026-005`, and declined by it
|
||||
rather than solved locally — the right call, and the reason it reached a decision
|
||||
record instead of a commit.
|
||||
|
||||
`approval-engine/docs/approval-claim.md` verification item 4 is a **disjunction**:
|
||||
|
||||
> `binding.digest` equals the digest of the binding the consumer computed from the
|
||||
> proposed action, **or** `binding.pdp_digest` equals the `NewDecisionBinding` digest
|
||||
> of that request.
|
||||
|
||||
Both limbs are load-bearing for one property — *approved for **this** request*, as
|
||||
distinct from *approved*. Neither limb currently delivers it on the PDP path.
|
||||
|
||||
**Limb one requires a mapping nobody owns.** The claim's `binding.action` and
|
||||
`binding.target` are in `approval-engine`'s vocabulary, over
|
||||
`{action, actor, principal, purpose, target}`. `access-engine`'s policy package and
|
||||
`CheckRequest` are in its own. To compute limb one, a consumer must translate between
|
||||
them. No mapping is published. `access-engine` declined to invent one on the correct
|
||||
ground that a wrong mapping fails **open** in the worst way available: it silently
|
||||
accepts a claim approved for something else.
|
||||
|
||||
**Limb two is optional.** `binding.pdp_digest` is present only *"when recorded at
|
||||
issue"*.
|
||||
|
||||
So on the path this estate actually runs, correspondence rests entirely on an optional
|
||||
field, and where that field is absent a consumer can hold a claim with
|
||||
`valid_now: true`, receive an ALLOW, consume, and act — with nothing having
|
||||
established that the approval and the decision concern the same action and target.
|
||||
The claim would not be lying; `valid_now` answers a different question.
|
||||
|
||||
## Decision
|
||||
|
||||
**`binding.pdp_digest` is the binding correspondence on the `GH-DEC-2026-003` path,
|
||||
and it is required there. No cross-engine vocabulary mapping is published.**
|
||||
|
||||
1. **A consumer on the PDP path MUST verify `claim.binding.pdp_digest` equals the
|
||||
decision's `NewDecisionBinding.request_digest`.** It is no longer a preferred
|
||||
comparison; it is the comparison.
|
||||
2. **A claim carrying no `pdp_digest` MUST NOT be used on that path.** The consumer
|
||||
fails closed. It is not an error to be worked around, and specifically it is not a
|
||||
licence to fall back to limb one.
|
||||
3. **Limb one survives only where the consumer is natively in `approval-engine`'s
|
||||
vocabulary** — that engine's own tests, and the `T-06` assurance case, which uses
|
||||
the native binding precisely because no PDP digest exists there. It is not a
|
||||
fallback for a PDP-path consumer.
|
||||
4. **`approval-engine` MUST record the PDP digest at issue for any approval intended
|
||||
for use on the PDP path**, and the claim schema states which approvals those are
|
||||
rather than leaving it to the requester's memory.
|
||||
|
||||
### Why not publish the mapping
|
||||
|
||||
`access-engine` offered to co-author one. Declining is the substantive half of this
|
||||
record.
|
||||
|
||||
**A mapping is a translation; a digest is an identity.** A translation can be wrong in
|
||||
a way that still produces a confident answer — the failure mode `access-engine` named,
|
||||
and it fails open. A digest comparison has no wrong-but-plausible result: it matches
|
||||
or it does not.
|
||||
|
||||
**A published mapping would be owned by neither engine.** It would sit between two
|
||||
vocabularies, each of which is its own repository's to evolve under §2, and it would
|
||||
have to be revised whenever either side changed. That artifact is a third authority on
|
||||
what a request *is* — the shape §6 exists to prevent, arriving as plumbing rather than
|
||||
as a decision point.
|
||||
|
||||
**And computing limb one is the re-derivation `GH-DEC-2026-005` forbids.** A consumer
|
||||
recomputing `approval-engine`'s native binding from its own vocabulary is a consumer
|
||||
deriving another layer's data instead of validating against the layer that owns it.
|
||||
The rule this estate just adopted answers this question on its own terms.
|
||||
|
||||
### The cost, stated
|
||||
|
||||
An approval requested without a known `CheckRequest` cannot be used on the PDP path,
|
||||
because there was no PDP digest to record. That is a real constraint and it is
|
||||
**correct behaviour, not a defect**: an approval granted against an unspecified action
|
||||
does not become an approval for a specific one because a consumer later found a use
|
||||
for it. Any workflow needing detached approval must either bind the request at issue
|
||||
or accept that its approvals are not usable here.
|
||||
|
||||
This narrows what `approval-engine` may issue for this path. It does not narrow what
|
||||
it may issue.
|
||||
|
||||
## Reversal
|
||||
|
||||
Revert if a legitimate workflow genuinely cannot bind the `CheckRequest` at approval
|
||||
time — a standing human authorization for an action whose exact parameters are not
|
||||
knowable until execution is the plausible candidate. The falsifier is a concrete
|
||||
workflow, not the possibility of one. The expected resolution is that such a case
|
||||
needs a *class* binding with its own contract, not a relaxation of this one to an
|
||||
optional field, because an optional correspondence is indistinguishable at the
|
||||
consumer from no correspondence at all.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue