Extend build_towns.py: bounded way[highway](bbox) out geom per town -> raw cached in-repo -> roads[]
in A's v2 contract ({id,kind,pts:[[lat,lon]]}, kind=raw OSM highway class). katoomba_real is now
schema v2 with 484 real streets (Bathurst Rd, Cliff Drive, ...). Fidelity cut (charter risk #4, flagged
to A): fetch every ROAD_KIND tier -> raw; EMIT the town street tier only, excluding service/track/
footway/path/steps/cycleway (1461 of 2506 Katoomba ways = bushwalking tracks/sidewalks/stairs/driveways).
A's lift builds a real town from it (verified): 484 streets -> 872 nodes/799 edges (main 232 side 531
arcade 36) -> 12 blocks, 20 lots, 3 venues (pub/band_room/rsl), 12 gigs. pickVenues lands venues on the
real graph (ledger #2 met).
FINDING for A (alpha failure-list, charter risk #3): on the real graph, spine posters overhang crossing
carriageways -- selfcheck kerb-clearance fails on 4 seeds (-1.6 to -2.5m; e.g. seed 20261990 poster 50 vs
edge 58 = -2.42m). Same class as A's R15/R16 corner-poster fix, now on real cross-street geometry. Owner:
A (pickVenues/POSTER_CLEAR on the real graph, the 'A again' harden step) -- my roads data is VALID (cache
validates; failures are plan-level poster placement). Golden settles after A's fix (pre-fix 0x6014a8e6).
ODbL: raw roads committed, SOURCES.md updated.
-> Lane A: katoomba roads live; harden the lift + resolve the poster finding + re-pin the golden.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
471 lines
35 KiB
Markdown
471 lines
35 KiB
Markdown
# LANE E — cross-lane notes
|
||
|
||
## Round 18 — v4.0-alpha REAL ROADS: the road-geometry fetch ⟨v4-alpha⟩
|
||
|
||
> Ledger #3 — E fetches the real street geometry; A's lift builds the CityPlan graph from it.
|
||
> **`katoomba_real` is now schema v2 with 484 real streets** (the alpha town). → **Lane A: roads are
|
||
> live; harden the lift, resolve the surfaced poster-clearance finding below, re-pin the golden.**
|
||
|
||
**The data.** `build_towns.py` extended with a bounded `way["highway"~…](bbox); out geom;` per town →
|
||
raw cached in `_raw/<key>.roads.overpass.json` (fetch once, John's standing Overpass auth) → `roads[]`
|
||
in A's v2 contract (`{id, kind, pts:[[lat,lon],…]}`; `kind` = raw OSM highway class, A's `ROAD_KIND`
|
||
maps it, full geometry shipped for A's Douglas–Peucker). **katoomba_real: 484 streets** (residential
|
||
243, tertiary 65, trunk 58, secondary 51, primary 32, unclassified 23, pedestrian 12 — real names:
|
||
Bathurst Rd, Cliff Drive, Civic Place, …), 5344 points.
|
||
|
||
**Fidelity cut (charter risk #4 — flagged to A, who owns the knob).** I FETCH every `ROAD_KIND`-mapped
|
||
tier (→ raw cache, full provenance / beta-ready) but EMIT only the **town street tier** — drivable
|
||
roads + pedestrian malls — EXCLUDING `service`/`track`/`footway`/`path`/`steps`/`cycleway`. In
|
||
Katoomba's 3 km bbox that excluded tier is **1461 of 2506 ways** (Blue-Mountains bushwalking tracks,
|
||
sidewalks, stairs, driveways, parking aisles) — noise for a town street graph and a ~2500-edge blow-up.
|
||
The full set is in the raw cache if A wants a tier back (one filter change). This is the honest cut.
|
||
|
||
**A's lift builds a real town from it** (verified, `generatePlanFor` seed 20261990): 484 streets →
|
||
**872 nodes / 799 edges** (main 232, side 531, arcade 36) → 12 blocks, 20 lots, 20 shops, **3 venues
|
||
(pub/band_room/rsl), 12 gigs**. `pickVenues` lands venues on the real graph — ledger #2 requirement met.
|
||
|
||
**⚠ FINDING for Lane A (the alpha's failure-list deliverable — charter risk #3).** On the real graph,
|
||
**spine posters overhang a crossing carriageway**: selfcheck's kerb-clearance gate fails on 4 seeds
|
||
(spine poster clears its cross-edge by **−1.6 to −2.5 m**; e.g. seed 20261990 poster 50 vs edge 58 =
|
||
−2.42 m). Same CLASS as A's R15/R16 corner-poster fix, now on **real** cross-street geometry — a
|
||
venue's frontage seats on the spine where a real side street crosses, so its poster reaches over that
|
||
carriageway. **Owner: A** (`pickVenues` / `POSTER_CLEAR` on the real graph — the "A again" harden
|
||
step). **My roads data is valid** — the v2 cache validates (roads accepted); the 4 failures are
|
||
plan-level poster placement, not cache errors. Not a fidelity artifact either: the crossing streets are
|
||
real primary town streets, so trimming roads wouldn't fix it. Golden settles after A's fix (pre-fix
|
||
value `0x6014a8e6`). Handshake E→A: **katoomba roads live + this finding**.
|
||
|
||
**Other 4 towns:** roads produced + committed as data-only (A hardens against them as they land; only
|
||
katoomba gates the round). Roads are ODbL (© OSM contributors); raw responses committed, `SOURCES.md`
|
||
updated with the roads tier + the fidelity cut.
|
||
|
||
## Round 17 — v3.2 THE SCOUT: real town caches + the verify fix ⟨v3.2 amendment⟩
|
||
|
||
> The real-map scout for **v4 THE REAL MAP** (John's pick) — bounded data groundwork, no new game
|
||
> systems. **5 real AU town caches produced + the `--verify` false-drift fixed.** → **Lane A: caches
|
||
> are live under `web/assets/towns/`; pin the per-town goldens** (printed below by selfcheck) and feed
|
||
> them through `plan_osm` — E→A handshake.
|
||
|
||
**Ledger #4 — `publish.py --verify` false-drift fixed (the warm-up).** The drift check filtered the
|
||
depot list to `procity_*`, so genuinely-published non-`procity_` assets (`sit.glb`,
|
||
`vintage-cash-register.glb`) were false-flagged "not live." Now it compares the published-NAME record
|
||
(`_published.json`) against the depot's full file list (`fetch_depot_files()`), confirming any name is
|
||
live — and only flags OUR `procity_` namespace as unrecorded, ignoring other projects' files on the
|
||
shared depot. `--verify` now: `✓ all 39 recorded assets live`.
|
||
|
||
**Ledger #6 — the real-map scout (the round's biggest item).** `pipeline/build_towns.py` ports
|
||
thriftgod `build_city()`'s Overpass route to **per-town** caches in A's `procity-town-cache/1` contract
|
||
(`validateTownCache()`). Pipeline: **bounded** Overpass query → raw response cached in-repo → dedupe
|
||
(OSM node+way) → suburb-fill → **parody** the trademarked charity/franchise chains (indie shops keep
|
||
real OSM names) → OSM `shop=*` → registry SHOP_TYPE → `web/assets/towns/<key>.json`.
|
||
|
||
| cache | town | shops | span | types | selfcheck golden (→ A pins) |
|
||
|---|---|---|---|---|---|
|
||
| `katoomba_real` | Katoomba NSW | 20 | 1.11 km | book4 opshop13 record2 video1 | `0x61760542` |
|
||
| `newtown_real` | Newtown, Sydney | 18 | 2.36 km | book4 opshop6 record5 toy2 video1 | `0x95978b45` |
|
||
| `fremantle_real` | Fremantle WA | 21 | 1.89 km | book5 opshop10 pawn1 record1 toy3 video1 | `0xee9fd09a` |
|
||
| `bendigo_real` | Bendigo VIC | 9 | 2.06 km | book3 opshop3 pawn1 record1 video1 | `0x5e3e76a6` |
|
||
| `castlemaine_real` | Castlemaine VIC | 6 | 1.25 km | book2 opshop4 | `0xf1a628a1` |
|
||
|
||
**Data discipline:** deterministic (same raw → byte-identical cache; shops id-sorted, no RNG — verified
|
||
re-run byte-identical); bounded (all spans < 2.4 km, well under the 5 km mega-strip floor); raw
|
||
responses committed to `web/assets/towns/_raw/` so **re-runs never re-fetch** (Overpass hit once per
|
||
town, John-authorized). `katoomba_real` is the fixture-comparison town (vs the hand-made `katoomba`).
|
||
All 5 boot through the full structural + gig suites (selfcheck **16596/16596 green, synthetic golden
|
||
`0x3fa36874` unmoved**). **Provenance:** OSM is **ODbL** — `SOURCES.md` (E-owned licence table) +
|
||
`license`/`attribution` fields on every cache; only public POI data (name/type/coord/suburb), no
|
||
private-individual data.
|
||
|
||
## Round 16 — v3.1 THE FLIP: the drummer's sit clip (E asset half) ⟨v3.1 amendment⟩
|
||
|
||
> Ledger #2 asset half — the drummer finally sits. **E produces the clip; D wires it** (`_rotWithHips`
|
||
> + `spawnRig` foot re-plant + F's seated-stature gate). **`sit.glb` delivered, verified, committed +
|
||
> on depot.** → **Lane D: it's ready** (`web/models/peds/sit.glb`, canon binds 195/195, seated confirmed).
|
||
|
||
**Asset:** `web/models/peds/sit.glb` — a mesh-free animation clip, **structurally identical to the
|
||
shipped `walk.glb`/`idle.glb`**: 66 nodes (65 `mixamorig` bones + root), 0 meshes, 1 dangling skin
|
||
(inverseBindMatrices + 65 joints, referenced by 0 nodes — the vestigial artifact Blender glTF I/O
|
||
leaves, which makes glTF tools read it as an armature), 195 channels (65 bones × TRS). 109 KB, no Draco.
|
||
|
||
**Source + provenance:** Adobe **Mixamo `Sitting_Idle.fbx`** (533 KB) — fetched by `scp` from the
|
||
tailnet source box `johnking@100.91.239.7:~/Documents/mixamo-fetch/out/` per Lane D's recipe (the
|
||
local `~/Documents/mixamo-fetch` path is stale/empty). Converted on-device via Blender headless
|
||
(`pipeline/fbx_to_clip.py`, **new tool**): import FBX → export skinned GLB → pure-python mesh-strip
|
||
that keeps the skeleton + dangling skin + animation and re-indexes/compacts. Raw Mixamo `mixamorig:`
|
||
naming is already canonical (`rigs.js._canon` normalizes `mixamorig<N>` → `mixamorig`, so no bone
|
||
rename). **Licence zone: Adobe Mixamo royalty-free animation license — the SAME zone as the
|
||
already-shipped `walk`/`idle` clips** (the whole ped fleet is Mixamo-rigged). Mesh stripped, so only
|
||
the skeleton *motion* ships (retargeted onto the game's own peds), never Mixamo character geometry.
|
||
|
||
**Verified (E's half — D verifies the wired in-scene pose):**
|
||
- **three.js** (vendored GLTFLoader): loads, dur 4.33 s, 195 tracks, 65 bones, **195/195 bind** to a
|
||
ped skeleton after `_canon`, **0 NaN** track values (mesh-strip re-index is clean).
|
||
- **Blender** (the tool that measures `walk` at 1.276 m standing): the clip poses **seated** — Hips
|
||
descend 0.955 m (standing) → **0.299 m**, stature 1.276 → 0.758 m. Eyes-on bone-dot render confirms
|
||
the folded seated silhouette vs walk's upright one: `docs/shots/laneE/sit_clip.png` (left walk, right sit).
|
||
|
||
**Depot:** published to `digalot.fyi/3god` via the passwordless tailnet ingress under **John's R13
|
||
authorization** (`_published.json` 38 → 39; confirmed 200 on the public depot). **NOTE (benign):**
|
||
`sit.glb` — like `vintage-cash-register.glb` — is non-`procity_`-prefixed, so `publish.py --verify`'s
|
||
`procity_`-only drift filter lists it as "not live" (a **false drift** — it IS live). Cosmetic only;
|
||
`validate_manifest` gates are unaffected (clips are not manifest GLBs).
|
||
|
||
**"Manifest":** ped clips load by **convention** (`rigs.js` `base='models/peds/'`), NOT via
|
||
`web/assets/manifest.json` — so there is no asset-manifest entry, same as `walk`/`idle`. D owns the
|
||
loader wiring (`spawnRig`/fleet). Bench-sit loiter (Fable's stretch goal) reuses this same clip.
|
||
|
||
## Round 14 — v3.0 PROVENANCE FREEZE ✅ (release round)
|
||
|
||
> **FROZEN for the v3.0 tag** (2026-07-16). Lane E R14 is a provenance close-out — **no new synthesis**
|
||
> (genres final: pubrock / grunge / covers; John "fine for now"). Everything below is the durable
|
||
> Lane-E release contract; the deep per-asset narrative lives in `pipeline/AUDIT.md` (current through
|
||
> its R13 section). Counts tool-verified (glb_stat / ffprobe / manifest parse) and adversarially
|
||
> re-audited by two independent passes — both PASS, **0 release blockers**.
|
||
|
||
**Release status**
|
||
- **Gate 3 GREEN** — `validate_manifest.py --depot` → 0 errors, 0 warnings. Not blocking the tag.
|
||
- **`publish.py --verify`** — depot matches `_published.json` (38 entries). The lone drift note
|
||
(`vintage-cash-register.glb` absent from the *tailnet mirror*) is cosmetic: it's a depot-reuse asset
|
||
and IS live on the public `digalot.fyi` depot (Gate 3 confirms), so the manifest's real target is fine.
|
||
- **Licensing 🟢 clean** — 0 quarantined/branded assets in the manifest (grep of every AUDIT QUARANTINE
|
||
token — vitra/eames/calligaris/ikea/technics/1210/tuborg/uploads_files/akai/mpc/turntable — → 0 hits).
|
||
- **Poster templates (R13 debt #4): no ask from B** → no new templates (E builds only on B's ask).
|
||
- R14 doc-consistency fixes applied: AUDIT.md R13 section added; `roomtone-video` gain 0.3→0.35 aligned
|
||
to the manifest; manifest `glb_law` string amended to state the ≤8k-hero / ≤14k-instrument exceptions.
|
||
|
||
### Provenance & licensing — by source zone (all 🟢 web-ok)
|
||
|
||
| zone | assets | source | method | licence |
|
||
|---|---|---|---|---|
|
||
| House library GLBs | 18 | thriftgod / house depot, John-presorted **generic** props (benches, shelves, counter, crate, bin, streetlight, food_cart…) — no recognizable trademark | `normalize.py` (passthrough or COLLAPSE ≤5k) | 🟢 web-ok |
|
||
| On-device synthetic GLBs | 11 (+5 `_hi`) | MODELBEAST `flux_local` (FLUX.2-klein-4B) concept → `trellis_mac` mesh | 6 hero via `bake_lowpoly.py` (≤8k) · 5 gig instruments via `normalize`→`decimate_props.py` (14k) | 🟢 CC0-equiv, 100% on-device |
|
||
| fal-Hunyuan GLBs | 3 | `flux_local` concept → `fal-ai/hunyuan3d/v2` (glass_case, magazine_rack, bus_shelter) | `bake_lowpoly.py` (≤8k) | 🟢 original (~$0.64 total spend) |
|
||
| Depot-reuse GLB | 1 | `cash_register` = John's own `thriftgod_hero` (already live) | reuse (not regenerated) | 🟢 cleared |
|
||
| Skins (2D) | 96 | 90sDJsim ports (7 facade + 2 ground) + thriftgod R1 store-art + `flux_local` gap-fill fronts/posters | `gen_skins.py --local` (on-device) | 🟢 web-ok |
|
||
| Audio pack | 28 | 100% procedural numpy synthesis (`gen_audio.py`) | oscillator/noise render → ffmpeg OGG+M4A | 🟢 CC0-equiv, parody-safe |
|
||
| Stock packs | 3 (350/311/273) | GODVERSE `dealgod` Postgres — **real** cover art | `build_stock_pack.py`: parody-transform metadata + atlas | 🟢 real-art / parody-metadata |
|
||
| Ped clip **⟨v3.1⟩** | `sit.glb` (E R16; + pre-existing `walk`/`idle` from D/90sDJsim) | Adobe **Mixamo** `Sitting_Idle.fbx` | `fbx_to_clip.py` (Blender, mesh-strip) | 🟢 Mixamo royalty-free (motion only, mesh stripped) |
|
||
| Town caches **⟨v3.2⟩** | 5 real AU towns (Katoomba/Newtown/Fremantle/Bendigo/Castlemaine) | **OpenStreetMap** via Overpass API | `build_towns.py` (fetch → dedupe/suburb/parody/type-map) | 🟢 **ODbL 1.0** (© OSM contributors; `web/assets/towns/SOURCES.md`) |
|
||
|
||
**GLB fleet = 33 manifest (23 fit + 10 furniture) + 5 `_hi` = 38 published.** Only external spend ever:
|
||
~$0.64 (fal-Hunyuan, R5). Everything else is $0 / on-device (electricity only).
|
||
|
||
### Audio inventory — 28 assets (`web/assets/audio/`, committed; contract = `manifest.audio`)
|
||
|
||
All procedural, seeded, $0, parody-safe; OGG/Opus + M4A/AAC; beds+music loudnorm −16 LUFS, SFX peak-norm.
|
||
|
||
| group | n | assets | gig? |
|
||
|---|---|---|---|
|
||
| ambience | 7 | street-day, street-night, rain, roomtone-{retail,milkbar,video}, **crowd-walla** | crowd-walla `gig:true` |
|
||
| sfx | 8 + 6 steps | doorbell, door-open, till, riffle, toast, tram-bell, tram-rumble(loop), **applause**; footstep{pavement×3, timber×3} | applause `gig:true` |
|
||
| music | 7 | record-shop, milkbar, video-synth, arcade, **gig-pubrock**, **gig-grunge**, **gig-covers** | 3 gig beds `gig:true` |
|
||
|
||
- **Gig beds (R13, the manifest key IS the gigKey `gig-<genreKey>`):** `gig-pubrock` (pub, 0.6, 13.4s),
|
||
`gig-grunge` (band_room, 0.6, 17.2s), `gig-covers` (rsl, 0.5, 19.2s). Seamless loops, non-silent
|
||
(verified −15.7…−17.6 dB mean). Old `pubrock-live` fully retired (files gone, no live refs).
|
||
|
||
### GLB inventory — 38 (tri counts glb_stat-verified; footprints/heights match manifest; no Draco, tex ≤1024)
|
||
|
||
**Fittings (23):** record_crate 5k · wire_shelf 330 · clothes_rack 3.2k · bookshelf 5k · cube_shelf_wide 5k ·
|
||
counter 276 · coffee_table 495 · work_table 1.6k · box_crate 78 · crate_stack 3k · **listening_booth 8k ·
|
||
drinks_fridge 8k · arcade_cabinet 8k · milkshake_mixer 8k · counter_till 8k** (bake_lowpoly hero) ·
|
||
**glass_case 8k · magazine_rack 8k** (fal) · **electric_guitar / bass_guitar / drum_kit / guitar_amp /
|
||
mic_stand = 14k each** (R13 decimate) · cash_register 6k (reuse).
|
||
**Furniture (10):** bench 1.9k · bench_modern 640 · bench_wood 4k · longbench 1.2k · park_bench 5k ·
|
||
streetlight 5k · food_cart 5k · **novelty_record 8k** (bake_lowpoly) · **bus_shelter 8k** (fal) · bin 3k.
|
||
**Preserved `_hi` originals (5, on depot, NOT manifest-referenced — LOD/re-bake source):**
|
||
electric_guitar 43.5k · bass_guitar 36k · mic_stand 34.1k · drum_kit 26.2k · guitar_amp 96.6k.
|
||
|
||
### Skins — 96 (`web/assets/gen/*.jpg`, local; all present, 0 missing/orphan)
|
||
|
||
facade 48 (43 blank + 5 baked-sign: djsim-video/milkbar/pub, market, grimy) · sky 10 · ground 10 ·
|
||
wall 8 · interior floor 6 · interior surface 7 · awning 3 · **poster 4** (grunge/retro/screenprint/xerox,
|
||
blank name-band). Per-shop-type facade coverage ≥2 for all types (validate_manifest asserts; min = 3).
|
||
|
||
### Stock packs — 3 (indexes committed in `pipeline/packs/`; atlases on depot; read directly by Lane C)
|
||
|
||
record 350 (6 atlases, 256²) · book 311 (4 atlases, 176×256) · toy 273 (5 atlases, 256²). Real cover art
|
||
(GODVERSE), **parody-transformed metadata** (sha256-seeded word-banks; no real trademarks). All pass `validate_pack`.
|
||
|
||
### By-design notes (documented so a future reader doesn't re-flag them)
|
||
- **5 `_hi` in `_published.json` but not the manifest** — intentional archival hi-poly originals (R13).
|
||
- **Stock-pack indexes not in the manifest** — by design; Lane C reads `pipeline/packs/*` directly, atlases ship on the depot.
|
||
- **`glb_law` tiers** — ≤5k library / ≤8k baked hero (TRELLIS shell-soup, texture-intact) / ≤14k gig instruments; now stated in the manifest convention string + AUDIT.
|
||
- **`validate_manifest` prints "skins 85 groups"** — a shallow `sk.values()` count (the nested `interior` dict counts as 1); true leaf total is 96. Cosmetic label, flagged for v3.1.
|
||
- **Minor stale ledger figures** (non-live): `food_cart` 4991 vs `_normalize_results` 5000 (9-tri); a superseded `cube_shelf_01` entry in `_normalize_results` (DROP-filtered from the manifest, rejected in AUDIT). Harmless.
|
||
|
||
### Deferred to v3.1 (documented, not started)
|
||
- **`street_bin` decimation** — B's non-blocking, *conditional* ask ("if the combined-lane budget tightens"); the bin is ~3k tris (within house law), and B measured ~100k tri / ~150 draw margin at the busiest block, so no action for v3.0.
|
||
- **Texture-bake-to-lowpoly / KTX2-Basis atlas pass** — if a future budget tightens; the `_hi` instrument originals + the hero props are the LOD source (R9 VRAM note + AUDIT).
|
||
|
||
## Round 13 — two genres, the gigKey rename (debt #1), lighter instruments
|
||
|
||
**v3.0-beta THE DISTRICT.** All local work landed + verified; the one gated step (depot publish)
|
||
waits on John. Verification: core gates GREEN (scaffold · consistency 0-warn · citygen selfcheck
|
||
3273/3273 incl. v3 gig-layer + assets-on-disk), manifest validator `--depot` GREEN, and the real
|
||
app boots clean flags-off / `?gigs=1` (A's 3-venue district, 14 gigs/posters — all three gig beds
|
||
resolve by canonical key, no stale key, 0 console errors) / `?noassets=1&gigs=1` (0 audio + 0 GLB
|
||
fetches, silent-and-fine).
|
||
|
||
### 1. gigKey rename — DONE (debt #1). **→ Lane B + Lane F: switch your readers.**
|
||
The manifest music key IS the gigKey, **always `gig-<genreKey>`** — one key, zero mapping. Renamed
|
||
`pubrock-live` → **`gig-pubrock`**; added `gig-grunge`, `gig-covers`. Files are now
|
||
`audio/music-gig-{pubrock,grunge,covers}.{ogg,m4a}`; the old `music-pubrock-live.*` are **deleted**.
|
||
Manifest regen'd + validates 0/0; no stale `pubrock-live` anywhere.
|
||
- **→ Lane F (interior_mode.js):** DELETE the `GIG_BED` bridge (the `{'gig-pubrock':'pubrock-live'}`
|
||
map + `gigBedKey()`); read `ra.gigKey` (`gig-<genre>`) straight against `AUDIO.music[...]`. The
|
||
bridge currently maps to the now-gone `pubrock-live` → **fails soft to silence** (I verified: no
|
||
crash, just a silent band) until you delete it. This is your R13 task #4.
|
||
- **→ Lane B (audio.js ~L274/276):** you hardcode `'pubrock-live'` for the street spill — read each
|
||
venue's `gigKey` bed instead (`AUDIO.music[venue.gigKey]`, i.e. `'gig-'+genreKey`). Same
|
||
fail-soft-silent until you switch. Your R13 task #2 (per-genre spill) covers this.
|
||
- **CITY_SPEC §v3 contract line (coordinate wording w/ A):** *"The audio manifest key for a gig
|
||
genre IS the gigKey, always `gig-<genreKey>` (`gig-pubrock`, `gig-grunge`, `gig-covers`). No
|
||
mapping table: C emits `room.audio.gigKey`, E names the bed, B/F read the same key."*
|
||
|
||
### 2. Two new gig beds — grunge (band_room) + covers (RSL).
|
||
Procedural, seeded, loopable, OGG+M4A, loudnorm, $0 on-device (same discipline as R11/12). All three
|
||
are audibly + spectrally distinct (spectral centroids pubrock 2336 / grunge 2271 / covers 1115 Hz;
|
||
contact sheet `docs/shots/laneE/gig_beds_spectrograms.png`):
|
||
- **`gig-grunge`** (0.6 gain, 17s): down-tuned sludge, quiet-loud dynamic (clean-ish verse → full-fuzz
|
||
chorus — visible in the spectrogram), loose heavy kit. Sits at −14.6 LUFS (genre-dense; a 1.02
|
||
*inter-sample* peak, not real clipping — plays at gain 0.6 so absolute output ≈0.6).
|
||
- **`gig-covers`** (0.5 gain, 19s): mellow RSL covers band — new drawbar-`organ()` voice, walking
|
||
bass, soft kit, sparse organ answer. Warm, dark highs (that low continuous band + horizontal
|
||
harmonic striations in the spectrogram = the organ). New synth helpers: `organ()`, `grungechord()`.
|
||
- **John may re-flavour genres (names only) — I synthesize to order.** Note it's now a coordinated
|
||
A+E change: the genreKey lives in A's `registry.js` (drives `gigKeyFor` → the bed name), so a
|
||
re-flavour = A renames the genreKey + E renames/re-synths the bed. Defaults grunge/covers are live.
|
||
|
||
### 3. Instrument decimation — DONE. 5 GLBs → 14k tris each (≤15k target). **→ Lane D.**
|
||
Collapse-decimated (Blender, `pipeline/decimate_props.py`) — **not** voxel-remesh, so thin features
|
||
survive: mic pole + hanging cable, drum cymbals/stands, guitar necks/headstocks all read (thumbs
|
||
re-rendered + eyeballed). Dims/orientation/base-origin/WebP preserved; GLB-law clean (no Draco, tex
|
||
≤1024). Fleet **236k → 70k tris (−70%)** across the district's multiple stages.
|
||
|
||
| instrument | tris (norm→14k) | footprint (m) | height |
|
||
|---|---|---|---|
|
||
| electric_guitar | 43.5k → 14k | 0.38×0.08 | 1.00 |
|
||
| bass_guitar | 36.0k → 14k | 0.34×0.13 | 1.15 |
|
||
| mic_stand | 34.1k → 14k | 0.57×0.57 | 1.70 |
|
||
| drum_kit | 26.2k → 14k | 1.50×1.24 | 1.30 |
|
||
| guitar_amp | 96.6k → 14k | 0.52×0.29 | 0.61 |
|
||
|
||
- **→ Lane D:** wire against the SAME filenames (`procity_fit_{electric_guitar,bass_guitar,mic_stand,
|
||
drum_kit,guitar_amp}_01.glb`) — footprints/heights in the manifest are **unchanged**, so your
|
||
`instrumentFor(role)` fittings are unaffected; they're just lighter now. (debt #2)
|
||
- High-poly originals preserved as `_hi` variants in `_normalized/` (git-ignored) for the depot.
|
||
|
||
### 4. Depot publish — DONE (John OK'd direct publish). Gate 3 flag CLEARED.
|
||
Published via the passwordless tailnet ingress (`GOD3_DEPOT=http://100.94.195.115:8788` — this box is
|
||
allowlisted, so no secret handling). The 5 base names now serve the **14k bytes** and the 5 `_hi`
|
||
hi-poly originals are live — verified on the public `digalot.fyi` depot (amp base 5.9MB→1.3MB, `_hi`
|
||
5.9MB live, byte-identical to local). `_published.json` 33→38 (the 5 `_hi` recorded).
|
||
`validate_manifest --depot` GREEN. **The −70% decimation win is now live in production**, not just
|
||
?localdepot. (The `vintage-cash-register.glb` drift note from publish.py is pre-existing + unrelated —
|
||
a depot-reuse asset, not a Lane E file.)
|
||
|
||
## Round 9 — VRAM audit verdict + one rec for C
|
||
|
||
**VERDICT: fine to ship v2.0.** Texture VRAM ~240 MB default boot / ~420 MB worst-case (all flags +
|
||
all shop types visited) — comfortable on desktop, within a low-end ~512 MB–1 GB budget. Full numbers
|
||
in `AUDIT.md`. **Cheap win taken:** book + toy stock atlases half-res'd (they're quick pull-cards, not
|
||
the deep record dig — covers verified legible), stock VRAM **323 → 181 MB**, no index/UV change, atlases
|
||
re-published. Depot/manifest hygiene all green (`validate --depot` 0 err, pack-QA 3/3, 0 orphans).
|
||
|
||
**→ Lane C (optional, NOT blocking v2.0 — v3 follow-up):** `stockpack.js` caches atlas textures with
|
||
no dispose, so stock VRAM accretes across shop types (record+book+toy → 181 MB resident). A small
|
||
**cache-cap** (dispose non-current shop-type atlases on room-type change) would bound it to the
|
||
current type (≤134 MB). Not needed for the tag; flagging for when you next touch stockpack.
|
||
**→ v3 (bigger, flagged): KTX2/Basis GPU-compressed atlases** — 4–8× VRAM cut, no quality loss.
|
||
|
||
**→ Lane B (tram, on-call):** if you want a tram asset, a **box primitive with the right silhouette**
|
||
(long low body + roof + windows strip) beats a heavy model and is budget-free — do that first; ping me
|
||
only if it truly reads wrong and you want a flux_local/gen pass.
|
||
|
||
|
||
## Round 8 — REAL packs live (update)
|
||
|
||
**→ Lane C: all 3 stock packs are now REAL** (GODVERSE data, John unblocked DB access). Same schema
|
||
you wired — just richer data, no code change needed:
|
||
- **record** — 350 real vinyl (John's store stock, real Discogs covers), 6 atlases, cell 256×256.
|
||
- **book** — 311 real book covers (`dealgod.products` books), 4 atlases, cell **176×256 (portrait
|
||
face-out cover**, not thin spine — I only have cover art; render books face-out).
|
||
- **toy** — 273 real toy/collectible photos, 5 atlases, cell 256×256.
|
||
Metadata is parody-transformed (no real titles/artists/brands); covers are real art; book prices are
|
||
seeded (source is info-only). Indexes committed to `pipeline/packs/` + staged (`assets/models/`, your
|
||
`?localdepot` path); atlases published to the depot. All pass the pack-QA gate. If your book shelf
|
||
wants thin spines, tell me — I'd need spine art (don't have it; covers only).
|
||
|
||
## Round 8 (synthetic samples — superseded by REAL above)
|
||
|
||
**→ 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.
|