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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.3–4, §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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue