# 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.