Files
settled-reach/docs/sprints/sprint-14/client.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.7 KiB

title, description, type, status, sprint, team
title description type status sprint team
Sprint 14 — Client Briefing Monologue display, passive dialogue panel, F3 debug overlay sprint archived 14 client

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 <id> 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 #247ConversationEvent 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):

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(client): description" --description "body" --base main --head client