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
This commit is contained in:
parent
96c859f77c
commit
b76456134c
2 changed files with 176 additions and 0 deletions
171
docs/proposals/2026-10-31-decision-services-alignment-review.md
Normal file
171
docs/proposals/2026-10-31-decision-services-alignment-review.md
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
# 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](2026-10-31-nk56-t17-founder-approval-path.md)).
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
```text
|
||||||
|
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 `GET`s 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_ref`s?
|
||||||
|
|
@ -722,6 +722,11 @@ founder approves it, and the executor then consumes it. Needed:
|
||||||
`human_control: true`, `pdp_path: false`) and prints its id;
|
`human_control: true`, `pdp_path: false`) and prints its id;
|
||||||
- the existing run phase then consumes it.
|
- the existing run phase then consumes it.
|
||||||
|
|
||||||
|
**2026-10-31:** T17 showed that the founder cannot be presented an executor-filed approval today. The founder asked
|
||||||
|
for an alignment review of informed-decision, approval-engine and State Hub decisions
|
||||||
|
([review](../docs/proposals/2026-10-31-decision-services-alignment-review.md)). T14's request phase follows its
|
||||||
|
"present an existing approval" contract (S1 informed-decision, S2 here).
|
||||||
|
|
||||||
**Done when:**
|
**Done when:**
|
||||||
|
|
||||||
- the client and the CCR are prepared;
|
- the client and the CCR are prepared;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue