Add PRD and Use Case Catalog; file the gate-house request (T02-T04)
T02 — intake INFD-IN-0001 filed with gate-house and messages sent to gate-house,
key-cape and approval-engine. Status progress; it now waits on an external
ruling. key-cape was told explicitly why T07 is not answering KEY-WP-0013-T02
yet — a placeholder callback URI would either fail closed or register an origin
no component owns — and given the token shape to check now rather than at T07.
T03 — ProductRequirementsDocument.md. 30 requirements, each traced to a GOAL.md
DoD item, an INTENT principle or wrongness condition, a state-transition guard,
or a named external contract, and each with an observable pass condition.
Anti-requirements are stated as testable absences: no dwell timers, no attention
analytics, no dark patterns, no auto-approval, no approval-state caching, no
authorization endpoint. Four limitations are recorded up front, including that
escalate without a mandate graph is forwarding and that view_hash is computed by
the renderer.
T04 — UseCaseCatalog.md. L0-L5 with counterparties, plus ten negative cases
bound to guards and isolation vectors. Each case records the constraint it
places on the shared schema, so scale invariance is testable rather than
asserted. Closes with the four changes that would fork the object.
T05 and T07 remain gated on the ruling. T06 is independent and is next.
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 14:11:36 +02:00
|
|
|
# Use Case Catalog
|
|
|
|
|
|
|
|
|
|
**Repo:** informed-decision
|
|
|
|
|
**Workplan task:** `INFD-WP-0001-T04`
|
|
|
|
|
**Status:** draft, pending review
|
|
|
|
|
**Companion specs:** `ProductRequirementsDocument.md` (Stage 1 scope),
|
|
|
|
|
`ArchitectureBlueprint.md`, `EvidenceModel.md`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Why this catalog exists
|
|
|
|
|
|
|
|
|
|
Scale invariance is the thesis of this repository: one object model from a
|
|
|
|
|
ten-second login to a multi-party instrument. A thesis stated in a vision
|
|
|
|
|
document is decoration. This catalog exists to make it **testable** — every case
|
|
|
|
|
below must map onto the single Decision Memo schema with **no level-specific
|
|
|
|
|
object**.
|
|
|
|
|
|
|
|
|
|
Only UC-30 is in Stage 1 build scope. The rest are here to constrain the schema
|
|
|
|
|
now, so it cannot fork later when they arrive.
|
|
|
|
|
|
|
|
|
|
**The test this catalog applies to every case:** can it be expressed with the
|
|
|
|
|
fields in `decision-memo.schema.json` plus values, or does it need a new field?
|
|
|
|
|
A new field is permitted only if it is also expressible for UC-00 — the login
|
|
|
|
|
banner, the shallowest case. A field that only makes sense at L4 is a fork.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Depth spectrum overview
|
|
|
|
|
|
|
|
|
|
| Case | L | Act | Binding | Counterparty | Stage |
|
|
|
|
|
| --- | --- | --- | --- | --- | --- |
|
|
|
|
|
| UC-00 | L0 | Informed login | acknowledgment / organizational | `key-cape` | 2 |
|
|
|
|
|
| UC-10 | L1 | JIT privilege elevation | organizational | `key-cape`, `secrets-engine` | 2 |
|
|
|
|
|
| UC-20 | L2 | Formal ADR accept | organizational / AES | estate decision records | 2 |
|
|
|
|
|
| **UC-30** | **L3** | **Approval entry** | **organizational** | **`approval-engine`** | **1** |
|
|
|
|
|
| UC-40 | L4 | Contract / instrument | AES / QES | external counterparty | horizon |
|
|
|
|
|
| UC-50 | L5 | Multi-party instrument | QES per party | multiple | horizon |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-00 — Informed login (L0)
|
|
|
|
|
|
|
|
|
|
**Actor:** any principal signing in.
|
|
|
|
|
**Trigger:** authentication at a system boundary.
|
|
|
|
|
**Question:** *"Sign in to Payroll-Prod as Bernd Worsch?"*
|
|
|
|
|
**Requested act:** `login`.
|
|
|
|
|
|
|
|
|
|
**Binding slice (signed):** which identity (ordinary vs break-glass), which
|
|
|
|
|
scope/tenant/system, privileges granted at the gate, monitoring and consent
|
|
|
|
|
terms.
|
|
|
|
|
|
|
|
|
|
**Awareness slice (shown, hashed, not signed):** the role you will wear after
|
|
|
|
|
login and where that default came from (`last_used` | `system_default` |
|
|
|
|
|
`policy` | `explicit`); other roles available inside the bound scope; other
|
|
|
|
|
tenants you support; last session summary.
|
|
|
|
|
|
|
|
|
|
**Verbs:** `acknowledge` (completes), `decline`, `discuss`. **Not** `accept` —
|
|
|
|
|
this is Kenntnisnahme.
|
|
|
|
|
|
|
|
|
|
**Post-bind:** `configure(hat)` for non-elevating roles only, while
|
|
|
|
|
`session.status = active`. Switching to a tenant with `requires_new_bind` opens
|
|
|
|
|
a **child memo**, never a silent scope change.
|
|
|
|
|
|
|
|
|
|
**Evidence:** presentation with both hashes; `session.hat_selected` events that
|
|
|
|
|
do not rewrite `view_hash`.
|
|
|
|
|
|
|
|
|
|
**Why it constrains the schema:** UC-00 is the shallowest case and therefore the
|
|
|
|
|
strictest constraint. It forced the pre-sign/post-sign split that now applies at
|
|
|
|
|
every level — see `INTENT.md` principle 11. Any field proposed for a deeper case
|
|
|
|
|
must be expressible here or be justified as genuinely level-specific.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-10 — JIT privilege elevation (L1)
|
|
|
|
|
|
|
|
|
|
**Actor:** an operator needing elevated privilege for a bounded task.
|
|
|
|
|
**Question:** *"Elevate to Payroll Admin on ACME/Payroll-Prod for 60 minutes?"*
|
|
|
|
|
**Requested act:** `elevate`.
|
|
|
|
|
|
|
|
|
|
**Binding slice:** the privilege, the scope, the validity window, the stated
|
|
|
|
|
justification, the monitoring terms.
|
|
|
|
|
**Awareness slice:** current role, what the elevation adds, prior elevations.
|
|
|
|
|
|
|
|
|
|
**Verbs:** `acknowledge` or `accept` depending on whether the elevation is
|
|
|
|
|
self-service-with-record or requires a co-signer; `decline`; `escalate` where
|
|
|
|
|
the operator's own mandate is insufficient.
|
|
|
|
|
|
|
|
|
|
**Counterparty:** `key-cape` for identity; `secrets-engine` if the elevation
|
|
|
|
|
materialises credentials — which this repository never does itself.
|
|
|
|
|
|
|
|
|
|
**Constraint contributed:** an **elevating** role can never be reached by
|
|
|
|
|
`configure`. It requires either promotion into the binding slice with a new memo
|
|
|
|
|
version, or its own elevation memo. Guard `G_HAT`.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-20 — Formal ADR accept (L2)
|
|
|
|
|
|
|
|
|
|
**Actor:** an architect or owner with the mandate to accept a decision record.
|
|
|
|
|
**Question:** *"Accept ADR-0042 — Adopt the local outbox pattern?"*
|
|
|
|
|
**Requested act:** `accept_adr`.
|
|
|
|
|
|
|
|
|
|
**Binding slice:** the ADR id and version, the decision statement, consequences,
|
|
|
|
|
the accepting role.
|
|
|
|
|
**Packet:** the ADR document, plus any superseded record.
|
|
|
|
|
**Highlights:** the consequences section, typically `required_ack: true`.
|
|
|
|
|
|
|
|
|
|
**Verbs:** the full set. `return` with coded reasons is the common path and is
|
|
|
|
|
the point — an ADR sent back for a clearer consequences section is the surface
|
|
|
|
|
working.
|
|
|
|
|
|
|
|
|
|
**Constraint contributed:** *"accepted" must stop being a Git status and become
|
|
|
|
|
an identity act.* The evidence must answer who accepted it, in what role, having
|
|
|
|
|
seen which version. This is the case that most directly tests whether `view_hash`
|
|
|
|
|
carries enough to be useful.
|
|
|
|
|
|
|
|
|
|
**Note:** the estate's own decision records (`GH-DEC-*`, `FLEX-DEC-*`,
|
|
|
|
|
`APPROVAL-*`) are the natural first real consumer of UC-20. Stage 2.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-30 — Approval entry (L3) — **Stage 1**
|
|
|
|
|
|
|
|
|
|
**Actor:** a human approver holding the mandate, authenticated by `key-cape`.
|
|
|
|
|
**Trigger:** an approval exists in `approval-engine` in state `requested`,
|
|
|
|
|
awaiting an authenticated approval entry.
|
|
|
|
|
**Question:** *"Approve rotation of the production database credential for
|
|
|
|
|
task T-1183?"*
|
|
|
|
|
**Requested act:** `approve`.
|
|
|
|
|
|
|
|
|
|
**Binding slice:** the approval id; the action and target it authorizes; the
|
|
|
|
|
actor who may use it and the principal on whose behalf; the purpose; the
|
|
|
|
|
validity window; the approving identity and tenant.
|
|
|
|
|
|
|
|
|
|
**Awareness slice:** the approver's current role; other approvals pending for
|
|
|
|
|
them (orientation only — see PRD open question 1); prior related approvals.
|
|
|
|
|
|
|
|
|
|
**Packet:** the change or task the approval is for, and any policy citation
|
|
|
|
|
supplied by the requester.
|
|
|
|
|
**Highlights:** the target and the validity window, typically
|
|
|
|
|
`required_ack: true` — these are the two fields whose misreading is most costly.
|
|
|
|
|
|
|
|
|
|
**Verbs:** `accept` (submits the approval entry), `decline`, `return` with coded
|
|
|
|
|
reasons, `discuss`, `forward`, `escalate`, `comment`.
|
|
|
|
|
|
Revise specs against approval-engine's approver-surface requirements
approval-engine replied to INFD-IN-0001 with docs/approver-surface-requirements.md
(31da1af, 5203f46) and two corrections. Several of my requirements were wrong or
incomplete; revised rather than appended to.
Corrected:
- PR-02 listed only approval:approve. Wrong — the surface also needs
approval:read to fetch what it renders. As drafted it would have shipped a
client able to submit an entry it could never display. That changes a
registration key-cape has already implemented, so it is their call (open
question A).
- NC-03 implied approval-engine refuses non-human approver entries. It does not;
only /consume is principal-restricted, and the operator service client holds
approval:approve. Enforcement of "humans bind, agents draft" is therefore ours
alone, and is auditable via schema v4's entries[].principal_type — never from
the shape of subject_id.
Added:
- PR-04 assurance shape. approval-engine persists it verbatim and accepts an
empty object, so it is the only place MFA survives into the approval record.
Needs auth method, acr/amr, auth_time, agreed with key-cape.
- PR-05 entitlement. A 200 from the engine is not permission to view; we owe
access-engine a check before rendering. Consuming a decision, not making one.
- PR-06 response mapping, including 409 duplicate_approver rendered as SUCCESS
(a browser double-submit is routine and the first entry stands) and 503 as
fail-closed.
- PR-07 and a matching INTENT wrongness condition: never render `approved` as
permission to act. That is a PDP in the browser wearing UI copy.
- L-05, L-06 and EvidenceModel 8b: view_hash cannot ride into the entry — the
POST discards its body by design — so Stage 1 correlates by (approval_id,
subject, approved_at). DoD-3 is satisfied by the triple, not by a stored hash.
PRD open question 1 is answered by construction: there is no inbox endpoint and
there will not be one, so the approvals-inbox shape is foreclosed upstream.
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 14:20:13 +02:00
|
|
|
**Only `accept` reaches the engine.** `approval-engine` models entries against
|
|
|
|
|
an approval object and knows nothing of memo dispositions. `accept` becomes a
|
|
|
|
|
`POST /v1/approvals/{id}/entries`; `return`, `discuss`, `escalate` and the rest
|
|
|
|
|
are memo-level and must not be represented in that engine at all. This is the
|
|
|
|
|
correct separation, and it means the approval object alone does not show that a
|
|
|
|
|
human sent the question back — see `EvidenceModel.md` §8 and PRD L-06.
|
|
|
|
|
|
|
|
|
|
**Routes used:** `GET /v1/approvals/{id}` and `GET /v1/approvals/{id}/claim`
|
|
|
|
|
(scope `approval:read`), `POST /v1/approvals/{id}/entries` (scope
|
|
|
|
|
`approval:approve`). Never `/consume`. Any path containing `check` or ending in
|
|
|
|
|
`/authorize` returns 404 by design — there is no "may this person approve"
|
|
|
|
|
surface to call.
|
|
|
|
|
|
|
|
|
|
**Entitlement:** a `200` from the engine is not a statement that this human may
|
|
|
|
|
see the approval. `access-engine` is asked before rendering (PR-05).
|
|
|
|
|
|
|
|
|
|
**No inbox.** The engine exposes get-by-id only — no list, no search, no
|
|
|
|
|
"awaiting me". Approval ids arrive from whatever requested the approval.
|
|
|
|
|
|
Add PRD and Use Case Catalog; file the gate-house request (T02-T04)
T02 — intake INFD-IN-0001 filed with gate-house and messages sent to gate-house,
key-cape and approval-engine. Status progress; it now waits on an external
ruling. key-cape was told explicitly why T07 is not answering KEY-WP-0013-T02
yet — a placeholder callback URI would either fail closed or register an origin
no component owns — and given the token shape to check now rather than at T07.
T03 — ProductRequirementsDocument.md. 30 requirements, each traced to a GOAL.md
DoD item, an INTENT principle or wrongness condition, a state-transition guard,
or a named external contract, and each with an observable pass condition.
Anti-requirements are stated as testable absences: no dwell timers, no attention
analytics, no dark patterns, no auto-approval, no approval-state caching, no
authorization endpoint. Four limitations are recorded up front, including that
escalate without a mandate graph is forwarding and that view_hash is computed by
the renderer.
T04 — UseCaseCatalog.md. L0-L5 with counterparties, plus ten negative cases
bound to guards and isolation vectors. Each case records the constraint it
places on the shared schema, so scale invariance is testable rather than
asserted. Closes with the four changes that would fork the object.
T05 and T07 remain gated on the ruling. T06 is independent and is next.
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 14:11:36 +02:00
|
|
|
**Boundary — the part that must not drift:**
|
|
|
|
|
|
|
|
|
|
- The surface calls `approval-engine`'s approval-entry mutation with a token
|
|
|
|
|
carrying scope `approval:approve`. It never mutates approval state itself.
|
|
|
|
|
- It never requests or performs `approval:consume`. Consumption is the PEP's,
|
|
|
|
|
at the moment of use, per `GH-DEC-2026-003`.
|
|
|
|
|
- It never caches or serves approval validity.
|
|
|
|
|
- It never answers whether the underlying action is permitted. That is
|
|
|
|
|
`access-engine`.
|
|
|
|
|
|
|
|
|
|
**Evidence:** presentation record with `view_hash`, the disposition, and the
|
|
|
|
|
approval entry id returned by `approval-engine`, so the entry is reconstructable
|
|
|
|
|
back to what the approver saw.
|
|
|
|
|
|
|
|
|
|
**Why this is Stage 1:** it is the only case currently costing another
|
|
|
|
|
repository progress. `key-cape`'s `KEY-WP-0013-T02` is blocked on a browser
|
|
|
|
|
client for exactly this flow.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-40 — Contract or instrument (L4) — horizon
|
|
|
|
|
|
|
|
|
|
**Actor:** a signatory with commercial authority.
|
|
|
|
|
**Binding:** AES or QES. Identity imported from a QTSP or EUDI Wallet, never
|
|
|
|
|
issued here.
|
|
|
|
|
**Constraint contributed:** the signed attributes carry `memo_id`,
|
|
|
|
|
`memo_version`, `disposition.verb` and `presentation.view_hash` — and **not**
|
|
|
|
|
`awareness_hash` unless promotion ran. This is dynamic linking: the
|
|
|
|
|
human-readable content actually presented is bound into the signature.
|
|
|
|
|
|
|
|
|
|
**Open, not scoped:** retention and qualified archive
|
|
|
|
|
(exploration open question 6).
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## UC-50 — Multi-party international instrument (L5) — horizon
|
|
|
|
|
|
|
|
|
|
**Actors:** several parties, each with their own QTSP and jurisdiction.
|
|
|
|
|
**Constraint contributed:** parallel Mitzeichnung, per-party signature levels,
|
|
|
|
|
and per-party presentations of the same memo version. The schema must already
|
|
|
|
|
tolerate `parties[]` and multiple presentations per version — which it does.
|
|
|
|
|
|
|
|
|
|
Explicitly not v1, and named here only so nothing shipped earlier forecloses it.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Negative cases
|
|
|
|
|
|
|
|
|
|
These are as load-bearing as the positive ones. Each protects a named invariant
|
|
|
|
|
and each must be a test.
|
|
|
|
|
|
|
|
|
|
### NC-01 — `accept` on a Kenntnisnahme step
|
|
|
|
|
|
|
|
|
|
**Attempt:** the approver presses accept on an `inform`/`review`/`acknowledge`
|
|
|
|
|
step.
|
|
|
|
|
**Required behaviour:** the verb is **absent from the UI**, not present and
|
|
|
|
|
disabled; an API call attempting it is refused.
|
|
|
|
|
**Invariant protected:** Kenntnisnahme is not approval. Collapsing them is how
|
|
|
|
|
"I saw it" silently becomes "I approved it".
|
|
|
|
|
`state-transitions.md §B2 · PR-31`
|
|
|
|
|
|
|
|
|
|
### NC-02 — Bind with unacknowledged required highlights
|
|
|
|
|
|
|
|
|
|
**Attempt:** `accept` while a `required_ack` highlight is unacked.
|
|
|
|
|
**Required behaviour:** fail closed; no disposition created; error names the
|
|
|
|
|
outstanding highlight ids.
|
|
|
|
|
**Invariant protected:** the acknowledgment gate is the only positive evidence
|
|
|
|
|
of informedness the model has.
|
|
|
|
|
`guard G_ACK · DoD-4 · PR-20`
|
|
|
|
|
|
|
|
|
|
### NC-03 — Agent attempts a disposition
|
|
|
|
|
|
|
|
|
|
**Attempt:** a principal with `actor.kind = agent` submits any binding verb.
|
Revise specs against approval-engine's approver-surface requirements
approval-engine replied to INFD-IN-0001 with docs/approver-surface-requirements.md
(31da1af, 5203f46) and two corrections. Several of my requirements were wrong or
incomplete; revised rather than appended to.
Corrected:
- PR-02 listed only approval:approve. Wrong — the surface also needs
approval:read to fetch what it renders. As drafted it would have shipped a
client able to submit an entry it could never display. That changes a
registration key-cape has already implemented, so it is their call (open
question A).
- NC-03 implied approval-engine refuses non-human approver entries. It does not;
only /consume is principal-restricted, and the operator service client holds
approval:approve. Enforcement of "humans bind, agents draft" is therefore ours
alone, and is auditable via schema v4's entries[].principal_type — never from
the shape of subject_id.
Added:
- PR-04 assurance shape. approval-engine persists it verbatim and accepts an
empty object, so it is the only place MFA survives into the approval record.
Needs auth method, acr/amr, auth_time, agreed with key-cape.
- PR-05 entitlement. A 200 from the engine is not permission to view; we owe
access-engine a check before rendering. Consuming a decision, not making one.
- PR-06 response mapping, including 409 duplicate_approver rendered as SUCCESS
(a browser double-submit is routine and the first entry stands) and 503 as
fail-closed.
- PR-07 and a matching INTENT wrongness condition: never render `approved` as
permission to act. That is a PDP in the browser wearing UI copy.
- L-05, L-06 and EvidenceModel 8b: view_hash cannot ride into the entry — the
POST discards its body by design — so Stage 1 correlates by (approval_id,
subject, approved_at). DoD-3 is satisfied by the triple, not by a stored hash.
PRD open question 1 is answered by construction: there is no inbox endpoint and
there will not be one, so the approvals-inbox shape is foreclosed upstream.
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 14:20:13 +02:00
|
|
|
**Required behaviour:** refused at *this repository's* API, not merely hidden in
|
|
|
|
|
the UI.
|
Add PRD and Use Case Catalog; file the gate-house request (T02-T04)
T02 — intake INFD-IN-0001 filed with gate-house and messages sent to gate-house,
key-cape and approval-engine. Status progress; it now waits on an external
ruling. key-cape was told explicitly why T07 is not answering KEY-WP-0013-T02
yet — a placeholder callback URI would either fail closed or register an origin
no component owns — and given the token shape to check now rather than at T07.
T03 — ProductRequirementsDocument.md. 30 requirements, each traced to a GOAL.md
DoD item, an INTENT principle or wrongness condition, a state-transition guard,
or a named external contract, and each with an observable pass condition.
Anti-requirements are stated as testable absences: no dwell timers, no attention
analytics, no dark patterns, no auto-approval, no approval-state caching, no
authorization endpoint. Four limitations are recorded up front, including that
escalate without a mandate graph is forwarding and that view_hash is computed by
the renderer.
T04 — UseCaseCatalog.md. L0-L5 with counterparties, plus ten negative cases
bound to guards and isolation vectors. Each case records the constraint it
places on the shared schema, so scale invariance is testable rather than
asserted. Closes with the four changes that would fork the object.
T05 and T07 remain gated on the ruling. T06 is independent and is next.
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 14:11:36 +02:00
|
|
|
**Invariant protected:** humans bind, agents draft.
|
|
|
|
|
`guard G_NOAGENT · P-10 · W-"An agent binds"`
|
|
|
|
|
|
Revise specs against approval-engine's approver-surface requirements
approval-engine replied to INFD-IN-0001 with docs/approver-surface-requirements.md
(31da1af, 5203f46) and two corrections. Several of my requirements were wrong or
incomplete; revised rather than appended to.
Corrected:
- PR-02 listed only approval:approve. Wrong — the surface also needs
approval:read to fetch what it renders. As drafted it would have shipped a
client able to submit an entry it could never display. That changes a
registration key-cape has already implemented, so it is their call (open
question A).
- NC-03 implied approval-engine refuses non-human approver entries. It does not;
only /consume is principal-restricted, and the operator service client holds
approval:approve. Enforcement of "humans bind, agents draft" is therefore ours
alone, and is auditable via schema v4's entries[].principal_type — never from
the shape of subject_id.
Added:
- PR-04 assurance shape. approval-engine persists it verbatim and accepts an
empty object, so it is the only place MFA survives into the approval record.
Needs auth method, acr/amr, auth_time, agreed with key-cape.
- PR-05 entitlement. A 200 from the engine is not permission to view; we owe
access-engine a check before rendering. Consuming a decision, not making one.
- PR-06 response mapping, including 409 duplicate_approver rendered as SUCCESS
(a browser double-submit is routine and the first entry stands) and 503 as
fail-closed.
- PR-07 and a matching INTENT wrongness condition: never render `approved` as
permission to act. That is a PDP in the browser wearing UI copy.
- L-05, L-06 and EvidenceModel 8b: view_hash cannot ride into the entry — the
POST discards its body by design — so Stage 1 correlates by (approval_id,
subject, approved_at). DoD-3 is satisfied by the triple, not by a stored hash.
PRD open question 1 is answered by construction: there is no inbox endpoint and
there will not be one, so the approvals-inbox shape is foreclosed upstream.
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 14:20:13 +02:00
|
|
|
**Corrected 2026-09-09.** The first draft implied the refusal was also
|
|
|
|
|
`approval-engine`'s. It is not: that engine restricts only `/consume` by
|
|
|
|
|
principal type, and its `approval-engine-operator` service client holds
|
|
|
|
|
`approval:approve`, so a non-human principal can supply approver evidence there
|
|
|
|
|
today. Whether it should is `gate-house` doctrine, not ours.
|
|
|
|
|
|
|
|
|
|
Two consequences. First, enforcement of P-10 is **this surface's own
|
|
|
|
|
obligation** — there is no upstream backstop. Second, it is *auditable*:
|
|
|
|
|
schema v4 records the verified `entries[].principal_type` from the token. Read
|
|
|
|
|
that field. **Never** infer the answer from the shape of `subject_id`, which is
|
|
|
|
|
a naming convention, not a verified claim; entries predating v4 are `null` and
|
|
|
|
|
must not be read as `human`.
|
|
|
|
|
|
Add PRD and Use Case Catalog; file the gate-house request (T02-T04)
T02 — intake INFD-IN-0001 filed with gate-house and messages sent to gate-house,
key-cape and approval-engine. Status progress; it now waits on an external
ruling. key-cape was told explicitly why T07 is not answering KEY-WP-0013-T02
yet — a placeholder callback URI would either fail closed or register an origin
no component owns — and given the token shape to check now rather than at T07.
T03 — ProductRequirementsDocument.md. 30 requirements, each traced to a GOAL.md
DoD item, an INTENT principle or wrongness condition, a state-transition guard,
or a named external contract, and each with an observable pass condition.
Anti-requirements are stated as testable absences: no dwell timers, no attention
analytics, no dark patterns, no auto-approval, no approval-state caching, no
authorization endpoint. Four limitations are recorded up front, including that
escalate without a mandate graph is forwarding and that view_hash is computed by
the renderer.
T04 — UseCaseCatalog.md. L0-L5 with counterparties, plus ten negative cases
bound to guards and isolation vectors. Each case records the constraint it
places on the shared schema, so scale invariance is testable rather than
asserted. Closes with the four changes that would fork the object.
T05 and T07 remain gated on the ruling. T06 is independent and is next.
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 14:11:36 +02:00
|
|
|
### NC-04 — Tenant switch requiring a new bind
|
|
|
|
|
|
|
|
|
|
**Attempt:** after an L0/L1 bind, the subject selects a tenant marked
|
|
|
|
|
`requires_new_bind`.
|
|
|
|
|
**Required behaviour:** `scope.rebind_required` is set and a **child memo** is
|
|
|
|
|
opened. The current session's `view_hash` is not rewritten.
|
|
|
|
|
**Invariant protected:** scope is committed, not configured.
|
|
|
|
|
`guard G_HAT · isolation vector 4`
|
|
|
|
|
|
|
|
|
|
### NC-05 — Awareness field edited after presentation
|
|
|
|
|
|
|
|
|
|
**Attempt:** the proposed role or last-session summary changes between render
|
|
|
|
|
and bind.
|
|
|
|
|
**Required behaviour:** `awareness_hash` changes; `view_hash` does **not**; the
|
|
|
|
|
bind remains valid.
|
|
|
|
|
**Invariant protected:** the person is bound to what they committed to, not to
|
|
|
|
|
orientation material.
|
|
|
|
|
`isolation vector 2 · PR-40`
|
|
|
|
|
|
|
|
|
|
### NC-06 — Binding target changed
|
|
|
|
|
|
|
|
|
|
**Attempt:** `binding.target` is altered from ACME to Beta.
|
|
|
|
|
**Required behaviour:** `view_hash` changes, invalidating any outstanding
|
|
|
|
|
presentation.
|
|
|
|
|
**Invariant protected:** the binding slice is genuinely covered by the hash —
|
|
|
|
|
the positive control for NC-05's negative one.
|
|
|
|
|
`isolation vector 3`
|
|
|
|
|
|
|
|
|
|
### NC-07 — Bind against a stale memo version
|
|
|
|
|
|
|
|
|
|
**Attempt:** a presentation taken at version *n* is used to bind after the memo
|
|
|
|
|
advanced to *n+1*.
|
|
|
|
|
**Required behaviour:** version conflict; no silent upgrade.
|
|
|
|
|
**Invariant protected:** binding acts reference a presentation of the same
|
|
|
|
|
version.
|
|
|
|
|
`guard G_PRES · PR-14`
|
|
|
|
|
|
|
|
|
|
### NC-08 — `return` with no coded reason
|
|
|
|
|
|
|
|
|
|
**Attempt:** return with free text only.
|
|
|
|
|
**Required behaviour:** refused.
|
|
|
|
|
**Invariant protected:** return is structured feedback, not a shrug.
|
|
|
|
|
`guard G_REASONS · PR-32`
|
|
|
|
|
|
|
|
|
|
### NC-09 — `escalate` to an equal or lower mandate
|
|
|
|
|
|
|
|
|
|
**Attempt:** escalate to a target whose mandate is not strictly higher.
|
|
|
|
|
**Required behaviour:** refused as an escalation; offered as `forward`.
|
|
|
|
|
**Stage 1 caveat:** without the mandate graph this guard cannot be fully
|
|
|
|
|
evaluated. Stage 1 records the asserted intent and marks it **unverified**
|
|
|
|
|
rather than pretending the guard passed.
|
|
|
|
|
`guard G_ESC · limitation L-01`
|
|
|
|
|
|
|
|
|
|
### NC-10 — Key shuffling
|
|
|
|
|
|
|
|
|
|
**Attempt:** serialise the same binding document with object keys in a different
|
|
|
|
|
order.
|
|
|
|
|
**Required behaviour:** identical `view_hash`.
|
|
|
|
|
**Invariant protected:** canonicalization is real; otherwise every hash is an
|
|
|
|
|
artifact of serialisation order.
|
|
|
|
|
`isolation vector 1`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Schema pressure — cases that would fork the object
|
|
|
|
|
|
|
|
|
|
Recorded so a future contributor recognises them as forks rather than features:
|
|
|
|
|
|
|
|
|
|
- **A level-specific status.** e.g. `awaiting_notarisation` for L4. Belongs as a
|
|
|
|
|
step kind or a route step, never a new memo status.
|
|
|
|
|
- **Multiple questions per memo.** An approval bundling three unrelated acts is
|
|
|
|
|
three memos. The single-question invariant is what makes `view_hash` mean
|
|
|
|
|
something.
|
|
|
|
|
- **A per-level packet model.** L4 attachments are packet items, hashed exactly
|
|
|
|
|
like an L0 consent text.
|
|
|
|
|
- **An approvals-inbox object.** A list of memos is a query result, not a
|
|
|
|
|
schema entity. If it acquires state, the product has become a queue.
|