Generic BackgroundWorkerPool<Req, Resp> with crossbeam channels, closure
handlers, and 3 delivery strategies (Fallback, GracefulDegrade, ModalLock).
Stub workers registered as Bevy resources:
- ChunkGenWorker (2 threads) — terrain/props/navmesh generation
- NpcPrepWorker (1 thread) — pre-compute NPC state for incoming areas
- OffscreenTickWorker (1 thread) — advance NPCs outside active tier
Tick loop integration:
- PreInput: poll_worker_results drains completed work
- PostSnapshot: push_worker_requests queues new work (no-op until Phase 5)
Handlers are stubs — real computation plugs in when the phases that need
them arrive. The infrastructure (channels, threads, push/poll, shutdown)
is real and tested.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enable Bevy multi-threaded executor via bevy_tasks multi_threaded
feature. Systems within the same TickPhase that don't share mutable
resources now run in parallel automatically.
Add EntityRng component — per-entity ChaCha20Rng seeded from
world_seed + StableId via splitmix64 mixing. More deterministic than
shared SimRng (order-independent). Migrate all monologue systems
(4 of 13 SimRng consumers) to EntityRng, removing contention that
serialized them against conversation/dialogue systems.
Add rayon dependency (infrastructure only, no par_iter calls yet).
SimRng retained for world-level randomness: conversation pairing,
knowledge transfer, dialogue, ticker, storyteller.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10-phase linear pipeline: PreInput → Input → Movement → Simulation →
Economy → Storyteller → Snapshot → PostSnapshot → Knowledge → TickAdvance.
Each system assigned to exactly one phase via .in_set(TickPhase::X).
Cross-phase .after()/.before() eliminated — only intra-phase ordering
remains. Prevents schedule cycles by construction.
SimulationPlugin refactored into sub-plugins by domain:
- InputPlugin (player actions, interactions, dialogue dispatch)
- MovementPlugin (pathfinding, movement validation, spatial indexing)
- SocialPlugin (conversations, sound, voice enrichment, follow state)
- EconomyPlugin (tâtonnement tick, IPC query serving)
- TimePlugin (chunk streaming, news ticker, tick advancement)
All other plugins (NPC, Knowledge, Perception, Storyteller, Settings,
Bridge) updated to use TickPhase assignments instead of cross-plugin
ordering constraints. BridgePlugin trimmed to bridge I/O concerns only.
Part A of #843. Parts B (multi-threaded executor) and C (background
workers) follow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tick_economy_simulation was ordered .after(advance_tick) which created a
cycle: observer_snapshot → send_snapshot → advance_tick → tick_economy →
observer_snapshot. Moved to .after(process_player_input) instead — the
economy checks time.tick which works regardless of advance order.
Also removed the .after(tick_economy_simulation) from handle_debug_commands
that was added during Sprint 34 review — same cycle root cause.
This is a symptom of #843 (ad-hoc ordering is fragile). Pair session
scheduled to replace with system set phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Widen EventPort tick methods from u32 to u64 (prevents overflow)
- Add is_identity() guard on hot-path String allocation in modifiers
- Replace Vec::remove(0) with VecDeque::pop_front() in price history
- Add .after(tick_economy_simulation) ordering for debug commands
- Fix stale PROTOCOL_VERSION assertion (20 → 21) in serialization test
- Add D-181 Phase 2 visibility scope comment on serve_econ_state_query
- Eliminate double lookup in rebuild_signals via single-pass extraction
- Track economy seed TODO with backlog ticket reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix all Clippy warnings across the server codebase (2411 insertions, 1341
deletions). Raise type-complexity-threshold to 750 and too-many-arguments
to 12 in .clippy.toml for idiomatic Bevy ECS system signatures. The server
now passes `cargo clippy -- --deny warnings` cleanly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- line_pool.rs: add trace! when active_situations is empty (Layer 2
will silently filter all lines — important for content debugging)
- knowledge_grant.rs: document serde(untagged) ambiguity hazard; add
KnowledgeGrant::validate() for load-time field validation
- monologue.rs: annotate trigger_monologue as schedule ordering anchor;
fix stale "periodic trigger cooldown tracking" doc reference in
trigger_event_monologue (COOLDOWN_TICKS removed in #655)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per-player settings via rusqlite (bundled, zero runtime dep). Server
owns the settings DB; client sends ChangeSettings commands over IPC.
Extensible key-value with typed columns (String/Int/Float/Bool).
Protocol bumped to v20 with settings_response in ObserverSnapshot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete the hand-authored YAML content pipeline (server/src/content/) superseded
by the v0.2 generator-first approach (D-122, D-128). Runtime ECS types that were
co-located with content loading have been extracted to dedicated simulation modules:
- simulation/triangle.rs: TriangleState, TriangleCrisisEventQueue, tick/resolve systems
- simulation/line_pool.rs: LinePoolIndex, AccessTier, TrustTier, Mood, LinePoolIndexResource
- simulation/knowledge_grant.rs: KnowledgeGrant, Prerequisites
Monologue systems (trigger_monologue, trigger_recognition_monologue,
trigger_event_monologue) now use hardcoded fallback lines only; the
ContentStoreResource branch and select_pool_line function are removed.
Deleted: content/{loader,types,line_pool,hot_reload,spawn,instantiation,entanglement,mod}.rs
Deleted: tests/{content_loading,content_runtime,content_scaling,template_instantiation,template_schema}.rs
Deleted: bin/line_preview.rs (v0.1 tool)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updated docstrings in sqlite_connector, qdrant_connector,
decisions_sync, schema.sql, and two doc comments in generator.rs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ticker rotation: document sliding-window semantics (vs modulus-aligned)
- Ticker zone ID: add warning about Gauntlet vs production zone ID mismatch
- Proof-room movement profile: respect archetype instead of hardcoding smuggler
- Storyteller tie-break: use exact f32 equality (inputs are discrete integers)
- Observer: .map().flatten() → .and_then() (clippy strict)
- Content loader: remove dangling doc comment before section header
- Tests: replace assert!(false, ...) with TODO comments in ignored tests
- Tests: add frame limiter note on 302-update loop in tell expiry test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add character_archetype to StartupMessage with serde default (Detective)
- Bump PROTOCOL_VERSION to 19
- Add escalate_tells_on_activation() and expire_routine_deviations() systems
- RoutineDeviation inserted on triangle NPCs with 300-tick TTL
- Add TickerPool resource with deterministic SimRng rotation (200 ticks)
- Emit current_ticker in ObserverSnapshot when player is in bar zone
- Load ticker YAML from district content directories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Debug and LOS tracks for Sprint 23 (#580, #584):
- DebugCommandKind enum with 10 variants (AdvanceTicks,
SkipToContamination, TeleportToPosition, ForceContaminationActivate,
InspectNpc, ListTriangles, ListPopulation, GetContaminationStatus,
TeleportToLocation, ForceTriangleActivation)
- DebugResponsePayload on ObserverSnapshot, handle_debug_commands
system gated by DebugEnabled resource
- PROTOCOL_VERSION bumped 17 → 18
- VisibilitySector::BoundaryWall variant — 1-tile wall margin beyond
LOS boundary included in visible_tiles (not exploration/memory)
- compute_boundary_walls() pass in NaturalVision after FOV+cone
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Storyteller chain for Sprint 23 (#570, #571, #572, #579):
- EngagementRecord component: per-NPC observation_time_ticks,
conversation_count, monologue_trigger_count — incremented by
perception, dialogue, and monologue systems
- MovementHistoryBuffer resource: ring buffer of player positions
over last 3000 ticks with npcs_copresent_in_window() query
- Lifecycle rules: single activation per session, no concurrency,
no cooldown, terminal resolution constants
- activation_pass() system: gate check, proximity query, engagement
scoring, unentangled-NPC routing, module selection, emits
TriangleActivatedEvent on 10-tick cadence after contamination
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Spatial chain for Sprint 23 (#576, #577, #578):
- TileKind enum (Floor/Wall/Void/Restricted) on WalkabilityMap with
set_tile_kind/tile_kind API, backward-compatible with existing
is_walkable/set_walkable
- Location YAML tile format: tiles as string arrays (F/W/V/R chars),
load_location_tiles() stamps tile data onto WalkabilityMap from
ContentStore on production startup
- Chunk streaming system: ChunkLoadRadius + ChunkStreamingCadence
resources, loads/unloads chunks around player position on cadence.
v0.1 radius covers full district (no streaming stutter)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register StorytellerPlugin in main() and dump_schedule_graph() so
contamination system runs in production (critical, rounds 2+3)
- Add doc comment to z_bands_connected clarifying band indices vs
absolute z-levels (D-110)
- Add TODO on hardcoded modifications: vec![] in save_to_file
- Init ContaminationActive in minimal_world() test helper
- Replace ChaCha20Rng with SimRng in fuzzy_map tests (D-010)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three save/load bugs fixed:
- ContaminationActive not persisted in SaveStateV1 — caused double-fire
of contamination pressure on reload after tick 300.
- Loaded triangle entities missing ActiveSim marker — made them
invisible to escalation and contamination systems after any load.
- Existing triangle entities not despawned before load — created
duplicates, doubling tension escalation per tick.
Also: HashSet → BTreeSet for D-010 compliance, defensive event queue
reset on load, and three regression tests for triangle roundtrip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
50-seed randomized testing against 4 structural invariants:
walkable connectivity (BFS), entity bounds, door adjacency,
and minimum tile count floor. Includes generator module for
test-scoped procedural map creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New test_world room with two zones (Terminal/Corridor) separated by
a door. Adds ZoneCrossEventQueue resource and detect_zone_crossings
system to fire events when the player crosses zone boundaries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DLC entry point for future player construction system. Adds
Modification struct, ModificationType enum, and Modifications
component. Wired into SaveStateV1 with #[serde(default)] for
forward-compatible save format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Follow-up to b6a9b78: register TerminalInteractedQueue resource and
door/terminal interaction systems in SimulationPlugin; add door state
save/load in save_io (open doors round-trip through SaveStateV1);
make WalkabilityMap param optional in process_door_interaction so
plugin-only tests work without a loaded map; fix information_boundaries
test missing open_doors field.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add DoorState component tracking is_open and blocking_tile; add
DoorInteractRequest per-player component consumed by new
process_door_interaction system (toggles walkability each use)
- Add TerminalInteracted event, TerminalInteractedQueue resource,
TerminalInteractRequest component, and process_terminal_interaction
system (emits event on Use verb)
- Add ExamineText(String) component for authored object examine text;
extend process_examine_interaction with object examine path:
uses ExamineText if present, falls back to generic string if absent
- Fix: add Without<ObjectType> filter to npc_query in
process_examine_interaction — previously any entity with TilePosition
was mis-routed through the NPC text generator
- Add SaveStateV1.open_doors: Vec<StableId> with #[serde(default)]
for backward-compatible serialization
- Add "Open"/"Close" → DoorInteractRequest and "Use" →
TerminalInteractRequest dispatch in process_player_input
- 10 integration tests in tests/environmental_interaction.rs covering
all acceptance criteria: door toggle (both directions), open-to-close,
invalid target, readable examine (with/without ExamineText), out-of-range,
terminal event emission, request cleanup, and save state round-trip
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Triangle phase and tension were silently lost on save/load. Now
serialized as triangle_states vec in SaveStateV1, sorted by
triangle_id for determinism (D-010). Dedicated triangle entities
are respawned on load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Adds TemplateOwnership to NpcSaveState and TemplateReferenceMap to
SaveStateV1 so cross-template links survive save/load and tier
eviction (D-025, D-026). Both fields use serde(default) for backward
compatibility with existing saves.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
- monologue.rs: tracing::warn on unrecognized trigger in fallback arm
- disclosure.rs: borrow-sequencing comment, first() simplification note
- trait_modifiers.rs: delegate parse_confidence to KnowledgeConfidence::try_from
- npc_knowledge_transfer.rs: fix misleading "draw randomly" comment,
document one-directional transfer behavior
- graph.rs: document intentional no-retrigger after contradiction
- dialogue.rs: explain entity grant guardrail asymmetry (D-079)
- knowledge/mod.rs: document one-tick monologue lag from system ordering
- poi_discovery.rs: document D-079 carve-out for direct KG write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tell_state reads KG for other-entity relationship state (MVP boundary
per D-082). Contradiction monologue fires with pre-resolved entity names,
shifts ToldBy source to PersonOfInterest. Full THE FRIEND arc event chain.
Implements D-082 step 1, D-083 event chain.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
transfer_npc_knowledge system runs after conversations with trust-gated
fact exchange, confidence capping at KnowsOf, ToldBy source construction.
POI data model with PointOfInterest component and proximity-based discovery
via KnowledgeGranted events. Implements D-080. Closes Q-024.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tyre #1: Extract run_dialogue_pipeline() shared helper — eliminates ~60
lines of duplication between process_talk_interaction and
process_dialogue_response (L1-L4 pipeline).
Hoshe #1: process_dialogue_response now updates ActiveDialogue with
current tick on follow-up selection — prevents stale started_tick.
Tyre #4: process_dialogue_response now updates InteractionMemory on
follow-up — multi-turn conversations are visible in history.
Hoshe #6 / Tyre #6: handle_dialogue_response adds server-side range
check (CLOSE_RANGE), matching Talk/Confront pattern (D-010 info
boundary).
Hoshe #2: Weighted selection fallback replaced with unreachable!() —
score_line always returns >= 1, so the fallback was dead code.
Hoshe #3: assert!(false, ...) → panic!() in serialization.rs (clippy).
Hoshe #4: SetFacing and TeleportToHub added to roundtrip test.
Hoshe #5: setup_dialogue_response_world inlined (trivial pass-through).
Tyre #2: Doc comment on DialogueCooldownTracker explains per-player-global
design choice (line IDs are NPC-scoped per D-035, no collision risk).
Tyre #3: CONFRONTATION_LINES comment updated with TODO for D-028/D-035
migration.
Tyre #5: DialogueResponse fixture added for cross-language GDScript
testing (input_dialogue_response.msgpack).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move dialogue system registrations from BridgePlugin to NpcPlugin (#538):
game logic that depends on NPC-layer resources now registers where it
belongs. BridgePlugin retains only wire protocol concerns.
Implement DialogueResponse verb handler (#539): new process_dialogue_response
system runs the full D-028 four-layer pipeline to select follow-up lines
when the player picks a dialogue option. Clears ActiveDialogue when no
candidates remain. Fix latent schedule ambiguity — emit_observation_events
now has explicit .before(advance_tick) constraint.
Verify trust-gated gossip pipeline (#171): confirmed process_talk_interaction
correctly passes KnowledgeConfidence through relationship_to_trust() per
D-075. Added integration tests for Secret-tier access (Friendly+KnowsDetails)
and Surface-only fallback (Friendly+Suspects).
Wire DialogueCooldownTracker into selection (#338): added regression test
confirming no line_id repeats within the 600-tick cooldown window across
10 consecutive Talk interactions.
Closes#538, #539, #171, #338
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix protocol version comment (12 → 13) in ObserverSnapshot doc
- Widen Want intensity range from 3..=9 to 1..=10 to match spec and test
- Replace duplicated pool selection in trigger_recognition_monologue with
call to select_pool_line helper (~50 lines removed)
- Document NaiveSpatialIndex migration cost for grid/quadtree swap
- Remove misleading Default derive from TellCategory (Nervous is not a
sensible default for neutral NPCs)
- Add caller invariant doc on generate_npc (no TilePosition spawned)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Follow verb on interaction dispatcher. FollowTarget component tracks
target entity, proximity ticks, and LOS-lost ticks. update_follow_state
system: observation events fire at double frequency while following,
NPC suspicion increases via stress when player within 2 tiles for 60+
ticks (configurable). Follow ends on LOS lost timeout, suspicion
threshold crossed, or player issues different action. FollowStateWire
emitted in ObserverSnapshot for client HUD.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define SpatialIndex trait with entities_in_range, entities_at, and
update methods. NaiveSpatialIndex uses Vec backend with Manhattan
distance. sync_spatial_index system auto-updates from Changed<TilePosition>.
Registered as Bevy resource. Trait abstraction allows grid/quadtree
replacement later without touching callers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Server-side infrastructure for information asymmetry in dialogue.
NPCs display role labels (Worker, Supervisor, Patron) instead of
real names until the player explicitly learns them via KG lookup.
NpcColorIndex (stable_id % 8) persists across name reveal.
ConversationEvent and DialogueResponseEvent carry display names +
color indices on the wire with serde(default) for compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep all new variants from both branches: Greeting (copy), FirstMeeting
and RepeatedVisit (server). Combine Mood doc comments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>