2026-09-01 23:45:48 +02:00
|
|
|
---
|
|
|
|
|
id: APPROVAL-WP-0002
|
|
|
|
|
type: workplan
|
|
|
|
|
title: "Production readiness and consumer adoption"
|
|
|
|
|
domain: infotech
|
|
|
|
|
repo: approval-engine
|
2026-09-02 00:52:04 +02:00
|
|
|
status: active
|
2026-09-01 23:45:48 +02:00
|
|
|
owner: codex
|
|
|
|
|
topic_slug: netkingdom
|
|
|
|
|
created: "2026-09-01"
|
Record the claim vs ActionAuthorization envelope divergence
secrets-engine reports its PIP join is implemented and blocked on
deployment rather than contract. Reviewing its
validate_action_authorization against what this engine actually serves
shows that is not the whole story: it expects a state-hub-authority
ActionAuthorization (id, status, superseded_by, request,
approvals.entries, policy pin) while GET /v1/approvals/{id}/claim serves
the governed approval-claim (approval_id, state/valid_now, binding,
freshness, reason_code, issuer approval-engine).
Both envelopes declare schema_version 0.1, so the version check passes
and the mismatch surfaces as a field or authority error that reads like
an approval-engine outage.
Document the field-by-field divergence and why the omissions are
deliberate: a claim is a fact about an approval object, not a decision,
so approver identities and policy pins are not republished. Reconciling
the envelopes is a GH-DEC-2026-003 cross-repo change, so the governed
claim schema is left unchanged here.
No code change; 84 tests still pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 01:09:23 +02:00
|
|
|
updated: "2026-09-06"
|
2026-09-02 00:52:04 +02:00
|
|
|
reviewed_at: "2026-09-01"
|
|
|
|
|
reviewed_against_commit: "ebce5abb276c01ab29ce2526f3b8abb332dc9e90"
|
|
|
|
|
reviewed_note: >-
|
|
|
|
|
Reviewed against approval-engine's finished spine, KeyCape's RS256/JWKS and
|
|
|
|
|
service-token contract, access-engine caller-auth/binding surface,
|
|
|
|
|
audit-core's deployed authenticated ingestion plus open AUDIT-WP-0009
|
|
|
|
|
approval-source/cadence/reconciliation tasks, and secrets-engine's waiting
|
|
|
|
|
exact-action/decision-consumption tasks. Repo-owned implementation can
|
|
|
|
|
proceed; live T03-T05 closure remains evidence-gated on those owners.
|
2026-09-01 23:45:48 +02:00
|
|
|
origin: residual
|
|
|
|
|
origin_ref: APPROVAL-WP-0001
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_workstream_id: "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf"
|
2026-09-01 23:45:48 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# APPROVAL-WP-0002 — Production readiness and consumer adoption
|
|
|
|
|
|
|
|
|
|
Move the completed first-cut engine spine into an authenticated, durable,
|
|
|
|
|
observable production service and prove one PEP integration end to end. This is
|
|
|
|
|
the residual production scope deliberately excluded from APPROVAL-WP-0001.
|
|
|
|
|
|
|
|
|
|
The workplan is proposed pending review against the deployment estate and the
|
|
|
|
|
current key-cape, access-engine, audit-core, and secrets-engine contracts.
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
Review completed 2026-09-01. The plan is active. Production mode will verify
|
|
|
|
|
KeyCape JWT signatures and exact issuer/audience/scopes; local caller-supplied
|
|
|
|
|
identity never becomes authenticated evidence. SQLite remains the first
|
|
|
|
|
production store only as a single-replica StatefulSet with explicit migration,
|
|
|
|
|
backup, integrity, and restore gates. audit-core delivery can be implemented
|
|
|
|
|
against its existing authenticated idempotent ingest, while heartbeat findings,
|
|
|
|
|
count reconciliation, and sender registration remain external gates in
|
|
|
|
|
`AUDIT-WP-0009` T04/T06/T09. The first live PEP proof remains jointly gated on
|
|
|
|
|
secrets-engine T04/T02 and deployment of this service.
|
|
|
|
|
|
2026-09-01 23:45:48 +02:00
|
|
|
## Authenticate lifecycle mutations and approver evidence
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: APPROVAL-WP-0002-T01
|
2026-09-02 00:52:04 +02:00
|
|
|
status: progress
|
2026-09-01 23:45:48 +02:00
|
|
|
priority: high
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_task_id: "dc4523f5-e0af-5734-a0ef-07aa7f2b2a27"
|
2026-09-01 23:45:48 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Bind create, approval-entry, revoke, supersede, and consume callers to
|
|
|
|
|
authenticated identities. An API-supplied `subject_id`, `actor`, or
|
|
|
|
|
`decision_id` is provenance only until independently authenticated. Keep
|
|
|
|
|
authorization decisions in access-engine and approval doctrine in gate-house.
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
Acceptance: production startup requires a signature-valid KeyCape JWT verifier;
|
|
|
|
|
every non-health API route requires an explicit scope; approval entry identity
|
|
|
|
|
and assurance come only from verified claims; create binds `binding.actor` to
|
|
|
|
|
the authenticated subject; consume is restricted to service/agent principals;
|
|
|
|
|
missing, expired, wrong-issuer, wrong-audience, wrong-scope, or unverifiable
|
|
|
|
|
tokens fail closed without mutation.
|
|
|
|
|
|
|
|
|
|
Repository implementation complete 2026-09-02: RS256/JWKS verification,
|
|
|
|
|
issuer/audience/time/profile validation, exact scopes, store-tenant isolation,
|
2026-09-02 15:46:06 +02:00
|
|
|
verified approver evidence, and a deny-all default are covered by tests.
|
|
|
|
|
|
|
|
|
|
2026-09-02 follow-up: fail-closed coverage now includes wrong signature, HS256,
|
|
|
|
|
empty/invalid profile claims, deny-all mutation refusal, human-principal consume
|
|
|
|
|
rejection, and production CLI refusal of static tokens / missing audit
|
|
|
|
|
delivery. Requested KeyCape registrations are in
|
|
|
|
|
`docs/keycape-service-registrations.md` (`aud` MUST be the resource server
|
|
|
|
|
`approval-engine`, not the OAuth client id). Remains `progress` until KeyCape
|
|
|
|
|
owns and proves those audience/client/scope registrations.
|
2026-09-02 00:52:04 +02:00
|
|
|
|
2026-09-01 23:45:48 +02:00
|
|
|
## Harden durable storage and migrations
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: APPROVAL-WP-0002-T02
|
2026-09-02 00:52:04 +02:00
|
|
|
status: done
|
2026-09-01 23:45:48 +02:00
|
|
|
priority: high
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_task_id: "2bef94ca-9482-5eff-9bd9-39adef292a78"
|
2026-09-01 23:45:48 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Define the production persistence, backup/restore, migration, concurrency, and
|
|
|
|
|
recovery posture. Prove schema upgrades preserve existing approvals and that
|
|
|
|
|
crash recovery cannot separate mutations from outbox evidence.
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
Acceptance: schema version is explicit; production serve refuses an unmigrated
|
|
|
|
|
or in-memory store; migration is a separate repeatable command; backup uses
|
|
|
|
|
SQLite's online backup API and integrity verification; restore is documented as
|
|
|
|
|
a stopped-single-writer operation; tests cover legacy upgrade, backup/restore,
|
|
|
|
|
and outbox atomicity after restart.
|
|
|
|
|
|
|
|
|
|
Completed 2026-09-02: schema v2 migration, production no-auto-migrate gate,
|
|
|
|
|
integrity/status surface, online mode-0600 backup, stopped-writer restore runbook,
|
|
|
|
|
retry-attempt state, and migration/backup/atomicity tests are in place.
|
|
|
|
|
|
2026-09-01 23:45:48 +02:00
|
|
|
## Package and deploy the service
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: APPROVAL-WP-0002-T03
|
2026-09-02 00:52:04 +02:00
|
|
|
status: wait
|
2026-09-01 23:45:48 +02:00
|
|
|
priority: high
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_task_id: "f0aa2e6d-19e6-5b43-886c-efa4e3de5f22"
|
2026-09-01 23:45:48 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Add the governed image/deployment surface, health and readiness behavior,
|
|
|
|
|
resource bounds, and fail-closed caller configuration. A local WSGI development
|
|
|
|
|
server is not production evidence.
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
Acceptance: a digest-pin-ready image and single-writer StatefulSet manifest
|
|
|
|
|
exist with non-root/read-only-root controls, PVC, migration init container,
|
|
|
|
|
resource bounds, probes, and default-deny network policy. Live completion also
|
|
|
|
|
requires an immutable image digest, KeyCape registrations, audit sender
|
|
|
|
|
credential, successful rollout, and restart/restore evidence.
|
|
|
|
|
|
|
|
|
|
Repository implementation complete 2026-09-02: the digest-pin-ready non-root
|
|
|
|
|
image builds and runs; the single-writer StatefulSet, PVC, migration init,
|
|
|
|
|
read-only root, resources, probes, and default-deny policies pass client dry-run.
|
|
|
|
|
Waiting on release digest, KeyCape/audit registrations and credentials, rollout,
|
|
|
|
|
restart, and restore evidence.
|
|
|
|
|
|
2026-09-01 23:45:48 +02:00
|
|
|
## Wire outbox delivery and reconciliation
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: APPROVAL-WP-0002-T04
|
2026-09-02 00:52:04 +02:00
|
|
|
status: wait
|
2026-09-01 23:45:48 +02:00
|
|
|
priority: high
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_task_id: "777a5cf5-c1db-5e07-8de6-ab109db5ccdc"
|
2026-09-01 23:45:48 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Deliver the local outbox asynchronously to audit-core, preserve event-id
|
|
|
|
|
deduplication, publish lag/depth signals, emit the declared heartbeat, and prove
|
|
|
|
|
the Gate House reconciliation contract against accepted event counts.
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
Acceptance: the sender adapts the local event to audit-core's authenticated
|
|
|
|
|
HTTP ingest, reuses event id as idempotency key, rereads a mounted token file,
|
|
|
|
|
marks drained only on accepted/duplicate, retains retryable failures, exposes
|
|
|
|
|
attempt/lag state, and emits the declared heartbeat. Live reconciliation waits
|
|
|
|
|
on `AUDIT-WP-0009-T04/T06/T09`; do not invent that receiver surface here.
|
|
|
|
|
|
|
|
|
|
Repository implementation complete 2026-09-02: audit-core envelope adaptation,
|
|
|
|
|
event-id idempotency, mounted-token reread, accepted/duplicate handling,
|
2026-09-02 15:46:06 +02:00
|
|
|
retry-attempt/lag metrics, and periodic heartbeats are tested.
|
|
|
|
|
|
|
|
|
|
2026-09-02 follow-up: drain tests now cover HTTP 200 duplicate as drained and
|
|
|
|
|
urllib `HTTPError` 503 as pending. Waiting on the audit-core sender
|
|
|
|
|
registration/ingress and receiver-owned reconciliation work
|
|
|
|
|
(`AUDIT-WP-0009-T04/T06/T09`).
|
2026-09-02 00:52:04 +02:00
|
|
|
|
2026-09-01 23:45:48 +02:00
|
|
|
## Prove one live PEP consumption path
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: APPROVAL-WP-0002-T05
|
2026-09-02 00:52:04 +02:00
|
|
|
status: wait
|
2026-09-01 23:45:48 +02:00
|
|
|
priority: high
|
2026-09-01 23:47:54 +02:00
|
|
|
state_hub_task_id: "3196fb77-3df0-5358-ae12-2d07bac12041"
|
2026-09-01 23:45:48 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Integrate one protected-system consumer under `GH-DEC-2026-003`: claim before
|
|
|
|
|
decision, CAS consume after ALLOW and before side effect, same-digest retry,
|
|
|
|
|
different-digest conflict, spent-on-failure behavior, and no protected action
|
|
|
|
|
when approval-engine is unavailable.
|
2026-09-02 00:52:04 +02:00
|
|
|
|
|
|
|
|
Acceptance: a repeatable harness proves the PEP sequence against the real HTTP
|
|
|
|
|
surface without performing a protected action; live closure requires a
|
|
|
|
|
secrets-engine-owned handler and evidence that no OpenBao call occurs on every
|
|
|
|
|
failure case. This repo may supply the protocol client and fixture, but may not
|
|
|
|
|
claim the consumer's side effect.
|
|
|
|
|
|
|
|
|
|
Repository implementation complete 2026-09-02: the HTTP PEP client and
|
|
|
|
|
fail-closed sequencing harness prove claim-before-decision and CAS-consume-before
|
|
|
|
|
callback, including unavailable, DENY, digest mismatch, and conflict paths.
|
2026-09-02 15:46:06 +02:00
|
|
|
|
|
|
|
|
2026-09-02 follow-up: secrets-engine shipped the PEP consume-before-OpenBao
|
|
|
|
|
handler (`src/secrets_engine/approval_consume.py`; inbox `0e04b4e2`). This
|
|
|
|
|
engine's client now maps HTTP 409/401/403/404/503 and unreachability, requires
|
|
|
|
|
the canonical digest, and refuses a decision-shaped consume payload. The
|
|
|
|
|
repeatable harness in `tests/test_pep.py` drives the real HTTP surface: first
|
|
|
|
|
consume then same-digest retry, different-digest conflict, spent-on-failure
|
|
|
|
|
claim refusal, and unreachable-engine callback suppression. Waiting on live
|
|
|
|
|
closure: this service deployed (T03) and a durable consume binding served
|
|
|
|
|
(`SECRETS-WP-0007-T04` / `SECRETS-WP-0008-T02`). This repo does not claim the
|
|
|
|
|
OpenBao side effect.
|
Record the claim vs ActionAuthorization envelope divergence
secrets-engine reports its PIP join is implemented and blocked on
deployment rather than contract. Reviewing its
validate_action_authorization against what this engine actually serves
shows that is not the whole story: it expects a state-hub-authority
ActionAuthorization (id, status, superseded_by, request,
approvals.entries, policy pin) while GET /v1/approvals/{id}/claim serves
the governed approval-claim (approval_id, state/valid_now, binding,
freshness, reason_code, issuer approval-engine).
Both envelopes declare schema_version 0.1, so the version check passes
and the mismatch surfaces as a field or authority error that reads like
an approval-engine outage.
Document the field-by-field divergence and why the omissions are
deliberate: a claim is a fact about an approval object, not a decision,
so approver identities and policy pins are not republished. Reconciling
the envelopes is a GH-DEC-2026-003 cross-repo change, so the governed
claim schema is left unchanged here.
No code change; 84 tests still pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 01:09:23 +02:00
|
|
|
|
|
|
|
|
2026-09-06 follow-up: secrets-engine reports its PIP join is implemented and
|
|
|
|
|
blocked on deployment, not contract (inbox `61ae1174`). Review of its
|
|
|
|
|
`validate_action_authorization` shows a real envelope divergence: it expects a
|
|
|
|
|
`state-hub`-authority `ActionAuthorization` (`id`, `status`, `superseded_by`,
|
|
|
|
|
`request`, `approvals.entries`, policy pin) while this engine serves the
|
|
|
|
|
governed approval-claim (`approval_id`, `state`/`valid_now`, `binding`,
|
|
|
|
|
`freshness`, `reason_code`, `issuer: approval-engine`). Both declare
|
|
|
|
|
`schema_version` `0.1`, so the mismatch surfaces as a field/authority error
|
|
|
|
|
rather than a version error. Recorded in `docs/approval-consumption.md`;
|
|
|
|
|
reconciling the envelopes is a `GH-DEC-2026-003` cross-repo change, not a
|
|
|
|
|
unilateral edit here. T05 stays `wait`: still no deployed base URL (T03).
|
2026-09-06 01:36:03 +02:00
|
|
|
|
|
|
|
|
2026-09-06 ruling: the claim-envelope question is settled. `GH-DEC-2026-005`
|
|
|
|
|
confirms all three requested dispositions — the approval-claim is the step-1
|
|
|
|
|
artifact, `ActionAuthorization` is not required and MUST NOT be served from the
|
|
|
|
|
claim endpoint, and a PEP validates across the claim and the step-2
|
|
|
|
|
`DecisionEnvelope`. Gate House recorded the split as doctrine (a PIP must not
|
|
|
|
|
republish the PDP's decision) and struck the `provenance.authority ==
|
|
|
|
|
"state-hub"` requirement explicitly. flex-auth accepted as `FLEX-DEC-2026-006`,
|
|
|
|
|
argued against its own proposal, and traced the bad authority constant to a
|
|
|
|
|
fixture rather than prose. **This engine changes nothing: the claim schema
|
|
|
|
|
stands as published.** `APPROVAL-IN-0002` is closed. T05 remains `wait` on T03
|
|
|
|
|
deployment plus the secrets-engine validator split and its
|
|
|
|
|
`secrets-engine-approval` KeyCape registration (already requested verbatim in
|
|
|
|
|
`docs/keycape-service-registrations.md`).
|
Carry threshold evidence on issuance and use events
GH-DEC-2026-005 moved the distinct-approver check off the PEP onto this
engine's valid_now. secrets-engine has implemented the split and reports
it no longer verifies the threshold independently. Gate House accepted
that as correct on layering AND as a genuine reduction in defence in
depth, and named the compensating control: not a second check at the PEP,
which is the duplication the split removes, but reconstructability at the
issuer under §9.6.
The emitted events could not support that. approval.issuance carried
required_count but never who satisfied it, and approval.use carried no
threshold evidence at all, so an auditor replaying the stream could not
recompute the evaluation without reading live rows -- rows that may since
have been superseded, revoked, or expired.
Both events now carry a threshold object: required_count,
distinct_approver_count, threshold_met, and approvers with approved_at
plus assurance and evidence_ref when recorded. Tests prove reconstruction
from the use row alone, and that the claim still discloses no approver
identities -- they are evidence for audit-core, not consumer-facing, and
the claim keeps disclosing the least it can.
Writing the tests showed distinctness is already a storage invariant:
entries is UNIQUE on (approval_id, subject_id), so a repeat approver is
refused at insert and a separate entry_count could never differ from the
distinct count. Dropped that field rather than ship a number that cannot
vary, and the test now asserts the refusal instead.
88 tests pass (4 new).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 08:10:21 +02:00
|
|
|
|
|
|
|
|
2026-09-06 follow-on (T01): `GH-DEC-2026-005` assigned this engine a
|
|
|
|
|
compensating obligation. secrets-engine has implemented the split and reports
|
|
|
|
|
it no longer verifies the distinct-approver threshold independently; Gate House
|
|
|
|
|
accepted that as correct on layering and as a real reduction in defence in
|
|
|
|
|
depth, requiring instead that the threshold evaluation be reconstructable from
|
|
|
|
|
this engine's state transitions and its use outbox row under §9.6. Implemented:
|
|
|
|
|
`approval.issuance` and `approval.use` now carry a `threshold` object
|
|
|
|
|
(`required_count`, `distinct_approver_count`, `threshold_met`, and `approvers`
|
|
|
|
|
with `approved_at` plus assurance/evidence refs). Tests prove reconstruction
|
|
|
|
|
from the use row alone and that the claim still discloses no approver
|
|
|
|
|
identities. Documented in `docs/outbox-contract.md`.
|
State pdp_digest explicitly; decline to publish a vocabulary mapping
flex-auth asked whether this engine should publish an action/target
mapping between the claim binding's vocabulary (secrets.kv.destroy,
{"id": "lane-openbao-root"}) and a policy package's (destroy, lane:...),
since their package makes no cross-check that a claim was approved for
the action being decided.
Answered no. A PIP asserting that one vocabulary's action means
another's would author policy semantics it does not own, over
vocabularies it does not own, and the failure mode is asymmetric: a wrong
mapping silently accepts a claim approved for a different action, which
is worse than no mapping. binding.pdp_digest is the correspondence and
sidesteps vocabulary entirely -- it compares the PDP's own digest to the
PDP's own digest, with no translation by anyone.
Implemented the part that was ours. pdp_digest was emitted only when
recorded, so a consumer could not distinguish "not issued against a
decision" from "we forgot to look". It is now always present and null in
that case, required-but-nullable in the schema, and documented as
something a PEP on a privileged lane must refuse. This engine states the
fact; enforcing the lane's policy stays with the consumer.
Both published examples were already contradicting the updated schema by
omitting the field -- the same fixture-versus-contract defect flex-auth
hit twice this week and that secrets-engine implemented. Fixed both, made
them cover the PDP-bound and unbound shapes so neither is inferred from
the other, and added tests/test_examples.py to validate every example
against the schema so the class cannot recur here. jsonschema added as a
dev dependency.
94 tests pass (6 new).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 09:32:11 +02:00
|
|
|
|
|
|
|
|
2026-09-06 follow-on (T05): flex-auth asked whether this engine should publish
|
|
|
|
|
an action/target vocabulary mapping between the claim binding and their policy
|
|
|
|
|
package before `SECRETS-WP-0007-T04` makes destroy reachable. Answered no, and
|
|
|
|
|
recorded why in `docs/approval-claim.md`: a PIP asserting that one vocabulary's
|
|
|
|
|
action *means* another's would author policy semantics it does not own, and a
|
|
|
|
|
wrong mapping silently accepts a claim approved for a different action.
|
|
|
|
|
`binding.pdp_digest` is the correspondence — it compares the PDP's digest to
|
|
|
|
|
the PDP's digest with no translation. Implemented the part that was ours:
|
|
|
|
|
`pdp_digest` is now always present on the claim and `null` when the approval
|
|
|
|
|
was not issued against a PDP decision, so absence is a stated fact rather than
|
|
|
|
|
a missing key, and the schema requires it as nullable. A PEP on a privileged
|
|
|
|
|
lane must refuse a null. Both published examples were contradicting the schema;
|
|
|
|
|
fixed, and `tests/test_examples.py` now validates every example against it.
|
Implement GH-DEC-2026-008: declared PDP-path intent, enforced at issue
Gate House ruled binding.pdp_digest is the binding correspondence on the
GH-DEC-2026-003 path and is required there, having rejected a vocabulary
mapping for the reasons we gave. It asked this engine to record the PDP
digest at issue for approvals intended for that path, and to have the
claim state which approvals those are rather than leaving it to the
requester's memory.
Schema v3 adds approvals.pdp_path. create() refuses pdp_path true without
a pdp_digest, so an approval that would be unusable on the path fails at
issue rather than at the protected side effect. The claim exposes
binding.pdp_path, which makes it a guarantee rather than a hint: pdp_path
true implies pdp_digest is non-null.
Intent is declared and never inferred. A pdp_digest that happens to be
present is not a declaration anybody made, so a recorded digest alone
leaves pdp_path false, legacy rows migrate to false rather than being
back-filled from their digests, and a successor inherits its
predecessor's declaration. Approvals issued before the ruling stay usable
by consumers in this engine's own vocabulary and are simply not usable on
the PDP path -- the ruling's intended cost, stated as such.
Schema, both published examples, a v2-to-v3 migration test asserting
survivors keep their digest while declaring no path intent, and tests for
refusal at issue, claim exposure, non-inference, and successor
inheritance. 102 tests pass (8 new).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 14:51:23 +02:00
|
|
|
|
|
|
|
|
2026-09-06 follow-on (T01/T05): implemented `GH-DEC-2026-008`, which requires
|
|
|
|
|
`binding.pdp_digest` on the `GH-DEC-2026-003` path and directed this engine to
|
|
|
|
|
record the PDP digest at issue and have the claim state which approvals those
|
|
|
|
|
are. Schema v3 adds `approvals.pdp_path`; `create` refuses `pdp_path: true`
|
|
|
|
|
without a `pdp_digest`, so the failure lands at issue rather than at the
|
|
|
|
|
protected side effect. The claim exposes `binding.pdp_path`, making
|
|
|
|
|
`pdp_path: true` a guarantee that `pdp_digest` is non-null. Intent is declared,
|
|
|
|
|
never inferred from an incidental digest, and never back-filled: legacy rows
|
|
|
|
|
migrate to `false` and a successor inherits its predecessor's declaration.
|
|
|
|
|
Schema, both examples, and a v2→v3 migration test cover it (102 tests).
|