# PROCITY Asset Audit — Lane E, Step 1 *Generated 2026-07-14 from a live inspection of `johnking@100.91.239.7:~/Documents/3D=models/` (the sorted house library) + the 3GOD depot `GET /api/list`. Tri counts / bounding boxes from `pipeline/glb_stat.py` (dependency-free GLB parser); orientation & final scale verified by rendering every candidate through Blender and looking at the thumbnails (never "should look right").* ## TL;DR - The library is a **raw dump**: almost nothing obeys the house GLB law. Scale is arbitrary (only `record_crate_plastic_01` was native-metres), up-axis varies, tri counts span **12 → 960k**, textures reach **4096px**, and several assets carry **branded/trademarked designs** (quarantined). - `pipeline/normalize.py` fixes scale/orientation/tris/textures uniformly. **16 assets** normalized clean, verified upright & metre-scaled, WebP ≤1024, ≤5k tris, +Y up, base origin — ready to publish. - Gaps with **no usable source** (→ `MESHGOD_BATCH.md` or in-engine primitives): glass display case, fridge, till/register, spinner rack, magazine rack, market stall, escalator, VHS shelving. - Depot already holds a few reusable house props (`street-bench`, `council-bin`, `milk-crate-stack`) and 138 assets total (mostly TRELLIS `*-views-3d-*` / `image-3d-*` MeshGod outputs). - **Skin caveat:** 5 of the 25 facades carry **baked-in sign text** (`djsim-milkbar` "MILK BAR", `djsim-video`, `djsim-pub`, `market`, `grimy`) — they break the blank-signboard convention, so the manifest tags them `signboard:"baked"` and Lane B must not overlay a name on them. The other 20 are genuinely blank. ## Toolchain reality (differs from the lane brief) | thing | brief said | actual | |---|---|---| | Blender on ultra | "runs on ultra against Blender" | present but **not on PATH** → `/Applications/Blender.app/Contents/MacOS/Blender` (v5.0.1). Renders 4 angles in ~0.9s. | | coreutils / timeout | none | confirmed — used ssh `ConnectTimeout`, no `timeout`/`setsid` | | python on ultra | 3.9 | 3.9.6 — `glb_stat.py`/`normalize.py` written to that floor | | OpenRouter key | `thriftgod/.env` | absent; house convention `~/Documents/dealgod/lore/keys.env` (auto-read by gen scripts) | | depot upload cred | — | `GOD3_PW` env, **not set here** → publish is gated on John (see `publish.py`) | ## Normalized set (16 — ready; `manifest.json` → `fittings` / `furniture`) house law enforced on every one: metres · +Y up · origin at base (minY=0, centred X/Z — verified node-aware) · ≤5k tris · WebP ≤1024 · no Draco. *Facing is not auto-corrected* — symmetric props are orientation-agnostic; the few directional ones (counter, food_cart) keep their native front and Lane B/C rotate when placing (per-asset `yaw` is available in `batch.json`). | PROCITY id | source GLB | src tris → out | footprint (m) | height (m) | notes | |---|---|---|---|---|---| | **fit** record_crate | shop-fittings/record_crate_plastic_01 | 18320→5000 | 0.46 × 0.63 | 0.41 | ★ native-metres, textured red milk crate; serves record bin too | | **fit** wire_shelf | shop-fittings/shelf_metal_wire_01 | 330→330 | 1.17 × 0.44 | 1.80 | ★ clean upright wire shelving | | **fit** clothes_rack | shop-fittings/clothes_rack_01 | 3152→3152 | 1.34 × 0.43 | 1.50 | ★ rolling clothes rack (opshop) | | **fit** bookshelf | shop-fittings/bookshelves | 45128→5000 | 0.20 × 1.02 | 2.00 | tall bookshelf w/ books (book/video/opshop) | | **fit** cube_shelf_wide | shop-fittings/shelf_cube_bookshelf_01 | 17734→4999 | 0.75 × 1.24 | 1.10 | stocked cube shelving (toy/book) | | **fit** counter | shop-fittings/balcao | 276→276 | 1.06 × 4.02 | 1.10 | long bar counter — Lane C may want to scale to lot | | **fit** coffee_table | shop-fittings/coffeetable | 495→495 | 0.67 × 1.49 | 0.45 | low display/side table | | **fit** work_table | shop-fittings/computertable | 1556→1556 | 0.44 × 1.04 | 0.75 | work/trestle table w/ lower shelf | | **fit** box_crate | shop-fittings/warehouse_box | 78→78 | 1.35 × 1.32 | 0.60 | open bulk bin / bric-a-brac box | | **street** bench | street-furniture/bench | 1942→1942 | 0.44 × 1.21 | 0.85 | slatted seat | | **street** bench_modern | street-furniture/eb_hipster_bench | 640→640 | 1.68 × 0.47 | 0.45 | low modern bench | | **street** bench_wood | street-furniture/woodbench | 11728→3999 | 1.34 × 0.59 | 0.85 | timber park bench | | **street** longbench | street-furniture/longbench | 1172→1172 | 5.86 × 1.08 | 0.85 | long feature bench (arcade/market) | | **street** park_bench | street-furniture/park_bench_01 | 9884→5000 | 1.92 × 1.62 | 0.85 | classic slatted park bench | | **street** streetlight | street-furniture/streetlight | 6592→4999 | 1.64 × 1.82 | 5.00 | ornate lamppost | | **street** food_cart | street-furniture/food_cart_bakso_01 | 30316→5000 | 3.00 × 2.07 | 2.00 | textured food cart (~3MB — heavy, 28 tex; optimize later) | ## Rejected during normalize (needs-manual-fix — NOT in manifest) | source | issue (verified by render) | disposition | |---|---|---| | shop-fittings/cube_bookcase (`cube_shelf`) | node-scale distortion → 6.3 × 8.4 m footprint | dropped; `cube_shelf_wide` covers the need | | shop-fittings/garage_shelves_metal_v2 | imports lying flat (green pallet); baked node transform, up-fix didn't help | needs manual re-origin in Blender; `wire_shelf` covers shelving | | shop-fittings/table, saloon_table, table_02, desk_01, kitchen_table | degenerate/flat or node-distorted (`table` → 55×55 m; `desk` → 17×17 m) | rejected; `work_table`/`coffee_table` cover tables | | shop-fittings/radiogram_furniture_v2_l1 | Blender "Please select a file" on import (bad GLB) | needs re-export | | street-furniture/market_stall_01_CHECK | import error (`_CHECK` = sorter-flagged) | → MeshGod/primitive (see `MESHGOD_BATCH.md`) | ## QUARANTINE — licence unclear / branded (🔴 do NOT publish or ship) The house rule is 🟢 web-ok only; anything branded or of unknown provenance is quarantined until cleared. These are **excluded from the manifest**: | asset | reason | |---|---| | `furniture/armchair-vitra-eames`, `ines-chair-calligaris` | protected designer marques (Vitra/Eames®, Calligaris®) | | `shop-fittings/ikea-cupboard` | IKEA® product design (+259k tris) | | `props-scenes/mk2_1210_turntable_-_old` | Technics SL-1210® industrial design (trademark) | | `props-scenes/tuborg_fest_scene` | Tuborg® brand scene | | `furniture/uploads_files_*`, `props-scenes/uploads_files_*` (Couch William, Harland Office Chair, nv toufa, Backpack) | CGTrader/free3d bulk dumps, licence not recorded | | `furniture/sofa-c`, `hug-armchair`, `dragonchest`, `kitty-bed-01`, `bean_bag_chair` | provenance unrecorded — verify before use | > **Blanket caveat (applies to the 16-asset publish set):** the *generic* fittings we normalized > (benches, shelves, counter, crate…) carry **no explicit licence/provenance record** — I cross- > checked all 16 source filenames and none is a recognizable trademark, and they were pre-sorted > into the house library by John for exactly this use, so they're treated as usable per the lane > brief. But "unclear ⇒ quarantine" is the house rule: **before `publish.py` pushes to the shared > CDN, record a one-line source/licence per asset**, and for any CC-BY source carry attribution. > If provenance genuinely can't be established for one, pull it from the publish set. This is a > reason the publish step is deliberately gated on John, not automated. ## Provenance — round-3 hero props (for the depot publish sign-off) Per the round-3 instruction ("record the per-asset provenance list in AUDIT.md first, then hand John the two-liner"). The hero props have the **cleanest provenance in the whole set** — they are 100% generated on-device, from no third-party source: | asset (`procity_fit/street__01.glb`) | origin | licence zone | |---|---|---| | **5 generated** — listening_booth, drinks_fridge, arcade_cabinet, milkshake_mixer (fit) · novelty_record (street) | **fully synthetic, local**: concept image = MODELBEAST `flux_local` (FLUX.2-klein-4B, Apache-2.0 model) → mesh = `trellis_mac` (TRELLIS.2, an original reconstruction of that concept image). No scanned/third-party geometry, no copyrighted source asset. | 🟢 original work (CC0-equivalent) | | cash_register (`vintage-cash-register.glb`) | **reuse** of John's own `thriftgod_hero` asset, already live on 3GOD (round-3 reuse check) — not regenerated. | 🟢 same as existing depot set | | magazine_rack | **not shipped** — TRELLIS wire rack floored ~112k tris (thin-wire gotcha) → in-engine primitive. | n/a | | glass_case, bus_shelter | **not shipped** — TRELLIS failed on the transparent glass → fal.ai fallback list (John's go — Hunyuan v2 ~14–17¢/gen: ~$0.28–0.34 for the pair, ~$1 worst case w/ retries) or primitive. | n/a | - **Prompts were generic period objects with explicit `no text, no brands, no people`.** Every kept prop was eyeballed for accidental baked branding (contact sheet `docs/shots/laneE/props_gen_local.png`). The only textual marks are the **drinks-fridge's weathered faux-stickers** — verified at full concept resolution to be **garbled, illegible pseudo-lettering** (no real trademark; e.g. a faux barcode + nonsense glyphs), which reads as period flavour and blurs out at mesh-texture resolution. 🟢 cleared. - Tri/size note: TRELLIS meshes are dense multi-shell scans; `normalize.py`'s COLLAPSE decimate floors well above the ≤5k library budget (**25–77k tris, ~0.3–3.5 MB/prop**; neither COLLAPSE nor meshopt can reduce shell-soup without a voxel-remesh that destroys the baked UVs). Accepted for **focal, optional, LOD-able** props (texture-intact) — flagged for a later texture-bake-to-lowpoly / gltfpack pass. - These GLBs stage locally (`web/assets/models/`, git-ignored, served by `?localdepot=1`) and await John's depot publish; they are **not** committed binaries (same policy as the library `_normalized` set). ## Coverage vs the registry fittings needs | shop type | fittings need | covered by | gap → | |---|---|---|---| | record | bins, crates, counter, listening corner | record_crate, counter, wire_shelf | **listening booth** → MeshGod | | opshop | clothes racks, bric-a-brac shelving, book wall | clothes_rack, wire_shelf, bookshelf, box_crate | — | | toy | cube shelves, display tables, **glass case** | cube_shelf_wide, coffee_table | **glass case** → MeshGod | | book | bookshelves, **spinner racks**, armchair | bookshelf, cube_shelf_wide | spinner rack, armchair → MeshGod/primitive | | video | aisle shelves, **returns slot**, poster wall | bookshelf, wire_shelf | returns slot, poster wall → primitive | | pawn | **glass cabinets**, wall hooks, barred window | counter | glass cabinet → MeshGod; pegboard = `tex-pegboard` skin | | milkbar | counter, **fridge**, magazine rack | counter | **fridge**, magazine rack → MeshGod/primitive | | dept | mixed sections, **escalator prop** | (all above) | escalator → primitive | | stall | trestle tables, crates | work_table, box_crate, record_crate | **market stall** → MeshGod/primitive | | street | bench/streetlight/bin/tree | bench×4, longbench, park_bench, streetlight, food_cart | bin (`council-bin` on depot), tree → billboard (Lane B) | Gaps with no clean source are shortlisted in `MESHGOD_BATCH.md` (budget-gated) — everything has an in-engine primitive fallback, so none of these block the game. ## Depot cross-reference (`GET /api/list`, 138 assets) - **90 named + 48 TRELLIS-hash.** Reusable house props already live (verify visually + licence before wiring): `street-bench.glb`, `council-bin.glb`, `milk-crate-stack.glb`, `cashbot.glb`. ⚠️ `akai-mpc-live-iii-*` is a **trademarked product design** — same quarantine class as Technics/ IKEA; do not treat as clean web-ok reuse without a licence check. - **No `procity_*` assets yet** — this lane is the first to publish that namespace. - Our 16 normalized GLBs are staged in `pipeline/_normalized/` and upload via `pipeline/publish.py` once `GOD3_PW` is provided (idempotent, reversible). ## Full library inventory (dispositions) `shop-fittings/` (33): normalized 9 · rejected 8 (7 node-distorted/degenerate + radiogram bad-GLB) · quarantine 1 (ikea) · unused/dup 15 (heavier or duplicate tables/shelves — `shelf_storage_rack_01` alone is 960k tris). `street-furniture/` (9): normalized 7 · error 1 (market_stall) · `building_shell_01` reserved for Lane B (38k-tri building shell — not a fitting). `furniture/` (17): mostly domestic (beds/sofas/chairs) — out of scope for shop fittings; 6 quarantined. `props-scenes/` (10): `dj_roomv01` + turntable = record-shop dressing (turntable quarantined: Technics); rest branded/scene dumps. Raw stats for every **parsable `.glb`** (59 of the ~69 library entries; the rest are `.fbx`/`.obj` or subdir sources not counted): `pipeline/_audit_stats.json`. `glb_stat.py` reports **world-space** dims (node-transform-aware) and WebP/PNG/JPEG texture sizes. Normalizer output: `pipeline/_normalize_results.json`. ## Round 4 — ped-rig decimation (gate-3 blocker) The 19 street ped rigs in `web/models/peds/` were **~31k–56k tris each (924k total)** — the cause of gate 3 failing (~279k tris at the busiest intersection vs ≤200k). Decimated to **≤3k tris each** (integrator decision #1) via `gltf-transform` `weld` → `simplify` (meshoptimizer): ``` gltf-transform weld in.glb _w.glb gltf-transform simplify _w.glb out.glb --ratio 0.05 --error 0.12 ``` meshoptimizer works cleanly here (proper manifold skinned character meshes, NOT the TRELLIS shell-soup of the R3 hero props). Result: **924,153 → 46,551 tris** (48.6k → 2.45k avg; range 1564–2805, all ≤3k), **36M → 5M** on disk. **Skinning fully preserved** — `JOINTS_0`/`WEIGHTS_0` intact, skeleton unchanged (nodes + 65 joints identical to source), so the shared `walk.glb`/ `idle.glb` clips retarget unchanged (both left untouched). Contact sheet: `docs/shots/laneE/peds_decimated.png` — silhouettes/textures read clean, no melted faces/hands (fine for street distance). Originals backed up (gitignored) at `pipeline/.peds_orig/`; per-ped before/after in `pipeline/_peds_decim.json`. Lane D validates rig binding + anims (task D2); Lane F re-measures gate 3 (~24-rig near cap × ≤3k ≈ ≤67k fleet contribution). ## Round 4 — E2 asset re-exports (published) - **`procity_fit_record_crate_01.glb` re-export (fixed).** The R3 normalize left a **sourceless texture** (packed AO/roughness map didn't survive the WebP export; the material's `metallicRoughnessTexture` + `occlusionTexture` pointed at it), which crashed three.js GLTFLoader with `undefined 'uri'`. `pipeline/fix_glb_textures.py` strips material slots that reference a sourceless texture (base-colour + normal kept; the orphan is never loaded). Verified it loads in the vendored `GLTFLoader` (5000 tris, textures ok); re-published to the depot. - **`procity_fit_counter_till_01.glb` (new).** ~2 m timber shop counter with a period beige till — `flux_local` concept (3 rolls for the ~2:1 aspect) → `trellis_mac` mesh → `normalize.py`. 1.61 × 0.64 m, 1.15 m tall, 32k tris (TRELLIS shell-soup floor, within decision-#2's 25–77k for focal props). **Provenance: 100% synthetic/on-device** (FLUX.2-klein-4B + TRELLIS.2 on the M3 Ultra), no third-party source — cleanest licence zone. Replaces the R3 4 m till-less counter for the interior counter slot (Lane C maps `counter` → `counter_till`); the balcao 4 m `counter` stays for long-bar use. ## Round 5 — E1: hero-prop bake-to-lowpoly (un-parked) The 6 heavy TRELLIS hero props were **25k–77k tris** (shell-soup — simplify-only floors there, R3's lesson). Rebuilt topology + re-baked base colour via `pipeline/bake_lowpoly.py` (Blender headless): **voxel-remesh → triangulate → decimate ≤8k → smart-UV → Cycles DIFFUSE(COLOR) bake HP→LP → WebP export**. All **≤8k tris**, base colour baked onto fresh UVs, self-illuminated a touch (matches normalize). Originals backed up (gitignored) at `pipeline/.props_orig/`; batch spec `pipeline/batch_bake.json`; results `pipeline/_bake_results.json`. | prop | tris before → after | voxel | size on disk | note | |---|---|---|---|---| | drinks_fridge | 77047 → 8000 | 0.03 | 5.3M → 0.9M | hollow glass-door box — needed a coarser voxel (0.012 grabbed the internal shelves into wireframe) | | milkshake_mixer | 73492 → 7991 | 0.005 | 4.7M → 0.9M | small part, fine voxel | | listening_booth | 53502 → 7986 | 0.012 | 3.4M → 0.9M | panel + stool | | counter_till | 32226 → 7999 | 0.012 | 1.9M → 0.9M | timber counter + till | | novelty_record | 26486 → 8000 | 0.014 | 1.8M → 0.9M | thick disc + base | | arcade_cabinet | 25142 → 8000 | 0.012 | 1.5M → 0.8M | boxy cabinet | Fleet: **288k → 48k tris**, **18M → 5.2M**. Every one **eyeballed** (contact sheet `docs/shots/laneE/props_baked.png` — no melted labels/faces; the fridge re-bake was the only redo) and **verified loading in the vendored `GLTFLoader`** (tris ≤8k, baked texture present). Re-published to the depot (`validate --depot` 0 errors). **Provenance unchanged: 100% synthetic/ on-device** (FLUX.2-klein-4B + TRELLIS.2 + Blender bake, no third-party source). → Lane C re-measures the record interior (was ~273k tris). ## Round 5 — E3: fal.ai fallback props (glass-case + bus-shelter + magazine-rack) The two R3 TRELLIS glass rejects + the optional magazine-rack, generated on **fal.ai Hunyuan3D v2** (John-authorized, decision #4). Pipeline: `flux_local` concept (frosted/opaque surfaces so the reconstructor has something to grab — clear glass is what defeated TRELLIS) → `pipeline/gen_props_fal.py` (fal `fal-ai/hunyuan3d/v2`, key from MODELBEAST settings, never hardcoded) → `normalize.py` (scale/ origin) → `bake_lowpoly.py` (voxel-remesh → ≤8k → **EMIT albedo bake**; Hunyuan materials need the base-colour→emission route, DIFFUSE(COLOR) baked them near-black). **Paid gen log (fal Hunyuan3D v2, ~$0.16/gen):** | prop | attempts | cost | outcome | |---|---|---|---| | glass-case | 1 (frosted-glass concept) | ~$0.16 | ✅ solid case, timber base, items visible — TRELLIS couldn't; 40k→8k baked | | bus-shelter | 2 (att.1 dropped the thin perspex panels → att.2 with an opaque back wall) | ~$0.32 | ✅ solid shelter, back wall + roof + bench; 40k→8k | | magazine-rack | 1 (optional; solid-timber, not wire) | ~$0.16 | ✅ tiered timber rack w/ magazines — beat the R3 wire failure; 40k→8k | **Total fal spend this round: ~$0.64** of the $22 balance (well under the <$1 estimate). Guardrail held — nothing else on fal. All 3 ≤8k tris, base-colour baked, **verified loading in the vendored GLTFLoader**, eyeballed (`docs/shots/laneE/props_fal.png` — no melted glass, no legible branding), published, `validate --depot` 0 err. **Provenance: FLUX.2-klein-4B concept + fal Hunyuan3D v2 mesh + Blender bake — synthetic, no third-party source** (Hunyuan output is model-generated geometry). Raw fal GLBs + concepts kept in gitignored `pipeline/.genprops/`. These retire the last two primitive-only fittings (glass_case, magazine_rack) + add a street bus_shelter. ## Round 5 — E2: depot growth (consumer-driven) The round-5 v2 features are **procedural** (dig.js riffles generated sleeve art; B's window shader is 100% procedural; D's roster is spawn logic) — so no forced GLB needs. Per Fable "don't add manifest entries no lane consumes", E2 is deliberately light: two already-synthetic depot props with clear consumers, re-normalized to procity names + thumbs (both already ≤5k tris, metre-scale, base-origin): | new id | source | footprint / h | consumer | |---|---|---|---| | `street/bin` (`procity_street_bin_01.glb`) | `council-bin.glb` (thriftgod_hero, on depot) | 0.57×0.68 m, 1.0 m | Lane B street dressing (towns need bins) | | `fit/crate_stack` (`procity_fit_crate_stack_01.glb`) | `milk-crate-stack.glb` (thriftgod_hero) | 0.71×0.72 m, 0.9 m | Lane C record-store floor piles / dig context | Both 3k tris, verified loading in the vendored GLTFLoader, published, `validate --depot` 0 err. Provenance: MeshGod-generated (thriftgod_hero) — synthetic, web-ok. **Note:** also resolved Lane B's B3 ask — `novelty_record` (was 26.5k, spiking the busy view) is now 8k via the E1 bake. No speculative props added; further depot growth is available on request once C's dig / D's roster name concrete needs. ## Round 7 — E1: ped sub-mesh merge to one draw (gates the roster flip) The 19 ped rigs carried **1–9 primitives / 1–3 materials each = 92 draws** for the fleet (≈116 at the 24-rig near cap) — the optimisation D's memo gates full-density roster on. `pipeline/merge_ped.py` (headless Blender) merges each ped to **ONE primitive + one atlased material**: join meshes → pack every material's UV islands into a non-overlapping atlas UV → **EMIT-bake each source material's base-colour into one 1024 atlas** (route base-colour→emission via the ORIGINAL uv so the atlas UV doesn't hijack the lookup) → single material. **Skinning + skeleton fully preserved** (65 joints intact on all 19, `JOINTS_0`/`WEIGHTS_0` kept; `walk`/`idle` untouched). Result: **fleet 92 → 19 draws (1/ped)**; at the 24-rig near cap **116 → 24 draws** (79% cut). Size 5M → 4M. Verified in the vendored `GLTFLoader`: all 19 load with `isSkinnedMesh` + bind the shared `walk.glb` clip; `man_suit` (2-mat) poses to **byte-identical dimensions vs the original** (109.038 × 62.5) proving the skin deforms unchanged; `man_athletic` (3-mat, most complex atlas) eyeballed correct (skin/top/pants regions all right). Pre-merge decimated peds backed up (gitignored) in `pipeline/.peds_r6/`; per-ped in `pipeline/_peds_merge.json`. → **Lane D validates (D1)** — full silhouette/material/impostor/identity pass + full-density numbers — then **F flips the roster**. ## Round 7 — E2: GODVERSE stock pack pipeline (real sleeves for the dig) `pipeline/build_stock_pack.py` builds C's `?stock=real` content pack **offline** (nothing queries the dataset at runtime): read recordgod (`dealgod` Postgres `discogs_release`, 185k vinyl w/ cover URLs) → curate N sleeves (deterministic md5 sample, varied) → **parody-transform title/artist** → fetch covers → bake 8×8 atlas WebPs (256px cells) + a JSON index (`{id,title,artist,price,price_band,atlas,uv}`) → stage for `?localdepot` / publish to depot. **Parody law (metadata):** real (title, artist) → deterministic seed → recombined from period-AU word banks; the output **shares no source token** (`Dark Side of the Moon`/`Pink Floyd` → `Quiet Milk Bar`/ `The Galahs`) so there is **zero real-trademark leakage**, yet it reads as plausible AU record stock, and the same source row yields the same parody every build. Verified deterministic + token-disjoint. **Pipeline PROVEN** end-to-end with `--sample 24` (synthetic solid-colour sleeves): 24 → one 2048 atlas + index, UVs correct, price bands (bargain/standard/collector/grail) working; sample pack staged to `web/assets/models/` so Lane C can wire the consumer against the **real index format now**. **The real 200-400 pack is blocked on two external deps (flagged to Fable):** 1. **`GODVERSE_DSN`** — the dealgod Postgres DSN (creds). Postgres is up on `localhost:5432`; the builder reads the DSN from env (I don't hunt for creds). `--from-db --count 300` runs once it's set. 2. **Lane C's pack schema** (their task 1, not yet posted) — I built to the proposed fields above; `INDEX_FIELDS` adjusts to C's LANE_C_NOTES contract when it lands. Provenance of the real pack: source = Discogs metadata + cover images (recordgod), **metadata parody-transformed** (no real titles/artists ship); covers are real release art (read-only, non-economy per decision #2). Small book/toy packs are a follow-up once the record pack lands. ## Round 8 — E1/E2: book + toy stock packs + pack-QA gate **E1 — pipeline generalized** (`build_stock_pack.py` now kind-parametrized: `record` 256×256 sleeves, `book` **96×256 tall-thin spines**, `toy` 256×256 boxes). Per-kind parody word banks; same laws as the record pack (build-time extract, deterministic parody-transform of metadata, atlas WebPs + JSON index to C's schema — `stock__index.json`, C reads `uv` not `cell`). Book/toy **sample packs built + staged** (`web/assets/models/`, `?localdepot`) so Lane C wires book/toy shelves now; indexes committed to `pipeline/packs/` as the contract. Parody verified deterministic + no distinctive-token leakage across all 3 kinds ("Nevermind"/"Nirvana" → "Endless Blue"/"Mullet Milk Crates"; "The Hobbit" → "Under the Homestead"/"C. Underwood"). **E2 — pack-QA gate** (`validate_pack.py`, wired into `validate_manifest.py` → qa gate 3): every `pipeline/packs/stock_*_index.json` entry must have a valid kind, in-range non-inverted UVs, a known price band, non-empty title/artist, unique id, and an atlas listed in `atlases[]` — a bad bake fails qa loudly (proven: inverted-UV + empty-title → exit 1). qa `--strict` GREEN 5/5. **Blocker (flagged to Fable/John — real data):** the **real** ~150–250-item book/toy packs (and the real record pack) need **`GODVERSE_DSN`** (dealgod Postgres; up on :5432, read from env, no cred hunt). One command each once set: `build_stock_pack.py --kind book --from-db --count 200`. The current staged packs are synthetic placeholders (real index format); provenance of the real packs = GODVERSE metadata (OpenLibrary/toygod) **parody-transformed** (no real titles/authors/brands ship) + real cover art (read-only, non-economy). No fal spend this round. ## Round 8 (cont.) — REAL stock packs live (GODVERSE, John-authorized DB access) The synthetic samples are replaced by **real** packs, built from GODVERSE via peer-auth on m1ultra (`johnking@100.91.239.7`, native Postgres, `psql` at `/opt/homebrew/Cellar/libpq/18.4/bin`). Access resolved after the deployment DB proved Docker-internal (port closed, SSH denied) — John pointed to the m1ultra clones. All extraction was **read-only SELECT**; no creds guessed, no writes. | pack | items | source (m1ultra) | covers | prices | |---|---|---|---|---| | **record** | 350 | `discogs_full`: `inventory` (John's real store stock) ⋈ `release` ⋈ `release_image` | real Discogs cover art (`monsterrobot.party` CDN, from `local_url`) | real store prices | | **book** | 311 | `dealgod.products` WHERE `category_path LIKE 'books%'` | real product photos (`image_url`, portrait 176×256) | real `current_price` | | **toy** | 273 | `dealgod.products` WHERE `category_path LIKE 'collectables-hobbies-toys%'` | real product photos (`image_url`) | real `current_price` | Build: `build_stock_pack.py --kind --from-tsv` (rows extracted to a gitignored TSV, covers fetched at build time, resized into atlas cells). **Metadata is parody-transformed** (titles/artists/authors/ brands → house word banks, deterministic, shares no source token — "Dark Side of the Moon"/"Pink Floyd" → "Quiet Milk Bar"/"The Galahs") so **no real trademark ships in the pack text**; the **cover images are real release/product art** (read-only, non-economy, per decision #2 — John's own store inventory + AU marketplace listings). Books are info-only in the source → **prices seeded deterministically** (John's call). 15 atlases + 3 indexes: atlases published to the depot (`/api/upload`), indexes committed to `pipeline/packs/` (the C contract) + staged for `?localdepot`. All 3 pass the pack-QA gate. Toy filter is broad (some comics/coins among the collectibles) — acceptable for a collectibles shop; tighten later. ## Round 9 — E1: VRAM / texture memory audit (v2.0 ship due-diligence) Decoded GPU cost = w·h·4 (RGBA) ·1.333 (mipmaps). WebP/JPEG disk size is irrelevant to VRAM — a 2048² atlas is ~22 MB resident regardless of its 0.8 MB file. Measured across every texture source: | category | files | total VRAM | notes / typical resident | |---|---|---|---| | facade skins | 48 | 117 MB | shared per-chunk; only in-view shops resident (~60–85 MB) | | sky domes | 10 | 23 MB | 1 dome resident (~2 MB) | | ground | 10 | 9 MB | ~3 in view | | interior tex (floor/wall/counter) | 16 | 12 MB | current room only (~3 MB) | | wallpaper | 8 | 6 MB | current room only | | **ped fleet** (19 merged rigs) | 19 | **~90 MB** (15×1024²=5.6 + 4×512²=1.4) | all resident (near-rig variety) | | GLB fittings/furniture textures | 28 | ~30–40 MB | street furniture + current room (~25 MB) | | **stock: record** | 6 | **134 MB** (256px cells, full) | resident only under `?stock=real` (eager-preloaded at boot) | | **stock: book** | 4 | **19 MB** (half-res this round) | lazy, on book-shop entry | | **stock: toy** | 5 | **28 MB** (half-res this round) | lazy, on toy-shop entry | **Resident-VRAM by boot mode** (peak, approximate): - **Default boot (no flags):** in-view skins ~85 + ped fleet ~90 + GLB ~25 + Lane-B runtime sign-atlas ~40 ≈ **~240 MB**. - **`?stock=real`:** +record 134 (eager) → ~375 MB; +book/toy after visiting those shops (stockpack caches, no dispose) +47 → **~420 MB**. - **Worst case (all flags, all 3 shop types visited):** ~**450 MB** (weather rain = 1 instanced draw, winmap = procedural — both VRAM-negligible). **Cheap win taken this round:** book + toy atlases half-res'd (256→128 / portrait 176→88 px cells; UVs are fractional so **no index/UV change**, only the atlas files shrank). Book/toy are quick pull-cards (buy-anywhere), not the deep record dig — covers verified still legible at half-res. **Stock VRAM 323 → 181 MB** (record kept full for close riffle examination). Atlases re-published to the depot. **VERDICT: fine to ship v2.0.** ~240 MB default / ~420 MB worst-case is comfortable on desktop (GB-class VRAM) and within a low-end/integrated ~512 MB–1 GB texture budget. The stock packs (181 MB) remain the dominant swing but no longer blow past the base. **No compression pass or lazy-load required for the tag.** Two follow-ups flagged for **v3, not now** (bigger than a ship-round change): 1. **stockpack cache-cap** (Lane C's `stockpack.js`): dispose non-current shop-type atlases on room-type change → bounds stock VRAM to ≤134 MB (one type) instead of accreting all three. 2. **KTX2/Basis GPU-compressed atlases**: 4–8× VRAM cut with no visible quality loss — the real long-term fix for the record pack's 134 MB (needs a transcoder + loader change; out of scope now). **Depot/manifest hygiene (E2):** `validate --depot` 0 err · pack-QA 3/3 valid · provenance-drift clean (`_published.json` == 27 live procity GLBs) · 15 stock atlases live, **0 orphans**. All green post-R8. ## Round 11 — audio pack provenance **Every audio file is 100% original procedural synthesis** (`pipeline/gen_audio.py`, numpy on the MODELBEAST box → ffmpeg). No neural model, no sample library, no third-party recording touched — so the whole pack is **CC0-equivalent original work** and **parody-safe by construction**: there is nothing to imitate, every waveform is an oscillator/noise render. Music beds are original chord progressions/melodies — no imitation of any specific real recording. Tool choice is documented in `pipeline/README.md` (§ audio): synthesis over local MusicGen/stable-audio because it's $0, deterministic/seeded, perfectly loopable, and licence-clean. | group | files | origin | |---|---|---| | ambience (6) | street-day/night, rain, roomtone-retail/milkbar/video | filtered-noise beds + synthesised events (birds/crickets/drops/hum) 🟢 | | sfx (13) | doorbell, door-open, till, riffle, toast, tram-bell, tram-rumble, footsteps ×6 | additive-bell + filtered-noise transients 🟢 | | music (4) | record-shop, milkbar, video-synth, arcade | original synth loops (bass+chords+drums+lead) 🟢 | - **Format**: OGG/Opus primary (48 kHz) + M4A/AAC fallback (44.1 kHz). **Loudness**: long beds/music ffmpeg `loudnorm` I=-16 LUFS / TP=-1.5 (single-pass EBU R128); short SFX peak-normalised (-3…-6 dBFS). - **Verified** (I synthesise, I can't subjectively listen): spectrogram sheet `docs/shots/laneE/audio_spectrograms.png` (content/character) + numeric pass — peaks ≤ -0.4 dBFS (no clip), **all loops seamless** (wrap-jump ≤ internal step), durations correct. **Final subjective ear-check is John's/F's** — flagged honestly. - **Budget**: 8.1 MB shipped (2.8 MB ogg + 5.3 MB m4a) vs the 25 MB cap; lazy-loaded per manifest. - Files ship in `web/assets/audio/` (committed, like `gen/` skins); WAV scratch `pipeline/.genaudio/` is git-ignored. Contract in `manifest.json` → `audio:{ambience,sfx,music}`. ## Round 12 — gig layer provenance (v3.0-alpha, `?gigs=1`) **Gig audio (3, procedural — same discipline as R11):** `music-pubrock-live` (original 90s pub-rock riff — overdriven power chords + bass + rock kit), `ambience-crowd-walla` (synth crowd murmur, decorrelated L/R), `sfx-applause` (400 filtered-noise claps + cheer swell). 🟢 100% original synthesis, $0, seamless loops, parody-safe. Flagged `gig:true` in the manifest; F plays them when a gig is on. Pack now 26 assets, 9.1 MB of 25 MB. **Gig posters (4 skins, `skins.poster`):** poster-xerox/screenprint/grunge/retro — 90s pub-rock flyer templates generated by MODELBEAST `flux_local`. **Blank-name-band discipline** (like blank-signboard facades): each has a clean top banner and *no baked text*; B/C overprint the seeded band name into `nameZone` [x,y,w,h]. Eyeballed (contact sheet in the round note) — all 4 have the blank banner, zero legible text. 🟢 original generation, portrait 768×1024 JPEG in `web/assets/gen/`. **Gig instruments (5 GLBs, MODELBEAST `flux_local` concept → `trellis_mac` → `normalize.py`):** 🟢 100% original synthesis, $0. **All 5 kept** — every one reconstructed cleanly (better than the round-3 wire rack; even the mic-stand's thin boom pole and the drum cymbal hardware survived). Held by D's band rigs; primitive fallback if a GLB is absent (house law). | instrument | tris | footprint (m) | h (m) | note | |---|---|---|---|---| | electric_guitar | 43k | 0.38×0.08 | 1.0 | sunburst solid-body, clean | | bass_guitar | 36k | 0.34×0.12 | 1.15 | black bass, clean | | drum_kit | 30k | 1.50×1.24 | 1.3 | full kit, cymbals survived | | guitar_amp | **96k** | 0.51×0.29 | 0.6 | combo amp — heaviest (grille cloth → shell-soup) | | mic_stand | 34k | 0.57×0.57 | 1.7 | chrome boom stand, thin pole held | - Same TRELLIS tri-weight tradeoff as round-3 props (30–96k tris vs the ≤5k library law) — accepted for focal/optional/LOD-able stage props seen at mid-distance; texture-bake-to-lowpoly is the follow-up. Amp (96k) is the outlier — a candidate for that pass first. Eyeballed (contact sheet). - Staged to `web/assets/models/` for local validation (`?localdepot=1`). Depot publish was **PENDING John's OK** at R12 → **DONE in R13** (see the R13 section below; the 30–96k figures in the table above are the R12 originals, decimated to 14k each in R13). ## Round 13 — gig-layer finalization (v3.0-beta, `?gigs=1`) Supersedes the R12 instrument table + pending-publish note above. Three closes, all 🟢 provenance unchanged (100% on-device synthetic / procedural — decimation and renaming don't touch the licence zone): **1. Instrument decimation (`pipeline/decimate_props.py`, Blender collapse-decimate — NOT voxel-remesh, so thin features survive).** The R12 instruments were the TRELLIS shell-soup tri-weight outlier; a district means multiple concurrent stages, so all 5 were taken to a uniform **14 000 tris** (fleet **236k → 70k, −70%**). Collapse (keeps original UVs/textures, no re-bake) held the mic boom pole + cable, drum cymbals/stands and guitar necks — verified by re-rendered thumbs. The high-poly originals are kept as `_hi` variants (published to the depot as archival source, not manifest-referenced). | instrument | R12 tris → R13 | `_hi` original | footprint (m) | h (m) | |---|---|---|---|---| | electric_guitar | 43 451 → **14 000** | 43 451 (`_hi`) | 0.38 × 0.08 | 1.00 | | bass_guitar | 36 031 → **14 000** | 36 031 (`_hi`) | 0.34 × 0.12 | 1.15 | | mic_stand | 34 079 → **14 000** | 34 079 (`_hi`) | 0.57 × 0.57 | 1.70 | | drum_kit | 26 238 → **14 000** | 26 238 (`_hi`) | 1.50 × 1.24 | 1.30 | | guitar_amp | 96 583 → **14 000** | 96 583 (`_hi`) | 0.51 × 0.29 | 0.61 | **2. gigKey rename + two new beds (debt #1).** The manifest music key IS the gigKey, always `gig-`. `music-pubrock-live` → **`gig-pubrock`**; added **`gig-grunge`** (band_room) and **`gig-covers`** (rsl, organ-led) — all procedural numpy synth, $0, seamless, parody-safe (new synth helpers `organ()` + `grungechord()`). Audio pack **26 → 28 assets**. Old `pubrock-live` files deleted. **3. Depot publish — DONE (John OK'd direct publish, R13).** Pushed via the passwordless tailnet ingress (this box is allowlisted): the 5 base names now serve **14k bytes** + the 5 `_hi` originals are live. Verified on the public `digalot.fyi` depot (amp base **5.9 MB → 1.31 MB**, `_hi` 5.9 MB live). `_published.json` **33 → 38**. `validate_manifest --depot` GREEN; Gate-3 provenance flag **cleared**. ## Round 36 wave 1 — the 2 a.m. ped assets, swept (provenance + depot regularization) `046668e` (4 dance clips) + `b339402` (**5** trellis DJ bodies — the R36 brief said 3; the diffstat says 5) landed July 20 outside the round system. R36 wave 1 swept all **9** into the pipeline; the files themselves are untouched (byte-identical to both commits and to their 90sDJsim originals). **Dance clips — zone corrected by measurement: ROKOKO, not Mixamo.** Source = Rokoko's free 263-asset mocap library (`~/Documents/Rokoko_Free_Mocap_FBX_263`, `Rokoko Studio (Mocap)/Dancing/*_mixamo.fbx`, Rokoko's own Mixamo-skeleton export), baked mesh-free by 90sDJsim `tools/bake_clips.py` (the `fbx_to_clip.py` port). Licence per the pack's rokoko.com page: royalty-free including commercial use. Structure (all 4): 0 meshes · 68 nodes · 198 tracks · 52-joint dangling skin with IBM · 0 NaN/Inf · binds 65/66 canonical targets on a shipped ped skeleton (only `mixamorigReference` unbound — dropped silently by three.js; matches 90sDJsim's in-game 64/65). Durations 30.58/30.50/44.04/52.42 s. | clip | bytes | sha1 | |---|---|---| | dance_party.glb | 688 220 | 38587f7b0cf4aaf199f55677a139db087acdd80d | | dance_medium.glb | 1 028 164 | 4a30fb62163b6e5137c8fb513dfc70d5f6f322b4 | | dance_drink.glb | 962 464 | d818ddb0868e19c87a435b26c8b6ffab14832be4 | | dance_sway.glb | 1 132 560 | 8af9112538e5219fc6a7b6cb36bf1e87e5ac24ff | **DJ bodies — on-device TRELLIS.2 mesh (CC0-equiv, the instrument zone) + Adobe Mixamo auto-rig, converted/decimated by MESHGOD `scripts/fbx_to_ped.py`.** GLB law: no Draco, 1024² webp (`EXT_texture_webp` — roster-standard), 1 mtl/1 img, 65-joint mixamorig skin, IBM 0 NaN. Ped law ≤5k: PASS. ⚠ bind-pose skeleton spans ~0.95–0.99 units vs the existing roster's ~173–180 (cm convention) — absorbed at spawn (`rigs.js:163` height/span normalize), flagged to F's R36-w2 figure-scale gate. | body | tris | bytes | sha1 | |---|---|---|---| | dj_phrtt_01.glb | 4 000 | 712 792 | 21cf0db5a291f88f7fd9b9cfdb0eac8413c7cf2e | | dj_techno_01.glb | 4 000 | 702 760 | 4cb6bf44a752f921e14d1355f9944b7859c92386 | | woman_dj_01.glb | 3 999 | 731 440 | 7d364dfc7e7659c0b15bead82a5d8bbd285e5286 | | woman_dj_02.glb | 3 999 | 715 116 | 2413ededff1a7684999d6fd6fd58417b77f25c10 | | woman_dj_03.glb | 3 998 | 718 128 | ec39692fa8019645bd200aab9eb6d1078535189c | **Depot:** staged to `_normalized/` (the sit/look practice) and published via the passwordless tailnet ingress under John's R22 standing authorization — `_published.json` **44 → 53**, drift check green, and all 9 fetched back from the public depot (`digalot.fyi/3god/a/`) sha1-identical to local. Gates after the sweep: `validate_manifest` 0 errors / 0 warnings · `publish.py --verify` ✓ 53/53 live. ## Round 38 — v8 wave 1: the asset wave (pipeline swap · audio · grounds · the mesh probe) *Everything below is measured on this box today. Where the R38 review's costed figures differ from what the hardware actually did, the measured number is given and the difference is named.* ### 1. THE PIPELINE FIX — `trellis_mac` → `trellis2_mlx`, and the output-contract trap it hides `gen_props.py` hardcoded **`trellis_mac`** (the review's farm telemetry: p50 245.4 s, 33 jobs/7 d). Swapped to **`trellis2_mlx`** (384 jobs/7 d, p50 134.6 s, better PBR bake). Operator contract read from its `manifest.json`, not assumed — `pipeline_type` / `seed` / `steps` / `baker` / `texture_size` / `max_bake_faces` / `alpha_mode`; we pass `1024_cascade`, `baker:metal`, `max_bake_faces:500000` (fal parity, M3-Ultra-verified), `alpha_mode:opaque`. **The two operators are NOT drop-in on output, and the naive swap would have shipped untextured meshes.** `trellis_mac` writes one `*.glb`; **`trellis2_mlx` writes BOTH `candidate.glb` (raw) and `candidate_pbr.glb` (baked)** into the same tree, and the old code took `sorted(rglob('*.glb'))[0]` — which is alphabetically the **raw** one. Fixed with `_pick_glb()`: trust the operator's own `result.json` first, then `candidate_pbr.glb` by name, then anything. `--legacy-trellis` keeps the old operator for an A/B. Also added the **`bg_remove_local` (RMBG-2.0) cutout stage** the review's path calls for (flux → bg_remove → trellis2 → normalize), fail-soft: if RMBG errors the raw concept is fed through and the mesh operator segments internally as before. **Measured on the 5-prop R38 batch (wall clock, mesh stage only):** 122 s · 83 s · ~108 s · ~123 s → **mean ≈ 109 s**, i.e. *faster* than the farm's 134.6 s p50 and **~2.2× the old operator's 245.4 s**. The cutout stage costs **5–6 s** per prop. **The review's ~2× throughput claim is confirmed with a real number.** Retry budget went unused: **5/5 first-attempt success, 0 failures, 0 OOM-kills.** ### 2. AUDIO — the inhabitation layer (item 1.1). 6 new PACK entries, $0, 🟢 original Same doctrine as R11/R12 and for the same reasons: **100% procedural numpy synthesis**, no neural model, no sample library, no third-party recording — **CC0-equivalent original work, parody-safe by construction** (every waveform is an oscillator or a noise render, so there is nothing to imitate). Deterministic through `crc32(name)` → the same dog is the same dog on every boot. | asset | cat | dur | ogg | m4a | synthesis | |---|---|---|---|---|---| | `sfx-dog-bark-fence` | sfx | 3.2 s | 16 KB | 37 KB | 8 barks: pitch-dropping saw through 2 vocal-tract formants + attack noise; 2.2 kHz lid + a 28 ms paling slap-back + yard reverb 🟢 | | `sfx-magpie-carol` | sfx | 2.6 s | 28 KB | 42 KB | fluted glides under a 28–52 Hz quaver, **every note doubled a fifth up** (the syrinx has two sides — that is why a carol sounds like two birds) 🟢 | | `sfx-roller-door` | sfx | 2.8 s | 24 KB | 45 KB | slow-fast-slow clatter train + sheet-metal drum + panel modes **amplitude-followed** off the clatter + the stop 🟢 | | `ambience-mower-distant` | ambience | 12.0 s | 78 KB | 190 KB | firing pulse train w/ load wander, blade hiss, 900 Hz lid. Loop **genuinely folds**: every LFO is a harmonic of 1/12 s and the firing rate is a whole number of cycles per loop 🟢 | | `ambience-radio-through-window` | ambience | 8.6 s | 82 KB | 136 KB | an **original** 4-bar turnaround (D A G E) → 380–2600 Hz little-speaker band → 1.5 kHz glass lid 🟢 | | `ambience-sprinkler` | ambience | 8.0 s | 53 KB | 127 KB | impulse arm ticking across a 62% arc then swinging back silent, under a sweeping spray hiss 🟢 | **Budget: +281 KB ogg / +577 KB m4a = +0.86 MB.** Pack 10 MB → **10.9 MB against the 25 MB cap**, which matches the charter's ~11 MB estimate. **Zero draws, zero tris** — as chartered, this item cannot touch the render budget under any circumstance. **Verified** (I synthesise; I cannot subjectively listen): spectrogram sheet `docs/shots/laneE/inhabitation_spectrograms.png` + a numeric pass — peaks **0.33–0.71** (no clip), loop wrap-jump **≤ 0.18× the 95th-percentile internal step** on all three beds (radio 0.00×, mower 0.08×), and band-energy fractions that match the intent: mower **99% below 1 kHz**, radio **87%/13%/0%** across <1k/1–4k/>4k, carol **86% in 1–4 kHz**, bark **89% below 1 kHz**. **One defect found and fixed by looking**: at spray gain 0.5 the sprinkler's hiss masked its own ticks and it read as a hose — the tick *is* the sprinkler. Rebalanced to 0.28 and re-rendered; the ticks and the silent swing-back are now visible in the sheet. **Final subjective ear-check is John's / F's** — flagged honestly, same as R11. ### 3. GROUND SKINS — the per-town palette (item 1.2). 4 new, `flux_local`, $0, 🟢 `gen_skins.py` had **no GROUND template at all** — the ten shipped grounds predate it. Added one, plus a `--grounds` mode and a dedicated `GROUND_STYLE` prefix: the house `STYLE` bible says *"warm fluorescent lighting"*, which is an **interior** cue and wrong for an outdoor overhead surface. | skin | use | px | bytes | note | |---|---|---|---|---| | `ground-grass-dry` | yard | 512² | 157 801 | straw-blond patchy summer lawn, bare soil through it 🟢 | | `ground-grass-temperate` | yard | 512² | 183 917 | dense damp deep-green lawn + clover 🟢 | | `ground-coastal-sand` | yard | 512² | 131 231 | pale wind-rippled sand + shell grit 🟢 | | `ground-bitumen-reddust` | road | — | — | **re-rolled, see below** | **Cost correction.** The review costs `gen_skins.py --local` at **5.7 s**; measured here at **~9.7 s each**. That is not a regression — the review's figure is for the 1024×576 facade geometry and these are **1024² square tiles, 1.78× the pixels**. Per-pixel the two agree. Square is the deliberate choice: grounds tile through `RepeatWrapping`, and the two `djsim-*` grounds (the only 512×512 ones) are the correct precedent — the eight flux-era grounds are **512×279** and stretch under a square UV tile. That is a pre-existing defect, recorded not fixed. ### 4. MESHES — the proof batch (items 1.3 + 1.4), `flux_local` → `bg_remove_local` → `trellis2_mlx` **Licence: 🟢 CC0-equivalent** under `AUDIT.md:101`'s standing doctrine — a `flux_local` concept on an Apache-2.0 ungated model (FLUX.2-klein-4B), reconstructed by TRELLIS.2 into original geometry. Nothing downloaded, nothing pooled. **The 3GOD depot was not read as a pool** (971 live, 53 recorded, known trademarked items inside — it stays a read-only archive) and **the hardyards GLBs are REJECTED on provenance**, as the round ruled: 39 GLBs with provenance documented for one file. Every one of these five is ours. **Operator timings, all five, wall clock (includes the 5–6 s RMBG cutout):** | prop | trellis2_mlx | raw tris | outcome | |---|---|---|---| | magpie | **129.0 s** | 473 513 | ✅ shipped 894 tris | | hills-hoist | **88.1 s** | 503 716 | ❌ **thin-structure failure** | | paling-fence-panel | **112.3 s** | 479 611 | ✅ shipped 1 121 tris | | tv-aerial | **128.0 s** | 494 204 | ❌ **thin-structure failure** | | water-tank | **489.0 s** | 475 047 | ✅ shipped 500 tris | **median 128.0 s** — inside the farm's 134.6 s p50 and **1.92× faster than `trellis_mac`'s 245.4 s**. Mean is 189.3 s, dragged by one 489 s outlier (3.8× the median, same params, same box, no error) — worth naming: **the operator's tail is long even when nothing fails.** 5/5 first-attempt success, **0 retries used** against the 4–5% budget, 0 OOM-kills. #### 4a. SHIPPED — three assets, all GLB-law clean House law verified on every one: metres · +Y up · base origin · **1 material / 1 image / 512² WebP** · no Draco. | id | tris | footprint (m) | height (m) | notes | |---|---|---|---|---| | **magpie** | **894** | 0.15 × 0.40 | 0.358 | the 1.3 asset. Silhouette excellent at 894 — beak, legs, folded wing and tail all read. **Albedo flagged, see 4c.** 🟢 | | **paling_fence** | 1 121 | 0.28 × 3.59 | 1.788 | weathered grey palings with gaps + two rails. Overshot its 400 target (see 4b) 🟢 | | **water_tank** | **500** | 1.73 × 1.64 | 2.186 | corrugated galvanised tank, domed lid, outlet tap. **Cleanest asset in the batch** 🟢 | Contact sheet (4 angles each): `docs/shots/laneE/r38_props_shipped.png`. **⚠ NOT PUBLISHED — one step outstanding.** All three are normalized, baked to budget and staged in `pipeline/_normalized/`, but they are **not on the 3GOD depot** and therefore deliberately **not in `manifest.json`**: pushing to a public depot is an outward-facing action and this lane had no in-session authorization for it. `validate_manifest.py` correctly fails any manifest GLB with no `_published.json` record, so the results file is held at **`pipeline/_r38_results.PENDING_PUBLISH.json`**. `build_manifest.py` is already wired to `_r38_results.json`: **publish, rename, re-run** and the three land. The gate is green as committed (0 errors, 0 warnings). #### 4b. THE DECIMATION FLOOR — a real finding, not a tuning miss **`normalize.py`'s collapse decimator cannot reach small budgets on TRELLIS output**, and nobody had ever asked it for ≤900 before. Measured, all five: 470 442→**13 849** (target 900) · 478 236→**250 068** (600) · 476 974→**18 791** (400) · 485 565→**89 306** (400) · 475 047→**70 893** (500). The cause is the documented shell-soup floor — collapse cannot merge across disconnected shells, so the floor *is* the shell count. `bake_lowpoly.py` (R5's voxel-remesh → decimate → Cycles bake) is the correct tool and clears it: magpie **894**, water_tank **500**, tv_aerial 299, hills_hoist 496. paling_fence floored at **1 121** against a 400 target — a flat panel of separate palings has an irreducible shell count at voxel 0.012; it is shipped at 1 121 rather than remeshed coarser, because coarser merges the palings into a slab and the gaps are the whole point of a paling fence. #### 4c. THE TWO FAILURES — thin structures, exactly as the review predicted **`hills_hoist` and `tv_aerial` FAIL and are NOT shipped.** Both were deleted from `_normalized/` rather than left where a later manifest build could catalogue them. Evidence: `docs/shots/laneE/r38_thin_structure_failure.png`. TRELLIS reconstructs the *solid* parts well — the hoist's pole, concrete footing and four arms are all there at 250 k tris, and it is recognisably a Hills Hoist. **The wire is what dies**: it comes back as thousands of tiny disconnected fragments, which is simultaneously why the mesh reads as frayed *and* why it will not decimate. And **both escape routes fail in opposite directions**: collapse floors at 250 k / 89 k with the fraying intact, and a voxel remesh coarse enough to reach 600 tris **erases the pole entirely** — the 496-tri hoist is a floating horizontal smear with no upright, and the 299-tri aerial is scattered debris. There is no voxel size that keeps a 30 mm pole and a 2 mm wire at 600 tris. **This is a DECISIVE capability answer, and it is the one the round asked for.** Thin-structure props are not a TRELLIS class. **→ Lane B: use primitives for the hoist and the aerial**, which is the `boxMats` path anyway and costs **+0 draws** — strictly better than the GLB would have been. #### 4d. THE ALBEDO PROBE — TRELLIS crushes high-contrast markings, and the input is exonerated The magpie came back as an **all-black bird — a crow, not a magpie**. Traced properly rather than guessed at, because the fix differs completely depending on where it happened: 1. **The input is provably perfect.** The `bg_remove_local` cutout fed to TRELLIS has a bright white nape, white wing patches, a white tail tip and a pale blue-grey beak. Not the concept's fault, not RMBG's. 2. **The raw 500 k TRELLIS mesh has already lost it** (`docs/shots/laneE/r38_magpie_albedo_probe.png`): the white nape survives only as a pale blue-grey wash and the wing bar and tail tip are gone. Its 1024² albedo atlas is near-uniformly black with sparse pale slivers. **So the loss is the operator's PBR bake — our normalize/decimate/re-bake chain is exonerated.** 3. **The documented workaround does not work through our normalizer.** The manifest advertises `baker: "vertex"` as "the 1s clean-albedo bake, cleanest colors". Re-ran the magpie with it (159 s): the GLB comes back with **0 materials and 0 images** — colour lives only in vertex attributes, and `normalize.py` (join → decimate → re-export) drops them. The result renders **pure white**. `baker: vertex` is unusable here without normalizer work. **Shipped anyway, flagged honestly**, because the silhouette is what a swooping bird reads as at street distance and 1.3 cannot ship without a mesh. **Fix path for a later round** (stated, not done): bake the LP's albedo from the *concept image* rather than from the reconstructed mesh, or hand-tint the nape/wing/tail regions of the 512² baked atlas. **Both are cheap; neither is this round's job.** ### 5. REUSE BEFORE GENERATING — the review's nine, re-measured, and its "one line each" corrected All nine confirmed idle and re-measured off the bytes. **But "one line each" is wrong for four of them**, and the reason is in `furniture.js`'s own merge contract: `extractGLB` merges every mesh into ONE geometry and **keeps only the FIRST material**. So a multi-material GLB does not fail — it renders **entirely in material #1**, silently. | id | tris | mtl | verdict | |---|---|---|---| | `novelty_record` | **8 000** | 1 | ✅ true one-liner. `furniture.js:275` already calls `glb('novelty_record',…)`; `GLB_FILES` has no key, so the call can never fire. **The blocking comment at `furniture.js:30` says "26.5k tris" and is STALE** — Lane E baked it to 8 000 in R5. | | `streetlight` | **4 999** | 1 | ⚠️ **decimate first.** It is placed every ~18 m — at ~50 instances that is ~250 k tris on a 200 k budget. The review is right that it needs `decimate_props.py` to ~800 before it is wired. | | `bench_modern` | **640** | 1 | ✅ true one-liner | | `longbench` | **1 172** | 1 | ✅ true one-liner. 5.86 m — the arcade/market feature bench | | `park_bench` | **5 000** | **2** | ⚠️ renders in material #1 only | | `bench_wood` | **3 999** | **0** | ⚠️ **zero materials** → falls to `extractGLB`'s grey `0x888888` default | | `coffee_table` | **495** | **2** | ⚠️ material #1 only | | `box_crate` | **78** | **3** | ⚠️ material #1 only | | `counter` (balcao) | **276** | 1 | ✅ 1.06 × 4.02 m, 1.10 m — exactly the `bar_counter` fitting still primitive-only in all three venue types. Different loader (`interiors/glb.js`), not `furniture.js`. | **Nothing was re-generated that already existed.** No duplicate of any of these was made this round. ### 6. LICENCE — every R38 asset lands 🟢 | class | count | zone | method | |---|---|---|---| | audio | 6 | **🟢 original by construction** | pure numpy synthesis, `gen_audio.py`, deterministic via `crc32(name)` | | ground skins | 4 | **🟢 original** | `flux_local` / FLUX.2-klein-4B (Apache-2.0, ungated), on-device | | meshes | 3 shipped | **🟢 CC0-equivalent** (`AUDIT.md:101` doctrine) | `flux_local` concept → `bg_remove_local` (RMBG-2.0) → `trellis2_mlx` (TRELLIS.2-4B) → `normalize.py` → `bake_lowpoly.py` | **The 3GOD depot was NOT used as a pool** — it stays a read-only archive (971 live, 53 recorded, known trademarked items inside). **The hardyards set stays REJECTED on provenance** (39 GLBs, provenance documented for one file); every mesh here is ours. **$0 spent — no cloud key touched.**