approval-engine/workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md
tegwick 9c9528f5b2 Implement the engine spine: claim, outbox, machine, API
Contracts first (T02–T04): approval claim schema with issuer, freshness,
and binding digest; local transactional outbox wire; load-bearing cadence
as heartbeat or reconciliation (layer.yaml declared).

Then the object (T06–T08): SQLite closed state machine, CAS supersession,
distinct-approver fail-closed, revocation without holder cooperation,
outbox insert in the same transaction. Tests fail the mutation when
emission fails, and revoke while the drain sink is down.

Introspection GET /v1/approvals/{id}/claim is a PIP fact, not a decision.
No public consume (T05 waits on GH-WP-0002-T06). Canon T-06 coverage for
wrong binding, expiry, revoke, and supersede.

FLEX-WP-0017 T03 is unblocked on this object; T05 remains blocked only on
consumption ordering.

Assistant: grok
Assistant-Session: 01a04ceb-2057-7e20-b0f9-c282964d5dd9
2026-08-29 12:52:49 +02:00

9 KiB

id type title domain repo status owner topic_slug created updated state_hub_workstream_id
APPROVAL-WP-0001 workplan v0.7 alignment and the engine spine infotech approval-engine active grok netkingdom 2026-08-29 2026-08-29 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

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

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

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

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 — Wait on consumption ordering; do not implement it

id: APPROVAL-WP-0001-T05
status: wait
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".

T06 — Durable object, closed state machine, authenticated entries

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.

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

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 / /consume. 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

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

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.

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.