PROCITY/pipeline
m3ultra 74d6257940 Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie
A. DEPOT — closed, under John's R22 standing Lane-E authorization (the authority that
   shipped sit.glb in R28 and the nine R36 sweep assets). Pushed via the passwordless
   tailnet ingress: _published.json 53 -> 56, drift check clean. Then verified the way
   this lane verifies — FETCHED BACK OFF THE PUBLIC DEPOT and hashed, not "the upload
   said 200": magpie/paling_fence/water_tank all HTTP 200, byte counts exact, sha1
   identical to local, 3/3. _r38_results.PENDING_PUBLISH.json renamed, build_manifest
   re-run (GLBs 33 -> 36), validate_manifest 0 errors / 0 warnings.

B. THE MAGPIE TINT — done, not offered. The bird shipped as an all-black crow (R38 4d
   traced the loss to trellis2_mlx's PBR bake). "Hand-tint the atlas" is not a paint job:
   the bake is a smart-UV unwrap of ~100 islands and none of them announces itself as a
   nape. So pipeline/tint_atlas.py defines the tint in the MESH's frame and projects it
   through the UVs — rasterize every UV triangle to texel->(position,normal), evaluate
   soft anatomical windows on the positions (white nape behind a black crown, shoulder
   bar on the flanks only so the dorsal midline stays black, rump + tail base stopping
   short of the black terminal band), paint keeping the bake's own luminance as shading,
   then dilate 5 texels into the UV padding so no black fringe survives bilinear/mip.
   3-D masks give one consistent soft edge across every island at once; a 2-D blur could
   not (it would bleed island-to-island through the black background).
   GEOMETRY UNTOUCHED byte for byte: still 894 tris / 1 mtl / 1 img / 512^2 WebP / no
   Draco / 0.155x0.358x0.404 m. Painted luma median 0.84 (p90 0.95) vs the bake's 0.13.
   VERIFIED IN THE GAME'S OWN RENDERER: the PUBLISHED file loaded off digalot.fyi through
   the vendored GLTFLoader + three.js — 1 mesh, 894 tris, 512x512 texture decoded (which
   also proves the PIL-written WebP decodes in a browser).
   VERDICT: it reads. Pied and unmistakable at 64 and 48 px, still black-and-white at 32.
   Honest caveat: from directly below-and-in-front it stays dark — correct, a real
   magpie's bib and belly ARE black. Render: docs/shots/laneE/r38_magpie_tint.png.
   Standing after B's 4514cb9: an UPGRADE PATH, not a dependency — B's 182-tri procedural
   bird is 4.9x lighter and the right call. Nothing probes for the GLB and nothing should.

C. CORRECTIONS FOLDED INTO THE RECORD so the next asset round does not re-learn them:
   skins are 9.7 s not 5.7 s (1024^2, 1.78x the pixels, per-pixel identical) - the reuse
   list's "one line each" holds for 5 of 9 (extractGLB keeps only the FIRST material) -
   normalize.py's collapse decimator floors at the shell count on TRELLIS output while
   bake_lowpoly.py clears it every time - the 489 s outlier at 3.8x median means
   throughput plans on a tail, not a mean - furniture.js:30's stale comment is fixed by B.
   NEW, and the one worth keeping: an asset's tri budget is tris x THE INSTANCE COUNT THE
   PLACEMENT RULE IMPLIES. paling_fence is a good 1,121-tri panel and a rejected asset —
   905 instances = 1.01M tris, ~47x over. Ask the consuming lane for the instance count
   BEFORE generating anything that tiles, edges or repeats per-lot.

New tools, all on-device and $0: tint_atlas.py, render_views.py (view sheet + --eevee +
roster-consistent --thumb), view_sheet.py (the distance strip no 256px thumbnail can make).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:02:06 +10:00
..
_godverse Lane G R26 (v5.0-beta): the mint baker, the crate menu, the atlas manifest (ledgers #2, #6) 2026-07-17 10:33:40 +10:00
packs Lane E round 8: REAL stock packs (record 350 / book 311 / toy 273) live on depot 2026-07-15 04:09:14 +10:00
_audit_stats.json Lane E (assets): pipeline, manifest + 23 on-device facade skins 2026-07-14 13:48:25 +10:00
_gig_results.json Lane E R13 (v3.0-beta): gigKey rename + grunge/covers beds + instrument decimation 2026-07-15 23:03:14 +10:00
_normalize_results.json Lane E R22 (v4.0): the venue_night tri diet — B's #1a driver was mine, fixed (BLOCKING #1b) 2026-07-16 22:04:56 +10:00
_peds_decim.json Lane E round 4 (E1): decimate 19 ped rigs 924k→46k tris (≤3k each) for gate 3 2026-07-14 18:29:50 +10:00
_peds_merge.json Lane E round 7 (E1): merge ped rigs to ONE draw each (gates roster flip) 2026-07-15 00:33:03 +10:00
_props_results.json Lane E R22 (v4.0): the venue_night tri diet — B's #1a driver was mine, fixed (BLOCKING #1b) 2026-07-16 22:04:56 +10:00
_published.json Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00
_r38_results.json Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00
AUDIT.md Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00
bake_lowpoly.py Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64) 2026-07-14 21:20:44 +10:00
batch_bake.json Lane E round 5 (E1): bake 6 hero props to <=8k tris (288k->48k), live on depot 2026-07-14 20:56:07 +10:00
batch_counter.json Lane E round 4 (E2): fix record_crate + ship counter-with-till, both live on depot 2026-07-14 19:07:15 +10:00
batch_e2.json Lane E round 5 (E2): depot growth — street bin + record-crate stack (consumer-driven) 2026-07-14 21:27:13 +10:00
batch_fal.json Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64) 2026-07-14 21:20:44 +10:00
batch_peds.json Lane E round 7 (E1): merge ped rigs to ONE draw each (gates roster flip) 2026-07-15 00:33:03 +10:00
batch_props.json Lane E round 3 (landed by Fable after review): 5 hero props gen'd local + published, local-depot mode, manifest 14 fittings + 8 furniture 2026-07-14 18:03:03 +10:00
batch_r38.json Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
batch.json Lane E (assets): pipeline, manifest + 23 on-device facade skins 2026-07-14 13:48:25 +10:00
build_manifest.py Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
build_stock_pack.py Lane E round 8: REAL stock packs (record 350 / book 311 / toy 273) live on depot 2026-07-15 04:09:14 +10:00
build_towns.py Lane E R24 (v5.0-alpha): the crate's real street, the honest gate, and Ballarat retired 2026-07-17 01:06:00 +10:00
decimate_props.py Lane E R13 (v3.0-beta): gigKey rename + grunge/covers beds + instrument decimation 2026-07-15 23:03:14 +10:00
fbx_to_clip.py Lane E R16 (v3.1): the drummer's sit clip — sit.glb produced + published 2026-07-16 12:37:27 +10:00
fix_glb_textures.py Lane E round 4 (E2): fix record_crate + ship counter-with-till, both live on depot 2026-07-14 19:07:15 +10:00
gen_audio.py Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
gen_props_fal.py Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64) 2026-07-14 21:20:44 +10:00
gen_props.py Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
gen_skins.py Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
gig_instruments.json Lane E R12 (v3.0-alpha gig layer): 5 instruments + 4 posters + 3 gig audio 2026-07-15 19:44:48 +10:00
gig_norm_batch.json Lane E R12 (v3.0-alpha gig layer): 5 instruments + 4 posters + 3 gig audio 2026-07-15 19:44:48 +10:00
glb_stat.py Lane E (assets): pipeline, manifest + 23 on-device facade skins 2026-07-14 13:48:25 +10:00
godverse_server.py Lane G R27 (v5.0): tier 2 — the two read endpoints, the three fences, and the gate's missing subject 2026-07-17 13:56:02 +10:00
godverse_stock.py Lane G R26 (v5.0-beta): the mint baker, the crate menu, the atlas manifest (ledgers #2, #6) 2026-07-17 10:33:40 +10:00
godverse_town.py Lane G R24 (v5.0-alpha): the crate's real street — redhill_godverse, the keyed re-emit, and two contract conflicts 2026-07-17 01:31:07 +10:00
merge_ped.py Lane E round 7 (E1): merge ped rigs to ONE draw each (gates roster flip) 2026-07-15 00:33:03 +10:00
meshgod_batch_r38.json Lane E R38 (v8 wave 1): the asset wave — trellis2_mlx swap verified 1.92x, six sounds, four grounds, and the thin-structure answer 2026-08-03 18:30:33 +10:00
meshgod_batch.json Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64) 2026-07-14 21:20:44 +10:00
MESHGOD_BATCH.md Fable: correct stale fal.ai budget — Hunyuan v2 ~14-17¢/gen, remainder is 2-3 props (~$0.30-0.50, ~$1 ceiling), not $2.70 2026-07-14 20:36:12 +10:00
normalize.py Lane E round 3 (landed by Fable after review): 5 hero props gen'd local + published, local-depot mode, manifest 14 fittings + 8 furniture 2026-07-14 18:03:03 +10:00
publish.py Lane E R17 (v3.2): the real-map scout — 5 AU town caches + publish --verify fix 2026-07-16 14:49:40 +10:00
README.md Lane E R22 (v4.0): MODELBEAST notes refresh + provenance v4-final + G2a atlas review 2026-07-16 21:54:49 +10:00
render_views.py Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00
SKIN_PROMPTS.md Lane E (assets): pipeline, manifest + 23 on-device facade skins 2026-07-14 13:48:25 +10:00
stage_local_depot.py Lane E round 3 (landed by Fable after review): 5 hero props gen'd local + published, local-depot mode, manifest 14 fittings + 8 furniture 2026-07-14 18:03:03 +10:00
tint_atlas.py Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00
validate_atlas.py Lane E R26 w4: the mint-id fence — my rule was the stale thing (wave 4, E's one line) 2026-07-17 13:08:40 +10:00
validate_manifest.py Lane E R23 (v5.0-alpha): the atlas validator + toowoomba retired (ledger #4) 2026-07-16 23:14:00 +10:00
validate_pack.py Lane E round 8 (E1+E2): book/toy stock packs + pack-QA gate 2026-07-15 02:27:50 +10:00
view_sheet.py Lane E R38 s2: the three GLBs PUBLISHED + sha1-verified off the public depot; the magpie is a magpie 2026-08-03 19:02:06 +10:00

PROCITY pipeline (Lane E) — how to re-run everything

The asset pipeline: audit ultra's library → normalize the good bits to house GLB law → publish to the 3GOD depot → ship web/assets/manifest.json (the contract Lanes B/C/D read) → fill facade

  • prop gaps. Everything is optional at runtime — the game runs asset-free; this lane makes it gorgeous, it doesn't make it work.

Where things live

what where
ultra (library + Blender) ssh johnking@100.91.239.7 — key auth, no coreutils/timeout, py3.9, Blender at /Applications/Blender.app/Contents/MacOS/Blender (not on PATH)
model library ultra ~/Documents/3D=models/{shop-fittings,street-furniture,furniture,props-scenes}
3GOD depot https://digalot.fyi/3godGET /a/<f>, GET /api/list, POST /api/upload?name=<f>
OpenRouter key (skins) env OPENROUTER_KEY or ~/Documents/dealgod/lore/keys.env (never committed)
depot password (publish) env GOD3_PW (never committed; ask John) — cookie = hmac(GOD3_PW,"3god-ok")
Ollama (free LLM) http://100.89.131.57:11434 (qwen3) — for names/lore/cataloguing, unused so far
MODELBEAST (free local gen) this box, ~/Documents/MODELBEAST + its own queue on :8777. flux_local = FLUX.2-klein-4B text→image on MPS (the facade skins — ungated, weights cached). sf3d/trellis_mac = image→GLB, local + PROVEN, weights already cachedtrellis_mac (TRELLIS.2 1024_cascade) went 15/15 on thriftgod's Tier-1 props (Lane G/ultra, R1); sf3d = seconds-fast draft. Also live on the queue: bg_remove_local, mflux_image_edit, seedvr2_upscale. (The old "HF-gated / setup needed" note here was stale — flagged by ultra, corrected R22.)

The steps (each is idempotent / resumable)

1. Audit — AUDIT.md (free)

scp pipeline/glb_stat.py johnking@100.91.239.7:/tmp/
ssh johnking@100.91.239.7 'cd ~/Documents/3D=models && python3 /tmp/glb_stat.py --json shop-fittings street-furniture furniture props-scenes' > pipeline/_audit_stats.json

glb_stat.py is a dependency-free GLB parser (tris, bbox, texture sizes, Draco flag). Findings + licensing triage are written up in AUDIT.md.

2. Normalize — normalize.py → 16 GLBs + thumbs (free, runs on ultra)

scp pipeline/normalize.py pipeline/batch.json johnking@100.91.239.7:/tmp/
BL=/Applications/Blender.app/Contents/MacOS/Blender
ssh johnking@100.91.239.7 "\$BL --background --python /tmp/normalize.py -- /tmp/batch.json /tmp/procity_norm"
scp johnking@100.91.239.7:/tmp/procity_norm/_results.json pipeline/_normalize_results.json
scp 'johnking@100.91.239.7:/tmp/procity_norm/thumbs/*.png' web/assets/thumbs/
scp 'johnking@100.91.239.7:/tmp/procity_norm/*.glb' pipeline/_normalized/

Enforces the house GLB law (metres, +Y up, base origin, ≤5k tris, WebP ≤1024, no Draco), renders a 256px thumbnail each. Extends MESHGOD/scripts/finish_glb.py (solidify/decimate/bright/base-origin)

  • adds texture-webp, height-normalize, outlier cleanup, per-asset up/yaw. Edit batch.json to add assets; always eyeball the thumbnails — node-transform-distorted sources (see AUDIT rejects) look fine in-frame but have wrong footprints.

3. Manifest — build_manifest.py + validate_manifest.py (free)

python3 pipeline/build_manifest.py          # → web/assets/manifest.json
python3 pipeline/validate_manifest.py       # Lane F gate: 0 errors = green
python3 pipeline/validate_manifest.py --depot   # stricter: require GLBs live on the depot

build_manifest.py derives skins from web/assets/gen/ + fittings from _normalize_results.json; the curated facade→shop-type maps live at the top of that file (edit there when adding skins).

4. Publish — publish.py → 3GOD depot (the one outward-facing step)

python3 pipeline/publish.py --dry-run        # free: list what would upload
export GOD3_PW=...                           # ask John
python3 pipeline/publish.py                  # POST each GLB + thumb to the depot (idempotent)

5. Facade skin gaps — gen_skins.py --local (DONE, free, on-device)

The 23-skin gap pack is already generated + harvested + mapped. To re-run or extend:

python3 pipeline/gen_skins.py --local --dry-run   # free: list the pack, no GPU
python3 pipeline/gen_skins.py --local             # MODELBEAST flux2-klein-4b on MPS → .genraw/ (~5.7s/img)
python3 pipeline/gen_skins.py --harvest           # → web/assets/gen/facade-*.jpg, then re-run build_manifest.py

--local drives ~/Documents/MODELBEAST/server/operators/flux_local/run.py directly (no server, no key, weights cached). It runs one GPU job at a time. Add skins by editing PROMPTS in gen_skins.py and their shop-type map in build_manifest.py (FACADE_TYPES). Always eyeball .genraw/ and delete rejects before --harvest. Cloud fallback (drop --local): OpenRouter google/gemini-3.1-flash-image, ~$0.004/img, needs OPENROUTER_KEY. Manual flow: SKIN_PROMPTS.md.

6. Bespoke hero props — gen_props.py (MODELBEAST) (round-3: DONE, free, on-device)

The meshgod_batch.json gap list now goes through MODELBEAST instead of fal.ai — free, local:

python3 pipeline/gen_props.py --dry-run                 # list props + what's done
python3 pipeline/gen_props.py --concepts                # flux_local concept images → .genprops/*.png (eyeball!)
python3 pipeline/gen_props.py --mesh --only glass-case  # trellis_mac 1024_cascade image→GLB (~5-8 min)
python3 pipeline/gen_props.py --mesh                    # all props with a concept, no glb
# then normalize (uses local Blender), stage, catalogue:
BL=/Applications/Blender.app/Contents/MacOS/Blender
"$BL" --background --python pipeline/normalize.py -- pipeline/batch_props.json pipeline/_normalized web/assets/thumbs
cp pipeline/_normalized/_results.json pipeline/_props_results.json && python3 pipeline/build_manifest.py

5 shipped (arcade_cabinet, listening_booth, drinks_fridge, milkshake_mixer, novelty_record) + cash-register reused from depot. magazine-rack → primitive (thin-wire gotcha); glass-case/bus-shelter → fal.ai fallback (TRELLIS can't do transparent glass; sf3d --draft gives a featureless blob). TRELLIS meshes run heavy (2577k tris) — see AUDIT provenance. Eyeball every thumbnail; ≤2 local attempts then fal (John's go).

⚠️ Finishing a trellis_mac mesh — ultra's post-mortem (Lane G, R1) — folded in verbatim

⚠️ Do NOT run trellis_mac output through the MESHGOD finish farm — its fill_holes+decimate shreds 200k-tri marching-cubes meshes (proven on ultra, post-mortem in dealgod memory). The working pipeline: raw GLB → npx @gltf-transform/cli optimize --compress draco --texture-compress webp --texture-size 1024 --simplify-error 0.01 → 0.41.8 MB at raw-quality look.

Credit: Lane G / ultra (G-progress.md), proven post-mortem — it cost them a shredded batch so it shouldn't cost us one. One amendment, per Fable's Ruling 1 (docs/LANES/FABLE_TO_LANE_G.md): NO-DRACO WINS — the depot speaks uncompressed GLB and PROCITY's loader stays decoder-free. So drop --compress draco; everything else in ultra's command stands (WebP ≤1024 is already our law):

npx @gltf-transform/cli optimize IN.glb OUT.glb \
  --texture-compress webp --texture-size 1024 --simplify-error 0.01     # NO --compress draco

Files land ~23× bigger than the Draco'd version and that's fine — the depot holds ONE law. Note our own in-house paths remain valid for their own cases and are NOT the finish farm: bake_lowpoly.py (voxel-remesh + bake, for shell-soup that must hit a low budget) and decimate_props.py (collapse, detail-preserving — see the R13 instrument diet).

7. Local-depot mode — loaders.js ?localdepot=1 + stage_local_depot.py (round-3: DONE)

Lets Lanes B/C/F validate GLB loading with no network / before publish:

python3 pipeline/stage_local_depot.py     # mirror _normalized/*.glb → web/assets/models/ (git-ignored)
# then load the game with ?localdepot=1 — depot:<file> resolves to assets/models/<file> instead of the CDN

8. Audio pack — gen_audio.py (round-11: DONE, free, on-device)

Tool choice (Fable asked E to pick + document): pure procedural synthesis in numpy, not a neural model. MODELBEAST has no audio operator, and for this pack synthesis wins outright — $0, no model download, deterministic/seeded (house law), perfectly seamless loops (neural gens don't loop), parody-safe by construction (every sample is an original oscillator/noise render — nothing to imitate), and precise ≤25 MB budget control. Pipeline: numpy synth → WAV → ffmpeg OGG/Opus (+ M4A/AAC).

PY=~/Documents/MODELBEAST/venvs/mflux/bin/python     # numpy lives in the mflux venv
$PY pipeline/gen_audio.py --dry-run                  # list the 23-asset pack + budget
$PY pipeline/gen_audio.py                            # render all → web/assets/audio/*.ogg + *.m4a
$PY pipeline/gen_audio.py --only music               # just the music beds
python3 pipeline/build_manifest.py                   # catalogue → manifest.json audio:{ambience,sfx,music}

23 assets, 8.1 MB shipped (opus+aac): 6 ambience beds (street day/night, rain, 3 roomtones), 13 sfx (doorbell, door, till, riffle, toast, tram bell/rumble, 6 footsteps), 4 instrumental 90s music loops. Loudness: beds/music loudnorm -16 LUFS, sfx peak-normalised. Ear-ball = spectrogram sheet docs/shots/laneE/audio_spectrograms.png + numeric loop-seam/peak check (I can't subjectively listen — final ear-check is John's/F's). Add assets by editing PACK in gen_audio.py + the AUDIO_* maps in build_manifest.py. WAV scratch pipeline/.genaudio/ is git-ignored; OGG/M4A ship committed.

9. Gig layer — round-12 (v3.0-alpha, ?gigs=1) (DONE, free, on-device)

Three asset streams for one-pub-end-to-end, all $0:

# instruments (5 GLBs): flux concept -> trellis -> normalize, via the --batch override
python3 pipeline/gen_props.py --batch pipeline/gig_instruments.json --concepts   # eyeball first
python3 pipeline/gen_props.py --batch pipeline/gig_instruments.json --mesh        # ~3-4 min each
BL=/Applications/Blender.app/Contents/MacOS/Blender
"$BL" --background --python pipeline/normalize.py -- pipeline/gig_norm_batch.json pipeline/_normalized web/assets/thumbs
# posters (4 skins): portrait flyers with a blank name-band (B/C overprint like signboards)
python3 pipeline/gen_skins.py --posters && python3 pipeline/gen_skins.py --harvest
# gig audio (3): pub-rock live bed + crowd-walla + applause — already in gen_audio.py PACK
~/Documents/MODELBEAST/venvs/mflux/bin/python pipeline/gen_audio.py --only music   # etc.
python3 pipeline/build_manifest.py     # catalogues skins.poster + audio gig assets + fittings

Instruments: amp/guitar/bass reconstruct well; thin objects (mic-stand pole, drum hardware) may fall to D's primitive (round-3 thin-wire lesson — spec allows "primitives when absent"). Posters carry nameZone [x,y,w,h] in skins.poster. Gig audio is flagged gig:true in manifest.audio.

Cost summary

step cost
audit, normalize, manifest, validate, publish free (compute + bandwidth)
facade skin gap-fill (23 imgs) free — done on-device (MODELBEAST flux_local); cloud fallback ~9¢
hero props (round-3) free — 5 via MODELBEAST trellis_mac; glass-case/bus-shelter on fal.ai list (≤$2.70, John's go)
audio pack (round-11, 23 assets) free — 100% procedural synthesis (numpy → ffmpeg), $0, no model

Artifacts (not committed — regenerated from the steps)

pipeline/_normalized/ (staging GLBs, live on depot), pipeline/.genraw/ (raw skin gen). Committed: the scripts, *.md, batch.json, meshgod_batch.json, _audit_stats.json, _normalize_results.json, web/assets/manifest.json, web/assets/thumbs/, docs/shots/laneE/.