diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 33ecff6..d203bda 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -17,3 +17,4 @@ | task | INFD-WP-0001-T06 | todo | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md | | task | INFD-WP-0001-T07 | todo | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md | | task | INFD-WP-0001-T08 | todo | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md | +| intake | INFD-IN-0001 | open | blue | intakes/intakes.md | diff --git a/docs/specs/ProductRequirementsDocument.md b/docs/specs/ProductRequirementsDocument.md new file mode 100644 index 0000000..9a10bd7 --- /dev/null +++ b/docs/specs/ProductRequirementsDocument.md @@ -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. diff --git a/docs/specs/UseCaseCatalog.md b/docs/specs/UseCaseCatalog.md new file mode 100644 index 0000000..fef4e60 --- /dev/null +++ b/docs/specs/UseCaseCatalog.md @@ -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. diff --git a/intakes/intakes.md b/intakes/intakes.md index 2073e80..011c679 100644 --- a/intakes/intakes.md +++ b/intakes/intakes.md @@ -36,4 +36,5 @@ description: >- binding digest authoritative for replay and view_hash authoritative only for what was shown. Full request: docs/gate-house-decision-request-layer-placement.md. Blocks INFD-WP-0001 T05 and T07; T03, T04 and T06 proceed regardless. +state_hub_intake_id: "01a08610-f458-7fd5-b284-26a65d1d73c2" ``` diff --git a/workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md b/workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md index 7c46f6f..1ce2a74 100644 --- a/workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md +++ b/workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md @@ -80,7 +80,7 @@ Two corrections made during the same task, recorded rather than silently fixed: ```task id: INFD-WP-0001-T02 -status: todo +status: progress priority: high state_hub_task_id: "4f94134b-2260-5404-84e0-12f2b08ef565" ``` @@ -118,7 +118,7 @@ recorded as a decision, not left implicit. Blocking for T05 and T07. ```task id: INFD-WP-0001-T03 -status: todo +status: done priority: high state_hub_task_id: "86465a35-1af5-5956-a767-57ad838dffa9" ``` @@ -141,11 +141,23 @@ Acceptance: every requirement traces to either a `GOAL.md` definition-of-done item or a named external contract; each requirement is testable; the document names what it is deliberately not requiring and why. +Completed 2026-09-09: `docs/specs/ProductRequirementsDocument.md`. 30 numbered +requirements, each with a `trace:` line to a `GOAL.md` DoD item, an `INTENT.md` +principle or wrongness condition, a state-transition guard, or a named external +contract, and each with an observable pass condition. Anti-requirements +(PR-70..75) 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 known limitations are recorded up front rather than +discovered later — chiefly that `escalate` without a mandate graph is forwarding +(L-01) and that `view_hash` is computed by the renderer, so a compromised +surface can present X and attest Y (L-02). Four open questions are left for +review rather than answered by assumption. + ## Use Case Catalog ```task id: INFD-WP-0001-T04 -status: todo +status: done priority: medium state_hub_task_id: "00a83db5-fe0e-522a-b885-6f9ef034bc07" ``` @@ -168,6 +180,14 @@ Acceptance: every use case maps onto the single Decision Memo schema with no level-specific object; each negative case names the invariant it protects; the catalog states for each level which estate repository would consume it. +Completed 2026-09-09: `docs/specs/UseCaseCatalog.md`. Six use cases L0-L5 with +counterparty and stage, ten negative cases each bound to a guard or isolation +vector, and a closing section naming the four changes that would fork the +object — a level-specific status, multiple questions per memo, a per-level +packet model, and an approvals-inbox entity that acquires state. Each case +records what it contributes as a *constraint* on the shared schema, so the +scale-invariance claim is checkable rather than asserted. + ## Architecture Blueprint ```task