Files
code_of_conquest_dnd/docs/roadmap.md
2026-07-10 11:15:38 -05:00

66 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Roadmap
Source of truth for scope is [`CLAUDE.md`](../CLAUDE.md). This tracks sequencing.
## POC — the one question (charter §17)
> **Does bounded AI dialogue feel alive, or does it feel like a chatbot in a costume?**
Test that before building anything else. ~30 minutes of play.
### POC scope
- One town, **two NPCs** with authored knowledge lists
- One dungeon: **three fights, one boss**
- **Two companions:** Cadwyn, Brannoc
- **Three playable classes:** Sellsword, Assassin, Priest
- Luck fully wired: generation, drift, one shrine, one cursed item
- Full proxy, full role split, full canon log
- Authored fallback text for every AI surface
### Out of scope (charter §17)
AI-generated story skeletons · multiplayer (never) · mobile · credits/payments/auth · companions leaving · positional combat · more than three classes.
## 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.
The milestones are ordered to reach the POC's one question as early as possible, then build the game around a validated answer:
| 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). |
---
### 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.** Pure `DmService` core behind an injectable transport seam; throwaway harness scene for the first on-screen prose. **Live-proven** against qwen3.5 through Docker (grounded scene prose, no numbers per §7). 67/67 client tests; merged to `dev` (`b949cd2`). Surfaced + fixed two Docker-dev gaps in M1 (prompts not packaged, no container→host-Ollama route) — M1's live proof had been venv-on-host only. *§2: state (client owns the loop, consumes text) · 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. AI-generated story skeletons (v2 — the seeded story is authored for now).