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

110 lines
7.8 KiB
Markdown

---
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 <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_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<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):
```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): sprint 16 dialogue server" --description "body" --base main --head server
```