E1 memory audit: measured all texture sources (decoded RGBA+mips). Resident VRAM ~240MB default boot / ~420MB worst case (all flags + all shop types); stock packs the swing. Cheap win: half-res'd book+toy atlases (quick pull-cards, not the deep record dig — covers verified legible) → stock VRAM 323->181MB, no index/UV change, re-published to depot. Verdict: fine to ship v2.0; flagged stockpack cache-cap (C) + KTX2/Basis (4-8x) for v3. E2 hygiene: validate --depot 0 err, pack-QA 3/3, provenance-drift clean (27 live), 0 orphan atlases. Numbers + verdict in AUDIT.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
LANE E — cross-lane notes
Round 9 — VRAM audit verdict + one rec for C
VERDICT: fine to ship v2.0. Texture VRAM ~240 MB default boot / ~420 MB worst-case (all flags +
all shop types visited) — comfortable on desktop, within a low-end ~512 MB–1 GB budget. Full numbers
in AUDIT.md. Cheap win taken: book + toy stock atlases half-res'd (they're quick pull-cards, not
the deep record dig — covers verified legible), stock VRAM 323 → 181 MB, no index/UV change, atlases
re-published. Depot/manifest hygiene all green (validate --depot 0 err, pack-QA 3/3, 0 orphans).
→ Lane C (optional, NOT blocking v2.0 — v3 follow-up): stockpack.js caches atlas textures with
no dispose, so stock VRAM accretes across shop types (record+book+toy → 181 MB resident). A small
cache-cap (dispose non-current shop-type atlases on room-type change) would bound it to the
current type (≤134 MB). Not needed for the tag; flagging for when you next touch stockpack.
→ v3 (bigger, flagged): KTX2/Basis GPU-compressed atlases — 4–8× VRAM cut, no quality loss.
→ Lane B (tram, on-call): if you want a tram asset, a box primitive with the right silhouette (long low body + roof + windows strip) beats a heavy model and is budget-free — do that first; ping me only if it truly reads wrong and you want a flux_local/gen pass.
Round 8 — REAL packs live (update)
→ Lane C: all 3 stock packs are now REAL (GODVERSE data, John unblocked DB access). Same schema you wired — just richer data, no code change needed:
- record — 350 real vinyl (John's store stock, real Discogs covers), 6 atlases, cell 256×256.
- book — 311 real book covers (
dealgod.productsbooks), 4 atlases, cell 176×256 (portrait face-out cover, not thin spine — I only have cover art; render books face-out). - toy — 273 real toy/collectible photos, 5 atlases, cell 256×256.
Metadata is parody-transformed (no real titles/artists/brands); covers are real art; book prices are
seeded (source is info-only). Indexes committed to
pipeline/packs/+ staged (assets/models/, your?localdepotpath); atlases published to the depot. All pass the pack-QA gate. If your book shelf wants thin spines, tell me — I'd need spine art (don't have it; covers only).
Round 8 (synthetic samples — superseded by REAL above)
→ Lane C (E1 — book + toy packs staged for you): build_stock_pack.py is generalized over kinds;
book/toy sample packs are staged (web/assets/models/stock_{book,toy}_{index.json,atlas_00.webp},
?localdepot) + indexes committed to pipeline/packs/. Same schema you proved for records — wire
book/toy shelves through the stockAdapter, fail-soft per kind. Cell geometry: record 256×256
(square), book 96×256 (tall-thin spine), toy 256×256 (box). You read uv (I confirmed from your
notes), so render each kind on its own shelf geometry; the index also carries cell_w/cell_h if
useful. artist carries author (book) / brand (toy). These are synthetic placeholders (real index
format) until the DSN lands — your fail-soft + wiring are unaffected when real covers swap in.
E2 — pack-QA gate is live (validate_pack.py in qa gate 3 via validate_manifest): a bad bake
(bad UV/price-band/empty title/dangling atlas) fails qa. If you add index fields, tell me and I'll
extend REQUIRED.
→ Fable (blocker, flagged early per the slip rule): the real book/toy/record packs need
GODVERSE_DSN (dealgod Postgres — up on :5432; builder reads it from env, I don't hunt creds).
One command per kind once set (--kind book --from-db --count 200). Pipeline + parody + atlas + QA
gate all proven on samples; not blocking C (samples staged). No fal spend.
Round 7
→ Lane D (D1 — the flip gate): merged peds ready to validate. web/models/peds/*.glb (19 rigs)
are merged to ONE draw each (1 primitive + 1 atlased material) — fleet 92→19 draws, at the
24-rig near cap 116→24. Method: Blender join + UV-pack + EMIT-bake all materials into one 1024
atlas (pipeline/merge_ped.py). Skinning + skeleton preserved — 65 joints intact on all 19,
walk/idle untouched, so clips retarget unchanged. I verified in the vendored GLTFLoader: all 19
load as SkinnedMesh + bind walk; man_suit poses to byte-identical dims vs the original (skin
deforms unchanged); man_athletic (3-mat) atlas eyeballed correct. Please do the full pass
(silhouettes/materials/impostor bake/identity variety) + the full-density draws/tris numbers for
F's flip. Pre-merge peds backed up (gitignored) at pipeline/.peds_r6/ if you need a diff; per-ped
in pipeline/_peds_merge.json. If any ped's atlas/silhouette reads wrong, name it — I re-run that
one (per-ped atlas size is tunable; a couple with dense detail could go 2048).
→ Lane C (E2 — stock pack): pipeline built + PROVEN, sample staged for you now.
pipeline/build_stock_pack.py produces the pack (parody-transform → atlas WebPs + JSON index). A
24-sleeve sample pack is staged at web/assets/models/stock_record_{index.json,atlas_00.webp} —
wire ?stock=real against it NOW (real index format, synthetic covers), and the real covers drop in
unchanged. Proposed index schema (I built to this — confirm/adjust in LANE_C_NOTES and I'll match
INDEX_FIELDS):
{ "version":1, "atlas_px":2048, "cell":256, "atlases":["stock_record_atlas_00.webp"],
"items":[ {"id":"rec_0000","title":"Neon Suburbs","artist":"Trev Wollemi","price":39,
"price_band":"collector","atlas":"stock_record_atlas_00.webp","uv":[0,0,0.125,0.125]} ] }
uv = [u0,v0,u1,v1] into the named atlas (origin top-left); price_band ∈ bargain/standard/collector/grail.
Seeded pick per bin is yours (same shop.seed → same crate). Fail-soft: pack missing → parody canvas.
→ Fable (E2 blockers — flagging mid-round per your schedule-harness note): the real 200-400 pack
needs two things I can't self-serve: (1) GODVERSE_DSN — the dealgod Postgres dsn/creds (Postgres
is up on :5432; I read it from env, don't hunt creds) → --from-db --count 300; (2) C's schema
confirmation. Both external. The pipeline + parody law + atlas are done and proven on --sample; the
real pack builds in one command once the DSN lands. Not blocking C's consumer wiring (sample staged).
Round 6
E1 done — publish.py now merges into _published.json (no more R5 clobber); --verify +
post-publish drift-check vs the live depot; validate_manifest.py fails if a manifest GLB isn't in
_published.json. Record verified 27==depot. All 27 procity GLBs stay live + recorded.
→ Lane B / Lane C — I'm on-call for the props I shipped:
- B: if
bus_shelter/binneed a scale, origin, or facing tweak once placed on the street graph, tell me the exact issue (current: bus_shelter 2.33×1.41 m / 2.4 m, bin 0.57×0.68 m / 1.0 m, both base-origin minY=0). I can re-normalize + re-publish same-name (idempotent, merge-safe now). - C: if
glass_case/magazine_rack/crate_stackread wrong in-room (scale, the fal glass_case's interior clutter, the magazine_rack density), flag it. A broken-prop fal re-gen is pre-authorized ≤2 attempts (decision #5) — I'll log any spend in AUDIT. Footprints in the manifest; all base-origin. Mapping guidance for the 3 orphans is in the Round-5 → Lane C section below.
Round 5
→ Lane C: hero props BAKED to ≤8k tris — re-measure the record interior
The 6 heavy TRELLIS props are re-baked and live on the depot (same filenames, footprints unchanged, so no re-map needed — they upgrade in place):
| prop | tris before → after |
|---|---|
| drinks_fridge | 77k → 8.0k |
| milkshake_mixer | 73k → 8.0k |
| listening_booth | 54k → 8.0k |
| counter_till | 32k → 8.0k |
| novelty_record | 26k → 8.0k |
| arcade_cabinet | 25k → 8.0k |
Fleet 288k → 48k tris. Method: voxel-remesh → decimate → Cycles base-colour bake (see
pipeline/bake_lowpoly.py, AUDIT R5). Verified loading in the vendored GLTFLoader; base colour
baked (no separate metallic/normal maps → also dodges the record_crate dangling-texture class).
→ Please re-measure the record interior (was ~273k tris in your R4 note) and record the new
number in LANE_C_GLB_VALIDATION.md — it should drop by ~most of the prop weight.
→ Lane C: 3 new fittings LIVE (fal Hunyuan) — glass_case + magazine_rack
The two remaining primitive-only fittings are now real GLBs on the depot (≤8k, base-colour baked):
glass_case(procity_fit_glass_case_01.glb, 1.08×1.42 m, 1.0 m tall) — timber base + glass panels + items inside. Registry idsglass_case(toy/dept) +glass_cabinets(pawn) both alias to it. → addglass_case/glassCase(+ your pawn cabinet kind) →'glass_case'inKIND_TO_GLB.magazine_rack(procity_fit_magazine_rack_01.glb, 0.54×0.44 m, 1.2 m tall) — tiered timber rack w/ magazines (milkbar/pawn). Registrymagazine_rackaliases to it. → map your kind →'magazine_rack'.- Both base-origin, verified loading in the vendored GLTFLoader.
manifest.fittingAliasesalso mapsfridge→drinks_fridgeandlistening_corner→listening_boothif you iterate registry ids.
→ Lane B: new street props — bus_shelter + bin
procity_street_bus_shelter_01.glb(furniture.bus_shelter, 2.33×1.41 m, 2.4 m, ≤8k, base-origin). Solid painted-steel shelter (back wall + roof + bench) — place at bus-stop spots along the footpath.procity_street_bin_01.glb(furniture.bin, 0.57×0.68 m, 1.0 m, 3k) — red council wheelie bin, an obvious town-furniture gap. Mapbinin your furniture GLB-upgrade path if you want it on footpaths.- B3 resolved:
novelty_recordis now 8k (was 26.5k) via the E1 bake — re-measure the busy view. All furniture GLBs base-origin + footprint-accurate; fail-soft as usual.
→ Lane C also: crate_stack for floor piles / dig
procity_fit_crate_stack_01.glb (fittings.crate_stack, 0.71×0.72 m, 0.9 m, 3k) — a stack of 3
plastic milk crates, good for record-store floor dressing or the dig scene. Optional; map a kind to
'crate_stack' if useful.
→ Fable: E1 (bake) + E3 (fal) status
- E1 bake DONE + committed — 6 props ≤8k, published. Feeds C's re-measure + your v1.1 tag.
- E3 fal DONE — glass_case + bus_shelter + magazine_rack generated on fal Hunyuan v2, ~$0.64
total (log in AUDIT R5), ≤2 attempts each, all baked ≤8k + published. Guardrail held (nothing
else on fal).
validate --depot0 err. Last two primitive-only fittings retired. - E2 depot growth next (v2-serving library props).
Round 4
→ Lane D (task D2): decimated peds ready to validate
The 19 ped rigs in web/models/peds/ are decimated and staged (committed 518678d, ref
lane-e/round4-peds). Please validate rig binding + animation:
- ≤3k tris each (range 1564–2805; per-ped before/after in
pipeline/_peds_decim.json). Fleet total 924k → 46.5k tris (48.6k → 2.45k avg). At the 24-rig near cap ≈ ≤67k. - Skinning preserved:
JOINTS_0/WEIGHTS_0intact; skeleton unchanged (nodes + 65 joints identical to source on every ped I checked), so the sharedwalk.glb/idle.glbclips retarget unchanged.walk.glb/idle.glbwere NOT touched. - Method:
gltf-transform weld→simplify --ratio 0.05 --error 0.12(meshoptimizer — works because these are manifold skinned meshes, not R3 TRELLIS shell-soup). Contact sheet:docs/shots/laneE/peds_decimated.png(silhouettes/textures clean at street distance; fingers merge + faces flatten as expected at 3k, no melting). - Originals backed up (gitignored) at
pipeline/.peds_orig/if you need a before/after diff.
If any rig fails to bind/animate or a silhouette reads badly, tell me the file(s) and I'll re-run that one with a gentler error (0.08 keeps ~50% more tris but still <5k). Then F re-measures gate 3 after your sign-off.
→ Lane C: E2 asset re-exports
procity_fit_record_crate_01.glb— FIXED. Root cause of yourundefined 'uri'crash: the material'smetallicRoughnessTexture+occlusionTextureboth pointed at a sourceless texture (the packed AO/roughness map failed to encode to WebP during normalize; base-colour- normal were fine). Fix:
pipeline/fix_glb_textures.pystrips material slots that reference a sourceless texture (orphaning it — three.js loads textures lazily, so no crash). Verified it loads in the real vendoredGLTFLoader(meshes=1, tris=5000, textures ok) alongside a bookshelf control. Re-published to the depot — re-maprecord_crate/recordBininglb.js(they should already be mapped) and it'll upgrade instead of falling back.
- normal were fine). Fix:
- counter-with-till — LIVE. New fitting
counter_till(procity_fit_counter_till_01.glb): 1.61 × 0.64 m footprint, 1.15 m tall, timber counter + cabinet doors + period beige till on top (flux_local concept → trellis_mac, on-device). Fixes the R3 4 m till-less counter that squashed. Verified it loads in the vendoredGLTFLoader(32k tris — TRELLIS shell-soup floor, within decision-#2's 25–77k for focal props) and live on the depot (validate --depot0 err). → Inglb.jsre-addcountertoKIND_TO_GLBmapping to'counter_till'(NOT the oldcounter= balcao, which is a 4 m long bar counter kept for shops that want it). The manifestfittingAliases.counter→counter_tilltoo. It's base-origin (minY=0), so your footprint-fit + floor-plant works unchanged; at a 1.4–2.0 m counter slot it scales to a believable ~1.0–1.4 m tall counter.
→ Fable: status
- E1 (gate-3 critical path): DONE. Peds ≤3k, committed + pinned. Unblocks D2 → F3.
- E2a record_crate: DONE + verified (loads in vendored GLTFLoader). Re-published.
- E2b counter: in progress (local, on-device — no fal spend per decision #3).
- Not doing: fal.ai spend (glass-case/bus-shelter stay parked), hero-prop bake-to-lowpoly.