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

@@ -8,13 +8,13 @@ Read this before writing code. When a request conflicts with something written h
## 1. What this is
A single-player, party-based fantasy RPG. Combat is deterministic JRPG-style. The story skeleton is authored; the *prose* is generated. An AI acts as Dungeon Master — narrating scenes, voicing NPCs, and improvising minor events — while all game state lives in code.
**Code of Conquest** — a single-player, party-based **tactical CRPG** in a gritty high-fantasy world ("The Margreave"). Turn-based, D&D-flavored combat is deterministic (§7/§10). The story skeleton is authored; the *prose* is generated. An AI acts as Dungeon Master — narrating scenes, voicing NPCs, and improvising minor events — while all game state lives in code.
**Not multiplayer.** Not now, not later. Companions are code-controlled NPCs. Do not introduce networking, lockstep, or authority abstractions "in case."
**Target:** Desktop first (Windows/Linux). Mobile is a later consideration and should not constrain POC decisions.
**Target:** Desktop first (Windows/Linux), **1920×1080 (16:9), Godot 4.7.** Mobile is a later consideration and should not constrain design.
**Current phase:** POC. The question the POC answers is *does bounded AI dialogue feel alive, or does it feel like a chatbot in a costume?* Everything else is scaffolding for that experiment.
**Phase.** The original POC question — *does bounded AI dialogue feel alive, or a chatbot in a costume?* — is **answered.** Bounded NPC dialogue, validated moves, and the DM loop are built and live-proven (M0M2). The project is now **building the game the mockups describe** (§16, `/mockups`): the tactical CRPG across the 11 screens of that UI bible. The AI's job is unchanged — narrate, voice NPCs, improvise — and it never owns state.
---
@@ -348,15 +348,16 @@ Single integer, 100..100, per companion. Adjusted by player choices weighed a
## 10. Combat
**Positionless, turn-based.** No grid. No flanking. No terrain.
**Tactical, turn-based, gridless.** Turn order by initiative; an **action economy** (Action · Bonus · Move) per turn; abilities picked from a bar; enemies targeted by click. AC, HP, and initiative in the D&D-flavored idiom of the Combat HUD mockup (§16).
Combat is not the risk surface of this project. Ship the boring one; prove the interesting one.
**No real movement grid.** The isometric board and tokens are *visual flavor* — there are no positional rules, no flanking, no terrain, no reachable-tile movement. Positioning was the risk surface the old charter warned against; it stays out. The tactics live in the **action economy and resources**, not the map.
- **Damage is deterministic.** No variance rolls on damage.
- **The player controls only their own character.** The DM resolves the AI companions' and enemies' turns.
- **Damage is deterministic (§7). OPEN:** the Combat HUD mock rolls damage from a range, but §7 forbids damage variance so Luck can *select* outcomes, not fudge numbers. Reconcile at the combat brainstorm — either fixed damage with Luck governing crit thresholds + borderline status checks, or a conscious, written change to §7. Do **not** silently adopt damage ranges.
- **Initiative is rolled once at encounter start** and is relatively random. DEX modifies.
- **The interesting decision is resource management** — MP, cooldowns, consumables. Not matchup-reading. Pick one axis for the POC.
- **Resources are the interesting axis** — the action economy, cooldowns, consumables. Not matchup-reading.
- **Luck influences crit thresholds and borderline status checks.** Nothing else. See §7.
- **Combat flavor text is async and optional.** The numbers land immediately. The prose catches up. If the AI call fails, the fight continues with authored text.
- **Combat flavor text is async and optional.** The numbers land immediately; the prose (a Narrator/DM call) catches up. If the AI call fails, the fight continues with authored text.
### Determinism and seeding
@@ -366,6 +367,8 @@ This costs nothing now and is nearly impossible to retrofit. It is the differenc
Log the seed *and* the full prompt with every AI call, for the same reason.
The Combat HUD mock's `Component` state machine (initiative order, action economy, ability resolution, victory/defeat) is the reference spec for the GDScript turn manager.
---
## 11. The canon log
@@ -454,14 +457,14 @@ Roughly 13k tokens per call, dozens of calls per session.
## 15. Input model
**Hybrid.**
**Button-driven actions; free-text dialogue.**
- **Menus:** combat, navigation, inventory.
- **Free text:** dialogue, and explicit "what do you do?" moments.
- **Buttons / menus:** combat abilities, world actions, navigation, inventory, shop. The player picks from **legal options code presents** — no input can produce an illegal or absurd action ("I triple-backflip off the wall and roundhouse him" never reaches the rules). Bulletproof by construction.
- **Free text:** **NPC dialogue** — the aliveness surface, kept free-text (bounded by the move vocabulary §6, so wild input is harmless). Plus an optional **"describe your own action"** line on the main window (2a mock) for the tabletop feel.
**The boundary must be visible.** The player should always know which mode they are in. Ambiguity here reads as broken.
**The boundary must be visible.** The player should always know which mode they are in.
Free text goes to the Adjudicator, which maps it to a legal action or rejects it with an in-world reason.
**The Adjudicator (§5) is reshaped.** Its primary job is no longer parsing free text into a world action — code presents legal actions as buttons. It survives only as the interpreter for the *optional* free-text "describe your own action" escape hatch: free text → a legal action or an in-world rejection. Most turns never touch it.
---
@@ -470,7 +473,7 @@ Free text goes to the Adjudicator, which maps it to a legal action or rejects it
- **Client:** Godot 4, **GDScript.** Prefer readability. Drop to C# only if GDScript genuinely cannot do the thing — and say why before you do.
- **Proxy:** Python, **FastAPI.**
- **Models:** Ollama (self-hosted, dev/staging) → Replicate (prod). Routed per-role, server-side.
- **Repo:** greenfield. No existing code.
- **Repo:** the engine (M0M2) is built — canon-log contract + client engine, the server model-call pipeline, `/dm/narrate` + `/npc/speak`, bounded moves, considering-state. Build the game on top; follow the existing patterns.
### Layout
@@ -481,9 +484,23 @@ Free text goes to the Adjudicator, which maps it to a legal action or rejects it
/docs API-specific docs
/prompts Role prompts (versioned, reviewed like code)
/content Authored: quests, NPC knowledge lists, fallback text
/mockups The UI/systems bible — 11 hi-fi HTML/CSS/JS screen refs + README
/docs Cross-cutting docs — roadmap, ADRs, anything affecting both sides
```
**`/mockups` is the UI + systems bible.** Eleven high-fidelity HTML/CSS/JS screen
references (title, character creation, character sheet, inventory, world map,
quest log, shop, pause/settings, dialogue, combat HUD, main exploration window)
plus a `README.md` handoff. **Not production code** — each screen is recreated as
a **Godot 4.7 Control-node scene** pulling from a shared **`Theme`** resource
(the README's palette, three fonts, and reusable styleboxes keep the look one
system). Each `.dc.html`'s `Component` class is the **behavior spec** — the
combat turn manager, dialogue graph, shop economy, and point-buy creation are the
highest-value logic to port; the seed-data maps (items, quests, locations, units,
abilities) are starting `Resource`s. Dashed "art slots" are placeholders for real
art. When the mock and this charter disagree, the disagreement is a decision to
make, not a default to follow — see §17's reconcile list.
`/api` is still the proxy described in §4 — auth, metering, prompt-hiding, model
routing. It is named `api` for the client's convenience, not because it is a
generic pass-through. Docs are split: side-specific docs live under
@@ -494,33 +511,39 @@ Prompts are source code. They get reviewed. They get versioned. Changing a promp
---
## 17. POC scope
## 17. Scope — the game (the POC is done)
**~30 minutes of play.**
The aliveness POC is **answered** (M0M2, live-proven). The target is now the
tactical CRPG in the mockups (§16): the 11 screens and the systems they imply —
character creation, the exploration shell + DM narration, tactical (gridless)
combat, free-text NPC dialogue, inventory/equipment, character sheet, world-map
travel, a shop economy, a quest log, title/pause.
- 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
Build it **screen by screen** against the mockups' shared visual system, on the
proven engine (canon log, DM/NPC pipeline, bounded moves, Luck, character-creation
model — all already aligned with the mocks). Each screen/system is its own
**spec → plan → implementation** cycle; the roadmap sequences them toward a
playable vertical slice first, then breadth.
### The POC's job
**Unchanged and load-bearing:** §2 (code owns state, AI owns text) — a tactical
CRPG has *far* more state, all code-owned; the AI stays narrator/DM/NPC-voice.
Also §3 (tone), §7 (Luck), §9 (companions), the canon log (§11), the output
contracts (§12), and Failure UX (§13).
Answer one question: **does bounded AI dialogue feel alive, or does it feel like a chatbot in a costume?**
### To reconcile as we build (flagged, not silently decided)
Test that before building anything else. Every other system exists to make that test possible.
- **Damage determinism** — §7/§10's deterministic damage vs the Combat HUD mock's
damage *ranges* (settle at the combat brainstorm).
- **Class names** — §8 uses Sellsword/Assassin/Priest/Scholar/Oathbound/Tracker/
Berserker; the Character-Creation mock uses callings Sellsword/Cutpurse/
Hedge-Mage/Bonesetter/Trapper (same archetypes + primary stats, different
names). Settle at the character-creation brainstorm.
### Explicitly out of scope
### Still out of scope (never / later)
- AI-generated story skeletons (v2 — the seeded story is authored for now)
- Multiplayer (never)
- Mobile
- Credits, payments, auth
- Companions leaving the party
- Positional combat
- More than three classes
- Multiplayer (**never**) · mobile (later) · AI-generated story skeletons (v2 —
the seeded story is authored for now) · credits/payments/auth (retrofit onto
the proxy later, §4).
---

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.*