From d721dfdd3045867ddb546f4a1a62d9de1bac006d Mon Sep 17 00:00:00 2001 From: Chris Guidry Date: Wed, 5 Aug 2026 06:23:17 -0400 Subject: [PATCH] Key the roadmap on plan numbers, not phase ordinals The roadmap numbered phases by ordinal and linked each to a plan, so the aside was phase 11 in plans/0013 and persistence was phase 10 in plans/0014. A plan number is the order the plan was written, not its build order, and we write some plans ahead of their phase, so the two never agreed after session state. The roadmap now keys every row by its plan number and lists it in build order. A plan keeps its number wherever it lands, and a row with no plan yet stays a stub until its plan is written. Completed plans that referenced an old phase now carry the plan they map to, and plans/README no longer tells us to renumber the phases that follow a new plan. --- plans/0000-roadmap.md | 42 +++++++++++-------- plans/0012-the-archivist.md | 4 +- plans/0013-the-aside.md | 2 +- plans/README.md | 4 +- plans/completed/0005-tool-calls.md | 6 +-- .../completed/0006-stream-into-scrollback.md | 3 +- plans/completed/0008-streaming-markdown.md | 3 +- plans/completed/0009-timekeeping.md | 11 ++--- plans/completed/0010-context-stack.md | 4 +- plans/completed/0011-session-state.md | 11 +++-- 10 files changed, 52 insertions(+), 38 deletions(-) diff --git a/plans/0000-roadmap.md b/plans/0000-roadmap.md index 8ddd0d4..329cdf4 100644 --- a/plans/0000-roadmap.md +++ b/plans/0000-roadmap.md @@ -57,48 +57,54 @@ from it have to earn their way into the reboot. Each phase gets its own plan when we're about to start it, and that's when the real design happens. The blurbs here stay deliberately loose. -- [x] **Phase 0: Scaffolding.** A Rust project that builds a single binary +The rows are keyed by plan number and listed in build order. A plan's number +is the order it was written in, not a phase ordinal, so the numbers do not +always ascend: we write some plans ahead of the phase that builds them, and +the row keeps its plan's number wherever it lands in the build. A row with +no plan yet is a stub and keeps its number until its plan is written. + +- [x] **0001: Scaffolding.** A Rust project that builds a single binary with formatting, linting, and tests wired up. *You can now: run `storied` and get a hello.* ([0001](completed/0001-scaffolding.md)) -- [x] **Phase 1: SRD download and prep.** A subcommand that fetches the 5e +- [x] **0002: SRD download and prep.** A subcommand that fetches the 5e SRD 5.2.1, extracts it, and splits it into per-section markdown with frontmatter. A batch job with files in and files out, which makes it a gentle Rust warm-up. *You can now: read Fireball from a file the binary produced.* ([0002](completed/0002-srd.md)) -- [x] **Phase 2: Talk to a model.** A hand-rolled client for the chat API +- [x] **0003: Talk to a model.** A hand-rolled client for the chat API with streaming, config for the endpoint and model, and the first terminal: a prompt box at the bottom, the transcript scrolling above, the DM's words streaming in. No tools, no persistence. *You can now: have a conversation with a DM in your terminal.* ([0003](completed/0003-talk-to-a-model.md)) -- [x] **Phase 3: Dice.** The full 5e dice grammar as a pure module, plus +- [x] **0004: Dice.** The full 5e dice grammar as a pure module, plus a `storied roll` subcommand to play with it. *You can now: roll `4d6kh3` at your shell and watch the dice fall.* ([0004](completed/0004-dice.md)) -- [x] **Phase 4: Tool calls.** The loop grows in-process tools, starting +- [x] **0005: Tool calls.** The loop grows in-process tools, starting with dice. This phase settles how a tool declares itself, how results flow back through the loop, and how tool calls get their own styling in the transcript. *You can now: watch the DM roll real dice mid-scene.* ([0005](completed/0005-tool-calls.md)) -- [x] **Phase 5: Rule lookup.** A tool that looks up entries in the rules +- [x] **0007: Rule lookup.** A tool that looks up entries in the rules layer by case-insensitive keywords, nothing smarter. *You can now: watch the DM quote the actual rule.* ([0007](completed/0007-knowledge-mount.md)) -- [x] **Phase 6: Streaming markdown.** A renderer that turns the DM's +- [x] **0008: Streaming markdown.** A renderer that turns the DM's markdown into styled rows as it streams, committing each block as soon as it settles and holding back only the one still forming. *You can now: read the DM's words styled, not asterisked.* ([0008](completed/0008-streaming-markdown.md), and the scrollback viewport it streams into: [0006](completed/0006-stream-into-scrollback.md)) -- [x] **Phase 7: Timekeeping and the campaign log.** Game time, a +- [x] **0009: Timekeeping and the campaign log.** Game time, a transcript of every turn, and a campaign log of events — the backbone that DM memory, player recall, and context stacking all sit on. The clock is the last anchor in the log; there is no separate state. `lookup` for what's true, `recall` for what happened. *You can now: quit, come back, and the clock picks up where it left off.* ([0009](completed/0009-timekeeping.md)) -- [x] **Phase 8: The context stack.** The DM's knowledge layered into each +- [x] **0010: The context stack.** The DM's knowledge layered into each turn: what gets pushed into every prompt versus what it pulls through tools. This phase builds the layering architecture and writes the first real DM prompt that uses it: personality and style, the screen line @@ -106,7 +112,7 @@ the real design happens. The blurbs here stay deliberately loose. player earns them. The campaign log and transcript fuel it all. *You can now: meet the innkeeper without being told she's a retired assassin.* ([0010](completed/0010-context-stack.md)) -- [x] **Phase 9: Session state and the context budget.** The DM's +- [x] **0011: Session state and the context budget.** The DM's narration carries wikilinks that name entities, and the engine scans those references to inject the right facts into each turn's prompt. Entities the DM names become real as stubs, the DM's attention is what @@ -114,7 +120,7 @@ the real design happens. The blurbs here stay deliberately loose. session replays enough of it for the DM to pick up where it left off. *You can now: leave the tavern, and the tavern leaves the prompt.* ([0011](completed/0011-session-state.md)) -- [x] **Phase 10: Persistence.** Character sheet, world entities, and +- [x] **0014: Persistence.** Character sheet, world entities, and worlds that live somewhere real on disk. This phase decides the world root, the one-line decision three plans have stepped around. A fresh session's first prompt carries the session brief and the replayed tail @@ -122,7 +128,7 @@ the real design happens. The blurbs here stay deliberately loose. just the clock. *You can now: your character persists across sessions.* ([0014](completed/0014-persistence.md)) -- [ ] **Phase 11: Behind the screen.** The `aside` tool: the DM commits +- [ ] **0013: Behind the screen.** The `aside` tool: the DM commits secret canon to the transcript in a fenced block the player never sees. Secrets must be written, because a secret that lives only in the model's thinking drifts, dies at session end, and cannot be @@ -130,7 +136,7 @@ the real design happens. The blurbs here stay deliberately loose. scanner reads them, so secret entities become stubs too. *You can now: lose to a mystery whose answer was on disk the whole time.* ([0013](0013-the-aside.md)) -- [ ] **Phase 12: The Archivist.** A background agent that trails the +- [ ] **0012: The Archivist.** A background agent that trails the transcript and tends the world's files. The record (transcript and campaign log) stays append-only and belongs to the session; the notes (entity files) are the Archivist's to write, rewrite, and cross-link. @@ -141,7 +147,7 @@ the real design happens. The blurbs here stay deliberately loose. record is point at the notes. *You can now: come back tomorrow and find the innkeeper's page written up and a brief for where you left off.* ([0012](0012-the-archivist.md)) -- [ ] **Phase 13: The DM's storytelling voice.** Instructions that teach +- [ ] **The DM's storytelling voice.** Instructions that teach the DM craft, not just rules, drawn from real storytelling theory: cadence and rhythm (short beats when danger is close, longer lines when the world is calm), scene shape (arrive late, leave early, open @@ -152,7 +158,7 @@ the real design happens. The blurbs here stay deliberately loose. the base game sets the craft and a world or module tunes the voice. *You can now: feel the difference between a bar fight and a quiet morning.* -- [ ] **Phase 14: The Weaver.** A background agent ahead of the story +- [ ] **The Weaver.** A background agent ahead of the story where the Archivist is behind it. It reads the record and the notes, follows the threads the table left hanging, and drafts future possibilities: hooks, encounters, consequences already on their way. @@ -160,7 +166,7 @@ the real design happens. The blurbs here stay deliberately loose. merely intended never reads as canon. Deliberately loose; its real design starts after the Archivist has run for a while. *You can now: walk into a plot that was waiting for you.* -- [ ] **Phase 15: Encounter mode.** The DM gets two full modes: +- [ ] **Encounter mode.** The DM gets two full modes: storytelling, which is everything built so far, and encounter, for when time slows down to rounds. Each mode has its own timekeeping rules (the SRD's and homebrew's round scale instead of the campaign @@ -169,7 +175,7 @@ the real design happens. The blurbs here stay deliberately loose. pools, and positioning arithmetic tight instead of holding them in its head. Deliberately loose; the design starts when we get here. *You can now: fight six goblins and the DM never loses count.* -- [ ] **Phase 16: The real character sheet.** Phase 10's sheet is a +- [ ] **The real character sheet.** Persistence's sheet is a flat set of frontmatter keys, enough to persist a character and nowhere near enough to represent one. 5e characters carry structure: inventory and equipment, prepared and known spells, slots by level, @@ -179,7 +185,7 @@ the real design happens. The blurbs here stay deliberately loose. injected stat block to match. Deliberately loose; the shape should come from what real play breaks first. *You can now: cast from a spell list the sheet actually tracks.* -- [ ] **Phase 17 and beyond: ideas that have to earn it.** Character +- [ ] **Beyond: ideas that have to earn it.** Character creation, background world motion, advancement, name generation, colors and theming (the styles all live in `markdown::style` waiting for it). Each one starts as a conversation, not a commitment. diff --git a/plans/0012-the-archivist.md b/plans/0012-the-archivist.md index b9b9861..2c4ccb1 100644 --- a/plans/0012-the-archivist.md +++ b/plans/0012-the-archivist.md @@ -29,7 +29,7 @@ prep notebook. A note gets rewritten freely as the story moves. The notes are what we currently believe about the world, built from the record plus whatever Chris edits in by hand. -This split answers a question phase 9 kept circling: where does an +This split answers a question plan 0011 kept circling: where does an entity's history live in its file? It doesn't. An entity's history is a query against the record. Search the transcript and log for the entity's wikilinks, and every scene it ever appeared in comes back, anchored in @@ -252,7 +252,7 @@ the campaign has met. Curation is the Archivist's job. Until the Archivist exists, a hand-written `session.md` works the same way, which is how the startup mechanism can ship before the agent -does. Where the file lives is decided with the world root in phase 10. +does. Where the file lives is decided with the world root in plan 0014. ## The loop, drawn diff --git a/plans/0013-the-aside.md b/plans/0013-the-aside.md index 443c05c..eaa1da5 100644 --- a/plans/0013-the-aside.md +++ b/plans/0013-the-aside.md @@ -71,7 +71,7 @@ skips them. That resolves a real tension with `session.md`, which is the one note a player would legitimately want to read as a "previously on." -Reading the raw files past the fences is the same honor system phase 8 +Reading the raw files past the fences is the same honor system plan 0010 established for `visibility: screened` lore. The screen line is enforced where the game renders, not by hiding bytes from the person who owns the disk. diff --git a/plans/README.md b/plans/README.md index 678f904..a34afdf 100644 --- a/plans/README.md +++ b/plans/README.md @@ -23,7 +23,9 @@ way "see 0003" always means the same thing. - **Design first.** We talk an idea through before any code exists. The plan gets written, Chris signs off, then we build. After the plan is written, we add it to the roadmap in `0000-roadmap.md` - and renumber the phases that follow it. + under its own number. The roadmap is keyed by plan number and listed in + build order; a plan number is the order the plan was written, not a phase + ordinal, so a plan written ahead of its phase does not renumber anything. - **Small pieces.** We build in phases, and every phase ends with something you can actually run. We do not spend months on foundations with nothing to show for it. diff --git a/plans/completed/0005-tool-calls.md b/plans/completed/0005-tool-calls.md index a5327da..c86316b 100644 --- a/plans/completed/0005-tool-calls.md +++ b/plans/completed/0005-tool-calls.md @@ -62,7 +62,7 @@ after dice takes the same path. ``` `Toolbox` holds `Vec>`. Tools own their own state: the - dice tool owns its RNG, and the phase 5 lookup tool will own the + dice tool owns its RNG, and the phase 5 lookup tool (plan [0007](0007-knowledge-mount.md)) will own the rules layer. Adding a tool is one file pair plus one `Box::new`. A tool's `Err(String)` goes back to the model as that call's tool result, worded so the model can correct and retry; the player never @@ -148,7 +148,7 @@ after dice takes the same path. inserted like any tool line) and a transient working phrase in the live region, where the thinking indicator already lives. We build them when a tool is actually slow; dice resolve in microseconds and - phase 5's lookup is a local file scan. Escape interrupts between + phase 5's lookup (plan [0007](0007-knowledge-mount.md)) is a local file scan. Escape interrupts between deltas and calls; interrupting inside a running tool is future work. ## The wire @@ -211,7 +211,7 @@ wrapping. ## Out of scope -- **Rule lookup.** Phase 5, one file pair away once this lands. +- **Rule lookup.** Phase 5 (plan [0007](0007-knowledge-mount.md)), one file pair away once this lands. - **Announcements and working phrases.** The seams above, built when a slow tool exists. - **Parallel tool dispatch.** Sequential until a turn needs more. diff --git a/plans/completed/0006-stream-into-scrollback.md b/plans/completed/0006-stream-into-scrollback.md index c5a9b2e..a09316b 100644 --- a/plans/completed/0006-stream-into-scrollback.md +++ b/plans/completed/0006-stream-into-scrollback.md @@ -125,4 +125,5 @@ cursor staying visible on every row. conversation; this plan streams plain styled text. - **Scrollback search, mouse support, screen readers.** The terminal's own features cover the first two for now. -- **Rule lookup.** Phase 5 of the roadmap becomes plan 0007. +- **Rule lookup.** Phase 5 of the roadmap becomes plan + [0007](0007-knowledge-mount.md). diff --git a/plans/completed/0008-streaming-markdown.md b/plans/completed/0008-streaming-markdown.md index 1db18a4..edafb24 100644 --- a/plans/completed/0008-streaming-markdown.md +++ b/plans/completed/0008-streaming-markdown.md @@ -254,4 +254,5 @@ calls them stays out of coverage, same as the rest of `terminal.rs`. - **Markdown in tool lines or player input.** Tools compose their own styling; the player's words stay verbatim. - **Teaching the DM about the renderer.** Whether the system prompt - should mention tables or headings is phase 7's tuning pass. + should mention tables or headings is phase 7's tuning pass (plan + [0009](0009-timekeeping.md)). diff --git a/plans/completed/0009-timekeeping.md b/plans/completed/0009-timekeeping.md index 59870d3..6bd9702 100644 --- a/plans/completed/0009-timekeeping.md +++ b/plans/completed/0009-timekeeping.md @@ -85,8 +85,9 @@ based on visibility. The DM controls the screened text per call. You can quit mid-session, come back, and `recall` remembers everything that happened. The clock picks up where it left off. This is the foundation for: -- Phase 8: DM prompting — personality, style, secrets behind the screen -- Phase 9: the context stack — what the DM sees each turn, layered from - rules/world/player -- Phase 10: full persistence — character sheet, world entities, session - state +- Phase 8 (plan [0010](0010-context-stack.md)): DM prompting — + personality, style, secrets behind the screen +- Phase 9 (plan [0010](0010-context-stack.md)): the context stack — + what the DM sees each turn, layered from rules/world/player +- Phase 10 (plan [0014](0014-persistence.md)): full persistence — + character sheet, world entities, session state diff --git a/plans/completed/0010-context-stack.md b/plans/completed/0010-context-stack.md index 247d187..d09d3ba 100644 --- a/plans/completed/0010-context-stack.md +++ b/plans/completed/0010-context-stack.md @@ -85,8 +85,8 @@ worlds/{world}/ lore/ → world-layer knowledge entries player/ _system.md → player-layer system prompt fragment (personality) - campaign-log/ → (Phase 7) - transcript/ → (Phase 7) + campaign-log/ → (plan 0009) + transcript/ → (plan 0009) ``` The SRD and module layers already live outside the world directory. Their diff --git a/plans/completed/0011-session-state.md b/plans/completed/0011-session-state.md index 7f635d9..9bf8d56 100644 --- a/plans/completed/0011-session-state.md +++ b/plans/completed/0011-session-state.md @@ -2,7 +2,7 @@ ## What we're building -The context stack (phase 8) pushes `context: true` entries and layer +The context stack (phase 8; plan [0010](0010-context-stack.md)) pushes `context: true` entries and layer `system.md` fragments into every turn's prompt. It has no idea what is true right now. Walk into a tavern, walk out of it, and the tavern's `context: true` entry stays in the prompt forever. The DM knows the @@ -58,7 +58,8 @@ the engine creates a stub: {world-root}/entities/{canonical-name}.md ``` -(Where the world root lives on disk is phase 10's decision.) +(Where the world root lives on disk is phase 10's decision — plan +[0014](0014-persistence.md).) The stub is born as a small note: frontmatter for the machines, and the narration that summoned it as its first prose. The note format is @@ -181,7 +182,8 @@ the engine can re-scan it on restart. ## What this does not cover - **Character sheet.** HP, spell slots, inventory, conditions. That is - phase 10+, and its design will start from this phase's vocabulary. + phase 10+ (plan [0014](0014-persistence.md) and beyond), and its + design will start from this phase's vocabulary. - **The context budget.** The prompt grows with every entity the DM references. A token budget that decides what to drop has to exist, but it is not urgent while entities are small stubs. It will be a natural @@ -212,7 +214,8 @@ the engine can re-scan it on restart. that has the DM open with a "Previously, on …" recap, then draw a rule before play begins. `storied sandbox --world DIR` reuses a directory across runs so a resumed session is playable before - phase 10 picks the real world root + phase 10 (plan [0014](0014-persistence.md)) picks the real world + root ## Relationship to the Python prototype -- 2.51.2