net-kingdom/docs/proposals/2026-10-31-decision-services-alignment-review.md
tegwick b76456134c
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Decision services alignment review: approval-engine, informed-decision, State Hub decisions
Founder-requested review (2026-10-31), driven by NK-WP-0056-T17. Findings:
informed-decision couples presentation with creation (actor informed-decision),
one weight for every decision, two unlinked decision stores, executor-filed
approvals invisible. Proposal: present-existing-approval contract, decision
classes C0-C3 (executed means C2), hub decisions resolved by memo write-back;
first slice S1-S6 with owners.

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-10-31 21:15:52 +01:00

13 KiB
Raw Blame History

Decision services alignment: approval-engine, informed-decision and State Hub decisions (review, 2026-10-31)

Status: review and proposal. Nothing here changes another repository; every change named below belongs to its owner.

Trigger:

  • NK-WP-0056-T17 found that the founder has no path today to approve a Bounded Execution Admission (finding).
  • The founder asked for an architecture, code, INTENT and SCOPE review of informed-decision and approval-engine. It should align them, attach State Hub's decision records, and make the facility proportionate: easy for simple cases, complete for complex ones (founder, 2026-10-31).

1. Practical needs (what the estate does today)

Need How it is handled now Problem
N1. Executor admissions (NK-WP-0056-T06): a service files an approval for one exact admission, and the founder approves it at aal2 before a bounded run No path. The executor must be the approval's actor (approval-engine create rule, BEA), but informed-decision presents only approvals it creates itself The pilot cannot start
N2. Routine founder decisions: workplan approvals, adopting owner proposals, choosing between options An agent writes "approved by the founder in session" into a workplan, a CCR comment or a State Hub decision (decided_by is free text) No binding evidence. The same decision is repeated in several places. The full memo path is too heavy for these, so it is not used
N3. Credential approvals (CCRs): platform-owner and net-kingdom-owner approve one request The credential-change.py approve tool appends a review comment; for some types the second approval is recorded by hand (CCR-2026-0058) Evidence is a YAML comment written by an agent on the founder's instruction
N4. Hub coordination: an open question waits for a human (needs_human, decision_id) State Hub decisions, resolved through the API with a rationale Not linked to any presented or bound evidence. informed-decision's write-back to the hub is disabled

2. The architecture as built (evidence)

  requester (agent / service)                    founder (browser, KeyCape aal1/aal2)
        │                                                    │
        │ State Hub memo dispatch (STATE-WP-0096,            │ decisions.coulomb.social
        │ private queue, POST /v1/memos)                     ▼
        └──────────────► informed-decision ──── presents memo, records view_hash,
                          intake: creates approval     highlights, disposition
                          actor="informed-decision" │
                                                    │ accept → POST /entries (approval:approve)
                                                    ▼
                                              approval-engine  ◄── consume (approval:consume, service)
                                              object, CAS,         by whoever holds the scope
                                              outbox → audit-core

  State Hub decisions table: title, rationale, decided_by (string), status open/resolved. No link to the above.
  • approval-engine (INTENT, SCOPE, docs/bounded-execution-admission-carrier.md):
    • It is a PIP. It owns the durable approval object: binding to action, target, actor, principal and purpose, authenticated entries, atomic consume and supersede, and revocation.
    • It is explicitly not an inbox, workflow or notification surface. It has get-by-id and no list.
    • Only the actor creates (binding.actor must equal the token subject).
    • Consume does not check consumer = actor (APPROVAL-WP-0007-T02, waiting).
    • aal2 is stored, not enforced (APPROVAL-WP-0007-T01).
  • informed-decision (INTENT, SCOPE, GOAL, code):
    • It is the live approver surface. Its thesis is scale invariance: one memo model from an L0 informed login to an L4 contract, with L1 being privileged JIT authorization. Only L3 (management approval) is built; L0 and L2 are "Stage 2, direction only".
    • Intake (intake.py) refuses a memo that pins approval_id (approval_pinned_by_submitter).
    • intake_approval.py creates every approval with actor: "informed-decision", 24 h validity and pdp_path: false.
    • Submitters are configured per username with memo_classes, review_groups and a daily_limit (intake_service.configuration).
    • On accept it posts the human's entry, scopes openid approval:read approval:approve.
    • view_hash is not inside the approval entry; correlation is (approval_id, subject, approved_at).
  • State Hub:
    • decisions is a coordination record (made|pending, open|resolved|escalated|superseded, free-text decided_by).
    • memo_dispatch forwards a validated envelope (informed-decision.memo-intake v1) to informed-decision's private intake. Hub resolution write-back is disabled.

