140 lines
7.0 KiB
Markdown
140 lines
7.0 KiB
Markdown
# 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.
|