Commit Graph
60 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.6 d808d95659 refactor(simulation): address PR #72 review suggestions
- Hoshe #3: replace O(n²) Vec scan in fallback NPC assignment with
  BTreeSet; prevent same NPC assigned to two roles in one triangle
- Hoshe #4: add From impls for RoleId, TriangleId, StableId, and
  TriangleCrisisEventWire — eliminate fragile .0 access on newtypes
- Hoshe #5: consolidate near-identical unit tests with integration
  counterparts — keep only unique tests in #[cfg(test)] module
- Tyre #3: replace O(N*M) scan in apply_resolve_triangle with
  BTreeMap<TriangleId, Entity> index for O(1) per-command lookup
- Tyre #4: document &mut World on generate_intra_template_triangles
- Observer snapshot uses TriangleCrisisEventWire::from instead of
  manual field mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:35:23 +01:00
jpmschweitzerandClaude Opus 4.6 9bc5925220 fix(simulation): wire triangle crisis event queue into observer snapshot
The TriangleCrisisEventQueue was populated by tick_triangle_escalation
but never drained into ObserverSnapshot — clients always saw an empty
vec despite protocol v16 advertising the field. Now drains the queue
each tick and converts to TriangleCrisisEventWire.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:48:11 +01:00
jpmschweitzerandClaude Opus 4.6 c436ae905f feat(simulation): wire triangle escalation and crisis events into protocol
Registers tick_triangle_escalation and apply_resolve_triangle systems
in SimulationPlugin. Adds TriangleCrisisEventWire to ObserverSnapshot
(protocol v16) for future client rendering of triangle crises (#250,
D-087). Observer emits empty vec by default; escalation system will
populate when triangles reach Active phase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:33:21 +01:00
jpmschweitzer 1bd528e521 Merge remote-tracking branch 'origin/main' into ci
# Conflicts:
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack
#	client/tests/fixtures/msgpack/snapshot_empty.msgpack
#	client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack
#	client/tests/fixtures/msgpack/snapshot_one_npc.msgpack
#	client/tests/fixtures/msgpack/snapshot_player.msgpack
#	client/tests/fixtures/msgpack/snapshot_v2_full.msgpack
#	server/src/perception/observer/mod.rs
2026-02-25 12:51:41 +01:00
jpmschweitzerandClaude Opus 4.6 732b5f3adc fix(simulation): resolve duplicate examine_result field and regenerate fixtures
Removed duplicate examine_result from ObserverSnapshot (old ExamineResultEvent
superseded by ExamineResultWire). Fixed serde_default test version assertion.
Regenerated golden file and all fixture .msgpack files. 1070 Rust tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:48:17 +01:00
jpmschweitzerandClaude Opus 4.6 6ed8d11502 feat(simulation): Sprint 19 — 7 server systems
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>
2026-02-25 12:13:03 +01:00
jpmschweitzer f5c5ac635c Merge remote-tracking branch 'origin/server'
# Conflicts:
#	CHANGELOG.md
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack
#	client/tests/fixtures/msgpack/snapshot_empty.msgpack
#	client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack
#	client/tests/fixtures/msgpack/snapshot_one_npc.msgpack
#	client/tests/fixtures/msgpack/snapshot_player.msgpack
#	client/tests/fixtures/msgpack/snapshot_v2_full.msgpack
#	server/src/bridge/types.rs
2026-02-25 10:09:27 +01:00
jpmschweitzerandClaude Opus 4.6 276538cde1 feat(simulation): Sprint 18 — 9 server systems
Background tier state machines (#95), NPC vision (#115), player-
awareness behavior (#244), skill system & combat flag (#91), social
propagation (#249), examine mechanic (#242), character pressure
framework (#248), save state data model (#256), tell state wiring
(#337). Protocol version bumped 13→14 for examine_result and
character_pressure snapshot fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:50:37 +01:00
jpmschweitzerandClaude Opus 4.6 a82eb0748a fix(client): address PR #65 review — POI key, ToldBy parsing, KG dirty flag
- minimap.gd: fix "poi_category" → "category" key mismatch so POI
  colors and shapes render correctly
- journal_panel.gd: parse ToldBy(N) source format, resolve entity
  names from player_knowledge; move confidence/source labels to
  UIStrings per D-042
- observer/mod.rs: add Changed<KnowledgeGraph> dirty flag to skip
  per-tick KG serialization when unchanged
- types.rs: fix stale version doc comment (13 → 14)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 02:41:31 +01:00
jpmschweitzerandClaude Opus 4.6 dd3ead7a1b feat(simulation): protocol v14 — POI list, examine result, player knowledge wire types
Add three new ObserverSnapshot fields for client Sprint 18 tickets:
poi_list (Vec<PoiWire>) for minimap #151, examine_result
(Option<ExamineResultWire>) for #174, player_knowledge
(Option<PlayerKnowledgeWire>) for journal #264. POI list populated
live from KG-discovered PointOfInterest components. KG dump
serializes entity/fact knowledge with confidence, source, and state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 02:29:29 +01:00
jpmschweitzerandClaude Opus 4.6 3d636fd4bb feat(simulation): knowledge grant schema, events, and contradiction detection (#545, #546, #547)
KnowledgeGrant untagged enum (Fact + Entity variants), ContentEntityRegistry
resource, KnowledgeGranted event processing, ContradictionClaim struct with
600-tick window detection in observe_entity. Wires knowledge_grant field in
dialogue line selection. Implements D-079, D-083. Closes Q-026.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 12:14:53 +01:00
jpmschweitzerandClaude Opus 4.6 8538f916af feat(simulation): wire Sprint 15 systems + protocol v13
Register all new systems in NpcPlugin and SimulationPlugin with correct
ordering constraints. Protocol bumped to v13: tell_state on VisibleEntity,
follow_state and PostConversationQueue on ObserverSnapshot, Follow verb
on VerbKind. Observer snapshot populates tell state from DerivedTellState
and follow state from FollowTarget. System ordering: tolerance after
mood, deviation after activity, tell after mood+deviation, follow after
visibility geometry, event monologue after conversations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:40:09 +01:00
jpmschweitzerandClaude Opus 4.6 517318c8a7 feat(simulation): extend monologue event generation with new triggers (#119)
Add observe_npc, hear_sound, observe_anomaly, witness_interaction, and
post_conversation triggers to monologue system. trigger_event_monologue
system fires after sound collection, NPC conversations, and walk-away.
Context tags (location, situation, character_state) populated for D-035
content pool matching. COOLDOWN_TICKS=300 anti-spam guard respected.
witness_interaction fires after overheard NPC-to-NPC conversation per
D-078. Voice sounds and ambient sounds correctly excluded from
hear_sound trigger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:39:58 +01:00
jpmschweitzerandClaude Sonnet 4.6 aa0d8ced37 feat(simulation): NPC-to-NPC conversation system (#247, D-078)
Implements server-authoritative per-word occlusion for NPC conversations.
ConversationEventBuffer drains into ObserverSnapshot each tick so the client
receives only words audible from the player's position. Updates test fixtures
to include the new conversation_events and conversation_ended fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 18:41:44 +01:00
jpmschweitzerandClaude Opus 4.6 dd1dd0feec fix(simulation): address PR #46 review — zone tests, doc accuracy, TBD comment
Add 3 zone enrichment tests to observer pipeline (tile inside zone,
tile outside zone, absent ZoneMap resource). Fix misleading Decay
Observatory doc comment that claimed Direct start when test starts at
KnowsDetails. Add production population TBD note on ZoneMap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:22:48 +01:00
jpmschweitzerandClaude Opus 4.6 51abc3e3c1 feat(simulation): #523 add zone_id to ObserverSnapshot (D-077, protocol v11)
Server-tracked zone_id on VisibleTile for D-073 zone crossfade and
D-059 deep fog temperature tint. ZoneMap resource backed by BTreeMap,
observer enrichment in snapshot assembly. Backwards-compatible: v10
payloads deserialize with zone_id: None.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:10:06 +01:00
jpmschweitzer 4bdcc8c3fa Merge remote-tracking branch 'origin/ci'
# Conflicts:
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack
#	client/tests/fixtures/msgpack/snapshot_empty.msgpack
#	client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack
#	client/tests/fixtures/msgpack/snapshot_one_npc.msgpack
#	client/tests/fixtures/msgpack/snapshot_player.msgpack
#	client/tests/fixtures/msgpack/snapshot_v2_full.msgpack
#	server/Cargo.toml
#	server/src/bridge/text_renderer.rs
#	server/src/bridge/types.rs
#	server/src/perception/observer/mod.rs
#	server/src/simulation/path_follow.rs
#	server/tests/bridge_ipc.rs
#	server/tests/bridge_tcp.rs
#	server/tests/gen_fixtures.rs
#	server/tests/serialization.rs
2026-02-19 15:35:56 +01:00
jpmschweitzerandClaude Opus 4.6 03c44bebaf fix(simulation): address PR #42 review — 8 items from Hoshe and Tyre
1. Sound producers: document empty v0.1 pipeline explicitly (critical)
2. Routine tests: add ActiveSim to 3 tests that passed trivially
3. Rename _observer_pos → observer_pos (used at line 191)
4. Add FactionOnly positive test case (matching faction_id)
5. Fix stale doc comment "Current: 9" → 10 in ObserverSnapshot
6. Remove orphaned SimulationTier/LastInteraction/ScopeTag types
7. Add tracing::warn on FactionOnly non-numeric parse failure
8. Document Medium-range occlusion gap as TODO in audible_at
9. Insert SoundEventQueue in observer test setup_world

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:08:32 +01:00
jpmschweitzerandClaude Opus 4.6 3d306b8a2b feat(ci): add tracing::instrument to heavy per-tick systems, fix stale protocol comment (#344, #527)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 14:55:00 +01:00
jpmschweitzerandClaude Sonnet 4.6 6a7dc915de feat(bridge): add rng_seed to ObserverSnapshot for deterministic replay (#527)
Adds rng_seed: Option<u64> to ObserverSnapshot. The WRONG button (#507) captures
inputs.jsonl and seed.txt for replay, but seed.txt was writing "unavailable"
because the server did not include the RNG seed in ObserverSnapshot.

Changes:
- bridge/types.rs: PROTOCOL_VERSION 9→10, rng_seed field with serde(default,
  skip_serializing_if = "Option::is_none") for backward compatibility
- perception/observer/mod.rs: inject Res<SimRng> into compute_observer_snapshot,
  populate rng_seed: Some(rng.seed()) each tick
- All test files: add rng_seed: None to ObserverSnapshot constructors
- tests/serialization.rs: bump protocol_version_constant assertion 9→10
- Regenerate msgpack fixtures and golden file for protocol v10

Completes the WRONG button capture loop: replays can now fully reproduce
observed bugs with the exact RNG seed from the capture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 14:15:34 +01:00
jpmschweitzerandClaude Sonnet 4.6 3248603838 feat(simulation): ban HashMap via clippy disallowed_types, fix violations (#343)
Adds server/.clippy.toml with disallowed-types for std::collections::HashMap
and std::collections::HashSet. HashMap iteration order is non-deterministic
and breaks deterministic simulation replay (D-030).

Changes:
- server/.clippy.toml: disallow HashMap and HashSet crate-wide
- simulation/movement.rs: WalkabilityMap.chunks and occupied map → BTreeMap;
  add PartialOrd+Ord to ChunkCoord, TilePosition, TilePresence
- simulation/monologue.rs: MonologueState.shown_ids → BTreeSet (simulation state)
- perception/shadowcast.rs: #![allow] — per-frame FOV scratch, iteration irrelevant
- perception/interpretation.rs: #![allow] — per-frame lookup table, key-only access
- perception/query.rs: #![allow] — sector_lookup is a per-frame read-only cache

Also applies cargo fmt to pre-existing format drift in contraband.rs,
dialogue.rs, test_world/mod.rs, and several integration tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 14:14:07 +01:00
jpmschweitzerandClaude Sonnet 4.6 8e00db2b2d feat(simulation): #94 #99 active tier filtering and transition system
#94 — Active tier simulation (complete):
- Add ActiveSim marker to all 9 test world room NPC spawns
- Fix test entities in routine.rs and path_follow.rs to include ActiveSim
  so With<ActiveSim> queries match correctly in unit tests

#99 — Tier transition logic (complete):
- Implement update_tier_markers system in tier.rs
- Promotes/demotes tier markers by manhattan distance from PlayerCharacter:
  ≤40 tiles → ActiveSim, ≤120 → BackgroundSim, beyond → StateSaved
- Handles cross-z-level as u32::MAX (effectively unreachable)
- No-op when no PlayerCharacter entity present (headless tests safe)
- 11 new unit tests covering all distance bands and boundary cases
- TierPlugin now registers the system after movement::validate_movement

Also picks up extended test coverage added by hoshe:
- observer/tests.rs — 230 lines of perception observer tests
- sound.rs — additional sound event integration tests

All 548 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:54:37 +01:00
jpmschweitzerandClaude Opus 4.6 9ea9fd2a74 feat(engine): add rng_seed to ObserverSnapshot for deterministic replay (#527)
Add rng_seed: Option<u64> to ObserverSnapshot (protocol v10). Populated
from SimRng state each tick. Completes the WRONG button capture loop —
seed.txt now writes a valid u64 instead of "unavailable", enabling
deterministic replay from bug reports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:34:14 +01:00
jpmschweitzerandClaude Opus 4.6 0f5a990f82 feat(ci): ban HashMap in simulation crate via clippy (#343)
Add clippy::disallowed_types for std::collections::HashMap scoped to
the simulation crate. Replace HashMap with BTreeMap in movement.rs for
deterministic iteration order. Allow exception in perception/query.rs
where iteration order is irrelevant (per-frame scratch buffer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:34:04 +01:00
jpmschweitzerandClaude Opus 4.6 066f8031fd feat(simulation): sprint 12 server — tier system, sound events, KG access, line previewer
Implements 4 completed tickets + partial progress on 2 more:

- #93 Tier marker components (ActiveSim, BackgroundSim, StateSaved + TierPlugin)
- #138 Information tag schema (ObserverAccess enum in knowledge/types.rs)
- #124 Sound event system (SoundEventEmitter, SoundEventQueue, bridge wiring)
- #193 Line previewer CLI (line_preview binary with filter/explain/sequence modes)
- #94 Active tier simulation (in progress — With<ActiveSim> filters)
- #139 Component-level access control (in progress — filter_by_access)

Updates snapshot fixtures and test golden files for new sound_events field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:33:23 +01:00
jpmschweitzerandClaude Sonnet 4.6 d103e445e7 feat(simulation): contraband detection + dialogue confidence gate (D-075) (#425, #524)
Contraband detection (Task #425, D-065):
- New module: simulation/contraband.rs — NPC scan checks carried items
  against the KnowledgeGraph confidence gate. NPCs with Authority access
  can initiate a scan; scan outcome depends on item CarriedBy + KG entry.
- Adds ContrabandScanResult event type and ContrabanEntry component.
- Wired into simulation/mod.rs module list.

Dialogue confidence gate (Task #524, D-075 — OQ-18 resolution):
- relationship_to_trust() gains confidence parameter (KnowledgeConfidence).
- Trust tier mapping: (Friendly, KnowsDetails+)→Secret, (Friendly|Known,
  KnowsOf+)→Real, otherwise Surface. Access tier (Layer 1) unchanged.
- Caller process_talk_interaction passes observer KG confidence_of target.
- Resolves OQ-18: confidence co-gates TrustTier, not AccessTier.

Supporting changes:
- decisions/content.md: add D-075 (16 decisions, dated 2026-02-19)
- knowledge/types.rs: expose KnowledgeConfidence comparison helpers
- knowledge/registry.rs: minor API polish
- bridge/types.rs: ContrabandScanResult wire type
- bridge/text_renderer.rs: render contraband scan status
- perception/observer: include carried item count in snapshot
- npc/mod.rs: NPC scan range constant, authority flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 11:12:24 +01:00
jpmschweitzerandClaude Opus 4.6 6c62e2228f test(simulation): sprint 10 — replay loading, content scaling, serialization v9, observer tests
#483: Replay loading in test-client — JSONL file loading, tick-scheduled
PlayerInput sending, 13 unit tests, 3 sample replay files.
#500: Content scaling test — baseline + extra NPC comparative, tick budget
assertion (D-026), determinism check across content packs.
#514: Serialization tests for protocol v9 — blocked_entities roundtrip,
backward compat (v5→v9, v8→v9), regenerated msgpack fixtures.
Observer perception tests for confrontation + walk-away mechanics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:58:27 +01:00
jpmschweitzerandClaude Opus 4.6 273d29f26f feat(simulation): sprint 10 — hub teleport, confrontation response, walk-away phase 2, gauntlet rooms 4-7, blocked_entities debug field
#491: PlayerAction::TeleportToHub — moves player to hub spawn, clears
dialogue/monologue/interaction buffer, Gauntlet-only with log warning.
#520: ConfrontationDelivered event — Tier 2 animation shift, relationship
state decrement (D-033), monologue spike emission.
#519: Walk-away Phase 2 — NPC animation shift + routine deviation on
dialogue exit (D-064).
#498: Four new Gauntlet rooms — Interaction Gallery, Fog Theater, Crowd
Plaza, Dialogue Room with constants and wiring.
#514: blocked_entities Vec<u64> on ObserverSnapshot, protocol v9.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:58:14 +01:00
jpmschweitzerandClaude Opus 4.6 d5a1f6dadb refactor(simulation): apply rustfmt formatting
Formatting-only changes across server source and test files.
No logic changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 01:39:44 +01:00
jpmschweitzerandClaude Opus 4.6 b1fdeabb7c test(simulation): sprint 8 test suite — pause guards, registry, boundary, determinism
Add 50+ tests: pause guard suite (movement, unpause, roundtrip, stance,
interact, batch, tick_rate), EntityRegistry lifecycle (stale mapping,
re-register, unknown unregister), boundary value encode/roundtrip (41
values), encoding asymmetry (GDScript signed→Rust unsigned), malformed
batch rejection, determinism gauntlet (20-tick replay), per-fix
determinism unit tests, and recognition monologue integration tests.
Fix pause guard to block all actions except Pause/Unpause while paused.
Fixes #461-463, #466-469, #471-473, #479.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:41:33 +01:00
jpmschweitzerandClaude Opus 4.6 695d2ac843 feat(perception): anomaly detection and recognition monologue during delay
Add AnomalyMarker component and detect_anomalies() system that flags
entities with KG relationship PersonOfInterest or Contradicted state
for urgent cognitive delay (0.3s vs 0.6s normal). Add
trigger_recognition_monologue() that fires monologue at delay START
(when grey blob appears), not at completion — the monologue IS the
recognition process per D-060. Includes v0.1 fallback recognition
lines and cooldown tracking. Fixes #450, #451.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:41:14 +01:00
jpmschweitzerandClaude Opus 4.6 af8e20ab9a fix(simulation): determinism fixes — BTreeSet ordering, entity sort, mover sort
Replace HashSet with BTreeSet for visible_ids, sort visible_tiles by
coordinates, sort visible entities in snapshot by entity_id, and sort
movers by Entity bits in validate_movement. Required by D-010 principle 4
(deterministic simulation). Fixes #456, #457, #458.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:40:57 +01:00
jpmschweitzerandClaude Opus 4.6 d1aba3d554 fix(simulation): use non-blocking TCP to prevent game loop stall
read_framed() called read_exact() which blocked the entire bevy Update
schedule waiting for client input — no systems ran until a keystroke
arrived. Switch TcpStream to non-blocking mode so receive_inputs()
returns Ok(vec![]) on WouldBlock instead of blocking. Toggle to
blocking for snapshot writes (reliable delivery). Add 50ms frame
throttle (~20 ticks/sec) since the non-blocking loop would otherwise
spin. Downgrade input receive logging to trace, add error logging for
missing bridge resource and failed observer queries.

Fixes bug #1 (server never sends snapshots) and #2 (camera doesn't
center until first keystroke).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:25:45 +01:00
jpmschweitzerandClaude Opus 4.6 d4fdbf426e fix(server): address PR #23 review — 4 critical bugs, 3 warnings, 11 suggestions
Critical fixes:
- Add PendingRecognitionWire serialization roundtrip test
- Check Option return from delay.cancel() before logging
- InputQueue capacity limit (1000) with drop-oldest and warning
- TODO in observation.rs references ticket #450

Warning fixes:
- Hot-reload guards against invalid/empty content root
- Location header mismatch warning in line pool indexing
- walk_yaml() depth limit (100) against symlink loops
- Consecutive reload failure counter (warns after 5+)

Test additions:
- Negative prerequisite filtering test for monologue lines
- Integration test for pending_recognitions in observer snapshot
- Eavesdrop threshold ordering assertion (Careful < default)

Documentation:
- Playtesting expectation comments on delay constants
- is_pending() scalability note for future NPC cognitive delay
- ID format regex validation in line pool spec
- BTreeMap vs sort() ordering clarification in loader
- Multiplayer TODO in relationships.rs references D-010
- ContentSlug ticket #452 filed for entity slug resolution

394 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:03:51 +01:00
jpmschweitzerandClaude Opus 4.6 4d8fe996fc feat(perception): add cognitive delay system for fog recognition (#423)
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>
2026-02-16 00:41:08 +01:00
jpmschweitzerandClaude Opus 4.6 d0663c4193 refactor(server): apply rustfmt and clippy suggestions
Formatting pass across simulation, perception, knowledge, NPC, and
test modules. Includes two clippy fixes in monologue.rs (.values()
instead of for (_, v) pattern).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:40:43 +01:00
jpmschweitzerandClaude Opus 4.6 3d63a4f9f6 test(simulation): QA coverage for Phase 2 verbs, sprint suppression, occupancy, inventory
Add comprehensive test suites across sprint 6 systems:
- Phase 2 observer filter: Confront injection, archetype label relabeling,
  contradiction marking, POI priority ordering (12 tests)
- Sprint anomaly: detection during sprint, queue semantics, delay timing,
  D-055 visible-but-suppressed integration (7 tests)
- Wire format: Confront and CharacterArchetype msgpack roundtrip (4 tests)
- TilePresence: 4-layer coexistence on same tile (1 test)
- Inventory: Take/Place full roundtrip, capacity enforcement (3 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:00:32 +01:00
jpmschweitzerandClaude Opus 4.6 df81b24cd1 feat(simulation): sprint anomaly double-take monologue (#428, D-055)
When sprinting past a Contradicted entity, queue a delayed retroactive
monologue that fires after ~1.5s: "Wait — something wasn't right back
there." SprintAnomalyQueue component with first-in-wins semantics.
Detection in observer pipeline, processing in monologue system.
Wire MovementProfile, Stance, and SprintAnomalyQueue into player spawn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:00:20 +01:00
jpmschweitzerandClaude Opus 4.6 6ef7029707 feat(simulation): smuggler inventory — CarriedBy, Take/Place verbs (#424)
Implement physical inventory system per D-065:

- CarriedBy(StableId) component marks items as carried by an entity
- ItemName and InventorySlot components for display and slot assignment
- Take verb: removes TilePosition, adds CarriedBy + InventorySlot
- Place verb: removes CarriedBy + InventorySlot, adds TilePosition
- Observer snapshot populates player_inventory from carried items query
- Info boundary: carried items lack TilePosition, naturally excluded
  from all spatial queries (visibility, interactions) for other entities
- 9-slot capacity (3x3 grid per OQ-24), auto-assigns first free slot
- 8 new tests covering take, place, sequential slots, full rejection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:44:37 +01:00
jpmschweitzerandClaude Opus 4.6 98f4cedc03 feat(simulation): Sprint 6 Touch — stance, tile presence, verbs, protocol v6
Implements the core Sprint 6: Touch systems across 5 tickets:

- #449 ObserverSnapshot v6: add player_stance (MovementStance) and
  player_inventory (Vec<InventoryItem>) wire fields with serde defaults
  for backward compatibility. Bump PROTOCOL_VERSION 5→6.

- #417 Stance system: Sprint/Walk/Careful/Crouch movement stance with
  tick-based speed (1/2/3/4 ticks per move), monologue rate multipliers,
  and PlayerMoveCooldown component. ToggleStanceUp/Down player actions.

- #420 TilePresence: posture-layer collision system allowing same-tile
  occupancy for different layers (Standing/Prone/Seated/Fixture).
  Layer-based collision in validate_movement.

- #421 ObjectType component: Readable/Container/Terminal/Door/Pickup/
  Furniture types with Phase 1 verb sets computed from type + proximity.

- #422 Phase 2 verb filter: KG-gated observer-side verb processing —
  POI priority flips, Confront injection at KnowsDetails+, contradiction
  marking, archetype-specific label relabeling (Smuggler/Detective).

217 unit tests + 17 integration tests passing. All MessagePack fixtures
regenerated for v6 wire format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:38:59 +01:00
jpmschweitzerandClaude Opus 4.6 3c106aa2d0 feat(simulation): complete interaction and monologue pipelines (#413, #414, #415)
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>
2026-02-13 19:23:47 +01:00
jpmschweitzerandClaude Opus 4.6 29c5003403 fix(simulation): add TileKind to VisibleTile wire protocol (#412)
Server was sending visible_tiles without tile type data, so the client
could not distinguish floor from wall in live mode. Add TileKind enum
(Floor/Wall/Door/Object) to VisibleTile, populated from WalkabilityMap
in the NaturalVision perception query. Update test fixtures to include
the new field. Uses #[serde(default)] for backward compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:07:19 +01:00
jpmschweitzerandClaude Opus 4.6 18d8253bfe refactor(server): decompose observer pipeline and fix interaction boundary
Extract visibility geometry into a separate system behind a
PerceptionQuery trait, enabling D-017 perception mode swapping.
Two-stage pipeline: compute_visibility_geometry writes to
VisibilityGeometry resource, compute_observer_snapshot reads it.

Remove KnowledgeGraph from compute_nearby_interactions (simulation
phase boundary violation). Verb availability stays in simulation;
POI-based priority adjustment moves to observer via
apply_poi_verb_priority helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:32:41 +01:00
jpmschweitzerandClaude Opus 4.6 02fa89b1d7 refactor(server): extract observer tests to separate file
Split observer/mod.rs (820 lines) into production code (244 lines) and
tests (480 lines). Reduces module size per Hoshe #3 review item.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 22:52:52 +01:00
jpmschweitzerandClaude Opus 4.6 6f9537fff3 refactor(server): NearbyInteractionBuffer from Resource to Component
Per-entity Component attached to PlayerCharacter instead of global
Resource. Makes the interaction buffer multiplayer-ready (D-009) —
each observer gets their own buffer without pipeline refactoring.

Updated all 8 files touching the buffer: system signatures, player
spawn bundles, and ~30 test spawn sites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 22:52:47 +01:00
jpmschweitzerandClaude Opus 4.6 ba867564a8 fix(server): add error logging, protocol version, and architecture docs
- Replace silent Entity::to_bits() fallbacks with tracing::error in
  observer.rs and interaction.rs (makes unregistered entities loud)
- Add PROTOCOL_VERSION constant to types.rs, use in observer snapshot
- Document single-observer assumption on NearbyInteractionBuffer
- Document proximity-only (no LOS) limitation on compute_nearby_interactions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 21:22:34 +01:00
jpmschweitzerandClaude Opus 4.6 9943684f2c refactor(server): address PR #16 review feedback
Hoshe + Tyre review items:
- Use StableId consistently for wire entity_id (H4) across observer,
  observation, interpretation, and interaction systems
- Make NearbyInteractionBuffer.interactions private with take() (H1/H20)
- Add system ordering constraint for compute_nearby_interactions (H5)
- Panic on missing PlayerCharacter in input processing (H2)
- Remove redundant paused field from GameTime (Tyre8)
- Remove #[serde(default)] from nearby_interactions (H3)
- Change NearbyInteraction.distance from f32 to u32 (H8)
- Add sort stability for equal verb priorities (H6)
- Scope constants to pub(crate) (H7)
- Add debug_assert for last_observed_tick ordering (H10)
- Strengthen unregistered entity handling to debug_assert + error (H11)
- Document fractional tick accumulation (Tyre9)
- Extract collect_remembered_entities helper (Tyre2/H17)
- Add half_rate_no_drift_over_10000_frames test (H14)
- Add mid-range and deterministic sort tests (H15)
- Add fixture version assertion (H16)
- Regenerate msgpack fixtures for wire format changes

146 unit + 19 integration tests pass, zero clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 20:14:35 +01:00
jpmschweitzerandClaude Opus 4.6 2de413dd87 feat(simulation): add proximity detection and interaction verbs
Implement compute_nearby_interactions system that detects entities
within close (≤2) and mid (≤5) Manhattan distance, computes
available verbs per D-060 spec. NPCs get Talk+Observe at close
range, Observe-only at mid range; PersonOfInterest flips priority.
Objects get Examine. Results populate nearby_interactions[] on
ObserverSnapshot v4. Bump protocol version 3→4. Implements #404.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:48:55 +01:00
jpmschweitzerandClaude Opus 4.6 ad653cffc3 refactor(server): fix clippy warnings from Rust 1.93
- Use #[derive(Default)] + #[default] instead of manual Default impls
  for FacingDirection, KnowledgeState, RelationshipState, EntityVisibility
- Replace manual modulo check with .is_multiple_of()
- Collapse nested if in shadowcast symmetry check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:18:04 +01:00
jpmschweitzerandClaude Opus 4.6 2fd16f08b0 fix(server): address PR #14 review — ordering, docs, version pin
- Fix NpcPlugin system ordering: .before(compute_paths) instead of
  .after(advance_tick) so PathRequests are picked up same frame
- Fix stale doc comment in interpretation.rs: system runs BEFORE
  knowledge events, not after
- Add TODO(v0.2) on RelationshipGraph about information boundary
  limitation for multiplayer
- Document cardinal-only movement as deliberate v0.1 choice
- Add comment on manhattan_distance u32::MAX fallback for cross-z
- Pin pathfinding crate to 4.11

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:17:55 +01:00