approval-engine/workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md

284 lines
11 KiB
Markdown
Raw Permalink Normal View History

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