informed-decision/history/20260909-initial-exploration/state-transitions.md
tegwick ee2cca579c Establish INTENT, Stage 1 GOAL, and founding workplan
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
2026-09-09 10:47:36 +02:00

184 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 steps 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 memos `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