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:
tegwick 2026-09-06 09:32:20 +02:00
parent 865bab3955
commit f0f888ca7e
4 changed files with 196 additions and 4 deletions

View file

@ -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.

View file

@ -27,8 +27,9 @@ Assembly and circulation are `GH-WP-0003-T06`.
| A2 | §11 | `docs/contracts/approval-emission-detection.md` | T02 |
| A3 | §13, §13.1 | `GH-DEC-2026-006` | T03 |
| A4 | §9.5 | `GH-DEC-2026-007` | T04 |
| A5 | §6.4, §8 | `GH-DEC-2026-005` | T05 |
| A5 | §6.4, §8 | `GH-DEC-2026-005`, `GH-DEC-2026-008` | T05 |
| A6 | §17 | `GH-DEC-2026-004` | T07 |
| A7 | §11, §12 | four observed instances; proposed by `approval-engine` | T08 |
---
@ -230,8 +231,16 @@ join two artifacts will re-derive it or get it wrong.
> second check at the consumer. That is detection, not prevention, and it belongs
> in the same register as §9.6's other residual.
>
> Raised by `approval-engine`, and by `access-engine` against its own proposed
> artifact. Settled in `GH-DEC-2026-005`.
> **Correspondence is established by identity, not by translation.** Where two
> artifacts use different vocabularies for the same request, the consumer compares
> a digest one layer computed and the other recorded — it MUST NOT recompute one
> layer's binding from the other's vocabulary, and no cross-engine mapping is
> published for it to use. A mapping can be wrong in a way that still produces a
> confident answer, and it would be a third authority on what a request is.
>
> Raised by `approval-engine`, and by `access-engine` twice against its own
> interest — once on its proposed artifact and once on a gap it declined to close
> locally. Settled in `GH-DEC-2026-005` and `GH-DEC-2026-008`.
**Add to §8's demarcations:**
@ -274,6 +283,61 @@ statute is told the estate has not decided something it has.
---
## A7 — §11 and §12, derived summaries must be marked (T08)
**Why.** Four instances in one week, in four repositories, each caught only by another
repository reading the authoritative body:
- gate-house sourced a revisit trigger in `GH-DEC-2026-005` from a 2026-08-29
alignment record rather than from `flex-auth`'s current schema;
- gate-house read a change log for what §3.4 contained, which is the defect
`kings-guard` raised against v0.6 and the reason
`docs/conformance/2026-09-06-v06-findings-audit.md` checked the body instead;
- `access-engine` published a dual-control rule written against its own assumption of
the approval claim rather than `approval-engine`'s published schema — it required a
status field and an approver list that do not exist, and every live destroy would
have denied regardless of the approval (failed closed, fixed in `68ad039`);
- `secrets-engine` read a fixture for its contract's prose, and separately carried two
workplans asserting that remaining authorization work was external, which kept an
unimplemented local stub invisible.
Different artifact classes — change log, alignment record, fixture, workplan summary —
one shape. As `approval-engine` put it, at four instances this is not coincidence but
**a property of how this estate publishes**: authoritative bodies with derived
summaries and examples alongside them, and no staleness marker on the derivatives. A
reader cannot tell from the artifact whether it is current, so the diligent and the
careless read it the same way.
Three of the four were reported by the repository that had made the error, against its
own interest. That is the loop working, and it is also the argument for a check: a
control that depends on four repositories volunteering corrections is not a control.
**Add to §11's "Mechanically checkable" list:**
> - every derived artifact in an estate repository — change log, review record,
> alignment note, example, fixture, or summary that restates a normative body — is
> marked as derived, names the authoritative artifact it derives from, and carries
> the version or commit it was derived at.
**Add to §12, as a paragraph of the loop:**
> **A derived summary is not evidence of what the body says.** A repository acting on
> another repository's contract, schema, or standard MUST read the authoritative
> artifact, not a summary, example, or review record of it. This is not a counsel of
> care: the estate has produced four instances in a single week, in four repositories,
> each caught only because a second repository read the body. Diligence did not
> prevent any of them, and in three the erring repository reported itself.
>
> The obligation on the publishing side is §11's marking rule; this is its consumer
> half. Where a derivative is unmarked, treat it as stale.
**Note on scope.** This is the cheap half. It does not detect a derivative that is
marked and *still* wrong, and it should not claim to — the marking makes staleness
visible, it does not measure it. Naming that limit here so the check is not later read
as stronger than it is, which would be the same defect one layer up.
---
## Not in this set
Recorded so the omissions are deliberate rather than forgotten.

View file

@ -4,7 +4,8 @@
**PIP:** approval-engine
**PDP:** access-engine (currently flex-auth)
**Status:** normative for GH-WP-0002-T06; recorded as `GH-DEC-2026-003`,
amended by `GH-DEC-2026-005` (step-1 artifact and split validation)
amended by `GH-DEC-2026-005` (step-1 artifact and split validation) and
`GH-DEC-2026-008` (the PDP digest is the binding correspondence)
**Date:** 2026-09-06
**Statute:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` §9.4, §9.7.34, §16
**Blocks:** `APPROVAL-WP-0001-T05`, `FLEX-WP-0017-T05`
@ -60,6 +61,20 @@ claim.
holds no runtime approval authority; the issuer to check is the engine that
served the claim.
**The correspondence is the PDP digest.** Amended by `GH-DEC-2026-008`. A consumer
on this path MUST verify that `claim.binding.pdp_digest` equals the decision's
`NewDecisionBinding.request_digest`, and MUST NOT use a claim that carries no
`pdp_digest`. Recomputing `approval-engine`'s native `binding.digest` from a
`CheckRequest` is **not** a permitted fallback here: it requires translating between
two engines' vocabularies, no mapping is published, and a wrong translation fails
open by silently accepting a claim approved for something else. The native limb
survives only for consumers already in `approval-engine`'s vocabulary, including the
`T-06` assurance case.
Without this, `valid_now: true` plus an ALLOW establishes *approved* and *permitted*
but never *approved for this request*, which is the property the binding exists to
carry.
## Why consume is before the side effect
§9.7.3 says consumption MUST NOT be inferred from a decision record. That

View file

@ -163,3 +163,22 @@ cite the decision; keep §17's drafter credit to `kings-guard`.
Surfaced by `docs/conformance/2026-09-06-v06-findings-audit.md`, which also confirms
that all fifteen v0.6 review findings are dispositioned in v0.7 — this is the one
paragraph a later decision made stale, not a missed finding.
```task
id: GH-WP-0003-T08
status: done
priority: medium
```
**Derived summaries must be marked (A7).** Four instances in one week, in four
repositories, of a repository acting on a derived summary rather than the
authoritative body — a stale revisit trigger here, a change log read for section text
here, a dual-control rule written against an assumed schema in `access-engine`, a
fixture read for contract prose in `secrets-engine`. Proposed by `approval-engine` on
the observation that at four instances it is a property of how the estate publishes
rather than four separate lapses: authoritative bodies with derived summaries beside
them and no staleness marker on the derivatives.
Drafted as A7 — a §11 marking obligation on the publisher and a §12 paragraph on the
consumer. The limit is stated in the draft: marking makes staleness visible, it does
not detect a marked derivative that is still wrong.