chore(meta): add Sprint 8 briefings and DB backup
Sprint 8: React — briefings for server, client, audio, and joint teams. Database snapshot after Sprint 8 ticket assignments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
# Sprint 8: React — Audio Tasks
|
||||
|
||||
**Goal:** Activate the dialogue pipeline end-to-end — player talks to NPCs, NPCs respond, the world reacts.
|
||||
|
||||
**Branch:** `audio`
|
||||
**Agents:** Inigo (sound design)
|
||||
|
||||
## Carry-over from Sprint 7
|
||||
|
||||
| # | Title | Status | Notes |
|
||||
|---|-------|--------|-------|
|
||||
| #440 | 6 interaction audio assets — cursor hover, weapon aim, etc. | in_progress | 4/6 assets delivered, 2 remaining |
|
||||
|
||||
## New Tickets
|
||||
|
||||
None for Sprint 8. Focus on completing carry-over #440.
|
||||
|
||||
Use `db/connectors/ticket show <id>` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/scope.md` — D-038 (audio in v0.1 scope: 8 files via Stable Audio Open)
|
||||
- `decisions/perception.md` — D-018 (three-range sound model)
|
||||
- `decisions/content.md` — D-063 (confrontation weight via audio dip)
|
||||
|
||||
## Notes
|
||||
|
||||
### #440: 6 interaction audio assets (MEDIUM, carry-over)
|
||||
**What exists:**
|
||||
- 4/6 assets delivered in Sprint 7:
|
||||
- `sfx_cursor_hover.ogg` — soft tick on entity hover
|
||||
- `sfx_cursor_entity_lock.ogg` — short pulse when locking to entity
|
||||
- `sfx_interaction_confirm.ogg` — gentle chime on [E] press
|
||||
- `sfx_weapon_aim_enter.ogg` — tense metallic slide when entering aim mode
|
||||
|
||||
**What the ticket needs to deliver (2 remaining assets):**
|
||||
- `sfx_weapon_aim_lock.ogg` — sharper variant of cursor_entity_lock for weapon targeting. Distinct from general entity lock — should feel more clinical/dangerous. 0.3-0.5s. Weapon-selected + entity in sights = this sound plays.
|
||||
- `sfx_stance_change.ogg` — soft mechanical click for stance toggle (Walk ↔ Sprint ↔ Careful ↔ Crouch). Subtle, not intrusive. Neural insert feedback aesthetic (D-048). 0.2-0.4s. Plays on every stance change (Shift/Ctrl toggle).
|
||||
|
||||
**Integration:**
|
||||
- Both assets use Stable Audio Open (per D-038: self-hosted, free commercial use)
|
||||
- Client integration: audio events emitted by cursor_state_machine.gd (weapon_aim_lock) and stance_indicator.gd (stance_change)
|
||||
- Audio autoload (SoundManager) handles playback
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- weapon_aim_lock should NOT be loud or startling — it's UI feedback, not a combat sound. Think "confirmation beep" not "alarm."
|
||||
- stance_change fires frequently (player may toggle stances multiple times while positioning). Keep it short and pleasant — no long tails or reverb.
|
||||
- Both sounds are UI/insert layer (z-layer 7), not diegetic world sounds. They don't spatialize or attenuate with distance.
|
||||
|
||||
**Sprint goal:**
|
||||
Deliver final 2 assets. Audio team unblocked after this — no new audio tickets planned for Sprint 9 unless design requests.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#440 (interaction audio) → standalone, completes Sprint 7 audio scope
|
||||
```
|
||||
|
||||
## 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(audio): interaction audio completion (Sprint 8)" --description "Delivers final 2/6 interaction audio assets (#440) — weapon aim lock, stance change" --base main --head audio
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
# Sprint 8: React — Client Tasks
|
||||
|
||||
**Goal:** Activate the dialogue pipeline end-to-end — player talks to NPCs, NPCs respond, the world reacts.
|
||||
|
||||
**Branch:** `client`
|
||||
**Agents:** Stig (dev), Tyre (arch), Hoshe (QA)
|
||||
|
||||
## Carry-over from Sprint 7
|
||||
|
||||
| # | Title | Status | Notes |
|
||||
|---|-------|--------|-------|
|
||||
| #431 | Fog entity visualization — sound pings, cognitive delay anim | in_progress | Protocol v7 bump + visualization implementation |
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #435 | Dialogue response selection — max 3 visible, invisible locks | #434 (done) |
|
||||
| #437 | Walk-away mechanic — WASD detect during dialogue, 300ms fade | — |
|
||||
| #436 | Confrontation text styling — italic voice, monologue beat | #435 |
|
||||
|
||||
Use `db/connectors/ticket show <id>` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/content.md` — D-062 (invisible locked dialogue options), D-063 (confrontation same box different weight), D-064 (walk-away 3-phase consequences)
|
||||
- `decisions/perception.md` — D-059 (fog shader 5 layers), D-060 (cognitive delay), D-061 (dialogue box max 20% height)
|
||||
- `decisions/architecture.md` — D-066 (dual-scale grid 0.5m sim / 1m visual)
|
||||
|
||||
## Open Questions to Resolve Early
|
||||
|
||||
None.
|
||||
|
||||
## Notes
|
||||
|
||||
### #431: Fog entity visualization — sound pings, cognitive delay anim (HIGH, carry-over)
|
||||
**What exists:**
|
||||
- `client/scripts/rendering/fog_shader.gd` — FogShader node with 5-layer shader (D-059), noise animation
|
||||
- `client/scripts/protocol/protocol.gd` — MessagePack decoder for ObserverSnapshot (protocol v6)
|
||||
- Fog layers 1-3 (clear, light fog, deep fog) render via shader, no entity visualization yet
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Protocol v7 bump:** Add `pending_recognitions` and `sound_events` fields to ObserverSnapshot in protocol.gd decode logic
|
||||
- **Sound ping rendering:** On FogState layer 6 (insert overlay, NOT affected by fog shader), render 2-3 thin concentric expanding rings from sound source direction. Sonar-style. Insert white-blue color. Loud = 3 rings bright fast. Quiet = 1 ring faint slow. Fade over 1.5s. Use simple Sprite2D or Line2D, not particles.
|
||||
- **Recognized entity in fog:** Entity with `pending: false` renders on layer 6 as: D-033 color glow (via PointLight2D ~40% blend) + faint silhouette feature (cached sprite at ~20% opacity) + 0.8s breathing pulse (shader uniform or tween) + ±0.5 tile position drift (approx position, not exact). Silhouette feature: Kael's vest, Lera's apron (sprite metadata from entity.silhouette_feature field).
|
||||
- **Unrecognized entity in fog:** Entity with `pending: true` renders as neutral grey #555566 blob (simple circle sprite, no identifying features). No silhouette. No D-033 color.
|
||||
- **Recognition transition:** When `pending` flips from true → false, tween grey blob → color+silhouette over 0.3s (within the 0.6s cognitive delay window per D-060). Monologue fires during this tween.
|
||||
|
||||
**Integration points:**
|
||||
- Pairs with server #423 (cognitive delay system, done Sprint 7)
|
||||
- Protocol v7 wire format must match server ObserverSnapshot struct additions
|
||||
- Builds on fog shader from Sprint 6
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Sound pings and fog entities render on layer 6 (insert overlay), NOT layer 5 (fog shader). Insert data is computational, not perceptual — not affected by vision cone.
|
||||
- ±0.5 tile drift is VISUAL only (client-side randomness for "approximate" feel). Server sends exact position; client adds noise.
|
||||
- Silhouette feature sprite is entity metadata, not a full sprite. Think "identifying accessory" not "full character render."
|
||||
- Tween recognition over 0.3s to align with monologue timing (D-060: monologue fires DURING delay, not after)
|
||||
|
||||
---
|
||||
|
||||
### #435: Dialogue response selection — max 3 visible, invisible locks (HIGH)
|
||||
**What exists:**
|
||||
- `client/ui/interaction_list.gd` — Vertical list for entity interaction verbs (Talk, Observe, etc.)
|
||||
- `client/scripts/protocol/protocol.gd` — Decodes NearbyInteraction from snapshot
|
||||
- D-061 dialogue box spec: bottom screen, max 20% height, max-width, no portraits
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **New scene:** `dialogue_box.tscn` — Bottom-screen container with two sections: NPC speech (Label, top), player response options (VBoxContainer, bottom). Max 20% viewport height, max-width (exact value: viewport width minus 40px margin). Left-aligned text. Insert-styled per D-057/D-061.
|
||||
- **Response option rendering:** Each response is a Button (or custom Control with hover state). Show max 3 visible options. Sort by priority (server provides sorted list). D-062: locked options are invisible (not rendered at all). No grayed-out options. No lock icons. Player doesn't know what they don't know.
|
||||
- **New protocol field:** `dialogue_response` in ObserverSnapshot — array of {text: String, response_id: String, priority: int}. Decode in protocol.gd.
|
||||
- **Integration:** When player selects Talk verb from interaction_list, server sends snapshot with dialogue_response populated. Client shows dialogue_box, hides interaction_list. Player clicks response → send PlayerInput::DialogueResponse{response_id}.
|
||||
- **Monologue interaction:** Monologue floats ABOVE dialogue box on z-layer 7 (spatial separation, D-061). Both can be visible simultaneously — character thinks one thing while NPC says another.
|
||||
|
||||
**Integration points:**
|
||||
- Blocked by #434 (interaction verb execution, done Sprint 7)
|
||||
- Pairs with server #305 (dialogue selection pipeline)
|
||||
- Blocks #436 (confrontation styling)
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Max 3 VISIBLE means if server sends 5 options, show top 3 by priority. But D-062 invisible locks mean server should never send locked options at all (server filters before protocol). Client just renders what it receives.
|
||||
- Player response text can be variable length (confrontation lines are longer than casual small talk). Use word wrap, auto-expand button height.
|
||||
- No close button per D-064 — walk-away is the exit. Dialogue box stays until player walks away or server sends empty dialogue_response (conversation ended).
|
||||
|
||||
---
|
||||
|
||||
### #437: Walk-away mechanic — WASD detect during dialogue, 300ms fade (MEDIUM)
|
||||
**What exists:**
|
||||
- Input handling in `client/scripts/main.gd` captures WASD for movement, sends PlayerInput::Move
|
||||
- Dialogue box (#435) renders dialogue but doesn't handle walk-away yet
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Input override:** When dialogue_box is visible, WASD input triggers walk-away instead of movement. Send PlayerInput::WalkAway{npc_id} to server.
|
||||
- **Visual fade:** Dialogue box fades out over 300ms (Tween modulate alpha 1.0 → 0.0). No sound. Silence. D-064 phase 1.
|
||||
- **NPC may react:** Server may send updated NPC animation or monologue after walk-away (D-064 phase 2). Client renders these as normal snapshot updates — no special walk-away animation on client.
|
||||
- **KG recording:** Server handles D-064 phase 3 (#427). Client just sends WalkAway input.
|
||||
|
||||
**Integration points:**
|
||||
- Pairs with server #427 (walk-away KG recording)
|
||||
- Depends on #435 (dialogue box exists)
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Walk-away is ANY WASD press during dialogue, not just "walk away from NPC." Even pressing W (toward NPC) triggers walk-away. The direction doesn't matter — the intent to move ends the conversation.
|
||||
- After WalkAway input sent, dialogue box fades but player can't move until fade completes (300ms input block). Prevents "walk through NPC" edge case.
|
||||
- If server sends new dialogue_response during fade (NPC talks again), cancel fade and re-show dialogue. Rare edge case but possible (NPC calls after player).
|
||||
|
||||
---
|
||||
|
||||
### #436: Confrontation text styling — italic voice, monologue beat (MEDIUM)
|
||||
**What exists:**
|
||||
- Dialogue box (#435) renders NPC speech + player responses
|
||||
- D-063 spec: confrontation uses same UI, different weight via styling + monologue beat
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Confrontation option styling:** Response options marked as `confrontation: true` (new protocol field) render in italic + first-person voice. Example: regular = "Shift schedule", confrontation = *"I saw you in corridor B-7"* (italic BBCode tag).
|
||||
- **Pre-delivery monologue beat:** When player clicks confrontation option, 1-2 second pause before sending DialogueResponse input. During pause, show monologue: *"This changes things. No taking it back."* (hardcoded v0.1 line, future from content pools). Monologue displays on layer 7, dialogue box dims slightly (reduce opacity to 70%).
|
||||
- **Audio dip:** Ambient audio volume drops to 50% during confrontation selection (via AudioServer bus manipulation). Restore after response sent.
|
||||
- **No camera change:** D-063 defers camera tighten to post-v0.1. Same camera position throughout.
|
||||
|
||||
**Integration points:**
|
||||
- Blocked by #435 (dialogue box)
|
||||
- Pairs with server #305 (dialogue pipeline marks confrontation lines)
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Monologue beat is client-side delay (pause before sending input). Server doesn't know about the pause — from server's view, DialogueResponse arrives 1-2s after Talk verb.
|
||||
- Italic rendering: use BBCode `[i]...[/i]` in RichTextLabel. Standard Label doesn't support italics.
|
||||
- Don't apply italic to NPC speech — only to player confrontation responses. Regular dialogue responses stay non-italic.
|
||||
- Audio dip is temporary (duration of monologue beat only). Restore ambient volume after response sent, not after NPC replies.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#431 (fog visualization) → standalone, protocol v7 prerequisite for other features
|
||||
#435 (dialogue box) → #437 (walk-away) depends on this, #436 (confrontation styling) depends on this
|
||||
#437 (walk-away) → standalone once #435 exists
|
||||
#436 (confrontation styling) → depends on #435
|
||||
```
|
||||
|
||||
## 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(ui): dialogue pipeline client (Sprint 8 client)" --description "Implements #431 #435 #437 #436 — fog entity viz, dialogue response selection, walk-away, confrontation styling" --base main --head client
|
||||
```
|
||||
@@ -0,0 +1,125 @@
|
||||
# Sprint 8: React — Joint / Integration Tasks
|
||||
|
||||
**Goal:** Activate the dialogue pipeline end-to-end — player talks to NPCs, NPCs respond, the world reacts.
|
||||
|
||||
**Teams:** Server + Client
|
||||
**Agents:** All implementation agents
|
||||
|
||||
## Pre-Sprint
|
||||
|
||||
None. All required decisions (D-028, D-060, D-062, D-063, D-064) are confirmed.
|
||||
|
||||
## Sprint Completion Proof
|
||||
|
||||
Concrete observable criteria when Sprint 8 is done:
|
||||
|
||||
1. **Dialogue flow works end-to-end:**
|
||||
- Player walks to Kael (or any NPC), presses [E], selects Talk
|
||||
- NPC greeting appears in dialogue box (bottom 20% screen, max-width)
|
||||
- 2-3 player response options visible below NPC text
|
||||
- Player clicks response → NPC replies with new text + new options
|
||||
- Conversation continues until player walks away (WASD)
|
||||
|
||||
2. **Walk-away triggers visual + server recording:**
|
||||
- Player in dialogue with NPC, presses WASD (any direction)
|
||||
- Dialogue box fades out over 300ms, silence
|
||||
- Server logs incomplete interaction in KG (verify via debug print or db query)
|
||||
- Player can walk away mid-conversation without crash
|
||||
|
||||
3. **Confrontation option appears and feels different:**
|
||||
- Detective character approaches Sera Venn after observing corridor contradiction
|
||||
- Talk → response options include at least one confrontation line (italic, first-person: *"I saw you avoiding Torek..."*)
|
||||
- Click confrontation → 1-2s pause, monologue appears ("This changes things..."), ambient audio dips to 50%
|
||||
- After pause, NPC responds (text changes, relationship may shift color on next observation)
|
||||
|
||||
4. **Fog entity recognition timing:**
|
||||
- NPC enters fog (outside vision cone but within sound/thermal range)
|
||||
- Grey blob appears for 0.6s (6 ticks normal, or 0.3s/3 ticks if anomaly)
|
||||
- During delay, monologue fires ("Those footsteps... that's Kael's walk")
|
||||
- After delay, grey blob transitions to D-033 color + faint silhouette over 0.3s
|
||||
|
||||
5. **Sound ping rendering:**
|
||||
- NPC makes loud sound outside LOS (door slam, footsteps, voice)
|
||||
- 2-3 concentric rings expand from sound direction on insert overlay (white-blue, sonar aesthetic)
|
||||
- Rings fade over 1.5s
|
||||
- No crash, no visual glitches
|
||||
|
||||
6. **Invisible locks work:**
|
||||
- Player talks to Sera with Surface trust level
|
||||
- Gets 2-3 response options (small talk, routine questions)
|
||||
- Player CANNOT see that Real/Secret tier options exist (no grayed-out text, no lock icons, no hint)
|
||||
- After trust increase (via repeated positive interactions or evidence discovery), new options appear on next Talk — player realizes "wait, there's more now"
|
||||
|
||||
## Test Plan Alignment (D-030 phases)
|
||||
|
||||
**Phase 2 (Sprint 3-4): Integration tests**
|
||||
- Sprint 8 falls in integration test phase
|
||||
- Required: End-to-end dialogue pipeline integration test (server sends dialogue_response, client renders, player input returns, server logs)
|
||||
- Required: Walk-away flow integration test (client sends WalkAway, server records KG, dialogue state clears)
|
||||
- Negative test: Locked dialogue options are NOT sent to client (server filters before protocol, client never sees them)
|
||||
|
||||
## Cross-Team Dependencies
|
||||
|
||||
| Ticket | Team | Depends on | Delivers to |
|
||||
|--------|------|------------|-------------|
|
||||
| #305 | server | (none) | Client #435 consumes dialogue_response protocol field |
|
||||
| #427 | server | #452 (ContentSlug) | Client #437 triggers via WalkAway input |
|
||||
| #435 | client | #434 (done) | Server #305 sends data to render |
|
||||
| #436 | client | #435 | Uses dialogue box from #435 |
|
||||
| #437 | client | #435 | Pairs with server #427 |
|
||||
| #450 | server | (none) | Enables urgent recognition for #431 |
|
||||
| #451 | server | #450 | Pairs with client #431 fog visualization |
|
||||
| #431 | client | Protocol v7 | Consumes pending_recognitions from server |
|
||||
|
||||
**Critical path:** Server #305 (dialogue pipeline) + Client #435 (dialogue box) are the headline integration. Everything else is enhancement/polish around that core.
|
||||
|
||||
**Parallel tracks:**
|
||||
- Track 1 (dialogue): #305 (server) ↔ #435 (client) → #436 (confrontation styling) → #437 (walk-away) ↔ #427 (server KG)
|
||||
- Track 2 (perception): #450 (server anomaly) → #451 (server monologue) → #431 (client fog viz)
|
||||
- Track 3 (infrastructure): #452 (server ContentSlug) → used by #427
|
||||
|
||||
## Integration Risks
|
||||
|
||||
1. **Protocol version mismatch:** Client #431 bumps protocol to v7 (pending_recognitions field). Server must match. Verify protocol.gd PROTOCOL_VERSION == server bridge/types.rs PROTOCOL_VERSION after merge.
|
||||
|
||||
2. **Dialogue response format:** Server #305 populates `dialogue_response: Vec<DialogueOption>` in ObserverSnapshot. Client #435 must decode this correctly (MessagePack array of maps). Test with 0 options (no dialogue), 1 option, 3 options, and 5+ options (client shows top 3).
|
||||
|
||||
3. **Walk-away during network lag:** Client sends WalkAway input. Server may send 1-2 more dialogue snapshots in flight before WalkAway is processed. Client should handle: fade dialogue box, ignore late dialogue_response updates.
|
||||
|
||||
4. **Cognitive delay + monologue timing:** Server #451 fires monologue at delay START (when grey blob appears). Client #431 must show monologue DURING the 0.6s grey-to-color transition. If monologue arrives after transition completes, timing feels wrong. Verify monologue timestamp is within delay window.
|
||||
|
||||
5. **Confrontation italic rendering:** Client uses BBCode `[i]...[/i]` for italic. If NPC name or response text contains special BBCode chars (`[`, `]`), text will break. Server should escape BBCode in dialogue text, or client should use `bbcode_text.c_escape()` before rendering.
|
||||
|
||||
## Notes
|
||||
|
||||
**Server-side dialogue filtering (D-062 invisible locks):**
|
||||
- Server filters dialogue options BEFORE sending to client. Client never sees locked options in protocol.
|
||||
- Access tier filter: RelationshipState → AccessTier mapping must be shared understanding (or document in decisions/content.md amendment)
|
||||
- Trust tier filter: KG.relationship → TrustTier. Unknown = Surface, Known/Friend = Real, PersonOfInterest+Secret fact = Secret.
|
||||
- Situation filter: Game state (time, location, NPC routine) → active Situation tags. Document the mapping function (or make it data-driven YAML config).
|
||||
|
||||
**Client-side dialogue rendering (max 3 visible, D-061 20% height):**
|
||||
- If NPC sends 5 valid options (all unlocked, all pass filters), client shows top 3 by priority.
|
||||
- If later in conversation NPC sends 2 options, client shows 2.
|
||||
- Dialogue box height scales with content (NPC text + options) but never exceeds 20% viewport height. If content overflows, scroll NPC text, keep options visible.
|
||||
|
||||
**Monologue + dialogue spatial separation (D-061, D-063):**
|
||||
- Monologue floats above dialogue box on z-layer 7.
|
||||
- During confrontation beat: monologue visible ("This changes things..."), dialogue box dimmed to 70% opacity.
|
||||
- After beat: monologue fades, dialogue box returns to 100% opacity, NPC response appears.
|
||||
|
||||
**Audio dip during confrontation (D-063):**
|
||||
- Client-side: When player clicks confrontation option, AudioServer.set_bus_volume_db(ambient_bus, -6.0) (50% volume).
|
||||
- After monologue beat + response sent, restore: AudioServer.set_bus_volume_db(ambient_bus, 0.0).
|
||||
- Duration: ~1-2 seconds (duration of monologue beat). Not tied to NPC response arrival.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. All design decisions confirmed. If blockers arise during implementation, escalate to Team Leader (Jeroen).
|
||||
|
||||
## Files Written
|
||||
|
||||
- /var/home/jeroenschweitzer/Projects/settled-reach/main/docs/sprints/sprint-8/server.md
|
||||
- /var/home/jeroenschweitzer/Projects/settled-reach/main/docs/sprints/sprint-8/client.md
|
||||
- /var/home/jeroenschweitzer/Projects/settled-reach/main/docs/sprints/sprint-8/audio.md
|
||||
- /var/home/jeroenschweitzer/Projects/settled-reach/main/docs/sprints/sprint-8/joint.md
|
||||
@@ -0,0 +1,168 @@
|
||||
# Sprint 8: React — Server Tasks
|
||||
|
||||
**Goal:** Activate the dialogue pipeline end-to-end — player talks to NPCs, NPCs respond, the world reacts.
|
||||
|
||||
**Branch:** `server`
|
||||
**Agents:** Dudley (dev), Tyre (arch), Hoshe (QA)
|
||||
|
||||
## Carry-over from Sprint 7
|
||||
|
||||
None. Sprint 7 server tickets (#308, #326, #427 partial) completed.
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #305 | Dialogue selection pipeline — 4-layer filtering engine | — |
|
||||
| #427 | Walk-away KG recording | — |
|
||||
| #450 | Wire RecognitionTrigger::Urgent to anomaly detection | — |
|
||||
| #451 | Monologue fires during cognitive delay window | — |
|
||||
| #452 | Add ContentSlug component | — |
|
||||
|
||||
Use `db/connectors/ticket show <id>` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/content.md` — D-028 (dialogue architecture: tagged line pools with 4 relational layers), D-035 (tag taxonomy), D-062 (invisible locked options), D-063 (confrontation same box), D-064 (walk-away 3-phase consequences)
|
||||
- `decisions/architecture.md` — D-041 (knowledge graph data model)
|
||||
- `decisions/perception.md` — D-060 (cognitive delay for fog recognition), D-061 (dialogue box layout)
|
||||
|
||||
## Open Questions to Resolve Early
|
||||
|
||||
None.
|
||||
|
||||
## Notes
|
||||
|
||||
### #305: Dialogue selection pipeline — 4-layer filtering engine (CRITICAL)
|
||||
**What exists:**
|
||||
- `server/src/content/line_pool.rs` — LinePoolIndex with full D-028 Layer 1-3 filtering already implemented:
|
||||
- `query_dialogue()` filters by access tier, situation, and trust
|
||||
- All tag enums defined (AccessTier, TrustTier, Situation, Topic, Mood, Trigger, Character)
|
||||
- Indexed pools built from ContentStore at startup
|
||||
- `server/src/content/mod.rs` — ContentPlugin loads YAML content and builds LinePoolIndex resource
|
||||
- `server/src/simulation/interaction.rs` — compute_nearby_interactions() generates verb options but doesn't yet invoke dialogue selection
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Layer 4 (topic + mood weighted selection):** New system `select_dialogue_line()` that takes the filtered lines from `query_dialogue()` and scores them by topic/mood match
|
||||
- **NPC mood component:** Add `CurrentMood(Mood)` component to NPCs, computed from NPC axes (Tolerance, Contentment, recent events)
|
||||
- **Situation derivation:** Map current game state (time of day, location, NPC routine phase, interaction history) → active Situation tags
|
||||
- **Integration with Talk verb:** When player selects Talk from interaction list, trigger dialogue pipeline: access tier (from KG RelationshipState) → situations (from context) → trust tier (from KG) → topic+mood scoring → select line → send to client via ObserverSnapshot.dialogue_response
|
||||
- **Deliverable:** Full pipeline from Talk verb → selected line text on client dialogue box. Test with 1 NPC (Kael or Sera), 3-5 lines per situation.
|
||||
|
||||
**Integration points:**
|
||||
- Reads LinePoolIndex resource (already exists)
|
||||
- Reads KnowledgeGraph component for access/trust tier
|
||||
- Writes to new DialogueResponse buffer component for snapshot inclusion
|
||||
- Pairs with client #435 (dialogue response selection UI)
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Access tier mapping: RelationshipState → AccessTier needs a lookup table (Unknown=public, Known=peer, PersonOfInterest=peer+authority for detective, Friend=insider)
|
||||
- Situation activation: NPC's current RoutinePhase + game time (D-031 phases) + location → 1-3 active situations. Don't hardcode — use a mapping table/function.
|
||||
- Layer 4 is weighted, not hard filter. Empty topic/mood tags on lines should NOT exclude them — they're topic-neutral.
|
||||
- Cooldown tracking per line ID to prevent repeating the same line within a session (MonologueState pattern, but for dialogue)
|
||||
|
||||
---
|
||||
|
||||
### #427: Walk-away KG recording (medium)
|
||||
**What exists:**
|
||||
- `server/src/knowledge/graph.rs` — KnowledgeGraph component with entity/fact maps
|
||||
- Client #437 detects WASD during dialogue, sends WalkAway input event
|
||||
- D-064 specifies 3-phase consequence model
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **KnowledgeEvent::IncompleteInteraction:** New event type carrying NPC stable_id, interaction_type (Talk/Confront), tick
|
||||
- **process_walk_away_input():** System that handles PlayerInput::WalkAway during active dialogue. Emits IncompleteInteraction event, clears dialogue state.
|
||||
- **KG recording:** process_knowledge_events() adds entry to entities[npc_id].known_attributes["incomplete_interactions"] = array of {tick, type}
|
||||
- **Future query hook:** Stub function `has_incomplete_interaction(npc_id) -> bool` for dialogue/monologue to check (not called in v0.1, but scaffolds D-064 phase 3)
|
||||
|
||||
**Integration points:**
|
||||
- Pairs with client #437 (WASD detect during dialogue)
|
||||
- Builds on KnowledgeGraph infrastructure from Sprint 2
|
||||
- Future: monologue system (#451) will check this for post-conversation triggers
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Walk-away during confrontation vs casual talk should both record, but the `interaction_type` field differentiates them (future dialogue may react differently)
|
||||
- Don't emit KnowledgeEvent if no dialogue was active (player hits WASD while near NPC but not talking)
|
||||
|
||||
---
|
||||
|
||||
### #450: Wire RecognitionTrigger::Urgent to anomaly detection (medium)
|
||||
**What exists:**
|
||||
- `server/src/perception/cognitive_delay.rs` — Full cognitive delay system with RecognitionTrigger enum (Normal = 0.6s, Urgent = 0.3s)
|
||||
- `server/src/perception/observation.rs` — emit_observation_events() queues PendingRecognition but hardcodes RecognitionTrigger::Normal (line 82: `// TODO(#450)`)
|
||||
- ObserveAnomaly monologue trigger defined in line_pool.rs Trigger enum
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Anomaly detection:** Add system `detect_anomalies()` that marks entities as anomalous when KG.relationship == PersonOfInterest OR KG.state == Contradicted
|
||||
- **Component:** `AnomalyMarker` — transient marker on entities flagged as anomalous this tick (cleared each tick)
|
||||
- **Wire to cognitive delay:** In emit_observation_events(), if target has AnomalyMarker, use RecognitionTrigger::Urgent instead of Normal
|
||||
- **Verify:** Contradicted entity in fog resolves in 0.3s (3 ticks) instead of 0.6s (6 ticks)
|
||||
|
||||
**Integration points:**
|
||||
- Builds on Sprint 7 cognitive delay system (#423 done)
|
||||
- Future: monologue system will also check AnomalyMarker for ObserveAnomaly trigger priority
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- AnomalyMarker is per-tick — clear it at tick start, recompute each tick based on KG state
|
||||
- Don't mark Player entity as anomalous (causes unnecessary self-checks)
|
||||
- Sprint double-take (#428 done) already fires monologue for sprint-past anomalies; this ticket affects fog recognition timing only
|
||||
|
||||
---
|
||||
|
||||
### #451: Monologue fires during cognitive delay window (medium)
|
||||
**What exists:**
|
||||
- `server/src/simulation/monologue.rs` — Trigger enum includes ObserveAnomaly
|
||||
- `server/src/perception/cognitive_delay.rs` — CognitiveDelay component tracks pending recognitions with tick countdown
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Trigger during delay:** When a PendingRecognition is queued (entity enters fog), immediately check monologue pools for `trigger: observe_anomaly` + character match
|
||||
- **Recognition monologue:** New system `trigger_recognition_monologue()` that fires when cognitive delay starts (NOT when it completes). Line text should match D-060 spec: "Those footsteps... that's Kael's walk" — character is thinking during the delay.
|
||||
- **Pair with visual:** Monologue appears DURING the grey-blob → color+silhouette transition (client shows both simultaneously)
|
||||
|
||||
**Integration points:**
|
||||
- Reads CognitiveDelay.pending() to detect new recognitions
|
||||
- Writes to MonologueBuffer (existing pattern from #414)
|
||||
- Pairs with client #431 (fog entity visualization with delay animation)
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- Fire monologue at delay START (when grey blob appears), not at delay END (when color resolves). The D-060 design is "monologue IS the recognition process."
|
||||
- Don't fire recognition monologue for entities already in KG (only for new entities entering perception)
|
||||
- Cooldown still applies — don't spam recognition monologue every tick if the same entity blinks in/out of fog
|
||||
|
||||
---
|
||||
|
||||
### #452: Add ContentSlug component (medium)
|
||||
**What exists:**
|
||||
- Content loading in `server/src/content/spawn.rs` spawns NPCs from YAML but doesn't attach content IDs
|
||||
- KnowledgeGraph interaction memory (#427) will need to reference "which NPC" for dialogue history
|
||||
|
||||
**What the ticket needs to deliver:**
|
||||
- **Component:** `ContentSlug(String)` — stable content identifier from YAML (e.g. "kael-davan", "sera-venn")
|
||||
- **Attach during spawn:** content/spawn.rs reads `npc.slug` field from YAML and attaches ContentSlug component to spawned NPC entity
|
||||
- **Use in KG:** Interaction memory records ContentSlug instead of Entity (Entity is unstable across save/load; ContentSlug is stable)
|
||||
|
||||
**Integration points:**
|
||||
- Used by #427 (walk-away recording)
|
||||
- Future: dialogue Layer 2 (relationship history) will query past interactions by ContentSlug
|
||||
|
||||
**Non-obvious gotchas:**
|
||||
- ContentSlug is independent of StableId. StableId is runtime entity tracking (KG references). ContentSlug is authoring/content identity (which authored NPC template).
|
||||
- Not all entities have ContentSlugs (e.g., procedurally spawned NPCs, furniture). Component is optional.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#450 (anomaly detection) → standalone, enables urgent recognition
|
||||
#451 (monologue during delay) → depends on #450 for anomaly context
|
||||
#452 (ContentSlug) → #427 (walk-away KG) depends on this
|
||||
#305 (dialogue pipeline) → standalone, headline feature
|
||||
#427 (walk-away KG) → depends on #452, pairs with client #437
|
||||
```
|
||||
|
||||
## 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(content): dialogue selection pipeline (Sprint 8 server)" --description "Implements #305 #427 #450 #451 #452 — dialogue 4-layer filtering, walk-away KG, urgent recognition, delay monologue, ContentSlug component" --base main --head server
|
||||
```
|
||||
Reference in New Issue
Block a user