- record_crate: R3 normalize left a sourceless texture (packed AO/roughness map didn't survive WebP export; metallicRoughness+occlusion pointed at it) → three.js 'undefined uri' crash. New pipeline/fix_glb_textures.py strips material slots that reference a sourceless texture. Verified loads in vendored GLTFLoader; re-published. - counter_till (new fitting): ~2m timber counter + period till, flux_local→trellis_mac on-device, 1.61x0.64m / 1.15m tall, 32k tris. Verified loads; published. Lane C maps kind counter -> counter_till (balcao 4m 'counter' kept for long-bar use). - publish.py: --only <name> filter + passwordless tailnet detection (GOD3_DEPOT override). - manifest rebuilt (15 fittings, alias counter->counter_till); validate --depot 0 errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15 KiB
LANE E — ASSETS · progress (PROCITY-E)
2026-07-14. Owner: PROCITY-E. Scope: pipeline/*, web/assets/*, web/assets/manifest.json.
Reviewed against docs/LANES/LANE_E_ASSETS.md + docs/LANES/ROUND3_INSTRUCTIONS.md. Nothing in
other lanes' files was touched.
Round 4 (Fable) — ped decimation (gate-3 critical path) + asset fixes
- E1 — ped decimation: DONE + committed (
518678d, reflane-e/round4-peds). The 19 ped rigs inweb/models/peds/were 924k tris total (31k–56k each) — the gate-3 blocker. Decimated to ≤3k each (1564–2805; fleet 924k→46.5k, ~2.45k avg) viagltf-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/idleuntouched); contact sheetdocs/shots/laneE/peds_decimated.pngclean at street distance. Originals gitignored inpipeline/.peds_orig/; per-ped inpipeline/_peds_decim.json. → Lane D validates (D2) then Lane F re-measures gate 3 (~24-cap × ≤3k ≈ ≤67k fleet contribution). - E2a —
record_crateFIXED, verified + published. Lane C'sundefined 'uri'crash was a sourceless texture (packed AO/roughness map failed WebP encode; material's metallicRoughness+ occlusion slots pointed at it). New toolpipeline/fix_glb_textures.pystrips those slots (three.js loads textures lazily → no crash). Verified it loads in the real vendoredGLTFLoader(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_tillfitting (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_localconcept →trellis_mac, 3 concept rolls to land the ~2:1 aspect), normalized to house law, verified loads inGLTFLoader, and published. Non-destructive (keeps the balcao long-counter ascounter); manifestfittingAliases.counter→counter_till; Lane C re-maps kindcounter→counter_tillinglb.js. - Publish: DONE (John approved). Both re-exports live; manifest
validate --depotGREEN (0 err, 23 GLBs live).publish.pygained 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_localconcept image →trellis_macimage→GLB (1024_cascade) →normalize.py→ thumbs → manifest. 5 generated + kept (listening_booth, drinks_fridge, arcade_cabinet, milkshake_mixer, novelty_record — contact sheetdocs/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): resolvesdepot:<file>toweb/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 adepot:GLB, CDN path intact). - Manifest: 14 fittings + 8 furniture (5 hero + cash_register added);
validate_manifest.pygreen (0 err). Provenance recorded inAUDIT.md(props are 100% synthetic/local — cleanest in the set). - Known tradeoff: TRELLIS meshes are dense shell-soup — decimate floors 25–77k 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 --strictGREEN (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 (onlyrecord_crate_plastic_01was 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, extendsMESHGOD/finish_glb.py(solidify/decimate/ bright/base-origin) + adds height-normalize, outlier cleanup, texture→WebP≤1024, per-asset up/yaw, 256px thumbnail. Driven bypipeline/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 inweb/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 (cookiehmac(GOD3_PW,"3god-ok"),POST /api/upload+/api/thumb). Reads the secret from env — never contains/logs it.--dry-runverified all 16 ready. Not published yet:GOD3_PWisn'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.conventionsblock 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;--depotenforces 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 fromthriftgod/gen_assets.py— same STYLE bible + blank- signboard template so new skins match. Resumable.genraw/,--dry-run/--only/--harvest.--localbackend (the one I used): MODELBEASTflux_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/--paramsJSON) — 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-sidewalls (3). - Coverage after mapping (
build_manifest.pyFACADE_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.stallwent 2→4, retiring the Lane A single-facade gap. - Corner-lot
-sideskins now exist (brick/render/weatherboard): taggedface:"side"with emptytypesin the manifest so no shop-front pool ever picks a windowless wall; Lane B maps them to the exposed side face (conventions.corner_lots+ newconventions.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.pyjoined meshes but never flattened parent node hierarchies, so assets under a parent empty shipped off-origin:park_benchwas ~1 km off-origin, floating 30 m;food_cart15 m off;clothes_racksank 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 nowcenter=[0, h/2, 0], minY=0.0. - Major — tri budget counted faces, not triangles (latent for quad FBX/OBJ). Fixed: triangulate
in
clean_meshso the ≤5k gate + reported counts are true triangles. - Major — fitting-id namespace. Added
fittingAliases(registry semantic id → physical id) to the manifest. (Lane C'sglb.jsalready 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'sstallpool 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
yawavailable). 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.pyliveness 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.mdcomplete. - ✅ ≥15 normalized GLBs (16) with thumbnails — ⛔ live on 3GOD pending publish (needs cred).
- ✅
manifest.jsonvalidates 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 indocs/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>viacore/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". Widedept-*anchors (when generated) want a wider UV plane.awning/ground/skyskins are yours;wall/interiorare Lane C's. - Lane F: wire
python3 pipeline/validate_manifest.pyinto the gate (add--depotpost-publish).
Open questions for Fable/John
- 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 + needsGOD3_PW, so I left it for your go rather than assume it. 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 viasf3d/trellis_mac, but those weights are HF-gated — a setup step before that path is free too.)- 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.