PROCITY/E-progress.md
m3ultra 7795780336 Lane E R12 (v3.0-alpha gig layer): 5 instruments + 4 posters + 3 gig audio
Instruments (MODELBEAST flux_local concept -> trellis_mac -> normalize):
electric_guitar, bass_guitar, drum_kit, guitar_amp, mic_stand -> manifest
fittings + thumbs, published to the tailnet depot. All 5 reconstructed
cleanly (thin mic-boom + cymbals survived); heavy tris (30-96k, amp the
outlier) accepted like the R3 props. gen_props.py gains --batch;
gig_instruments.json + gig_norm_batch.json + _gig_results.json.

Posters (flux_local, gen_skins.py --posters): poster-xerox/screenprint/
grunge/retro -> skins.poster with nameZone for band-name overprint, blank
name-band (no baked text) like blank-signboard facades.

Gig audio (procedural, gen_audio.py): music-pubrock-live (original 90s
pub-rock riff), ambience-crowd-walla, sfx-applause -> manifest audio,
flagged gig:true for F. Pack now 26 assets, 9.1 MB of 25.

build_manifest reads _gig_results.json + emits skins.poster + gig audio;
validate_manifest green (--depot 0 err, all live). Provenance in AUDIT.md
(section Round 12); README + E-progress updated. Touched no other lane's
files. F owns the v3.0-alpha tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:44:48 +10:00

347 lines
26 KiB
Markdown
Raw Permalink 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.

