# DONE Completed task sections moved from `TODO.md`. Sections are moved only when every item is finished and verified. ## Project Setup - [x] Confirm Godot 4.x editor version to standardize on (Godot 4.3 stable, official build). - [x] Initialize a git repository and add a sensible root `.gitignore` for Godot and export artifacts. - [x] Scaffold the Godot project (`project.godot`, input map, folder structure). - [x] Add the GUT test framework and a runnable headless test command. (Deviation: GUT requires the editor plugin; replaced with a lightweight custom harness in `src/test/test_runner.gd` that runs without the editor.) - [x] Document setup, run, and test commands in `README.md`. ## Prototype Scene - [x] Build a placeholder Hollywood Boulevard building entrance with steps, sidewalk, and a restricted door (procedural `WorldBuilder`). - [x] Add a controllable guard character with first-person camera controls (`GuardController`). - [x] Add basic keyboard/mouse input: move, look, interact, and cancel. - [x] Add a debug overlay showing NPC intent, escalation level, and shift timer (`F` toggles). ## Core Simulation — Systems - [x] Implement `ShiftCoordinator` to start, advance, and end a shift. - [x] Implement `VenueScenario` as a `Resource` with locations, rules, NPC pools, and event pools. - [x] Implement an event scheduler that emits routine traffic and occasional scripted events (`EventScheduler`). - [x] Implement `NPCController` with movement toward waypoints, doors, and queues (movement handled by `NpcAvatar`). ## NPC Interaction - [x] Define `NpcProfile` resources with intent, temperament, compliance, and escalation tolerances. - [x] Implement a lightweight finite state machine for NPC states (`FSM`). - [x] Implement the observation phase (Level 0) without forced intervention. - [x] Implement simple deterrence (Level 1): raise hand, point, eye contact, and head shake. - [x] Implement polite dialogue prompts and reaction logic driven by escalation level and profile. - [x] Implement formal warnings (Level 2) and make NPC reactions depend on profile and level. - Note (2026-10-31): the selectable dialogue tone-of-voice system (Professional/Friendly/Firm/ Suspicious) was intentionally removed from code and docs; reactions now depend solely on escalation level and NPC profile. - [x] Implement blocking and physical positioning (Level 3) without default force. - [x] Wire typical NPC reactions: leave, argue, ignore, record, move closer, or return later. ## Access Verification - [x] Implement `AccessVerifier` with input from recognition, wristbands, IDs, tickets, credentials, and guest lists. - [x] Add wristband inspection UI showing color, pattern, date, and event marking. - [x] Model valid, expired, counterfeit, mismatched, missing, and ambiguous access artifacts. - [x] Record admit, redirect, hold, and refuse decisions for shift review. - [x] Add feedback scoring for incorrectly denying an authorized person or admitting an unauthorized one. ## Escalation and Radio — Core - [x] Implement `EscalationManager` with explicit levels 0 through 5 and available-action gating. - [x] Implement the security-assistance radio call (Level 4) with a report form and arrival delay. - [x] Implement the emergency radio (Level 5) with hold-to-confirm and a report form. - [x] Add radio cooldown and battery limits to prevent spam. ## Incident, Reputation, and Shift Review - [x] Implement `IncidentRecorder` emitting structured events (actor, behavior, action, evidence, escalation, outcome). - [x] Implement body-camera evidence tied to incident events. - [x] Implement `ReputationEvaluator` for peaceful resolutions, accuracy, complaints, damage, and entrance security. - [x] Implement `CrowdSystem` reactions to calm, aggressive, armed, and emergency situations. - [x] Implement the end-of-shift review screen summarizing decisions, consequences, and justification. ## Scenario Content — Venue Data - [x] Author a minimal Hollywood Boulevard building scenario with tourist, confused-visitor, and simple-trespasser NPCs. - [x] Author a rare violent-threat event that requires justified emergency escalation. - [x] Add The Joke Shop scenario data: ID checks, guest lists, and wand-screening policy. - [x] Add the Hollywood Boulevard Late Night TV Studio scenario data with the full NPC mix and credential checks. - [x] Add a mental-health-crisis event with careful, respectful de-escalation guidance. ## Tooling and Quality - [x] Add headless test coverage for access verification rules. - [x] Add headless test coverage for escalation transitions and scoring. - [x] Add fixed-seed scenario tests for deterministic NPC behavior. - [x] Set up GitHub Actions to run headless tests on push (`ci.yml`). - [x] Add a smoke-test checklist for launching one full shift (in `README.md`). ## Bug Fixes — LATE shift phase - [x] Add a `Rules.Phase.LATE` entry (`start_s` 700) to `shift_phases` in all three venues. - [x] Confirm the LATE-tagged `violent_emergency_event` is now pulled from `EventScheduler.pull_due`. - [x] Add a test asserting the LATE phase is reached and the event is eligible in phase. ## Missing PLAN Features — Protester NPC - [x] Add `PROTESTER` to `Rules.NpcType` and `NPC_TYPE_NAMES`. - [x] Add a protester profile (id `"protester"`) with ARGUE/IGNORE/RECORD/CALL_SOMEONE reactions. - [x] Add a `TYPE_COLORS` entry for `PROTESTER` in `npc_avatar.gd`. - [x] Add routine protester events to the Hollywood Boulevard and TV Studio event pools. - [x] Add scenario tests asserting the protester profile and events exist. ## Missing PLAN Features — “Call someone else” reaction - [x] Add `CALL_SOMEONE` to `Rules.Reaction`. - [x] Handle `CALL_SOMEONE` in `NPCController._update_state_for_reaction` (transitions to ARGUING). - [x] Give influencer, trespasser, and protester profiles CALL_SOMEONE weights. - [x] Add a reaction log line in `GameManager` and a `Strings` entry. - [x] Add a test that CALL_SOMEONE can be rolled and updates the FSM. ## Missing PLAN Features — Door lock/secure - [x] Add a `DoorState` core class with locked/unlocked state and `attempt_force()`. - [x] Add a visible door lock indicator (green open / red locked) to the world. - [x] Add the `L` key binding toggling the door lock, with HUD indication. - [x] Make FORCE_ENTRY reactions fail while the door is locked (thwarted outcome). - [x] Add `Strings` entries and a headless test for the lock state machine. ## Missing PLAN Features — Flashlight - [x] Add a `G` key binding toggling the guard's flashlight. - [x] Attach a `SpotLight3D` to the guard that toggles with the flashlight. - [x] Widen NPC perception radius while lit at evening/late phases (`perceive_guard` bonus). - [x] Add `Strings` entries and a headless test for the perception effect. ## Persistence - [x] Call `Save.save_shift_result(review)` from `GameManager._on_shift_ended`. - [x] Load past results and show record count + best score on the shift review screen. - [x] Add a test that a saved review round-trips through the `Save` helper. ## Navigation - [x] Add a `NavigationRegion3D` with a hand-authored flat navmesh covering sidewalk, steps, and entrance. - [x] Switch `NpcAvatar` from direct movement to `NavigationAgent3D` path following (with direct-movement fallback). - [x] Keep per-intent waypoint and returning-trespasser re-targeting on the navmesh. - [x] Make the step geometry visual-only (no collider) so pathing to the entrance works. ## Localization — JSON strings - [x] Move the string table to `res://content/strings.en.json` (41 keys). - [x] Load the JSON at startup; `Strings.lookup`/`Strings.fmt` signatures unchanged. - [x] Update the strings tests to confirm every JSON key resolves to a real value. ## Distribution — Downloadable executables - [x] Commit `export_presets.cfg` with Linux, Windows, and macOS desktop presets. - [x] Add `tools/build_releases.sh` for headless release exports. - [x] Add `docs/RELEASING.md` with template installation, build, and Gitea release steps. - [x] Add a prominent **Downloads** section at the top of `README.md` linking to the release page. - [x] Add `tools/publish_release.sh` (Gitea API release + asset upload, requires `GITEA_TOKEN`). ## CI/CD — Gitea Actions full build - [x] Add `.gitea/workflows/build.yml` with a fast checks job (import + headless tests on push/PR) and a full build job (export templates, Linux/Windows/macOS exports, artifact upload) on tags and `workflow_dispatch`. - [x] Tag builds automatically publish the Gitea release via `tools/publish_release.sh` using the `GITEA_TOKEN` secret. - [x] Remove the old `.github/workflows/ci.yml` to avoid double runs on hosts scanning both paths. - [x] Document the CI flow in `docs/RELEASING.md`. ## Gameplay — Venue selection - [x] Add a `V` control that cycles Hollywood Blvd → The Joke Shop → TV Studio and restarts the shift (`GameManager._cycle_venue`), with a `hud.venue_switched` string entry. ## 🎨 Visual polish pass (v0.2.0, pre-release graphics) - [x] Phase-based lighting: per-phase sun direction/energy/color, sky gradient (ProceduralSkyMaterial), and ambient in `GameManager.PHASE_LIGHTING`; applied on phase change and shift start. - [x] Street lamps: poles with emissive bulbs + OmniLights on the sidewalk; `WorldBuilder.set_night_mode` switches bulbs/lights on at night. - [x] Marquee signage: emissive “STEP LINE” strips + Label3D above the entrance. - [x] Street dressing: crosswalk stripes, planters, and low-poly trees. - [x] NPC visual variety: per-seed height/brightness variation (deterministic LCG). - [x] Denser routine traffic (tourists/performers/influencers/trespassers). - [x] `capture_screenshots.gd` now fast-forwards to the LATE phase and captures a night set (lamps + marquee + flashlight cone); README gallery is now 2×3 (day + night); six shots regenerated in `docs/screenshots/`. - [x] `config/version` → `0.2.0`; `build/` artifacts rebuilt and Linux binary smoke-tested. - [x] New world/lighting headless tests (56 total). ## Screenshot Refresh - [x] Regenerate and verify the six README screenshots from the current build. ## 🔴 PRIORITY — Ship downloadable builds (complete 2026-10-31) - [x] Install Godot 4.3 export templates locally. - [x] `./tools/build_releases.sh` produces Linux/Windows/macOS artifacts (0.2.0). - [x] Linux binary smoke-tested (headless, 0 script errors). - [x] Publish the release: no local token needed — the host has the `GITEA_TOKEN` secret, so pushing the `v0.2.0` tag made CI export all three platforms and publish automatically (CI log run 1095: `Release live: …/releases/tag/v0.2.0`, Job succeeded). - [x] Confirm links resolve: tag page and `/releases` return HTTP 200 publicly. Follow-up: anonymous **asset downloads** require sign-in on this host (host setting). - [x] CI packaging follow-up was already live (`.gitea/workflows/build.yml` on tags). ## 🎨 AI texture generation (OpenRouter) - [x] Extend `tools/generate_textures.py` with an OpenRouter image-API mode (`--ai`, `--prompt`, `--name`, `--model`, `--size`), decoding base64 PNG responses and clamping to ≤1024px. - [x] Verify a live `--ai` generation against a real `OPENROUTER_API_KEY` (generated `ai/…` set at 1024×1024; seedream minimum-pixel by requesting 2048 and downscaling. ## 🧹 Runtime hygiene + timeline/polish items - [x] RefCounted cycle cleanup: `FSM` now holds its owner via `WeakRef`; `ShiftCoordinator` is `RefCounted` (logic-only, no tree needs); persistence tests free the `Save` node. Test suite now exits with **zero** “resources still in use” warnings. - [x] Full-length shifts: the prototype now uses each scenario's `shift_duration_s` (900s ≈ 10-15 min per PLAN.md) instead of a hardcoded 300s test duration. - [x] Wand cooldown feel: a short `WAND_COOLDOWN_S` recharge between wand scans with a `wand.recharge` message (Open Polish item). ## 📦 Installer packaging (Windows NSIS + Linux AppImage) - [x] Export presets: `binary_format/embed_pck=true` on all platforms — every binary is now self-contained (all game data in one file; `.pck` no longer shipped). - [x] App icon: `assets/textures/icon.png` (1024) + multi-size `icon.ico`, copied to `packaging/windows/step-line.ico` and `packaging/linux/step-line.png` via the generator. - [x] NSIS script `packaging/windows/step-line.nsi` (installs `Step Line.exe`, uninstaller, shortcuts, registry entry; `makensis /DVERSION=… /DARTIFACT=…`). - [x] AppImage: `packaging/linux/step-line.desktop`, `AppRun`, appdata.xml, and `build_appimage.sh` (appimagetool, `APPIMAGE_EXTRACT_AND_RUN=1`). - [x] Local end-to-end: rebuilt embedded exports and produced `step-line-0.2.0-x86_64.AppImage`, ran it headless (0 script errors). - [x] CI: NSIS + AppImage steps added to `.gitea/workflows/build.yml` build job; publish glob already uploads `build/step-line-**` (covers setup/AppImage names). - [x] CI run 1110 verified end-to-end (v0.2.1): NSIS installer `step-line-setup-0.2.1.exe` built, `7z l` shows `step-line.exe` + `uninstall.exe`, AppImage built (33.9 MB), release `v0.2.1` published, `Job succeeded`. - Gotchas fixed on the way: makensis needs `-D` defines (not `/D`) and absolute paths (it resolves relatives against the script dir); installed exe must be space-free (`step-line.exe`); icon.ico must use classic BMP entries (ImageMagick; PIL emits PNG-compressed ICOs NSIS rejects). ## 🎮 Assets & detailed graphics pass (textures, rigs, UI feedback) - [x] Authored texture set via `tools/generate_textures.py` (deterministic PIL): brick facade, seamless concrete, asphalt, awning, skyline, vignette, HUD icons, access-card art — all ≤1024px and power-of-two; imported under `assets/textures/`. - [x] Environment: textured facade + glass door, concrete sidewalk, asphalt street, striped awning with “STEP LINE”, generated skyline backdrop planes, soft sun shadows. - [x] NPCs: humanoid rig (torso/head/limbs) with archetype props (camera, delivery box, hat, sign, backpack), procedural walk bob, head-turns-toward-guard cue. - [x] UI/feedback: inspection artifact cards, mode icons (flashlight/door/wand), escalation vignette, body-camera overlay (corner brackets + REC + shift clock). - [x] Assets convention documented in README; `test_asset_textures_present_and_pow2` guards the pipeline (57 tests total). - [ ] (remaining, in TODO) crowd performance profiling and LOD/culling when the crowd grows. ## CI — cache timeout mitigation - [x] Remove `actions/cache@v4` from the checks job (runner cannot reach GitHub's cache service; ~2 min of retry timeouts per run; Godot import is fast without it). - [x] Add `GODOT_SILENCE_ROOT_WARNING=1` to both jobs. - [x] Confirmed in run 1090: zero `CacheService` lines, 54/54 tests pass, job succeeds. - [x] Document the cache situation and re-enable path in `docs/RELEASING.md`. - [x] Ignore runner logs in `.gitignore` (`.gitea/logs/`). ## Gameplay — Venue door-staff dialogue - [x] Add `VenueScenario.door_staff_line(context, rng)` picking a seeded-random line from `door_staff_lines` (empty for unknown contexts/venues). - [x] Wire `GameManager` to log venue lines: id/ticket check line when inspection opens, wand-check line when screening starts, prohibited-item line on correct wand refusal, and refusal line on denied entry (The Joke Shop flavor only). - [x] Add a headless test covering all Joke Shop contexts, unknown contexts, empty venues, and deterministic seeded picks. ## Core Simulation — NPC Perception - [x] Implement a perception pass so NPCs react to the guard's gestures and proximity. - [x] Give NPCs local waypoints per intent (photo spots, queue, stage door, sidewalk exit) instead of all walking to the entrance. - [x] Add returning-trespasser behavior: an NPC that was directed away may circle back later in the shift. ## Escalation and Radio — Responder Dispatch - [x] Spawn a dispatched security NPC / armed responder avatar that arrives at the venue after the call delay. - [x] Give the responder command, restraint, evacuation, and arrest outcomes that resolve the incident. - [x] Add a visible emergency-radio cooldown indicator in the HUD. ## Scenario Content — Wand Screening (The Joke Shop) - [x] Implement the handheld wand inspection interaction with alerts from keys, belts, phones, and weapons. - [x] Enforce screening-before-entry flow and record false alarm / prohibited item decisions. - [x] Add venue-specific dialogue lines for door staff. ## Polish and Accessibility - [x] Add aim-free targeting (Tab) and venue cycling (V). (The earlier dialogue tone picker was superseded and removed with the tone-of-voice system.) - [x] Add interaction alternatives that do not require precise aiming (e.g. scan-and-select pinned list of nearby NPCs). - [x] Add visual and audio feedback per escalation level (colors, tones, shake). - [x] Add audio for crowd ambience, radio responses, and confirmation prompts. - [x] Externalize dialogue and UI text for localization. ## Generalized Game Asset Tooling - [x] Reuse the OpenRouter image backend for role-aware arbitrary asset generation. - [x] Add exact-dimension image crop and aspect-preserving resize commands. - [x] Audit existing assets for role-specific dimensions, aspect, alpha, and tile seams; report projection/perspective checks for visual review. - [x] Add automated tests and document asset-pipeline usage. ## Generated Asset Integration - [x] Regenerate the AI asset set at role-specific aspect ratios and dimensions. - [x] Audit, process, and wire every generated asset into the game runtime. - [x] Add screenshot auditing and refresh the README screenshot gallery. ## Access artifacts actually match the venue Completed in the working tree (uncommitted with the control pass). Headless **74/74**. - [x] Build artifacts from the event (`override_access`, carried items) and the venue policy, not from `NpcType` alone. (`src/core/access_loadout.gd`; spawn signal is `(profile, intent, npc_id, event)`.) - [x] Joke Shop: valid ID+ticket, ID without ticket, expired/missing ID. Recognition does not auto-grant on every `AUTHORIZED_GUEST`. - [x] Wand venues roll `carried_items` per spawn (seeded); override can force a pocket. - [x] Studio counterfeit / follow-through events carry `override_access`. - [x] `AccessVerifier.verify(..., already_screened)` so a screened guest is not still denied. - [x] Joke Shop / Studio `sidewalk` and `steps_end` waypoints so film/photo intents do not walk to the entrance. - [x] Wand shortcut is `C`, not `W`. Tests: `src/test/tests/test_access_loadout.gd`. ## Player control, focus circle, and radial action menu Completed 2026-10-31 with ADR-0007–0010. Headless **77/77**, plus a windowed smoke (`src/test/smoke_focus_radial.gd`) that opens the wheel and saves screenshots. ### Locomotion - [x] Wand off `W` (now `C` + wheel). `A`/`D` allow/deny only while inspect/wand is open (they step the wheel while it is open). `Q` remains a closed-menu polite shortcut. - [x] Accel/decel on walk; gravity only when airborne; `floor_snap_length` 0.3. - [x] Mouse look only while captured and `not input_locked`. Freeze walk+look while radial / inspect / wand / radio / review is up. Recapture on close. - [x] `cancel` (Esc / RMB) closes the top modal first; with nothing open, Esc toggles mouse capture and RMB does nothing. ### Focus and focus circle - [x] Focus cone in `src/game/npc_focus.gd` (~10 m, ~10°). Closest-to-forward wins; occlusion ray skips building hits. `Tab` still cycles 12 m. - [x] 8-frame miss grace so walk-bob does not drop focus. - [x] Billboarded blue circle in front of the face on `Rig/Head`; brighter while the radial is open; `Label3D` `display_name`. HUD target line uses the display name, not the node name. - [x] Screen-center crosshair. ### Radial HUD - [x] `src/game/ui/action_radial.gd`: LMB or `Space` opens when someone is focused. Opening click ignored for 120 ms. Slices: raise hand, point, head shake, polite, inspect, wand (grey if no wand), warning, block, radio assist. Emergency stays on `R`. - [x] Highlight by mouse angle, or `A`/`D` / arrows. Click slice or `Space` confirms. `Esc` / RMB closes with no action (no recorder event). - [x] Inspect / wand / radio slices close the wheel then open the existing panels. Freeze until that panel closes. - [x] Number keys `1`–`6` still work when the wheel is closed. ### NPC display - [x] Procedural rig: neck, shoes, hair cap from seed, two eye spheres. Props, height variation, walk bob, notice-head-turn kept. Collision capsule scaled to rig height. Named `Head` is the ring attach point. Group `npc`. ### Input, copy, tests - [x] Named input map: `action_menu` (Space/LMB), `confirm` (Space/Enter), `cancel` (Esc/RMB), `inspect` (E), `wand` (C). `GameManager` uses `is_action_pressed`. - [x] Windowed smoke: focus circle + name, wheel open/step/confirm/cancel (`src/test/smoke_focus_radial.gd`, exits non-zero on failure). - [x] README controls table, smoke checklist, test count, implemented/not-yet lists. ## Visual polish — Skyline backdrop transparency Found during the control smoke test; verified with PIL analysis. - [x] Regenerated `assets/textures/game_ready/skyline.png` with transparent background (commented out opaque border line 234 in `tools/generate_textures.py`). - [x] Integrated backdrop into `WorldBuilder` via `_build_skyline_backdrop()`: creates a PlaneMesh with T_SKYLINE texture at Vector3(-80, 12, -50) behind the building. - [x] PIL verification confirms RGBA mode with transparent pixels in bottom rows. ## Runtime boot and spawn fixes Found while running the windowed smoke; all verified with a boot run and regenerated screenshots. - [x] `GameManager` did not compile: `radial` was typed `CanvasLayer`, so `radial.is_open` / `confirm_selected()` made `:=` inference fail. Typed as `ActionRadial` (the parse error would have broken the whole main scene). - [x] Camera sat at the guard's feet: `Head` had no height offset, and the new gravity pulled the guard to the floor. `Head` is now at y=1.6, and the guard spawns on the landing. - [x] Guard spawned inside the building collider and fell through the world. Building volume moved back to z ∈ [-8, -4] so the door / awning / marquee sit on its front face; the guard spawns in front of the door facing the street. - [x] Steps rose away from the door; treads now rise toward the entrance. - [x] Event scheduler was polled every physics frame (~60 Hz), spawning >1200 NPCs in seconds. `ShiftCoordinator` now accumulates time and pulls once per simulated second; regression test in `test_scenarios.gd`. - [x] Live NPCs capped at `MAX_LIVE_NPCS` (16); routine traffic is dropped at the cap and a scripted event evicts the oldest unfocused NPC. - [x] Removed the skyline backdrop planes (opaque black texture). Tracked as a follow-up in `TODO.md`. - [x] README screenshots regenerated with the fixed eye height and entrance staging. ## World and presentation follow-ups (found during the control smoke) Completed 2026-10-31 (working tree). Headless **88/88**; `src/test/smoke_session_shell.gd` and the windowed focus/radial smoke both pass. - [x] The skyline backdrop was removed: `assets/textures/ai/game_ready/skyline.png` has an opaque black background, so the planes rendered as black slabs with window dots. Re-add the backdrop only with a transparent asset or a keyed shader. - [x] Guard settles ~0.5 m above the ground after spawning on the landing. Check the capsule offset and the spawn `y`, and verify eye height while walking the steps. - [x] Steps and the landing are visual-only (no collider). The guard walks through them; add simple step colliders or restage the landing so it does not intersect the walk path. - [x] Marquee text (`STEP LINE`) is tiny from the sidewalk. Scale the Label3D up. - [x] Focus circle and name tag are small at 2–3 m. Consider a larger circle / font after a real playtest. - [x] NPC crowd cap (`MAX_LIVE_NPCS`) silently drops routine traffic at 16. Add a short despawn/lifetime for ordinary guests so the entrance does not fill up with standers. ## Session shell Completed 2026-10-31 (working tree). Headless **88/88**; `src/test/smoke_session_shell.gd` and the windowed focus/radial smoke both pass. The game boots into a live 900 s shift. `Save` declares `settings.json` and never uses it. `V` cycles venues by restarting the shift. - [x] Startup menu: pick venue, start shift, quit. Keyboard + click. Replaces `V` as the player-facing path (`V` may stay as a debug bind). - [x] Pause overlay when `Esc` releases the mouse (or a dedicated pause key): resume, restart shift, settings, quit to menu. - [x] Settings: mouse look sensitivity, persist through `Save` / `settings.json`. Load on boot. - [x] Shift review "Run another shift" returns to the venue menu, not a silent restart of the same scenario. ## Documentation drift Completed 2026-10-31 (working tree). - [x] README controls table, smoke checklist, test count, and implemented/not-yet lists refreshed for the control pass (77 tests). - [x] `PLAN.md` Tech Stack / Planned Commands still say GUT and GitHub Actions; the project uses `src/test/test_runner.gd` and `.gitea/workflows/build.yml`. - [x] MEMORY: door_staff_lines log through `GameManager`; 300 s default removed. ## Remaining PLAN verbs (radial exists) Completed 2026-10-31 (working tree). Headless **98/98**; `src/test/smoke_plan_verbs.gd` exercises all five verbs. - [x] Eye contact as a Level 1 action (PLAN / glossary). Distinct from raise-hand. Add a radial slice if it ships. - [x] "I recognize this person" is a player decision recorded as evidence, not a pre-attached artifact on every staff/guest type. Staff still auto-recognize when `override_access` is empty (`AccessLoadout`). - [x] Formal warning can activate the body-camera / security-light flag (PLAN Level 2). Overlay is currently always on; toggling should write an incident event. - [x] Blocking stations the guard between the person and the door (PLAN Level 3). Today it only sends `apply_player_action(BLOCKING)`. - [x] "Request reason for entry" as a Level 3 ask, with a short NPC reply from `dialogue_lines`. ## Crowd presentation Completed 2026-10-31 (working tree). Headless **105/105**; `src/test/smoke_crowd.gd` verifies rubbernecking and panic clearing. A 16-NPC entrance measured ~1.2 ms/frame (824 fps, vsync off), so no LOD/culling was added at the current cap. `CrowdSystem` is a mood/attention float. PLAN wants people watching, gathering, and panicking. - [x] On `aggressive_behavior` / `emergency_responder`, nearby idle NPCs stop and face the incident (rubberneck). On `crowd_panic`, they move away from the door. - [x] Profile a busy entrance with textured materials and props; add LOD or culling for distant NPCs if frame time drops. ## Emergency aftermath Completed 2026-10-31 (working tree). Headless **113/113**; `src/test/smoke_emergency.gd` covers the unjustified-call investigation, the evacuation aftermath, and the review copy. Responder outcomes (`command` / `restraint` / `evacuation` / `arrest`) are log lines. PLAN also wants evacuation, a closed street, and an investigation when the call was unjustified. - [x] `evacuation` outcome actually clears the entrance (NPCs leave, door stays locked). - [x] Unjustified emergency (low danger, no weapon) writes an investigation note on the shift review and applies the existing false-report / reputation hit. - [x] Review copy names the responder outcome, not only battery and crowd %. ## Body-camera replay Completed 2026-10-31 (working tree). Headless **120/120**; `src/test/smoke_timeline.gd` opens the timeline from the review, steps through every event, and returns to the review. - [x] Shift review: a timeline of recorded incident events (ADR-0005 already stores them). Scrub or step through; no live 3D replay required in the first version. ## NPC theater Completed 2026-10-31 (working tree). Headless **127/127**; verified visually with a staged sit/photo/perform lineup. Intents `sit` / `photo` / `film` / `perform` already pick waypoints. ADR-0010 rig (face/hair) is in the working tree. Bodies still idle-stand at those spots. - [x] Sit pose at `steps_end`. Photo: brief raise of the influencer/tourist prop. Performer stays put and plays the hat/sign. ## Audio Completed 2026-10-31 (working tree). Headless **134/134**; `src/test/smoke_audio.gd` checks the bed follows mood and the radio crackles, headless and windowed (real audio server). `AudioFeedback` is sine beeps. DONE called that "crowd ambience"; it is not. - [x] Loop a low crowd bed that rises with `CrowdSystem.mood`. - [x] Radio open/close crackle on the existing radio panel, still headless-safe (no-op without an audio server). ## Accessibility Completed 2026-10-31 (working tree). Headless **139/139**; verified visually at 1.4x font scale with the shape meter showing. - [x] Escalation colors that still read in grayscale (shape or label, not hue alone). - [x] HUD font scale in settings. ## Scenario content still missing vs PLAN lists Completed 2026-10-31 (working tree). Headless **149/149**; `src/test/smoke_scenario_content.gd` spawns the refusal event and the celebrity arrival through the real signal path. - [x] Joke Shop: guest on the reservation list without ID; borrowed/mismatched ID; guest refuses wand screening; late arrival during a performance. - [x] Studio: filming-boundary repeat (influencer ignores a posted line); celebrity-arrival crowd spike (more rubberneckers, not only one `arrive` spawn). - [x] Building: "activate body camera" and "request reason" once those verbs exist, used in at least one authored event. ## Distribution follow-up (host config) Completed 2026-10-31 (working tree). Headless **153/153**. Verified via API+host credentials (2026-10-31): - [x] Repo is **public** (`private:false`) and account has admin rights. - [x] Release `v0.2.1` now carries all five artifacts (portable binaries + NSIS installer + AppImage). - [x] **Host setting applied**: `[service] REQUIRE_SIGNIN_VIEW = false` in `/etc/gitea/app.ini` (backup written alongside), Gitea restarted. Verified anonymously: repo page 200, API 200, and a release asset downloads with the full 50.8 MB AppImage. ## Release pipeline follow-up Completed 2026-10-31 (working tree). Headless **153/153**. - [x] Fix `tools/publish_release.sh`: the `~/.git-credentials` branch never set `AUTH`, so every tag build since v0.2.2 failed to publish. - [x] Add a `github.token` fallback in `.gitea/workflows/build.yml` (the repo has no `GITEA_TOKEN` secret). - [x] Fall back to the checkout's git extraheader when no token/credentials exist (the Gitea runner does not expose `github.token`). - [x] Uploads now fail loudly and the release is verified to carry every artifact (a silent HTTP 4xx used to leave an empty release with a green job). - [x] Host disk expanded to 16G; v0.2.0 and v0.2.9 releases restored from the local artifacts. - [x] Host maintenance via `root@192.168.88.30`: apt cache cleaned and journald capped (`SystemMaxUse=300M` in `/etc/systemd/journald.conf.d/99-size.conf`); ~7.6G free. - [x] Verified end to end: tagging v0.2.12 produced a release with all five artifacts (AppImage, Linux, macOS, NSIS installer, Windows) from CI, with no repo secret. - [x] Backfill decision: v0.2.4–v0.2.8 stay superseded; their tags and source remain, and the release page points at the current build. No rebuilds planned. ## Open Polish Completed 2026-10-31 (working tree). Headless **153/153**. - [x] Keep `%` placeholders stable in `Strings.fmt` when adding keys (positional formatting). ## Screen design references (QA tooling) Completed 2026-10-31 (working tree). 30 Python tooling tests pass; the live judge run classed 3 captures as match and 3 as review (concept mockups are more stylised), with no confident mismatch. - [x] `tools/screen_designs.py generate` writes one reference design per screen (from the `docs/` descriptions) to `docs/screens/designs/`, with an offline `--draft` wireframe mode and an OpenRouter `--ai` mockup mode. - [x] `tools/screen_designs.py compare` checks `docs/screenshots/*.png` against the designs (aspect ratio, size, blank-frame, and layout/palette/detail drift). - [x] Vision-model judge for semantic screenshot comparison: `judge` sends the capture + reference design to a vision model, classes verdicts as match/review/diff with a confidence threshold, supports `--dry-run`, `--json`, `--only`, and `--model`, and reads the repo `.env` for the API key. ## Tooling (when an editor workflow exists) Completed 2026-10-31 (working tree). Headless **157/157**; the exported `.tres` venues round-trip through the resource loader. - [x] Author venues as `.tres` resources: `tools/export_venues.gd` writes `content/venues/.tres` from the factory, and a test loads them back to check nothing is lost (events, profiles, policy, phases, door lines, locations). - [x] GUT decision: **not adopted.** The custom headless runner (`src/test/test_runner.gd`) already covers 157 assertions without an editor plugin, runs in CI, and keeps the project dependency-free; adding GUT would duplicate coverage and require the editor. ## In-Game Scene Examples Completed 2026-10-31 (v0.3.0 working tree). `python3 tools/screen_designs.py list` reports 19 screens (8 scene, 11 UI); the Python tooling tests pass. - [x] Extend the `tools/screen_designs.py` catalogue with the screens that exist in code but had no reference: wand screening, Joke Shop door queue, TV Studio celebrity arrival, crowd panic, responder arrival, body-camera timeline, escalation HUD. - [x] Add a `list` subcommand so the catalogue is inspectable without generating images. - [x] Cover the new entries with `tools/test_screen_designs.py` assertions. - [x] Regenerate the deterministic draft references under `docs/screens/designs/`. (`generate` now keeps existing references unless `--force` is passed, so a draft run cannot replace an AI mockup; `--only` selects a subset.) ## Rendering Quality Completed 2026-10-31 (v0.3.0 working tree). Screenshots regenerated; `screen_designs.py compare` reports 6 good / 0 bad. - [x] Add a shared surface-material module (`src/game/materials.gd`) with roughness/metallic/uv-scale profiles per surface and a single environment tune-up (tonemap, glow, fog, shadows). - [x] Rebuild `WorldBuilder` surfaces with the material profiles and add street detail: window bands, door trim, bollards, bench, bin, curb, planter foliage. - [x] Per-phase environment values (fog, sky energy, glow) so night reads darker and the marquee glows. - [x] Improve NPC silhouette and materials: per-role clothing palette, caps/hoods/backpacks, sleeves and hands, walking leg swing. - [x] Cover materials/environment/avatar helpers with headless tests (`test_materials.gd`, `test_target_cycle.gd`). ## AI 3D Model Tooling Completed 2026-10-31 (v0.3.0 working tree). `model_pipeline.py build` + `audit` pass for all six props; `test_generated_props.gd` proves they import as scenes and that the world places them. - [x] Add `tools/model_pipeline.py`: prop catalogue, deterministic low-poly GLB builder, OpenRouter concept-image generation, palette sampling and GLB audit. - [x] Add `tools/test_model_pipeline.py` (valid GLB structure, deterministic output, audit failures, sRGB→linear conversion, role assignment). - [x] Generate the prop models into `assets/models/generated/` and import them. - [x] Load the generated props in `WorldBuilder` with a procedural fallback when the GLB is missing. ## Controls, Movement and NPC Selection Completed 2026-10-31 (v0.3.0 working tree). The windowed smoke now covers wheel cycling and right-click clearing; the headless suite passes 177/177. - [x] Record the re-evaluated control model as ADR-0011. - [x] Rank focus candidates by view angle then distance (`NpcFocus.rank_indices`) instead of insertion order. - [x] Add mouse-wheel target cycling next to `Tab`, right-click clearing focus, and a `Shift` precision walk speed. - [x] Show the nearby-candidate count and the cycle hint in the HUD. - [x] Cover ranking, cycling and walk speed with tests, then re-run the windowed smoke. ## Web Export Build Target Completed 2026-10-31 (v0.3.0). Headless **181/181**; `godot --headless --export-release "Web"` produces a 63 MB static bundle and it serves locally with HTTP 200s for html/wasm/pck. - [x] Add a `Web` preset to `export_presets.cfg` using `variant/thread_support=false` so the build runs on plain static hosting without COOP/COEP headers. - [x] Export to `build/web/` and package `build/step-line-web-.zip` from `tools/build_releases.sh` (zip or Python fallback). - [x] Publish the web zip with the desktop artifacts (`tools/publish_release.sh`). - [x] Cover the preset, the non-threaded choice, and the release wiring in `src/test/tests/test_web_export.gd`. - [x] Document the artifact in `docs/RELEASING.md`, the README gallery/status, and `PLAN.md`. ## Prop Texture Atlas Completed 2026-10-31 (v0.3.1). Headless **182/182**, Python tooling **53/53**; screenshots regenerated and `screen_designs.py compare` still reports 6 good / 0 bad. - [x] `model_pipeline.atlas_for()` packs each prop's palette roles into one small power-of-two PNG and a matching metallic/roughness atlas (roughness in G, metallic in B), with nearest sampling so a palette cell stays a flat colour. - [x] `build_glb()` merges every part into a single primitive with per-vertex atlas UVs and one material (`baseColorTexture` + `metallicRoughnessTexture`), so a prop is one draw material instead of one per part. - [x] `audit_glb()` checks the single-material/atlas contract (one material, both textures, TEXCOORD_0 on every primitive, two images). - [x] Tests: atlas determinism and power-of-two sizing, UV range, palette override reaching the atlas, and a Godot test that each imported prop scene uses exactly one material. - [x] Removed the now-unused sRGB→linear factor helpers (colours travel in the sRGB texture). ## Complete AI Mockup Set Completed 2026-10-31 (v0.3.2). All 19 catalogue screens now have an AI reference; Python tooling **53/53**. - [x] Generated mockups for the seven entries that only had wireframes: wand screening, Joke Shop queue, TV Studio arrival, crowd panic, responder arrival, body-camera timeline, escalation HUD. - [x] `generate` loads `.env` like `judge` already did, so `--ai` works without exporting `OPENROUTER_API_KEY` by hand. - [x] Verified every reference is 1280x720, reviewed the new images visually, and confirmed `list` reports 19 designs (12 kept, 7 new). ## Staged Captures For The New Screens Completed 2026-11-01 (v0.3.3). `screen_designs.py compare` reports **9 good / 0 bad**; vision judge: wand **match** (0.90), timeline **match** (1.00), escalation **review**, 0 diff. Python tooling **53/53**, headless **182/182**. - [x] `capture_screenshots.gd` now continues past the night set: switches to The Joke Shop, stages a guest, retries the wand until it alerts (falls back to the documented keys false alarm), applies formal warning + blocking, and opens the body-camera timeline. - [x] New captures `07-wand-screening.png`, `08-escalation-hud.png`, `09-body-camera-timeline.png` added to `docs/screenshots/` and to the README gallery. - [x] Wired `ref` for `wand_screening`, `escalation_hud` and `body_camera_timeline` so `compare` and `judge` include them (9 of 19 screens now have a staged capture). - [x] Regenerated the escalation HUD mockup as a third-person over-the-shoulder night shot so the reference matches the game camera. - [x] Added `Hud.clear_log()` so staged captures drop target-cycling noise; the escalation shot keeps the real reaction lines. ## Prop Collision And Placement Completed 2026-11-01 (v0.4.0). Headless **187/187**, Python tooling **53/53**, the new `smoke_props.gd` passes windowed, and `screen_designs.py compare` still reports 9 good / 0 bad. - [x] Every generated prop now gets a fitted static collider: a cylinder for bollard, bin, and hydrant, a box for everything else. The collider is computed from the model's mesh bounds in holder-local space, so placement rotation and variant scale carry over. - [x] Added `smoke_props.gd` (windowed): the guard walks into a bench and stops instead of passing through it; a guest heading for a point beyond the same bench routes around it with navigation avoidance and never ends up inside the collider. - [x] Props publish a `NavigationObstacle3D` footprint, and `NpcAvatar` enables agent avoidance with a `velocity_computed` callback. The navmesh stays a flat walkable plane; avoidance does the steering around street furniture. - [x] Replaced the hard-coded prop calls with `WorldBuilder.PROP_PLACEMENTS`, grouped by entrance, mid-block, and curb zones, with a static `placement_problems()` validator for bounds, overlap/min-spacing, duplicates, and the door/steps/guest-lane exclusions. - [x] Deterministic variants per placement (`variant_for`): yaw jitter, subtle scale, and optional procedural clutter (bag by the bin, crate by the newspaper boxes, cone by the hydrant), so repeated props stop looking cloned. - [x] Fallback primitive props go through the same bounds fit, so a missing `.glb` cannot silently remove collision; a unit test covers the fallback body, shape, width, and navigation obstacle. - [x] Tests: placement validation and its failure modes, variant determinism/range, colliders on every placed prop (including obstacle presence), and the fallback collider path. ## Prop Pipeline Checks Completed 2026-11-01. Python tooling **56/56**, headless **188/188**, prop gallery **match** (0.90) in the vision judge, `compare` **10 good / 0 bad**. - [x] `audit_glb()` now decodes positions/UVs/indices and enforces the art budget (`MAX_TRIANGLES = 400`, `MAX_VERTICES = 600`), rejects degenerate triangles, UVs outside `[0, 1]`, a prop that floats above or sinks below the ground (`min y ~= 0`), and the single-material/atlas contract. The newspaper box sat 3 cm high; its catalogue parts moved down so it rests on y = 0. - [x] Added `capture_prop_gallery.gd`: renders all six props on a studio backdrop (two rows on a display platform) to `docs/screenshots/10-prop-gallery.png`, with a `prop_gallery` catalogue entry and AI mockup, so `compare`/`judge` cover close-range prop art. - [x] Confirmed instances share the imported atlas material (headless test) and documented the triangle/vertex budget in the pipeline docstring. - [x] README lists the gallery capture command and image; the pipeline docstring and audit docs describe the budget checks.