From 9f990dc1cffd53c9a0fdee853c7c10db95e09f66 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Fri, 7 Aug 2026 08:19:44 -0700 Subject: [PATCH 1/4] add phase 16 implementation plan (pre-review draft) Claude-Session: https://claude.ai/code/session_01GeUYYGfPQYGPRuk8C5Ju88 --- docs/phase-16-plan.md | 107 ++++++++++++++++++++++++++++++++++++++++++ docs/spec.md | 2 + 2 files changed, 109 insertions(+) create mode 100644 docs/phase-16-plan.md diff --git a/docs/phase-16-plan.md b/docs/phase-16-plan.md new file mode 100644 index 0000000..4f0fc0b --- /dev/null +++ b/docs/phase-16-plan.md @@ -0,0 +1,107 @@ +# Phase 16 plan — documentation refresh + +Implementation plan for phase 16 of [the osrlib spec](spec.md): the technical-writer pass over the documentation phases 10–15 updated incrementally. The charter is a five-track review of the published site, the docstrings, and the examples conducted after phase 15 landed (getting-started, the seven guides, the three front-end walkthroughs, the phase 10–15 docstring surface, and the site's information architecture), whose findings this plan triages into work items; the findings are folded in here directly rather than kept as a separate audit artifact — unlike the phase 7 audit, which chartered a 33-module overhaul, this review's output is already work-item-shaped, and a second document would duplicate the plan. The review's calibration matters as much as its findings: every phase 10–15 surface already has a learning-oriented home, every runnable docs example passes under the CI harness, the three TUI transcripts are byte-accurate, the generated references picked up every new module, command, event, and code with zero manual wiring, and the strict build is clean. What needs fixing is a handful of published sentences the later phases falsified, the three pages phase 15 never touched (the FastAPI and LLM-referee walkthroughs, the front door), an information-architecture drift — the authored layer's teaching home is a getting-started page the navigation gives no name — and a vocabulary register that makes the site harder to enter than it needs to be. The milestone: **a developer who has never seen this repository finds, learns, and correctly uses gates, triggers, and quests from the published site alone — and no published sentence contradicts the shipped library.** + +This phase's roadmap entry does not yet exist — the spec's roadmap ends at phase 15 — so this plan PR adds it, pinned verbatim: + +> **Phase 16 — documentation refresh.** The technical-writer pass over the post-phase-15 documentation: the staleness sweep, the authored layer's move from the getting-started tutorial into a guides-level home, the front-end walkthroughs brought into the interpreter era, the front-door and vocabulary pass, and the docstring linkage pass. Milestone: a developer who has never seen the repository finds, learns, and correctly uses gates, triggers, and quests from the published site alone, and no published sentence contradicts the shipped library. + +Four disciplines bound the phase: + +- **Docs adapt to code, and nothing moves but words.** The phase 7 rule verbatim: no symbol renames, no signature changes, no new public symbols, no RNG-path or serialized-name changes, no version bump. The `src/` diff for the entire phase is docstring and comment hunks only. `examples/` code may change — example sources are not the frozen surface, and three of the findings are example rendering defects — but no example change may alter a command sequence or an RNG draw: the golden files stand byte-for-byte, `SCHEMA_VERSION` stays 3 with no new migration, and any golden diff in this phase's changes is a defect by definition. +- **The one-way bridge.** The project's precise vocabulary (gates, triggers, beats, the journal, streams, draws) is load-bearing — it maps one-to-one onto API names — so the fix for the jargon wall is on-ramps, not synonyms. Common, jobs-to-be-done language *locates* a concept (navigation labels, headings, opening sentences: "you want a door that needs a key"); the project term is introduced once, job-first ("osrlib calls this a gate"), and from that sentence on the docs commit to the term. No page maintains a parallel vocabulary, and docstrings stay in full project vocabulary — they are reference, and precision wins there. The B/X domain vocabulary (referee, keyed areas, marching order) is the audience's own and stays, with at most a first-use gloss. +- **The voice gradient.** The docs' authorial register is an asset in the deep material and a cost at the front door, where the reader is also absorbing new vocabulary. The funnel top — README, index, quickstart, guide openings — is written plain, every sentence doing instruction; the register stays rich in guide interiors, walkthroughs, and reference prose, where the reader has bought in. +- **The harness discipline holds through the restructuring.** Every teaching page keeps a complete runnable program under the examples harness, and narrated fragments stay excerpts of their page's program — so the page split below produces two complete programs, not one program and a pile of orphaned fragments. + +## Scope + +In scope: + +- The falsehood and staleness sweep across the guides, getting-started, README, and the walkthroughs — every item named in work items 2–4 +- The split: the authored layer (gates, triggers, quests, narrator steering) moves from `docs/getting-started/building-an-adventure.md` into a new guide, with navigation, cross-links, and both complete programs reworked +- The front-end walkthrough refresh: the FastAPI page gains the interpreter and the served content, the LLM-referee page gains the guidance surface and the authored-narrative story, the TUI page drops the delta-loop idiom — with the small example-code changes those pages need to excerpt +- The front door and vocabulary pass: README, `docs/index.md`, quickstart hand-offs, the concepts table, the heading audit, and a documentation-conventions block in `AGENTS.md` +- The docstring linkage pass over the phase 10–15 modules — orientation staleness, missing cross-references, per-field docstrings where the reference renders bare fields +- Toolchain nits: `dedent_subsections` for snippet excerpts, and the development-history sweep over user-facing files the phase 7 tripwire does not scan (`examples/` READMEs, docs pages) + +Out of scope (deferred to the track that picks each up, or declined with rationale): + +- **Versioned documentation (mike or equivalent)** — the release skill owns the adoption trigger; nothing here changes it +- **A redirects plugin** — declined: no page URL changes in this phase (`building-an-adventure.md` keeps its path; the new guide is a new path), and the sections that move leave a page that still exists, so a stale external anchor degrades to the page top rather than a 404. Renaming `kernel-a-la-carte.md` to match its new title is declined for the same reason: the navigation label and H1 change, the URL does not +- **A published changelog page** — declined: `CHANGELOG.md` links repo paths the site excludes (`docs/spec.md`), so a snippet-included page fails the strict build's link validation; rewriting the changelog's links per release is maintenance without a demonstrated consumer. `docs/index.md` links the GitHub changelog instead (work item 5) +- **A JTBD rewrite of docstrings** — declined by the one-way-bridge discipline itself: docstrings are reference and keep full project vocabulary +- **The LLM-referee example agent** — not shipped and no longer promised; the page's roadmap sentence is reworded (work item 4), and whether an example agent is worth building is a post-phase decision on its own merits +- **New mechanical gates for prose counts or transcript accuracy** — declined: the two count-drift instances are fixed by removing the counts and the `AGENTS.md` convention forbids new ones; a transcript-diff test would couple the docs build to a TUI run for a hazard the walkthrough's re-capture procedure (work item 4) already manages +- **Engine changes of any kind** — a docs phase; where documentation wants a code shape the code doesn't have, the docs adapt + +## Work items + +### 1. The split — `docs/guides/gates-triggers-quests.md` (new), `docs/getting-started/building-an-adventure.md`, `mkdocs.yml` + +- The new guide, pinned: `docs/guides/gates-triggers-quests.md`, navigation label "Gates, triggers, and quests", placed after "Listeners and flags" — the interpreter is a listener, so the authored layer reads as the shipped application of the pattern the previous guide just taught. These three terms are common game-design vocabulary as well as project vocabulary, so the title needs no translation; the opening frame does the bridge work instead ("You want a door that needs a key, a lever that opens a portcullis across the map, an errand that ends the adventure. osrlib authors all three as data…"). +- What moves, pinned: "Gating a door or a stair", "Wiring the dungeon with triggers" (all three subsections), "Authoring a quest" (all six subsections, including "Which beat goes where" and "Steering a narrator"), and the three snippet includes those sections carry (`fetch-quest`, `bundled-idol`, `level-guidance`). What stays in building-an-adventure: the grid and its edges, keyed areas, the dungeon/town/root assembly, validation, and a complete program. The stale "Trigger-driven one-time unlocks of that shape arrive in a later release." sentence (line 81) dies in the move rather than as a separate fix — settled in review discussion — and its replacement is a live forward reference to the triggers section it now shares a page with. +- Two complete programs, pinned: building-an-adventure keeps a trimmed program — geometry, a keyed area with its encounter, the root, validation, entry, the goblin spawn beat — and the new guide takes the full gated/triggered/quested program with its journal, source-stamp, and completion assertions. Every narrated fragment on both pages is re-derived as an excerpt of its own page's program. A structural bonus, taken deliberately: the getting-started page's opening fragment currently references the `sentinel` gate defined three sections later; with gates gone from the page, its geometry authors an ungated door and the first fragment finally stands alone. +- The ending, taught by name: the new guide's "The completion rule and the ending" section names `AdventureCompletedEvent` (`session.adventure.completed`) as the event a front end watches for the victory transition — currently stated nowhere in guide prose. +- The `cost_gp=0` question, preempted: one clause where the key and idol templates appear — a bundled item's price is moot because the town shop never stocks it — cross-linking the authoring guide's `items.purchase.not_stocked` treatment. +- Cross-link rewiring, enumerated: `mkdocs.yml` navigation; quickstart's "Where next" (work item 5 rewords it anyway); building-an-adventure's "Where next" points forward to the new guide; `listeners-and-flags.md`'s interpreter section and fetch-quest hand-off links move to the new guide's anchors; `views-and-visibility.md` and `sessions-commands-events.md` gain the outbound links work item 2 specifies; `tui-crawler.md`'s quest-section links retarget. The strict build's anchor validation is the completeness check. + +### 2. The staleness sweep — the guides + +- `sessions-commands-events.md`: the victory paragraph ("Nothing in the library enters it yet — the transition arrives with the authored quest layer") is rewritten to the shipped truth — `CompleteQuest` on a quest authored `concludes_adventure=True` is the one entrance, linking the new guide's ending section — resolving its contradiction with the same page's lifecycle section. "The lifecycle commands" moves after "Session modes and mode gating", whose vocabulary (terminal modes, legal-in-every-mode) it leans on; the intro's page-contract sentence updates to announce the source stamp and lifecycle sections it now walks. The rejections section gains the gate refusal as the one rejection family carrying authored player-facing text (`exploration.door.gate_refused`, `exploration.transition.gate_refused`, the author's `refusal` beat in `params`), linking the new guide. The interpreter-stamp passage gains an inline link to where triggers and quests are authored. +- `determinism-saves-replay.md`, the review's other high-severity page: the schema paragraph corrects "`SCHEMA_VERSION` … is currently `2`" to 3 and describes both migrations (1→2, and 2→3's lossless rewrite of a treasure trap's never-read `trigger="enter"` to `"open"`). A new subsection under replay states the listener rule this page never mentions: `replay_game` builds a session with **no listeners registered**, that is sufficient because every trigger and quest effect is a logged command and the interpreter emits nothing and holds nothing — and the corollary a developer can get badly wrong: re-register listeners (the interpreter included) after `load_game` before executing *new* commands, never before replaying a log, because an interpreter reacting to re-executed events would issue duplicate commands and diverge. The save-contents list extends to what phases 11–15 added — the flag store, trigger fired-marks, the journal, quest state, listener state — so "does my authored progress survive?" is answerable from the page. The `ReplayVersionError` sentence notes the check runs only when the caller passes the save's recorded `engine_version`. +- `views-and-visibility.md`: one sentence adds the third channel authored words reach a player by — a gate's `refusal` beat riding a rejection — beside events and views, linking the new guide. "What tells a client the journal grew" is promoted out of "The stable difference" to its own section (it is client how-to, not contrast). "Where next" gains `listeners-and-flags.md` (flags' visibility is discussed inline without a link) and the new guide. +- `listeners-and-flags.md`: the fetch-quest listener — currently the guides' only code sample with no runnable twin — folds into the page's complete program with its imports, exercised minimally, so the harness covers it. The cascade sentence links the new guide's "When something doesn't land". +- `authoring-custom-content.md`: the scope statement and the "Where next" bullet reroute authored *behavior* — gates, triggers, quests — to the new guide, replacing the pre-phase-11 description of building-an-adventure. +- `ruleset-options.md`: the "Where next" sentence routing quests and triggers to listeners-and-flags is rewritten to the post-phase-15 model — session flags and game-owned listeners there, authored triggers and quests in the new guide. + +### 3. The front-end walkthroughs — `docs/front-ends/`, `examples/` + +- The TUI example's rendering changes, pinned as the full list of `examples/` code changes this phase makes: `_run` iterates `result.events` instead of diffing the event log — the delta idiom's stated rationale is false, since the phase 14 `CommandResult` fold already returns everything nested listener-issued commands logged — and prints a rejection's authored `refusal` text when its params carry one, so phase 11's beats stop being silently dropped by the renderer everyone copies; `_status` lists active quests with objective states (rendering `PlayerView.quests`); a `journal` verb prints the journal with clock stamps (rendering `PlayerView.journal`). `examples/fastapi_crawler/content.py` gains snippet markers around `new_session` and `restore_session` (marker comments only — no behavior change). None of these changes touches a command sequence or a draw: `milestone.txt` is unchanged, the goldens capture state and logs rather than stdout, and `tests/test_example_crawler.py` assertions move only if they pin output shapes the rendering changes altered. +- Transcript re-capture, pinned as procedure: the walkthrough's quoted transcripts are re-captured from the real `--seed 21 --script milestone.txt` run after the rendering changes, never hand-edited — the same discipline that made the current transcripts byte-accurate. +- `tui-crawler.md`: the delta-loop prose (lines 29–33, 57–59) rewrites to the true contract — the result envelope carries the interpreter's reactions, which is why the loop is a plain iteration; the intro's "even a whole quest — is ordinary application code" reframes to the authored-data story its own quest section tells; "a quest listener's reactions" becomes the interpreter; the player-view section teaches `quests` and `journal` with the new excerpts; the source-stamp sentence shows the literal form and notes triggers stamp too; the shrine-excerpt framing stops promising an encounter the excerpt doesn't contain; the reward-economics sentence unpacks "the last award has already fired" so pay-in-coin isn't misread as pay-nothing; and a short passage teaches `SessionMode.terminal` as the loop condition a front end checks — with an honest note that the TUI deliberately keeps its loop open after victory so the closing `status` can be read, and a link to the LLM page's loop sketch where the terminal check is shown. +- `fastapi-pattern.md`, untouched since phase 7 and the review's biggest walkthrough gap: a new section on the served content teaches what the page never says — the server registers `Interpreter` when it creates a session *and again when it restores one*, excerpting the two `content.py` functions; the restore paragraph names the interpreter as the listener it re-registers, making the page's own lesson (listeners are code, saves are data) concrete. The wire-view section adds `journal` and `quests` to the whitelist enumeration and notes the player-visible quest events crossing in `events`, plus the `rejections[].params.refusal` field a web client should render. The `CreateSession` model and its exactly-one-of validator join the excerpts the prose already leans on. "All 44 of them" and every other numeric surface count dies in favor of "every command in the registry". +- `llm-referees.md`: a new "Narrating authored content" section delivers the surface built for this page's audience — `NarrativeBlock.guidance` and `LevelSpec.guidance` as steering text a referee-side narrator reads from the adventure document and never prints verbatim (excerpting `content.py:level-guidance`); the authored beats arriving on player-visible events with `speaker` attribution as text to weave, not paraphrase; registering the library `Interpreter` so an agent refereeing an authored adventure does no trigger or quest bookkeeping; and `Command.source` as the attribution that keeps the agent's own commands distinguishable from authored consequences in the log — this page's own eval story, completed. "Events never carry baked prose" is qualified to what it means: no *engine-baked English*, while authored narrative rides events as structured fields the formatter appends. The authorial-surface section adds the journal and note commands (an agent's durable in-world memory), the quest lifecycle commands with the sharp edge that a hand-issued `CompleteQuest` grants no rewards, and the counts die as above. The "complete example agent is on the roadmap" sentence — a promise no roadmap holds — rewords to fact: the schemas, the referee surface, and the determinism story are the pieces such an agent would consume. +- The development-history sweep: user-facing files outside the phase 7 tripwire's scan (`examples/*/README.md`, docs pages) drop their phase references — the TUI README's "It is the Phase 5 milestone" becomes plain description. The tripwire itself stays `src/`-scoped; the sweep is editorial, verified by grep in the closing pass. + +### 4. The front door and the vocabulary pass — `README.md`, `docs/index.md`, `docs/getting-started/quickstart.md`, `AGENTS.md` + +- README and `docs/index.md`, updated in sync (their positioning is deliberately twinned): one plain sentence advertises the authored surface — adventures carry their own items, gated doors, triggers, and quests; the library ships the interpreter that plays them to a victory ending — and the guide-list sentence names the new guide. The two pages' reference-surface enumerations align (today they drift). `docs/index.md` gains a link to the GitHub changelog. +- The save/load correction, both carriers: README's "Saved games replay from the seed and the command log" and quickstart's "Loading replays the command log against the same seed" are rewritten to the state-restore truth — a save restores from serialized state alone; replay is the separate `replay_game`, and the two agreeing is the determinism guarantee. The quickstart twin blocks are code, not prose, so the twin-equality gate is untouched. +- The concepts table, pinned: a short "What things are called" table on `docs/index.md` — the JTBD row set: quest log → the journal; scripted event → a trigger; locked door needing an item → a gate; the text an event shows → beats on a narrative block; what the player may see → the player view; seedable randomness → named streams and draws; save file → a stamped document; win condition → a concluding quest and `victory` — each row linking where the term is taught. Rows are added sparingly; the table is an index, not a glossary essay. +- Quickstart and index hand-offs: the "Where next" and "Where to start" bullets describing building-an-adventure predate phase 10 ("assembles a small dungeon model by model") and rewrite to name what each page now teaches, with the new guide as the authored-layer destination. +- The heading audit, pinned to its one real offender: "The kernel à la carte" becomes navigation label and H1 "Using the rules without a session", the term *kernel* introduced job-first in the opening sentence; the filename and URL stand. The remaining guide titles survive the audit — they are either API vocabulary developers hold (sessions, commands, events, listeners) or common game-design words (views, determinism, ruleset). Guide openings that lack a job-first frame gain one sentence each, per the one-way bridge; interiors are untouched. +- `AGENTS.md` gains a short documentation-conventions block, pinned to four rules: prose states no numeric surface counts (registries and generated references carry the numbers); vocabulary follows the one-way bridge (job first, term once, then commit — no parallel vocabulary); the voice gradient (front door plain, depth rich); transcripts and captured output are re-captured from real runs, never hand-edited. + +### 5. The docstring linkage pass — `src/`, docstring and comment hunks only + +- Orientation staleness: `crawl/narrative.py`'s module docstring drops "a trigger or a quest as those land" — both landed — and names the three carriers as current; `crawl/adventure.py`'s module docstring adds the authored-content surfaces (`items`, `triggers`, `quests`) and validation's grown reach, with links to the three modules. +- The missing one-way links, closed: `crawl/triggers.py`'s module docstring (and `TriggerSpec`) gains the sentence naming the `Interpreter` as the shipped listener that plays these; `crawl/quests.py`'s module docstring gains the same plus the four lifecycle commands that are quest state's only writers; `crawl/gates.py`'s module docstring links `DoorSpec` and `TransitionSpec`, the only two carriers a `GateSpec` can hang on. +- The selector rule, surfaced where authors read: `GrantItem`, `GrantCoins`, and `AwardXP` each gain one sentence — in an authored consequence or reward, `character_id` takes the party selectors (`@party`, `@first`), expanded by the interpreter before issue; issued directly, it must be a literal member id or the command rejects. +- Field docstrings where the reference renders bare: `NarrativeBlock`'s nine fields (the pure authoring surface — the field list is where an author looks up which beat is which); `DoorEvent.narrative` (matching its already-documented twin on `LocationEnteredEvent`); `QuestView.id`/`.name` and `ObjectiveView.id` for consistency with their sibling fields. +- Envelope completeness: `CommandResult`'s docstring states the phase 14 fold — `events` carries everything nested listener-issued commands logged, in log order — where today only `GameSession.execute` says so. +- Housekeeping while there: the `_record_deaths` link to the filtered private member `_end_on_party_wipe` becomes plain prose (no anchor exists for it). + +### 6. Toolchain — `mkdocs.yml` + +- `pymdownx.snippets` gains `dedent_subsections: true`, so sections excerpted from inside functions stop rendering at source indentation; the affected excerpts (`parse-command`, `idol-shrine-area`, `level-guidance`, the new `content.py` markers) are visually verified in the built site. + +## Sequencing + +1. Work item 1 with work item 6 — the split settles the page structure and anchors everything later edits link to, and the snippet dedent lands before the pages that re-excerpt are proofread. +2. Work item 2 — the guides sweep, against settled structure. +3. Work item 3 — the example code changes, the transcript re-capture, and the three walkthrough refreshes together, so prose and excerpts move in one motion. +4. Work item 4 — the front door last among the content passes, so its links and concepts table point at final anchors. +5. Work item 5 — the docstring pass, independent of page structure, anytime after 1; by convention it lands here. +6. The closing sweep: grep-verified zero numeric surface counts and zero phase references in user-facing files, the full gate on both OSes, and the blind-task list handed to the implementation rubber-duck. + +## Definition of done + +- `uv sync && uv run ruff format --check && uv run ruff check && uv run pyright && uv run pytest && uv run mkdocs build --strict` green on both OSes; the golden files byte-identical and unregenerated; `SCHEMA_VERSION` untouched at 3 with no new migration; the package version untouched; the entire `src/` diff docstring and comment hunks only; the `examples/` diff limited to the changes work item 3 names, with every command sequence and draw unchanged. +- The six falsified claims are gone and stated correctly: the victory entrance, the trigger-unlock "later release" sentence, the schema version, the save/load mechanism (both carriers), the delta-loop rationale, and the two surface counts — and a grep for numeric surface counts in prose returns nothing. +- The new guide is live in the navigation with its complete program under the harness; building-an-adventure stands trimmed with its own program, its first fragment self-contained; every cross-link the split rewires resolves under the strict build. +- The FastAPI page teaches interpreter registration on both paths from real excerpts; the LLM page teaches the guidance surface, the beat-weaving rule, and the completed authorial surface; the TUI page renders quests, the journal, and refusal text through the updated example, its transcripts re-captured byte-accurate. +- The front door advertises the authored layer, the concepts table is live, the kernel guide carries its job-first title, and `AGENTS.md` carries the four documentation conventions. +- The docstring pass holds under the strict build's cross-reference validation: the two stale module orientations rewritten, the one-way links closed in `triggers`/`quests`/`gates`, the selector sentence on the three grant commands, and the named field docstrings rendering in the reference. +- `CHANGELOG.md` `[Unreleased]` gains Changed bullets for the example rendering changes (result-events loop, refusal text, quest status, the `journal` verb) — the docs-only changes carry no bullets. +- Acceptance, pinned: the implementation-phase rubber-duck includes a fresh-eyes, docs-only walkthrough of four blind tasks — author a key-gated door, a trigger, and a concluding quest from the published site alone; build a minimal front end that renders quests, the journal, and a gate's refusal from the walkthroughs alone; answer "what survives a save, and why does replay run with no listeners?" from the determinism guide alone; and, starting from the home page, reach quest authoring in at most two clicks. All four must succeed without reading library source. +- Every declined item in this plan names its rationale, and `docs/adaptations.md` gains no entries — a docs phase pins engineering, not rules. diff --git a/docs/spec.md b/docs/spec.md index 7a9cfb9..4a906e1 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -389,3 +389,5 @@ Phases 10–15 deliver authored gates, triggers, and quests (see that section). **Phase 14 — triggers and the interpreter.** The trigger spec, authored triggers on the adventure document, and the library-shipped interpreter listener: document-order matching, once-only fired-state, the depth-4 cascade bound with truncation notes, and dropped-consequence recording; `validate_adventure` grows trigger reference checks. Milestone: the lever-opens-portcullis scenario authored as data replays identically with no listeners, and a trigger spawn colliding with an open encounter drops and records its note. **Phase 15 — quests, victory, and the authored example.** The quest spec, the quest lifecycle commands with their state and player-visible events, adventure completion into victory, active quests in the player view, the per-level ambient guidance slot, and quest/objective reference validation; the example TUI crawler registers the interpreter, authors its fetch quest as adventure data, and deletes its hand-rolled listener. Closing deliverable: file the osr-forge issue recording that its overrides schema has no quest surface — quest authoring stays native-project-only until osr-forge grows one in its own repo against this settled schema. Milestone: the crawler's fetch quest — activation, objective completion matched on the idol's catalog id, reward, journal beats, victory — runs end to end as authored data and replays clean. + +**Phase 16 — documentation refresh.** The technical-writer pass over the post-phase-15 documentation: the staleness sweep, the authored layer's move from the getting-started tutorial into a guides-level home, the front-end walkthroughs brought into the interpreter era, the front-door and vocabulary pass, and the docstring linkage pass. Milestone: a developer who has never seen the repository finds, learns, and correctly uses gates, triggers, and quests from the published site alone, and no published sentence contradicts the shipped library. From e4c8a0bf51d55bc679508ec644c62e5384193df5 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Fri, 7 Aug 2026 08:39:38 -0700 Subject: [PATCH 2/4] revise phase 16 plan per rubber-duck review Both blocking findings addressed: the pinned examples/ change list gains the app.py CreateSession marker it omitted, and the replay-listener rule is scoped to observe-only listeners with a pinned sentence-level spec amendment (the spec's blanket re-registration claim is false for command-issuing listeners). Non-blocking: refusal-print format pinned to keep the (refused: tripwire literal, the journal verb gains milestone script and test coverage, the root section's authored-layer prose gets a pinned destination, the interpreter one-liner fragment gains its runnable twin, plus the cross-reference, count, and attribution corrections. Claude-Session: https://claude.ai/code/session_01GeUYYGfPQYGPRuk8C5Ju88 --- docs/phase-16-plan.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/phase-16-plan.md b/docs/phase-16-plan.md index 4f0fc0b..565d2ee 100644 --- a/docs/phase-16-plan.md +++ b/docs/phase-16-plan.md @@ -39,24 +39,24 @@ Out of scope (deferred to the track that picks each up, or declined with rationa ### 1. The split — `docs/guides/gates-triggers-quests.md` (new), `docs/getting-started/building-an-adventure.md`, `mkdocs.yml` - The new guide, pinned: `docs/guides/gates-triggers-quests.md`, navigation label "Gates, triggers, and quests", placed after "Listeners and flags" — the interpreter is a listener, so the authored layer reads as the shipped application of the pattern the previous guide just taught. These three terms are common game-design vocabulary as well as project vocabulary, so the title needs no translation; the opening frame does the bridge work instead ("You want a door that needs a key, a lever that opens a portcullis across the map, an errand that ends the adventure. osrlib authors all three as data…"). -- What moves, pinned: "Gating a door or a stair", "Wiring the dungeon with triggers" (all three subsections), "Authoring a quest" (all six subsections, including "Which beat goes where" and "Steering a narrator"), and the three snippet includes those sections carry (`fetch-quest`, `bundled-idol`, `level-guidance`). What stays in building-an-adventure: the grid and its edges, keyed areas, the dungeon/town/root assembly, validation, and a complete program. The stale "Trigger-driven one-time unlocks of that shape arrive in a later release." sentence (line 81) dies in the move rather than as a separate fix — settled in review discussion — and its replacement is a live forward reference to the triggers section it now shares a page with. +- What moves, pinned: "Gating a door or a stair", "Wiring the dungeon with triggers" (all three subsections), "Authoring a quest" (all seven subsections, including "Which beat goes where" and "Steering a narrator"), and the three snippet includes those sections carry (`fetch-quest`, `bundled-idol`, `level-guidance`). The root section's authored-layer prose moves too: the `items`/`triggers`/`quests` field descriptions and the ordering rules they carry — both tuples are document order, and the interpreter walks an event's triggers before its quests — land in the new guide's trigger and quest sections, while the root section keeps a one-sentence forward pointer to the guide. What stays in building-an-adventure: the grid and its edges, keyed areas, the dungeon/town/root assembly (its root authoring none of the three moved fields), validation, and a complete program. The stale "Trigger-driven one-time unlocks of that shape arrive in a later release." sentence (line 81) dies in the move rather than as a separate fix — settled in review discussion — and its replacement is a live forward reference to the triggers section it now shares a page with. - Two complete programs, pinned: building-an-adventure keeps a trimmed program — geometry, a keyed area with its encounter, the root, validation, entry, the goblin spawn beat — and the new guide takes the full gated/triggered/quested program with its journal, source-stamp, and completion assertions. Every narrated fragment on both pages is re-derived as an excerpt of its own page's program. A structural bonus, taken deliberately: the getting-started page's opening fragment currently references the `sentinel` gate defined three sections later; with gates gone from the page, its geometry authors an ungated door and the first fragment finally stands alone. - The ending, taught by name: the new guide's "The completion rule and the ending" section names `AdventureCompletedEvent` (`session.adventure.completed`) as the event a front end watches for the victory transition — currently stated nowhere in guide prose. - The `cost_gp=0` question, preempted: one clause where the key and idol templates appear — a bundled item's price is moot because the town shop never stocks it — cross-linking the authoring guide's `items.purchase.not_stocked` treatment. -- Cross-link rewiring, enumerated: `mkdocs.yml` navigation; quickstart's "Where next" (work item 5 rewords it anyway); building-an-adventure's "Where next" points forward to the new guide; `listeners-and-flags.md`'s interpreter section and fetch-quest hand-off links move to the new guide's anchors; `views-and-visibility.md` and `sessions-commands-events.md` gain the outbound links work item 2 specifies; `tui-crawler.md`'s quest-section links retarget. The strict build's anchor validation is the completeness check. +- Cross-link rewiring, enumerated: `mkdocs.yml` navigation; quickstart's "Where next" (work item 4 rewords it anyway); building-an-adventure's "Where next" points forward to the new guide; `listeners-and-flags.md`'s interpreter section and fetch-quest hand-off links move to the new guide's anchors; `views-and-visibility.md` and `sessions-commands-events.md` gain the outbound links work item 2 specifies; `tui-crawler.md`'s quest-section links retarget. The strict build's anchor validation is the completeness check. ### 2. The staleness sweep — the guides - `sessions-commands-events.md`: the victory paragraph ("Nothing in the library enters it yet — the transition arrives with the authored quest layer") is rewritten to the shipped truth — `CompleteQuest` on a quest authored `concludes_adventure=True` is the one entrance, linking the new guide's ending section — resolving its contradiction with the same page's lifecycle section. "The lifecycle commands" moves after "Session modes and mode gating", whose vocabulary (terminal modes, legal-in-every-mode) it leans on; the intro's page-contract sentence updates to announce the source stamp and lifecycle sections it now walks. The rejections section gains the gate refusal as the one rejection family carrying authored player-facing text (`exploration.door.gate_refused`, `exploration.transition.gate_refused`, the author's `refusal` beat in `params`), linking the new guide. The interpreter-stamp passage gains an inline link to where triggers and quests are authored. -- `determinism-saves-replay.md`, the review's other high-severity page: the schema paragraph corrects "`SCHEMA_VERSION` … is currently `2`" to 3 and describes both migrations (1→2, and 2→3's lossless rewrite of a treasure trap's never-read `trigger="enter"` to `"open"`). A new subsection under replay states the listener rule this page never mentions: `replay_game` builds a session with **no listeners registered**, that is sufficient because every trigger and quest effect is a logged command and the interpreter emits nothing and holds nothing — and the corollary a developer can get badly wrong: re-register listeners (the interpreter included) after `load_game` before executing *new* commands, never before replaying a log, because an interpreter reacting to re-executed events would issue duplicate commands and diverge. The save-contents list extends to what phases 11–15 added — the flag store, trigger fired-marks, the journal, quest state, listener state — so "does my authored progress survive?" is answerable from the page. The `ReplayVersionError` sentence notes the check runs only when the caller passes the save's recorded `engine_version`. +- `determinism-saves-replay.md`, the review's other high-severity page: the schema paragraph corrects "`SCHEMA_VERSION` … is currently `2`" to 3 and describes both migrations (1→2, and 2→3's lossless rewrite of a treasure trap's never-read `trigger="enter"` to `"open"`). A new subsection under replay states the listener rule this page never mentions: `replay_game` builds a session with **no listeners registered**, and that is sufficient because every reaction a listener issued live was an ordinary logged command — re-executing the log rebuilds its every effect. The corollary, scoped precisely: after `load_game`, re-register listeners before executing *new* commands; during a replay, a listener that only observes (accumulating `listener_state`, emitting annotations) may be registered and reproduces its state, but a listener that reacts by *issuing commands* — the interpreter above all — must not be, because the log already carries its reactions and a second issuer would duplicate them and diverge. The spec's own replay bullet currently makes the blanket claim ("Replays reproduce engine state exactly with or without listeners … Re-registering the same listeners during a replay reproduces their state too") that is true only of the observe-only case and false for the interpreter, in tension with the spec's authored-layer section ("a replay, which runs with no listeners"); this phase therefore pins a sentence-level spec amendment scoping that bullet to observe-only listeners and stating the command-issuing exclusion — the phase 12/15 altitude test met exactly, a developer would mispredict from the current text — applied with the implementation PR per the phase 15 precedent. The save-contents list extends to the session-state blocks it omits — the flag store and listener state (the phase 5 extension surface, never listed here) and the trigger fired-marks, journal, and quest state phases 13–15 added — so "does my authored progress survive?" is answerable from the page. The `ReplayVersionError` sentence notes the check runs only when the caller passes the save's recorded `engine_version`. - `views-and-visibility.md`: one sentence adds the third channel authored words reach a player by — a gate's `refusal` beat riding a rejection — beside events and views, linking the new guide. "What tells a client the journal grew" is promoted out of "The stable difference" to its own section (it is client how-to, not contrast). "Where next" gains `listeners-and-flags.md` (flags' visibility is discussed inline without a link) and the new guide. -- `listeners-and-flags.md`: the fetch-quest listener — currently the guides' only code sample with no runnable twin — folds into the page's complete program with its imports, exercised minimally, so the harness covers it. The cascade sentence links the new guide's "When something doesn't land". +- `listeners-and-flags.md`: the fetch-quest listener — the guides' largest code sample with no runnable twin — folds into the page's complete program with its imports, exercised minimally, so the harness covers it. The page's other twin-less fragment, the one-line `register_listener(Interpreter(session))`, closes the same way: the complete program registers the interpreter beside its own listener — legal and inert on an adventure that authors no triggers or quests — so the fragment becomes a true excerpt. The cascade sentence links the new guide's "When something doesn't land". - `authoring-custom-content.md`: the scope statement and the "Where next" bullet reroute authored *behavior* — gates, triggers, quests — to the new guide, replacing the pre-phase-11 description of building-an-adventure. - `ruleset-options.md`: the "Where next" sentence routing quests and triggers to listeners-and-flags is rewritten to the post-phase-15 model — session flags and game-owned listeners there, authored triggers and quests in the new guide. ### 3. The front-end walkthroughs — `docs/front-ends/`, `examples/` -- The TUI example's rendering changes, pinned as the full list of `examples/` code changes this phase makes: `_run` iterates `result.events` instead of diffing the event log — the delta idiom's stated rationale is false, since the phase 14 `CommandResult` fold already returns everything nested listener-issued commands logged — and prints a rejection's authored `refusal` text when its params carry one, so phase 11's beats stop being silently dropped by the renderer everyone copies; `_status` lists active quests with objective states (rendering `PlayerView.quests`); a `journal` verb prints the journal with clock stamps (rendering `PlayerView.journal`). `examples/fastapi_crawler/content.py` gains snippet markers around `new_session` and `restore_session` (marker comments only — no behavior change). None of these changes touches a command sequence or a draw: `milestone.txt` is unchanged, the goldens capture state and logs rather than stdout, and `tests/test_example_crawler.py` assertions move only if they pin output shapes the rendering changes altered. +- The TUI example's rendering changes, pinned as the full list of `examples/` code changes this phase makes: `_run` iterates `result.events` instead of diffing the event log — the delta idiom's stated rationale is false, since the phase 14 `CommandResult` fold already returns everything nested listener-issued commands logged — and prints a rejection's authored `refusal` text when its params carry one, so phase 11's beats stop being silently dropped by the renderer everyone copies. The refusal line's format is pinned: the literal `(refused:` prefix stands, the authored text appended after the code — two clean-run tripwires (`tests/test_example_crawler.py` and the golden generator's marker check) grep for that literal's *absence*, and a reworked prefix would pass green while blinding both. `_status` lists active quests with objective states (rendering `PlayerView.quests`); a `journal` verb prints the journal with clock stamps (rendering `PlayerView.journal`), and `milestone.txt` gains exactly one `journal` line before the closing `status` so the verb has transcript and test coverage — a pure view read like `status`, executing no command and drawing nothing, so the command log and the golden stand unchanged by construction. `examples/fastapi_crawler/content.py` gains snippet markers around `new_session` and `restore_session`, and `examples/fastapi_crawler/app.py` gains one around the `CreateSession` model and its exactly-one-of validator, which sit outside every existing marker (marker comments only — no behavior change in either file). None of these changes touches a command sequence or a draw: the goldens capture state and logs rather than stdout, and `tests/test_example_crawler.py` assertions move only where they pin output shapes the rendering changes altered, gaining one substring assertion on the `journal` output. - Transcript re-capture, pinned as procedure: the walkthrough's quoted transcripts are re-captured from the real `--seed 21 --script milestone.txt` run after the rendering changes, never hand-edited — the same discipline that made the current transcripts byte-accurate. - `tui-crawler.md`: the delta-loop prose (lines 29–33, 57–59) rewrites to the true contract — the result envelope carries the interpreter's reactions, which is why the loop is a plain iteration; the intro's "even a whole quest — is ordinary application code" reframes to the authored-data story its own quest section tells; "a quest listener's reactions" becomes the interpreter; the player-view section teaches `quests` and `journal` with the new excerpts; the source-stamp sentence shows the literal form and notes triggers stamp too; the shrine-excerpt framing stops promising an encounter the excerpt doesn't contain; the reward-economics sentence unpacks "the last award has already fired" so pay-in-coin isn't misread as pay-nothing; and a short passage teaches `SessionMode.terminal` as the loop condition a front end checks — with an honest note that the TUI deliberately keeps its loop open after victory so the closing `status` can be read, and a link to the LLM page's loop sketch where the terminal check is shown. - `fastapi-pattern.md`, untouched since phase 7 and the review's biggest walkthrough gap: a new section on the served content teaches what the page never says — the server registers `Interpreter` when it creates a session *and again when it restores one*, excerpting the two `content.py` functions; the restore paragraph names the interpreter as the listener it re-registers, making the page's own lesson (listeners are code, saves are data) concrete. The wire-view section adds `journal` and `quests` to the whitelist enumeration and notes the player-visible quest events crossing in `events`, plus the `rejections[].params.refusal` field a web client should render. The `CreateSession` model and its exactly-one-of validator join the excerpts the prose already leans on. "All 44 of them" and every other numeric surface count dies in favor of "every command in the registry". @@ -104,4 +104,4 @@ Out of scope (deferred to the track that picks each up, or declined with rationa - The docstring pass holds under the strict build's cross-reference validation: the two stale module orientations rewritten, the one-way links closed in `triggers`/`quests`/`gates`, the selector sentence on the three grant commands, and the named field docstrings rendering in the reference. - `CHANGELOG.md` `[Unreleased]` gains Changed bullets for the example rendering changes (result-events loop, refusal text, quest status, the `journal` verb) — the docs-only changes carry no bullets. - Acceptance, pinned: the implementation-phase rubber-duck includes a fresh-eyes, docs-only walkthrough of four blind tasks — author a key-gated door, a trigger, and a concluding quest from the published site alone; build a minimal front end that renders quests, the journal, and a gate's refusal from the walkthroughs alone; answer "what survives a save, and why does replay run with no listeners?" from the determinism guide alone; and, starting from the home page, reach quest authoring in at most two clicks. All four must succeed without reading library source. -- Every declined item in this plan names its rationale, and `docs/adaptations.md` gains no entries — a docs phase pins engineering, not rules. +- The spec changes exactly twice across the phase: the roadmap entry (this PR) and the replay-listener scoping amendment work item 2 argues (the implementation PR). Every declined item in this plan names its rationale, and `docs/adaptations.md` gains no entries — a docs phase pins engineering, not rules. From 4606aa26f980ed028c3a3f336bb171de6d4a4441 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Fri, 7 Aug 2026 08:41:45 -0700 Subject: [PATCH 3/4] note journal verb and script line land together per review sign-off Claude-Session: https://claude.ai/code/session_01GeUYYGfPQYGPRuk8C5Ju88 --- docs/phase-16-plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/phase-16-plan.md b/docs/phase-16-plan.md index 565d2ee..28be2bd 100644 --- a/docs/phase-16-plan.md +++ b/docs/phase-16-plan.md @@ -56,7 +56,7 @@ Out of scope (deferred to the track that picks each up, or declined with rationa ### 3. The front-end walkthroughs — `docs/front-ends/`, `examples/` -- The TUI example's rendering changes, pinned as the full list of `examples/` code changes this phase makes: `_run` iterates `result.events` instead of diffing the event log — the delta idiom's stated rationale is false, since the phase 14 `CommandResult` fold already returns everything nested listener-issued commands logged — and prints a rejection's authored `refusal` text when its params carry one, so phase 11's beats stop being silently dropped by the renderer everyone copies. The refusal line's format is pinned: the literal `(refused:` prefix stands, the authored text appended after the code — two clean-run tripwires (`tests/test_example_crawler.py` and the golden generator's marker check) grep for that literal's *absence*, and a reworked prefix would pass green while blinding both. `_status` lists active quests with objective states (rendering `PlayerView.quests`); a `journal` verb prints the journal with clock stamps (rendering `PlayerView.journal`), and `milestone.txt` gains exactly one `journal` line before the closing `status` so the verb has transcript and test coverage — a pure view read like `status`, executing no command and drawing nothing, so the command log and the golden stand unchanged by construction. `examples/fastapi_crawler/content.py` gains snippet markers around `new_session` and `restore_session`, and `examples/fastapi_crawler/app.py` gains one around the `CreateSession` model and its exactly-one-of validator, which sit outside every existing marker (marker comments only — no behavior change in either file). None of these changes touches a command sequence or a draw: the goldens capture state and logs rather than stdout, and `tests/test_example_crawler.py` assertions move only where they pin output shapes the rendering changes altered, gaining one substring assertion on the `journal` output. +- The TUI example's rendering changes, pinned as the full list of `examples/` code changes this phase makes: `_run` iterates `result.events` instead of diffing the event log — the delta idiom's stated rationale is false, since the phase 14 `CommandResult` fold already returns everything nested listener-issued commands logged — and prints a rejection's authored `refusal` text when its params carry one, so phase 11's beats stop being silently dropped by the renderer everyone copies. The refusal line's format is pinned: the literal `(refused:` prefix stands, the authored text appended after the code — two clean-run tripwires (`tests/test_example_crawler.py` and the golden generator's marker check) grep for that literal's *absence*, and a reworked prefix would pass green while blinding both. `_status` lists active quests with objective states (rendering `PlayerView.quests`); a `journal` verb prints the journal with clock stamps (rendering `PlayerView.journal`), and `milestone.txt` gains exactly one `journal` line before the closing `status` so the verb has transcript and test coverage — a pure view read like `status`, executing no command and drawing nothing, so the command log and the golden stand unchanged by construction; the verb and the script line land in the same commit, since a script naming a verb that doesn't exist yet trips the generator's unknown-command marker check. `examples/fastapi_crawler/content.py` gains snippet markers around `new_session` and `restore_session`, and `examples/fastapi_crawler/app.py` gains one around the `CreateSession` model and its exactly-one-of validator, which sit outside every existing marker (marker comments only — no behavior change in either file). None of these changes touches a command sequence or a draw: the goldens capture state and logs rather than stdout, and `tests/test_example_crawler.py` assertions move only where they pin output shapes the rendering changes altered, gaining one substring assertion on the `journal` output. - Transcript re-capture, pinned as procedure: the walkthrough's quoted transcripts are re-captured from the real `--seed 21 --script milestone.txt` run after the rendering changes, never hand-edited — the same discipline that made the current transcripts byte-accurate. - `tui-crawler.md`: the delta-loop prose (lines 29–33, 57–59) rewrites to the true contract — the result envelope carries the interpreter's reactions, which is why the loop is a plain iteration; the intro's "even a whole quest — is ordinary application code" reframes to the authored-data story its own quest section tells; "a quest listener's reactions" becomes the interpreter; the player-view section teaches `quests` and `journal` with the new excerpts; the source-stamp sentence shows the literal form and notes triggers stamp too; the shrine-excerpt framing stops promising an encounter the excerpt doesn't contain; the reward-economics sentence unpacks "the last award has already fired" so pay-in-coin isn't misread as pay-nothing; and a short passage teaches `SessionMode.terminal` as the loop condition a front end checks — with an honest note that the TUI deliberately keeps its loop open after victory so the closing `status` can be read, and a link to the LLM page's loop sketch where the terminal check is shown. - `fastapi-pattern.md`, untouched since phase 7 and the review's biggest walkthrough gap: a new section on the served content teaches what the page never says — the server registers `Interpreter` when it creates a session *and again when it restores one*, excerpting the two `content.py` functions; the restore paragraph names the interpreter as the listener it re-registers, making the page's own lesson (listeners are code, saves are data) concrete. The wire-view section adds `journal` and `quests` to the whitelist enumeration and notes the player-visible quest events crossing in `events`, plus the `rejections[].params.refusal` field a web client should render. The `CreateSession` model and its exactly-one-of validator join the excerpts the prose already leans on. "All 44 of them" and every other numeric surface count dies in favor of "every command in the registry". From 5c5a0026740d51654d42d4b7b323f2fdfb0f5e2b Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Fri, 7 Aug 2026 08:57:33 -0700 Subject: [PATCH 4/4] revise phase 16 plan per maintainer review: rename the kernel guide file too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan kept kernel-a-la-carte.md's filename under its new title for URL stability — a consumer nobody can name, the exact accommodation the greenfield discipline forbids. Docs URLs are not among the compatibility contracts AGENTS.md names as real, so the file renames to rules-without-a-session.md with its title; the redirects-plugin decline stands, now for the greenfield reason rather than a no-URL-changes claim. Claude-Session: https://claude.ai/code/session_01GeUYYGfPQYGPRuk8C5Ju88 --- docs/phase-16-plan.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/phase-16-plan.md b/docs/phase-16-plan.md index 28be2bd..b116f68 100644 --- a/docs/phase-16-plan.md +++ b/docs/phase-16-plan.md @@ -27,7 +27,7 @@ In scope: Out of scope (deferred to the track that picks each up, or declined with rationale): - **Versioned documentation (mike or equivalent)** — the release skill owns the adoption trigger; nothing here changes it -- **A redirects plugin** — declined: no page URL changes in this phase (`building-an-adventure.md` keeps its path; the new guide is a new path), and the sections that move leave a page that still exists, so a stale external anchor degrades to the page top rather than a 404. Renaming `kernel-a-la-carte.md` to match its new title is declined for the same reason: the navigation label and H1 change, the URL does not +- **A redirects plugin** — declined, for the greenfield reason rather than URL stability: the phase renames one page (`kernel-a-la-carte.md`, work item 4) and moves sections off another, and accommodates neither with a redirect layer. Docs URLs are not among the compatibility contracts `AGENTS.md` names as real (the public API, `schema_version`, the determinism contract), the site's inbound-link surface is effectively zero, and a redirect kept for a consumer nobody can name is exactly the just-in-case scaffolding the discipline forbids. A stale anchor into `building-an-adventure.md` degrades to the page top; a stale link to the old kernel path 404s, and that is accepted - **A published changelog page** — declined: `CHANGELOG.md` links repo paths the site excludes (`docs/spec.md`), so a snippet-included page fails the strict build's link validation; rewriting the changelog's links per release is maintenance without a demonstrated consumer. `docs/index.md` links the GitHub changelog instead (work item 5) - **A JTBD rewrite of docstrings** — declined by the one-way-bridge discipline itself: docstrings are reference and keep full project vocabulary - **The LLM-referee example agent** — not shipped and no longer promised; the page's roadmap sentence is reworded (work item 4), and whether an example agent is worth building is a post-phase decision on its own merits @@ -69,7 +69,7 @@ Out of scope (deferred to the track that picks each up, or declined with rationa - The save/load correction, both carriers: README's "Saved games replay from the seed and the command log" and quickstart's "Loading replays the command log against the same seed" are rewritten to the state-restore truth — a save restores from serialized state alone; replay is the separate `replay_game`, and the two agreeing is the determinism guarantee. The quickstart twin blocks are code, not prose, so the twin-equality gate is untouched. - The concepts table, pinned: a short "What things are called" table on `docs/index.md` — the JTBD row set: quest log → the journal; scripted event → a trigger; locked door needing an item → a gate; the text an event shows → beats on a narrative block; what the player may see → the player view; seedable randomness → named streams and draws; save file → a stamped document; win condition → a concluding quest and `victory` — each row linking where the term is taught. Rows are added sparingly; the table is an index, not a glossary essay. - Quickstart and index hand-offs: the "Where next" and "Where to start" bullets describing building-an-adventure predate phase 10 ("assembles a small dungeon model by model") and rewrite to name what each page now teaches, with the new guide as the authored-layer destination. -- The heading audit, pinned to its one real offender: "The kernel à la carte" becomes navigation label and H1 "Using the rules without a session", the term *kernel* introduced job-first in the opening sentence; the filename and URL stand. The remaining guide titles survive the audit — they are either API vocabulary developers hold (sessions, commands, events, listeners) or common game-design words (views, determinism, ruleset). Guide openings that lack a job-first frame gain one sentence each, per the one-way bridge; interiors are untouched. +- The heading audit, pinned to its one real offender: "The kernel à la carte" becomes "Using the rules without a session" — navigation label, H1, and file, renamed to `guides/rules-without-a-session.md`, because docs URLs carry no compatibility contract and a filename fossilized under a renamed title is the accommodation the greenfield discipline exists to kill. The term *kernel* is introduced job-first in the opening sentence; the navigation entry and every inbound guide link move with the rename, the strict build catching stragglers. The remaining guide titles survive the audit — they are either API vocabulary developers hold (sessions, commands, events, listeners) or common game-design words (views, determinism, ruleset). Guide openings that lack a job-first frame gain one sentence each, per the one-way bridge; interiors are untouched. - `AGENTS.md` gains a short documentation-conventions block, pinned to four rules: prose states no numeric surface counts (registries and generated references carry the numbers); vocabulary follows the one-way bridge (job first, term once, then commit — no parallel vocabulary); the voice gradient (front door plain, depth rich); transcripts and captured output are re-captured from real runs, never hand-edited. ### 5. The docstring linkage pass — `src/`, docstring and comment hunks only @@ -100,7 +100,7 @@ Out of scope (deferred to the track that picks each up, or declined with rationa - The six falsified claims are gone and stated correctly: the victory entrance, the trigger-unlock "later release" sentence, the schema version, the save/load mechanism (both carriers), the delta-loop rationale, and the two surface counts — and a grep for numeric surface counts in prose returns nothing. - The new guide is live in the navigation with its complete program under the harness; building-an-adventure stands trimmed with its own program, its first fragment self-contained; every cross-link the split rewires resolves under the strict build. - The FastAPI page teaches interpreter registration on both paths from real excerpts; the LLM page teaches the guidance surface, the beat-weaving rule, and the completed authorial surface; the TUI page renders quests, the journal, and refusal text through the updated example, its transcripts re-captured byte-accurate. -- The front door advertises the authored layer, the concepts table is live, the kernel guide carries its job-first title, and `AGENTS.md` carries the four documentation conventions. +- The front door advertises the authored layer, the concepts table is live, the kernel guide carries its job-first title at its renamed path, and `AGENTS.md` carries the four documentation conventions. - The docstring pass holds under the strict build's cross-reference validation: the two stale module orientations rewritten, the one-way links closed in `triggers`/`quests`/`gates`, the selector sentence on the three grant commands, and the named field docstrings rendering in the reference. - `CHANGELOG.md` `[Unreleased]` gains Changed bullets for the example rendering changes (result-events loop, refusal text, quest status, the `journal` verb) — the docs-only changes carry no bullets. - Acceptance, pinned: the implementation-phase rubber-duck includes a fresh-eyes, docs-only walkthrough of four blind tasks — author a key-gated door, a trigger, and a concluding quest from the published site alone; build a minimal front end that renders quests, the journal, and a gate's refusal from the walkthroughs alone; answer "what survives a save, and why does replay run with no listeners?" from the determinism guide alone; and, starting from the home page, reach quest authoring in at most two clicks. All four must succeed without reading library source.