--- id: APPROVAL-WP-0001 type: workplan title: "v0.7 alignment and the engine spine" domain: infotech repo: approval-engine status: finished owner: grok topic_slug: netkingdom created: "2026-08-29" updated: "2026-09-01" state_hub_workstream_id: "546f2fae-c53e-5ea5-8c63-320118d8ee1e" --- # APPROVAL-WP-0001 — v0.7 alignment and the engine spine Evolve this repository from a v0.5 seed INTENT into a v0.7-aligned PIP with the contracts and first implementation that make Canon `T-06` passable and unblock `FLEX-WP-0017` T03/T05. Source: - `net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted) - `net-kingdom/SECURITY-COMPANION.md` v0.2 - `history/2026-08-29-security-layer-model-v0.7-scope-intent-review.md` - `INTENT.md`, `SCOPE.md`, `layer.yaml` **Rule of the workplan.** Contracts before code. Consumption is not implemented until `GH-WP-0002-T06` settles it. A reachable mutation that guesses the three races is a defect, not a prototype. Peer work this does not own and must not duplicate: `GH-WP-0002` (T02 outbox wire, T03 revocation failure mode, T04 detection surface, T06 consumption ordering), `FLEX-WP-0017` T03/T05, `AUDIT-IN-0001`. ## T01 — Align declaration to the accepted statute ```task id: APPROVAL-WP-0001-T01 status: done priority: high state_hub_task_id: "090ab9d5-1467-5a82-b3cd-ac4215bec0f0" ``` 2026-08-29: INTENT frontmatter `layer: Engine` / `role: PIP`; aligned to v0.7 (PIP typing, threat decomposition, load-bearing cadence, custody closed, consumption as mutation, issuer/freshness, Railiance non-axis). SCOPE.md written as first-cut boundary with standard sections. `layer.yaml` declared in this repository's own voice. Review filed under `history/`. Registered with State Hub (`infotech`, topic custodian). Register's bootstrap workplan was discarded: it collided on `APPROVAL-WP-0001` and its three tasks are already discharged by this file, `AGENTS.md`, and `SCOPE.md`. ## T02 — Publish the approval claim contract ```task id: APPROVAL-WP-0001-T02 status: done priority: high state_hub_task_id: "ca2ec789-b760-525a-9960-95d15760191e" ``` 2026-08-29: `schemas/approval_claim.schema.json`, `docs/approval-claim.md`, examples. Native digest is sorted-key JSON of action/actor/principal/purpose/target; optional `pdp_digest` records `NewDecisionBinding.request_digest` at issue. Yields to Taxonomy §17. Specify the input claim `access-engine` consumes: approval identifier, canonical-binding digest over the same binding the PDP already computes, issuer (this engine), and freshness. Schema and examples in-repo. Until Taxonomy's request-claim schema exists (statute §17, ownership proposed not assigned), this is the boundary contract. Mark it as yielding to Taxonomy; do not treat a local shape as permanent. Acceptance: a document `access-engine` can implement a consumer against without reading this engine's source; wrong-action / wrong-target / wrong-scope are distinguishable; a PDP can state a deadline for this input class. ## T03 — Specify the local transactional-outbox contract ```task id: APPROVAL-WP-0001-T03 status: done priority: high state_hub_task_id: "a2eb2e8f-9b49-598e-b4a1-fb93e65f1bf3" ``` 2026-08-29: `docs/outbox-contract.md`. Classes issuance/use/supersession/revocation/heartbeat. Payload is `audit-core.event.v1alpha1`. Synchronous `audit-core` inside the mutation transaction is named forbidden. Event classes (issuance, use, supersession, revocation, and the heartbeat class T04 needs), same transaction as the object mutation, queue local to this engine, at-least-once into the outbox (`audit-core` dedupes on event id). No synchronous `audit-core` call inside the state-change transaction. Coordinate with `GH-WP-0002-T02`; do not author doctrine this engine does not own. INTENT already states the boundary and the locality. Acceptance: a wire contract another repository can review; an implementer cannot satisfy it by emitting synchronously to `audit-core`. ## T04 — Declare load-bearing cadence as heartbeat or reconciliation ```task id: APPROVAL-WP-0001-T04 status: done priority: high state_hub_task_id: "fce0df25-0106-5c0a-8187-87b956ca6220" ``` 2026-08-29: `cadence.yaml` + `docs/emission-cadence.md`. `layer.yaml` `evidence.cadence_status: declared`. Missing heartbeat and count divergence are findings. Rate monitoring forbidden. Approval evidence is load-bearing and low-volume. Rate monitoring is the wrong form (statute §9.6). Publish the source-side declaration: expected classes, the heartbeat (*nothing to report*, signed, itself able to go missing) and/or reconciliation of this engine's transitions against `audit-core` event counts per class. Coordinate with `GH-WP-0002-T04`, which is the detection surface. This task is the source that surface reads. Acceptance: `layer.yaml` `evidence.cadence_status` is no longer `undeclared`; a missing heartbeat or a count divergence is specified as a finding, not as a log line. ## T05 — Implement the assented consumption ordering contract ```task id: APPROVAL-WP-0001-T05 status: done priority: high state_hub_task_id: "204bdfeb-a669-569e-a654-10c1a9fcacbc" ``` Blocked on `GH-WP-0002-T06` / `access-engine`. The three races (allow never consumed; double consumption; consume after failed action) are named in statute §9.7.4 and §16. This engine performs the mutation; it does not choose the signal, the point relative to the decision, or the handling of the three states. Acceptance: a written contract both engines have assented to, recorded here, *then* a consume path. Until then, no `consume` endpoint, no inferred consumption from a decision record, no demo that "just consumes on allow". 2026-09-01: Gate House resolved the blocker in `GH-DEC-2026-003` and `docs/contracts/approval-consumption.md`, with an explicit State Hub handoff from gate-house (`2ade6044`). Implemented `POST /v1/approvals/{id}/consume`: the PEP presents the PDP decision binding's `request_digest` after ALLOW and before the protected side effect. The SQLite CAS stores the digest and optional decision id; same-digest retries are idempotent success, different digests conflict, and there is no unconsume. The `approval.use` outbox row remains in the same transaction. Concurrency, rollback, API-shape, legacy-database migration, and consume-side T-06 tests are included. ## T06 — Durable object, closed state machine, authenticated entries ```task id: APPROVAL-WP-0001-T06 status: done priority: high state_hub_task_id: "44c4997b-6833-5540-a554-0e24210809f2" ``` 2026-08-29: SQLite object + closed machine in `approval_engine/store.py`. CAS supersession (concurrent test), distinct-approver fail-closed, revocation without holder cooperation. `_cas_consume` is unexported. 2026-09-01: the assented T05 contract replaced the private seam with the public digest-bound `consume` mutation and added an in-place SQLite column migration. Depends on T02 and T03. Implement the object and the machine in SCOPE: identifiers; bindings (action, target, actor, principal, purpose, validity window, approvers); authenticated entries; distinct-approver counting; CAS supersession; validity/expiry; revocation without holder cooperation. Do not ship a public `consume` until T05 is unblocked. Internal CAS consumption may exist behind an unexported seam so T08 can be tested without guessing the ordering contract. Acceptance: state transitions are total and reconstructable; two concurrent supersessions cannot both succeed; distinct-approver requirements fail closed on duplicates; revocation is effective at the next introspection. ## T07 — Introspection API as input claims ```task id: APPROVAL-WP-0001-T07 status: done priority: high state_hub_task_id: "ea51499d-ced5-50fa-b852-396719a8c0f4" ``` 2026-08-29: `GET /v1/approvals/{id}/claim`. Tests forbid decision-shaped keys and `/v1/check` / `/authorize`. Store unavailable → 503. Depends on T02 and T06. An API that answers INTENT's question and returns the T02 claim. No endpoint answers "may this actor do X". Fail-closed when this engine's own store is unavailable. Acceptance: `access-engine` can fetch a claim; a decision rendered against approval A for request R cannot be replayed for R' on the digest; tests forbid a decision-shaped response. ## T08 — Local outbox in the mutation transaction ```task id: APPROVAL-WP-0001-T08 status: done priority: high state_hub_task_id: "b9267011-71b3-5138-a3f9-5256374a6b3b" ``` 2026-08-29: Outbox insert in the same IMMEDIATE transaction. Test fails the transaction when emission fails (no leftover mutation). Revoke succeeds while the drain sink is down; the revocation row stays undrained. Depends on T03 and T06. Every issuance, use, supersession, and revocation inserts the outbox row in the same transaction. Drain is asynchronous. `audit-core` outage does not block a revocation. Acceptance: a test that fails the transaction when emission fails, not an inspection of the code; a test that revokes while the evidence engine is unreachable; no path where the state change commits and the outbox row does not. ## T09 — Canon T-06 and the FLEX-WP-0017 handoff ```task id: APPROVAL-WP-0001-T09 status: done priority: medium state_hub_task_id: "b4946568-5dda-5bd7-9941-83a5f0137700" ``` 2026-08-29: `tests/test_t06_replay.py` encodes Canon T-06 for wrong target, wrong action, later time, revoked, superseded. Consume-side replay stays out. Handoff: `docs/flex-auth-handoff.md`. `FLEX-WP-0017` T03 is unblocked on this object; T05 remains blocked only on consumption ordering. 2026-09-01: consume-side replay coverage added after `GH-DEC-2026-003`: same-digest retry is idempotent and different-digest replay conflicts. The handoff document now records the PEP-before-side-effect protocol. Depends on T07, T08, and T05 (the last only for the consume-side replay cases). Point Canon `T-06 — Approval Replay` at a live implementation. Hand `access-engine` / `secrets-engine` a claim they can validate before privileged production actions (`FLEX-WP-0017` T03/T05). Acceptance: `T-06` passes; T03 is unblocked on the object (not on a hub substitute); T05 remains blocked only on consumption ordering if T05 of this workplan is still `wait`, never on a missing object or a missing digest. ## Closeout Finished 2026-09-01. All nine tasks are complete and the full test suite passes. The first-cut engine now has a durable approval object, authenticated-entry shape, atomic supersession and consumption, claim introspection, transactional outbox, cadence declaration, and complete Canon T-06 replay coverage. Residual production hardening and consumer adoption are carried by `APPROVAL-WP-0002`. The unassigned shared Taxonomy request-claim schema is carried by residual intake `APPROVAL-IN-0001`; the local claim continues to yield rather than claiming permanent vocabulary ownership.