approval-engine/workplans/APPROVAL-WP-0007-bounded-execution-carrier.md
tegwick 5c45d3c157 APPROVAL-WP-0007-T02 done: actor_consumes; T05 principal_approves split out
Founder decision 2026-11-01, relayed by the net-kingdom coordinator: "Agree to
split APPROVAL-WP-0007-T02: build actor_consumes now (opt-in at issue; the
bounded executor will set it); principal_approves keeps waiting for the KeyCape
subject mapping."

- Optional actor_consumes (boolean) at issue, schema v8. Consume refuses with
  403 unless the consuming subject equals binding.actor, also on idempotent
  retries; internal calls without a consumer fail closed. Inherited on
  supersession (existing-successor mismatch 409), in audit details, served in
  GET/claim only when declared so undeclared claims keep exact bytes. Legacy
  rows migrate without the flag.
- GH-DEC-2026-026 Ruling 6: under human_control, add_entry refuses (403) the
  binding actor as approver. human_control already covered a service actor;
  this covers a human actor.
- Workplan: T02 done (actor_consumes); new T05 principal_approves (wait).
- Not released or deployed: needs a v8 image and founder-run rollout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
2026-11-01 00:39:34 +01:00

5.5 KiB

id type title domain repo status flavor owner topic_slug created updated depends_on state_hub_workstream_id
APPROVAL-WP-0007 workplan Bounded-execution admission carrier: assurance floor, correspondence and revoker evidence infotech approval-engine active implementation approval-engine netkingdom 2026-10-31 2026-11-01
af365506-f451-50b5-a0a2-f0ba9a0a4d9c

Demand: NetKingdom NK-WP-0056-T02 owner request (hub message 40344380-ea33-4670-9b27-dafca1699d36) to carry a Bounded Execution Admission v0.1 (proposed) as an approval. Owner answer: docs/bounded-execution-admission-carrier.md. The binding, consume and revoke already work. This plan closes the engine-side gaps the answer found. Every change is opt-in at issue, so existing approvals and consumers keep their behavior. Native digest, CAS consumption and no-unconsume are unchanged.

Declared minimum approver assurance

id: APPROVAL-WP-0007-T01
status: done
priority: high
state_hub_task_id: "9ce0aca8-7694-53c6-a76a-f78d395a9861"

Add an optional required_assurance (minimum level, ordered aal1 < aal2 < aal3) to POST /v1/approvals. Store it and serve it in the object and the claim, and inherit it on supersede. add_entry refuses an entry whose verified token assurance.level is weaker or missing. _evaluate and consume refuse when an existing entry does not meet it. Schema migration leaves old rows NULL, meaning no floor. Tests cover aal1 refused, aal2 accepted, missing level refused, supersede inheritance and the claim reason_code. Serves BEA-C4. Whether KeyCape emits aal2 is KeyCape's question, outside this plan.

Done 2026-10-31 in source (schema v7, tests/test_required_assurance.py, contract in docs/approval-claim.md). The member is present only when declared, so undeclared claims keep their exact bytes for strict readers. Not released or deployed: a v7 image and a founder-run rollout are still needed.

Opt-in consumer = actor (actor_consumes)

id: APPROVAL-WP-0007-T02
status: done
priority: medium
state_hub_task_id: "9657820d-23f9-54d9-920c-8f2a72110ad0"

Optional issue flag actor_consumes: true on POST /v1/approvals: the consuming subject must equal binding.actor. Closes the denial-of-run gap (today any tenant service with approval:consume can spend the object). Adds no identity mapping, because the actor is already pinned to the creator's token subject. Serves GH-DEC-2026-026 build-mode relaxation B2.

Split from the original T02 by founder decision 2026-11-01, relayed by the net-kingdom coordinator: "Agree to split APPROVAL-WP-0007-T02: build actor_consumes now (opt-in at issue; the bounded executor will set it); principal_approves keeps waiting for the KeyCape subject mapping." principal_approves moved to T05.

Done 2026-11-01 in source (schema v8, tests/test_actor_consumes.py, contract in docs/approval-claim.md). Stored on the object, served in GET and the claim only when declared (undeclared claims keep their exact bytes), carried in audit details, inherited on supersession (existing-successor mismatch is 409). Consume refuses with 403 when the consuming subject differs, also on idempotent retries; an internal call without a consumer fails closed. Legacy rows migrate without the flag. Same commit: under human_control, add_entry refuses (403) the binding actor as approver (GH-DEC-2026-026 Ruling 6); human_control alone already covered a service actor, this covers a human actor. Not released or deployed: a v8 image and a founder-run rollout are still needed.

Opt-in approver = principal (principal_approves)

id: APPROVAL-WP-0007-T05
status: wait
priority: medium

Optional issue flag principal_approves: true: every entry subject must equal binding.principal. It gives principal an enforced meaning, so it waits. Wait reasons: the KeyCape subject mapping (NetKingdom's principal strings must equal KeyCape subjects exactly) and the S5 doctrine. Split from T02 by the founder decision of 2026-11-01 quoted under T02. Until it lands, any human holding approval:approve for the tenant can approve, and the executor checks the entry subject itself (docs/bounded-execution-admission-carrier.md §3).

Revoker identity in revocation evidence

id: APPROVAL-WP-0007-T03
status: todo
priority: medium
state_hub_task_id: "7ceb5ffd-3ba8-5e9d-96a2-5e89c38a5c43"

Record the verified revoker's subject, principal_type and assurance, plus an optional bounded reason, in the approval.revocation outbox event. Persist them on the object (the revoked_by fields) without changing the CAS or terminal semantics. This is the evidence for the founder's pre-start kill switch (NK-WP-0056-T07). Do not require the revoker to equal the principal: revocation must stay possible without the holder.

Synthetic bounded-execution carrier proof

id: APPROVAL-WP-0007-T04
status: todo
priority: medium
state_hub_task_id: "673890fe-3d49-5fe3-8ad7-c8c73e4d9376"

Add an in-process test and an example request covering the full path: the executor (service) creates the request (action: bounded-execution, target: {admission_id, admission_digest}, human_control: true, pdp_path: false); a human aal2 entry approves it; GET binding comparison; consume with request_digest = admission digest; idempotent retry; a different digest returns 409; revoke before consume gives 409 on consume; consumption outside the window is refused. Synthetic fixtures only, with no live service.

Founder decision 2026-10-31

Approved by the founder (proposed -> ready) after the NK-WP-0056-T02 owner answers (hub decision 19afd15e).