--- title: "Sprint 16 — Server Briefing" description: "Dialogue response handler, trust-gated gossip, line variety tracker" type: sprint status: archived sprint: 16 team: "server" --- # Sprint 16: Converse — Server Tasks **Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams. **Branch:** `server` **Agents:** Dudley (simulation), Tyre (arch), Hoshe (QA) ## Carry-over from Sprint 15 None. Sprint 15 was 100% complete (16/16 done). ## New Tickets | # | Title | Blocked by | |---|-------|------------| | #538 | Arch: Move dialogue systems from BridgePlugin to NpcPlugin | — | | #539 | Server: implement DialogueResponse verb handler | #538 | | #171 | Layer 3: Trust-gated gossip | — | | #338 | Line variety tracker — prevent repeat dialogue | — (unblocked: #305 done) | Use `db/connectors/ticket show ` for full details. ## Key Decisions - `decisions/content.md` — D-028 (dialogue architecture, four relational layers), D-035 (tag taxonomy, trust/access tiers), D-075 (layered confidence gate on trust tier) - `decisions/architecture.md` — D-041 (knowledge graph data model, KnowledgeConfidence hierarchy), D-010 (no baking player identity into game loop) ## Notes ### #538 — Arch: Move dialogue systems from BridgePlugin to NpcPlugin **What exists:** `server/src/bridge/mod.rs` (lines 181-187) registers `process_talk_interaction`, `process_walk_away`, and `process_confrontation_response` inside `BridgePlugin::build()`. These systems depend on NPC-layer resources (`TrustEventQueue`, `KnowledgeGraph`, `KnowledgeEventQueue`) but live in the bridge module, which is supposed to own only wire protocol concerns. **What to deliver:** Move the three `.add_systems()` calls from `BridgePlugin::build()` to `NpcPlugin::build()` in `server/src/npc/mod.rs`. The function implementations in `server/src/simulation/dialogue.rs` do not move — only their registration site changes. The scheduling constraints (`.after(process_player_input)`, `.before(compute_observer_snapshot)`) must be preserved exactly. `NpcPlugin` already has access to `TrustEventQueue` and other NPC resources (it `init_resource`s them), so no new resource registration is needed. **Gotcha:** The `game_loop` integration tests in `dialogue.rs` that manually set up `TrustEventQueue` were the signal that this was misplaced. After the move, those tests should no longer need the manual init. Verify no test breaks. **Integration point for #539:** This must merge before #539 starts, since #539 adds a new dialogue system that should register in `NpcPlugin` from the start. ### #539 — Server: implement DialogueResponse verb handler **What exists:** `process_talk_interaction` in `server/src/simulation/dialogue.rs` handles the initial `Talk` verb. It selects a line via the 4-layer pipeline and writes to `DialogueResponseBuffer`. The client sends `DialogueResponse { response_id }` when the player picks an option, but no server system consumes it. `PlayerAction::DialogueResponse` is already defined in the bridge types. **What to deliver:** A new system `process_dialogue_response` in `server/src/simulation/dialogue.rs` that: 1. Reads `DialogueResponse` actions from the player input queue 2. Looks up the NPC's `DialogueProfile` (location, role) and active `KnowledgeGraph` state 3. Runs the same 4-layer filtering pipeline (`query_dialogue` on `LinePoolIndexResource`) to select a follow-up line based on the response_id context 4. Writes the result to `DialogueResponseBuffer` for snapshot inclusion 5. For conversations that have ended (no follow-up lines), clears `ActiveDialogue` **Key design constraints from D-028 + D-062:** Dialogue options the player hasn't unlocked are invisible — so `response_id` will only arrive for options that were legitimately sent. No validation needed beyond confirming the response_id maps to a known dialogue context. The server does not need to validate "was this a valid choice?" — if the client sent it, it was shown. **Integration with #171:** If trust-gated gossip (#171) is complete in the same sprint, this system should also route `Real`/`Secret` tier responses through the trust-gated pipeline. If #171 lands after, stub with `Surface`-only for now. **Register in NpcPlugin** (after #538 lands). ### #171 — Layer 3: Trust-gated gossip **What exists:** The trust tier gate is already fully implemented. `relationship_to_trust()` in `server/src/simulation/dialogue.rs` (lines 199-216) takes `RelationshipState` and `KnowledgeConfidence` and returns `TrustTier` per D-075. The `LinePoolIndexResource` query pipeline in `content/mod.rs` accepts a `TrustTier` and filters lines accordingly. The content YAML files already carry `trust: real` and `trust: secret` tags on authored lines. **What to deliver:** Three disclosure tiers per NPC role (surface / real / secret). The engine is ready — the gap is that `process_talk_interaction` may not be passing the correct `KnowledgeConfidence` from the observer's `KnowledgeGraph` to `relationship_to_trust()`. Verify in `process_talk_interaction` that: 1. The observer's `KnowledgeGraph` is queried for the NPC's `StableId` 2. `confidence_of(&target_sid)` is passed as the second arg to `relationship_to_trust()` 3. The resulting `TrustTier` filters lines in `query_dialogue` If this path is already wired (check near line 345 in `dialogue.rs`), the ticket may be a content validation: confirm that authored lines with `trust: real` and `trust: secret` actually surface for a player with `KnowsOf+` / `KnowsDetails+` knowledge. Write a test that initializes a player KG with `KnowsDetails` on a target NPC, runs `process_talk_interaction`, and asserts a `Secret`-tier line was selected. **Progressive revelation:** The same NPC gives different answers on first meeting (Surface only) vs. after investigation (Real/Secret unlocked). This is not a UI change — it is already handled by D-062's invisible locks. The engine must simply populate `DialogueResponseBuffer` with the tier-appropriate line. ### #338 — Line variety tracker — prevent repeat dialogue **What exists:** `DialogueCooldownTracker` is already implemented as a `Component` in `server/src/simulation/dialogue.rs` (lines 78-105). It tracks `line_id → tick_used` in a `BTreeMap` with a 600-tick (1 game-hour) cooldown. `record()`, `is_on_cooldown()`, and `prune()` methods exist. **What to deliver:** Wire the tracker into `process_talk_interaction`: 1. Query `DialogueCooldownTracker` on the player entity (it may already be queried — check the system signature near line 345) 2. After the 4-layer pipeline returns candidates, filter out lines where `tracker.is_on_cooldown(line_id, current_tick)` returns true 3. Call `tracker.record(selected_line_id, current_tick)` after selection 4. Call `tracker.prune(current_tick)` each time to prevent unbounded growth If the tracker is already being queried but not used to filter, this is a 10-line change. If it is not queried at all, add it to the system signature and wire it in. Write a test that sends `Talk` repeatedly to the same NPC and asserts no line_id appears twice within `LINE_COOLDOWN_TICKS`. ## Dependency Chain ``` #538 (BridgePlugin → NpcPlugin refactor) → #539 (DialogueResponse verb handler) #171 (Layer 3: Trust-gated gossip) → standalone, parallel track [may inform #539 if both land same sprint] #338 (Line variety tracker wiring) → standalone, parallel track ``` ## 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): sprint 16 dialogue server" --description "body" --base main --head server ```