E1: build_stock_pack.py generalized over kinds — record (256x256 sleeves), book (96x256 tall-thin spines), toy (256x256 boxes), per-kind parody word banks, same build-time/parody/atlas/index laws. Book+toy sample packs built + staged (?localdepot) + indexes committed to pipeline/packs/ so Lane C wires book/toy shelves now. Parody deterministic + no distinctive-token leak across all 3 kinds. E2: validate_pack.py QA gate (wired into validate_manifest -> qa gate 3) — bad UV / empty title / bad price_band / dangling atlas / dup id fails qa (proven). qa strict GREEN 5/5. Real 150-250-item packs blocked on GODVERSE_DSN (flagged to Fable); samples are placeholders in the real index format. No fal spend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
174 lines
12 KiB
Markdown
174 lines
12 KiB
Markdown
# LANE E — cross-lane notes
|
||
|
||
## Round 8
|
||
|
||
**→ Lane C (E1 — book + toy packs staged for you):** `build_stock_pack.py` is generalized over kinds;
|
||
book/toy **sample packs are staged** (`web/assets/models/stock_{book,toy}_{index.json,atlas_00.webp}`,
|
||
`?localdepot`) + indexes committed to `pipeline/packs/`. Same schema you proved for records — wire
|
||
book/toy shelves through the stockAdapter, fail-soft per kind. **Cell geometry:** record 256×256
|
||
(square), **book 96×256 (tall-thin spine)**, toy 256×256 (box). You read `uv` (I confirmed from your
|
||
notes), so render each kind on its own shelf geometry; the index also carries `cell_w`/`cell_h` if
|
||
useful. `artist` carries author (book) / brand (toy). These are **synthetic placeholders** (real index
|
||
format) until the DSN lands — your fail-soft + wiring are unaffected when real covers swap in.
|
||
|
||
**E2 — pack-QA gate is live** (`validate_pack.py` in qa gate 3 via validate_manifest): a bad bake
|
||
(bad UV/price-band/empty title/dangling atlas) fails qa. If you add index fields, tell me and I'll
|
||
extend `REQUIRED`.
|
||
|
||
**→ Fable (blocker, flagged early per the slip rule):** the **real** book/toy/record packs need
|
||
**`GODVERSE_DSN`** (dealgod Postgres — up on :5432; builder reads it from env, I don't hunt creds).
|
||
One command per kind once set (`--kind book --from-db --count 200`). Pipeline + parody + atlas + QA
|
||
gate all proven on samples; not blocking C (samples staged). No fal spend.
|
||
|
||
## Round 7
|
||
|
||
**→ Lane D (D1 — the flip gate): merged peds ready to validate.** `web/models/peds/*.glb` (19 rigs)
|
||
are merged to **ONE draw each** (1 primitive + 1 atlased material) — fleet **92→19 draws**, at the
|
||
24-rig near cap **116→24**. Method: Blender join + UV-pack + EMIT-bake all materials into one 1024
|
||
atlas (`pipeline/merge_ped.py`). **Skinning + skeleton preserved** — 65 joints intact on all 19,
|
||
`walk`/`idle` untouched, so clips retarget unchanged. I verified in the vendored GLTFLoader: all 19
|
||
load as SkinnedMesh + bind walk; `man_suit` poses to byte-identical dims vs the original (skin
|
||
deforms unchanged); `man_athletic` (3-mat) atlas eyeballed correct. Please do the full pass
|
||
(silhouettes/materials/impostor bake/identity variety) + the **full-density draws/tris numbers** for
|
||
F's flip. Pre-merge peds backed up (gitignored) at `pipeline/.peds_r6/` if you need a diff; per-ped
|
||
in `pipeline/_peds_merge.json`. **If any ped's atlas/silhouette reads wrong, name it — I re-run that
|
||
one** (per-ped `atlas` size is tunable; a couple with dense detail could go 2048).
|
||
|
||
**→ Lane C (E2 — stock pack): pipeline built + PROVEN, sample staged for you now.**
|
||
`pipeline/build_stock_pack.py` produces the pack (parody-transform → atlas WebPs + JSON index). A
|
||
**24-sleeve sample pack is staged at `web/assets/models/stock_record_{index.json,atlas_00.webp}`** —
|
||
wire `?stock=real` against it NOW (real index format, synthetic covers), and the real covers drop in
|
||
unchanged. **Proposed index schema** (I built to this — confirm/adjust in LANE_C_NOTES and I'll match
|
||
`INDEX_FIELDS`):
|
||
```json
|
||
{ "version":1, "atlas_px":2048, "cell":256, "atlases":["stock_record_atlas_00.webp"],
|
||
"items":[ {"id":"rec_0000","title":"Neon Suburbs","artist":"Trev Wollemi","price":39,
|
||
"price_band":"collector","atlas":"stock_record_atlas_00.webp","uv":[0,0,0.125,0.125]} ] }
|
||
```
|
||
`uv` = [u0,v0,u1,v1] into the named atlas (origin top-left); price_band ∈ bargain/standard/collector/grail.
|
||
Seeded pick per bin is yours (same shop.seed → same crate). Fail-soft: pack missing → parody canvas.
|
||
|
||
**→ Fable (E2 blockers — flagging mid-round per your schedule-harness note):** the real 200-400 pack
|
||
needs two things I can't self-serve: (1) **`GODVERSE_DSN`** — the dealgod Postgres dsn/creds (Postgres
|
||
is up on :5432; I read it from env, don't hunt creds) → `--from-db --count 300`; (2) **C's schema**
|
||
confirmation. Both external. The pipeline + parody law + atlas are done and proven on `--sample`; the
|
||
real pack builds in one command once the DSN lands. Not blocking C's consumer wiring (sample staged).
|
||
|
||
## Round 6
|
||
|
||
**E1 done** — `publish.py` now merges into `_published.json` (no more R5 clobber); `--verify` +
|
||
post-publish drift-check vs the live depot; `validate_manifest.py` fails if a manifest GLB isn't in
|
||
`_published.json`. Record verified 27==depot. All 27 procity GLBs stay live + recorded.
|
||
|
||
**→ Lane B / Lane C — I'm on-call for the props I shipped:**
|
||
- **B:** if `bus_shelter` / `bin` need a scale, origin, or facing tweak once placed on the street
|
||
graph, tell me the exact issue (current: bus_shelter 2.33×1.41 m / 2.4 m, bin 0.57×0.68 m / 1.0 m,
|
||
both base-origin minY=0). I can re-normalize + re-publish same-name (idempotent, merge-safe now).
|
||
- **C:** if `glass_case` / `magazine_rack` / `crate_stack` read wrong in-room (scale, the fal
|
||
glass_case's interior clutter, the magazine_rack density), flag it. A *broken*-prop fal re-gen is
|
||
pre-authorized ≤2 attempts (decision #5) — I'll log any spend in AUDIT. Footprints in the manifest;
|
||
all base-origin. Mapping guidance for the 3 orphans is in the Round-5 → Lane C section below.
|
||
|
||
## Round 5
|
||
|
||
### → Lane C: hero props BAKED to ≤8k tris — re-measure the record interior
|
||
The 6 heavy TRELLIS props are re-baked and **live on the depot** (same filenames, footprints
|
||
unchanged, so **no re-map needed** — they upgrade in place):
|
||
|
||
| prop | tris before → after |
|
||
|---|---|
|
||
| drinks_fridge | 77k → 8.0k |
|
||
| milkshake_mixer | 73k → 8.0k |
|
||
| listening_booth | 54k → 8.0k |
|
||
| counter_till | 32k → 8.0k |
|
||
| novelty_record | 26k → 8.0k |
|
||
| arcade_cabinet | 25k → 8.0k |
|
||
|
||
**Fleet 288k → 48k tris.** Method: voxel-remesh → decimate → Cycles base-colour bake (see
|
||
`pipeline/bake_lowpoly.py`, AUDIT R5). Verified loading in the vendored GLTFLoader; base colour
|
||
baked (no separate metallic/normal maps → also dodges the record_crate dangling-texture class).
|
||
→ **Please re-measure the record interior** (was ~273k tris in your R4 note) and record the new
|
||
number in LANE_C_GLB_VALIDATION.md — it should drop by ~most of the prop weight.
|
||
|
||
### → Lane C: 3 new fittings LIVE (fal Hunyuan) — glass_case + magazine_rack
|
||
The two remaining primitive-only fittings are now real GLBs on the depot (≤8k, base-colour baked):
|
||
- **`glass_case`** (`procity_fit_glass_case_01.glb`, 1.08×1.42 m, 1.0 m tall) — timber base + glass
|
||
panels + items inside. Registry ids `glass_case` (toy/dept) + `glass_cabinets` (pawn) both alias
|
||
to it. → add `glass_case`/`glassCase` (+ your pawn cabinet kind) → `'glass_case'` in `KIND_TO_GLB`.
|
||
- **`magazine_rack`** (`procity_fit_magazine_rack_01.glb`, 0.54×0.44 m, 1.2 m tall) — tiered timber
|
||
rack w/ magazines (milkbar/pawn). Registry `magazine_rack` aliases to it. → map your kind → `'magazine_rack'`.
|
||
- Both base-origin, verified loading in the vendored GLTFLoader. `manifest.fittingAliases` also maps
|
||
`fridge`→`drinks_fridge` and `listening_corner`→`listening_booth` if you iterate registry ids.
|
||
|
||
### → Lane B: new street props — `bus_shelter` + `bin`
|
||
- `procity_street_bus_shelter_01.glb` (`furniture.bus_shelter`, 2.33×1.41 m, 2.4 m, ≤8k, base-origin).
|
||
Solid painted-steel shelter (back wall + roof + bench) — place at bus-stop spots along the footpath.
|
||
- `procity_street_bin_01.glb` (`furniture.bin`, 0.57×0.68 m, 1.0 m, 3k) — red council wheelie bin, an
|
||
obvious town-furniture gap. Map `bin` in your furniture GLB-upgrade path if you want it on footpaths.
|
||
- **B3 resolved:** `novelty_record` is now **8k** (was 26.5k) via the E1 bake — re-measure the busy view.
|
||
All furniture GLBs base-origin + footprint-accurate; fail-soft as usual.
|
||
|
||
### → Lane C also: `crate_stack` for floor piles / dig
|
||
`procity_fit_crate_stack_01.glb` (`fittings.crate_stack`, 0.71×0.72 m, 0.9 m, 3k) — a stack of 3
|
||
plastic milk crates, good for record-store floor dressing or the dig scene. Optional; map a kind to
|
||
`'crate_stack'` if useful.
|
||
|
||
### → Fable: E1 (bake) + E3 (fal) status
|
||
- **E1 bake DONE + committed** — 6 props ≤8k, published. Feeds C's re-measure + your v1.1 tag.
|
||
- **E3 fal DONE** — glass_case + bus_shelter + magazine_rack generated on fal Hunyuan v2, **~$0.64
|
||
total** (log in AUDIT R5), ≤2 attempts each, all baked ≤8k + published. Guardrail held (nothing
|
||
else on fal). `validate --depot` 0 err. Last two primitive-only fittings retired.
|
||
- **E2 depot growth** next (v2-serving library props).
|
||
|
||
---
|
||
|
||
## Round 4
|
||
|
||
## → Lane D (task D2): decimated peds ready to validate
|
||
|
||
The 19 ped rigs in `web/models/peds/` are decimated and staged (committed `518678d`, ref
|
||
`lane-e/round4-peds`). Please validate rig binding + animation:
|
||
|
||
- **≤3k tris each** (range 1564–2805; per-ped before/after in `pipeline/_peds_decim.json`).
|
||
Fleet total **924k → 46.5k tris** (48.6k → 2.45k avg). At the 24-rig near cap ≈ **≤67k**.
|
||
- **Skinning preserved**: `JOINTS_0`/`WEIGHTS_0` intact; **skeleton unchanged** (nodes + 65
|
||
joints identical to source on every ped I checked), so the shared `walk.glb`/`idle.glb`
|
||
clips retarget unchanged. **`walk.glb`/`idle.glb` were NOT touched.**
|
||
- Method: `gltf-transform weld` → `simplify --ratio 0.05 --error 0.12` (meshoptimizer — works
|
||
because these are manifold skinned meshes, not R3 TRELLIS shell-soup). Contact sheet:
|
||
`docs/shots/laneE/peds_decimated.png` (silhouettes/textures clean at street distance; fingers
|
||
merge + faces flatten as expected at 3k, no melting).
|
||
- Originals backed up (gitignored) at `pipeline/.peds_orig/` if you need a before/after diff.
|
||
|
||
If any rig fails to bind/animate or a silhouette reads badly, tell me the file(s) and I'll
|
||
re-run that one with a gentler error (0.08 keeps ~50% more tris but still <5k). Then F re-measures
|
||
gate 3 after your sign-off.
|
||
|
||
## → Lane C: E2 asset re-exports
|
||
|
||
1. **`procity_fit_record_crate_01.glb` — FIXED.** Root cause of your `undefined 'uri'` crash: the
|
||
material's `metallicRoughnessTexture` + `occlusionTexture` both pointed at a **sourceless
|
||
texture** (the packed AO/roughness map failed to encode to WebP during normalize; base-colour
|
||
+ normal were fine). Fix: `pipeline/fix_glb_textures.py` strips material slots that reference a
|
||
sourceless texture (orphaning it — three.js loads textures lazily, so no crash). **Verified it
|
||
loads in the real vendored `GLTFLoader`** (meshes=1, tris=5000, textures ok) alongside a
|
||
bookshelf control. Re-published to the depot — re-map `record_crate`/`recordBin` in `glb.js`
|
||
(they should already be mapped) and it'll upgrade instead of falling back.
|
||
2. **counter-with-till — LIVE.** New fitting `counter_till` (`procity_fit_counter_till_01.glb`):
|
||
**1.61 × 0.64 m footprint, 1.15 m tall**, timber counter + cabinet doors + period beige till on
|
||
top (flux_local concept → trellis_mac, on-device). Fixes the R3 4 m till-less counter that
|
||
squashed. **Verified it loads in the vendored `GLTFLoader`** (32k tris — TRELLIS shell-soup floor,
|
||
within decision-#2's 25–77k for focal props) and **live on the depot** (`validate --depot` 0 err).
|
||
→ **In `glb.js` re-add `counter` to `KIND_TO_GLB` mapping to `'counter_till'`** (NOT the old
|
||
`counter` = balcao, which is a 4 m long bar counter kept for shops that want it). The manifest
|
||
`fittingAliases.counter` → `counter_till` too. It's base-origin (minY=0), so your footprint-fit +
|
||
floor-plant works unchanged; at a 1.4–2.0 m counter slot it scales to a believable ~1.0–1.4 m
|
||
tall counter.
|
||
|
||
## → Fable: status
|
||
|
||
- **E1 (gate-3 critical path): DONE.** Peds ≤3k, committed + pinned. Unblocks D2 → F3.
|
||
- **E2a record_crate: DONE + verified** (loads in vendored GLTFLoader). Re-published.
|
||
- **E2b counter: in progress** (local, on-device — no fal spend per decision #3).
|
||
- Not doing: fal.ai spend (glass-case/bus-shelter stay parked), hero-prop bake-to-lowpoly.
|