PROCITY/E-progress.md
m3ultra 063a959e29 Lane E (assets): pipeline, manifest + 23 on-device facade skins
Audit -> normalize (16 GLBs + thumbs) -> web/assets/manifest.json (the
contract Lanes B/C/D read) -> facade gap-fill.

Step 4 facades generated locally on the M3 Ultra via MODELBEAST
flux_local (FLUX.2-klein-4B / MLX on MPS): 23 skins, free, ~5.7s each,
blank signboard, eyeballed, harvested, mapped to shop types. Every
registry type now has >=3 facades (stall 2->4, closing the Lane A gap).
Corner-lot -side walls tagged face:"side" with empty types so no shop
front ever selects a windowless wall. manifest validates green.

Still gated on John (both optional -- the game runs asset-free):
publish the 16 GLBs to 3GOD (needs GOD3_PW), MeshGod hero props (~$2.70).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 13:48:25 +10:00

11 KiB
Raw Blame History

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 acceptance. Nothing in other lanes' files was touched.

Status at a glance

Step Deliverable State
1 Audit pipeline/AUDIT.md (+ glb_stat.py, _audit_stats.json) done
2 Normalize pipeline/normalize.py16 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 MESHGOD_BATCH.md + meshgod_batch.json (9 solids) spec'd; not run (John's go, ~$2.70)
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.pysame 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/furnituredepot:<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.