Files
security-game/MEMORY.md
T

16 KiB
Raw Blame History

MEMORY

Session memory for durable decisions, gotchas, and commands. Implementation history lives in DONE.md, user-facing changes in CHANGELOG.md, the live task list in TODO.md, and the canonical implemented-features list in docs/development-status.md.

Decisions

  • Local-only reference override (2026-11-03, owner): development-only photo research ignores the licence gate. Downloads live in tmp/research/ (gitignored): DDG image search, street-view frames, neighbouring buildings — never committed, never shipped. Promotion into docs/screens/refs/ always passes the sidecar licence + privacy gate and CREDITS.md.
  • AI-assisted asset review loop (2026-11-04): head/face shape decisions use a concept + judge loop — generate a target concept (generate_textures.generate_image, Seedream) and use tools/describe_image.py as the vision judge, iterating until the judge's specific fixes converge. Review pages: docs/screens/head-review/README.md, docs/3d-model-review.md §AI-driven head rework.
  • Project agent skills (2026-11-03): two tracked skills live in .agents/skills/: gig-line-blender-asset-contract and gig-line-release-qa. They are guidance only, add no runtime dependency and are referenced from tools/README.md. The third-party blender-skills pack remains gitignored.
  • TV Studio shares the Hollywood location (owner): the Late Night TV Studio is the same El Capitan Entertainment Centre block at 6840 Hollywood Blvd (which houses Jimmy Kimmel Live!), restaged for a late-night arrival. VENUE_SPECS["tv_studio"] is a 30.0 m variant of the 29.6 m Hollywood facade. One reference board serves both venues; never create a separate TV Studio board.
  • Jimmy Kimmel Live = starting point: the Hollywood first shift and the TV Studio arrival both begin at the El Capitan block. Stated in docs/venues.md#hollywood-boulevard and docs/gameplay.md.
  • Street-view limitation: Google Street View panoramas need a Google API key or manual browser capture, so the local reference sets use DDG street-level photos instead.
  • Image-recitation filter: Gemini image models reject well-known branded facades (Comedy Store wall) as generation anchors (IMAGE_RECITATION). Workaround: anchor on a less-branded frame of the same block, or generate text-only when the reference adds no geometry.
  • OpenRouter image-to-image: /v1/images is text-to-image only; the working path is POST /v1/chat/completions with a content array carrying a base64 image_url. Response images arrive as message.images[].image_url.url (fallback: a data URI inside the message text). Implemented in generate_textures.generate_image(reference_path=...) and scene_concepts.py generate/anchor; provenance sidecars are .refgen.json.
  • Facade/venue material picks (2026-11-03): the Masonic/El Capitan facade uses the photo-anchored limestone_ashlar field (warm tint Color(0.85, 0.81, 0.74)) with stone_steps for trim; the Joke Shop uses dark_stucco; the marquee canopy/sign box stay black_metal by design (fictional "GIG LINE" marquee, not the red El Capitan Theatre marquee next door). The TV Studio reuses masonic_facade. Committed Comedy Store refs were removed in favour of the local-only tmp/research/comedy-store/ set; the three-up board's photo panel is a historical public-domain frame.
  • Godot 4.7.2 --import gotcha: with a stale .godot/ cache, new image files never import (no .import created) and preload() of the new texture fails every dependent script with the misleading Could not resolve external class member "spec". Fix: import in a scratch copy with .godot/ removed, then copy back the .png.import and .godot/imported/*.ctex/.md5.
  • Blender skills pack (local aid): MIT blender-skills at .agents/skills/blender-skills/ (94 skills + shared references/), gitignored, kept out of Godot's scanner by .agents/.gdignore. Keep the pack flat when updating (cp -a <pack>/.claude/skills/. ...). Pi gates project .agents/skills behind project trust.
  • Gaussian splatting (unversioned): offline/development-only until an ADR proves licence, privacy, Compatibility/Web cost and mesh collision. Accepted photo channels: team captures, permissioned user/volunteer photos, clearly licensed downloads; every set needs a provenance/consent sidecar. No runtime splat dependency in 0.10.0.
  • 0.10.0 balance protocol: docs/release-qa.md#balance-acceptance defines targets/denominators for the 36 venue/difficulty/phase cells, seeds 1–100, and access/emergency error rates. Gotcha: _read_npc_truth() is type-derived, not behaviour-derived; the emergency-fairness review must account for that oracle mismatch.
  • Blender MCP is optional (2026-11-04): setup and safe part workflow are in docs/asset-tooling.md#optional-interactive-blender-mcp. The headless flatpak scripts remain authoritative; MCP is never a build/CI/test/runtime dependency, and anything MCP touched must pass the CLI audit/fit gates.
  • Character mesh cap (2026-11-05): all characters use 24,000 tris / 24,000 glTF vertices (CHAR_MAX_TRIS/CHAR_MAX_VERTS in tools/blender_assets.py; npc_pipeline.MAX_TRIANGLES/ MAX_VERTICES). The required 13 core bones stay, optional extra bones are allowed, and the low-poly npc_base fallback remains the shipped fallback. The raise funds the Quaternius base bake and the 12-variant clothing detail. Measure the 24-NPC/Web budget after any geometry change; never silently lower the cap without a new decision.
  • Quaternius is the NPC base (2026-11-05, owner): ADR-0017 supersedes the generator-first sourcing decision. The CC0 Quaternius Ultimate Modular character pack is the primary NPC mesh and animation source. Stage 1 bakes a proportion-warped, 13-core-contract npc_base so NpcAvatar, gear and animations keep working; the 62-joint/24-clip source rig is authoring input, not a runtime dependency. If the warp fails the silhouette gate, Stage 2 needs a new ADR for a 62-joint runtime, not a hybrid. The MakeHuman head and procedural npc_base remain tested fallbacks until the Quaternius rollout ships. Quaternius source: CC0, attribution in CREDITS.md; the three researched GLBs and licence notes are kept in tmp/npc-research/quaternius/. MPFB/MakeHuman body work is closed; do not add new work there.
  • Engine baseline: Godot 4.7.2 stable (official editor ~/.local/bin/godot). The system's godot-runner cannot load arbitrary project paths (compiled without path overrides); use the official editor binary.
  • GDScript/architecture: cross-file references use explicit preload() constants (not class_name) so scripts compile in headless --script runs; the test runner is a custom minimal harness (not GUT, which needs the editor plugin); no autoload signal bus — ShiftCoordinator owns the EventBus instance passed to subsystems.
  • Skinned NPCs (v0.6.0): NpcAvatar._build_skinned_body() loads npc_base.glb; procedural _build_body() is the fallback. Bone pose rotation is the absolute local orientation, not an offset — compose with get_bone_rest(i).basis or the body snaps to a broken pose. Five tint slots via SurfaceMaterials.npc_slot(); keep _rig.scale uniform on the skinned path.
  • NPC facing: the avatar's visual front is +Z; Godot look_at() aims -Z (made NPCs walk backwards). Use NpcAvatar.yaw_towards(dir) and set rotation.y; never look_at() on avatars. Head-turn offsets use +basis.z for the same reason.
  • Release blockers (still open): macOS unsigned, AI image provider terms/commercial rights not cleared, storefront/rating approval pending, external playtests and full hardware matrix pending. Recorded in CREDITS.md, PRIVACY.md, docs/accessibility.md, docs/known-limitations.md.

Gotchas / Known Issues

  • Two character builders — do not confuse them. tools/blender_assets.py is the real builder for the eight library characters, 18 gear pieces and props (it has the 0.9.4 face/hand sculpting in _face()/_hands_and_shoes()); tools/npc_pipeline.py builds only assets/models/generated/npc_base.glb, the frozen plain-primitive fallback. Reading npc_pipeline.py to judge the library's face/hand detail is wrong.
  • Pose capture fixed (2026-11-04): the harness was missing set_physics_process(false)/ set_process(false) on the avatar, so the FSM (_advance_fsm + _move_toward_target) ran every tick, reset arrived/velocity and made theater_pose() revert to idle/rest. Disabling both fixes it (sit vs idle diff 5.63, walk vs idle 2.29). The earlier "moving the Rig child does not render" probe was a red herring — it set the position inside _shoot() in the same frame as root.get_texture().get_image(), so it never rendered a frame. Lesson: pose/portrait harnesses must freeze the avatar's process/physics before driving _animate().
  • Capture/pose harness gotchas: scripts that read captured-mouse input must run windowed (smoke_props.gd); the NPC FSM rewrites rotation.y every physics tick, so pose/portrait captures set the yaw after set_physics_process(false); visible=false does not keep the crowd out of a portrait frame (park the rest of the crowd away instead).
  • Blender: bisect_plane and thin bevels leave zero-area triangles (Godot "non-finite normal in LOD generation"); _clean() + a weld after bisect fixes them and the audit rejects them.
  • Image models have per-model size floors/caps (Seedream needs 1920+; GEN_SIZE=2048, do not lower; gpt-5-image-mini caps lower). generate_image applies model-specific sizing; concept generation defaults to --request-size 1536.
  • Local photo research tooling: DDG rate-limits by IP (HTTP 202) — the refcollect.py token bucket (0.5 req/s, burst 3) plus retry/backoff handles it; some DDG frames are AVIF mislabeled .png/.jpg (convert with ffmpeg; refcollect.py sheet skips unreadable). Commons API needs a descriptive User-Agent and rate-limits thumbnail downloads (fetch all metadata in one titles=A|B|C query).
  • Frozen decisions: do not re-open override_access; do not bind wand to W; do not remove the radio easter egg.
  • GDScript: no static signals; lambdas capture locals by value (mutating a captured local does not affect the outer variable — use member functions for accumulators).
  • Reference cycles: FSM holds its owner via WeakRef, ShiftCoordinator is RefCounted; if a new subsystem introduces a cycle, repeat the WeakRef pattern.
  • Headless (dummy renderer) logs Parameter m is null from mesh_get_surface_count — benign, does not occur with a real GL driver.
  • macOS export requires rendering/textures/vram_compression/import_etc2_astc=true (universal build demands ETC2 ASTC).
  • Typed spawn signal: handlers must accept the 4th event: Dictionary arg or Godot will not call them.
  • GDScript inference trap: a var typed as a base class (radial: CanvasLayer) exposes script members as Variant; type it as the script type (ActionRadial). --check-only --script cannot see the Save autoload — use a real boot (--quit-after 180) to verify full-scene compile.
  • Do not snapshot right after changing a transform in the same frame — capture_screenshots.gd needs a state/frame gap or the capture uses the old camera orientation.
  • EventScheduler probabilities are per simulated second, not per call; never poll it per frame.
  • Light falloff: omni_attenuation = 0.0 is constant, not physical, falloff in Godot 4.7 — all local lights use 1.0.
  • glTF baseColorFactor is LINEAR — writing sRGB makes props pastel; srgb_to_linear() is applied in build_glb. A new/changed .glb needs godot --headless --import before load() sees it.
  • Rebuilding a character GLB also requires re-baking its AO (tools/bake_ao.py -- --only <name>). blender_assets.py build writes the GLB only; the old <name>_ao.png then maps onto the new geometry and produces large dark cheek/jaw blotches. Bake the AO, re-import, and refresh the stale .godot cache (scratch-copy import) before judging a face/body change.
  • Save.SETTINGS_FILE is declared and unused; settings persist through load_settings()/save_settings().
  • Crowd reactions emit event_bus.crowd_changed; GameManager._connect_crowd() must reconnect after every coordinator.setup() (same class of bug as the radio-dispatch reconnect).
  • Packaging: NSIS uses -D defines (never /D) and resolves relative defines against the script directory (pass $PWD-absolute paths); installed exe must be space-free; ICO must be classic BMP entries (PIL emits PNG-compressed ICOs NSIS can't load). appimagetool runs system appstreamcli — pass --pedantic.
  • Credentials: ~/.git-credentials holds the Gitea creds; .env holds only OPENROUTER_API_KEY.
  • assets/textures/game_ready/ is not imported (no .import) — use the imported assets/textures/skyline.png path.
  • Godot 4.7 writes *.gd.uid files — kept out of git status via .git/info/exclude.
  • Collaborators occasionally leave uncommitted work in the shared tree; check git status before committing and leave foreign changes untouched.

Commands

  • Tests: godot --headless --path . --script res://src/test/test_runner.gd
  • Boot check: godot --headless --path . --quit-after 120
  • Single screenshot: godot --path . -s res://src/test/take_screenshot.gd
  • Staged gallery: tools/check_screenshots.sh (re-run the whole script, never a partial capture)
  • Blender build: flatpak run org.blender.Blender --background --python tools/npc_pipeline.py -- build (flatpak cwd is /app/blender and cannot read /tmp — use absolute repo paths; .gdignore markers keep Godot from importing .blend/tmp)
  • Textures: python3 tools/generate_textures.py (then godot --headless --import); AI mode python3 tools/generate_textures.py --ai; general pipeline python3 tools/asset_pipeline.py
  • Character checks: python3 tools/blender_assets.py inspect|audit|fit, python3 tools/npc_proportions.py
  • Docs: python3 tools/check_docs.py (every tracked image must be linked from its folder README)
  • Head/part pipeline: python3 tools/npc_parts.py build|extract|assemble|inspect|sheet|compare (Blender subcommands run under flatpak). Head sources: build_character(head="face"|"proto"|"mh").
  • Reference research: python3 tools/refcollect.py search <q> --slug <block> --limit N (local-only), tools/refboard.py (Commons), tools/three_up.py compose/rows, tools/scene_concepts.py generate/anchor/realism

Open Questions

  • Whether the responder avatar should be playable/controllable or purely scripted.
  • Localization target languages beyond English.
  • macOS notarization approach for broad distribution.

Current State

The playable build covers the three venues, tutorial/campaign/checkpoints, the 0.9.3–0.9.6 polish/texture/integration passes and the Blender + realism asset pipeline (see DONE.md and CHANGELOG.md). The 12-character library (realistic face + four crowd variants) and the Comedy Store map rebuild are complete. Priority (2026-11-05, owner): 1) the Quaternius NPC base migration (bake the CC0 Quaternius character to the runtime contract), 2) the 3D model and map improvements, 3) ship it all together in the 0.10.0 minor. The 0.9.7/0.9.8 character patches are published; TODO.md is the priority source. The active implementation is the Quaternius base migration (ADR-0017); planning/docs were rewritten and tmp/ cleaned on 2026-11-05, but no character GLB has been rebuilt yet. Test counts as of 2026-11-05: Python 167/167 (last run), Godot 277/277 (last run before the docs-only pivot), check_docs OK.