Server shipped ObserverSnapshot v2 with game_time, player_facing,
visible_tiles (with visibility sectors), and per-entity visibility.
Protocol decoder was silently ignoring these fields. Now extracts
all v2 data with null defaults for backward compatibility.
GameState gains game_time, player_facing, visibility_sectors vars.
Derives visible_positions from visible_tiles when present (for real
server mode). Test snapshot updated with v2 fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded entity colors with D-033 relationship palette:
Player=#e0e8ff, Npc=unknown teal #4a9ebb, Object=grey #8b8ba0.
Phase 1 defaults by entity kind; Phase 2 (#361) will derive color
from RelationshipState via knowledge graph.
Entities in peripheral vision dimmed to 50% alpha (D-015).
Player entity gets a Polygon2D triangle indicator showing facing
direction, rotated from GameState.player_facing.
82 tests total, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clean implementation with strong test coverage (149 new test lines). One critical type safety bug in edge case.
#
File
Severity
Issue
1
game_state.gd:66-73
critical
visible_tiles loop accesses vtile.x/vtile.y without validating vtile is a Dictionary — will crash on malformed data (e.g. null element)
2
entity_renderer.gd:96
warning
_facing_to_rotation(GameState.player_facing) — if player_facing is somehow null, the _: fallback handles it silently. Low risk since GameState defaults to "North"
3
test_rendering.gd
warning
No test for malformed visible_tiles entries (negative path for issue #1)
4
entity_renderer.gd:87
suggestion
modulate.a written every frame even if unchanged — minor perf optimization to check before write
5
test_rendering.gd
suggestion
Add test for facing indicator rotation accuracy, not just presence
Tyre (Architecture): APPROVE
Architecturally correct — follows thin client principle perfectly. No coupling or boundary violations.
#
File
Severity
Issue
1
entity_renderer.gd:48-51
suggestion
Add clarifying comment that null visibility (v1 backward compat) defaults to full alpha
2
test_protocol.gd
suggestion
Add negative test for v1 backward compatibility (no v2 fields -> graceful null defaults)
3
constants.gd:7-12
suggestion
Clarify comment that Phase 1 maps by entity kind, Phase 2 (#361) by RelationshipState
Architectural positives: D-010/D-020 thin client principle respected (pure renderer, no game logic). D-033 color palette matches spec exactly. D-015 peripheral dimming correct. D-031 game_time fields stored for future HUD. No inappropriate server knowledge in client.
Verdict: CHANGES REQUESTED
Blocking issue:
Type safety in game_state.gd visible_tiles loop — add Dictionary validation before accessing .x/.y fields
The rest are suggestions. Solid PR overall.
## Dual-Agent Review: client -> main (PR #11)
### Hoshe (Code Quality): REQUEST_CHANGES
Clean implementation with strong test coverage (149 new test lines). One critical type safety bug in edge case.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `game_state.gd:66-73` | critical | `visible_tiles` loop accesses `vtile.x`/`vtile.y` without validating vtile is a Dictionary — will crash on malformed data (e.g. `null` element) |
| 2 | `entity_renderer.gd:96` | warning | `_facing_to_rotation(GameState.player_facing)` — if `player_facing` is somehow null, the `_:` fallback handles it silently. Low risk since GameState defaults to `"North"` |
| 3 | `test_rendering.gd` | warning | No test for malformed `visible_tiles` entries (negative path for issue #1) |
| 4 | `entity_renderer.gd:87` | suggestion | `modulate.a` written every frame even if unchanged — minor perf optimization to check before write |
| 5 | `test_rendering.gd` | suggestion | Add test for facing indicator rotation accuracy, not just presence |
### Tyre (Architecture): APPROVE
Architecturally correct — follows thin client principle perfectly. No coupling or boundary violations.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `entity_renderer.gd:48-51` | suggestion | Add clarifying comment that null visibility (v1 backward compat) defaults to full alpha |
| 2 | `test_protocol.gd` | suggestion | Add negative test for v1 backward compatibility (no v2 fields -> graceful null defaults) |
| 3 | `constants.gd:7-12` | suggestion | Clarify comment that Phase 1 maps by entity kind, Phase 2 (#361) by RelationshipState |
**Architectural positives:** D-010/D-020 thin client principle respected (pure renderer, no game logic). D-033 color palette matches spec exactly. D-015 peripheral dimming correct. D-031 game_time fields stored for future HUD. No inappropriate server knowledge in client.
### Verdict: CHANGES REQUESTED
**Blocking issue:**
1. Type safety in `game_state.gd` visible_tiles loop — add Dictionary validation before accessing `.x`/`.y` fields
The rest are suggestions. Solid PR overall.
Fix commit also addressed Hoshe's critical type safety issue and two suggestions:
Type safety in visible_tiles loop (CRITICAL): Added Dictionary validation (if not vtile is Dictionary or not vtile.has("x") or not vtile.has("y"): continue) — prevents crash on malformed data. ✅
Skip redundant modulate.a writes (SUGGESTION): Changed to if not is_equal_approx(entity_node.modulate.a, target_alpha): entity_node.modulate.a = target_alpha — minor perf improvement. ✅
Test for malformed visible_tiles (SUGGESTION): New test test_game_state_skips_malformed_visible_tiles() validates null, string, and missing-field cases. ✅
Test for facing rotation accuracy (SUGGESTION): New test test_entity_renderer_facing_indicator_rotation_accuracy() validates all 8 cardinal/diagonal rotation values. ✅
Architectural Compliance (D-010 / D-020)
The implementation continues to perfectly respect the thin client principle:
Pure renderer: Client receives ObserverSnapshot, draws what it's told. Zero game logic in GDScript.
Server authority: All visibility, relationship state, and entity properties flow from Rust simulation.
Backward compatibility: v1 snapshots (pre-visibility) gracefully degrade to full alpha. No client-side assumptions about missing data.
Future-ready: Phase 2 color derivation (#361) will be server-side (knowledge graph → RelationshipState → color in snapshot). Client just renders the color it receives.
No boundary violations. No inappropriate server knowledge.
New Issues Introduced by Fix Commit
NONE.
The fix commit is clean, focused, and addresses all feedback without introducing regressions or new concerns.
Verdict: APPROVE
This PR is ready to merge. All architectural, code quality, and test coverage issues resolved.
## TYRE Re-Review: APPROVE
All three suggestions from my previous review have been addressed in fix commit 64897a4.
### Issue Resolution Status
**SUGGESTION 1: Add clarifying comment that null visibility defaults to full alpha**
- **Status:** RESOLVED
- **Location:** `client/scripts/rendering/entity_renderer.gd:82`
- **Evidence:** Comment added: `# null visibility (v1 backward compat) defaults to full alpha`
- **Assessment:** Clear and accurate. Explains both the technical behavior (null → full alpha) and the reason (v1 backward compatibility).
**SUGGESTION 2: Add v1 backward compatibility test**
- **Status:** RESOLVED
- **Location:** `client/tests/test_protocol.gd:327-345`
- **Evidence:** New test `test_decode_v1_snapshot_graceful_defaults()` validates:
- v1 snapshot (tick + entities only) decodes without error
- v2 fields (version, game_time, player_facing, visible_tiles) gracefully default to null/empty
- Entity visibility field is null (not missing, not crashing)
- **Assessment:** Comprehensive negative-path test. Exactly what was needed.
**SUGGESTION 3: Clarify D-033 Phase 1/Phase 2 comments**
- **Status:** RESOLVED
- **Location:** `client/scripts/constants.gd:10-11`
- **Evidence:** Updated comment:
- Phase 1: "default colors mapped by entity kind (Player/Npc/Object/Terrain)"
- Phase 2: "colors derived from RelationshipState via the knowledge graph"
- **Assessment:** Crystal clear. Phase 1 = static mapping, Phase 2 = dynamic knowledge-driven. Links to ticket #361.
### Bonus Fixes (from Hoshe's review)
Fix commit also addressed Hoshe's critical type safety issue and two suggestions:
1. **Type safety in visible_tiles loop** (CRITICAL): Added Dictionary validation (`if not vtile is Dictionary or not vtile.has("x") or not vtile.has("y"): continue`) — prevents crash on malformed data. ✅
2. **Skip redundant modulate.a writes** (SUGGESTION): Changed to `if not is_equal_approx(entity_node.modulate.a, target_alpha): entity_node.modulate.a = target_alpha` — minor perf improvement. ✅
3. **Test for malformed visible_tiles** (SUGGESTION): New test `test_game_state_skips_malformed_visible_tiles()` validates null, string, and missing-field cases. ✅
4. **Test for facing rotation accuracy** (SUGGESTION): New test `test_entity_renderer_facing_indicator_rotation_accuracy()` validates all 8 cardinal/diagonal rotation values. ✅
### Architectural Compliance (D-010 / D-020)
The implementation continues to perfectly respect the **thin client principle**:
- **Pure renderer:** Client receives `ObserverSnapshot`, draws what it's told. Zero game logic in GDScript.
- **Server authority:** All visibility, relationship state, and entity properties flow from Rust simulation.
- **Backward compatibility:** v1 snapshots (pre-visibility) gracefully degrade to full alpha. No client-side assumptions about missing data.
- **Future-ready:** Phase 2 color derivation (#361) will be server-side (knowledge graph → RelationshipState → color in snapshot). Client just renders the color it receives.
**No boundary violations. No inappropriate server knowledge.**
### New Issues Introduced by Fix Commit
**NONE.**
The fix commit is clean, focused, and addresses all feedback without introducing regressions or new concerns.
---
**Verdict: APPROVE**
This PR is ready to merge. All architectural, code quality, and test coverage issues resolved.
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
game_time,player_facing,visible_tiles, and per-entityvisibilitysectors from ObserverSnapshot v2 (backward compatible with v1)Closes #130.
Test plan
make ci-client)🤖 Generated with Claude Code
Dual-Agent Review: client -> main (PR #11)
Hoshe (Code Quality): REQUEST_CHANGES
Clean implementation with strong test coverage (149 new test lines). One critical type safety bug in edge case.
game_state.gd:66-73visible_tilesloop accessesvtile.x/vtile.ywithout validating vtile is a Dictionary — will crash on malformed data (e.g.nullelement)entity_renderer.gd:96_facing_to_rotation(GameState.player_facing)— ifplayer_facingis somehow null, the_:fallback handles it silently. Low risk since GameState defaults to"North"test_rendering.gdvisible_tilesentries (negative path for issue #1)entity_renderer.gd:87modulate.awritten every frame even if unchanged — minor perf optimization to check before writetest_rendering.gdTyre (Architecture): APPROVE
Architecturally correct — follows thin client principle perfectly. No coupling or boundary violations.
entity_renderer.gd:48-51test_protocol.gdconstants.gd:7-12Architectural positives: D-010/D-020 thin client principle respected (pure renderer, no game logic). D-033 color palette matches spec exactly. D-015 peripheral dimming correct. D-031 game_time fields stored for future HUD. No inappropriate server knowledge in client.
Verdict: CHANGES REQUESTED
Blocking issue:
game_state.gdvisible_tiles loop — add Dictionary validation before accessing.x/.yfieldsThe rest are suggestions. Solid PR overall.
TYRE Re-Review: APPROVE
All three suggestions from my previous review have been addressed in fix commit
64897a4.Issue Resolution Status
SUGGESTION 1: Add clarifying comment that null visibility defaults to full alpha
client/scripts/rendering/entity_renderer.gd:82# null visibility (v1 backward compat) defaults to full alphaSUGGESTION 2: Add v1 backward compatibility test
client/tests/test_protocol.gd:327-345test_decode_v1_snapshot_graceful_defaults()validates:SUGGESTION 3: Clarify D-033 Phase 1/Phase 2 comments
client/scripts/constants.gd:10-11Bonus Fixes (from Hoshe's review)
Fix commit also addressed Hoshe's critical type safety issue and two suggestions:
if not vtile is Dictionary or not vtile.has("x") or not vtile.has("y"): continue) — prevents crash on malformed data. ✅if not is_equal_approx(entity_node.modulate.a, target_alpha): entity_node.modulate.a = target_alpha— minor perf improvement. ✅test_game_state_skips_malformed_visible_tiles()validates null, string, and missing-field cases. ✅test_entity_renderer_facing_indicator_rotation_accuracy()validates all 8 cardinal/diagonal rotation values. ✅Architectural Compliance (D-010 / D-020)
The implementation continues to perfectly respect the thin client principle:
ObserverSnapshot, draws what it's told. Zero game logic in GDScript.No boundary violations. No inappropriate server knowledge.
New Issues Introduced by Fix Commit
NONE.
The fix commit is clean, focused, and addresses all feedback without introducing regressions or new concerns.
Verdict: APPROVE
This PR is ready to merge. All architectural, code quality, and test coverage issues resolved.
Re-Review: client -> main (PR #11) — APPROVED
Fix commit
64897a4addresses all 6 issues from the initial review.Hoshe (Code Quality): APPROVE
85/85 tests passing. No new issues.
Tyre (Architecture): APPROVE
All suggestions addressed. Thin client principle (D-010/D-020) fully respected. No boundary violations.
Verdict: APPROVED — ready to merge.
Pull request closed