Files
security-game/MEMORY.md
T

5.0 KiB

MEMORY

Session memory for durable decisions, state, and gotchas. Keep concise, remove obsolete entries.

Decisions

  • Engine: Godot 4.3 stable (official editor build, ~/.local/bin/godot). The system's preinstalled godot-runner (4.7.2, Fedora) cannot load projects from arbitrary paths because it was compiled without path overrides; use the official editor binary instead.
  • Language: GDScript. All cross-file references use explicit preload() constants (not class_name globals) so scripts compile in headless --script runs without the editor's class cache.
  • Test framework deviation: the project uses a custom minimal headless test runner (src/test/test_runner.gd) instead of GUT, because GUT needs the editor plugin. CI runs it with --headless.
  • Eventing: no autoload signal bus. ShiftCoordinator owns an EventBus instance (src/core/event_bus.gd) passed to subsystems; subsystems emit only when a bus is assigned. Autoloads at parse time are unreliable under --script.
  • Static signals are not available in Godot 4.3 GDScript ("Expected func or var after static") — do not use them.
  • GDScript lambdas capture local variables by value; mutating a captured local inside a lambda does not affect the outer variable. Use member functions for accumulators in tests.
  • Prototype shift duration defaults to 300 s for testing; production targets 10-15 minutes per PLAN.md.
  • RNG: NPC reactions and event scheduling are seedable for deterministic tests.

Current State

  • Core simulation, three scenarios, headless tests (41 passing), and a first-person prototype with interaction HUD, access inspection, radio (assist + emergency hold-to-confirm), debug overlay, and shift review are implemented and green.
  • NPC avatars now walk to per-intent waypoints (VenueScenario.waypoint_for_intent): enter/trespass/deliver/arrive → entrance, photo/film/perform/wander/roam → sidewalk, sit → steps_end. NpcAvatar.setup takes an explicit door_position.
  • Returning-trespasser behavior: NpcProfile.return_probability (trespasser 0.6, mental-crisis 0.4, others 0). NPCController._maybe_schedule_return() moves a directed-away NPC to LOITERING_DISTANT; tick_return(delta) fires after 20-60s and transitions back to APPROACH. NpcAvatar._advance_fsm re-targets the door on return.
  • Perception pass: NPCController.perceive_guard(distance) returns {noticed, band, reaction} with bands unaware (>6m), aware (2-6m), near (<2m). Near + temperament ≥0.6 → ARGUE, else COMPLY. GameManager._perception_pass() feeds real guard distance each physics tick and logs first-time notice.
  • Responder dispatch: RadioSystem tracks pending_calls and decrements arrival_delay_s in tick; emits responder_arrived when a call's delay elapses. RadioSystem.responder_outcome(call) maps danger/weapons → command/restraint/evacuation/arrest. GameManager._on_responder_arrived spawns a RESPONDER avatar at the entrance and logs the outcome. HUD shows an emergency-radio cooldown label via _update_radio_cooldown.
  • Wand screening (Joke Shop): WandScreener (new src/core/wand_screener.gd) classifies carried items as prohibited (weapon/knife/gun/blade) vs false-alarm (keys/belt/phone/coins/watch/buckle) vs clear. NpcProfile.carried_items holds guest items. GameManager binds W to _toggle_wand_screening and A/D to decide_wand; decide_access blocks admission at wand venues until controller.screened is true. VenueScenario.door_staff_lines holds venue-specific door-staff dialogue (Joke Shop has id/ticket/wand/refusal/prohibited lines).
  • Polish/accessibility: tone picker (T cycles selected_tone via TONE_ORDER; HUD tone_label), aim-free targeting (Tab cycles nearby NPCs within NEARBY_RADIUS), color-coded escalation indicator (Hud.set_escalation), procedural audio (AudioFeedback node generates sine tones for confirm/radio/escalation/deny), and a centralized Strings table (src/core/strings.gd, lookup/fmt) for localization. NOTE: Strings.get was renamed to Strings.lookup because get collides with Object.get.
  • Run tests: godot --headless --path . --script res://src/test/test_runner.gd
  • Boot check: godot --headless --path . --quit-after 120
  • Screenshot tool: godot --path . --script res://src/test/take_screenshot.gd (writes to user://screenshot.png).

Known Issues / Notes

  • RefCounted reference cycle: NPCController holds an FSM that holds a back-reference to its owner, so test suites report "resources still in use at exit". Benign (process exit), but worth breaking later (e.g. WeakRef or explicit dispose()).
  • Headless (dummy renderer) logs "Parameter m is null" from mesh_get_surface_count; it does not occur with a real GL driver and does not affect logic.
  • Wand screening (Joke Shop) is policy-only; the interaction is not yet in the prototype (see TODO).

Open Questions

  • Tone picker UI design and whether tones should be selectable per interaction.
  • Whether the responder avatar should be playable/controllable or purely scripted.
  • Localization target languages.