Files
security-game/MEMORY.md
T
zwitschi 140a7ada0f
Full build / Import + headless tests (push) Successful in 48s
Full build / Full desktop build + optional release (push) Skipped
Plan the 0.9.9-1.0.0 roadmap and re-evaluate the NPC base
Move the gap analysis and risks into PLAN.md, version the character track
(0.9.9 rig correction, 0.9.10 roster) and split 0.9.11-0.9.12 polish and
verification out of the old 0.10.0 milestone. Add roadmaps to 0.10.0, 0.11.0
and 1.0.0, record the Quaternius face-fidelity gate and the rest-relative
pose finding in MEMORY.md, and move the completed 0.9.7 and pose-harness
sections to DONE.md.
2026-11-06 09:40:28 +01:00

20 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.

  • Quaternius bake keeps source detail (2026-11-05, owner): during the contract bake (§2), keep the Quaternius mesh as close to source as possible — no unnecessary decimation or LOD reduction. The canonical base is only 3,199 verts / 6,206 tris, far under the 24,000 cap, so there is no budget reason to decimate before the silhouette gate. Do not reuse the old 7,000-triangle or MPFB decimation targets.

  • Quaternius runtime rig (2026-11-05, owner, ADR-0018 option 2): the Stage 1 proportion warp failed the silhouette gate (arm ×1.63 / leg ×0.74 / torso ×1.61; vision judge: paddle hands, lost forearms, stretched shins). The owner chose to adopt the Quaternius 62-joint rig at runtime instead of warping: NpcAvatar maps the Quaternius joint names, gear attach points are re-derived from the Quaternius skeleton (hip ≈0.87, shoulder ≈0.148/1.39, wrist ≈0.99, knee ≈0.52), the mesh keeps source LOD (arm-down rest pose baked), and Idle/Walk play directly on the 62-joint rig (no retarget).

  • 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.

  • Release roadmap (2026-11-05, owner): 0.9.9 Quaternius pilot base (rig/export fix, rest-relative pose math, owner face/silhouette gate), 0.9.10 12-role roster, 0.9.11 scheduled venue/prop/lighting polish, 0.9.12 performance/Web/accessibility verification, 0.10.0 public-readiness minor, then 0.10.1–0.10.3 fixes, 0.11.0 replay + fourth venue + localization foundation, 0.12.0 signed/storefront builds + save v2, 0.13.x content complete and release candidates, 1.0.0 stable. The full gap analysis and roadmap live in PLAN.md; the character track is versioned now, so a blocked rig cannot block the release. Planning only — none of these versions is built yet.

  • Quaternius face/readability gate (2026-11-05): the base is 6.2k tris total and replaces a library with a sculpted realistic face, so the 0.9.9 gate is a product decision, not just an art check. Owner picks full swap / crowd-only / extra head detail. Do not lower character fidelity silently.

  • Pose composition is rest-relative (2026-11-05, hypothesis to verify at 0.9.9): Godot's set_bone_pose_rotation offsets from the bone rest, so rest * Quaternion(axis, angle) only equals the target when the rest is ±identity (the old 180° rig). The candidate fix is rest.inverse() * target, proven with measured global bone transforms.

  • One character source, one fallback: no new NPC source without an ADR. MPFB body work stays closed, the Stage-1 proportion warp is not reopened, and the procedural npc_base + MakeHuman head stay tested until the Quaternius base passes its gate.

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.
  • Quaternius arm-down bake: pose-rotate UpperArm.L/R −90° local X → apply Armature modifier → armature_apply → re-add modifier bakes the rest, but export_apply=True writes wrong inverse bind matrices (Godot renders A-pose even though the rest is arm-down). The runtime _pose_bone rest * Quaternion(axis, angle) only cancels when rest² = identity (old rig's 180° rest); the Quaternius ~76° rest does not, so the pose formula must be re-derived for a non-180° rest. Godot's bone pose rotation is relative to the bone rest, not absolute: treat rest.inverse() * target as the candidate and verify with measured global bone transforms.
  • 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.
  • Quaternius scope: full 12-role swap, crowd-only, or extra head detail (owner decision at 0.9.9).
  • Localization target languages beyond English (0.11.0/0.12.0).
  • macOS notarization approach for broad distribution (0.12.0).
  • Fourth-venue choice for 0.11.0 (candidate: TCL Chinese Theatre forecourt).

Current State

v0.9.8 is published. The build covers the three venues, the shift loop, tutorial/campaign/ checkpoints, the 0.9.3–0.9.8 polish/texture/integration/character passes and the Blender + realism asset pipeline (see DONE.md and CHANGELOG.md). The 12-character procedural library (realistic face + four crowd variants) and the Comedy Store map rebuild are complete.

Planning pivot (2026-11-05, owner): the character and map work was re-sequenced into patches (0.9.9–0.9.12) with 0.10.0 as the public-readiness minor. The gap analysis and the three release roadmaps now live in PLAN.md; TODO.md is the executable task list. ADR-0017 Stage 1 (proportion warp) is dead; ADR-0018 option 2 (62-joint runtime) is accepted. The quaternius_base.glb, joint map, slot map and NpcAvatar name mapping exist, but the arm-down skin export and the pose composition are still wrong — no Quaternius character has rendered correctly in Godot yet, and no character GLB has been rebuilt since the pivot.

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.