# Persistent boards, gated exhibits, and citation codes Status: **proposed** (design locked, not implemented). Extends the story flow graph ([story-graph.md](story-graph.md)) and the narrative layer ([narrative-todo.md](narrative-todo.md)); interlocks with the Claim/Case Report work in [TODO.md](TODO.md) Milestone 5. Depends on the **flags / case-state** primitive (a per-playthrough key→value store) that the gates and dialogue effects also read and write. ## Why A mystery is broken into short pieces so we can **meter how much evidence the player carries at once**. Interruptions — a phone call, a knock, a revealed note — should not throw the player off the board they are working on. So a single investigation scene is a **persistent, player-mutated board** that several graph nodes return to, and new evidence **arrives into that same board** when the story warrants it rather than being dumped up front or split across duplicated templates. Two capabilities, independent and both wanted: 1. **A board that survives across nodes** — `[board] → [phone call] → [same board]` returns the player to their exact arrangement and connections. 2. **Flag-gated exhibits inside that board**, revealed with a diegetic "it arrived" moment (reuses the existing document locator beam). ## Board identity and reuse An authored level node **cannot** reference a clone id — the clone is created per playthrough at runtime. Instead: - A **level node** references a `level_template_version_id` **plus a `board_key`** (a logical board slot, e.g. `"harbor-desk"`). `board_key` defaults to the template if the author doesn't care about sharing. - A **playthrough** holds a map `board_key → level_id` (the mutable clone). - Entering a level node: - if the playthrough already has a clone for that `board_key` → **reuse it** (the player's mutated board, arrangement and connections intact); - otherwise → **clone the template** and record `board_key → new level_id`. `board_key` is the thing that makes two nodes share a board. Two nodes on the same template with **different** keys get **separate** boards. ## Gated exhibits - Every exhibit is **cloned in**, including gated ones; a gated exhibit carries a **`reveal` condition over flags**. The player's saved board already contains the note — it is simply inert until the flag flips. - **Visibility is computed server-side at load.** Given the playthrough's flags, the play-mode payload includes only exhibits whose `reveal` condition is met; hidden exhibits are stripped from the response (same discipline that already hides authoring-only fields like `expectedPartyKind`). Players cannot peek unrevealed evidence in the API. - **Gate the edges too.** Any `exhibit_connection` or event link whose endpoint is a not-yet-revealed exhibit is hidden until that exhibit appears, so there are no dangling references. ## The arrival reveal - The board's effective content can change **while the player is standing on it** — the phone is an always-available surface, so a call can set a flag mid-scene. So reveal is **not load-only**: after any phone/dialogue interaction that sets flags, re-evaluate board visibility and reveal live (the phone overlay refetches visibility on hang-up). - Track a per-playthrough **`revealed-seen` set**. On (re)load or after an interaction, any exhibit now visible but not yet in the set → play the arrival animation (a sibling/reuse of the **document locator beam**), then add it to the set. The flourish fires **once**, not on every reload. ## Reset and New Game - **Reset = tidy the desk back to the delivered state.** Authored exhibits return to their arrival positions and folders; the player's rearrangement is undone. **Flags are untouched**, so anything a flag has already revealed is still "arrived" and is restored at its authored position. - **New Game** re-clones the whole tree and is therefore the only thing that **resets flags** and returns gated exhibits to hidden. **Open decision — player notes on reset.** Milestone 5's rule is "reset discards player-created claims." Current lean: reset also removes player-created exhibits (it restores the *delivered* state), and New Game is the only wipe of flags. The alternative is a gentler reset that re-files arrived exhibits but leaves the player's own notes in place. Settle before implementing. ## Exhibit citation codes (A / B / M) A single running number breaks when players create many note exhibits, so codes live in fixed namespaces: - **Authored exhibits: group letter + index** — `A1, A2, …` for the initial dossier, `B1, B2, …` for a batch that arrives later via a flag reveal, etc. **The letter is the arrival group**, which ties straight into gated reveal: "new evidence arrived" *is* the B-series lighting up. Indices are assigned **within each group at freeze time** and frozen in the template, so authored codes never shift, no matter what the player does. - **Player-created exhibits: a running `M` (miscellaneous) series** — `M1, M2, M3…` in creation order, in their own namespace so player note-making cannot disturb authored codes. M-numbering is per-level-instance and (given the reset lean above) restarts only when player notes are cleared. - The author chooses an exhibit's **group**; the system owns the **numbering**. These codes are the **citation token everywhere** — board, luggage-tag Claims, the Case Report, and the LLM gate. ## LLM gate contract - A gate can be **scripted to require specific authored codes** — e.g. "the report must cite `A1` and `B2`, connected." That authored requirement list is the deterministic backbone *under* the LLM, per the section-9 intent of scripting the cognitive shim rather than trusting it blind. - If the player types the full report freehand, they must include the Exhibit codes; the gate checks the required codes are present (and, later, that the reasoning holds). ## Relationship to fresh templates This **complements**, not replaces, new templates: - **Shared `board_key`** — interruptions *within* one investigation scene (calls, a knock, a revealed note). Cleanly subsumes the earlier "note appears on the next level" idea: the note is a gated exhibit on the *same* board that reveals after the call. - **A fresh template** — the player genuinely moves to a new location/chapter with a different board. ## Implemented vs deferred Nothing here is built yet. New plumbing, smallest-first: 1. **Flags** — the per-playthrough key→value store (shared with gates/dialogue). 2. **`board_key` reuse** in level traversal + the playthrough `board_key → level_id` map (clone-or-reuse). 3. **`reveal` conditions on exhibits** + server-side play-mode visibility filtering (exhibits and their edges). 4. **`revealed-seen` set** + the arrival animation (locator-beam sibling), live after flag changes. 5. **A / B / M citation codes** — authored group + frozen index, player M-series; surfaced on the board and threaded into Claims/report/LLM gate. Reset (desk-restore, flags-persist) and New Game (full re-clone) semantics as above.