Protocol handshake (#555): HandshakeMessage as first IPC frame,
HandshakeState resource, forward-compatible input handling.
State serialization (#96): serialize_npc_to_frozen/deserialize with
full D-024 axis coverage (10 new optional fields on NpcSaveState).
Scope tags (#98): ScopeTagKind enum, ScopePinned marker, automatic
assignment from KnowledgeGraph and RelationshipGraph.
Timestamp eviction (#97): LastInteractionTick, SimSpacePressure,
BinaryHeap LRU eviction respecting ScopePinned entities.
Save/load (#553): save_to_file/load_from_file via MessagePack,
SaveGame/LoadGame IPC commands, SaveLoadResultWire on snapshot.
Test infrastructure (#200): Layer 3 integration test entry point,
three-layer architecture documented per D-030.
Information boundary tests (#272): 4 negative tests proving no
passive KG leakage, LOS fog holds, tier boundary holds, save
isolation per NPC.
1063 tests passing, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Good code quality overall — thorough doc comments, clean system ordering in SimulationPlugin, solid test coverage at the unit level. One concrete bug and four suggestions.
#
File
Severity
Issue
1
bridge/types.rs:20
warning
PROTOCOL_VERSION not bumped to 15 after adding save_result. Changelog docstring says v15 adds save_result but the constant is still 14. One-line fix.
2
bridge/tcp.rs:48
suggestion
Misleading comment — says read_framed handles WouldBlock by returning Ok(None), but Ok(None) means EOF. WouldBlock propagates as Err and is handled at line 202. Comment should say so.
3
simulation/save_io.rs:59
suggestion
Silent overwrite of pending save/load command — no tracing::warn! when a second command arrives before the first executes. Double-tap F5 edge case.
4
simulation/tier.rs:358
suggestion
SimSpacePressure.active_count reflects pre-eviction count, not post-eviction. Doc note on the field would prevent future systems from reading stale data.
5
simulation/save_io.rs:85
suggestion
KnowledgeGraph::new() fallback silently drops player knowledge if player entity missing at save time. A tracing::warn! on the fallback branch would surface this.
Tyre (Architecture): REQUEST_CHANGES
Architecturally sound — D-010 determinism respected, D-020 MessagePack used correctly, information boundaries honoured in serialization model. Same protocol version bug independently flagged.
#
File
Severity
Issue
1
bridge/types.rs:20
warning
PROTOCOL_VERSION not bumped to 15. Same as Hoshe — the constant and ObserverSnapshot.version doc comment must both say 15. Same mismatch pattern fixed in PR #66.
2
simulation/save_io.rs:56
warning
SaveLoadPending silently drops commands when a second arrives before the first executes. No test for the overwrite case, no tracing log, no client feedback. At minimum a tracing::warn! on overwrite.
3
simulation/tier.rs:339
suggestion
No test asserting ScopePinned NPCs survive evict_excess_active. The Without<ScopePinned> query filter is the core invariant of scope tags — needs regression coverage.
4
simulation/tier.rs:370
suggestion
Entity-based tie-breaking in eviction heap is non-deterministic across save/load cycles. Acceptable because tier assignment is re-derived on load, but should be documented inline.
5
tests/information_boundaries.rs
suggestion
File was inaccessible during review — could not verify D-012 test coverage. Needs human eyes before merge.
Verdict: CHANGES REQUESTED
Required fixes before merge:
Bump PROTOCOL_VERSION to 15 and update doc comment on ObserverSnapshot.version
Add tracing::warn! when SaveLoadPending overwrites a pending command
## Review: server → main (type: code)
### Hoshe (Code Quality): REQUEST_CHANGES
Good code quality overall — thorough doc comments, clean system ordering in SimulationPlugin, solid test coverage at the unit level. One concrete bug and four suggestions.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `bridge/types.rs:20` | warning | `PROTOCOL_VERSION` not bumped to 15 after adding `save_result`. Changelog docstring says v15 adds `save_result` but the constant is still 14. One-line fix. |
| 2 | `bridge/tcp.rs:48` | suggestion | Misleading comment — says `read_framed` handles `WouldBlock` by returning `Ok(None)`, but `Ok(None)` means EOF. `WouldBlock` propagates as `Err` and is handled at line 202. Comment should say so. |
| 3 | `simulation/save_io.rs:59` | suggestion | Silent overwrite of pending save/load command — no `tracing::warn!` when a second command arrives before the first executes. Double-tap F5 edge case. |
| 4 | `simulation/tier.rs:358` | suggestion | `SimSpacePressure.active_count` reflects pre-eviction count, not post-eviction. Doc note on the field would prevent future systems from reading stale data. |
| 5 | `simulation/save_io.rs:85` | suggestion | `KnowledgeGraph::new()` fallback silently drops player knowledge if player entity missing at save time. A `tracing::warn!` on the fallback branch would surface this. |
### Tyre (Architecture): REQUEST_CHANGES
Architecturally sound — D-010 determinism respected, D-020 MessagePack used correctly, information boundaries honoured in serialization model. Same protocol version bug independently flagged.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `bridge/types.rs:20` | warning | `PROTOCOL_VERSION` not bumped to 15. Same as Hoshe — the constant and `ObserverSnapshot.version` doc comment must both say 15. Same mismatch pattern fixed in PR #66. |
| 2 | `simulation/save_io.rs:56` | warning | `SaveLoadPending` silently drops commands when a second arrives before the first executes. No test for the overwrite case, no tracing log, no client feedback. At minimum a `tracing::warn!` on overwrite. |
| 3 | `simulation/tier.rs:339` | suggestion | No test asserting `ScopePinned` NPCs survive `evict_excess_active`. The `Without<ScopePinned>` query filter is the core invariant of scope tags — needs regression coverage. |
| 4 | `simulation/tier.rs:370` | suggestion | Entity-based tie-breaking in eviction heap is non-deterministic across save/load cycles. Acceptable because tier assignment is re-derived on load, but should be documented inline. |
| 5 | `tests/information_boundaries.rs` | suggestion | File was inaccessible during review — could not verify D-012 test coverage. Needs human eyes before merge. |
### Verdict: CHANGES REQUESTED
**Required fixes before merge:**
1. Bump `PROTOCOL_VERSION` to 15 and update doc comment on `ObserverSnapshot.version`
2. Add `tracing::warn!` when `SaveLoadPending` overwrites a pending command
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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 19 server delivery — 7 tickets, 1063 tests passing.
HandshakeMessageas first IPC frame,HandshakeStateresource, forward-compatible input handlingserialize_npc_to_frozen/deserialize_npc_from_frozenwith full D-024 10-axis coverageScopeTagKindenum,ScopePinnedmarker, auto-assignment from KG and RelationshipGraphLastInteractionTickLRU,SimSpacePressure, BinaryHeap eviction respecting scope pins (cap: 80)save_to_file/load_from_filevia MessagePack,SaveGame/LoadGameIPC commandsKey decisions
D-010 (determinism), D-020 (IPC/MessagePack), D-026 (simulation tiers), D-030 (test architecture), D-041 (knowledge graph), D-085 (per-game save dirs)
Files changed
22 files, +2561/-35 lines
Test plan
cargo testinserver/— 1063 tests, 0 failuresinformation_boundaries,integration_layer3🤖 Generated with Claude Code
Review: server → main (type: code)
Hoshe (Code Quality): REQUEST_CHANGES
Good code quality overall — thorough doc comments, clean system ordering in SimulationPlugin, solid test coverage at the unit level. One concrete bug and four suggestions.
bridge/types.rs:20PROTOCOL_VERSIONnot bumped to 15 after addingsave_result. Changelog docstring says v15 addssave_resultbut the constant is still 14. One-line fix.bridge/tcp.rs:48read_framedhandlesWouldBlockby returningOk(None), butOk(None)means EOF.WouldBlockpropagates asErrand is handled at line 202. Comment should say so.simulation/save_io.rs:59tracing::warn!when a second command arrives before the first executes. Double-tap F5 edge case.simulation/tier.rs:358SimSpacePressure.active_countreflects pre-eviction count, not post-eviction. Doc note on the field would prevent future systems from reading stale data.simulation/save_io.rs:85KnowledgeGraph::new()fallback silently drops player knowledge if player entity missing at save time. Atracing::warn!on the fallback branch would surface this.Tyre (Architecture): REQUEST_CHANGES
Architecturally sound — D-010 determinism respected, D-020 MessagePack used correctly, information boundaries honoured in serialization model. Same protocol version bug independently flagged.
bridge/types.rs:20PROTOCOL_VERSIONnot bumped to 15. Same as Hoshe — the constant andObserverSnapshot.versiondoc comment must both say 15. Same mismatch pattern fixed in PR #66.simulation/save_io.rs:56SaveLoadPendingsilently drops commands when a second arrives before the first executes. No test for the overwrite case, no tracing log, no client feedback. At minimum atracing::warn!on overwrite.simulation/tier.rs:339ScopePinnedNPCs surviveevict_excess_active. TheWithout<ScopePinned>query filter is the core invariant of scope tags — needs regression coverage.simulation/tier.rs:370tests/information_boundaries.rsVerdict: CHANGES REQUESTED
Required fixes before merge:
PROTOCOL_VERSIONto 15 and update doc comment onObserverSnapshot.versiontracing::warn!whenSaveLoadPendingoverwrites a pending command🤖 Generated with Claude Code
Pull request closed