docs(charter+roadmap): pivot to the tactical CRPG (the POC is answered)

The aliveness POC (M0-M2) is proven, so redefine the project as the tactical
CRPG the /mockups describe. Charter: §1 reframed (Code of Conquest / The
Margreave), §10 combat = tactical + action-economy but GRIDLESS (tokens are
flavor, no positional rules), §15 = button-driven actions + free-text NPC
dialogue (Adjudicator reshaped to the optional free-text escape hatch), §16
adopts /mockups as the UI/systems bible, §17 rewritten to the game's scope.
Two conflicts flagged not silently decided (§18): damage determinism (§7 vs
mock ranges → M5) and class names (§8 vs mock callings → M4).

Roadmap: M0-M2 kept as the built engine; M3-M9 re-sequenced around the mock
screens (Theme+shell → creation → combat → dialogue → inventory/sheet → world
systems → framing/AI-flavor/persistence). Streaming stays shelved; v2 NPC
ideas preserved, re-homed to M6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 16:12:48 -05:00
parent 49bc6b22c0
commit 29474e5751
2 changed files with 108 additions and 63 deletions

View File

@@ -2,40 +2,40 @@
Source of truth for scope is [`CLAUDE.md`](../CLAUDE.md). This tracks sequencing.
## POC — the one question (charter §17)
## The game — building the tactical CRPG (charter §17)
> **Does bounded AI dialogue feel alive, or does it feel like a chatbot in a costume?**
> **The POC's one question — *does bounded AI dialogue feel alive?* — is answered.** Bounded NPC dialogue, validated moves, and the DM loop are built and live-proven (M0M2). We are now building the game the mockups describe (§16, `/mockups`): a tactical CRPG across 11 screens.
Test that before building anything else. ~30 minutes of play.
The engine is done and proven. What's left is the **game on top of it** — recreated screen by screen against the mockups' shared visual system, on the canon log / DMNPC pipeline / bounded-moves / Luck / character-creation model we already built.
### POC scope
### Reconcile as we build (charter §17)
- 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
- **Damage determinism** — §7/§10 (deterministic) vs the Combat HUD mock's damage ranges → combat brainstorm (M5).
- **Class names** — §8 names vs the mock's callings (same archetypes) → character-creation brainstorm (M4).
### Out of scope (charter §17)
### Out of scope (never / later)
AI-generated story skeletons · multiplayer (never) · mobile · credits/payments/auth · companions leaving · positional combat · more than three classes.
Multiplayer (never) · mobile (later) · AI-generated story skeletons (v2) · credits/payments/auth (retrofit onto the proxy, §4).
## 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:
M0M2 built the **engine** (the contract, the client, the server loop, bounded dialogue) and answered the aliveness question. M3+ build the **game** — the CRPG screens/systems of the mockups, sequenced toward a playable vertical slice first, then breadth:
| Milestone | Goal it proves |
| Milestone | Goal it advances |
|---|---|
| **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** ✅ | **Does bounded AI dialogue feel alive?**the whole POC question (§17). Build complete + live-proven; the *feel* is confirmed in playtest. |
| **M3 — Make it a game***here* | 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). |
| **M2 — Prove aliveness** ✅ | **Does bounded AI dialogue feel alive?**answered; bounded NPC dialogue + DM loop live-proven. |
| **M3 — Visual foundation & shell***here* | The `Theme` + the exploration HUD; every screen inherits the look and lives in the shell. |
| **M4 — Character creation** | The player enters the world; the proven stat/Luck model gets its UI. |
| **M5 — Tactical combat (gridless)** | Real stakes: the Combat HUD turn manager. |
| **M6 — Dialogue screen** | The proven NPC free-text loop inside the mock's conversation UI. |
| **M7 — Inventory & character sheet** | State the player can see and manage. |
| **M8 — World systems** | World-map travel, a shop economy, the quest log. |
| **M9 — Framing, AI-flavor & persistence** | Title/pause, save/load, and the Improviser/Banter/Luck-drift roles + fallback sweep. |
---
@@ -52,22 +52,44 @@ The milestones are ordered to reach the POC's one question as early as possible,
-**Considering-state + transport timeout** — while a model call is in flight the client rotates authored in-character "the Master considers…" lines (§13 spirit — even *loading* is content; `ConsideringPhrases` + `ConsideringIndicator`, 3.0s cadence) instead of a frozen surface, and `HttpTransport` sets a request timeout (35s, rejects ≤0) so a hung call degrades to the authored fallback instead of spinning forever. Meets §14's anti-freeze *goal* by **reframing** the wait, on the *proven* non-streaming loop, with no new failure surface. **Human-confirmed live** including the degrade path (API down → fallback). Merged to `dev` (`099714d`); 110/110 client tests. *§2: text/UI · goal: the wait is framed (in-character) and bounded (never hangs).*
- ~~**Streaming** (§14)~~ — **SHELVED for the POC.** Real token streaming would genuinely *hide* the ~2s call, but it is the POC's largest transport rework (client `HTTPRequest` → poll-based `HTTPClient`) and adds mid-stream error/tag-boundary failure surface not worth the risk. The considering-state above reframes the wait instead and is tested and live. Design kept as a record (`specs/2026-07-10-narrator-streaming-design.md`, banner SHELVED); revive only if playtest proves the wait genuinely hurts. Conscious deviation from §14's "stream everything," recorded per §18.
### M3 — Make it a game ◀ *here — (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.*
Each screen is recreated as a Godot 4.7 Control-node scene against the mockups (§16); each `.dc.html`'s `Component` class is the behavior spec. Sequenced toward a playable vertical slice (shell → creation → combat → dialogue) first, then breadth.
### M4POC 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.*
### M3Visual foundation & shell ◀ *here*
-**Shared `Theme`** — palette tokens, the three font families, and reusable styleboxes (parchment card, dark panel, primary CTA, tab, item tile, tag/chip) from the mock README as a Godot `Theme` resource. Every screen pulls from it; build it first so the look is one system. *§2: n/a (presentation) · depends on nothing · goal: one consistent visual identity.*
-**Main Window shell (2a)** — the exploration HUD: isometric world-view slot + the permanent DM "narration book" (wired to the proven `/dm/narrate` loop + considering-state), the turn-order rail, minimap slot, the slide-in system dock (Inventory/Character/Quest/Map/Party toggles), and the bottom command bar (HP/MP/gold, consumables, End Turn). The frame everything else lives in. *§2: state (client owns the HUD, consumes DM text) · depends on the DM loop (M2) · goal: the game's main screen, on screen.*
-**Title screen** — new game / continue / load / settings; the entry point. *§2: n/a · depends on the Theme · goal: first impression, into the world.*
### 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).
### M4 — Character creation
-**Character creation UI** — the mock's screen over our proven model: race + calling cards, six stats with **LCK hidden entirely** (§7), 3d6 floors, the **+3 additive-only spend-up** pool (§8, matches the `character-creation-stats` model), live origin blurb + nameplate. **Reconcile class names** here (§8 vs the mock's callings). Feeds new-game canon-log construction (already built). *§2: state · depends on the creation model (M0) + Theme · goal: build a character.*
### M5 — Tactical combat (gridless)
-**Combat turn manager + HUD** — the Combat HUD `Component` state machine as GDScript: initiative order, the action economy (Action/Bonus/Move), ability resolution, click-to-target, victory/defeat; tokens on the iso board as **flavor only, no movement grid** (§10). Seeded per encounter (§10). **Reconcile damage determinism** here (§7/§10 vs the mock's ranges). Combat flavor is async optional Narrator calls (reuses M1). *§2: state (flavor text a thin AI layer) · depends on the pipeline for flavor · goal: real stakes.*
### M6 — Dialogue screen
-**Dialogue UI + reshaped Adjudicator** — wrap the *proven* NPC free-text loop (`/npc/speak`, M2) in the mock's conversation screen: NPC portrait, live disposition meter, the NPC's spoken line + DM narration beat, and the **hybrid** input (choice chips with skill/approach tags **plus** the free-text line, §15). The **Adjudicator** (`/dm/adjudicate`, strict JSON §12, one-retry) lands here as the interpreter for the free-text "describe your own action" escape hatch — most turns use buttons. *§2: both (text = NPC voice · state = validated moves/actions) · depends on the NPC role (M2) · goal: conversation inside the game's UI.*
### M7 — Inventory & character sheet
-**Inventory & equipment** — the paper-doll (12 slots) + Satchel grid, category tabs, item detail, encumbrance, derived stats; item seed-data from the mock's `items()` map as `Resource`s. *§2: state · depends on the Theme + item model · goal: gear the player manages.*
-**Character sheet** — attributes (modifier-forward, LCK still hidden), saving throws, the "in a fight" derived grid, skills, talents, afflictions/conditions, the DM's-notes bio. *§2: state (display) · depends on the stat model · goal: the character, legible.*
### M8 — World systems
-**World-map travel** — region map, location markers (settlement/dungeon/fog), roads + planned route, danger ratings, travel time, set-out. *§2: state · depends on the shell · goal: move between places.*
-**Shop economy** — buy/sell with a fence: purses, stock/owned, the ~40%-sell loop, gritty disabled-CTA reasons, merchant reaction lines. *§2: state (merchant reaction = a thin authored/AI layer) · depends on inventory · goal: an economy loop.*
-**Quest log** — active/completed/failed tabs, objectives checklists, rewards, tracked quest; reads the canon log's `active_quests` (§11). *§2: state · depends on the quest model · goal: track the story.*
### M9 — Framing, AI-flavor & persistence
-**Save / load + pause / settings** — the pause overlay + data-driven settings; save/load over the canon log + game state (seeds preserved, §10). *§2: state · depends on the systems existing · goal: persistence + framing.*
-**Luck fully wired** — generation exists (Plan B); this adds **drift events** (real bed, shrine blessing, killing something praying), **one shrine**, **one cursed item** (+STR / LCK). Visible only in prose (§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** — a validator that only applies whitelisted state changes (dignity, never progress; structurally incapable of ending a quest). Fires on Luck; hooks Cadwyn. *§2: both · 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. *§2: text · depends on the humiliation log (M0) + events · goal: the comedy loop closes.*
-**Authored-fallback sweep** — every AI-dependent surface has authored fallback *content* (§13), written as writing; the final pass so no surface can show the player an error. *§2: text · depends on every surface existing · goal: a degraded DM still has a voice.*
### Deferred (later / never)
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). Streaming (§14) — SHELVED (`specs/2026-07-10-narrator-streaming-design.md`); the considering-state reframes the wait instead.
#### NPC & relationship depth (v2)
Three ideas mined from [NarrativeEngine-P](https://github.com/Sagesheep/NarrativeEngine-P) (MIT) — design borrowed, not code. All three are **§2: state** (code owns them; the AI only voices from what code decides), so they strengthen the §2 boundary rather than loosen it. None is in POC scope (§17); they fold into the NPC role (§6) and canon log (§11) once bounded dialogue is proven alive. Sequence them after M3's Adjudicator, since they enrich the same conversation surface.
Three ideas mined from [NarrativeEngine-P](https://github.com/Sagesheep/NarrativeEngine-P) (MIT) — design borrowed, not code. All three are **§2: state** (code owns them; the AI only voices from what code decides), so they strengthen the §2 boundary rather than loosen it. They fold into the NPC role (§6) and canon log (§11); sequence them **with or after the Dialogue screen (M6)**, since they enrich the same conversation surface.
-**Directional dispositions** — split the single per-pair disposition into two directed fields (A→B and B→A), so liking is asymmetric. Unrequited regard, undisclosed betrayal (player still trusts the captain who already sold them out), and the Cadwyn↔Brannoc mutual-but-differently-flavored dislike (§9) all become expressible. Player→NPC gates dialogue *tone*; NPC→player gates *offered moves* (§6 `available_moves`) — two arrows, two levers. Extends `party[]` in the canon log (§11) and `npc_dispositions` on the client; keep the 100..100 range, just double the field. *§2: state · depends on the disposition model existing (M2, done) · goal: relationships that aren't symmetric.*
-**Witness tracking** — every logged event carries a `witnesses: [...]` list; a fact enters an NPC's `knowledge[]` (§6) only if they witnessed it or a witness told them. This is the missing **input-side rule** for the knowledge list — CLAUDE.md §6 says the list is "the entire content the NPC has to draw on" but never says how a fact gets *in*. Structurally prevents the blacksmith spoiling a twist he never saw, gives shady acts real "were you seen?" stakes without a stealth system, and turns rumor spread into a code operation (witness meets NPC → code copies the fact, optionally degraded) rather than AI invention. Adds a field to `recent_events[]`/`established_facts[]` (§11) and a propagation rule on character encounters. *§2: state · depends on the canon log event model (M0, done) + knowledge lists (§6) · goal: NPCs know only what they could plausibly know.*