From 16bc87c590bc13a296da4c2b45ef270173aa01fc Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 19 Feb 2026 19:52:50 +0100 Subject: [PATCH] =?UTF-8?q?docs(sprints):=20add=20Sprint=2014=20"Live"=20b?= =?UTF-8?q?riefings=20=E2=80=94=2022=20tickets=20across=204=20teams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NPC mood, trust, routine execution, NPC-to-NPC conversations with D-078 occlusion, monologue display, content line pools, and visual specs. Teams: server (7), client (3), copy (6), visual (6). Co-Authored-By: Claude Opus 4.6 --- docs/sprints/sprint-14/client.md | 70 ++++++++++++++++++++ docs/sprints/sprint-14/copy.md | 89 +++++++++++++++++++++++++ docs/sprints/sprint-14/joint.md | 107 +++++++++++++++++++++++++++++++ docs/sprints/sprint-14/server.md | 82 +++++++++++++++++++++++ docs/sprints/sprint-14/visual.md | 101 +++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+) create mode 100644 docs/sprints/sprint-14/client.md create mode 100644 docs/sprints/sprint-14/copy.md create mode 100644 docs/sprints/sprint-14/joint.md create mode 100644 docs/sprints/sprint-14/server.md create mode 100644 docs/sprints/sprint-14/visual.md diff --git a/docs/sprints/sprint-14/client.md b/docs/sprints/sprint-14/client.md new file mode 100644 index 000000000..000f55836 --- /dev/null +++ b/docs/sprints/sprint-14/client.md @@ -0,0 +1,70 @@ +# Sprint 14: Live — Client Tasks + +**Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. + +**Branch:** `client` +**Agents:** Stig (UI/rendering dev), Tyre (architect), Hoshe (QA) + +## Carry-over from Sprint 13 + +None. All Sprint 13 client tickets done. + +## New Tickets + +| # | Title | Blocked by | +|---|-------|------------| +| #122 | Monologue display — client | — | +| #535 | Passive dialogue panel — overheard NPC conversation display | #247 (server) | +| #511 | F3 debug overlay (deferred — WRONG button covers same data) | #495 done | + +Use `db/connectors/ticket show ` for full details. + +## Key Decisions + +- `decisions/architecture.md` — D-020 (ObserverSnapshot — `current_monologue` field), D-042 (UI microcopy format — YAML autoload for HUD labels) +- `decisions/perception.md` — D-016 (internal monologue — text display, atmosphere function), D-018 (three-range sound model — NPC-to-NPC conversations are Voice events), D-055 (sprint suppresses monologue at 40% rate — client renders what server sends; no client-side suppression needed) +- `decisions/content.md` — D-032 (separate monologue pools per character — client only needs to render; no character branching in display layer), D-035 (tag taxonomy — monologue has `character`, `trigger`, `prerequisite` tags; client ignores, server filters) +- D-078 (overheard NPC conversation — passive dialogue panel, server-authoritative occlusion, pre-occluded text rendering, speaker attribution) + +## Notes + +- **#122 — Monologue display — client:** The server-side monologue trigger system is complete — `MonologueEvent` arrives in `ObserverSnapshot.current_monologue` (field added Sprint 5, `server/src/bridge/types.rs`). The visual spec for monologue display was authored in Sprint 13 (#315, `docs/design/monologue-display-spec.md`). Sprint 14 work: implement the client-side display using that spec. Key requirements from the spec: + - **Position:** Bottom-left of viewport, above the interaction verb list, below the minimap. Not in the dialogue box area (max 20% height, D-061). + - **Typography:** Italic, smaller than dialogue (spec has exact px/pt — read `docs/design/monologue-display-spec.md` §Typography). + - **Stacking:** Queue — if a new monologue arrives while one is displaying, queue it (max depth from spec). Do not overwrite mid-display. Sprint 13 note: "monologue not lost on overwrite" is a P0 test (#477, done). + - **Fade:** Auto-fade after display duration. Duration is embedded in `MonologueEvent.display_duration` (float, seconds). Standard Godot `Tween`. + - **Character differentiation:** Smuggler and detective lines use different text colours — hex values in spec. Client reads the active character from `game_state.gd` (autoload at `client/scripts/autoloads/game_state.gd`) or derives it from the snapshot. The server already partitions monologue pools per character (D-032) so the client receives only the correct character's lines — no client-side branching needed. + - Existing `world_renderer.gd` (`client/scripts/rendering/world_renderer.gd`) is the root scene; monologue display is a new `CanvasLayer` child node or a `Control` node in the HUD layer (z-layer 6 per D-049 rendering stack). Do not put it in the dialogue box scene. + - **Integration with audio:** Monologue display does NOT trigger audio (monologue chime fires at cognitive delay onset from the server's recognition pipeline, not from text display). No client-side audio calls needed here. + - Delivery: `client/scripts/rendering/monologue_display.gd` (or similar), integrated into the HUD scene, reading `ObserverSnapshot.current_monologue`. Tests via Hoshe's gdUnit4 harness: queue management, fade-out timing, character colour, no-overwrite on active display. + +- **#535 — Passive dialogue panel — overheard NPC conversation display:** Consumes `ConversationEvent` from `ObserverSnapshot` (added by server #247). Render overheard NPC-to-NPC conversations in the existing dialogue box in passive (read-only) mode. Per D-078 amendment — occlusion is server-authoritative; this ticket is a pure renderer of pre-occluded text. Key requirements: + - **Speaker attribution:** Display header as "Speaker → Target" (e.g. "Kael → Mira") using `speaker_name` and `target_name` from the event. Use the same attribution label slot as player conversation NPC-name display — no new layout region needed. + - **Pre-occluded text renderer:** Render `occluded_line` as-is. The server has already performed per-word Bernoulli drops and replaced dropped words with `...`. The client performs no stochastic logic — no `clarity_score`, no `randf()`, no per-word processing. Display the received string directly in the passive panel. + - **Read-only mode:** The dialogue box must suppress response options and player input when displaying a passive conversation. No response list, no input capture. Player can still move freely. + - **Dismissal:** On `conversation_end` event (or if player moves far enough that no `ConversationEvent` arrives for the NPC pair), dismiss via the standard 300ms fade (same `Tween` as walk-away fade, #437 done). + - **Monologue isolation:** Monologue display (#122, z-layer 6) is unaffected. Do not route conversation text through the monologue display. The server triggers `witness_interaction` which may produce a monologue — that arrives separately via `current_monologue` and is rendered by #122 normally. + - **Blocked by #247** — `ConversationEvent` struct must exist on `ObserverSnapshot` before this can be wired. Start with a stub event type and placeholder renderer if #247 is not yet landed. + - Delivery: passive mode in `client/scripts/ui/dialogue_box.gd` (or a new `passive_dialogue.gd` sibling), verbatim `occluded_line` render, speaker attribution header, `conversation_end` dismissal. Tests via Hoshe's gdUnit4 harness: attribution renders correctly, `occluded_line` with `...` tokens renders verbatim, panel does not show response options in passive mode, dismisses on `conversation_end`. + +- **#511 — F3 debug overlay (deferred):** This ticket is low priority. The WRONG button (F12, #507 done) already captures snapshot state, text render, and description on demand without per-frame cost. The F3 overlay would duplicate that information as a persistent overlay. Stig recommended deferral in Sprint 12. Only implement this sprint if server tickets finish early and QA explicitly needs real-time overlay data that WRONG button cannot provide. Default: skip. + +## Dependency Chain + +``` +#122 (monologue display) — standalone, no server dependency this sprint +#535 (passive dialogue panel) — blocked by #247 (server, NPC-to-NPC conversation system) +#511 (F3 overlay) — standalone, low priority, implement only if capacity allows +``` + +#122 has no blockers — `current_monologue` in the snapshot has been live since Sprint 5. The spec (#315) is done. This is a pure client rendering task. + +#535 is blocked on #247 landing the `ConversationEvent` struct with `occluded_line: String` on `ObserverSnapshot`. Stig can stub the event type locally and build the renderer against it in parallel; wire-up follows when #247 merges. No stochastic logic needed on the client — render `occluded_line` verbatim. + +## PR Workflow + +When ready to submit, create a PR with `tea` CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section): + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(client): description" --description "body" --base main --head client +``` diff --git a/docs/sprints/sprint-14/copy.md b/docs/sprints/sprint-14/copy.md new file mode 100644 index 000000000..9e7a5798b --- /dev/null +++ b/docs/sprints/sprint-14/copy.md @@ -0,0 +1,89 @@ +# Sprint 14: Live — Copy Tasks + +**Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. + +**Branch:** `copy` +**Agents:** Mellanie (author), Paula (narrative designer), Gestalt (systems designer) + +## Carry-over from Sprint 13 + +None. All Sprint 13 copy tickets done (#368 knowledge vocabulary). + +## New Tickets + +| # | Title | Blocked by | +|---|-------|------------| +| #253 | Monologue content architecture | — | +| #120 | Monologue line pool system | — | +| #121 | Character voice variation | — | +| #168 | Tagged line pool structure | #240 done | +| #328 | Access tier shift design document | #261 done | +| #536 | NPC-to-NPC overheard dialogue line pool | — | + +Use `db/connectors/ticket show ` for full details. + +## Key Decisions + +- `decisions/content.md` — D-028 (dialogue architecture — four relational layers; #168 implements Layer 1-2 tag structure), D-032 (separate monologue pools per character — hard partition, no shared lines), D-034 (THE FRIEND — Kael Davan / Sera Venn, all monologue content must be coherent with their arcs), D-035 (converged tag taxonomy — 6 structural + 3 selection + 3 monologue-specific tags), D-036 (Sova Transit District setting — Krenn naming conventions, "functional warmth" tone), D-037 (contraband — unlicensed lattice components, moral ambiguity), D-062 (invisible locked dialogue options — new trust tier content appears silently), D-063 (confrontation mechanics — internal voice, italic first-person options), D-064 (walk-away consequences — KG records incompleteness) +- `decisions/perception.md` — D-016 (internal monologue — four functions: perception bridge, atmosphere, diegetic tutorial, unreliable narrator), D-018 (three-range sound model — NPC-to-NPC conversations are Voice events the player overhears) +- D-078 (overheard NPC conversation — lines must survive partial occlusion; front-load key information, short declarative sentences, avoid pronoun-first openers) + +## Notes + +- **#253 — Monologue content architecture:** Define the authoring contract for all future monologue content. This is a **design document**, not a content file. Output: `docs/design/monologue-content-architecture.md`. Required sections: + - Identity-driven interpretation frame: what each character "cares about, fears, is responsible for" — the interpretive lens that makes the same observation produce different monologue per character. Smuggler: exposure, protection of ring, Kael's loyalty. Detective: institutional duty, personal cost, Sera Venn's reliability. + - Category definitions: perception (translating non-visual senses), atmosphere (environment, mood), tutorial (diegetic hints), observation (NPC-specific). Add a fifth if needed. + - Trigger → category mapping: which triggers (from D-035: `enter_location`, `observe_npc`, `hear_sound`, `observe_anomaly`, `post_conversation`, `discover_evidence`, `witness_interaction`, `time_idle`, `return_visit`) map to which categories. Not 1:1 — one trigger can source multiple categories. + - Volume target: how many lines per trigger type per character for v0.1. Per D-032, all volumes are per-character. + - `prerequisite` map design: what game state conditions gate a line. Examples: `kael_interaction_count >= 1`, `knows_kael_has_secret = true`, `location = maintenance_corridor`. These must be expressible in the KG (D-041 FactIds). + - Mellanie authors, Paula reviews for narrative coherence with THE FRIEND arc (D-034), Gestalt validates prerequisite map is implementable from KG state. + +- **#120 — Monologue line pool system:** Produce the first monologue line pool YAML files. Output: `content/` directory (follow existing content structure — see `server/src/content/mod.rs` and `server/src/content/loader.rs` for load path conventions). File structure per D-032: `monologue-smuggler.yaml`, `monologue-detective.yaml`, with subdirectory per location (`terminal/`, `bar/`, `corridor/`). Required tags per line per D-035 (monologue-specific additions): `character`, `trigger`, `prerequisite` (null if unconditional). All 6 structural tags required: `id`, `text`, `role`, `access`, `trust`, `situation`. For monologue, `role` = `player_character`, `access` = `[public]`, `trust` = `surface` (monologue is the player character's internal voice — no access/trust gating applies; tags must still be present for schema compliance). + - Minimum deliverable for Sprint 14: `enter_location` (2-3 lines per location per character) and `time_idle` (2-3 lines per location per character). Eight locations minimum (hub, bar, corridor × 2 characters = 6; add 2 more for the maintenance corridors or gauntlet-equivalent spaces). + - Line quality bar: these are the first lines the game speaks. Every line must pass Mellanie's voice test. Per D-034 THE FRIEND guidance: "no generation expansion" for THE FRIEND content specifically — apply same standard to monologue that will surface during THE FRIEND arc (Kael/Sera scenes). + +- **#121 — Character voice variation:** Write the trait modifier system for monologue voice. This is a **companion authoring guide** to #253, not a separate system. Output: `docs/design/monologue-voice-guide.md`. Required: + - Smuggler vs detective base voice registers (tone, vocabulary, sentence structure). + - How `PersonalityTraits` (D-024 — `Cautious`, `Bold`, `Honest`, `Deceptive`, `Compassionate`, `Ruthless`, `Curious`, `Incurious`, `Social`, `Reclusive`) modify monologue delivery. Not per-trait scripts — a transformation guide: "a Cautious character says X instead of Y" with 2-3 example rewrites. + - Background-dependent phrasing: Guardian background vs Senator background vs Worker background produce different idioms, references, assumptions. At minimum: two examples per background per character. + - Mood influence (feeds into #323 server-side): how the 8+1 moods (D-035 amendment: `neutral`, `anxious`, `frustrated`, `content`, `suspicious`, `warm`, `hostile`, `focused`) colour monologue delivery. Not separate line pools — the same line can be delivered more tersely when the character is Anxious, more expansively when Content. + - Paula writes the narrative framing, Mellanie writes the example lines, Gestalt validates mood → delivery mappings are mechanically coherent. + +- **#168 — Tagged line pool structure:** Implement the converged D-035 YAML schema as a formal JSON Schema or YAML schema document and update the line previewer CLI. Output: `content/_schema/dialogue-line.schema.json` (or `.yaml`) with all 6 structural + 3 selection + 2 authoring-only tags. Monologue additions (`character`, `trigger`, `prerequisite`) as a separate schema extension or `$ref`. The schema is what the content cross-reference validation (CI ticket #464, done) runs against. Update the line previewer CLI (Sprint 5 `#326`, already done) to validate against the new schema on load. Gestalt leads schema design. Mellanie validates against authoring workflow. The 14 v0.1 situations and 9 v0.1 topics from D-035 must be enumerated in the schema as allowed values. + - Note: `server/src/content/line_pool.rs` already implements the Rust-side `IndexedDialogueLine` type — schema must stay in sync. Cross-reference `server/src/content/line_pool.rs` to confirm enum variants match D-035 enumeration. + +- **#328 — Access tier shift design document:** A design document authored by Paula and Mellanie (no code). Output: `docs/design/access-tier-shifts.md`. Required per ticket: + - Per social site, 2-3 most likely tier transitions per character with specific triggers. + - Smuggler at The Terminal (hub): `insider → hostile` when cover blown; `insider → peer` after routine trust-building. + - Smuggler at The Last Shift (bar): `public → insider` after Kael introduction; `insider → hostile` if contraband conversation overheard. + - Detective at The Terminal: `authority → peer` after collaboration event; `authority → hostile` if confrontation fails. + - Detective at The Last Shift: `public → insider` via Sera Venn; `authority → peer` after buying rounds. + - For each transition: what observable event triggers it, which KG facts are required (`FactId` references from #368 knowledge vocabulary), and whether it is reversible. + - This document feeds #169 (Layer 1 access tier filtering, deferred to S15) as its design input. + +- **#536 — NPC-to-NPC overheard dialogue line pool:** Write the line pool for NPC-to-NPC conversations that the player can overhear, per D-078. Lines must work at both full clarity (heard normally) and under heavy occlusion (many words dropped). The stochastic word-drop renderer operates per-word — lines must be authored so that partial information is still meaningful and interesting, not just broken. + - **Occlusion-resilient authoring rules:** (1) Front-load key information — the most important word should be in the first third of the sentence. (2) Short declarative sentences — one idea per line turn. (3) No pronoun-first openers (`"She told me..."` → `"Kael told me..."`) — the first word is statistically the most likely to drop; pronouns without antecedent are unresolvable. (4) Each exchange turn must be self-contained — a player who hears only one side of the exchange should still get a complete thought. + - **Volume and tagging:** Minimum 40 exchange pairs (speaker A line + speaker B response = 1 pair). Cover three registers: social (idle chat, personal news), work (shift logistics, job gripes), gossip (third-party knowledge payload — something the player could use). Tag each pair with: `relationship_type` (colleague, friend, hostile, romantic), `topic`, and `knowledge_payload` (a brief description of what the player can infer from hearing this exchange clearly — or partially). + - **Format:** Use the D-035 YAML schema (see #168 for schema file). These are NPC-sourced lines, not player-character lines — `role` = `npc`, `access` and `trust` tags apply normally. Monologue-specific tags (`character`, `trigger`, `prerequisite`) are not used for this pool; replace with `relationship_type` and `knowledge_payload` as custom extensions, or confirm schema handling with Gestalt before writing. + - **Placement:** Output to `content/npc-conversations/overheard.yaml` (new directory, establish convention). + - Mellanie writes lines, Paula validates narrative coherence (Kael/Sera-adjacent exchanges must honour D-034 THE FRIEND arc), Gestalt validates knowledge_payload tags are achievable from KG state. + - Delivery: `content/npc-conversations/overheard.yaml`, minimum 40 exchange pairs, all tagged, all passing the occlusion-resilience checklist. No dependency on server #247 — content is authored ahead of the system that will select and emit it. + +## Dependency Chain + +``` +#253 (monologue architecture) → #120 (line pool files) → #121 (voice guide) +#168 (tagged line pool schema) → parallel with #253 track +#328 (access tier shifts) → parallel, standalone design doc +#536 (NPC-to-NPC overheard line pool) → parallel, standalone, feeds server #247 +``` + +#253 must be started first — it defines the authoring contract that #120 writes to and #121 extends. #120 and #121 can overlap once #253 is drafted (Paula/Mellanie can start voice guide while Mellanie writes lines). #168 and #328 are fully independent and can proceed in parallel with the monologue track. #536 is also fully independent — no blockers, can proceed in parallel with all other copy work. + +## PR Workflow + +When ready to submit, create a PR with `tea` CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section): + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "docs(content): description" --description "body" --base main --head copy +``` diff --git a/docs/sprints/sprint-14/joint.md b/docs/sprints/sprint-14/joint.md new file mode 100644 index 000000000..aacaa6350 --- /dev/null +++ b/docs/sprints/sprint-14/joint.md @@ -0,0 +1,107 @@ +# Sprint 14: Live — Joint / Integration Notes + +**Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. + +**Sprint 14 ID:** 14 +**Status:** planning (activate with `db/connectors/sprint start`) + +--- + +## Pre-Sprint Decisions + +The following decisions are implemented or directly exercised this sprint and must be cross-referenced by the relevant team: + +| Decision | Domain | Implementing ticket(s) | +|----------|--------|----------------------| +| D-024 (10-axis NPC model + combat component) | content | #323 (mood), #324 (trust), #325 (interaction tracking), #101 (routine) | +| D-026 (simulation tiers — ActiveSim scope for behavior) | architecture | #323, #101, #247 | +| D-028 (dialogue — four relational layers) | content | #325 (Layer 2 data), #324 (Layer 3 data), #168 (schema) | +| D-031 (game clock / day phases — routine transitions) | architecture | #101 | +| D-032 (separate monologue pools per character) | content | #120, #121, #122 | +| D-034 (THE FRIEND — Kael Davan / Sera Venn) | content | #318 (visual spec), #253 (monologue architecture) | +| D-035 (converged tag taxonomy) | content | #168 (schema implementation) | +| D-041 (knowledge graph — trust co-gate) | architecture | #324, #325 | +| D-075 (dialogue filtering — trust + confidence co-gate) | content | #324 (trust progression feeds `relationship_to_trust()`) | + +--- + +## Cross-Team Dependencies + +| Dependency | From | To | Risk | +|------------|------|----|------| +| #323 (Mood component) | server | copy #253 (monologue architecture must describe mood influence) | Low — copy doc can reference Mood enum; server implements | +| #325 (InteractionMemory) | server | copy #328 (access tier shifts doc references interaction count as trigger) | Low — spec can be written with intent; server implements | +| #324 (trust progression) | server | copy #121 (voice guide references mood/trust for line selection) | Low — parallel, doc references trust tier conceptually | +| #304 (entity color spec) | visual | client (future sprint — entity_renderer.gd implementation) | Low — spec must be complete before client implements | +| #316 (text display hierarchy) | visual | client #122 (monologue display references hierarchy for positioning) | Low-Medium — Stig should read #316 spec before completing #122 layout | +| #251 (tell visual/behavioral spec) | visual | server (future sprint — tell simulation behavior) | Low — server implements behavior from Araminta's spec next sprint | + +**Coordination protocol:** +- Visual team (#304 first): Start entity color spec before #318 (THE FRIEND visual cross-references it). Signal to joint channel when #304 is drafted. +- Client team (#122): Read the Sprint 13 monologue display spec (`docs/design/monologue-display-spec.md`) before implementing. Also read #316 once Araminta drafts it — consult if positioning conflicts arise. Default to #315 spec if #316 is not ready. +- Server team (#323 first): Mood component is the most downstream dependency for copy and visual. Draft the `Mood` enum in `server/src/npc/mood.rs` early — copy team needs the enum values for #253 authoring contract and #121 voice guide. +- Copy team (#253 first within copy track): Monologue architecture doc gates #120 and partially gates #121. Start it day one. + +--- + +## Sprint Completion Proof + +When Sprint 14 is done, the following is concretely observable: + +1. **NPCs have moods:** Query the ECS world during a gauntlet run — Active-tier NPCs have a `Mood` component with a non-Neutral value after sufficient simulation ticks. The `Mood` transitions when `ToleranceThreshold.current_stress` exceeds threshold, or when day phase changes. + +2. **Trust accumulates:** Talk to the same NPC three times in sequence. After each interaction, `RelationshipGraph.get_relationship(player_id, npc_id).trust` increments. Walk away mid-conversation (D-064 mechanic) — trust decrements. Trust values are deterministic across replays with the same input sequence. + +3. **Routine execution closes the loop:** NPCs in the Active tier move to their routine destination on day-phase transition. After `Morning → Afternoon`, NPCs with an Afternoon `location` in their `DailyRoutine` have an active `PathRequest` issued and eventually reach the target tile. The `ActivityState` component reflects the current routine activity string. + +4. **NPC-to-NPC conversations generate sound:** Two NPCs in the same zone within 3 tiles emit a `SoundEvent` of kind `Voice` on the `ObserverSnapshot.sound_events` field. The client's `sound_indicator_renderer.gd` renders a voice-coloured (#e8c547) pulse at the fog edge in the direction of the conversation. The `AudioManager` plays the murmur asset on the WorldSFX bus (already wired Sprint 13). + +5. **Monologue displays in client:** Launch the game. Walk into a new room. Within 2 ticks, a monologue line appears in the bottom-left HUD area in italic text. Wait idle for ~100 ticks — a `time_idle` line appears. Lines fade after their display duration. No line is lost — if a new line arrives while one is showing, it queues. Character-specific colour is applied. + +6. **Monologue content pools exist:** `content/` directory contains `monologue-smuggler.yaml` and `monologue-detective.yaml` files with at least 8 lines each covering `enter_location` and `time_idle` triggers for at least two locations. All lines validate against the D-035 schema in `content/_schema/dialogue-line.schema.json`. + +7. **Tagged line pool schema is authoritative:** Running `make pre-pr` (CI cross-reference validation, #464) validates all content YAML files against the schema. No validation errors. + +8. **Visual specs are actionable:** `docs/design/entity-color-system.md`, `docs/design/text-display-hierarchy.md`, `docs/design/sound-indicator-visual.md`, `docs/design/the-friend-visual-treatment.md`, `docs/design/environmental-text-standards.md`, and `docs/design/tell-visual-expression.md` all exist with sufficient detail for Stig to implement from the spec alone — hex values, pixel dimensions, z-layer assignments, animation durations. + +9. **Invariant test suite passes:** `cargo test` in `server/` includes `invariants::*` tests that execute against all existing gauntlet rooms without failure. 36 invariants across 4 categories all assert green. + +10. **Access tier shift design exists:** `docs/design/access-tier-shifts.md` documents 2-3 tier transitions per character per social site with specific KG fact triggers and reversibility notes. Ready as design input for #169 (Layer 1, S15). + +--- + +## Test Plan Alignment (D-030) + +Sprint 14 is in the integration phase (D-030 Phase 2+). Test focus: + +- **Server:** New systems (#323 mood, #324 trust, #325 interaction tracking, #101 routine, #103 relationship dynamics, #247 NPC conversations) each require unit tests in their respective modules. Hoshe's test harness pattern: ECS `World` setup, inject components, advance system, assert state change. Determinism required: all random values through `SimRng` (D-010 principle 4). #508 invariant tests run as integration-style tests over gauntlet rooms — these are the first map-agnostic quality gate. +- **Client:** Hoshe's gdUnit4 harness. #122 (monologue display) tests: queue management (queue depth at max, oldest-first eviction?), fade timing, character color correctness, no-overwrite behavior. Verify `current_monologue: None` produces no display (no ghost text from previous tick). +- **Copy:** Content validation is CI-driven (#464 cross-reference check in `make pre-pr`). Line pool YAML files (#120) must pass schema validation. No manual test required for docs (#253, #121, #328, #168). +- **CI:** `make ci` must pass on all branches. `make pre-pr` runs content cross-reference validation including schema check. No new HashMap in simulation crate (clippy ban active). + +--- + +## Decision Coverage Gaps (remaining after Sprint 14) + +The following confirmed decisions still have no implementing tickets. Flag to Team Leader if any block upcoming work: + +- D-031 (time system) — exercised by #101 routine execution and gauntlet shift change room. No standalone ticket needed. +- D-033 (entity color = relationship to player) — #304 this sprint is the spec. Client implementation is a Sprint 15 ticket (not yet created). +- D-036 (Sova Transit District setting) — no world map authoring ticket yet. Relevant to #155 (hand-crafted location authoring, backlog). +- D-040 (wiki taxonomy) — documentation structure, not code. +- D-043–D-046 (art direction decisions) — referenced by #304, #316, #317, #334 this sprint. No standalone implementing tickets. +- D-062 (invisible locked dialogue) — design constraint, implemented by server-side filtering (already in `dialogue.rs`). No new ticket. +- D-070 (confrontation as cognitive vulnerability) — implemented Sprint 13 (audio dip). No further ticket. +- D-074 (audio aesthetic identity) — implemented Sprint 13 (murmur asset). No further ticket. + +--- + +## Notes for Sprint Start + +To activate the sprint once planning is approved: + +```bash +db/connectors/sprint start +``` + +This sets Sprint 14 to `active`. Teams then run `db/connectors/sprint start-work --team ` for their full context dump. diff --git a/docs/sprints/sprint-14/server.md b/docs/sprints/sprint-14/server.md new file mode 100644 index 000000000..02f3b5c04 --- /dev/null +++ b/docs/sprints/sprint-14/server.md @@ -0,0 +1,82 @@ +# Sprint 14: Live — Server Tasks + +**Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. + +**Branch:** `server` +**Agents:** Dudley (simulation dev), Tyre (architect), Hoshe (QA) + +## Carry-over from Sprint 13 + +None. All Sprint 13 server tickets done. + +## New Tickets + +| # | Title | Blocked by | +|---|-------|------------| +| #323 | NPC mood state machine | — | +| #324 | Trust progression system | — | +| #325 | Interaction tracking component | — | +| #101 | Routine execution system | #87 done, #238 done | +| #103 | Relationship dynamics | — | +| #247 | NPC-to-NPC conversation system | — | +| #508 | Map-agnostic invariant tests (36 invariants) | — | + +Use `db/connectors/ticket show ` for full details. + +## Key Decisions + +- `decisions/architecture.md` — D-024 (10-axis NPC model), D-026 (simulation tiers — ActiveSim scope), D-031 (game clock / day phases — routine transitions), D-041 (knowledge graph — trust feeds Layer 3 filtering), D-054 (tile-based movement), D-075 (dialogue filtering — trust co-gate with KnowledgeConfidence) +- `decisions/content.md` — D-023 (three-tier content model), D-028 (dialogue four-layer model — Layers 2-4 fed by trust/interaction tracking), D-029 (population entanglement ratio — 30/50/20), D-034 (THE FRIEND — trust arc requirements), D-062 (invisible locked options — new trust tier = new options appear silently) +- `decisions/perception.md` — D-016 (internal monologue — mood drives monologue tone), D-018 (three-range sound model — NPC-to-NPC conversations emit Voice events) + +## Notes + +- **#323 — NPC mood state machine:** Add a `Mood` component to `server/src/npc/mod.rs`. Eight moods for v0.1: `Neutral`, `Anxious`, `Frustrated`, `Content`, `Suspicious`, `Warm`, `Hostile`, `Focused` (9th: added Sprint 8, D-035 amendment). Mood changes based on: triangle pressure (high ToleranceThreshold stress → Anxious/Hostile), time of day (Evening + long shift → Frustrated), recent interactions (positive player interaction → Warm). Write a `MoodSystem` in a new `server/src/npc/mood.rs` module. Mood is read by the monologue trigger system (`server/src/simulation/monologue.rs`) and will gate Layer 4 dialogue selection (`server/src/simulation/dialogue.rs`). Blocks #337 (tell state derivation, deferred to S15). Add to `NpcPlugin` in `server/src/npc/mod.rs`. Keep all values `i16`/enum — no floats, per D-010 determinism. + - Delivery: `Mood` component, `update_mood` system, unit tests in `server/src/npc/mood.rs`. + +- **#324 — Trust progression system:** Trust level per player-NPC pair drives D-028 Layer 3 (trust-gated gossip). The `RelationshipGraph` at `server/src/npc/relationships.rs` already holds `trust: i8` on every `RelationshipEdge`. Sprint 14 work: (1) a system that advances trust based on interaction quality — `TalkVerb` completion → small positive increment, confrontation walk-away → negative decrement, repeat visit (from #325 interaction count) → small positive; (2) expose the current trust value on `ObserverSnapshot` or leave it in the KnowledgeGraph (D-041). Trust maps to D-028 TrustTier via `relationship_to_trust()` in `server/src/simulation/dialogue.rs` — D-075 adds KnowledgeConfidence as co-gate to that function. No new ECS component needed — `RelationshipGraph` resource is the store. Write a `update_trust` system in `server/src/npc/relationships.rs`. Blocks #171 (Layer 3 trust-gated gossip, deferred to S15). + - Integration point: `server/src/simulation/dialogue.rs` `process_talk_interaction` must call `relationship_to_trust()` with `KnowledgeConfidence` (D-075 implementation if not already wired). + - Delivery: `update_trust` system, tests verifying trust increments/decrements on interaction events. + +- **#325 — Interaction tracking component:** `InteractionMemory` component per NPC-pair — fields: `interaction_count: u32`, `last_interaction_tick: u64`, `notable_events: Vec`. The `interaction_count` drives D-028 Layer 2 situation activation: `first_meeting` when count == 0, `repeated_visit` when count >= 3. Add to `server/src/npc/mod.rs` or a new `server/src/npc/interaction.rs`. Populated by `process_talk_interaction` in `server/src/simulation/dialogue.rs` each time a Talk verb completes. `notable_events` stores walk-aways (D-064) and confrontations (D-063) — these are already recorded in the KG but `InteractionMemory` provides fast per-pair access without a full KG query. + - Integration point: `server/src/simulation/dialogue.rs` — increment count and stamp tick on each completed Talk. `dialogue.rs` already reads KG and RelationshipGraph; add InteractionMemory to the same query. + - Delivery: `InteractionMemory` component, incremented by dialogue system, used by situation resolver for Layer 2. + +- **#101 — Routine execution system:** The routine data model exists (`DailyRoutine`, `RoutineEntry` in `server/src/npc/mod.rs`). The phase-transition trigger exists (`check_phase_transition` in `server/src/npc/routine.rs`) — it already issues `PathRequest` on phase change. Sprint 14 work: verify the full execution loop closes — `PathRequest` → pathfinder → `path_follow` → entity reaches destination and enters the routine activity state. Add an `activity: String` → `ActivityState` ECS component (or reuse `DailyRoutine.entries[].activity`) so the simulation knows what an NPC is currently doing. This feeds the `DuringActivity` tell trigger (`TellTrigger::DuringActivity` in `server/src/npc/mod.rs`) and the Layer 2 `situation` tag matching. Existing pathfinding: `server/src/simulation/pathfinding.rs`, `server/src/simulation/path_follow.rs`. Gauntlet room `shift_change_*` (Sprint 13 #505) already validates phase-boundary transition — extend it or write new integration tests for full routine loop. + - Delivery: `ActivityState` component (or equivalent) attached/updated as NPCs execute routines; tests verifying NPC reaches routine destination and holds activity state. + +- **#103 — Relationship dynamics:** Relationship values decay and reinforce over time. Existing `RelationshipEdge` in `server/src/npc/relationships.rs` has `trust: i8` and `history: Vec`. Sprint 14 work: (1) passive decay — trust drifts toward 0 at ~1 point per game-day if no recent interaction (controlled by `last_interaction_tick`); (2) interaction reinforcement — talking, helping (future), witnessing positive events strengthens; (3) `RelationshipEvent` is appended on notable interactions. This system runs in the Background tier (D-026) — lightweight, 1 update per game-minute. Add `update_relationship_dynamics` system to `server/src/npc/relationships.rs`, registered in `NpcPlugin`. Blocks #249 (player-action social propagation, deferred to S15). + - Note: Do not confuse with #324 (player-NPC trust). #103 covers NPC-NPC relationship dynamics — the relationship graph that generates the social texture (#249 feeds into). The player-NPC trust arc (THE FRIEND, #324) is a separate ticket. + +- **#247 — NPC-to-NPC conversation system:** NPCs in the Active tier who are in proximity (≤3 tiles) and share a social site occasionally enter NPC-to-NPC conversations. Implementation: (1) a `ConversationSystem` in `server/src/simulation/` that detects eligible NPC pairs (same zone, ActiveSim, not already in player conversation, not sprinting away), initiates a `NpcConversation` state component for the pair, and emits a `SoundEvent` of kind `Voice` at the conversation tile — picked up by `server/src/simulation/sound.rs` and included in `ObserverSnapshot.sound_events`; (2) conversation duration in ticks (configurable constant, ~30-120 ticks = 3-12 game-minutes); (3) conversation terminates when one NPC leaves the zone or duration expires. The client's `sound_indicator_renderer.gd` already handles `Voice` sound events. This is the first source of overheard conversations — the eavesdrop mechanic (D-426 `ListeningFocus`) becomes meaningful when NPCs are actually talking. + - **D-078 addition (amended — occlusion is SERVER-AUTHORITATIVE):** Each `ConversationEvent` on `ObserverSnapshot` must carry: `occluded_line: String` (the NPC dialogue line with dropped words replaced by `...`), `speaker_id: EntityId`, `target_id: EntityId`, `speaker_name: String`, `target_name: String`. The server performs per-word occlusion before emission — the client receives pre-occluded text and renders it verbatim. + - **Per-word occlusion algorithm:** Iterate the dialogue line word by word. For each word, perform an independent Bernoulli trial using a seedable RNG (seeded per tick for deterministic replay, D-010). Per-word drop probability is derived from three inputs at the moment of emission: (a) tile distance from player to the speaking NPC — linear decay from 0.0 drop probability at 0 tiles to 1.0 at the `Voice` sound range boundary; (b) ambient noise level at the player position (already tracked in `ObserverSnapshot.ambient_noise`) — adds up to 0.3 to drop probability; (c) whether the player entity has `ListeningFocus` stance active — subtracts 0.2 from drop probability (clamped to [0.0, 1.0]). Words that fail the trial are replaced with `...` in `occluded_line`. + - When a conversation ends (NPC departs zone or duration expires), emit a `conversation_end` event with the same pair IDs so the client can dismiss the panel. Trigger `witness_interaction` on the player observation pipeline whenever the player receives any conversation event (regardless of occlusion level). + - No dialogue content needed in this ticket — content is sourced from the NPC-to-NPC line pool (#536, copy team, Sprint 14). + - Delivery: `NpcConversation` state component, `run_npc_conversations` system, `Voice` `SoundEvent` emission, `ConversationEvent` struct with `occluded_line` on `ObserverSnapshot`, `conversation_end` event, per-word occlusion function with seedable RNG, tests: `npc_conversation_emits_voice_event_when_in_range`, `npc_conversation_terminates_when_apart`, `occlusion_drops_words_with_distance`, `occlusion_suppressed_by_listening_focus`, `occlusion_deterministic_with_same_seed`. Blocks #535 (client). + +- **#508 — Map-agnostic invariant tests (36 invariants):** 36 invariants across 4 categories from Gestalt's workshop output. Implement as a test suite in `server/src/test_world/` that runs against any valid map (gauntlet rooms). Categories: structural (8 — tile counts, wall connectivity, spawn point validity), perception (5 — LOS symmetry at range, sound range boundaries), population (8 — NPC count limits, tier assignment correctness), simulation (8 — no entity teleports, determinism, pathfinder termination, interaction buffer clear on sprint). Add a `run_invariants(world: &World)` function callable from gauntlet room tests — each room calls it after setup to assert structural invariants hold. Dynamic map system not required — invariants work on static gauntlet maps. + - Note: #509 (fuzzy tests for procedural maps) remains deferred — requires dynamic map generation. + - Delivery: `server/src/test_world/invariants.rs` module with 36 test assertions; each gauntlet room test calls `run_invariants`. + +## Dependency Chain + +``` +#323 (mood) ─────────────────────────────────────→ #337 (tell derivation, S15) +#324 (trust) ────────────────────────────────────→ #171 (Layer 3, S15) +#325 (interaction tracking) → #101 (routine) is parallel + → #103 (relationship dynamics) is parallel + → #247 (NPC conversations) is parallel +#247 (NPC conversations) ────────────────────────→ #535 (client passive panel, blocked) +#508 (invariants) — standalone, parallel with all above +``` + +All six simulation tickets (#323, #324, #325, #101, #103, #247) are independent of each other and can proceed in parallel. #508 is also fully standalone. #535 (client) is blocked on #247 landing the `ConversationEvent` struct on `ObserverSnapshot` — specifically `occluded_line: String` (pre-occluded by server), `speaker_name`, `target_name`, and `conversation_end`. + +## PR Workflow + +When ready to submit, create a PR with `tea` CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section): + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): description" --description "body" --base main --head server +``` diff --git a/docs/sprints/sprint-14/visual.md b/docs/sprints/sprint-14/visual.md new file mode 100644 index 000000000..65fcd43d6 --- /dev/null +++ b/docs/sprints/sprint-14/visual.md @@ -0,0 +1,101 @@ +# Sprint 14: Live — Visual Tasks + +**Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. + +**Branch:** `visual` +**Agents:** Araminta (art direction) + +## Carry-over from Sprint 13 + +None. All Sprint 13 visual tickets done (#315 monologue display spec). + +## New Tickets + +| # | Title | Blocked by | +|---|-------|------------| +| #304 | Entity Color System Spec — relationship-to-player mapping | #303 done | +| #316 | Text display hierarchy spec — 4 content pipelines | #303 done | +| #317 | Sound indicator visual design — fog-edge pulse | #303 done | +| #318 | THE FRIEND visual treatment spec | #303 done, #297 done, #298 done | +| #334 | Environmental text visual standards | #303 done | +| #251 | Tell visual/behavioral expression | — | + +All six tickets are unblocked. All are spec/design documents — Araminta authors; no code deliverables from the visual branch this sprint. + +Use `db/connectors/ticket show ` for full details. + +## Key Decisions + +- `decisions/content.md` — D-033 (entity color = relationship to player — approved color hex values: unknown=teal #4a9ebb, known/friendly=green #6bc9a6, person-of-interest=amber #e8c547, hostile=red #d45d5d, static objects=grey #8090a8), D-034 (THE FRIEND — phase 1 identical to other friendly NPCs, earned visual detail only), D-043 (art direction — functional warmth), D-044 (visual hierarchy — entity > object > structure), D-045 (environmental neutrality — zero shift with narrative state), D-046 (lighting system — three-reference model), D-047 (two-tier animation system — Tier 1 readable, Tier 2 ambiguous), D-048 (neural insert overlay), D-049 (z-level rendering stack — 8 layers) +- `decisions/perception.md` — D-016 (internal monologue — text rendering), D-017 (perception modes), D-018 (three-range sound model — color codes for sound indicator: neutral #c8d0e0, voices #e8c547, danger #d45d5d), D-035 (tag taxonomy — monologue display spec references character field), D-062 (invisible locked dialogue — no lock icons, no hints) +- `decisions/scope.md` — D-027 (vertical slice success criteria — #3: player names an NPC they felt conflicted about = THE FRIEND) + +## Notes + +- **#304 — Entity Color System Spec:** Araminta formalizes the approved D-033 color model into a full spec document. Output: `docs/design/entity-color-system.md`. Required sections: + - Relationship state → color mapping table (hex values confirmed in D-033 are the canonical source). + - How relationship state is determined at render time: client receives `relationship: RelationshipState` on each `VisibleEntity` in the snapshot (`server/src/bridge/types.rs` `VisibleEntity`). Client maps state to color via a lookup table in `entity_renderer.gd` (`client/scripts/rendering/entity_renderer.gd`). + - Transition behavior: when an entity's relationship state changes (e.g., PersonOfInterest after confrontation), how the color shift renders — instant or tween, duration. + - Static objects: always `#8090a8` regardless of any relationship state. Define what counts as a static object (fixtures, furniture, terminals) vs entity (NPCs, player, carried items). + - Edge cases: player character color (not subject to relationship coloring — different treatment), entities in fog (D-033 color does NOT show through fog — unrecognized fog blobs are grey until cognitive delay resolves), entities at periphery (reduced saturation per D-046 lighting reference model). + - Color blindness note: D-033 colors were selected for functional warmth — include Araminta's assessment of whether the teal/green/amber/red set is distinguishable under deuteranopia and protan deficiency. Flag if adjustment needed. + - This spec is the direct input to the client-side implementation (future sprint — no client ticket this sprint). Must be self-contained enough for Stig to implement from the doc alone. + +- **#316 — Text display hierarchy spec:** How the four content pipelines render distinctly. Output: `docs/design/text-display-hierarchy.md`. Four pipelines: + 1. **Dialogue** (NPC speech + player response options): bottom panel, max 20% height, max ~100 chars per line (D-061). Speaker name styled. Response options styled differently from speech. + 2. **Internal monologue** (player character): left-side or bottom-left, italic, smaller font, character colour from #315 spec (done Sprint 13). + 3. **Observation / overheard** (what the player perceives about NPCs or overhears): distinct treatment from monologue — diegetic information output, not character voice. Consider: greyed label, different font weight, positioned differently. + 4. **Environmental text** (signage, terminals, news tickers in-world): diegetic — appears in the world layer, not the HUD layer. Two-language treatment per D-036 (Concordat Standard + Krenn vernacular). Character limits per format (signage shorter, terminal longer, news ticker scrolling). + - Each pipeline needs: position on screen, font size/weight, colour scheme, max width/height, fade/truncation behaviour. D-049 z-layer assignments for each. + - Reference: `docs/design/monologue-display-spec.md` (#315, done) for monologue pipeline specifics — this spec extends and cross-references it. + +- **#317 — Sound indicator visual design:** Fog-edge pulse indicators for the D-018 three-range sound model. Output: `docs/design/sound-indicator-visual.md`. Design requirements (from ticket and D-018): + - **Purpose:** Complementary to audio, not replacement. Indicates sound presence for players with audio off or in loud environments. + - **Colors:** neutral #c8d0e0 (footsteps, ambient), voices #e8c547 (NPC conversation), danger #d45d5d (alarms, alerts). + - **Position:** Fog edge — where the visible area meets unexplored/deep fog. Not a minimap overlay, not a screen-edge indicator. Appears at the boundary tiles of the player's visible cone. + - **Shape and animation:** Pulse — how many pixels, what frequency, what easing, what opacity range. Must not be distracting during exploration; must be noticeable when specifically a threat. + - **Direction encoding:** Indicates direction the sound comes from — how? Arc segment on fog edge in the direction of source? Size variation? Specify clearly for Stig's implementation. + - **Range differentiation:** Close sounds (≤3 tiles) produce larger/brighter pulses; medium (≤8 tiles) moderate; long (≤20 tiles) subtle. Define the three visual levels. + - **When it does not appear:** When audio is playing and the sound is within the visible area (no need to indicate what you can already see/hear). Rules for suppression. + - The `sound_indicator_renderer.gd` file exists at `client/scripts/rendering/sound_indicator_renderer.gd` — Araminta should note what it currently does vs what the spec calls for. + +- **#318 — THE FRIEND visual treatment spec:** How Kael Davan (smuggler's FRIEND) and Sera Venn (detective's FRIEND) look different from other NPCs through earned visual detail only — no special marking. Output: `docs/design/the-friend-visual-treatment.md`. Per D-034 design principle: Phase 1 identical to other friendly NPCs (green rectangle per D-033). Differentiation accrues through story, not through marking. Required sections: + - **Phase 1 (before player builds relationship):** Identical to any other Known/Friendly NPC. Green rectangle, no distinguishing visual. + - **Phase 2 (after 3+ interactions, trust building):** What subtle visual shift occurs — if any — that is diegetically justified, not metatextual marking. Examples: does the character carry an item that renders (a manifest, a specific color? per D-033 object color rules)? Does their routine placement become more predictable visually? Define what "earned visual detail" means concretely. + - **Phase 3 (contradiction discovered — PersonOfInterest):** Transition to amber #e8c547 per D-033. Monologue firing rate spikes. Spec: when does the color shift? Immediately on confrontation? On player delivery of contradiction knowledge? Tween duration. + - **Animation tier:** THE FRIEND is in Tier 2 animation (ambiguous, privately motivated behaviors per D-047) once the player has seen their contradiction. Before contradiction: Tier 1. Spec the transition point. + - Cross-reference `docs/design/entity-color-system.md` (#304, this sprint) once it is drafted. + +- **#334 — Environmental text visual standards:** How signage, terminals, and news tickers render in-world. Output: `docs/design/environmental-text-standards.md`. Per ticket: + - **Signage:** Short (1-3 words typical), both Concordat Standard and Krenn vernacular. Character limits. Font size relative to tile size (D-066: 1m visual tiles, 2x retina factor). Position: floating above the tile, or rendered on the tile surface. + - **Terminals:** Longer text, readable on interaction (Observe verb). Two-state: ambient (icon/identifier visible from range) and active (text readable when player is adjacent). + - **News tickers:** Scrolling text, ambient, not blocking gameplay. Where on screen — in-world (floating above terminal), not HUD overlay. + - **Bilingual treatment:** Concordat Standard is the colonial lingua franca (neutral, bureaucratic). Krenn vernacular is the local dialect (warm, compact, consonant-heavy per D-036 naming conventions). Which text in which language per context? Formal signage = Concordat Standard. Informal social text (bar menu, worker notices) = Krenn vernacular. Mixed where both audiences are intended. + - **Rendering layer:** Per D-049 z-layer stack — environmental text is object-layer (layer 2-3), not HUD layer (layer 6+). Must not occlude entities. + +- **#251 — Tell visual/behavioral expression:** How NPC behavioral tells manifest in the top-down renderer. Output: `docs/design/tell-visual-expression.md`. Per D-024 tell system (5 categories: nervous, angry, friendly, guarded, routine deviation) and server's `TellSystem` component (`server/src/npc/mod.rs`). In v0.1, tell expression is via **monologue text** (server emits monologue on observe_npc trigger when tell is active), not visual animation — this is confirmed. However, Araminta's task is to define: + - What observable behaviors in the top-down renderer accompany tells — movement hesitation (NPC pauses before entering a room), route changes (NPC takes alternate path), grouping behavior (NPC lingers near another), interaction changes (NPC avoids certain tiles). + - These are **behavioral patterns in the simulation** described visually — the spec tells Dudley what the tell should look like at the tile level, which he implements as movement/pathfinding modifiers. The spec is the design input to server-side tell behavior. + - Per D-047 animation tier: Tier 2 animation is "ambiguous, privately motivated behaviors." Tells are the canonical Tier 2 examples — movement hesitation is a Tier 2 animation. Define which of the 5 tell categories maps to which Tier 2 behaviors. + - Araminta's scope here: the visual description. Dudley implements the simulation behavior. Mellanie writes the monologue that accompanies the tell. + +## Dependency Chain + +``` +#304 (entity color spec) ─────────────────→ #318 (THE FRIEND visual, cross-ref) +#316 (text display hierarchy) ───────────→ standalone +#317 (sound indicator visual) ───────────→ standalone +#318 (THE FRIEND visual) ────────────────→ #251 (tell visual, cross-ref) +#334 (environmental text) ───────────────→ standalone +#251 (tell visual) ──────────────────────→ standalone +``` + +Recommended order: start #304 (entity color) first — #318 cross-references it. #251 and #318 can proceed in parallel once #304 is drafted. The other three (#316, #317, #334) are fully independent and can proceed in any order. + +## PR Workflow + +When ready to submit, create a PR with `tea` CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section): + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "docs(visual): description" --description "body" --base main --head visual +```