19 KiB
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 intodocs/screens/refs/always passes the sidecar licence + privacy gate andCREDITS.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 usetools/describe_image.pyas the vision judge, iterating on the mesh until the judge's specific fixes converge. This is what picked the MakeHuman head over the procedural displacement head (_head_protooscillates between "decorated egg" and "spiky wedges" at ~3/5 on the coarse sphere). Review page:docs/screens/head-review/README.md; methodology note: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-contractandgig-line-release-qa. They are guidance only, add no runtime dependency and are referenced fromtools/README.md. The third-partyblender-skillspack 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-boulevardanddocs/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/imagesis text-to-image only; the working path isPOST /v1/chat/completionswith acontentarray carrying a base64image_url. Response images arrive asmessage.images[].image_url.url(fallback: a data URI inside the message text). Implemented ingenerate_textures.generate_image(reference_path=...)andscene_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_ashlarfield (warm tintColor(0.85, 0.81, 0.74)) withstone_stepsfor trim; the Joke Shop usesdark_stucco; the marquee canopy/sign box stayblack_metalby design (fictional "GIG LINE" marquee, not the red El Capitan Theatre marquee next door). The TV Studio reusesmasonic_facade. Committed Comedy Store refs were removed in favour of the local-onlytmp/research/comedy-store/set; the three-up board's photo panel is a historical public-domain frame. - Godot 4.7.2
--importgotcha: with a stale.godot/cache, new image files never import (no.importcreated) andpreload()of the new texture fails every dependent script with the misleadingCould not resolve external class member "spec". Fix: import in a scratch copy with.godot/removed, then copy back the.png.importand.godot/imported/*.ctex/.md5. - Blender skills pack (local aid): MIT
blender-skillsat.agents/skills/blender-skills/(94 skills + sharedreferences/), 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/skillsbehind 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-acceptancedefines 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_VERTSintools/blender_assets.py;npc_pipeline.MAX_TRIANGLES/MAX_VERTICES). The required 13 core bones stay, optional extra bones are allowed, and the low-polynpc_basefallback 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_basesoNpcAvatar, 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 proceduralnpc_baseremain tested fallbacks until the Quaternius rollout ships. Quaternius source: CC0, attribution inCREDITS.md; the three researched GLBs and licence notes are kept intmp/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'sgodot-runnercannot load arbitrary project paths (compiled without path overrides); use the official editor binary. - GDScript/architecture: cross-file references use explicit
preload()constants (notclass_name) so scripts compile in headless--scriptruns; the test runner is a custom minimal harness (not GUT, which needs the editor plugin); no autoload signal bus —ShiftCoordinatorowns theEventBusinstance passed to subsystems. - Skinned NPCs (v0.6.0):
NpcAvatar._build_skinned_body()loadsnpc_base.glb; procedural_build_body()is the fallback. Bone pose rotation is the absolute local orientation, not an offset — compose withget_bone_rest(i).basisor the body snaps to a broken pose. Five tint slots viaSurfaceMaterials.npc_slot(); keep_rig.scaleuniform on the skinned path. - NPC facing: the avatar's visual front is +Z; Godot
look_at()aims -Z (made NPCs walk backwards). UseNpcAvatar.yaw_towards(dir)and setrotation.y; neverlook_at()on avatars. Head-turn offsets use+basis.zfor 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.pyis 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.pybuilds onlyassets/models/generated/npc_base.glb, the frozen plain-primitive fallback. Readingnpc_pipeline.pyto 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, resetarrived/velocity and madetheater_pose()revert to idle/rest. Disabling both fixes it (sit vs idle diff 5.63, walk vs idle 2.29). The earlier "moving theRigchild does not render" probe was a red herring — it set the position inside_shoot()in the same frame asroot.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 rewritesrotation.yevery physics tick, so pose/portrait captures set the yaw afterset_physics_process(false);visible=falsedoes not keep the crowd out of a portrait frame (park the rest of the crowd away instead). - Blender:
bisect_planeand 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_imageapplies model-specific sizing; concept generation defaults to--request-size 1536. - Local photo research tooling: DDG rate-limits by IP (HTTP 202) — the
refcollect.pytoken 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 sheetskips unreadable). Commons API needs a descriptive User-Agent and rate-limits thumbnail downloads (fetch all metadata in onetitles=A|B|Cquery). - Frozen decisions: do not re-open
override_access; do not bind wand toW; 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:
FSMholds its owner viaWeakRef,ShiftCoordinatorisRefCounted; if a new subsystem introduces a cycle, repeat the WeakRef pattern. - Headless (dummy renderer) logs
Parameter m is nullfrommesh_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: Dictionaryarg or Godot will not call them. - GDScript inference trap: a
vartyped as a base class (radial: CanvasLayer) exposes script members asVariant; type it as the script type (ActionRadial).--check-only --scriptcannot see theSaveautoload — 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.gdneeds a state/frame gap or the capture uses the old camera orientation. EventSchedulerprobabilities are per simulated second, not per call; never poll it per frame.- Light falloff:
omni_attenuation = 0.0is constant, not physical, falloff in Godot 4.7 — all local lights use1.0. - glTF
baseColorFactoris LINEAR — writing sRGB makes props pastel;srgb_to_linear()is applied inbuild_glb. A new/changed.glbneedsgodot --headless --importbeforeload()sees it. - Rebuilding a character GLB also requires re-baking its AO (
tools/bake_ao.py -- --only <name>).blender_assets.py buildwrites the GLB only; the old<name>_ao.pngthen maps onto the new geometry and produces large dark cheek/jaw blotches. Bake the AO, re-import, and refresh the stale.godotcache (scratch-copy import) before judging a face/body change. Save.SETTINGS_FILEis declared and unused; settings persist throughload_settings()/save_settings().- Crowd reactions emit
event_bus.crowd_changed;GameManager._connect_crowd()must reconnect after everycoordinator.setup()(same class of bug as the radio-dispatch reconnect). - Packaging: NSIS uses
-Ddefines (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 systemappstreamcli— pass--pedantic. - Credentials:
~/.git-credentialsholds the Gitea creds;.envholds onlyOPENROUTER_API_KEY. assets/textures/game_ready/is not imported (no.import) — use the importedassets/textures/skyline.pngpath.- Godot 4.7 writes
*.gd.uidfiles — kept out ofgit statusvia.git/info/exclude. - Collaborators occasionally leave uncommitted work in the shared tree; check
git statusbefore 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/blenderand cannot read/tmp— use absolute repo paths;.gdignoremarkers keep Godot from importing.blend/tmp) - Textures:
python3 tools/generate_textures.py(thengodot --headless --import); AI modepython3 tools/generate_textures.py --ai; general pipelinepython3 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.
Handoff
2026-11-05 — NPC plan pivot and tmp cleanup
What changed: the NPC character direction was rewritten around the researched CC0
Quaternius character. ADR-0017 supersedes the generator-first decision in ADR-0015. PLAN.md,
TODO.md, MEMORY.md, README.md, CREDITS.md, the architecture/asset/review docs and the
docs index now point to the Quaternius base. The old MPFB/MakeHuman body tracks were closed and
their plans removed from TODO.md; the MakeHuman head and procedural npc_base remain tested
fallbacks only.
Cleanup: tmp/ was reduced from ~2.9 GB to 4.7 MB. Removed the MPFB/MakeHuman scripts,
review renders, map scratch output and logs. Kept only the three researched Quaternius GLBs,
the Quaternius mirror HTML/README/licence, prototype_quaternius.py, and a local
tmp/npc-research/quaternius/README.md with hashes and re-download links. Do not delete that
folder while the migration is active.
Implementation status: planning and documentation only. No character GLB, code, manifest, AO map or test was changed. The 12 shipped NPC GLBs still use the old procedural bodies.
Next task (start here): TODO.md → Quaternius NPC base migration → §1 Intake and
canonical base. First concrete step: inventory the four meshes, 62 joints, 24 clips and
material names in each of the three GLBs, then build a welded, grounded, animation-free
quaternius_base.glb at 1.81 m with front on Blender −Y.
Main risk: the source is a 62-joint T-pose with realistic proportions (research measured ~0.96 m hip, ~1.68 m arm span, ~0.153 m shoulder offset), while the runtime contract is an arm-down 13-core rig with a 0.62 m hip and 0.27 m wrist targets. The Stage 1 proportion warp is the make-or-break step. Judge the Quaternius result directly; do not reuse the old MPFB arm warp. If the warp fails the silhouette/gear gate, stop and write the Stage 2 62-joint ADR.
Key paths:
- Source GLBs:
tmp/npc-research/quaternius/{Casual,Casual_Hoodie,Worker}.glb - Source hashes and inventory:
docs/npc-model-sourcing.md - Decision:
docs/adr/0017-quaternius-npc-base.md - Contract:
docs/architecture.md#character-and-gear-asset-contract - Runtime selector/fallback:
src/game/npc_avatar.gd,assets/models/blender/ - Current library builder/fallback:
tools/blender_assets.py,tools/npc_pipeline.py,assets/models/generated/npc_base.glb
Useful checks: python3 tools/check_docs.py; pytest -q tools/tests;
python3 tools/blender_assets.py audit|fit; Blender headless:
flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- inspect --category npc --file "$PWD/<file>.glb".
This session ran check_docs and git diff --check after the doc rewrite; Python tests were
last green at 167/167. Godot tests were not rerun because no runtime file changed.
Commit: 2c90743 Rewrite NPC plans around the Quaternius base. Working tree clean.
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.