Files
security-game/TODO.md
T

94 KiB
Raw Blame History

TODO

Work is grouped by theme. Each item should be small enough to complete and verify on its own; see AGENTS.md for the workflow and MEMORY.md for the decisions behind these plans.

Priority order (2026-11-04, owner): 1) Blender NPC model overhaul — head, face and hands with a local part pipeline; 2) finish the MakeHuman character + the 3D model and map improvements; 3) ship it together in the 0.10.0 minor. The 0.9.7/0.9.8 character patches are published. See AGENTS.md §Priority and Versioning and §Local Blender skills.

The MIT Blender skill pack lives locally at .agents/skills/blender-skills/ (gitignored). Every 3D workstream below names the skills to load; they are guidance only, and the project contract, blender_assets.py audit/fit/inspect and the capture gates stay authoritative.

Blender NPC model overhaul (top priority)

Goal: replace the sphere-and-primitive face and hand construction with a local, part-based development pipeline that can build, inspect and compare the head/face and hands separately before assembling them into the shipped character. Prioritise face readability at portrait distance, then hand silhouette and gear contact. Preserve the npc_base contract, the 13 core-bone rig (optional extra bones allowed), five tint slots, 16,000/16,000 character cap, optional extra bones beyond the required 13, and the Compatibility/Web path.

Skill route: character-artist, retopology, sculpting, rigging, uv-workflow, texture-workflow, materials, lookdev, asset-optimization, qa-review.

Current state: assets/models/npc/ contains the tracked frozen npc_base.blend and its gitignored .blend1; the 12 production characters are assembled procedurally by tools/blender_assets.py from _shape_head_mesh, _face, _hair and _hands_and_shoes. The MakeHuman head is now a second head source (build_character(head="mh"), build --head mh, npc_parts.py assemble --head mh) and all 12 variants assemble inside the 16,000-triangle cap with it. The procedural body, hands and clothing remain unchanged. The MakeHuman body prototype and its temporary head/hand swap live under tmp/npc-research/ and are not the shipped path. This overhaul supplies the reusable head/face and hand parts that both the procedural characters and the MakeHuman body can consume.

This workstream supersedes the face/head and hands items in §C until the prototypes are approved; the rest of §C remains the broad review checklist.

1. Baseline and acceptance

  • Inventory the real starting point: the frozen assets/models/npc/npc_base.blend, the 12 generated character GLBs/AO maps, the current _face/_hands_and_shoes builders and the temporary MakeHuman head/hand swap. Recorded tris/verts, head subdivision, material slots, UV layout and the exact locations of eye/mouth/finger geometry in docs/3d-model-review.md §Baseline inventory.
  • Define measurable acceptance: face front/three-quarter/profile at 1.5 m and 1.85 m; hands open and gripping at 0.8 m; silhouette, eye/mouth readability, ear/nose/jaw planes, finger separation and gear contact. Keep the 16,000/16,000 cap; optional extra bones are allowed, but the required 13 core bones and rest positions must not move. Recorded in docs/3d-model-review.md §Overhaul acceptance criteria.
  • Confirm the identity boundary: npc_base.blend is the frozen fallback and must not absorb prototype detail unless a separate contract decision approves it. Recorded in docs/3d-model-review.md §Baseline inventory and docs/blender-assets.md §Frozen fallback boundary.
  • Choose a local working-set layout: tmp/npc-parts/<variant>/ (gitignored) for part .blend and preview GLBs; only the assembled character GLBs, AO/maps, manifest and review sheets are committed. Do not add a new committed .blend source tree without a docs/ADR decision. Recorded in docs/3d-model-review.md §Working-set layout and docs/asset-tooling.md §Recommended part-workflow.