# LANE E — ASSETS · progress (PROCITY-E)
*Owner: PROCITY-E. Scope: `pipeline/*`, `web/assets/*`. Nothing in other lanes' files was touched.*
## Round 12 (v3.0-alpha gig layer, `?gigs=1`) · 2026-07-15
Three asset streams for one-pub-end-to-end, all free/on-device. C works the pub interior in parallel
(I touched none of `web/js/interiors/*`).
- **Instruments (5 GLBs)** — `flux_local` concept → `trellis_mac``normalize.py`, via
`gen_props.py --batch gig_instruments.json`. **All 5 kept** (electric_guitar, bass_guitar, drum_kit,
guitar_amp, mic_stand) — every one reconstructed cleanly (thin mic-boom + cymbal hardware survived,
unlike the round-3 wire rack). Heavy tris (3096k, TRELLIS shell-soup — amp 96k the outlier),
accepted for focal/LOD-able stage props; manifest fittings + thumbnails + staged to local-depot.
D's rigs hold them; primitive fallback if absent.
- **Posters (4 skins)** — `gen_skins.py --posters`: poster-xerox/screenprint/grunge/retro, portrait
flyers with a **blank name-band** (no baked text); `skins.poster[].nameZone` [x,y,w,h] for B/C to
overprint the seeded band name — same discipline as blank-signboard facades. Eyeballed, zero rejects.
- **Gig audio (3)** — extended `gen_audio.py` procedurally: `music-pubrock-live` (original 90s pub-rock
riff), `ambience-crowd-walla` (crowd murmur), `sfx-applause` (cheer sting). All `gig:true` in the
manifest for F. Pack now 26 assets, 9.1 MB of 25. Verified (spectrogram + seams/peaks).
- **Manifest**: `skins.poster` + audio gig assets + 5 instrument fittings; `build_manifest.py` reads
`_gig_results.json`; `validate_manifest.py` covers audio. Provenance in `AUDIT.md` (§ Round 12).
- **Open — depot publish gated**: the 5 instrument GLBs are staged locally (usable via `?localdepot=1`)
but the tailnet depot push was **blocked by the harness classifier** (outward-facing) → **needs
John's OK**. Until then `validate --depot` / qa Gate 3 flag provenance-drift on the 5. F owns the
v3.0-alpha tag; I did not commit yet pending the publish call.
## Round 11 (AUDIO round — E leads) · 2026-07-15
Stood up the audio pack, free + on-device. **Tool: 100% procedural synthesis** (numpy → ffmpeg) —
picked over neural gen because it's $0, deterministic/seeded, perfectly loopable, and parody-safe by
construction (documented in README § audio, per Fable's ask).
- **`pipeline/gen_audio.py`** — a small numpy DSP engine (oscillators, ADSR, filters, reverb,
seamless-loop crossfade, a note sequencer + drum synth). 23 assets, seeded, `--dry-run`/`--only`.
- **`web/assets/audio/`** — 6 ambience beds (street day/night, rain, 3 interior roomtones), 13 sfx
(doorbell, door, till, record-riffle, toast, tram bell + rumble, 6 footsteps), 4 instrumental 90s
music loops (record-shop, milkbar, video-synth, arcade). OGG/Opus + M4A/AAC. **8.1 MB** of 25 MB.
- **Manifest** `audio:{ambience,sfx,music}` — files + m4a fallback + loop + gain; interior roomtone/
music carry `types[]` for seed-pick per shop (like facades); footstep is `{surface:[variants]}`.
`build_manifest.py` builds it, `validate_manifest.py` checks it. **qa green, audio 23, 0 errors.**
- **Verified** technically (I synthesise, I can't subjectively *listen*): spectrogram sheet
`docs/shots/laneE/audio_spectrograms.png` + numeric — peaks ≤ -0.4 dBFS (no clip), **all loops
seamless**, durations correct. **Subjective ear-check is John's/F's** (flagged honestly).
- Provenance in `AUDIT.md` (§ Round 11). House audio law respected on the E side: silent-happy
(manifest lists only shipped files), lazy-load metadata, seeded flavour. B wires WebAudio, C gives
the `room.audio` contract, F integrates + owns the **v2.1 tag/push** — I did **not** push.
---
*Round 3 and earlier below.*
*Reviewed against `docs/LANES/LANE_E_ASSETS.md` + `docs/LANES/ROUND3_INSTRUCTIONS.md`.*
## Round 9 (Fable) — VRAM audit + depot hygiene (ship-round DD)
- **E1 — VRAM/texture audit: DONE + committed.** Measured every texture source (decoded RGBA+mips).
Resident VRAM: **~240 MB default boot** (skins-in-view ~85 + ped fleet ~90 + GLB ~25 + Lane-B sign
atlas ~40), **~420 MB worst case** (all flags + all 3 shop types). **Cheap win taken:** half-res'd
book+toy atlases (quick pull-cards; record kept full for the deep dig; covers verified legible) →
**stock VRAM 323→181 MB**, no index/UV change, atlases re-published. **Verdict: fine to ship v2.0**
— no compression/lazy-load needed for the tag. Flagged for v3: stockpack cache-cap (C) + KTX2/Basis.
- **E2 — depot/manifest hygiene: confirmed green.** `validate --depot` 0 err · pack-QA 3/3 · provenance
-drift clean (`_published.json` == 27 live) · 15 stock atlases live, **0 orphans**.
- **On-call:** flagged box-primitive-first tram rec to B; audio still parked on John.
## Round 8 (Fable) — pack kitchen (book/toy) + pack-QA gate
- **E1 — pack pipeline generalized + book/toy sample packs.** `build_stock_pack.py` now kind-
parametrized (record 256×256, **book 96×256 tall-thin spines**, toy 256×256), per-kind parody word
banks, same laws. Book/toy sample packs built + staged (`?localdepot`) + indexes committed to
`pipeline/packs/` so **Lane C wires book/toy shelves now**. Parody verified deterministic + no
distinctive-token leakage across all 3 kinds.
- **E2 — pack-QA gate: DONE.** `validate_pack.py` (wired into `validate_manifest.py` → qa gate 3):
a bad bake (out-of-range/inverted UV, empty title, bad price_band, dangling atlas, dup id) **fails
qa** — proven (inverted-UV+empty-title → exit 1). qa `--strict` GREEN 5/5.
- **Tags pushed** (John-delegated): origin/main = HEAD (1a20501) + v1.0/v1.1/v2.0-alpha/v2.0-beta all
live on origin — verified.
- **REAL packs LIVE (John unblocked DB access).** All 3 packs rebuilt from GODVERSE (m1ultra
Postgres clones, read-only SELECT, peer auth): **record 350** (`discogs_full.inventory` — John's
real store stock + real Discogs covers), **book 311** + **toy 273** (`dealgod.products` by
category, real product photos + prices). Metadata parody-transformed (no real trademarks); covers
are real art (decision #2); book prices seeded (info-only source). 15 atlases published to the
depot, indexes committed to `pipeline/packs/` + staged, all pass the pack-QA gate. `build_stock_pack.py`
gained `--from-tsv` (extract on the DB box → build here) + portrait book cell + seeded-price
fallback. Access path: deployment DB was Docker-internal/SSH-denied → used the m1ultra clones per
John. No fal spend.
- **On-call** (B rain-streak/puddle, D door-swing tell): none requested.
## Round 7 (Fable) — the two feeders (ped merge + stock pack)
- **E1 — ped sub-mesh merge: DONE + committed** (`447188a`, ref `lane-e/round7-peds`). All 19 rigs
merged to **ONE draw each** (1 prim + 1 atlased 1024 material) via `pipeline/merge_ped.py` (Blender
join → UV-pack → EMIT-bake all materials into one atlas). Skinning + 65-joint skeleton preserved,
`walk`/`idle` untouched. **Fleet 92→19 draws; near-cap 116→24 (79% cut).** Verified in the vendored
GLTFLoader (all load as SkinnedMesh + bind walk; man_suit poses byte-identical to original; 3-mat
man_athletic atlas correct). Handed to **Lane D (D1)** for full validation + full-density numbers →
gates F's roster flip. qa `--strict` GREEN 5/5.
- **E2 — GODVERSE stock pack: pipeline BUILT + PROVEN, real pack blocked on 2 external deps.**
`pipeline/build_stock_pack.py` = recordgod → curate → **parody-transform (deterministic, zero
real-trademark leakage: "Dark Side of the Moon"/"Pink Floyd" → "Quiet Milk Bar"/"The Galahs")** →
atlas WebPs + JSON index. Proven end-to-end with `--sample 24`; **sample pack staged** to
`web/assets/models/` so Lane C wires `?stock=real` against the real index format now. **Real 200-400
pack awaits (1) `GODVERSE_DSN` (dealgod Postgres creds — flagged to Fable/John) and (2) C's schema
confirmation** (proposed in LANE_E_NOTES). One command (`--from-db --count 300`) once they land.
- **E3 — on-call** (B rain-streak/puddle texture): none requested. No fal spend (balance ~$21.36).
## Round 6 (Fable) — non-destructive pipeline + on-call
- **E1 — publish.py non-destructive + provenance-drift gate: DONE + committed.**
- `publish.py` now **merges** into `_published.json` (read existing → union → sorted write),
never replaces — fixes the R5 `--only` clobber (record dropped 27→1). **Proven:** a real no-op
re-publish of `record_crate` kept the record at **27 (was 27)**, not 1.
- Post-publish + a new `--verify` mode **drift-check** the record against the live depot
(`/api/list` filtered to `procity_*`) and warn on either direction. `--verify` confirms 27 == 27.
- `validate_manifest.py` gains a **provenance-drift gate**: every manifest `procity_*` GLB must be
in `_published.json`, else QA fails. **Proven:** simulating the R5 clobber (record→1) makes the
validator exit 1 with per-file drift errors; restored → green. So the clobber class fails loudly.
- **Record verified**: `_published.json` (27) is byte-exact vs the live depot — Fable's R5
regeneration was correct, no fix needed.
- `qa.sh --strict` GREEN (5 gates incl. F1's new v2-flags harness).
- **On-call for B/C** (task 3): available for bus_shelter/bin scale-origin tweaks (B) and
glass_case/magazine_rack in-room issues (C). Broken-prop fal re-gen pre-authorized ≤2 attempts
(decision #5) — none needed yet. No speculative generation.
## Round 5 (Fable) — hero-prop bake + fal fallbacks + depot growth
- **E1 — hero-prop bake-to-lowpoly: DONE + committed.** New `pipeline/bake_lowpoly.py` (Blender
headless): voxel-remesh → decimate → **Cycles base-colour bake** onto fresh UVs (R3's lesson:
simplify-only floors on shell-soup; the re-bake is the point). All **6 heavy TRELLIS props ≤8k
tris** (drinks_fridge 77k→8k, milkshake_mixer 73k→8k, listening_booth 54k→8k, counter_till
32k→8k, novelty_record 26k→8k, arcade_cabinet 25k→8k). **Fleet 288k→48k tris, 18M→5.2M.** Each
eyeballed (`docs/shots/laneE/props_baked.png` — fridge needed a coarser voxel re-bake, rest clean)
+ verified loading in the vendored GLTFLoader. Re-published; `validate --depot` 0 err. Originals
gitignored in `pipeline/.props_orig/`. → Lane C re-measures the record interior (was ~273k).
- **E3 — fal.ai fallbacks: DONE.** glass_case + bus_shelter + magazine_rack via fal Hunyuan3D v2
(`pipeline/gen_props_fal.py`, key from MODELBEAST settings). Frosted/opaque concepts (`flux_local`)
gave the reconstructor a surface — all 3 succeeded where TRELLIS failed. Baked to ≤8k (EMIT albedo
bake — Hunyuan materials need base-colour→emission or DIFFUSE bakes near-black), verified loading,
published, `validate --depot` 0 err. **Total fal spend ~$0.64** (glass 1 att, bus 2 att, mag 1
att × ~$0.16), well under the <$1 estimate; guardrail held. Costs logged in AUDIT R5. Last two
primitive-only fittings (glass_case, magazine_rack) retired; new street `bus_shelter`. Manifest
now 17 fittings + 9 furniture (26 GLBs). Contact sheet `docs/shots/laneE/props_fal.png`.
- **E2 — depot growth: DONE (light, consumer-driven).** The v2 features are procedural (dig uses
generated sleeves, B's window shader + D's roster need no GLBs), so per Fable "no unconsumed
entries" I added two clear-consumer props: `street/bin` (council-bin → Lane B) + `fit/crate_stack`
(milk-crate-stack → Lane C floor piles/dig). Both 3k tris, re-normalized + published, verified
loading, `validate --depot` 0 err. Also **resolved Lane B's B3 ask** — novelty_record 26.5k→8k
via the E1 bake. Manifest now **18 fittings + 10 furniture (28 GLBs)**. Further growth on request.
## Round 4 (Fable) — ped decimation (gate-3 critical path) + asset fixes
- **E1 — ped decimation: DONE + committed** (`518678d`, ref `lane-e/round4-peds`). The 19 ped rigs
in `web/models/peds/` were **924k tris total** (31k56k each) — the gate-3 blocker. Decimated to
**≤3k each** (15642805; fleet **924k→46.5k**, ~2.45k avg) via `gltf-transform weld → simplify
--ratio 0.05 --error 0.12` (meshoptimizer — works on these manifold skinned meshes, unlike R3
TRELLIS shell-soup). **Skinning + skeleton fully preserved** (65 joints intact, `walk`/`idle`
untouched); contact sheet `docs/shots/laneE/peds_decimated.png` clean at street distance. Originals
gitignored in `pipeline/.peds_orig/`; per-ped in `pipeline/_peds_decim.json`. → **Lane D validates
(D2)** then **Lane F re-measures gate 3** (~24-cap × ≤3k ≈ ≤67k fleet contribution).
- **E2a — `record_crate` FIXED, verified + published.** Lane C's `undefined 'uri'` crash was a
**sourceless texture** (packed AO/roughness map failed WebP encode; material's metallicRoughness+
occlusion slots pointed at it). New tool `pipeline/fix_glb_textures.py` strips those slots (three.js
loads textures lazily → no crash). **Verified it loads in the real vendored `GLTFLoader`** (meshes=1,
tris=5000). **Re-published to the depot** (the fixed version is live, tex refs all-sourced).
- **E2b — counter-with-till (~2 m): LIVE.** New `counter_till` fitting
(`procity_fit_counter_till_01.glb`, **1.61 × 0.64 m, 1.15 m tall**, 32k tris) — timber counter +
cabinet doors + period till. Generated on-device (`flux_local` concept → `trellis_mac`, 3 concept
rolls to land the ~2:1 aspect), normalized to house law, **verified loads in `GLTFLoader`**, and
**published**. Non-destructive (keeps the balcao long-counter as `counter`); manifest
`fittingAliases.counter``counter_till`; Lane C re-maps kind `counter``counter_till` in `glb.js`.
- **Publish: DONE** (John approved). Both re-exports live; **manifest `validate --depot` GREEN
(0 err, 23 GLBs live)**. `publish.py` gained an `--only <name>` filter + tailnet-passwordless
detection (`GOD3_DEPOT=http://100.94.195.115:8788`, decision #4).
- **Not doing** (per Fable decisions): fal.ai spend (glass-case/bus-shelter parked); hero-prop
bake-to-lowpoly.
## Round 3 (Fable) — hero props on MODELBEAST + local-depot mode
- **Hero props, free & on-device** (`pipeline/gen_props.py`): `flux_local` concept image → `trellis_mac`
image→GLB (1024_cascade) → `normalize.py` → thumbs → manifest. **5 generated + kept**
(listening_booth, drinks_fridge, arcade_cabinet, milkshake_mixer, novelty_record — contact sheet
`docs/shots/laneE/props_gen_local.png`), **cash_register reused** from the depot
(`vintage-cash-register.glb`). 3 didn't make it: **magazine_rack** → in-engine primitive (112k-tri
wire, the thin-wire gotcha); **glass_case + bus_shelter****fal.ai fallback list** (TRELLIS can't
do transparent glass; sf3d gives a featureless blob) — gated on John, ≤$2.70, both have primitive
fallbacks. Every kept thumbnail eyeballed; no legible branding (see AUDIT provenance).
- **Local-depot mode** (`core/loaders.js` `?localdepot=1` + `pipeline/stage_local_depot.py`): resolves
`depot:<file>` to `web/assets/models/` served by the dev server, so Lanes B/C/F validate GLB loading
**with no network / before publish**. Verified live in-browser (loads a `depot:` GLB, CDN path intact).
- **Manifest**: 14 fittings + 8 furniture (5 hero + cash_register added); `validate_manifest.py`
green (0 err). Provenance recorded in `AUDIT.md` (props are 100% synthetic/local — cleanest in the set).
- **Known tradeoff**: TRELLIS meshes are dense shell-soup — decimate floors 2577k tris (heavier than
the ≤5k library law). Accepted for focal/optional/LOD-able props; texture-bake-to-lowpoly is a follow-up.
- **Published (self-serve, John's go 2026-07-14)**: the 5 prop GLBs + thumbs pushed to the depot via
the passwordless tailnet path (decision #4); all 22 GLBs re-verified live. **`qa.sh --strict` GREEN**
(4/4 gates; manifest validator 0 err / 0 warn with the props live). Provenance was recorded first.
- **Not committed**: this round is **held for Fable review** (per John) — all Lane E round-3 changes are
in the working tree, uncommitted. GLB binaries stay git-ignored/staged; the depot has them.
## Status at a glance
| Step | Deliverable | State |
|---|---|---|
| 1 Audit | `pipeline/AUDIT.md` (+ `glb_stat.py`, `_audit_stats.json`) | ✅ done |
| 2 Normalize | `pipeline/normalize.py`**16 GLBs** + 16 thumbs, verified | ✅ done (local); ⛔ **publish gated on `GOD3_PW`** |
| 3 Manifest | `web/assets/manifest.json` + `validate_manifest.py` (+ `build_manifest.py`) | ✅ done, validates green |
| 4 Facade skins | `gen_skins.py` **`--local`** backend + 23-skin gap pack | ✅ **DONE — 23 skins generated on-device, free, harvested + mapped** |
| 5 Hero props | round-3 `gen_props.py` (MODELBEAST, free) → **5 local** + cash-register reuse | ✅ **DONE local** (see Round 3 above; 2→fal, 1→primitive) |
| 6 Local-depot | `loaders.js ?localdepot=1` + `stage_local_depot.py` | ✅ **DONE, verified in-browser** |
| — | `pipeline/README.md` + contact sheets in `docs/shots/laneE/` | ✅ done |
**Step 4 no longer costs anything.** The 23 facades were generated **locally on the M3 Ultra**
via MODELBEAST `flux_local` (FLUX.2-klein-4B on MPS) — free, ~5.7s each, weights already cached.
No OpenRouter key, no ~9¢ spend. See "Step 4 (done, local)" below.
**Two things still need John (both optional — the game runs asset-free):** (1) the depot password
`GOD3_PW` to publish the 16 GLBs live; (2) a go/no-go on the paid **hero-prop** step (5 ≈ $2.70).
## What landed
### Step 1 — Audit
- `pipeline/glb_stat.py`: dependency-free GLB parser (tris, bbox, texture px, Draco) — runs on
ultra's py3.9. Full library stats → `pipeline/_audit_stats.json`.
- `pipeline/AUDIT.md`: the library is a raw dump — almost nothing obeys the GLB law (only
`record_crate_plastic_01` was native-metres; scale/orientation arbitrary; tris 12→960k; textures
to 4096; several **branded/trademarked** assets quarantined). Candidate→PROCITY-id table with
status, coverage-vs-registry table, QUARANTINE list, depot cross-reference.
- Toolchain reality documented (Blender present on ultra but off PATH; no coreutils/timeout).
### Step 2 — Normalize & publish
- `pipeline/normalize.py`: headless Blender, extends `MESHGOD/finish_glb.py` (solidify/decimate/
bright/base-origin) + adds **height-normalize, outlier cleanup, texture→WebP≤1024, per-asset
up/yaw, 256px thumbnail**. Driven by `pipeline/batch.json`.
- **16 assets** normalized to house law (metres, +Y up, base origin, ≤5k tris, WebP ≤1024, no
Draco). Every one **eyeballed** — Y-up confirmed by re-importing exports through `render_glb.py`.
Footprints from Blender-space `_normalize_results.json`. Thumbs in `web/assets/thumbs/`.
- fittings (9): record_crate, wire_shelf, clothes_rack, bookshelf, cube_shelf_wide, counter,
coffee_table, work_table, box_crate
- furniture (7): bench, bench_modern, bench_wood, longbench, park_bench, streetlight, food_cart
- `pipeline/publish.py`: mirrors the meshgod→3GOD contract exactly (cookie `hmac(GOD3_PW,"3god-ok")`,
`POST /api/upload` + `/api/thumb`). Reads the secret from env — never contains/logs it. `--dry-run`
verified all 16 ready. **Not published yet: `GOD3_PW` isn't set here.** Run: `export GOD3_PW=… &&
python3 pipeline/publish.py` (idempotent, reversible).
### Step 3 — Manifest
- `web/assets/manifest.json` (v1): the contract Lanes B/C/D read. All **69 skins** catalogued
(25 facade / 10 sky / 10 ground / 8 wallpaper / 6 floor / 7 surface / 3 awning) + 16 fittings/
furniture with **footprints, heights, thumbs**. `conventions` block documents the GLB law,
blank-signboard rule, corner-lot policy, fallbacks, and depot load path.
- Every registry shop type has **≥2 facades** (verified in the validator). Facades tagged
`signboard: blank|baked` — 5 djsim/market/grimy skins carry baked sign text and must not get a
name overlay; the other 20 are clean overlay targets.
- `pipeline/validate_manifest.py`: Lane F gate — parses JSON, checks every skin exists, every GLB
is local-or-on-depot, thumbs exist, footprints/heights sane, ≥2 facades/type. **Green** (16
local-only warnings until publish; `--depot` enforces live).
- `pipeline/build_manifest.py`: reproducible generator (curated type-maps + derived skin/fitting
lists) so the manifest regenerates when assets change.
### Step 4 — Facade skin expansion (DONE, local)
- `pipeline/gen_skins.py`: ported from `thriftgod/gen_assets.py` — **same STYLE bible + blank-
signboard template** so new skins match. Resumable `.genraw/`, `--dry-run`/`--only`/`--harvest`.
- **`--local` backend (the one I used):** MODELBEAST `flux_local` → FLUX.2-klein-4B on the M3
Ultra's MPS GPU. Apache/**ungated**, weights already cached (15 GB), **free**, ~5.7 s/img, one
GPU job at a time. Direct subprocess to `~/Documents/MODELBEAST/server/operators/flux_local/run.py`
(`--outdir`/`--params` JSON) — no server, no cloud, no key. Cloud OpenRouter path kept as fallback.
- **All 23 generated → harvested → mapped.** 0 failures, 2:11 total. Eyeballed every one
(contact sheet `docs/shots/laneE/facades_gen_local.png`): every shopfront has a **genuinely
blank signboard** (Lane B name-overlay target), style indistinguishable from the 25 anchors.
The pack: toy (2), book (2), video (2), milkbar (3), wide dept anchors (2), stall/market (2),
warehouse (2), arcade portal (1), residential house fronts (4), corner-lot `-side` walls (3).
- **Coverage after mapping** (`build_manifest.py` `FACADE_TYPES`, mirrors registry.js + additive):
record 3 · opshop 3 · toy 5 · book 5 · video 4 · pawn 7 · milkbar 5 · dept 4 · **stall 4**
— every registry type ≥3. **`stall` went 2→4**, retiring the Lane A single-facade gap.
- **Corner-lot `-side` skins now exist** (brick/render/weatherboard): tagged `face:"side"` with
empty `types` in the manifest so no shop-front pool ever picks a windowless wall; Lane B maps
them to the exposed side face (`conventions.corner_lots` + new `conventions.facade_face`).
- `pipeline/SKIN_PROMPTS.md`: identical pack kept for the free manual flow (fallback if MODELBEAST
is ever down).
### Step 5 — Bespoke hero props
- `pipeline/meshgod_batch.json` + `MESHGOD_BATCH.md`: 9 solids (glass case, fridge, till, listening
booth, arcade cabinet, magazine rack, milkshake mixer, novelty record, bus shelter) in the
thriftgod_hero pattern. Documents the **MeshGod gotcha** (thin wires/flat discs → primitives),
reuse-first (cash-register/glass-case likely already on depot), and the primitive fallbacks.
**Not run** — gated on John (~$2.70). Nothing blocks: all have in-engine fallbacks.
## Adversarial review & fixes (self-run, 4-dimension × verify)
Ran a 19-agent adversarial review over all deliverables (correctness / contract-fit / doc-accuracy /
house-law), each finding independently verified. **15 confirmed** (1 blocker, 5 major, 9 minor).
All fixed and re-verified:
- **BLOCKER — off-origin exports.** `normalize.py` joined meshes but never flattened parent node
hierarchies, so assets under a parent empty shipped off-origin: `park_bench` was **~1 km off-origin,
floating 30 m**; `food_cart` 15 m off; `clothes_rack` sank 0.19 m. My earlier spot-check missed it
(the clean assets have no hierarchy). **Fixed:** `parent_clear(KEEP_TRANSFORM)` + drop non-mesh
nodes before join. Re-verified node-aware: all 16 now `center=[0, h/2, 0]`, **minY=0.0**.
- **Major — tri budget counted faces, not triangles** (latent for quad FBX/OBJ). Fixed: triangulate
in `clean_mesh` so the ≤5k gate + reported counts are true triangles.
- **Major — fitting-id namespace.** Added `fittingAliases` (registry semantic id → physical id) to
the manifest. (Lane C's `glb.js` already maps its kinds to my physical ids, so it worked — this
makes it explicit + future-proof.)
- **Major — facade map vs registry.js.** Aligned my facade→type map to be a consistent superset of
Lane A's `registry.js`; flagged that registry's `stall` pool has only 1 facade (Lane A gap).
- **Major — facing -Z not enforced.** Corrected the docs: symmetric props are orientation-agnostic;
no auto-facing (per-asset `yaw` available). Removed the false "enforced" claim.
- **Major — licence provenance.** Strengthened the publish-set caveat (record per-asset provenance
before pushing to the shared CDN) — a reason publish stays gated on John.
- **Minors:** hardened `glb_stat.py` (node-aware world bounds + WebP size sniff — the exact blind
spots that hid the blocker); `validate_manifest.py` liveness now uses a Range GET not HEAD;
Akai-MPC trademark flag; corrected audit counts (59 parsed `.glb`, rejects 8, food_cart tris).
## Acceptance check (LANE_E)
-`AUDIT.md` complete.
- ✅ ≥15 normalized GLBs (**16**) with thumbnails — ⛔ *live on 3GOD pending publish (needs cred)*.
-`manifest.json` validates green; fittings/furniture carry footprints for B/C to place untouched.
- ✅ Every registry shop type has ≥2 facades — now **≥3** (48 facades total). New skins
**generated** (not just planned) and eyeball-matched to the anchor style
(`facades_style_anchor.png` = 25 originals; `facades_gen_local.png` = 23 new local skins;
`fittings_normalized.png` = the 16 GLBs — all in `docs/shots/laneE/`).
- ✅ One-page `pipeline/README.md` (re-run each step, costs, where creds live).
## Notes for the other lanes
- **Lane B / C:** read `web/assets/manifest.json`. `fittings`/`furniture` → `depot:<file>` via
`core/loaders.js` (placeholder box on miss — the game runs asset-free). Footprints are `[w,d]`
metres on the ground; heights in metres; all base-origin, +Y up, facing Z.
- **Lane B:** overlay shop names only on facades with `signboard:"blank"`. Wide `dept-*` anchors
(when generated) want a wider UV plane. `awning`/`ground`/`sky` skins are yours; `wall`/`interior`
are Lane C's.
- **Lane F:** wire `python3 pipeline/validate_manifest.py` into the gate (add `--depot` post-publish).
## Open questions for Fable/John
1. **Publish now?** I have 16 verified GLBs staged in `pipeline/_normalized/` ready to push to the
shared depot. It's an outward-facing write to shared infra + needs `GOD3_PW`, so I left it for
your go rather than assume it.
2. ~~Spend the ~9¢ on facades?~~ **Resolved — done for free on-device** (MODELBEAST flux_local).
Only the **~$2.70 hero-prop** MeshGod batch (Step 5) still wants a go/no-go; all props have
in-engine primitive fallbacks, so it's a polish upgrade, not a blocker. *(Note: image→GLB is
also local on this box via `sf3d`/`trellis_mac`, but those weights are HF-gated — a setup
step before that path is free too.)*
3. A few library assets were near-misses (garage_shelves, radiogram, market_stall — see AUDIT
rejects). Worth a manual Blender re-origin pass later, or leave to primitives? I left them out.