diff --git a/C-progress.md b/C-progress.md index 036f42e..3aa7b1a 100644 --- a/C-progress.md +++ b/C-progress.md @@ -3,7 +3,54 @@ *Status: **v1 complete & verified**. Standalone interiors library + test page. Every shop door opens into a unique, seeded, themed interior, generated on demand in ~4ms, byte-identical every revisit.* -Last updated: 2026-07-16 (round 22) · owner: PROCITY-C · reviewer: Fable +Last updated: 2026-07-16 (round 23) · owner: PROCITY-C · reviewer: Fable + +--- + +## Update 2026-07-16 (round 23, v5.0-alpha THE FIRST REAL CRATE) — the crate in the room (ledger #3) + +R23 §Lane C. **Verdict: the contract holds — G's atlas needs no re-emit.** Verified against G's +`stock_godverse/3962749/` (120 real records from Monster Robot Party's DEEP HOUSE crate 550, 2 × 2048², +cell 256²) in a fresh context. Ran **before** Fable's merge deliberately — if the contract failed, that +blocks the merge. It didn't. + +**PASS — the first real crate renders in a PROCITY room:** +- **Resolves through the existing `stockAdapter` path** via a base override — no C code change needed for + the single-shop alpha. 120 items · 2 atlases · **8/8 stock meshes atlas-backed** (real covers). The + sleeves are legible on screen: *peperecordings · DEEPCHILD · "39" · mff FREAKS · The Angels Instinct feat + Charlie Ashcroft* — a real deep-house crate. +- **Ceilings hold:** **118 draws** with real covers (≤350 law), `pathOK`, 0 carves. +- **Fail-soft holds** (clean cache): missing atlas → `null` → parody (20 parody meshes, 110 draws); no + adapter (the `?noassets` shape) → parody, zero fetch. **0 console errors.** +- **G's index is compliant:** `items[]` sorted by id (my R22 determinism ask honoured), provenance present, + `uv ⊂ [0,1]`, 2 atlases = exactly my cap, `cell_w/cell_h` harmless (C reads `uv`, never `cell`). + +**I got a number wrong, and the first real data caught it (LANE_C_PUB §7.2 CORRECTED).** My R22 ceiling +said "≤8 MB decoded" — derived from assuming a "typical shop" is ~16 items @1024². **A real record shop is +120 records.** At the contract's own 256² cell that's 2 × 2048² ≈ **32 MB**, 4× my figure. The *structural* +rules held (≤2 atlases; 2048² for 60+ items — G landed exactly on the cap); only the MB number was wrong, +because it described a shop that doesn't exist. **Corrected:** ≤2 atlases/shop @≤2048², **LRU = 1 shop's +atlas set resident ⇒ ≤32 MB peak** (still bounded; still far better than the 181 MB unbounded accretion E +measured). And explicitly: **don't shrink the cell below 256² to save VRAM** — the dig shows a cover +close-up; resolution is the one thing the player actually sees. + +**G's ask #1 CONFIRMED — and worse than reported (LANE_C_PUB §8: C's spec, C's fix).** `stockpack.js` keys +`_packs`/`_resolved` by **`type` alone**, so `base` is **ignored on a cache hit**. Measured: +`preloadStockPack('record', {base:'…/3962749/'})` then `preloadStockPack('record', {base:'assets/models/'})` +**returned the identical object**. Consequences: (1) two stocked shops collide — the 2nd serves the 1st's +crate (blocks v5.0-beta, as G said); (2) **it breaks the fail-soft law** — an *unstocked* shop of a cached +type inherits that crate instead of parody. The second is a correctness bug, not a mix-up. Seam spec +published (cache key = `type + base`; per-shop preload on entry; LRU=1 + dispose; **C consumes whatever +identity field lands — C does not invent it**). **C owns and lands it** — the §7 promise coming due. + +**Two gaps block F's gate #7a (neither is C's; both flagged):** (1) **the crate has no street** — +`redhill_real` doesn't exist and monsterrobot isn't in any town's `shops[]` (G's ask #2 → E/Fable); +(2) the shell's `interior_mode.js:192` calls `getStockPack(shop.type)` with `base` fixed to +`assets/models/`, so it can't reach a per-shop pack until §8's seam + the identity field land (C+F). + +*Process note:* the shared tree was checked out on `lane-G-godverse` for Fable's review, so I committed +these C docs from a temporary `git worktree` on `main` — the shared checkout was never touched, and G's +branch keeps the disjoint namespace the two-studio merge law depends on. --- diff --git a/docs/LANES/LANE_C_PUB.md b/docs/LANES/LANE_C_PUB.md index 29ff071..ae30454 100644 --- a/docs/LANES/LANE_C_PUB.md +++ b/docs/LANES/LANE_C_PUB.md @@ -7,6 +7,9 @@ > (ledger #2, the density widening; C→E handshake). The frozen venue contract (§0–§5) is unchanged. > **v4.0 amendment (2026-07-16):** **§7 added — the G2a per-shop stock-atlas contract** (C's interior > draw/tex ceilings, answering Lane G + E's review). §0–§6 unchanged. +> **v5.0-alpha amendment (2026-07-16):** **§7.2's VRAM number corrected + §7.8 added** — G's first real +> crate (120 records) proved my "≤8 MB" figure was derived from a wrong assumption, and the per-shop cache +> seam is now specified for G+F. **G's R23 atlas needs no re-emit.** §0–§6 unchanged. *The district ships **three** venue archetypes behind `?gigs=1` — `pub`, `band_room`, `rsl`. Lane A converts a chosen shop to a venue kind in place (`shop.type = kind`); C keys the interior recipe off @@ -242,10 +245,21 @@ worst rooms today are record 41 / book 58 / toy 57 draws against the ≤350 law; E's audit is correct and the cause is a deliberate C design note (`stockpack.js:11`): atlas textures are "shared/cached in the pack (**NOT per-room**), like depot GLBs". That is **right for 3 bounded type-packs** and **wrong for unbounded per-shop atlases** (23 towns × N shops). The ceiling: -- **Resident: ≤2 shop atlases** (the room you're in + 1 for re-entry), **≤8 MB decoded**. -- **Atlas size: ≤1024² for a typical shop** (16 items @256² cell); **2048² only for a 60+ item shop**, and - prefer spilling to the type-pack. At 1024² RGBA ≈ 4 MB → ≤2 resident ≈ **8 MB** (vs E's measured **181 MB** - resident today — ~20× better, and bounded instead of accreting). + +> **⚠️ CORRECTED v5.0-alpha — measurement beat my assumption.** I wrote "≤8 MB decoded" by assuming a +> "typical shop" is ~16 items @1024². **G's first real crate is 120 records** (Monster Robot Party, crate +> 550) — a real record shop is nothing like 16 items. At the contract's own 256² cell that's 2 × 2048² +> = **~32 MB decoded**, 4× my stated figure. The *structural* rules were right (≤2 atlases; 2048² for a +> 60+ item shop) — only the MB number was wrong, because it was derived from a shop that doesn't exist. +> **G's R23 atlas is compliant; no re-emit.** The corrected ceiling: + +- **Per shop: ≤2 atlases, ≤2048² each** (unchanged — G's 120-item crate lands exactly on the cap). +- **Resident: ONE shop's atlas set** (LRU = 1 shop, not 2) ⇒ **≤32 MB decoded peak**. Still bounded, and + still far better than the **181 MB unbounded** accretion E measured today. +- **≤1024² whenever the crate fits it** (≤64 items at 256²) — don't hand a 6-item shop a 2048². +- Cell size is E/G's call: **C reads `uv`, never `cell`** (so 256², 176×256 portrait, etc. are all fine). + Do **not** shrink the cell below 256² to save VRAM — the dig renders a cover close-up; resolution is the + one thing the player actually sees. - **C lands the LRU cap + dispose in `stockpack.js`** (evicted shop's atlas texture + material disposed). Type-packs keep today's shared cache (bounded, reused across rooms of a type) — unchanged. - **Scheduling:** today's 181 MB accretion is **pre-existing**, and `?stock=real` is **opt-in** @@ -274,4 +288,37 @@ a re-emit silently reshuffles every crate. Same discipline as E's byte-identical satisfies that naturally (better than the type-pack's seeded `atlasPool` pick). Parody = not buyable stays. **7. Parody law — E's (d), unchanged and not C's to relax.** Metadata deterministically parody-transformed; -cover art real; prices real-or-seeded. +cover art real; prices real-or-seeded. *(v5.0-alpha: John amended this at tier 1 — real titles/artists ship +in-house-green, flagged before public. Recorded in G's provenance; not C's to relax either way.)* + +**8. The per-shop cache seam (v5.0-alpha — C's spec; G+F build to this). CONFIRMED BROKEN in R23.** +G's ask #1 is real and I reproduced it. `stockpack.js` keys `_packs`/`_resolved` by **`type` alone**, and +`preloadStockPack(type, {base})` **ignores `base` on a cache hit**. Measured (R23, fresh context): +- `preloadStockPack('record', {base:'assets/stock_godverse/3962749/'})` → the 120-record godverse pack ✓ +- then `preloadStockPack('record', {base:'assets/models/'})` → **returned the identical object** ✗ + +Two consequences, the second worse than G's report: +1. **Two stocked shops collide** — the 2nd silently serves the 1st shop's crate (blocks v5.0-beta). +2. **It breaks the fail-soft law** — an *unstocked* shop of a cached type inherits that crate instead of + falling back to parody. (On a clean cache fail-soft is intact: missing atlas → `null` → parody, 0 errors. + The collision is what defeats it, so this is a correctness bug, not just a mix-up.) + +**The seam (C owns it, C lands it):** +- **Cache key = `type + base`** (equivalently a `packKey`; a godverse shop's key is its `stock_godverse//` base). `getStockPack` takes the same key. +- **Resolution order** stays: explicit per-shop base → the type-pack base → `null` → parody. +- **Per-shop preload on entry** (interior build/enter), **not** a boot-time preload of every shop. +- **LRU = 1 shop's atlas set, disposed on eviction** (§7.2) — the load-bearing half of the §7 promise. +- **Identity link:** a stocked shop must carry its pack pointer on the shop record (G's `godverse_id` → + `stock_godverse//`). Not in the town cache today — G flagged it ("monsterrobot must be added to the + godverse cache's `shops[]` as id 3962749"). C consumes whatever field lands; **C does not invent it.** + +**9. R23 verification — the first real crate, in a room (ledger #3). PASS.** +Measured against G's `3962749` atlas (120 real records, 2 × 2048², cell 256²) in a fresh context: +- **Resolves through the existing `stockAdapter` path** via a base override — no C code change needed for + the single-shop alpha. 120 items, 2 atlases, **8/8 stock meshes atlas-backed** (real covers, not parody). +- **Ceilings hold:** **118 draws** with real covers (≤350 law); room `pathOK`, 0 carves. +- **Fail-soft holds** (clean cache): missing atlas → `null` → parody (20 parody meshes, 110 draws, pathOK); + no `stockAdapter` (the `?noassets` shape) → parody, zero fetch. **0 console errors** throughout. +- **G's index is contract-compliant:** `items[]` sorted by id (my determinism ask honoured ✓), provenance + present ✓, `uv ⊂ [0,1]` ✓, 2 atlases = the cap ✓, `cell_w/cell_h` harmless (C reads `uv`) ✓. +- **Verdict: the contract holds. G re-emits nothing.** The only C-side work is §8's seam.