informed-decision/history/20260909-initial-exploration/decision-memo-schema.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

149 lines
7.2 KiB
Markdown

# Decision Memo object schema v0.2
Machine-readable schema: `decision-memo.schema.json`
Worked example: `decision-memo.example.json`
The Decision Memo is one object at every depth (L0 login banner through L5 multi-party instrument). Depth changes which fields are populated, not which types exist.
## 1. Object graph
```
DecisionMemo
├── requester / subject / parties[] identity + mandate
├── question + requested_act + binding_level what is being asked
├── brief + consequences
├── identity_context.binding PRE-SIGN identity + scope (in view_hash)
├── identity_context.awareness hats, last-used, other tenants (awareness_hash)
├── session POST-SIGN live hat inside the bound scope
├── packet[] files, hashed
├── highlights[] pointers into packet
├── route.steps[] Laufweg / Zeichnungsleiste
├── thread[] discussion stays on the object
├── presentations[] what each actor was shown
├── dispositions[] comment | return | forward | accept…
├── relations[] supersedes / relates_to
└── evidence hash-chained event log + optional seal
```
## 2. Lifecycle
```
draft
→ submitted
→ circulating / awaiting_actor / in_discussion
→ returned ──(new version)──→ submitted
→ completed (outcome set)
→ withdrawn | expired
completed ─(new memo)→ superseded
```
Rules:
- Edits in `draft` bump `version` without a new `id`.
- After `submitted`, content edits that change `brief`, `packet`, or `highlights` **must** bump `version` and invalidate unused presentations.
- `return` sets status `returned`, does not mutate the sealed packet in place. Requester opens version N+1, which `supersedes` the circulation of N.
- `forward` replaces the current step assignee. `escalate` adds or replaces a step of higher competence.
- Terminal statuses: `completed`, `withdrawn`, `expired`, `superseded`.
- `outcome` is only set when `status=completed` (or timed-out/cancelled mappings).
## 3. Invariants
I1. **One question.** `question` is a single present-tense sentence. If you need two questions, you need two memos.
I2. **Presentation binds the act.** Every `disposition` with verb in `{acknowledge, accept, decline, abstain, recuse, sign}` MUST reference a `presentation_id` whose `memo_version` equals the memo version at act time.
I3. **Dynamic linking.** Binding acts at `aes` or higher MUST set `signature.presentation_bound=true` and include `presentation.view_hash` in the signed attributes.
I4. **Required highlights.** If any highlight has `required_ack=true`, a binding act is invalid unless those ids are in `presentation.acked_highlight_ids`.
I5. **Mandate fit.** The actor on a `schlusszeichnung` / `sign` step MUST carry a `mandate` whose scope covers `requested_act` and any `legal` / value constraints.
I6. **Packet integrity.** `packet[].hash` is content-addressed. Replacing bytes is a new `item.id` (or a new item version + `replaces`).
I7. **Evidence is append-only.** `evidence.events[].event_hash = H(seq || type || at || payload_hash || prev_hash)`. `head_hash` is the last `event_hash`.
I8. **Agents may draft, not bind.** `party.kind=agent` may create highlights and brief text. Agents MUST NOT emit binding verbs.
I9. **Return is data.** `verb=return` requires `return.reasons[]`. Empty “please fix” is invalid.
I10. **Whole instrument vs excerpt.** Highlights never narrow the legal object. `legal` and the primary packet item define what is bound; highlights are attention, not scope.
I11. **Pre-sign vs post-sign.** `identity_context.binding` is in `view_hash`. `identity_context.awareness` is only in `awareness_hash`. Binding verbs sign the former. `configure` mutates `session` after bind and never rewrites `binding`.
I12. **Hats are not scopes.** A hat (`elevates=false`) is a perspective inside an already bound scope. A scope with `requires_new_bind=true` (other tenant, other identity, elevating hat) is a new memo.
I13. **Defaults are awareness.** `proposed_hat_source` of `last_used` or `system_default` must be shown and must not be silently copied into `view_hash`.
## 4. Disposition verbs
| Verb | Changes route? | Binding? | Required payload |
|---|---|---|---|
| comment | no | no | body |
| discuss | may set `in_discussion` | no | body + optional transfer.to for requester |
| return | yes → requester | no | return.reasons |
| forward | yes → peer assignee | no | transfer.to |
| escalate | yes → higher mandate | no | transfer.to + reason |
| acknowledge | completes inform/review step | weak | presentation_id |
| accept | may complete memo | yes | presentation_id + optional signature |
| decline | may complete memo | yes | presentation_id + body |
| abstain / recuse | completes step for actor | yes | presentation_id + reason |
| withdraw | terminal | requester only | body |
| stamp | no | system/org seal | signature on listed items |
## 5. Canonical `view_hash`
Serialize, UTF-8, JCS (RFC 8785), then hash:
```json
{
"memo_id": "...",
"memo_version": 2,
"question": "...",
"requested_act": "accept",
"binding_level": "aes",
"brief": "...",
"packet": [{"item_id":"...","hash":"sha256:..."}],
"highlights": [{"id":"...","item_id":"...","locator":{...},"required_ack":true}],
"ui_release": "informed-decision@0.3.1",
"locale": "en"
}
```
That document is what “informed” means in the evidence bundle.
## 6. Evidence event minimum set
For a completed accept:
1. `memo.created`
2. `memo.submitted`
3. `packet.attached` (per item)
4. `highlight.added` (per required highlight)
5. `route.started`
6. `identity.authenticated`
7. `presentation.opened`
8. `presentation.highlight_acked`
9. `disposition.recorded` (accept)
10. `signature.created` (if aes/qes)
11. `timestamp.qualified` (if qes / long retention)
12. `file.stamped` (per `is_to_be_stamped`)
13. `bundle.sealed`
## 7. Field usage by depth
| Field | L0 login | L1 elevate | L2 ADR | L3 approval | L4 contract | L5 treaty |
|---|---|---|---|---|---|---|
| identity_context | required | required | optional | optional | rare | rare |
| packet | optional | ticket + change | ADR + links | memo + annexes | contract + annexes | instruments + translations |
| highlights | optional | blast-radius | rollback / cost | risk spans | clauses | reservations |
| binding_level | acknowledgment / organizational | organizational | organizational / aes | organizational / aes | aes / qes | qes per party |
| route | one step | requester → approver | review → accept | matrix | sequential/parallel signers | hybrid + deposit |
| legal.form_requirement | none | none | none | none / textform | textform / schriftform | varies by party |
## 8. What is deliberately not in v0.1
- Full mandate-graph resolution (referenced by `mandate.policy_ref`, not modelled here)
- Payment / consideration objects
- Translation alignment and reservation semantics for L5
- Raw PII in evidence (use `ip_hash`, identifier schemes)
- UI widget tree — only `ui_release` + canonical view