Files
settled-reach/docs/sprints/sprint-16/server.md
T
jpmschweitzerandClaude Opus 4.6 bccdcfcdcb docs(docs): add frontmatter to all sprint briefings
Standardized YAML frontmatter on all 115 sprint briefing files across
sprints 1-26 with title, description, type, status, sprint number, and
team fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:15:45 +01:00

7.8 KiB

title, description, type, status, sprint, team
title description type status sprint team
Sprint 16 — Server Briefing Dialogue response handler, trust-gated gossip, line variety tracker sprint archived 16 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 <id> 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_resources 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<String, u64> 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):

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): sprint 16 dialogue server" --description "body" --base main --head server