Sprint 24 capstone client delivery — three tickets wiring the storyteller's activation event into player-visible consequences:
#588 Character archetype select screen: two-card UI (Smuggler/Detective) between New Game and session start. Keyboard+mouse selection, ESC cancels. GameState.character_archetype persisted and sent in StartupMessage. PROTOCOL_VERSION bumped to 19.
#590 Triangle activation consumer: decodes triangle_crisis_events from snapshot, fires sfx_monologue_chime_urgent once per triangle per session. No overlay UI — the monologue system handles the narrative response.
#592 News ticker HUD: scrolling marquee on UILayer, visible only when current_ticker is present in snapshot (The Last Shift zone). Zero-arg update_from_state reads from GameState.current_snapshot.
Coordination
⚠️PROTOCOL_VERSION = 19 — hard coordination point with server PR (already merged to main). Client and server must land together or handshake will reject.
Test plan
16 new tests in test_signal_sprint24.gd (all passing)
test_protocol_bridge.gd updated for v19 (passing)
Character select: New Game → card selection → archetype in GameState → correct StartupMessage
Triangle chime: fires once on first triangle_crisis_events, does not re-fire on subsequent ticks
News ticker: visible with text when current_ticker present, hidden when null
## Summary
Sprint 24 capstone client delivery — three tickets wiring the storyteller's activation event into player-visible consequences:
- **#588** Character archetype select screen: two-card UI (Smuggler/Detective) between New Game and session start. Keyboard+mouse selection, ESC cancels. `GameState.character_archetype` persisted and sent in `StartupMessage`. `PROTOCOL_VERSION` bumped to 19.
- **#590** Triangle activation consumer: decodes `triangle_crisis_events` from snapshot, fires `sfx_monologue_chime_urgent` once per triangle per session. No overlay UI — the monologue system handles the narrative response.
- **#592** News ticker HUD: scrolling marquee on UILayer, visible only when `current_ticker` is present in snapshot (The Last Shift zone). Zero-arg `update_from_state` reads from `GameState.current_snapshot`.
## Coordination
⚠️ **PROTOCOL_VERSION = 19** — hard coordination point with server PR (already merged to main). Client and server must land together or handshake will reject.
## Test plan
- [ ] 16 new tests in `test_signal_sprint24.gd` (all passing)
- [ ] `test_protocol_bridge.gd` updated for v19 (passing)
- [ ] Character select: New Game → card selection → archetype in GameState → correct StartupMessage
- [ ] Triangle chime: fires once on first `triangle_crisis_events`, does not re-fire on subsequent ticks
- [ ] News ticker: visible with text when `current_ticker` present, hidden when null
- [ ] No regressions in pre-existing test suite
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sprint 24 Signal — three client tickets delivering the player-facing
storyteller feedback loop:
- #588: Character archetype select screen between New Game and session
start. Two-card UI (Smuggler/Detective), keyboard+mouse, ESC cancels.
GameState.character_archetype persisted and sent in StartupMessage.
PROTOCOL_VERSION bumped to 19.
- #590: Triangle crisis event consumer. Decodes triangle_crisis_events
from snapshot, fires sfx_monologue_chime_urgent once per triangle per
session via AudioManager.CHIME_ACTIVATION.
- #592: News ticker HUD element. Scrolling marquee on UILayer, visible
only when current_ticker is present in snapshot (Last Shift zone).
Zero-arg update_from_state reads from GameState.current_snapshot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 tests covering character select, triangle activation consumer,
news ticker, and protocol v19 bridge. Includes show/hide behavior
for ticker on null current_ticker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three features well-structured with solid test coverage. Two correctness bugs need fixing.
#
File:line
Severity
Issue
1
protocol.gd:473
critical
String.capitalize() silently passes unknown archetype strings to server with no validation. Should be explicit match with push_error fallback.
2
main.gd:306–314
critical
_known_triangle_ids never cleared on teleport — _teleport_transition() clears _known_recognition_ids but not triangle IDs. Chime won't re-fire after room change.
3
news_ticker.gd:40–41
warning
get_minimum_size() called before layout pass — first headline scroll distance will be wrong. Defer width read by one frame.
4
test_signal_sprint24.gd
warning
No test for unknown archetype validation path in encode_startup_message.
5
test_signal_sprint24.gd
warning
Triangle chime dedup logic in main.gd untested — only Protocol decode is covered.
6
session_manager.gd:151–157
suggestion
save_character_archetype() not called inside new_game() — callers must pair them manually.
7
test_protocol_bridge.gd:285–288
suggestion
Hardcoded version 8 in harness test is misleading vs protocol v19.
8
character_select.gd:86–87
suggestion
String node paths ("CardBorder", "CardInner") not validated at parse time.
9
main_menu.gd:57–64
suggestion
Synchronous load() of character select scene — inconsistent with threaded pattern used elsewhere.
Tyre (Architecture): APPROVE
Protocol v19 contract matches server. Character select flow correctly threads archetype through GameState → SessionManager → StartupMessage. D-012 respected — triangle events carry only opaque IDs. Zone-gating is server-authoritative per D-020.
#
File:line
Severity
Issue
1
main.gd:306–314
warning
_known_triangle_ids not cleared on teleport (same as Hoshe #2).
- protocol.gd: replace capitalize() with explicit match for archetype
string mapping, push_error on unknown input with Detective fallback
- main.gd: clear _known_triangle_ids in _teleport_transition() alongside
_known_recognition_ids so chime re-fires after room change
- news_ticker.gd: defer get_minimum_size() via call_deferred to run
after layout pass, fixing first-frame scroll distance
- 3 new tests: unknown archetype fallback, triangle dedup per-id,
independent triangle ID firing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
✅_known_triangle_ids.clear() in _teleport_transition()
✅ Explicit match replaces capitalize() with push_error fallback
✅ Ticker width deferred via call_deferred("_update_content_width")
✅ Unknown archetype test added
✅ Triangle dedup tests added (2 tests)
✅ Intentional v0.1 field omission comment on triangle decode
Tyre (Architecture): APPROVE
Teleport clear correctly placed alongside recognition ID clear. Archetype match mirrors server #[serde(default)] — both sides default to Detective on bad input. Wire field omission comment accurate.
Verdict: APPROVED ✅
Ready for merge.
## Re-review: `client` → `main` (PR #86, round 2)
### Hoshe (Code Quality): APPROVE
All 5 round-1 items verified resolved:
1. ✅ `_known_triangle_ids.clear()` in `_teleport_transition()`
2. ✅ Explicit `match` replaces `capitalize()` with `push_error` fallback
3. ✅ Ticker width deferred via `call_deferred("_update_content_width")`
4. ✅ Unknown archetype test added
5. ✅ Triangle dedup tests added (2 tests)
6. ✅ Intentional v0.1 field omission comment on triangle decode
### Tyre (Architecture): APPROVE
Teleport clear correctly placed alongside recognition ID clear. Archetype match mirrors server `#[serde(default)]` — both sides default to Detective on bad input. Wire field omission comment accurate.
### Verdict: APPROVED ✅
Ready for merge.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Sprint 24 capstone client delivery — three tickets wiring the storyteller's activation event into player-visible consequences:
GameState.character_archetypepersisted and sent inStartupMessage.PROTOCOL_VERSIONbumped to 19.triangle_crisis_eventsfrom snapshot, firessfx_monologue_chime_urgentonce per triangle per session. No overlay UI — the monologue system handles the narrative response.current_tickeris present in snapshot (The Last Shift zone). Zero-argupdate_from_statereads fromGameState.current_snapshot.Coordination
⚠️ PROTOCOL_VERSION = 19 — hard coordination point with server PR (already merged to main). Client and server must land together or handshake will reject.
Test plan
test_signal_sprint24.gd(all passing)test_protocol_bridge.gdupdated for v19 (passing)triangle_crisis_events, does not re-fire on subsequent tickscurrent_tickerpresent, hidden when null🤖 Generated with Claude Code
Review:
client→main(PR #86, type: code)Hoshe (Code Quality): REQUEST_CHANGES
Three features well-structured with solid test coverage. Two correctness bugs need fixing.
protocol.gd:473String.capitalize()silently passes unknown archetype strings to server with no validation. Should be explicit match withpush_errorfallback.main.gd:306–314_known_triangle_idsnever cleared on teleport —_teleport_transition()clears_known_recognition_idsbut not triangle IDs. Chime won't re-fire after room change.news_ticker.gd:40–41get_minimum_size()called before layout pass — first headline scroll distance will be wrong. Defer width read by one frame.test_signal_sprint24.gdencode_startup_message.test_signal_sprint24.gdmain.gduntested — only Protocol decode is covered.session_manager.gd:151–157save_character_archetype()not called insidenew_game()— callers must pair them manually.test_protocol_bridge.gd:285–2888in harness test is misleading vs protocol v19.character_select.gd:86–87"CardBorder","CardInner") not validated at parse time.main_menu.gd:57–64load()of character select scene — inconsistent with threaded pattern used elsewhere.Tyre (Architecture): APPROVE
Protocol v19 contract matches server. Character select flow correctly threads archetype through GameState → SessionManager → StartupMessage. D-012 respected — triangle events carry only opaque IDs. Zone-gating is server-authoritative per D-020.
main.gd:306–314_known_triangle_idsnot cleared on teleport (same as Hoshe #2).protocol.gd:259–268role_assignments,trigger_npc_id,tickfrom wire. Add comment noting intentional v0.1 omission.main.gd:307,news_ticker.gd:28Verdict: CHANGES REQUESTED
Must-fix items:
_known_triangle_ids.clear()in_teleport_transition()(both reviewers flagged)encode_startup_message— explicit match, notcapitalize()_content_width— defer measurement by one frameRe-review:
client→main(PR #86, round 2)Hoshe (Code Quality): APPROVE
All 5 round-1 items verified resolved:
_known_triangle_ids.clear()in_teleport_transition()matchreplacescapitalize()withpush_errorfallbackcall_deferred("_update_content_width")Tyre (Architecture): APPROVE
Teleport clear correctly placed alongside recognition ID clear. Archetype match mirrors server
#[serde(default)]— both sides default to Detective on bad input. Wire field omission comment accurate.Verdict: APPROVED ✅
Ready for merge.
Pull request closed