- Cast Variant to String via str() before passing to _on_room_change
- Clear _current_room_id on null room transition (fixes re-entry skip)
- Add push_error for failed dir creation and file writes in _save_report
- Fix docstring: tests/gauntlet-stats.json → user://dev/gauntlet-stats.json
- Namespace stats path to user://dev/ to avoid save data collision
- Replace print() with push_warning in _save_report (codebase consistency)
- Downgrade client-only wire guard from push_warning to silent return
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-generate monologue chimes (were oversized placeholders) and
fog_recognition (was silent). Fix sample rate headers on remaining
assets (192kHz→44.1kHz). All 8 UI assets now at 44.1kHz stereo,
LUFS -16 normalized, Vorbis q6, durations within D-038 spec.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
F12 pauses simulation, shows modal LineEdit prompt, saves three files
to user://bug-reports/gauntlet-t{tick}-{timestamp}/: snapshot.json
(full ObserverSnapshot), render.txt (simplified client-side text
render), description.txt (tester notes + tick/room/stance metadata).
Esc cancels without saving. Double-activation guard prevents stacking.
BUG_REPORT action added to InputMapper with wire guard in SimBridge
(client-only, never sent to server). Dialog on ModalLayer (CL 30).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GauntletHUD in UILayer shows TIMER: MM:SS (PB: MM:SS). Timer starts on
room entry, resets on room change, records personal bests to
user://gauntlet-stats.json. Session summary printed on disconnect.
Hidden in non-gauntlet mode (anti-tedium guard).
Adds room_id and gauntlet_mode fields to GameState, parsed from
ObserverSnapshot. Main.gd wires update_from_state() and finalize().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add SimBridge._last_snapshot = null to P3 before_test() (contamination risk)
- Clarify mono.is_visible is custom property, not CanvasItem builtin
- Camera smoothing test uses approximate equality instead of exact
- Propagate inner _encode_message errors in array/dict encoding
- Add explanatory comment on int_64 encoder branch
- Document unfalsifiable gauntlet guard as intentional future-proof
- Remove loose D-053 citation from LERP_SPEED pin
- Assert FogOverlay is sibling of FogGroup under World
- Add CANVAS_MODAL=30 (ModalLayer) assertion in P3-Z04
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fail on encode errors instead of silently writing empty .msgpack files
- Fail test on missing/empty fixture dir instead of silent skip
- Add all missing action variants (MoveSouth, MoveEast, MoveWest,
Unpause, ToggleStanceDown, WalkAway) to GDScript fixture generator
- Add GDScript fixture staleness check to make pre-pr
- Validate repo root detection before writing outside client/
- Add file.flush() before close in headless mode
- Document fixture failure recovery in DEVOPS.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regression guards for Sprint 9 QA features:
- F12 bug report (2): no-crash without handler, no queued input action
(stubs for when #495 WRONG button lands)
- Gauntlet UI hidden (3): no gauntlet nodes visible in default mode,
no room_id/gauntlet_mode in normal snapshots, stays hidden across
multiple ticks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The int_64 branch condition `-(1 << 63) <= value` overflowed in
GDScript's signed 64-bit arithmetic, making the branch unreachable.
Negative values beyond int_32 range were incorrectly encoded as
uint_64 (0xcf) instead of int_64 (0xd3). Replaced with `value < 0`.
Updated boundary tests BV-N15 and BV-N16 to expect correct int_64
header byte.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump PROTOCOL_VERSION from 7 to 8 to match server. Three changes:
1. Add dialogue_response field decoding (DialogueResponseEvent with
line_id, text, speaker_entity_id) from server #305/D-028.
2. Fix Interact encoding: server changed PlayerAction::Interact from
unit variant to struct variant with {target_entity_id, verb}.
Extract _encode_action() helper to handle this consistently.
3. Update all test assertions that checked version=7 or expected
Interact as a bare string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
test_monologue_not_duplicated_after_consumption was failing because
poll_snapshot() in test mode returns _test_snapshot() without
consuming _last_snapshot. The carry-forward logic then incorrectly
preserved the monologue. Fix: directly clear _last_snapshot to
simulate the live-mode consumption path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update existing snapshot fixtures for protocol v8 (dialogue_response
field). Add 14 raw boundary value fixtures and 5 snapshot boundary
tick fixtures for encoding edge case coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fixes:
- hide_dialogue() sent PAUSE instead of UNPAUSE, permanently freezing
simulation after every dialogue (both reviewers)
- Confrontation monologue hardcoded in GDScript constant, violating
D-042/D-020 — moved to ui-strings.yaml as dialogue.confrontation_beat
- Walk-away WASD didn't call set_input_as_handled(), letting movement
event propagate and potentially stepping on the same frame
- is_dialogue_active() returned _is_showing only — interaction list
could flash during 300ms fade gap. Now includes dialogue_active state
- Removed dead _last_dialogue_id / get_dialogue_id() state (never read)
Warnings addressed:
- Audio registry now scans res://audio/ recursively (subdirs registered)
- add_bus_effect guarded against duplicate calls in tests
- int64 encoder dead code tagged KNOWN-DEFECT, filed as ticket #516
- D-073 zone crossfade stub comment clarifies Sprint 9+ deferral
- listening_focus dip documents caller tick-gate responsibility (D-069/D-071)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define VIS_HIDDEN/PERIPHERAL/FORWARD and EXP_UNEXPLORED/EXPLORED/
VISIBLE in FogState. Replaces 7 magic number usages in _resize() and
update_from_state(). Provides stable assertion targets for fog tests.
Implements #476.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 gdUnit4 tests verifying camera behavior: SimBridge test mode
connection, snapshot pipeline, player position extraction, camera
anchor in _ready(), smoothing disable/re-enable cycle, camera tracking
across frames, and player movement following.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entity renderer: add framerate-independent position lerping so entities
slide between tiles instead of snapping. Tuned for Sprint snappiness
and Walk/Careful fluidity.
Fog shader: set ColorRect to transparent fallback so a shader load
failure doesn't paint solid white over the world. Track camera position
(not player position) so fog stays synced during smooth camera pan.
Restructure GLSL to avoid early return (some GPU drivers miscompile it).
Minor type fixes: typed Array[Vector2] in cursor_renderer tick drawing,
untyped Array in inventory_grid to avoid Godot typed-array cast issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Off-by-one in all four signed int branches: upper bound used <= instead
of < (e.g., value <= 128 matched int8 instead of falling through to
uint8). Tick 128 was encoded as int8 -128, causing server deserialization
failure. Also remove stray debug prints from nil/false decode paths.
Fixes bug #4 (MessagePack encodes tick 128 as -128).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three client input/bridge fixes:
1. Pause toggle: add UNPAUSE action and toggle logic based on
GameState.game_time.tick_rate. Wire UNPAUSE in sim_bridge.gd.
2. Hold-to-move: replace press-event movement with polled _process()
direction sampling. Composite diagonals via simultaneous keys
(W+D → northeast). Client-side throttle per stance (D-053):
Sprint=200ms, Walk=400ms, Careful=600ms, Crouch=800ms.
3. Snapshot carry-forward: when a newer snapshot overwrites an
unconsumed one, carry forward current_monologue and current_dialogue
so one-shot events aren't silently dropped.
Fixes bugs #5 (monologue lost on overwrite) and #6 (overwrite spam).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Camera2D's internal smoothed_camera_pos starts at (0,0) and lerps
toward global_position, causing a visible white-screen drift on startup.
Disable position_smoothing in _ready(), snap camera to player position
from the first snapshot, then re-enable smoothing with reset_smoothing()
after the first anchored frame. Add tick guards to monologue/dialogue
consumption to prevent re-triggering when client FPS > sim tick rate.
Fixes bug #2 (camera doesn't center on player at startup).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Version numbers in test filenames go stale every sprint. The file tests
bridge protocol features (stance, inventory, fixtures) not a specific
protocol version.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generated via Stable Audio Open with sonic family prompt prefixes:
- cursor_hover.ogg (insert-tech, digital click)
- weapon_aim.ogg (insert-tech, mechanical latch sketch)
- implant_open.ogg (insert-tech, neural lattice rising tone)
- fog_recognition.ogg (organic, cognitive realization chime)
- sfx_monologue_chime.ogg (insert-tech, placeholder per D-071)
- sfx_monologue_chime_urgent.ogg (insert-tech, placeholder per D-071)
All assets need duration trimming in next audio sprint (#453).
Updated docs/assets/audio/ui.md status to draft.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fog entity cognitive delay rendering (D-059/D-060): sonar-style sound
pings (3 concentric rings, 1.5s fade), unrecognized grey blobs with
breathing pulse, D-033 color transition at 50% recognition progress,
±0.5 tile position drift. FogEntities node at z:950 between fog
shader and InsertOverlay.
Protocol v7 bump to match server PR #23 (pending_recognitions field).
Wires dialogue box and fog entities into game loop with mock test data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
D-061 compliant UI skeleton: NPC speech top, max 3 response options
below, left-aligned. Insert-styled colors (white-blue, amber hover,
green pressed). WASD walk-away fades over 300ms, no close button.
InsertOverlay (CanvasLayer 10, z-layer 6). Mock data in next commit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New CognitiveDelay component buffers perception events before emitting
KnowledgeEvents. Normal delay: 6 ticks (0.6s), urgent: 3 ticks (0.3s).
Drain system runs after emit_observation_events, before process_knowledge_events.
Adds pending_recognitions to ObserverSnapshot (protocol v7) for client
fog entity visualization. Includes cancellation when entity leaves LOS.
Monologue fires during delay (not after) per D-060 — cross-system
wiring deferred to follow-up ticket #451.
Ref: D-060 (cognitive delay), D-031 (10 tps), D-059 (fog layers)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upgrade client protocol bridge from v5 to v6 to match server.
Adds player_stance (4 variants) and player_inventory decode to
ObserverSnapshot. Adds TOGGLE_STANCE_UP/DOWN to InputMapper.
Includes 25 gdUnit4 tests for v6 decode + server serialization
test gap fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These are auto-generated by Godot 4 to provide stable resource
references across renames. Tracking them prevents UID divergence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes to make the gameplay loop functional end-to-end:
- Add Interactable component to NPC spawn so E-prompt detection works
- Build monologue trigger system (enter_location + time_idle) with
MonologueBuffer/MonologueState components, wire through ObserverSnapshot
as current_monologue field, decode on client and display via HUD
- Change PlayerAction::Interact from unit to struct variant carrying
optional target_entity_id and verb fields
Bumps protocol version from 4 to 5. Regenerates MessagePack fixtures.
All 200 tests pass (170 unit + 30 integration).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The copy team rewrote ui-strings.yaml with multi-level nesting,
renamed sections, and inline comments. Update the YAML parser to
use a stack-based approach for arbitrary nesting depth, update HUD
and interaction prompt to reference the new key names, and align
tests with the new structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
UI microcopy strings belong in the client data directory where
the Godot client can load them directly, not in campaign content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds UIStrings autoload that loads display text from a YAML file,
replacing hardcoded strings in HUD and interaction prompt. Copy team
can now author UI microcopy in client/data/ui-strings.yaml without
touching GDScript. Includes 38 strings across 5 categories and 16
gdUnit4 tests for the parser and lookup API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests used entity_id matching to find the player, but game_state now
finds the player by kind.variant == "Player". Update test fixture to
include a Player entity and remove stale player_entity_id assignments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client was sending Time.get_ticks_msec() (e.g. 12345) as the input
tick, but the server's drain_for_tick only processes inputs where
tick <= current_tick (a small frame counter). Inputs accumulated in
the queue and were never processed, making movement keys unresponsive
in live mode. Use GameState.current_tick from the latest snapshot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>