2. Local part pipeline

  • Add tools/npc_parts.py with a small, documented CLI: extract (pull the current head/hands for comparison), build --part head|face|hand_l|hand_r, assemble --variant <name>, inspect, compare and sheet. It reuses the existing builder helpers (blender_assets.py _face/_hands_and_shoes/_shape_head_mesh); no second face/hand implementation. Verified end-to-end: built head/face/hand_l/hand_r, extracted hand_l from the shipped GLB, assembled npc_casual (contract inspect: no issues).
  • Keep the part pipeline local-only and headless: flatpak Blender for authoring, stdlib/Pillow for inspection and sheets. No Blender MCP dependency, no new runtime dependency, no CI Blender step.
  • Add tests for deterministic part assembly, contract output (mesh root, bones, slots, budgets) and the non-Blender failure path. tools/tests/test_npc_parts.py covers the part-name set, the clean Blender-required error path, sheet/compare error paths and a real-PNG sheet. The assembled prototype passes inspect (npc_base, 14 bones, skinned, no issues).
  • Consider optional Blender MCP for interactive part exploration and document setup/safety. (docs/asset-tooling.md#optional-interactive-blender-mcp covers addon/client setup, Pi pi mcp add, flatpak networking, viewport/bpy workflow and the rule that headless audit/fit remains authoritative; MCP is not a build/CI/runtime dependency.)
  • Document the commands, working-set paths and "part source is not a shipped asset" rule in docs/asset-tooling.md, docs/blender-assets.md and .agents/skills/gig-line-blender-asset-contract/references/verification.md.

3. Head and face prototype

  • Build one npc_casual head/face prototype as a connected head shell plus intentional feature geometry — not a sphere with floating facial balls. Use loop-based eye sockets/lids, a readable nose bridge/tip/ala, a mouth with upper/lower lip volumes, ears with concha depth, and brow/cheek/jaw planes that survive the 256² AO bake. Outcome (AI-judged, 2026-11-04): the displacement shell oscillated egg/spiky at ~3/5; the judge picked the decimated MakeHuman head. That head is now built as _makehuman_head (build_character(head="mh"), npc_parts.py build --part head_mh): committed CC0 source assets/models/blender_src/makehuman_head.glb + project sclera/iris/lids/brows/mouth; assembled npc_casual passes inspect (14 bones, no issues). Remaining polish: weld the eye/brow pieces into the head shell and add a skull-conforming hair cap (owner-gated rollout).

  • Compare two or three subdivision/loop variants at 1.85 m front/three-quarter/profile and a 1.5 m portrait. Pick the variant that reads best before changing all 12 characters; keep the rejected variants local.

  • Rebuild a standalone MakeHuman-derived head from the researched CC0 base and save it to assets/models/blender_src/makehuman_head.glb; record provenance/review in docs/makehuman-head-review.md and the preview sheet. Final build: 12,642 tris / 7,690 split verts, bounds x −0.094..0.094, y −0.180..0.042, z 1.530..1.803. The owner accepted the basic human head on 2026-11-04; the automated vision gate still records the eye-region REJECT as deferred polish (globe depth, lid overlap, iris at three-quarter). The assembled path now adds a skull-conforming hair cap, a hidden-scalp trim and a narrowed neck/shoulder body. Assembled npc_casual --head mh: 14,753 tris / 12,822 raw / 12,823 seam-split verts / 14 bones / 9 materials, inspect clean.

  • Build true upper/lower eyelid masses that overlap the globe in front, three-quarter and profile views (not a socket hole), then re-run the vision gate. Deferred: the owner accepted the basic head, so this is polish, not a rollout blocker.

    Compared `head_proto` at seg 24 (670 verts), 48 (1,558 verts) and 64 (2,470 verts) at 1.85 m
    and 1.5 m, front/three-quarter/profile. Vision-judged: **s48 wins** — s24 is too faceted
    (nose/lips melt, jagged silhouette), s64 is visually identical to s48 at both distances
    (extra ~900 verts wasted). Rejected variants stay in `tmp/npc-parts/`.
    
  • [-] Verify head weights stay on the Head bone, headgear clearance still passes fit, and the face does not bake recessed creases into dark AO patches. Weight check done: npc_parts.py assemble --head-proto passes inspect (npc_base, 14 bones, skinned, no issues) and the head join is weighted 100% Head by construction (rigid=lambda p: "Head"). Headgear clearance and the AO no-blotch check are pending the §5 rollout (the fit/bake_ao tools read the committed assets; the ad-hoc AO bake on the imported GLB came out black and is superseded by the proper .blend-source bake). Flag: the prototype ears reach x ±0.162 vs the 0.1575 head-radius reference, so headgear may clip and must be re-measured at rollout.

  • [-] Owner review of the assembled one-variant prototype (npc_casual --head mh) before the 12 shipped GLBs change. The basic human head is accepted (2026-11-04); the review now covers the skull-conforming hair cap and the narrowed neck/shoulder body silhouette. Local renders: tmp/npc-parts/prototype_mh/renders/ (portrait front/three-quarter/profile + full body). Rollout starts only after this review.

4. Hands and grip prototype

  • Build separate left/right hand parts with a connected palm, four multi-segment fingers, a thumb, knuckles and rounded tips. Support an open palm and a relaxed grip; preserve the current HAND_ITEM_FORWARD behaviour for phone/cup/clipboard/camera/flashlight/wand/radio. npc_parts.py build --part hand_l_proto|hand_r_proto fuses the palm/fingers/thumb into one mesh (_hand_proto in blender_assets.py, remove-doubles at 6 mm). Vision-judged at 0.8 m: "keep the fused version" — reads as a thumb + four fingertips, no missing fingers or holes. Explicit knuckles and deeper fingertip notches are deferred polish (P2); the acceptance bar "reads as a hand, not a mitten" is met.
  • Decide finger articulation explicitly: the current rig has no finger bones, so keep the open/relaxed-palm approach unless an ADR approves optional finger bones and the budget/weight path. Decided 2026-11-04: keep the open palm + HAND_ITEM_FORWARD (no finger bones). Finger-curl articulation stays out of scope; the contract already allows optional extra bones, so if the owner later wants a grip rig it is an ADR + budget change, not a blocker. (Recorded in MEMORY.md; re-confirmed here.)
  • [-] Verify all hand gear at 0.8 m (contact, clipping, direction) and capture open/grip hand close-ups for the review sheet. Open close-up captured (0.8 m). Gear contact is unchanged by construction: _hand_proto reuses the same palm geometry and the same HAND_ITEM_FORWARD/Hand-proxy attach, so the existing fit hand-gear clearances carry over. The grip close-up + gear-in-hand context needs the §5 assembly (hand_proto swapped into the character), so it is verified there.
  • Roll the approved hands into all 12 variants only after the one-variant hand sheet passes.

5. Assembly and rollout

MakeHuman 12-variant rollout. - [x] Make the head source selectable from the main builder: blender_assets.py build --head mh --only npc_casual,... and npc_parts.py assemble --variant <name> --head mh --out <stem>. The default stays face until the hair/body review passes.

  • Verify every variant assembles with the MakeHuman head inside the 16,000/16,000 cap (pre-export triangle counts, inspect clean; npc_casual seam-split verts 12,823): npc_casual 14,753, npc_jacket 15,153, npc_coat 15,817, npc_vest 15,579, npc_dress 15,215, npc_hoodie 15,837, npc_firefighter 15,987, npc_guard 15,729, npc_crowd_a 14,947, npc_crowd_b 14,693, npc_crowd_c 15,433, npc_crowd_d 15,833.

  • Recover head budget without touching the accepted face: remove head faces fully covered by the hair cap, keep boundary faces as a safety band, and trim the hidden neck plug to 12 segments. This saves ~950 tris on every variant.

  • Owner approves the hair cap and full-body silhouette on the local npc_casual prototype. Status (2026-11-04): the current body candidate is the MPFB full-body prototype below, not the narrowed procedural body; the 12-variant rebuild is blocked on that review.

  • Rebuild the 12 committed character GLBs with --head mh, in asset-family batches, then regenerate their AO maps and manifest entries. Never run a no-op full-library rebuild.

  • Run blender_assets.py audit, fit, inspect and texels, plus python3 tools/npc_proportions.py report; record the 12-variant proportion table.

  • Re-run the Godot NPC tests and capture the 12 × 6 baseline (src/test/capture_npc_baseline.gd → python3 tools/npc_baseline.py sheet); inspect the sheet for outliers before committing.

  • Update docs/makehuman-head-review.md, docs/3d-model-review.md, docs/blender_previews/README.md, docs/npc-model-sourcing.md, CREDITS.md and docs/development-status.md with the accepted head/hair/body state and final counts.

  • Integrate the approved parts into build_character with deterministic joins, weights, UVs and AO. The final output remains one npc_base skinned mesh under the existing 13-core armature; optional extra bones are allowed but the core rest positions stay fixed; no second rig, no material-slot rename.

  • Keep the MakeHuman body prototype consumable with the same approved head/hands; do not let it grow a second face/hand implementation while the part pipeline is being proven.

  • Rebuild only the changed character GLBs, AO maps and manifest entries; never run a no-op full library rebuild. Commit one asset family per change.

  • Re-run blender_assets.py audit/fit/inspect, npc_proportions.py, the 24-NPC budget, Godot and Python tests, the 48-frame baseline and the 96-frame final-context review.

Full-body MPFB prototype

Goal: assemble the accepted MakeHuman head onto a CC0 MPFB body with project hands, shoes and 14-bone contract rig, inside the 16,000/16,000 character budget. Local development only until the owner reviews the silhouette and a production integration decision is recorded.

  • Build the dev pipeline at tmp/npc-research/build_mpfb_npc.py: create a macro-driven MPFB human, bake targets, add the game_engine rig, capture arm points/weights, remove the rig, apply the helper MASK, scale to 1.81 m, retarget the A-pose arms to the contract wrist (x=±0.27, z=0.78), cut the neck at 1.505, decimate, bisect the clothing boundaries, paint the five slots, add a short neck flare, project hands, rounded shoe shells, cuffs, the accepted head and the 14-bone armature. Current output: 15,317 tris / 13,690 glTF verts / 13,702 seam-split verts / 1.810 m / 14 bones / 10 materials; inspect clean, feet grounded. The torso is inflated radially by 12% through z 0.72–1.40 (tapering out at the collar) so the shirt reads as fabric rather than body paint.
  • Replace the spiky decimated MPFB hands with the project palm/finger primitives, straighten the splayed MPFB feet so the shoes point forward, and use rounded shoe shells + cuffs instead of flat blocks. Use flat slot colours in the prototype because the one-shot MPFB UV unwrap does not match the per-slot NPC atlases.
  • Fix the neck junction without touching the accepted face: cut the decimated neck column radially (all-vertex test inside r=0.085) so the shirt shoulder silhouette stays intact, then bridge the opening with a smooth four-ring lathe neck (r 0.100 → 0.056, 20 segments). The lower rings are painted cloth so the opening reads as a crew neck rather than bare chest. No torus, disk shelf, neck brace or sawtooth collar.
  • Reject destructive vertex smoothing at the neck: a smooth_vert pass moved the open body boundary away from the neck cone, opening a dark hole. The lathe gives smooth shading without moving the collar boundary.
  • Shrink the shoes and seat the ankles: shoe shell 0.12 × 0.30 × 0.11 m, an inset 0.111 × 0.286 m black sole, a tongue and shoe centre x=0.17, with a 0.08 m pant cuff (r 0.055/0.061) bridging leg and shoe. The sleeve cuffs now use the cloth slot and the accessory slot is carried by the sole, so the cuffs read as sleeve ends instead of metal bands.
  • Replace the bulbous toe/heel spheres with a single bevelled shoe shell (bevel 0.032, 2 segments) plus tongue and inset sole, and add one light smoothing pass to the closed thigh/hip region. Protruding flat sole plates read as skates or bricks; laces were dropped for triangle budget after they proved too small to read.
  • Reject the untucked shirt-hem band: both the 0.09 m and 0.12 m cloth cylinders read as a hanging white flap at the crotch. The V hem needs a proper clothing shell, not a band.
  • Fix the neck material rule: the radius < 0.08 skin test had no Z floor, so it painted a skin patch at the crotch. It is now guarded to z > 1.35; the crotch stays pants.
  • Reject three cheap hem fixes: lowering the shirt to z 0.80 turns it into a unitard; tilting the hem up at the front reads as high-cut swimwear; a 13% hem flare reads as a peplum. The remaining V is the dark crotch hollow under a body-painted shirt, not a slot bug.
  • Owner reviews the updated MPFB body renders (tmp/npc-parts/mpfb_proto/renders82/). Remaining known defects: the crotch hollow still reads as a V under the shirt hem, the shirt is only partly loosened by the 12% torso inflation, and the 2k-tri body budget leaves some angular thigh facets. A separate loose shirt shell is the proper fix for the first two; these are polish, not blockers for the body direction.
  • Build a separate loose shirt shell (open hem, shaped to the hips) instead of body-painted cloth. It must fit in the 683 spare triangles or come with a head/body triangle rebalance.
  • Rebalance the triangle budget before rollout: 683 spare is below the ~1,200 the firefighter and hoodie variants need. Protect the head budget first; do not lower the accepted face.
  • Fix the low-budget seams before any rollout: weld the torso/hip/arm components after the bisects, protect the hip/knee loops during decimation (or rebalance head/body triangles).
  • Record the production integration decision: either bake the approved MPFB body to a committed source GLB (CI and fresh checkouts stay MPFB-free) or keep the procedural body as the shipped path. Do not add the MPFB extension as a build/CI dependency.
  • If approved, roll the MPFB body into the 12 variants and regenerate AO maps, manifest entries, inspect/fit/audit/texels, the proportion report and the 12 × 6 baseline.

6. QA, docs and exit

  • Capture front/three-quarter/profile head comparisons and open/grip hand comparisons; add durable sheets to docs/blender_previews/ and link them from the review docs.
  • Update docs/3d-model-review.md, docs/npc-visual-review.md, docs/known-limitations.md, MEMORY.md and the project skills with the accepted limits and the new part workflow.
  • Exit: head/face and hands are approved on one prototype, rolled out across all 12 variants, all contract/budget/capture gates pass, the local part pipeline is documented, and no runtime dependency or skeleton contract changed.

Fix the Rig pose harness

Goal: src/test/capture_npc_pose.gd reports a pose but saves rest frames; the root cause is narrowed to the skinned mesh ignoring its Rig parent transform. Fixing it unblocks the weights/pose column of the 3D review and the pose/portrait evidence for the MakeHuman intake.

Skill route: rigging; evidence in docs/3d-model-review.md §"Pose evidence is blocked".

  • Root cause found (2026-11-04): the harness never disabled the avatar's _physics_process, so _advance_fsm + _move_toward_target ran every tick and reset arrived/velocity, which made theater_pose() return "idle" and reverted the pose to rest. (The earlier "moving the Rig child does not render" probe was a red herring: it set rig.position.y inside _shoot() in the same frame as root.get_texture().get_image(), so it never got a rendered frame.)
  • Harness rebuilt: capture_npc_pose.gd now calls set_physics_process(false)/ set_process(false) after staging the avatar; the pose survives because only the direct _animate() ticks drive it. Verified: sit vs idle diff 5.63, walk vs idle diff 2.29, and the vision judge confirms the seated frame reads as crouched.
  • Captured all poses for all 12 variants (idle / walk_contact / walk_pass / sit × full + shoulder views = 96 frames) and built sheet_pose.jpg/sheet_shoulder.jpg under tmp/. (No separate "wave" theater pose exists; the escalation raise-hand gesture is not a distinct capture in this harness.)

MakeHuman character pipeline

Goal: replace the procedural body generator with the CC0 MakeHuman base for the next character pass — retopologised, rigged and dressed in project-owned clothing — so bodies read more realistic than the current primitive shapes. This promotes the deferred MakeHuman evaluation to an active track; ADR-0015 (CC0 / CC-BY with attribution) still governs every intake.

Skill route: retopology, rigging, uv-workflow, materials, character-artist, asset-optimization, godot-export, qa-review.

Intake and adaptation

  • adapt path added to tools/blender_assets.py (adapt --file <in> --out <out> [--target-tris 16000]): imports the GLB, applies transforms, grounds the feet, welds seam splits and decimates to the budget, then exports. The five tint slots + 13-core rig (optional extra bones allowed) stay a manual step (recorded in the command doc).
  • audit stays the final gate; test_adapt_requires_blender covers the non-Blender error path (162/162 Python).
  • Adapter commands + go/no-go documented in docs/asset-tooling.md §Intake adaptation.

MPFB / MakeHuman body pipeline

  • Prototype the local pipeline: tmp/npc-research/makehuman_intake.py imports the CC0 base.obj, scales to 1.82 m, grounds the feet, triangulates, decimates and exports tmp/npc-research/makehuman/makehuman_body.glb — 3,790 verts / 6,444 tris, bounds x ±0.53 / y 0–1.82 / z −0.12…+0.35, one neutral material, UVs present, unskinned. The body (no rig) is the input for the rigging step; the script + mesh stay in tmp/ (CC0, not committed).
  • Rig strategy decided (owner): retarget the body to the 13-core contract; optional extra bones are allowed. tmp/npc-research/makehuman_retarget.py rotates the A-pose arms down (−38°/+38°), scales the arms 1.63× to the contract 0.70 m length, welds the split vertices, smooth-shades, adds the contract armature + _auto_weights, paints the five slot regions (hair/cloth/pants/accessory/ skin) and exports makehuman_retargeted.glb. Contract passes: 6,444 / 16,000 tris, 7,726 / 16,000 verts, 14 bones (13 + Hips), skinned, arm-down, grounded, five slots, no issues. Vision-reviewed: arms hang down and the silhouette + slot regions read, but the 1.63× arm warp distorts the deltoid/shoulder (spiky/warped), the decimated hands are blocky, and the slot boundaries are rough — these are the refinement steps below.
  • Head/hands/feet swap (makehuman_final.py): cut the decimated MakeHuman head/hands/feet and joined the project's _face/_hair/_hands_and_shoes, then re-weighted/re-UV'd. Contract passes: 6,407 / 16,000 tris, 6,651 / 16,000 verts, 14 bones, skinned, five slots, no issues. Vision-reviewed: the head now reads as a realistic face, the hands as detailed fingers and the shoes as attached. Remaining (manual retopology): a 1.63× arm-scale warp still distorts the deltoid/shoulder, stray hand blobs linger at the hips, shirt/shirt seams are rough, and the head-neck junction has a shadow gap. The procedural generator stays the shipped fallback until these are cleaned in an editor session.

3D model and local map re-evaluation

Goal: re-judge every shipped 3D asset against the newly available Blender skill guidance, rank the real gaps, and fix only what the rankings justify (sections A–G); and re-evaluate the three venues against licensed real-world data — OSM footprints and building massing, reference-derived photoreal facades and the photo-driven geometry pipeline (sections H–M). Evidence comes from three separate sources: licensed real-location photos, generated concept boards, and build captures. This is an unversioned development track: it must not change the npc_base contract, the renderer or the 0.10.0 release candidate.

Sequencing (2026-11-03, owner): the asset-review fixes (A–G, notably the character P0/P1 rebuilds and the face/foliage prototypes) stay blocked until the full local map rebuild (H–M) is done and a fresh 3D model review runs against the new maps. The measurements and scorecard in docs/3d-model-review.md stand as the pre-rebuild baseline, not a go-ahead to start character work now. (Character development has since shipped — see DONE.md — and the next character pass is the MakeHuman pipeline above.)

Map rebuild status (2026-11-03): H–M are complete — the Joke Shop is now a 6.0 m low hall (dark stucco, white signature fascia, tied-back curtains, hotel tower) and Hollywood/TV Studio share the limestone-ashlar Masonic facade. A fresh capture_npc_review.gd run produced 96/96 frames against the new maps (none blank) and a vision spot-check confirms the characters still read cleanly. The remaining §L signage-texture candidates and the deferred 3D facade-reveal depth are enhancements, not rebuild blockers.

Reference material is collected first (section A): local, gitignored, development-only photo research may use any source the owner authorises (DuckDuckGo image search, street-view frames, neighbouring properties, buildings across the street) without the licence gate. The licence and privacy gates still apply the moment a frame is promoted into docs/screens/refs/ or any committed asset — local tmp/research/ is the only licence-free zone.

Scope: the shipped 3D catalogue — 8 library characters, the frozen npc_base fallback, 18 gear pieces, 20 library props, 6 generated props and the three venue families — plus the local map pipeline that re-evaluates those venues (OSM data/licensing, reference imagery, the photoreal facade kit, AI-assisted composing, Gaussian-splatting evaluation and map QA).

Exit (asset review, A–G): docs/3d-model-review.md scores every catalogue asset; every P0/P1 is fixed or explicitly accepted with a measurement; inspect/audit/fit/proportions, the 24-NPC budget and the capture gates are green; no runtime contract, dependency or release artifact changed except the rebuilt assets themselves.

Exit (local map, H–M): one local block is reconstructed from licensed data with reference-derived photoreal facades, passes performance/navigation/visual QA and has complete provenance; or the pipeline is rejected with the findings recorded.

A. Local reference material collection (development-only, gitignored)

Owner override (2026-11-03): for local, development-only photo research the licence gate does not apply. Anything downloaded for this track stays under tmp/research/ (gitignored) and is never committed, shipped, or used as a committed asset. The moment a frame is promoted to docs/screens/refs/<venue>/ it must pass the sidecar licence + privacy gate; local research is the licence-free zone only.

  • Add a DuckDuckGo image-search fetcher: tools/refcollect.py search <query> --slug <block> --limit N downloads top results into tmp/research/<block>/ with a source-URL manifest (no licence filter, marked local_only/unchecked-local), and sheet composes a local contact sheet. Requests go through a token bucket plus polite jittered backoff that honours Retry-After, and send a full Firefox header set. Tests in tools/test_refcollect.py. Run note: DDG rate-limits aggressively; space batches and run the per-venue collection below when the limit clears.
  • Collect Hollywood block reference: facade, marquee/blade-sign neighbour, Walk of Fame and arrival staging are covered by tmp/research/elcapitan/, hollywood-block/ and the jimmy-kimmel-* sets (day, night, entrance and stage frames).
  • Collect Joke Shop / Comedy Store block reference: tmp/research/comedy-store/ covers the facade, canopy, street and visitor views; ramada-hotel/ adds the neighbouring hotel and comedy-store-hillside/ the hillside/houses behind the roofline.
  • [-] Collect street-view frames of both blocks: street-level DDG frames now cover the facades, corners and opposite frontages for both blocks; true Google Street View panorama capture is deferred (needs a Google API key or a manual browser capture — recorded in MEMORY).
  • Build a local contact sheet per block: sheet.jpg exists for every collected set (elcapitan, hollywood-block, jimmy-kimmel-stage, jimmy-kimmel-entrance, comedy-store).
  • Record which local frames changed a decision: added the frame→decision mapping to docs/venues.md#hollywood-boulevard (recent elcapitan-2026 geosearch → 2009 frame stays the geometry reference; jimmy-kimmel-entrance/07.jpg → the four entrance concepts) and docs/venues.md#the-joke-shop (comedy-store/ → the four concept pairs + map plan; comedy-store-hillside/ → the day-concept anchor that clears the image-recitation filter). Frames stay gitignored; only the provenance notes are committed.

B. Baseline, scorecard and routing

  • Load blender-director plus the per-family specialists before touching geometry, and record the skill stack and the advice that changed a decision in docs/3d-model-review.md. (Routing, QA severity/verdict, texel-density tiers, character loop criteria and budget rules recorded in the review doc; MCP-only steps translated to headless flatpak runs.)
  • Re-run the neutral NPC baseline (48 frames), the final-context review (96 frames), the prop gallery and the 24-NPC budget on the current build; keep raw frames in tmp/ and do not replace committed sheets until a fix lands. (48/48 and 96/96 frames captured under xvfb-run, none blank; raw sets in tmp/model-baseline and tmp/model-review; committed sheets untouched. 24-NPC smoke still 6.94 ms / 168 meshes / 131,616 triangles.)
  • Create docs/3d-model-review.md with a per-asset scorecard: silhouette, anatomy/proportion, topology/edge flow, UV/texel density, textures/materials, rig/weights, LOD/impostor, collision/grounding, export contract, provenance, skill route, verdict (P0/P1/P2/accept). (Doc created with character/gear/prop/venue tables, verdict columns and a ranked candidate list; unmeasured cells are marked — instead of being guessed.)
  • Collect the baseline measurements in one table: blender_assets.py inspect, audit, fit, npc_proportions.py, texel density, triangles/meshes, 24-NPC frame time. (All seven captured; texels and inspect --json --out were added to make the measurements repeatable. All 46 assets audit/inspect clean; texel density 64–122 px/m authored, 287–440 effective fabric.)
  • Review the scorecard with the project owner, rank the P0/P1 gaps and decide fix vs accept per item; record the decision and the reason in the review document. Deferred until the full map rebuild (H–M) lands and a fresh 3D review runs against the new maps.

C. Character re-evaluation (8 variants + fallback)

Skill route: character-artist, retopology, rigging, uv-workflow, texture-workflow, materials, hair-groom, asset-optimization, lookdev, qa-review.

  • Face/head review: the eight library characters ARE built with the 0.9.4 face detail in tools/blender_assets.py._face() — sculpted chin plate + cheekbones (_shape_head_mesh), socketed sclera + thin upper lid, nose bridge/tip/wings, rounded mouth and ear concha. The plain sphere head in tools/npc_pipeline.py is the frozen npc_base fallback only. The ranked P1 "flat faceted cheek/brow + slit eyes" is therefore a readability/scale question on the existing sculpt (head seg=18/rings=12, small offsets), not missing geometry. Next step if the owner funds it: a single-variant npc_casual prototype that raises head subdivision and deepens the cheek/brow offsets. Fix is gated on the owner scorecard review.
  • Hands/grip review: the hands ARE detailed — tools/blender_assets.py._hands_and_shoes() builds a wider shallower palm, four two-segment bent fingers with rounded tips and a thumb; gear contact is the open palm plus the HAND_ITEM_FORWARD (0.04 m) offset. Decision: keep the open palm + forward offset; a finger-curl rig stays out of scope — the current rig has no finger bones, so a curl rig is a contract decision, not a modelling tidy-up. Optional extra finger bones are now allowed by the cap/contract extension, but require an ADR and weight/budget verification. Recorded as ranked gap #6 (accept; revisit only if face work opens the budget).
  • Weights/pose evidence captured and vision-reviewed, and the P1s fixed: wrist/hand detachment (wrist + hand now 100% Hand, hand raised, sleeve cuff follows the hand), hip/thigh collapse + knee pinch (thigh/shin rigid with a short knee blend). Vision-verified: hip fix clean, knee crease minor residual. The guard hip-pouch clip and the residual knee crease remain as 0.10.0 refinements.
  • UV/texel density: measured (64–122 px/m authored, 287–440 px/m effective fabric on the 4× repeat; table in docs/3d-model-review.md). Decision: accept as-is — the maps are neutral/runtime-tinted (not painted skin), so density controls only subtle weave/noise, and fabric already sits in the standard tier. No atlas/UDIM/resolution change is justified until a portrait shows texture starvation; CHAR_MAX_TRIS/CHAR_MAX_VERTS stay unchanged.
  • Cloth and hair: hem clipping is already handled by the 0.76 stride scale for npc_dress/ npc_coat (v0.9.0), hair/beard are the five procedural styles, and headgear clearance is verified clean by blender_assets.py fit (0.9.4). Decision: cloth simulation stays out of scope for the 8-variant library and the Compatibility renderer — rigid procedural meshes plus stride scaling are the accepted approach; a baked cloth-sim pipeline would only be reconsidered if a later close-up shows visible hem/coat stiffness.
  • Fix only the ranked P0/P1s, one asset family per commit; refresh the comparison sheet and re-run inspect, audit, fit and npc_proportions.py after each fix. Blocked until the map rebuild + second 3D review (see the track's sequencing note).
  • Crowd-variety reference: five licence-cleared Commons photos fetched to tmp/research/crowd-variety/ (CC0 ×2, CC BY 2.0, CC BY 3.0, PD) covering umbrella/dense crowds and a pedestrian mall — general build/hair/outer-layer variety context, reference only (never traced). The AI proposal set (generated crowd-variety concepts) will carry the specific 4-variant detail when the character-development roster starts.
  • Generate a face/hand concept board for the highest-ranked P1 and compare it with the 0.9.4 baseline before rebuilding anything; keep it development-only and record model and prompt.
  • After each accepted fix, capture front/three-quarter/profile comparisons, add them to the review sheet, and refresh the README character row.
  • qa-review sign-off: all eight variants ship with notes (face realism landed, hands/ cloth/gear accepted, props reviewed); the weights/pose column is the only open item (blocked on the harness) and the full foliage carded-cluster is the one remaining owner-gated P1. Recorded in docs/3d-model-review.md §qa-review sign-off.

D. Gear and prop re-evaluation

Skill route: prop-artist, hard-surface, vegetation-artist, materials, collision-proxy, lod-pipeline, qa-review.

  • Gear: review the 18 pieces for silhouette, straps/buckles, helmet and lanyard readability and rebuild only what fails the scorecard. (Attach/clearance verification is done — the fit report is recorded in docs/3d-model-review.md with the back-gear-vs-hip-radius measurement caveat; the visual silhouette read is what remains, pending the owner scorecard.)
  • Props: review the 20 library props plus the 6 generated props for bevels, chamfers, silhouette, ground contact and shared material language (prop-artist, hard-surface); list the rebuilds that are actually worth the triangle budget. (Vision-reviewed 2026-11-03 via tools/describe_image.py: fire_hydrant rebuilt and shipped; planter_box/ round_planter foliage and street_tree/palm_tree are the foliage-language P1 (gated on the owner); newspaper_box slot and bollard collar are minor rebuild candidates; bench/ trash_bin are ship-ready.)
  • Foliage: replace the flat-shaded _shrub()/_tree() clumps with vegetation-artist-style carded clusters; measure triangles and keep the 60k LIBRARY_TRIANGLE_BUDGET. (Partial 2026-11-04: _shrub() clumps and the palm fronds now alternate two green tones (foliage/foliage_dark, palm_leaf/palm_leaf_dark) so the crown is no longer one flat colour, and a textured_solid bug that broke planter_box/round_planter imports was fixed. The two-tone read is subtle at gallery distance; the full carded-cluster pass remains the owner-gated P1.)
  • Materials and emissives: lookdev pass on bulbs, signs, lanterns, the marquee and the sedan at day and late; confirm the flashlight stays a tight spot with no beam geometry.
  • Collision: verify every prop collider and navigation obstacle matches its silhouette and add the missing collision-proxy shapes where walking is blocked.
  • Build a real-photo comparison for street furniture (bollard, bench, bin, hydrant, planter, lamp) and rank the silhouette and proportion mismatches.
  • Rebuild only the mismatched pieces through the assisted Blender pipeline (blender_assets.py inspect → scripted retopo/UV assist → atlas → audit/fit); document the exact commands in docs/asset-tooling.md.
  • Refresh sheet_props.jpg/sheet_npc_gear.jpg, the README asset rows and docs/blender_previews/README.md after the rebuilds.
  • Exit: a prop/gear capture sheet, budgets and collision checks pass with no unreviewed asset.

E. Venue and camera re-evaluation

Skill route: environment-artist, archviz, set-dressing, lighting, camera-cinematography, lookdev, rendering, qa-review.

  • [-] Facade/venue review: rank the top five shape-language fixes for Hollywood, the Joke Shop and the TV Studio (environment-artist, archviz) and check them against the map-budget tests. Ranked 2026-11-03: (1) TV Studio renders a generic pilaster/concrete box instead of the six-column Masonic Temple — tv_studio lacks masonic_facade, so _build_building() runs while the Jimmy Kimmel starting point is the same facade; (2) Joke Shop blade sign is a flat slab + label, not the reference's circular sign on a roof pole; (3) Joke Shop canopy fascia lacks the deep white-signature band; (4) Hollywood entrance signage shows EL/CAPITAN/LIVE banners where the real building carries the Jimmy Kimmel Live marquee (owner content call); (5) Joke Shop red-curtain bays and entrance-recess rhythm read flat under the canopy. Fix 1 landed: tv_studio now reuses masonic_facade (architecture masonic, ashlar field, six columns, eight steps, landing + guard spawn at y=1.0) while keeping the arrival staging (bulb_canopy, sedan, handrail, barriers); Godot 276/276, screenshot smoke 21/0, 13-studio-arrival.png recaptured. Fix 3 landed: the Joke Shop canopy signature band is now white (Color(0.90, 0.88, 0.80)) over the black fascia, matching the concept's "white celebrity signatures"; a test asserts the CanopySignatures label is white, Godot 276/276, 12-joke-shop.png + the three-up board recaptured. Note: fix 2 (blade sign) is already a circular disc on a roof pole in the code (37f925c); the ranking note was stale. Fix 5 landed: the red curtain bays are now seven tied-back drapes (hourglass cinch, dark recess between bays, scalloped hem) instead of four flat red slabs; vision-judged as "tied-back/gathered fabric" via tools/describe_image.py. Joke Shop map is now 25,828 tris / 518 meshes (under the 150k/700 budget); Godot 277/277, 12-joke-shop.png + board recaptured.
  • Lighting: lighting/lookdev pass on sconces, canopy, marquee and street lamps with day/late captures. (Attenuation is now gated: test_venue_lights_use_physical_falloff asserts every OmniLight3D/SpotLight3D in all three venues uses attenuation = 1.0, so a constant-falloff light cannot return unnoticed.) Vision review 2026-11-03 (tools/describe_image.py on the night + flashlight captures): the flashlight (light_energy 4.2, spot_angle 14°) washes the facade flat when pointed at the door from close range and backlights the nearest NPC into a silhouette (staged-shot artifact, but worth a shorter/softer cone or lower energy for the 06-night-flashlight stage); the El Capitan Theatre red marquee reads as a flat saturated block and the event banners read as flat emissive decals rather than glowing signs — both are Compatibility renderer limits (no bloom) plus the marquee's near-pure-red body; street lamps pool weakly on near-black ground. No code change yet; these are owner-scorecard inputs.
  • Set dressing: review entrance and sidewalk prop clusters (set-dressing) for occlusion, navigation obstacles and readable storytelling clusters. Vision review 2026-11-03 (tools/describe_image.py on the Joke Shop capture): the facade is well dressed, the sidewalk under-dressed. Findings: (1) the queue story is weak — the security_podium sits at x=6.8 while the entrance is at x=-8.8, so the host/podium and the two stanchion/crowd_barrier pieces do not read as a line at the door; (2) the rope barrier reads as thin bare geometry at night; (3) the west palm (x=-13.0) is inside the 27 m facade x-range so its fronds visually overlap the roofline from the beauty camera; (4) the sidewalk has no light pool or A-frame/poster cluster. These are P2 composition items, kept for the owner scorecard rather than moved unilaterally.
  • Camera height: already decided in the 0.9.0 pass — keep the 1.7 m gameplay camera; the ~1.55 m concept frames are art reference only (recorded in DONE.md 0.9.0 Animation).
  • Build the venue three-up boards (real photo | generated concept | current capture): tools/three_up.py compose renders labelled strips into docs/screens/boards/ (caption sidecar per board, rows emits README rows). Boards committed: El Capitan day, El Capitan night, Joke Shop day. Tests in tools/test_three_up.py.
  • Link each committed board from docs/venues.md#hollywood-boulevard and docs/venues.md#the-joke-shop.
  • Use the generated concept lighting and palette as the target for the lighting/lookdev pass, and record the deviations that stay accepted.
  • After the pass, regenerate the venue captures and update README plus both venue documents.
  • Hooded/winter portrait: capture_realism.gd now force-spawns the requested --portrait-type when the role is event-driven (types 5–7 do not appear in the routine day crowd), so the deferred close-up can be staged. Captured --portrait-type=5 and vision-reviewed: the hood reads as a hoodie (drawstrings + face opening) but is snug — no slack volume behind the head/drape at the neck — and the face stays fully lit with no inner-rim shadow. Acceptable; the snug hood is a minor scorecard note, not a P0/P1.

F. Reference images, concept boards and screenshots

Skill route: lookdev, lighting, environment-artist, qa-review; tooling: tools/scene_concepts.py, tools/screen_designs.py, tools/asset_pipeline.py.

Licensed photos are evidence of what a place looks like, generated boards are art-direction proposals, and captures are proof that the build matches. Keep the three labelled and separate, and never ship a generated or downloaded image without provenance.

  • Create docs/screens/refs/ with a README and a sidecar template: source URL, author, licence/attribution, capture or generation date, what it is evidence for, and whether it is shipped or development-only. (docs/screens/refs/README.md defines the layout, naming, licence and privacy gates and the three-way separation from concepts and captures; SIDECAR-TEMPLATE.md carries the photo table, redaction/permission notes, derived-decision log and the generated-board reference table. Linked from docs/screens/README.md.)
  • Hollywood board: five licence-cleared photos (facade day, blade sign, 1922 elevation, Ovation across street, premiere staging) with a sidecar and per-image purpose, linked from docs/venues.md#hollywood-boulevard and credited in CREDITS.md. Cropped the foreground crowd out of the facade shot; EXIF stripped. (docs/screens/refs/hollywood/.)
  • Joke Shop board: three licence-cleared photos (hillside/canopy block, frontage with palms, canopy signature band) with a sidecar, linked from docs/venues.md#the-joke-shop and credited in CREDITS.md. The CC BY-SA The Comedy Store.jpg from the old link list was deliberately not used; the public-domain frames cover the same evidence. (docs/screens/refs/joke-shop/.) Removed 2026-11-03: the committed Comedy Store photo board was replaced by the local-only tmp/research/comedy-store/ research set (owner override; see §3D model and local map re-evaluation → A).
  • TV Studio: no separate board — the venue is the same El Capitan Entertainment Centre block as the Hollywood day shift, restaged for a late-night arrival. The Hollywood board covers both venues and the premiere-staging photo is the arrival evidence; the reuse is now stated in docs/venues.md#hollywood-boulevard, docs/gameplay.md, PLAN.md and docs/development-status.md.
  • Fill the Hollywood gaps that have a clean licence: El Capitan Theatre neighbour and marquee, Walk of Fame terrazzo band, star close-up, and a night street-lighting context frame (cropped to remove people and a plate). The board now has nine photos. (CC BY-SA night frame is redistributed unmodified for study only; the review is recorded in the sidecar.)
  • Generate one concept board per venue with tools/scene_concepts.py generate (palette, facade rhythm, lighting), keep raw output gitignored, commit only the selected board, and record the model, prompt and spend in the sidecar.
  • Build a three-up sheet per venue (real photo | generated concept | current build capture) and use it as the reference for the character, prop and venue verdicts. (Done for the venue exteriors via docs/screens/boards/; character/prop verdicts still use the Blender preview sheets.)
  • Apply the screenshot rule to every workstream: each accepted fix updates the relevant row in README.md plus the matching docs page (docs/blender_previews/README.md, docs/screenshots/README.md, docs/3d-model-review.md or the venue map doc).
  • Re-run tools/check_screenshots.sh and python3 tools/check_docs.py after gallery changes and record both results in the handoff.
  • Keep AI-generated images out of assets/ until they are picked, cropped, credited and (for textures) audited; CREDITS.md and ADR-0015 still govern provenance.

G. Gates and documentation

  • Add the measurable checks the review needs (texel density, weight/pose sanity, collider coverage, LOD/impostor check) as stdlib or GDScript gates where possible; CI stays offline. (Texel-density gate in tools/test_blender_assets.py checks all five slots per character and the 40–400 px/m authored band plus the 250 px/m fabric floor; the pose→bone contract is covered by test_pose_reaches_the_skeleton_bones; LOD/impostor already has test_avatar_mesh_budget_and_lod_swap. Collider coverage is still open — split below.)
  • Add a collider/proxy check: every library prop that blocks walking has a StaticBody3D + NavigationObstacle3D, checked against the catalogue. (world_builder.gd tags every library node with a library_prop meta and declares the non-blocking marquee/handrail/lantern/door set; two tests in test_world.gd verify placed props (body + shape + avoidance radius, all three venues) and library dressing (body unless documented exempt). Mutation-checked: removing _add_prop_collision fails 3 tests, dropping handrail from the exemption list fails with the exact node. Suite is 275/275.)
  • Add a coverage test so every catalogue asset has a scorecard row and verdict. (tools/test_model_review.py: every CHARACTERS/GEAR/PROPS entry needs a row in docs/3d-model-review.md, and character rows need a non-empty verdict.)
  • Document the new gates: docs/blender-assets.md now lists texels, the test_model_review.py coverage gate and the test_world.gd collider/proxy gate; skill routes live in AGENTS.md §Local Blender skills and each TODO.md section; the scorecard and evidence table are in docs/3d-model-review.md.
  • Record accepted limits/deferred rebuilds: docs/known-limitations.md already records the stylized low-poly style, no finger-curl rig and the >26 m impostor; MEMORY.md holds the durable decisions (texel density accepted, cloth sim out of scope, open palm + forward offset, face subdivision prototype gated on the owner). The corrected face note is also in docs/blender-assets.md.

Local map workstreams (H–M). Build a local, data-backed map pipeline that can replace or supplement the stylized venue geometry with licensed OpenStreetMap footprints, 3D building massing, reference-derived photoreal facades and AI-assisted composition. This remains part of the same unversioned track, not a release gate. The shared reference/provenance gates in A/F still apply; the map pipeline has its own exit in M.

Local-map skill route: environment-artist, archviz, hard-surface, geometry-nodes, procedural-modeling, vegetation-artist, set-dressing, lighting, materials, texture-workflow, lookdev, scene-assembly, qa-review.

H. Local map data, licensing and scope

  • First target block + role: Hollywood (El Capitan, 6840 Hollywood Blvd) and the Comedy Store (8433 Sunset Blvd); the result supplements/informs the stylized VENUE_SPECS (reference-derived materials, facades and massing), never replaces them with imported OSM geometry. Recorded in both venue map docs.
  • ODbL audit: OpenStreetMap is used as reference only (hand-built geometry informed by OSM ways + Commons photos), so the shipped geometry is not an ODbL derived database and no share-alike obligation attaches. Attribution is still kept via the OSM way links in docs/venues.md#hollywood-boulevard / docs/venues.md#the-joke-shop. If derived OSM geometry ever ships, it needs the attribution + share-alike review in a separate ADR.
  • OSM extract workflow: no offline extract is required while OSM is reference-only. If the pipeline later imports footprints, pin an Overpass query + extract date and record it in a provenance sidecar (see below); no city-scale extract is imported into the runtime.
  • 3D building datasets: OSM Buildings/Overture/CityGML are not needed for the reference-only approach. If massing import returns, OSM Buildings LOD1 is the first candidate (CC0-ish footprints); CityGML/Overture need a licence review per source.
  • Provenance sidecar: map sources are attributed in the venue map docs (OSM ways, Commons photos, local-only research sets). Any future derived map asset adds a sidecar with source URL, licence, capture/extract date and attribution text before it is committed.

I. Local map reference imagery

Recent-photo research (2026-11-03): Commons geosearch around 34.10118/-118.34009 (500 m) plus category/date sorting shows usable imagery from the last 12 months: Dolby Theatre billboards (2026-09-30, CC0, 6 frames), Dolby street level and a fresh Walk of Fame star (2026-07-17, CC0), a Michael Jackson impersonator at the El Capitan frontage (2026-02-25, CC BY-SA 4.0), the Celia Cruz Square marker and a 2025-09-28 star series (CC0), and a Thunderbolts premiere (2025-04-28, CC BY-SA 2.0). No recent frame shoots the El Capitan facade straight-on in daylight; the best facade evidence is still the 2009 CC BY frame. The 8 downloaded candidates are in tmp/research/elcapitan-2026/ (gitignored) with meta.json; nothing was committed.

  • Wire tools/refboard.py (standalone, stdlib-only): search --source search|geo|category lists candidates with ISO date, age-in-months and a commit/review/skip licence gate (--allow-ccbysa after a recorded review); fetch downloads usable rows into tmp/ with spaced requests and writes meta.json; rows emits SIDECAR + README table rows from the manifest. Six recent CC0 photos of the block were re-fetched into tmp/research/elcapitan-2026-tool/. Tests in tools/test_refboard.py (licence gate, recent filter, review-row exclusion, row emission).

  • Add a --max-age-months filter (default 12) and a --must-reuse mode that matches the folder's existing sidecar entries so a refresh only reports what changed.

  • Research-diff step: keep the last-12-months board as the "current reality" evidence and the older photos as the geometry evidence, labelled per photo; no sidecar date field needs to change, only the purpose column.

  • Write a reference shot list per block: facade materials, windows, signage, rooflines, street furniture, curb cuts and lighting.

  • Download and catalogue committed reference images from Wikimedia Commons, local/own photography or other clearly licensed sources; record author, licence and attribution with the §F sidecar template. Local-only research that ignores licence limits (DuckDuckGo, street-view frames, neighbours, across-street buildings) is collected under §A into tmp/research/ and is never committed.

  • Separate development-only references from shipped textures; commit only images with clear redistribution rights and keep the rest under tmp/ or the ignored reference folders.

  • Build the per-block real-photo board under docs/screens/refs/<block>/ using the sidecar template from the 3D review §E, and link it from the venue map documents.

  • Define the photoreal target and a material-derivation workflow (albedo/normal/roughness, AO, weathering) that matches the existing Compatibility renderer (texture-workflow, materials, lookdev), with the recent 2026 street frames as the current-reality reference and the 2009 facade frame as the geometry reference.

  • Remaining Hollywood gap: a licensed night photograph of the hero facade and blade sign (the night frame on the board is a different block, near Vine), plus a sidewalk-level shot of the steps/door recess and the east retail neighbour. For the committed board the licence gate stays (no unlicensed Street View or scraped social media); local-only copies of the same viewpoints go through section A instead.

J. Local map geometry and AI-assisted composing

  • OSM footprint import and 3D building massing: deferred — the §H decision keeps OSM as reference-only, so no footprint/massing import runs; the procedural VENUE_SPECS plus the reference-derived materials carry the geometry instead.
  • Facade kit approach: superseded for this pass — §K delivered the banding/mass via procedural geometry plus photo-anchored tiles (limestone_ashlar, dark_stucco). A reusable Blender facade kit (window bays, cornices, storefronts) is a future enhancement, not a blocker for the current maps.
  • Joke Shop building-appearance pass: dark_stucco tile (1024 px) promoted via the picks pipeline and wired into _build_comedy_store_building, replacing the grey paver tile so the hall reads as flat matte charcoal instead of masonry; photo-anchored day concept generated from the hillside PD frame (the branded frontage photo trips the model's image-recitation filter — recorded in the concept README); day capture promoted and the three-up board rebuilt. Test asserts the hall wall carries dark_stucco.
  • Building-appearance realism pass, measured against the reference set: bring the masonic facade's material response (limestone value/roughness, joint lines, weathering streaks), the banner/canopy and marquee materials, and the night emissive balance to the level of the 2009 facade photo and the 2026 street frames; capture before/after and record each delta. (Ashlar field: limestone_ashlar tile, reference-anchored on the facade photo, 768 px over ~4.6 m of wall = ~167 px/m, promoted via the picks pipeline, warm-shifted tint behind VENUE_SPECS. Banner cloth: warm self-light registered with _marquee_emissive (0.14 base; 0.28 night / 0.028 day). Marquee sign face + bulbs: warm-incandescent already in the prop PALETTE, now test-verified to brighten at night via _marquee_emissive. Day and night captures promoted, both three-up boards rebuilt.)
  • Upgrade the facade from flat material bands to 3D photoreal detail (real cornices/reveals, not just the ashlar/stucco tiles). Recorded as a deferred enhancement — the tiles already give the material read; the remaining gap is geometric reveal depth (texture-projected or Blender modules). Verify texel density, tiling and night look when it lands.
  • Integrate behind WorldBuilder.VENUE_SPECS: the photo-anchored tiles are wired into _build_masonic_building / _build_comedy_store_building with colliders, navigation obstacles and the existing day/night materials.

K. Comedy Store map recreation

Plan (2026-11-03): rebuild the Joke Shop venue geometry from the new local-only reference set (tmp/research/comedy-store/, gitignored) and the four photo-anchored scene pairs now in docs/screens/concepts/joke_shop/. Evidence wins for shape; the concepts win for material/mood. Nothing is committed until the licence gate on any promoted reference and the map-budget tests pass. The current _build_comedy_store_building() stays as the fallback.

  • Extract the real proportions and reconcile: vision-read tmp/research/comedy-store/ frames via tools/describe_image.py and recorded the reconciled table in docs/venues.md#the-joke-shop §Measured proportions. Key finding: the game facade is proportionally taller than the real (3.2:1 vs ~6:1) — the main shape-language deviation; canopy pitch, blade-sign diameter, entrance and roofline all match closely. Curtain-bay count is crop-dependent (3–7), so 7 is accepted as a readable stylization.
  • Rank the shape-language gaps and write the top-five fix list (measurements from the proportions pass, §docs/venues.md#the-joke-shop): 1. Facade height — game 8.4 m vs real ~5.5–6 m (≈3.2:1 vs ~6:1 ratio). Fixed 2026-11-03: facade_height lowered to 6.0 m; the wall now reads as a low single-storey hall with the canopy capping the top (vision-verified). 2. Rounded west corner — the real building has a ~3–4 m radius corner; the game uses a hard 90° corner, and the entrance sits at the rounded corner. 3. Canopy depth/slope — game 4.9 m at 11° vs real ~4–4.5 m with a shallower pitch. 4. Neighbouring hotel/parking mass — absent; the hotel reference frame shows a low blocker mass east of the club. 5. Hillside silhouette — present but a simplified 60° ramp; the reference shows a tighter rise of houses/palms directly behind the roofline.
  • Canopy + blade sign: kept as procedural geometry in _build_comedy_store_building (no separate library prop needed) — the circular 3.9 m white disc on a roof pole and the sloping black canopy with the white signature fascia both match the reference and were vision-verified against the entrance concept.
  • Add the neighbouring hotel mass: WestHotelTower now rises behind the west cream block (12 × 18 m pink slab with a parapet and a 5×4 window grid, stepped back from the podium) matching the Ramada Plaza reference; Joke Shop map is 26,828 tris / 580 meshes (under the 150k/700 budget), Godot 277/277. The parking-ramp/drop-off detail stays out (low-detail blocker mass only).
  • Facade field banding: dark_stucco field, deep black canopy + shadow, white signature fascia, seven tied-back red curtain drapes and the west recessed entrance all landed and were vision-verified; the remaining shape gaps (facade height, rounded corner) are recorded in the top-five list.
  • Gates after the rebuild: Godot 277/277 (map budget 26,828 tris / 580 meshes under 150k/700; navigation/collider tests pass), tools/check_screenshots.sh 21 good / 0 bad; the three-up board and docs/venues.md#the-joke-shop were refreshed.
  • Update docs/development-status.md, docs/asset-tooling.md and CHANGELOG.md when the rebuild lands; record any accepted deviations from the reference.

L. Generated location imagery

Skill route: lookdev, lighting, environment-artist, texture-workflow; tooling: tools/scene_concepts.py, tools/asset_pipeline.py, tools/screen_designs.py.

Tool plan for reference-anchored generation (plan first, implement after this section's review):

  • Add scene_concepts.py generate --reference <photo.png>: attach a licence-cleared real photo as a source image to the OpenRouter image request (multimodal chat endpoint with a base64 data URL), with a prompt suffix that preserves the photographed facade geometry. Record the reference file, its licence and page in the .refgen.json provenance sidecar next to the generated frame.
  • Extend generate_textures.generate_image() with an optional reference_path argument (base64 data URL of the photo); existing call signatures unchanged, and the path is gated by scene_concepts._reference_source requiring a refs-board SIDECAR.md row (or an explicit --reference-unchecked). Verified live on the El Capitan day concept anchored on the CC BY 2.0 2009 facade photo; tests in ReferenceGateTests.
  • Add a sidecar-driven variant pass: scene_concepts.py anchor --reference <photo> --treatments day,night,weathered generates same-framing variants with treatment clauses, one .refgen.json provenance sidecar per frame. Verified live on the facade photo (night + overcast frames committed; the weathered treatment reads as mild overcast — noted on the board). Tests in AnchorTests.
  • Concept frames generated and committed: El Capitan facade day/night/weathered + entrance day-v2; Comedy Store facade/street/strip/visitor pairs (gemini + gpt) + entrance day/night + across-street; Jimmy Kimmel entrance day/night × two models. Each frame carries a .refgen.json sidecar (model, prompt, source photo).
  • Palette/lighting/material targets derived: limestone_ashlar (masonic field), dark_stucco (comedy hall), the warm banner/marquee emissive and the awning tile are the material targets; the deltas (3D reveal depth) are recorded in §J as the deferred facade-detail enhancement.
  • Generate signage, awning and poster texture candidates with asset_pipeline.py; run the role audit and the CREDITS.md provenance check before anything is used in the kit.
  • Compare generated concepts against the licensed photos: where they disagree, the photos win for shape and the generated board only informs colour, mood and detail density. Human review stays the acceptance gate, and AI output is never treated as licensed map data.

M. Local map QA and exit

  • Compare the block against screenshots/map docs and keep the stylized fallback: the three-up boards (photo | concept | capture) and docs/venues.md#the-joke-shop record the evidence chain; the procedural _build_comedy_store_building() remains the fallback.
  • Budget/navigation/collider + day/night captures: Godot 277/277 (Joke Shop 26,828 tris / 580 meshes under 150k/700; navmesh, step collider and prop-collider tests pass), tools/check_screenshots.sh 21 good / 0 bad.
  • Gallery + before/after: the Joke Shop day gallery frame and three-up board were recaptured after each facade change; the photo/concept/capture strip is the before/after record.
  • tools/check_screenshots.sh re-run (21/0) and the Joke Shop three-up board refreshed.
  • Docs: docs/venues.md#the-joke-shop (mapping, measured proportions, simplifications), docs/asset-tooling.md (describe_image.py) and docs/development-status.md updated. CHANGELOG.md is untouched (unversioned track; no release).
  • Exit: one local block (the Comedy Store) is reconstructed with reference-derived facades and massing — 6.0 m low hall, white signature fascia, tied-back curtains, hotel tower — passes performance/navigation/visual QA with complete provenance; the rounded west corner and hillside detail are recorded as accepted simplifications.

Maintenance and documentation hygiene

Unversioned housekeeping that keeps the planning sources trustworthy. Do these when they block a task, not on a schedule.

Status (2026-11-03): v0.10.0-rc.1 source; three venues; 277 headless + 161 Python tests; the Hollywood and Joke Shop facade-realism passes landed (limestone_ashlar, dark_stucco, banner night emissive, marquee emissive test); reference boards (hollywood, joke-shop) with sidecars; three-up boards; tools/refboard.py + reference-anchored generation (--reference, anchor) + tools/three_up.py. Gaussian splatting is deferred to the end of this file.

Memory and status accuracy

  • Trim MEMORY.md from ~1088 lines to the durable set: dropped the superseded 2026-11-01 handoff, the v0.2.x host/disk/NSIS/release anecdotes and duplicated release notes; kept the gotchas (async/event wiring, import order, colliders, RNG, contracts, Godot --import cache, image-recitation filter, capture-harness windowing, bisect zero-area triangles) and the easter-egg note. Now ~800 lines.
  • Re-check the remaining memory claims against code before deleting them; MAX_LIVE_NPCS is still 16 for gameplay while the separate budget smoke spawns 24, so do not "fix" that. (Verified phase timing is data-driven via shift_phases, fixed the stale REQUIRE_SIGNIN_VIEW contradiction and the old 272/108 counts.)
  • Fix stale status claims as they are found; add a documentation check or grep note only if the same class of drift returns. (Counts and the sign-in state corrected on 2026-11-03.)
  • Keep the Gap Analysis baseline and the README status line in sync when a test count, version or feature milestone changes (the baseline was bumped to 277/161 on 2026-11-03).

Imagery and provenance upkeep

  • Keep docs/screens/refs/ sidecars, concept-board records and CREDITS.md in sync whenever an image is added, replaced or dropped; photo-anchored concept frames must keep their .refgen.json provenance sidecar (source photo, licence, page, model) next to the frame.
  • Run python3 tools/check_docs.py after any gallery or reference-board edit so every tracked image stays linked from its folder README.
  • Use tools/refboard.py search/fetch/rows for new Commons research instead of ad-hoc API scripts; downloads stay under tmp/ and only licence-cleared rows (commit/review gate) reach a board after a sidecar entry.
  • Delete or ignore development-only photos and raw generations once a board is committed; the repository carries the picks and their provenance, not the working set.

Godot import-cache hygiene

  • When a new committed texture (assets/textures/..., picks/) fails to import under the existing .godot/ cache, use the scratch-copy import (rsync --exclude .godot ... then godot --headless --import and copy back the .import + .godot/imported/*.ctex/.md5); never hand-edit the uid= in a copied .import. Record any new instance in MEMORY.
  • Prefer tools/scene_concepts.py promote (or a manual move into game_ready/v2/ plus a PICKS entry) so new picks follow the same path as limestone_ashlar and dark_stucco.

Agent skill upkeep

  • Add the tracked project skills gig-line-blender-asset-contract and gig-line-release-qa under .agents/skills/, with references for the asset contract/verification and release/QA runbooks.
  • Keep the third-party blender-skills pack local/gitignored while allowing project-authored skills to be committed; reference the skills from tools/README.md, AGENTS.md and MEMORY.md.
  • Revisit the skills after the next Blender or release work changes the contract, commands or gates; stale skills are worse than no skill.

Blender skills upkeep

  • Re-check the installed Blender pack (version.json) when updating it and record the version in AGENTS.md if it changes; keep .agents/ gitignored and .agents/.gdignore in place.
  • Prune the skill routes in AGENTS.md if a skill stops matching the project (for example if a future second-rig path makes rigging/animation guidance authoritative).

Gap Analysis and 0.x Roadmap

Published baseline: v0.9.6 (2026-11-03) — three playable venues, tutorial/campaign/checkpoint resume, access/radio/escalation loop, shift review, the Blender asset library, 277 Godot + 161 Python tests, a 21/21 screen-design judge and Compatibility/Web/Desktop HQ builds. Current source: 0.10.0-rc.1. Its clean release-candidate build is verified, but it is not published while the gates below remain open.

Open gaps

Area Remaining gap Milestone
3D model and local map re-evaluation Skill-guided asset scorecard/repairs; OSM/3D data, reference-derived facades, AI-assisted composition and map QA Unversioned
Character development Complete (moved to DONE.md); the MakeHuman body pipeline is the next character pass 0.11.0
Content and balance External first-time-player evidence, balance sweeps/tuning and content lock 0.10.0
Audio, accessibility and localization Accessibility/localization acceptance, caption coverage and pseudo-locale/layout sign-off 0.10.0
Performance and platform Target-hardware matrix, Web/Desktop HQ profiling and frame/memory evidence 0.10.0
QA and release engineering Final smoke matrix, clean build/checksum verification and target-matrix launch tests 0.10.0
Legal, signing and storefront macOS signing/notarization, AI-provider rights, storefront/rating approval and privacy sign-off 0.10.0
Documentation and sign-off Final README/status/TODO/manual evidence and known-limitations sign-off 0.10.0
Future 0.x Live replay, more venues, VO/music, image-to-mesh props and seasonal content Unscheduled

The 0.9.3 UI, 0.9.4 animation, 0.9.5 texture, 0.9.6 integration and save-migration work are complete in DONE.md; they are not remaining gaps. The 2026-11-01 documentation audit (architecture ownership, stale-claim cleanup and obsolete-plan removal) is also closed.

Priority (2026-11-04): fix the Rig pose harness, then the MakeHuman character pipeline, then re-evaluate the 3D models, then ship the 0.11.0 minor release. The 0.10.0 candidate still accepts only release-blocking fixes until its external-playtest, legal/storefront, signing and platform gates clear.

Milestone 0.10.0 — 3D model and map improvements (minor)

Goal: ship all 3D model and map improvements together as the 0.10.0 minor — the MakeHuman body retarget (contract armature + project head/hands), the rebuilt Comedy Store/Hollywood maps, and the re-evaluated 3D scorecard — plus the external playtest, legal/storefront, signing and platform gates from the old release candidate. The character-development patch (0.9.7) ships first; this minor contains the model/map work.

Content lock and external playtest

Content lock and external playtest

  • Declare a content/feature freeze for 0.10.0; only release-blocking fixes and balance changes may land after the freeze. (Frozen 2026-11-03 after the 0.9.6 integration checkpoint; this pass is limited to release reliability, accessibility, balance evidence and packaging.)
  • Produce a release-candidate build with the tutorial, campaign, all three venues, Help, accessibility settings and checkpoint/resume enabled. (Clean worktree a9658ec built 0.10.0-rc.1: Linux 161.5 MB, Windows 197.2 MB, macOS 148.0 MB, Web 97.8 MB and HQ 241.6 MB. The Web/HQ archives include the credits, privacy, accessibility and known-limitations pages; SHA-256 manifests were written; the Linux and HQ binaries launched headless and the Web index/wasm/pck/docs returned HTTP 200. Not published because external playtest and legal approval remain open.)
  • Run at least five first-time-player sessions covering the tutorial and one full venue shift; record where players get stuck, misunderstand evidence or choose disproportionate escalation.
  • Triage findings by severity (P0/P1/P2) and fix every P0/P1 onboarding/accessibility issue; keep the rest as explicit known limitations or post-0.10 items.
  • Re-run the affected tutorial/campaign smokes after each fix and capture a fresh first-hour walkthrough for the release notes.

Balance and fairness pass

  • Define measurable targets for routine compliance, access-decision accuracy, peaceful resolution, justified emergency calls and false-positive/false-negative rates. (docs/release-qa.md#balance-acceptance defines cohorts, denominators, 36 matrix cells, seeds, thresholds and matched emergency-first comparisons. Documentation verified; targets are not measured results.)
  • Run scripted venue sweeps across easy/normal/hard and all four phases; compare outcomes and tune event weights, reaction tables, compliance and report strictness where targets fail.
  • Review emergency calls against actual danger: no routine behavior should make emergency the dominant winning move, and genuine threats must remain answerable. Include the type-derived _read_npc_truth() mismatch with observed threats/forced entry; use independently labelled behavior evidence, not the helper as its own oracle.
  • Verify the reputation grades, debrief explanations and campaign progression still communicate the de-escalation-first design after tuning.
  • Record the balance evidence and any deliberate exceptions in docs/development-status.md or MEMORY.md.

Save migration and session reliability

  • Add fixtures for a 0.8-era settings/campaign/history save and a 0.9.0 checkpoint; verify migration/default-filling and a clean first launch with no files. (Committed fixtures under src/test/fixtures/saves/; 9 migration/failure tests cover old settings/campaign/history, the 0.9.0 checkpoint, all three venue checkpoint round-trips, clean install, corrupt JSON and a blocked atomic replace. Suite is now 272 tests.)
  • Test fresh install → tutorial → campaign → pause → quit → resume → review → next venue on an isolated user-data directory. (Tutorial and campaign smokes shared one isolated XDG_DATA_HOME; tutorial completion/replay/skip then campaign resume/review-continue/help both passed. Pause/menu behavior is covered by smoke_session_shell.gd.)
  • Test corrupt JSON, partial temp files, missing optional fields and a failed atomic rename; confirm fallback behavior never destroys a valid save. (Corrupt/partial files fall back to defaults; a blocked temp path reports an error and leaves the prior valid settings intact. _load_json now uses the non-logging JSON instance parser.)
  • Verify checkpoint resume after all three venues, after an incident, with NPCs at the door and with flashlight/body-camera/door state set. (The migration suite round-trips a checkpoint for Hollywood, the Joke Shop and the TV Studio with an open incident, one NPC row, flashlight/body-camera/door flags; the campaign smoke resumes a live checkpoint through the real GameManager.)
  • Document save locations, schema versions, reset behavior and rollback/export instructions in docs/getting-started.md and the release notes. (getting-started.md now documents the four files, version-1 checkpoint, atomic write/fallback, clean install, reset and backup/rollback.)

Release engineering and distribution

  • Run tools/build_releases.sh from a clean checkout and verify Linux/Windows/macOS/Web/HQ artifact names, sizes and checksums. (Clean worktree a9658ec built the five artifacts listed above; PUBLISH_DRY_RUN=1 produced and printed build/SHA256SUMS-0.10.0-rc.1.txt without touching the API.)
  • Smoke-test fresh Linux portable/AppImage, Windows portable/installer, macOS unzip/launch, Web static hosting and Desktop HQ Vulkan fallback on the target matrix. (Linux portable and Desktop HQ launched headless; Web files served over a local static server with HTTP 200. AppImage/installer were not built locally and Windows/macOS launch testing still needs the target matrix.)
  • Add a disk-space preflight and retention policy to the release/publish scripts so a full Gitea host cannot leave a partially uploaded release. (publish_release.sh checks artifact size/free space, writes SHA-256 checksums, supports PUBLISH_DRY_RUN, rolls back uploaded assets/new releases on failure, and offers opt-in RETAIN_RELEASES pruning; dry-run verified.)
  • Verify release metadata, tag/version consistency, asset counts and download links; retain at least the latest 0.8/0.9 releases and document older-asset pruning. (Source/version and tag are both 0.9.6; all seven published releases carry 5–7 assets, including the retained v0.8.0/v0.8.1/v0.9.0/v0.9.1 line. publish_release.sh documents opt-in RETAIN_RELEASES pruning and RELEASING.md describes the seven-release policy.)
  • Decide and document whether macOS signing/notarization is required for 0.10.0 or explicitly remains a future release blocker; record the quarantine instructions either way. (0.10.0 ships unsigned with quarantine instructions; signing/notarization remains an explicit public-release blocker in RELEASING.md and known-limitations.md.)
  • Audit AI-assisted asset provenance and licenses; record source/model usage and any redistribution constraints for textures, characters, props and UI art. (CREDITS.md records the Blender/OpenRouter toolchain, default model slugs, missing per-file generation metadata and the commercial-rights review as an explicit release blocker.)
  • Audit audio/SFX/music licensing (or confirm everything is procedural) and add a credits file. (All runtime audio is procedural; no external music/SFX files are bundled. CREDITS.md records this.)
  • Review body-camera/privacy language, mental-health-crisis representation, age/content rating and storefront requirements; fix any release-blocking copy or content. (PRIVACY.md clarifies the simulated body camera/local-only data; the accessibility and known-limitations pages record the remaining mental-health/rating/storefront review as a release blocker rather than claiming approval.)
  • Add the player-facing credits, known limitations, accessibility statement and privacy note to the release package/README. (README.md links CREDITS.md, PRIVACY.md, docs/accessibility.md and docs/known-limitations.md; the Web and Desktop HQ package builders copy those documents into the shipped archives.)
  • Mark external legal/storefront approval as complete or explicitly block the 0.10.0 release on it.

Documentation and visual sign-off

  • Update README.md, PLAN.md, docs/architecture.md, ADRs, docs/development-status.md, docs/getting-started.md, docs/release-qa.md#manual-smoke-checklist, CHANGELOG.md and this roadmap.
  • Regenerate the full screenshot gallery and realism baselines from the release-candidate build (not the dev tree) after content lock; run check_screenshots.sh and the pinned vision judge, recording model/spend/verdicts, and refresh the README and venue-doc images.
  • Include the reference/concept boards and the updated asset sheets in the release docs, and verify every README and docs-page image resolves from the tagged build.
  • Run the manual smoke checklist on every target and attach/link the release-candidate evidence.
  • Verify the tutorial/campaign/help text at maximum font scale and with the longest strings.
  • Remove or archive stale plans/notes and confirm no contradictory version/status claims remain.
  • Document the character development toolchain in docs/architecture.md: base vs library sources, tool/artifact/gate table, runtime attach/animation contracts and rebuild discipline. (Added §5 "Character development tooling" plus risk and ownership-map updates; documentation check passed.)

Release-candidate QA and exit

  • Run the complete headless suite, Python tooling, tutorial/campaign/session/scenario smokes, 24-NPC budget smoke and release build from a clean checkout.
  • Run the manual QA matrix: fresh install, upgrade, offline launch, gamepad, accessibility settings, pause/resume, all venues and Desktop HQ fallback.
  • Confirm no P0/P1 defects remain; P2 items must be documented as known limitations or post-0.10 work.
  • Create the v0.10.0 version bump, changelog entry and tag; build/publish the release and verify all assets and download links.
  • Exit: the release candidate is content-locked, externally played, legally reviewed, documented, reproducibly built, and approved as the 0.10.0 release candidate.

Milestone 0.9.7 — Character development (patch)

Goal: release the finished procedural character development — the realistic face on all eight variants, the four crowd-variety characters and the per-role pools — as a patch on top of 0.9.6. This keeps the 3D model and map improvements out of the patch; they land together in the 0.10.0 minor.

  • Rig pose harness fixed and the weights/pose evidence captured (see the top track).
  • Character work done: 12-character library, realistic face, crowd variants, per-role pools, 24-NPC budget 6.94 ms (152,464 tris / 168 meshes).
  • QA + local build for 0.9.7: Godot 277/277, Python 161/161, audit 49 OK, fit/proportions 12/12, release smoke matrix green, gallery 21/0, clean worktree build (Linux 172 MB, Windows 206 MB, macOS 159 MB, Web 111 MB, HQ 266 MB), checksums verified, Linux binary launches headless. Tagged v0.9.7 (CI build/publish follows).

Exit: met — 0.9.7 ships the 12-character library (face realism + crowd variety) with the pose harness fixed; the runtime contract is unchanged. The MakeHuman body and the map rebuild are not in this patch (they target 0.10.0).

Roadmap dependencies and risks

  • Content volume is the largest schedule risk; tutorial and campaign work must not outrun the event/dialogue matrix.

  • The vision judge is a dev-time aid with an external key/budget; CI must never make it a hard gate.

  • Godot version changes can alter lighting/imports; do not combine an engine baseline move with a content/feature milestone.

  • Signed macOS builds and legal/AI provenance are release blockers, not polish.

  • Photo-derived map assets add a second provenance risk: downloaded or user-supplied photos can carry unclear licences, identifiable people, plates and property restrictions. No photo or splat-derived asset ships without a completed sidecar, consent/licence review and redaction.

  • Gaussian-splat training is GPU/disk-heavy and the toolchain churns quickly; keep it offline, version-pinned and outside CI, and never make a runtime splat renderer a release dependency.

  • Keep the de-escalation-first rules and rare-emergency design unchanged while adding progression, difficulty and feedback.

  • The Blender skill pack is local, unversioned guidance; it can drift from the project contract or assume Blender MCP. Re-verify every adopted technique with inspect/audit/fit and never make a skill, asset pack or tool version a build or CI dependency.

  • The 3D re-evaluation can expand without bound (retopo every head, rig every finger). Cap it with the scorecard ranking: P0/P1 only, one asset family per commit, and an explicit accept for everything else.

Future / deferred (not scheduled)

  • Live 3D body-camera replay; the timeline is text-only today.
  • Additional venues or seasonal events authored from the content/venues/ resources.
  • Real voice/dialogue audio; ambience, cues and radio crackle are procedural.
  • Swap the primitive prop builder for an image-to-mesh model API behind the same assets/models/generated/<prop>.glb contract.
  • Rare easter eggs/cameos (e.g. Bob Dobbs) only after core content and QA are stable.

Gaussian splatting evaluation (deferred)

Postponed for further development on 2026-11-03. The local-map track ships the facade kit and reference-anchored material passes first; this photo-driven pipeline stays an unversioned, offline study with no runtime or release dependency until an ADR closes it. All licence and privacy gates below remain in force whenever the track resumes.

Photo intake and provenance

Gaussian splatting is evaluated as a photo-driven pipeline: licensed or permissioned photos become a reference, texture source, mesh proxy or distant backdrop. It is an unversioned local track with no runtime dependency or release gate until the ADR closes it, and it extends the reference-image licence and privacy gates above rather than replacing them.

  • Define the accepted photo channels: project/team captures, volunteer or user-supplied photos with written permission, and downloaded images from clearly licensed archives (Wikimedia Commons, Openverse, Mapillary and similar). Record which channel produced each reconstruction.
  • Write a one-page capture brief for user-supplied photos: overlapping arc or orbit around the facade, 60–80% view overlap, 12 MP+ originals, fixed exposure/white balance, HDR and filters off, no motion blur, no people or plates in frame where avoidable, overcast or even light, and the facade height/doors/steps included in several frames.
  • Build an intake checklist and provenance sidecar per photo set: contributor, consent or property permission, licence and attribution text, source URL, capture date, camera/lens, location, faces/plates/interiors present, redactions applied, file hashes and whether the set is shipped or development-only.
  • Set the licence gate: own photos need an explicit contributor licence; CC0/CC BY are preferred; CC BY-SA triggers a share-alike review for derived textures and meshes; reject Street View, social-media scrapes, unlicensed commercial imagery and AI-upscaled copies of unknown originals. Attribution must be feasible in CREDITS.md before use.
  • Set the privacy gate: blur or remove faces, minors, licence plates, screens, private interiors and anything behind a closed door; obtain property permission where required; keep a redacted copy as the reconstruction input and never commit the originals.
  • Treat downloaded photo sets as sparse and of unknown intrinsics. Use them for reference, palette and texture study, or as extra training views; do not expect a usable splat from 10–30 casual web photos of a facade.
  • Keep photos, masks, COLMAP databases, checkpoints and splats outside git; commit only derived, licensed, provenance-stamped assets that pass the review gate.

Reconstruction and alignment

  • Pin a reproducible offline toolchain (for example COLMAP or GLOMAP for camera poses plus gsplat, OpenSplat, Nerfstudio or Brush for training), with exact versions, GPU/VRAM/disk needs and known failure modes recorded in the sidecar.
  • Reconstruct two quality tiers: a dense 100–400-photo arc/orbit capture and a sparse 10–30-photo downloaded set. Record camera-registration rate, training time, splat count, file size and held-out PSNR/SSIM/LPIPS for both.
  • Mask sky, people, vehicles and moving foliage before training; verify the masks do not erase facade edges, signage or window detail, and keep the redacted input variant.
  • Align the splat to game coordinates from the OSM footprint plus surveyed points (door width, column spacing, step rise, storefront height) and record alignment residuals in a Blender scene containing both the splat and the current venue geometry (scene-assembly).
  • Judge quality from held-out views and a render from the game's day/night camera poses (camera-cinematography, lookdev, qa-review), not from training views alone; reject splats that only look correct from the capture path.
  • Maintain a coverage map per block: which facades, corners and rooflines have usable views, and which areas must fall back to the facade kit or stylized geometry.

Map enhancement paths

  • Path A — Reference and QA (lowest risk): render the splat from game camera poses and compare it with the realism captures; derive dimensions, signage placement, facade rhythm and material colours. No splat data reaches the runtime.
  • Path B — Texture derivation: project splat renders or reference photos onto the facade kit to produce albedo, normal, roughness and AO candidates; de-light and bake them (texture-workflow), then verify UV scale, tiling and readability under the Compatibility renderer. Deriving directly from a committed reference photo requires its licence to allow derived works and a CREDITS.md entry; CC BY-SA sources stay out unless reviewed.
  • Path C — Mesh proxy: extract a mesh from the splat or dense reconstruction, retopo and dress it in Blender (retopology, prop-artist), assign LODs (lod-pipeline), and keep it inside the venue triangle/material budgets with mesh-based colliders (collision-proxy).
  • Path D — Distant backdrop: test a baked panorama, impostor or billboard generated from the splat for skyline and opposite-street depth where the player cannot walk; measure file size, loading and visible pop at the venue boundary.
  • Path E — User-photo supplement: let a small supplied photo set improve one facade or landmark (a signage patch, colour reference or hero detail) without rebuilding the venue; define the minimum evidence and the same licence/privacy/review gate as a full capture.
  • For every path record quality gain versus effort, licence compatibility of the derived output, and whether the result may ship or stays development-only. Prefer the cheapest path that closes the visible gap.

Runtime evaluation and decision

  • Probe Godot 4.7.2 Compatibility and Web support for splat rendering in a throwaway scene: addon or importer availability, shader/sort cost, memory, load time and fallback behaviour. Do not add a splat dependency to the release project during evaluation.
  • Set hard limits before any runtime experiment: maximum per-splat file size and total memory, allowed frame-time delta on the 24-NPC budget scene, acceptable Web load time, and no compute-shader requirement for the Compatibility/Web path.
  • Keep collision and navigation mesh-based even if a splat is displayed; a splat must never become the playable floor or the access/occlusion authority.
  • Write the ADR with a per-path go/no-go, the accepted licence/provenance rules, whether each result ships or stays development-only, rejected alternatives and the evidence bundle.
  • Exit: one licensed photo capture measurably improves a venue through a documented path and passes visual, performance and provenance review; or the approach is rejected with reproducible findings and removed from the release path.