Publish the valid-but-unbound claim; the missing shape was confounded

The example set satisfied §11's both-shapes clause only by accident. Both
values of pdp_digest and pdp_path appeared, but they appeared in perfect
correlation with validity: claim.valid carried a digest with pdp_path true,
claim.revoked carried null with pdp_path false, and nothing else existed.

Two independent dimensions presented as one. A reader could reasonably conclude
that pdp_digest is null because the claim is revoked, or that pdp_path tracks
validity. Both are false, and the example set is what would have taught them --
the failure the both-shapes clause exists to catch, which this repo proposed and
then shipped a case of.

The missing shape is the consequential one: a claim that is entirely valid --
valid_now true, reason_code ok, not consumed -- and carries no PDP binding. It
is usable for a consumer comparing the native binding.digest and unusable on the
GH-DEC-2026-003 path, where a PEP MUST refuse it. valid_now true is not
permission to proceed on that lane. A consumer writing that refusal previously
had no published shape to test against and would have had to invent a fixture,
which is the drift §12 names.

examples/claim.valid.no-pdp.json publishes it. The tests now assert the
decorrelation rather than mere presence: one requires a valid claim with no PDP
binding to exist, the other requires valid claims to cover both pdp_path
declarations. Verified both fail when the new example is removed, so they hold
the property rather than restating today's file list. 121 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM5HnEAhokxdfcPqBNpT7D

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715850@bnt-lap001
Assistant-Session: eb557e93-7cb1-45d0-9e57-7d15b3edc60e
This commit is contained in:
tegwick 2026-09-07 13:48:00 +02:00
parent 119359c33d
commit d5d1e41035
3 changed files with 99 additions and 2 deletions

View file

@ -272,5 +272,25 @@ Local fixtures, workplan ids, and prose are not this claim.
## Examples
See [`../examples/claim.valid.json`](../examples/claim.valid.json) and
[`../examples/claim.revoked.json`](../examples/claim.revoked.json).
| Example | Shape it publishes |
| --- | --- |
| [`claim.valid.json`](../examples/claim.valid.json) | valid, on the PDP path — `pdp_path: true`, `pdp_digest` non-null |
| [`claim.valid.no-pdp.json`](../examples/claim.valid.no-pdp.json) | **valid, with no PDP binding**`valid_now: true`, `reason_code: ok`, `pdp_path: false`, `pdp_digest: null` |
| [`claim.revoked.json`](../examples/claim.revoked.json) | revoked — `valid_now: false`, `reason_code: revoked` |
The middle one is the shape most likely to be missing from a consumer's tests,
and it is the one that matters most on a privileged lane: the approval is
genuinely valid and genuinely usable — for a consumer comparing the native
`binding.digest` — while being **unusable on the `GH-DEC-2026-003` path**, where
a PEP MUST refuse it. `valid_now: true` is not permission to proceed on that
lane.
It is published because the first two examples alone would confound two
independent dimensions. With only a valid claim carrying a digest and a revoked
claim carrying none, a reader can reasonably infer that `pdp_digest` is null
*because* the claim is revoked, or that `pdp_path` tracks validity. Both are
false, and the example set is what would have taught them — the failure
`security-layer-model` §11's both-shapes clause exists to catch.
`tests/test_examples.py` asserts the decorrelation, not merely that both values
appear somewhere.