3. Findings

  1. Creation is coupled to presentation. informed-decision creates the approval with itself as actor. Two consequences:

    • An actor that will use an approval (the executor, secrets-engine's PEP, a deploy agent) cannot have it presented. That is N1.
    • Approvals created this way name the surface, not the user, as actor. That weakens approval-engine's "actor who may use it" binding, which is the very thing BEA relies on.

    Neither INTENT requires this coupling. informed-decision's INTENT says it "never holds approval current-state", and approval-engine's says only the actor creates.

  2. One weight for every decision. Every bound decision takes the full memo path: packet, highlights, presentation record, view_hash, intake budget. The tiers that would make simple decisions cheap (L0 and L1, and L2 "ADR accept against the estate's own decision records") are not built. In practice, routine decisions (N2) bypass the surface entirely and become free text. The heaviness pushes the estate to less evidence, not more.

  3. Two decision stores with no link. A State Hub decision and an informed-decision memo about the same question do not reference each other. Write-back is off, so a hub decision is resolved by an agent's string. The same founder decision often lives in three places (hub decision, workplan text, CCR comment) with no binding between them.

  4. An approval filed by someone else is invisible. approval-engine has no list (correctly), and informed-decision lists only its own memos. An executor-filed approval therefore reaches the founder only if a memo is filed for it. That is the missing contract in Finding 1.

  5. The evidence join is partial. The approval entry does not carry view_hash, and assurance is checked by the surface, not enforced by the object (APPROVAL-WP-0007-T01). For bounded execution the executor checks aal2 itself today.

4. Proposed alignment

4.1 Roles (no change of ownership, sharper boundaries)

Service Owns Does not do
approval-engine The object, created only by the actor who will use it (or by informed-decision when informed-decision itself is the actor); entries, consume, revoke Present, list or notify
informed-decision The one human-facing surface for every bound decision. It presents questions, collects dispositions and records what was shown. For approvals it adds the human entry to an approval someone else created, after verifying its binding Create approvals on behalf of other actors; hold approval state
State Hub decisions The index and routing of open questions for work coordination: what is waiting, for whom, in which workplan. It links to evidence and is resolved by it Hold binding evidence; be resolved by free text for decisions that need binding
gate-house Doctrine: which decision classes exist, which weight each needs, who may approve Runtime state

4.2 Contract: "present an existing approval" (closes Findings 1 and 4, needed for N1)

  1. Create. The actor creates the approval in approval-engine, as itself, with human_control: true.
  2. Submit. The actor submits a memo to informed-decision that references approval_id and the binding digest. The memo carries the question, the packet (for an admission: the admission YAML and its digest) and the highlights (scope ceiling, window, budget, revert).
  3. Verify. informed-decision accepts the reference only from a registered submitter whose profile allows present_existing for that action class. It then GETs the approval and requires all of:
    • status requested;
    • no entries;
    • human_control: true;
    • binding.actor equal to the submitter's subject;
    • binding digest equal to the memo's;
    • the action class allowed for the submitter.
  4. Accept. Accepting posts the founder's entry with their token (aal2 where the class requires it). informed-decision never consumes and never creates in this path.
  5. Run. The actor reads the approval (GET) and consumes it. The executor's existing checks (actor, digest, aal2, listed approvers) still hold.

4.3 Proportional weight: decision classes (closes Finding 2)

One memo model, as informed-decision's thesis requires, with the amount of ceremony set per class by doctrine:

Class Example Surface Assurance approval-engine object Evidence
C0 note Record a finding; acknowledge a report State Hub only none no hub record
C1 confirm Approve a workplan; adopt an owner proposal; choose option A/B informed-decision single-screen confirm: question, one packet item, one required highlight, options fresh session (aal1) no memo acceptance with view_hash; resolves the hub decision
C2 bound approval Bounded-execution admission; credential lane (CCR); production deploy informed-decision full memo, presenting an existing approval (4.2) aal2, fresh yes, created by the actor memo + approval entry + audit-core
C3 instrument Contract or multi-party later (informed-decision L4) per party per party as informed-decision Stage 3+

The rule: anything a service will execute on the strength of the decision is C2. An admission's risk_class can add highlights or required approvers within C2, but cannot lower it to C1.

4.4 Attaching State Hub decisions (closes Finding 3)

  • A hub decision gains optional links: memo_id, approval_id and evidence_ref.
  • When it needs C1 or C2, the hub dispatches a memo (existing STATE-WP-0096 path), and informed-decision's write-back (now disabled) resolves the hub decision with:
    • the authenticated subject;
    • the memo revision and view_hash;
    • for C2, the approval id.
  • An agent must not resolve a C1 or C2 decision by text.
  • C0 stays as today.

This makes the hub the place to see what is waiting, and informed-decision the place where it is decided, without either duplicating the other.

5. First slice, driven by what blocks today

# Change Owner Unblocks
S1 informed-decision intake profile present_existing (4.2), with tests against approval-engine's real source informed-decision N1 / NK-WP-0056-T06
S2 Executor: KeyCape client nk-bind-executor-approval, OpenBao lane for its secret, a request phase that creates the approval and submits the memo net-kingdom (NK-WP-0056-T14) + KeyCape registration + railiance-platform CCR N1
S3 Write-back for memos that carry a hub decision_id, and decision links in State Hub informed-decision + state-hub N4, part of N2
S4 The C1 single-screen confirm in informed-decision informed-decision N2, and N3 later
S5 Doctrine for decision classes C0–C3 and the "executed means C2" rule gate-house Every class assignment
S6 approval-engine: aal2 floor (APPROVAL-WP-0007-T01) and actor_consumes (T02) approval-engine Moves the executor's own checks into the object

S1 and S2 are the minimum for the pilot. S3–S5 make routine founder decisions cheaper and better evidenced than today's free text.

6. Open questions

  1. For the founder: are the classes C0–C3 and the "executed means C2" rule the right cut?
  2. For informed-decision: does present_existing fit the intake model as a profile, or should it be a separate route? Should the memo's packet carry the admission YAML itself, or a digest plus link?
  3. For approval-engine: should an approval created by an actor and presented elsewhere carry a presentation_ref (the memo id) at create, so that the object points at its evidence? (Finding 5)
  4. For State Hub: decision links as new columns, or as typed evidence_refs?