Files

7.3 KiB

Persistent boards, gated exhibits, and citation codes

Status: demo subset implemented. Migration 025 implements level-local boolean flags, normalized Document requirements, server-side filtering, persisted reveal acknowledgements, and the arrival animation. Board-key reuse, narrative-triggered live reveals, citation groups, and per-user playthrough overlays remain deferred. The broader design extends the story flow graph (story-graph.md) and the narrative layer (narrative-todo.md); interlocks with the Claim/Case Report work in 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_keyreuse 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 + indexA1, 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) seriesM1, 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.