Claim ownership of the browser-facing approver UI that approval-engine deliberately does not contain. approval-engine's INTENT names an approvals inbox under Non-Goals, and docs/keycape-service-registrations.md records that the human approver client's client_id and callback URI "must come from its owner once it exists" — leaving key-cape's KEY-WP-0013-T02 blocked on an unassigned component. - INTENT.md: Decision Memo concept, the binding/awareness split and the two hashes, ownership and non-ownership against the named estate repositories, and a provisional PEP-shaped layer placement flagged for a gate-house ruling rather than asserted. - GOAL.md: Stage 1 is the L3 approval approver surface — the narrowest real consumer with a live blocking dependency — plus the written answer to who owns the approver UI. - workplans/INFD-WP-0001: founding documents, the gate-house layer/ownership ruling, the four specs (PRD, UseCaseCatalog, ArchitectureBlueprint, EvidenceModel), schema and canonicalizer promotion out of history/ with the isolation vectors under test, the key-cape client registration, and a walking skeleton that includes return and discuss. history/ is preserved unmodified as provenance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3W1dQG7GFFM9d94jFx7iR Assistant: claude-code Assistant-Model: opus Assistant-Process: 1565372@bnt-lap001 Assistant-Session: 16bb2f25-b34c-49ef-8e94-5fec3567a568
184 lines
8.9 KiB
Markdown
184 lines
8.9 KiB
Markdown
# Decision Memo state transitions v0.2
|
||
|
||
Actors: `requester`, `assignee` (current ready step), `party` (any listed party), `subject` (session principal after bind), `system`, `agent`.
|
||
|
||
Guards:
|
||
|
||
| Id | Meaning |
|
||
|---|---|
|
||
| G_STEP | Verb is in `route.steps[current].required_verbs` **or** is a always-on overlay verb for that step kind (table B) |
|
||
| G_PRES | `presentation_id` exists, `memo_version` matches, `phase` is `pre_bind` or `bind` |
|
||
| G_ACK | All `highlights[required_ack=true]` are in `presentation.acked_highlight_ids` |
|
||
| G_MAND | Actor mandate covers `requested_act` + legal/value constraints |
|
||
| G_LEVEL | Actor signature meets `step.min_binding_level` and memo `binding_level` |
|
||
| G_REASONS | `return.reasons` has ≥1 coded reason |
|
||
| G_TARGET | `transfer.to` is a resolvable party with compatible mandate |
|
||
| G_ESC | Target mandate is *strictly higher* than current assignee for this act |
|
||
| G_NOAGENT | `actor.kind ≠ agent` |
|
||
| G_SESSION | `session.status = active` and actor is `session` subject |
|
||
| G_HAT | `configure.hat.id` ∈ `awareness.available_hats` and `hat.elevates = false` |
|
||
| G_PROMOTE | Field moved from awareness → binding; new memo version; presentations invalidated |
|
||
|
||
Overlay verbs (always available when status allows, even if not in `required_verbs`): `comment`, `discuss`.
|
||
Requester-only overlay: `withdraw` while not terminal.
|
||
System-only: `stamp`, expiry.
|
||
|
||
## A. Memo-level status machine
|
||
|
||
```
|
||
submit
|
||
draft ──────────────► submitted
|
||
▲ │
|
||
│ │ route.started
|
||
│ edit (ver++) ▼
|
||
│ circulating
|
||
│ │
|
||
│ ├── first ready step → awaiting_actor
|
||
│ ├── discuss → in_discussion
|
||
│ │ │
|
||
│ │ └── assignee resumes → awaiting_actor
|
||
│ │
|
||
│ ├── return → returned ──requester revises (ver++)──► submitted
|
||
│ ├── last binding verb → completed (+ outcome)
|
||
│ ├── withdraw → withdrawn
|
||
│ └── due passed → expired (outcome timed_out)
|
||
│
|
||
└── completed ── new memo relation.supersedes ──► superseded
|
||
|
||
L0/L1 extra:
|
||
completed + requested_act ∈ {login, elevate}
|
||
→ session.status = active (bound_configuring window)
|
||
→ configure / session.ended
|
||
→ session.status = ended
|
||
```
|
||
|
||
`in_discussion` is a substate of circulation. It does not reset the step.
|
||
|
||
## B. Verb × status × step kind
|
||
|
||
Legend: `✓` allowed · `—` forbidden · `C` allowed and may complete the step · `M` allowed and may complete the *memo* · `S` session-only after bind.
|
||
|
||
Step kinds are grouped:
|
||
|
||
- **Weak:** `inform`, `comment`, `review`, `acknowledge`
|
||
- **Co-sign:** `mitzeichnung`, `approve`
|
||
- **Bind:** `schlusszeichnung`, `sign`, `seal`, `deposit`
|
||
|
||
### B1. Status `draft`
|
||
|
||
| Verb | requester | assignee | agent | Effect |
|
||
|---|---|---|---|---|
|
||
| comment | ✓ | — | ✓ | thread only |
|
||
| discuss | ✓ | — | — | stays draft |
|
||
| withdraw | ✓ | — | — | → withdrawn |
|
||
| accept / decline / acknowledge / return / forward / escalate / configure | — | — | — | not circulating |
|
||
| submit (system/requester) | ✓ | — | — | → submitted |
|
||
|
||
### B2. Status `submitted` / `circulating` / `awaiting_actor` / `in_discussion`
|
||
|
||
| Verb | Weak step | Co-sign | Bind step | Guards | Status after |
|
||
|---|---|---|---|---|---|
|
||
| comment | ✓ any party | ✓ | ✓ | — | unchanged (`in_discussion` if was circulating) |
|
||
| discuss | ✓ any party | ✓ | ✓ | — | `in_discussion` |
|
||
| return | ✓ assignee | ✓ | ✓ | G_NOAGENT G_REASONS | `returned` |
|
||
| forward | ✓ assignee | ✓ | ✓ | G_NOAGENT G_TARGET | stays circulating; step assignee replaced |
|
||
| escalate | ✓ assignee | ✓ | ✓ | G_NOAGENT G_TARGET G_ESC | circulating; higher step inserted/activated |
|
||
| acknowledge | C assignee | C | ✓ overlay | G_NOAGENT G_PRES G_ACK | step complete; memo completes only if this was the last step *and* `requested_act=acknowledge` |
|
||
| accept | — | C assignee | **M** assignee | G_NOAGENT G_PRES G_ACK G_MAND G_LEVEL G_STEP | last bind/co-sign step → `completed` outcome=`accepted` |
|
||
| decline | — | C assignee | **M** assignee | G_NOAGENT G_PRES G_STEP | → `completed` outcome=`declined` (memo ends; later steps skipped) |
|
||
| abstain | — | C assignee | C assignee | G_NOAGENT G_PRES | step complete for this actor; parallel group may continue; if actor was sole bind step → `completed` outcome=`abstained` |
|
||
| recuse | ✓ | C | C | G_NOAGENT G_PRES | step unassigned; must forward/escalate or return |
|
||
| withdraw | requester only | same | same | not terminal | → `withdrawn` |
|
||
| stamp | system | system | system | after a bind disposition or on completion | unchanged |
|
||
| configure | — | — | — | not yet bound | — |
|
||
| promote | requester | requester | requester | G_PROMOTE | version++; back toward submitted |
|
||
|
||
`accept` on a **weak** step is illegal. Weak steps take `acknowledge`. That is how Kenntnisnahme stays distinct from Schlusszeichnung.
|
||
|
||
### B3. Status `returned`
|
||
|
||
| Verb | Who | Effect |
|
||
|---|---|---|
|
||
| comment / discuss | any party | thread |
|
||
| revise + submit | requester | version++; → submitted; new route from policy |
|
||
| withdraw | requester | → withdrawn |
|
||
| binding verbs | — | illegal on a returned version |
|
||
|
||
### B4. Status `completed`
|
||
|
||
| Verb | Who | Effect |
|
||
|---|---|---|
|
||
| comment | parties | thread; does not unseal |
|
||
| stamp | system | file.stamped / bundle.sealed |
|
||
| configure | subject | **S** session hat; see §C |
|
||
| promote | requester | not on this version; open child memo |
|
||
| accept / return / … | — | sealed |
|
||
|
||
If `requested_act ∈ {login, elevate}` then `session.status` becomes `active` at the completing accept/acknowledge.
|
||
|
||
### B5. Terminal `withdrawn` | `expired` | `superseded`
|
||
|
||
No actor verbs except system `stamp` of the evidence bundle already accumulated.
|
||
|
||
## C. Post-bind session transitions
|
||
|
||
Applies when `session.status = active`.
|
||
|
||
```
|
||
pre_bind presentation
|
||
│ actor chooses identity + bind-scope
|
||
│ sees proposed hat / other tenants as AWARENESS only
|
||
▼
|
||
bind disposition (accept | acknowledge)
|
||
│ view_hash covers binding_slice only
|
||
│ awareness_hash recorded alongside, not signed
|
||
▼
|
||
session.active
|
||
├── configure(hat) if hat.elevates=false ∧ hat in available_hats
|
||
├── attempt other scope
|
||
│ requires_new_bind=false → still configure (same bind)
|
||
│ requires_new_bind=true → emit scope.rebind_required, open child memo
|
||
└── session.ended logout / expiry / revoke
|
||
```
|
||
|
||
| Event | In `view_hash`? | In `awareness_hash`? | Evidence event |
|
||
|---|---|---|---|
|
||
| Chosen identity | yes | copy ok | identity.authenticated |
|
||
| Chosen bind-scope / tenant of *this* login | yes | copy ok | disposition.recorded |
|
||
| Privileges granted *at the gate* | yes | preview ok | disposition.recorded |
|
||
| Proposed / last-used hat shown | no | yes | awareness.shown |
|
||
| Hat selected after login | no | yes | session.hat_selected |
|
||
| Hat with `elevates=true` | must promote | — | promote + new bind |
|
||
| Other tenant with `requires_new_bind` | no | listed | scope.rebind_required |
|
||
|
||
Defaulting the hat to `last_used` or `system_default` is an awareness act. Showing it on the login banner is required for situational awareness and forbidden from silently entering `view_hash`.
|
||
|
||
## D. Completing a step vs completing the memo
|
||
|
||
A step completes when the assignee records a verb in that step’s completion set:
|
||
|
||
| Step kind | Completion set |
|
||
|---|---|
|
||
| inform, review, acknowledge | acknowledge, return, recuse |
|
||
| comment | comment (if so configured) or acknowledge |
|
||
| mitzeichnung, approve | accept, decline, abstain, recuse, return |
|
||
| schlusszeichnung, sign, seal, deposit | accept, decline, abstain, recuse, return |
|
||
|
||
The memo completes when:
|
||
|
||
1. A bind-step `decline` is recorded, or
|
||
2. Every required step is `completed` or `skipped`, and at least one completing verb in the memo’s `requested_act` family was recorded, or
|
||
3. Requester `withdraw`, or system expiry.
|
||
|
||
Parallel groups: all members must reach a terminal step status before the next ordinal becomes `ready`.
|
||
|
||
## E. Illegal combinations (explicit)
|
||
|
||
- Agent + any binding verb
|
||
- `accept` on `inform`/`review`
|
||
- `configure` before `session.active`
|
||
- `configure` of a hat with `elevates=true`
|
||
- `configure` of a scope with `requires_new_bind=true`
|
||
- Binding verb whose presentation `phase=post_bind`
|
||
- Binding verb whose presentation `awareness_hash` was substituted for `view_hash`
|
||
- Mutating `identity_context.binding` after the first bind disposition of that version
|