diff --git a/docs/roadmap.md b/docs/roadmap.md index 109517f..964be80 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -25,22 +25,41 @@ AI-generated story skeletons · multiplayer (never) · mobile · credits/payment ## Sequencing Status: ✅ done · ▶ next · ○ planned. Each item is its own spec → plan → implementation cycle. +Each carries its **§2 side** (state = code owns it · text = AI owns it · both = it is two systems), what it **depends on**, and the **milestone goal** it advances. -### Foundations — contract + engines -- ✅ **Canon log + origin JSON-Schema contract**, api validation, unified 422 envelope (Plan A). -- ✅ **Client canon log engine** — typed GDScript model (invariants in the model), new-game construction (origin + world + creation → canon log), turn-to-turn maintenance mutators, pure `[FACT]`/`[MOVE]`/`[ADJUST_DISPOSITION]` tag extractor (Plan B). +The milestones are ordered to reach the POC's one question as early as possible, then build the game around a validated answer: -### Proving the loop — the POC's one question -- ✅ **Narrator / Ollama pipeline (server)** — the shared server-side model-call pipeline (Ollama httpx client, role→model routing config, prompt loader + canon-log digest renderer, §10 call logging, single-retry → typed 502) wired through `/dm/narrate`. Non-streaming. Both phases landed: **(1) pipeline foundation** (`config`/`routing`/`prompts`/`ollama_client`/`call_log`), **(2) Narrator online** (authored `narrator.md` body + `narrate` service + gated live smoke). Server returns raw prose; the client extracts tags. The other four roles become thin drop-ins on this pipeline. Live wire proven against qwen3.5; 49 tests. Merged to `dev` (`ddc00d4`). -- ▶ **Client HTTP loop** — client posts its canon log to `/dm/narrate`, displays the prose, harvests `[FACT]` via its `TagExtractor` into its own log, and shows the degraded-DM authored fallback (§13) on any non-200. Closes the first end-to-end Narrator scene. -- ○ **Bounded NPC conversation** — `/npc/speak` with persona + `knowledge[]` + `available_moves[]`; client validates and applies moves, silently drops the invalid, keeps the prose (§6). **The core experiment.** +| Milestone | Goal it proves | +|---|---| +| **M0 — Foundations** ✅ | The contract and the client-side engine exist and are trustworthy. | +| **M1 — Prove the server loop** ✅ | A real model sits behind `/dm/narrate` and the pipeline is reusable. | +| **M2 — Prove aliveness** ◀ *here* | **Does bounded AI dialogue feel alive?** — the whole POC question (§17). | +| **M3 — Make it a game** | The dialogue lives inside real stakes: a playable ~30-min dungeon slice. | +| **M4 — POC ship-readiness** | Every AI surface degrades with a voice, never an error (§13). | -### Fills out the POC (charter §17) -- ○ **Streaming** (§14) — server streams Ollama → client, landed with/after the client HTTP loop. -- ○ **Adjudicator** (free text → legal action, strict JSON), **Improviser** (§7 sandbox, whitelisted changes), **Banter** (§9 humiliation log). -- ○ **Luck fully wired** — drift events, one shrine, one cursed item. -- ○ **Combat** — deterministic, seeded; the dungeon (three fights, one boss). -- ○ **Authored fallback text** for every AI surface (§13). +--- + +### M0 — Foundations ✅ +- ✅ **Canon log + origin JSON-Schema contract** — api validation, unified 422 envelope (Plan A). *§2: state · foundation.* +- ✅ **Client canon log engine** — typed GDScript model (invariants in the model), new-game construction (origin + world + creation → canon log), turn-to-turn maintenance mutators, pure `[FACT]`/`[MOVE]`/`[ADJUST_DISPOSITION]` tag extractor (Plan B). *§2: state · depends on the contract.* + +### M1 — Prove the server loop ✅ +- ✅ **Narrator / Ollama pipeline (server)** — the shared server-side model-call pipeline (Ollama httpx client, role→model routing config, prompt loader + canon-log digest renderer, §10 call logging, single-retry → typed 502) wired through `/dm/narrate`. Non-streaming. Both phases landed: **(1) pipeline foundation** (`config`/`routing`/`prompts`/`ollama_client`/`call_log`), **(2) Narrator online** (authored `narrator.md` body + `narrate` service + gated live smoke). Server returns raw prose; the client extracts tags. **The other four roles become thin drop-ins on this pipeline.** Live wire proven against qwen3.5; 49 tests. Merged to `dev` (`ddc00d4`). *§2: text · depends on the contract.* + +### M2 — Prove aliveness ◀ *(the POC's one question)* +- ▶ **Client HTTP loop** — client posts its canon log to `/dm/narrate`, displays the prose, harvests `[FACT]` via its `TagExtractor` into its own log, and shows the degraded-DM authored fallback (§13) on any non-200. First end-to-end vertical slice: **state → HTTP → model → prose → screen → fact-harvest.** The first moment a human sees generated prose in the running game. *§2: state (client owns the loop, consumes text) · depends on M1 + the client engine · goal: put the Narrator on screen.* +- ○ **Bounded NPC conversation** — `/npc/speak` with persona + `knowledge[]` + `available_moves[]`; client validates and applies moves, silently drops the invalid, keeps the prose (§6). Drops onto the *proven* client loop — adds move-tag validation on top of a display+harvest pipeline that already works. **The core experiment: this is the surface §17 is really asking about.** *§2: both (text = the NPC's voice · state = the validated moves) · depends on the client loop · goal: answer the one question.* +- ○ **Streaming** (§14) — server streams Ollama → client; latency reads as *the DM is speaking* rather than *the game is frozen*, hiding the ~2s call. **Gated on aliveness:** landed only after the dialogue experiment above says the prose is worth polishing. Additive to `ollama_client` (shaped for it) + an SSE/chunked response + the client stream consumer. *§2: text · depends on the client loop · goal: polish, not proof.* + +### M3 — Make it a game *(the playable ~30-min slice — dungeon first, per the sequencing call)* +- ○ **Adjudicator** — `/dm/adjudicate`: free text → a legal action or an in-world rejection, **strict JSON** (§12), small/fast model, the one-retry-with-error-appended path. Unlocks the hybrid input model (§15) the playable slice needs — the "what do you do?" boundary. *§2: both (text = the in-world reason · state = the chosen action) · depends on the pipeline · goal: free-text world actions.* +- ○ **Combat** — deterministic, seeded per encounter from save state (§10); positionless turn-based; resource-management as the one interesting axis; Luck influences only crit thresholds + borderline status checks (§7). The dungeon: **three fights, one boss.** Combat flavor text is async, optional Narrator calls (reuses M1). "Ship the boring one" (§10) — mostly state work, low AI risk. *§2: state (flavor text = a thin AI layer on top) · depends on the pipeline for flavor · goal: real stakes.* +- ○ **Luck fully wired** — generation exists (Plan B); this adds **drift events** (sleeping in a real bed, a shrine's blessing, killing something praying), **one shrine**, **one cursed item** (e.g. +STR / −LCK). Visible only in prose, never as a number (§7). Gates the Improviser. *§2: state · depends on the item + event systems · goal: the signature system, live.* +- ○ **Improviser** — `/dm/improvise`: minor off-script events, **§7-sandboxed by construction** — output passes a validator that can only apply a whitelisted set of state changes (dignity, never progress; structurally incapable of ending a quest). Fires on Luck; hooks Cadwyn. *§2: both (text = the event · state = the whitelisted change) · depends on Luck being wired · goal: bad luck as a story, not a fine.* +- ○ **Banter** — `/party/banter`: companions comment on recent events; low stakes, high charm, **aggressively cacheable**; reads Brannoc's humiliation log (§9), stacking not overwriting. Prose only, no tags. The cheapest role, landed last. *§2: text · depends on the humiliation log (Plan B) + events to comment on · goal: the comedy loop closes.* + +### M4 — POC ship-readiness +- ○ **Authored fallback text — full sweep** — every AI-dependent surface has authored fallback *content* (§13), written as writing, not error handling. Each role lands with its own fallback; this is the final pass that guarantees no surface can show the player an error. *§2: text · depends on every surface existing · goal: a degraded DM still has a voice.* ### Deferred beyond POC -Replicate provider (prod) · auth · metering · credits/payments (§4) — retrofit onto the pipeline later, the game doesn't notice. +Replicate provider (prod) · auth · metering · credits/payments (§4) — retrofit onto the pipeline later, the game doesn't notice. AI-generated story skeletons (v2 — the seeded story is authored for now).