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
This commit is contained in:
tegwick 2026-09-09 14:11:36 +02:00
parent ebd35b59cf
commit b78c15a7ef
5 changed files with 686 additions and 3 deletions

View file

@ -0,0 +1,361 @@
# Product Requirements Document — Stage 1
**Repo:** informed-decision
**Stage:** 1 — the L3 approval approver surface
**Workplan task:** `INFD-WP-0001-T03`
**Status:** draft, pending review against the current `approval-engine`,
`key-cape`, `access-engine` and `audit-core` contracts
**Companion specs:** `UseCaseCatalog.md`, `ArchitectureBlueprint.md` (gated on
`INFD-WP-0001-T02`), `EvidenceModel.md`
---
## 1. Scope
Stage 1 delivers **one** thing: a browser-facing surface on which a human
approver is shown an `approval-engine` approval request, understands it, acts on
it with the full disposition vocabulary, and leaves a reconstructable record of
what they were shown when they bound themselves.
Everything in this document is L3 on the depth spectrum. L0, L1, L2, L4 and L5
appear only in `UseCaseCatalog.md`, and only to constrain the schema so it
cannot fork later.
**Traceability.** Every requirement below carries a `trace:` line pointing at
either a `GOAL.md` definition-of-done item (`DoD-n`), an `INTENT.md` principle
(`P-n`) or wrongness condition (`W-n`), or a named external contract. A
requirement that traces to nothing is out of scope by construction.
**Testability.** Every requirement states an observable pass condition. "The UI
should be clear" is not a requirement; "a bind attempt with an unacknowledged
required highlight returns an error and creates no disposition" is.
---
## 2. Personas
### 2.1 Approver (primary)
Holds a mandate. Receives a memo because the routing said they are the person
who may bind this act — not because they were available. Is usually busy, is
frequently not a specialist in the subject matter, and is accountable for the
outcome afterwards.
What they need: to know why this is on their desk, what happens if they say yes,
what happens if they say nothing, and what they are actually binding. What they
must be able to do without leaving the object: ask a question, send it back, and
push it upward.
### 2.2 Requester (secondary)
Assembled the memo, or an agent assembled it on their behalf. Needs to know
where it is, who is holding it, and what was asked for when it comes back.
Stage 1 does **not** give the requester an authoring UI — memos arrive from
`approval-engine`. Authoring is Stage 2+.
### 2.3 Observer / auditor (tertiary)
Arrives months later with a question of the form *"who decided this, having seen
what?"* Never uses the live surface. Consumes the evidence bundle export and
`audit-core`. Their requirement is that the answer exists and verifies offline.
### 2.4 Non-persona: the agent
An agent may assemble and enrich a memo. It has **no** disposition capability.
This is stated as a persona exclusion because the temptation to add "auto-approve
low-risk items" arrives in every approval product, and it is the single change
that would void the identity half of the promise.
`trace: P-10, W-"An agent binds", guard G_NOAGENT`
---
## 3. Functional requirements
### 3.1 Identity and session
**PR-01 — Identity is imported, never issued.**
Every principal is authenticated by `key-cape` via authorization-code + PKCE.
The surface holds no password, no local credential store, and issues no
assurance level of its own.
*Pass:* no code path creates a session not derived from a `key-cape` token;
authentication failure yields no session.
`trace: DoD-2, P-9, GOAL invariant "Identity is imported"`
**PR-02 — Token shape is fixed by the resource server.**
Tokens presented to `approval-engine` carry `aud=approval-engine`,
`principal_type: human`, `tenant: tenant:platform`, scope `approval:approve`.
The surface never requests `approval:consume`.
*Pass:* a token request including `approval:consume` fails a build-time check;
`approval-engine`'s verifier accepts the issued token.
`trace: approval-engine docs/keycape-service-registrations.md; DoD-2`
**PR-03 — The surface displays which identity and which scope are being used.**
Before any binding act, the approver sees the identity they are acting as and
the tenant/scope they are acting in, as committed values — not as inferred
context.
*Pass:* both values appear in the rendered binding document and therefore in
`view_hash`.
`trace: P-11, EvidenceModel binding slice`
### 3.2 Presentation
**PR-10 — A memo renders question, requested act, binding level, brief and
consequences before any action control is reachable.**
*Pass:* the disposition controls are not operable until the brief region has
been rendered; a memo missing a question fails validation and does not render.
`trace: INTENT "One question per memo"; GOAL invariant`
**PR-11 — The packet is always reachable in full.**
Highlights are an aid to attention, never a filter. Every document in the packet
is openable from the memo, and the count of packet items is visible without
interaction.
*Pass:* for a memo with *n* packet items, all *n* are reachable; no item is
hidden behind a highlight-only view.
`trace: P-8, W-"Highlights shrink the legal object"`
**PR-12 — The UI states unmistakably that the whole instrument is bound.**
Acknowledging highlights must never read as binding only the acknowledged
sections. This is a legal exposure, not a copy preference.
*Pass:* the bind control carries scope language naming the whole instrument;
reviewed and signed off explicitly as a requirement, not left to implementation.
`trace: P-8, W-"Highlights shrink the legal object"; exploration open question 5`
**PR-13 — Every render creates a presentation record.**
Carrying memo id, memo version, locale, UI release, timestamp, principal, and
the computed `view_hash` and `awareness_hash`.
*Pass:* rendering a memo twice creates two presentation records; a disposition
references exactly one.
`trace: DoD-3, DoD-5, guard G_PRES`
**PR-14 — A disposition binds to a presentation of the same memo version.**
A bind attempt against a stale version is refused, not silently upgraded.
*Pass:* incrementing the memo version invalidates outstanding presentations; a
bind against the old presentation returns a version conflict.
`trace: guard G_PRES; INTENT invariant "Binding acts reference a presentation of
the same version"`
### 3.3 The acknowledgment gate
**PR-20 — Required highlights must be acknowledged before a binding verb.**
Where `highlights[].required_ack` is true, `accept`, `decline` on a bind step,
and `acknowledge` are unavailable until every required highlight id is in the
presentation's acked set.
*Pass:* a bind attempt with an unacknowledged required highlight fails closed,
creates no disposition, and returns an error naming the outstanding ids.
`trace: DoD-4, guard G_ACK`
**PR-21 — Acknowledgment is an explicit act, never inferred.**
Scroll position, time on page, focus events and viewport intersection MUST NOT
acknowledge a highlight.
*Pass:* no acknowledgment is recorded by any event other than a deliberate
control activation.
`trace: P-12, INTENT "Informedness without surveillance"`
### 3.4 Disposition vocabulary
**PR-30 — The surface implements the full verb set for the step kind, not
approve/reject.**
Overlay verbs `comment` and `discuss` are available whenever the memo status
allows. `return`, `forward` and `escalate` are available to the assignee on
every step kind. `accept` and `decline` follow the legality table.
*Pass:* every verb in `state-transitions.md` §B2 legal for the step kind is
reachable in the UI for that step.
`trace: DoD-6, INTENT §"The Disposition Vocabulary"`
**PR-31 — `accept` is not offered on a weak step.**
On `inform`, `comment`, `review` and `acknowledge` steps, `accept` is absent —
not present-and-disabled. Kenntnisnahme is not approval.
*Pass:* the control does not exist in the rendered UI for weak steps; an API
call attempting it is refused.
`trace: state-transitions §B2 ("accept: —" on weak); INTENT design principle 6`
**PR-32 — `return` carries at least one coded reason.**
Free text alone is not a return. Return is a first-class success path and must
be as easy to reach as accept.
*Pass:* a return with zero coded reasons is refused (guard `G_REASONS`); the
return control has equal visual weight to accept.
`trace: DoD-6, P-4, guard G_REASONS`
**PR-33 — `return` and `decline` are distinguishable in the record.**
A memo sent back for improvement and a memo refused are different outcomes and
must not collapse to one status.
*Pass:* `returned` and `completed/declined` are distinct in the stored record
and in the export.
`trace: DoD-6, GOAL definition of done 6`
**PR-34 — `discuss` keeps the dialogue on the object.**
A question to the requester creates a thread entry on the memo. The surface
provides no "email the requester" affordance.
*Pass:* no outbound side-channel exists; the thread is part of the exported
evidence.
`trace: P-5`
**PR-35 — `escalate` requires a strictly higher mandate target.**
Where the target's mandate is not strictly higher for this act, the verb is
`forward`, not `escalate`.
*Pass:* guard `G_ESC` is evaluated; a same-level target is refused as an
escalation.
`trace: guard G_ESC`
*Known Stage 1 limitation:* without the mandate graph, `G_ESC` cannot be fully
evaluated. Stage 1 MUST record the distinction the approver asserted rather than
silently treating escalate as forward. See §6.
### 3.5 The binding / awareness split
**PR-40 — Awareness fields never enter `view_hash`.**
Proposed role, other available roles, other tenants, and last-session summary
are rendered on the same surface, hashed into `awareness_hash`, and excluded
from the signed binding document.
*Pass:* isolation vector 2 — editing an awareness field does not change
`view_hash` — is green in CI.
`trace: DoD-4, P-11, W-"Awareness leaks into the signature", EvidenceModel`
**PR-41 — Awareness is visually distinguished from binding.**
The approver can tell, without reading documentation, which part of the surface
they are committing to and which part is orientation.
*Pass:* the two regions are separately labelled; the labelling is part of the
reviewed design, not incidental styling.
`trace: P-11`
**PR-42 — Promotion is explicit and versions the memo.**
Moving a field from awareness into binding creates a new memo version and
invalidates outstanding presentations.
*Pass:* guard `G_PROMOTE`; a presentation taken before promotion cannot be bound
against after it.
`trace: guard G_PROMOTE, INTENT invariant on `awareness_promoted``
### 3.6 Evidence
**PR-50 — An evidence bundle exports and verifies offline.**
Containing the memo, its versions, the presentations with both hashes, the
dispositions, the thread, and the hash chain.
*Pass:* an exported bundle verifies with no network access; verification detects
a mutated field.
`trace: DoD-7, P-7`
**PR-51 — The export is the evidence, not a screenshot.**
No requirement is satisfied by a rendered image of the surface.
*Pass:* the bundle is structured data; the canonicalizer reproduces `view_hash`
from it.
`trace: P-7`
**PR-52 — The surface states the bound of its own guarantee.**
Documentation and the export state that attestation covers accident and later
tampering, not a compromised surface presenting X and attesting Y.
*Pass:* the residual appears in `EvidenceModel.md` and in the bundle metadata.
`trace: gate-house decision request §3; approval-engine's equivalent residual`
### 3.7 Locale and accessibility
**PR-60 — German and English, with German as a first-class source language.**
The *Umlaufmappe* vocabulary — Kenntnisnahme, Mitzeichnung, Schlusszeichnung,
Rücksprache, Rückgabe — is the domain vocabulary, not a translation of English
product terms.
*Pass:* both locales render every verb; locale is part of the presentation
record and therefore of `view_hash`.
`trace: DoD-6, INTENT §"The Disposition Vocabulary"; exploration open question 7`
**PR-61 — Locale changes the memo version's presentation, not its meaning.**
Because locale enters `view_hash`, a memo read in German and one read in English
produce different presentations of the same binding. This is intended and must
be documented, not smoothed over.
*Pass:* the two presentations differ in `view_hash` and reference the same memo
version.
`trace: canonicalize.md allow-list`
**PR-62 — Keyboard-operable and screen-reader-navigable throughout.**
An approver using assistive technology can read the brief, open packet items,
acknowledge highlights and bind, without a mouse.
*Pass:* every disposition control and every acknowledgment control is reachable
and operable by keyboard; the acknowledgment state is programmatically
determinable.
`trace: P-8; PR-21 (acknowledgment must remain explicit under AT too)`
---
## 4. Anti-requirements
These are requirements *not* to build things a reasonable implementer would
otherwise add. Each is testable as an absence.
**PR-70 — No dwell timers.** Minimum reading time, "you must spend 30 seconds on
this page", and countdown-gated buttons are forbidden.
**PR-71 — No keystroke, mouse-movement or attention analytics.** Informedness is
evidenced by the hash of the presented view plus explicit acknowledgment, never
by behavioural telemetry.
**PR-72 — No dark patterns.** Accept is not visually privileged over return.
Decline is not hidden behind a secondary menu. No pre-checked acknowledgments.
No confirm-shaming copy.
**PR-73 — No auto-approval, no delegation-to-agent, no "approve all".** Bulk
selection may exist for reading; it must not exist for binding.
**PR-74 — No approval-state caching.** The surface never stores or serves whether
an approval is valid. It asks `approval-engine`.
`trace: W-"It reimplements approval-engine", GOAL invariant`
**PR-75 — No authorization endpoint.** Nothing in this repository answers "may
this actor do X".
`trace: DoD-8, W-"It starts deciding"`
*Pass for §4:* each is a review checklist item and, where mechanically checkable,
a test asserting the absence.
---
## 5. Non-requirements for Stage 1
Not deficiencies — deliberate exclusions carried from `GOAL.md`:
- Memo authoring. Memos arrive from `approval-engine`.
- The mandate graph.
- L4/L5, QES, QTSP, qualified archival retention.
- Multi-party parallel Mitzeichnung.
- EUDI Wallet / eID identity import.
- Notification transport. A memo may be announced elsewhere; the dialogue stays
on the object.
- Mobile-native clients. Responsive web only.
- Offline operation.
---
## 6. Known limitations, stated up front
**L-01 — `escalate` without a mandate graph is forwarding.** Guard `G_ESC`
requires knowing that the target's mandate is strictly higher. Stage 1 has no
such map. The surface records the approver's asserted intent to escalate and
marks it unverified rather than either blocking the verb or pretending the guard
passed. `GOAL.md` records this as accepted; `EvidenceModel.md` must state that
an escalation in a Stage 1 bundle is an assertion, not a verified claim.
**L-02 — `view_hash` is computed by the renderer.** A compromised surface can
present one thing and attest another. Not closed, not closable at this layer,
and not claimed to be closed.
**L-03 — Binding level is organizational only.** Stage 1 produces no AES or QES
signature. `binding_level` is carried in the object and in `view_hash` so that
higher levels are expressible without a schema change, but nothing above
organizational is implemented or claimed.
**L-04 — `view_hash` versus `approval-engine`'s binding digest is unresolved.**
Filed as `INFD-IN-0001` R3. Until ruled, the surface computes `view_hash` and
does not assert any relationship to the approval claim's digest.
---
## 7. Open questions for review
1. Does the approver need to see *other* pending memos to prioritise, and does
that reintroduce the approvals-inbox shape this repository is supposed to
avoid? Proposed answer: a list is permitted; a list that is the product is
not.
2. What does the surface do when `approval-engine` is reachable but
`audit-core` is not? Fail closed on binding, or bind and queue? The
`ArchitectureBlueprint` must answer this per-dependency (T05).
3. Who is the requester for an `approval-engine`-originated memo, given the
engine does not model one? Likely the approval's `principal`, but this needs
confirming against the approval object.
4. Is `abstain` in Stage 1? It is in the state tables and it is real in
Mitzeichnung practice, but it has no obvious `approval-engine` counterpart.

View file

@ -0,0 +1,300 @@
# 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`.
**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.
**Required behaviour:** refused at the API, not merely hidden in the UI.
**Invariant protected:** humans bind, agents draft.
`guard G_NOAGENT · P-10 · W-"An agent binds"`
### 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.