PROCITY/pipeline/AUDIT.md
m3ultra 55f6698fb2 Lane E R14 (v3.0 release): provenance freeze + doc-consistency fixes
Provenance close-out for the v3.0 tag. Verified via a 6-agent inventory+audit workflow
(both adversarial audits PASS, 0 blockers) cross-checked against glb_stat/ffprobe/manifest.

- LANE_E_NOTES: v3.0-FROZEN provenance table + full audio(28)/GLB(38)/skins(96)/packs(3)
  inventories; by-design notes + v3.1 deferrals (street_bin decimation).
- AUDIT.md: add R13 gig-layer finalization section (14k decimation, _hi published, gigKey
  rename + 2 beds, depot publish done) — ledger was stale at R12.
- manifest glb_law string: state the documented <=8k-hero / <=14k-instrument tiers so the
  frozen contract is self-consistent (regenerated; 1-line diff, Gate 3 still 0/0).
- gen_audio: roomtone-video gain 0.3 -> 0.35 to match the shipped manifest.

Gate 3 GREEN; publish.py --verify clean; 0 quarantine assets in manifest; goldens unmoved
(synthetic 0x3fa36874, gig 0x1f636349). No new synthesis (genres final); no poster templates
(no ask from B).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 01:18:40 +10:00

484 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_<id>_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 ~1417¢/gen: ~$0.280.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 (**2577k tris, ~0.33.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 **~31k56k 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
15642805, 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 2577k 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 **25k77k 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 **19 primitives / 13 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_<kind>_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** ~150250-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 <k> --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 (~6085 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 | ~3040 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 MB1 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**: 48× 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 (3096k 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 3096k 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-<genreKey>`. `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**.