Commit Graph

13 Commits

Author SHA1 Message Date
m3ultra
6960bdd279 Lane C R27 (v5.0): the tier-2 seam ruling + LANE_C_PUB v5-FROZEN (ledgers #2, #6)
SEAM RULING (§8): bless the direct wire — NO lifecycle hook. F already holds currentAdapter
(interior_mode.js:67) and passes it to dig.open() PER DIG (:111), so the live read lands, F
updates the pack, the next riffle picks it up; not-landed/slow/dead => the adapter is simply the
tier-1 pack => the dig never blocks (charter risk #1). No C lifecycle change.

BUT THE QUESTION UNDERNEATH IT IS C'S, AND THE OBVIOUS WIRING IS WRONG. pickRealOffers indexes
pack.items[(rr()*len)|0], so FILTERING THE ARRAY RESHUFFLES THE CRATE. Measured, 5 crates: one
record selling changed 21 OTHER records (6/2/2/6/5 collateral). Pick-then-omit (draw from the
stable full list, drop gone after) = 0 collateral: a crate with 3 sold shows 13, not a different
16. Selling one record must remove one record, not re-stock the shop.

THE TRAP: both forms hide the sold record — so F's planned assertion ("a real gone item never
renders") GOES GREEN ON THE WRONG WIRING. It cannot see the 21. -> F: assert on COLLATERAL (the
survivors are unchanged across a gone delta), not absence. Vacuous-gate law, third application,
and the cheapest to miss because it passes.

LANDED (dig.js): pack.gone (Set|array) omitted at PICK time. F's invariant published: update item
FIELDS in place (live price) freely; NEVER add/remove/reorder items[] — that identity+order IS the
seeded pick's stability (R25's positional-id lesson, one layer up).

DETERMINISM BOUNDARY sits exactly here: server down => byte-identical tier 1 (gate-able); server
up => the crate legitimately changes (tier 2 is non-deterministic BY DESIGN), so no gate may
assert dig determinism with the server up, and pack.gone never enters a seeded stream or golden.

SCOPED: the bin fan is decorative (a different seeded pick from the dig's offers) — a sold cover
may linger there until rebuild. That's when I'd publish a post-build hook; the epoch's claim
doesn't need it, because the crate you RIFFLE is true.

VERIFIED: pure-function measurement for the collateral claim, then end-to-end through the real
dig's public surface (pull -> Esc -> scroll -> pull): sold record gone, survivors 4/4, collateral
0, items[] untouched at 120, live in-place price shows on the next pull. (My first harness
returned the same title 4x — I never un-pulled — so its number was an artifact; re-ran it properly
rather than report a metric I'd just watched lie.)

DOCS FREEZE (§6): LANE_C_PUB is v5.0-FROZEN, whole document, with the case law at the top: three
amendments here exist because a rule of mine met a case written AFTER it, and both times the lane
building to my rule took the red. Header now states it: when a line meets a case it didn't
foresee, the line is the stale thing — amend it, don't bend the case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:53:02 +10:00
m3ultra
53e83308a0 Lane C R26 w4: the mint-id fence — my rule was the stale thing (wave 4, C's one line)
F's fourth hold traced to MY §7.2a. I wrote "sku_-prefixed for real-stock packs" in R25 —
before mint existed — so it names one space where there are two. E implemented it faithfully
and its validator did exactly what I told it to: demand sku_ on 14 mint crates. The check
wasn't wrong; my rule was. (Second time a contract line of mine met a case written after it
and another lane took the red.)

AMENDED §7.2a — the id form is SOURCING-SCOPED and the prefix IS the fence:
  sourcing "real" -> sku_<POS id>            the shop's POS inventory (a physical copy)
  sourcing "mint" -> mint_<dealgod listing>  a dealgod listing (a plausible stand-in)

WHY G WAS RIGHT TO REFUSE THE EASY GREEN: filing dealgod listing ids under sku_ would have
gone green in one line and recreated the R24 collision INSIDE THE ECONOMY LAYER — where
tier-2's sold-means-gone looks up POS skus. R24's collision entered the wrong shop; the same
collision in the money layer sells the wrong record — a real copy, out of a real shop, to
satisfy a minted stand-in. A prefix that lies costs money, not a gate. Blessed as design:
disjoint namespaces; no gate, lookup or economy path may treat them as one (same standing
note as plan-ids vs godverse-ids).

VERIFIED against every crate before handing E the line: 15 crates (1 real + 14 mint), 0 fails
under the amended rule (prefix matches sourcing, ids unique within pack), and 0 ids appear in
both spaces across 344 distinct ids. The fence already holds in the data; only my wording didn't.

-> E: assert the form against `sourcing` (already read at line 178) — sku_ for real, mint_ for
mint; a crate wearing the other's prefix FAILS. Stronger than the check it replaces, not looser.
-> F: 6/1/0 should flip to 7/0/0. Doc-only: no code, no atlas bytes, no goldens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:32:45 +10:00
m3ultra
4f0adf12d8 Lane C R26 (v5.0-beta): three contract lines + the dig price sticker (ledgers #1, #4, #6)
Wave 1 — contract lines published first; G and E build to them.

#1 THE BRIEF SAID `tier`. IT CAN'T BE (§7.2b). G's atlases already emit tier: 1 = the CHARTER'S
LADDER RUNG (0 parody / 1 static-real / 2 live), and `kind` is already the stock type. A mint atlas
is ALSO rung 1 — same mechanism (a statically baked per-shop atlas); only the data's origin differs.
Real-vs-mint is an orthogonal axis, not a rung — overloading `tier` puts two meanings in one name,
the same species as the plan-id/godverse-id collision just standing-noted, and as R25's positional
ids. RULING: top-level `sourcing: "real"|"mint"`, REQUIRED; `tier` stays the numeric rung. A mint
index MUST NOT carry POS-snapshot fields (the separation is the honesty). Gates read `sourcing`;
absent => FAIL.

#4 `condition` ISN'T IN THE INDEX (§7.2c) — the ledger assumed it was. The index carries
artist/title/price/price_band; G's SNAPSHOT has condition + sleeve_cond and drops them at emit, so
pickRealOffers mapped cond:'' and the card rendered dangling separators around a hole. That WAS the
"database row". Ruled optional index fields, emitted when the source has them; no invented grades on
mint items (a plausible-but-fictional grade is what `sourcing` exists to prevent).

#6 THE ATLAS MANIFEST (§7.2d): stock_godverse/index.json is the authority for which shops carry
atlases — no entry => no fetch => zero 404s, so the zero-console-errors law holds with no
attribution exception. Shape + both-ways consistency specified. No manifest + no atlases = a
legitimate silent zero-shop state, not an error.

THE PRICE STICKER (code): the pull panel is now the shop's price sticker — manila card, rotation +
shadow, typewriter caps, italic title, a stamped condition grade, and the round yellow $ sticker the
sleeve art already uses (the panel echoes dig.js's own canvas language instead of fighting it).
Fields compose only when present: a missing field never leaves a stray separator.

VERIFIED (fresh context, 5 card shapes, both star branches): real grail VG+/VG + ★GRAIL; real
standard VG+ + STANDARD; real ungraded COLLECTOR (grade omitted cleanly); parody NM + 1973 PUNK;
parody rare (seed 4) VG + 1973 POP + ★RARE. No dangling separators anywhere.

The verification caught a defect in MY OWN card: a real grail read "GRAIL ★ GRAIL" — `s` carried the
price_band AND `rare` derived from that same band (parody is immune: its `s` is a genre). Fixed at
the mapping. And I didn't leave the parody-rare branch on assumption — scanned seeds until a rare
pull appeared and asserted it says RARE, not GRAIL. An unexercised branch is an unproven one.

Budgets: the card is DOM — zero draws, zero tris, no 3D change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:38:42 +10:00
m3ultra
0d8d59a22a Lane C R25: sku-derived slot ids — F's id-collision finding made structural (ledger #3)
One contract line, published first so G re-emits against it. §7.2a added.

THE COLLISION, MEASURED: both packs number items positionally from zero, so all 120 godverse
ids are also generic-pack ids — 120/120 overlap. An id-equality gate cannot discriminate which
pack a rendered item came from; that is exactly why the brief's own #7a spec was vacuous. (The
vacuous-gate law's first application broke the law — and my own R23 metric had the same disease.)

THE RULE (§7.2a): items[].id MUST derive from the source's stable key, never from position, and
MUST be unique across ALL packs. Real-stock packs: id = "sku_" + <POS sku> (G's snapshot already
carries `sku` per item, e.g. 20260314070442 — it's just dropped at emit, so this is index-only).
Generic packs keep rec_NNNN as their namespace; real-stock must not emit into it.

TWO REASONS, and the second outlives the gate:
1. positional ids collide by construction (the 120/120 above) -> id-equality gates can't
   discriminate. Sku ids fix that permanently, now, at two atlases instead of fifty.
2. POSITION IS NOT IDENTITY: a positional id renumbers on every re-bake — sell one record out of
   crate 550 and rec_0050 silently becomes a different record. Anything holding an id across
   snapshots (a wallet purchase, a saved dig, a gate assertion, and above all tier-2's
   sold-means-gone) would point at the wrong item. The POS sku is stable because it is the shop's
   own identity for that copy (release_id identifies the pressing, not the copy). So this is also
   the identity tier-2 will need, bought now for free.

SCOPE: index-only — no atlas bytes, no town cache, no goldens, no C code (id isn't read by the
loader, §7.3; the dig/buy consumers and gates read it).
-> G: re-emit with sku_ ids. -> E: assert the form (present, unique within pack, sku_-prefixed).

Committed by explicit pathspec — F's six staged files sit untouched, as its session left them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:18:37 +10:00
m3ultra
ba901d4a75 Lane C R24: the contract fix — the runtime is the authority (ledger #1, the root cause)
F held the tag because my published atlas contract and my own loader were mutually
incompatible. That was mine. Closed, and published FIRST so G and E re-emit against it.

WHAT WAS WRONG: my R22 §7 invented `stock_shop_<godverseShopId>_index.json`; the loader
(stockpack.js:25) fetches `${base}stock_${type}_index.json`. An index named per the contract
can never be fetched by the code meant to load it. G built runtime-compatible (correct), E
built contract-compatible (also correct — the contract said so), and E's validator globbed
stock_shop_*_index.json -> zero files -> vacuous pass. Nobody was wrong; the contract was.

§7 REWRITTEN AGAINST THE CODE (LANE_C_PUB):
- Authority rule: stockpack.js IS the contract; doc vs loader disagreement = doc bug.
- Layout ratified as G shipped it: assets/stock_godverse/<godverseShopId>/stock_<type>_index.json
  (`type` is load-bearing — SLOT_FOR picks the slot). stock_shop_* RETIRED.
  -> E: the glob becomes stock_godverse/**/stock_*_index.json (the vacuous gate's actual cause).
- Runtime-read vs ignored stated exactly: reads atlases[] + items[{atlas,uv}] (uv top-left, the
  loader flips V); ignores version/atlas_px/cell*/count/kind/tier/shop/crate/provenance.
- Provenance drift dies: runtime reads none of it, so the authority is E's validator + the 23
  shipped town caches -> TOP-LEVEL `license` (US), attribution, generator, snapshot. British
  `licence` retired; G lifts+renames those four, keeps its rich nested provenance{} as colour.
  atlas_px stays REQUIRED (checked against real dims). cell* informational — C reads uv, never
  cell; do NOT shrink the cell for VRAM (the dig renders covers close-up).

CODE (F's filed item): stockpack cache identity is now `${base}|${type}`;
getStockPack(type, base = DEFAULT_STOCK_BASE); new opts.stockBase on buildInterior = the seam F
wires. Omitted => town-wide packs, byte-identical to today. Pre-v5 callers untouched.

VERIFIED FRESH — and I fixed my own metric first: R23's "8/8 atlas-backed" only checked that a
map exists, but a parody canvas has a map too (same species as the vacuous gate). Re-asserted
falsifiably: every sleeve's texture source IS stock_godverse/3962749/stock_record_atlas_00/01.webp,
pack items == index items by id; negative control — the parody room's textures are (canvas, no src)
and touch the atlas zero times, so the metric discriminates. Collision FIXED (shop 120 + town 350
coexist). Fail-soft FIXED with a populated cache (missing base -> null -> parody). 98 draws with
real covers, pathOK, 0 carves, 0 console errors.

SCOPED NOT SLID: the LRU+dispose (§7.6) lands at v5.0-beta — one stocked shop can't accrete, and
safe eviction needs a room-exit hook (a live room holds the pack's shared material) that arrives
with F's per-shop preload. Written into the contract so it can't slide silently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:43:50 +10:00
m3ultra
d2eb9df03f Lane C R23 (v5.0-alpha): the first real crate verified in the room (ledger #3)
Verified G's stock_godverse/3962749/ (120 real records, Monster Robot Party crate 550,
2 x 2048^2, cell 256) in a fresh context — deliberately BEFORE the merge, so a contract
failure would block it. It didn't.

PASS — the contract holds; G re-emits nothing:
- Resolves through the existing stockAdapter path via a base override (no C code change for
  the single-shop alpha). 120 items / 2 atlases / 8-of-8 stock meshes atlas-backed (real
  covers: peperecordings, DEEPCHILD, "39", mff FREAKS...).
- Ceilings hold: 118 draws with real covers (<=350 law), pathOK, 0 carves.
- Fail-soft holds (clean cache): missing atlas -> null -> parody (20 meshes, 110 draws);
  no adapter (?noassets shape) -> parody, zero fetch. 0 console errors.
- G's index compliant: items[] sorted by id (my determinism ask honoured), provenance, uv in
  [0,1], 2 atlases = exactly the cap, cell_w/h harmless (C reads uv, never cell).

CORRECTED MY OWN CEILING (LANE_C_PUB §7.2): I wrote "<=8 MB decoded" from an assumed ~16-item
"typical shop". A real record shop is 120 records => 2 x 2048^2 ~= 32 MB, 4x my number. The
structural rules held (<=2 atlases; 2048^2 for 60+ items — G landed on the cap); only the MB
figure was wrong, because it described a shop that doesn't exist. New: <=2 atlases @<=2048^2,
LRU = 1 shop's atlas set resident => <=32 MB peak (bounded; vs E's 181 MB unbounded today).
Do NOT shrink the cell below 256 to save VRAM — the dig renders covers close up.

G's ask #1 CONFIRMED and worse than reported (LANE_C_PUB §8 — C's spec, C's fix): stockpack
keys _packs/_resolved by type alone, so base is ignored on a cache hit (measured: the 2nd
preload returned the identical object). (1) two stocked shops collide; (2) it BREAKS FAIL-SOFT
— an unstocked shop of a cached type inherits that crate instead of parody. Seam: key by
type+base, per-shop preload on entry, LRU=1 + dispose; C consumes whatever identity field
lands, C does not invent it.

Two gaps block F's #7a, neither C's: the crate has no street (redhill_real absent; monsterrobot
not in any shops[] — G's ask #2 to E/Fable), and interior_mode.js:192 pins base to
assets/models/ (needs §8 + the identity field).

Committed from a temp git worktree on main — the shared checkout stayed on lane-G-godverse
(Fable's review) and G's branch keeps its disjoint namespace. Docs-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:42:30 +10:00
m3ultra
b0e4ce103e Lane C R22 (v4.0): 23-town door-soak (#5) + the G2a atlas contract (#3)
Wave 3 — ran after A pinned the 36 goldens (3face97) and E's tri-diet fix (d81e499).

#5 — 23-TOWN DOOR-SOAK: five-town harness scaled (~10 doors/town; sample takes one of every
type present then stride-fills). 238 doors / 1,133 in-plan shops / 23 towns.
  23/23 PASS — 0 throws / 0 carves / 0 path-fails / 0 over-budget; worst 159 draws (fitzroy)
  <=350. All 12 archetypes covered town-set-wide (the whole registry). 23/23 towns landed 3
  venues — A's cluster bias + district placement hold from the 12-shop thin tail (darwin,
  toowoomba) to the 139-shop mecca (fitzroy, worst 159 = same band as everywhere: interiors
  don't care about town scale). Lane G's newtown_godverse passes (18 shops, worst 134).

#3 — G2a PER-SHOP ATLAS CONTRACT (C's half), published in LANE_C_PUB §7. All three Fable priors
hold; atlas-per-shop is strictly BETTER for draws than today's type-packs. The binding constraint
is VRAM, not draws — E's dispose ask GRANTED, it's my seam:
  - Draws: 1 atlas/shop = 1 draw (shared material + baked UV -> batch merges). Cap 2; 60+ item
    shops spill to the type-pack. Headroom huge (stock rooms 41-58 vs the <=350 law).
  - VRAM: E's 181MB audit is right, and the cause is my own design note (stockpack.js:11 caches
    atlases "NOT per-room, like depot GLBs") — correct for 3 bounded type-packs, wrong for
    unbounded per-shop. Ruling: <=2 resident atlases, <=8MB decoded, <=1024" typical; C lands the
    LRU cap + dispose in stockpack.js. Pre-existing + ?stock=real is opt-in (index.html:196) so
    it can't touch the default boot -> NOT a v4.0 blocker; C's first v4.x item, before G2a ships.
  - Schema: E's index confirmed verbatim, keyed per shop; UV top-left (C flips V); C reads uv not
    cell. Fail-soft/parody/?noassets unchanged. Ask to G: emit items[] in deterministic id order —
    C's seeded per-bin pick indexes into it.

C->F: 23-town interior verdict GREEN. Docs-only; both C items closed for v4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:12:00 +10:00
m3ultra
c7ca39b4f7 Lane C R20 (v4.0-beta): publish OSM-class -> archetype mapping (ledger #2, C's half)
The density widening's C half: real towns fetch general retail, not just secondhand.
Published the OSM shop-class -> registry-archetype mapping in LANE_C_PUB.md §6 (new
v4.0-beta amendment; frozen venue contract §0-§5 untouched). C->E handshake: E builds to it.

- No new archetypes. General retail wears existing recipes: food/grocer/newsagent/chemist
  -> milkbar; clothes/hardware/big-retail -> dept; produce/florist -> stall.
- Secondhand stays the hero (charity->opshop, books->book, music->record, video->video,
  pawn->pawn, toys->toy); never subsampled. Unknown/unlisted -> opshop (R18-proven).
- Subtle per John: texture not takeover. Copy-paste JSON map + tasteful fetch list (skip
  vehicle/funeral/industrial/pure-services) + guardrail for E to subsample if retail drowns
  the secondhand strip (count in norm.dropped).
- Mechanic: plan_osm keeps registry types, so E bakes a registry type per the map into each
  cache's shop.type -> no C code change, budgets unchanged.

Pre-validated before E builds to it: milkbar/dept/stall/opshop all build valid at a real
katoomba lot (8.5x14, 0 carves, <=143 draws); dept reads as a clean dressed-retail floor
(shelves+racks+counter, escalator does not dominate the 7.9x11.7 hall) -> clothes/hardware
-> dept confirmed. Full five-town door-soak (ledger #6) follows E's re-emitted caches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:32:05 +10:00
m3ultra
42b11e717d Lane C R16 (v3.1): stage.backline[] amp unification, drop primitive ampStacks (ledger #5, C half)
C exposes room.stage.backline[] — 2 seeded up-stage amp slots {x,z,ry,y} (pure data) — and
drops its primitive ampStacks from placeStage. Lane D plants E's guitar_amp GLB at backline[0]
(D's primitive fallback under ?noassets): one unified backline, GLB when present.

- backline[0] = (stage.x + stage.w*0.36, deckY, stage.z - stage.d*0.22) == D's R15-verified amp
  pose exactly, so plumbing D's plant to backline[0] moves nothing on screen. backline[1] = spare
  stage-left mirror. ry=pi (audience-facing), y=deckY.
- Verified fresh (seed 20261990): slots up-stage, on-deck, clear of all 4 bandPoses (nearest-bass
  0.87 pub / 0.70 band_room / 0.92 rsl) and all watchPoints (>=1.95 m); determinism byte-identical;
  0 leftover ampStack groups; drawSweep venues 122/120/131 (was 126/124/135, -~4 draws each) <=350.
- Determinism/golden-safe: dropping the amp loop only drops draws on the 'stage' sub-stream (nothing
  downstream reads it); placement snapshot excludes ampStacks; interiors aren't hashed; ?classic has
  no venues. Disjoint from F's flip. LANE_C_PUB §3 amended (v3.1 marker).

C->D handshake: backline shape live. Verify with D once the GLB is wired at backline[0].

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:16:38 +10:00
m3ultra
2446ae3a5d Lane C R15 (v3.0): verify D's guitar_amp reposition (ledger #4)
D moved the backline plant (66aec0f) to C's §3 corrected pose exactly
(band.js:236 = stage.x + stage.w*0.36, deckY, stage.z - stage.d*0.22).

Verified fresh (port-isolated context, seed 20261990, D's real GigCrew + GLB backline):
- matches §3 both venues; bass clearance 0.87 m (pub) / 0.92 m (rsl) — the R14
  interpenetration (0.29 m) is resolved; on-deck; flanks C's primitive ampStack
  (4.4 m pub / 3.5 m rsl) as a clean up-stage backline; no band/crowd clash.
- rendered pub gig confirms the read (amp up-stage behind the bassist).

LANE_C_PUB §3 updated RESOLVED; freeze otherwise stands. No code change (docs only).
Nothing else blocks on C — F runs the release (continuity wiring, alias delete, week
soak, tour, tag v3.0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:49:20 +10:00
m3ultra
62e38c5df8 Lane C R14 (v3.0 release): venue audit + docs freeze + guitar_amp ruling
Light release round — C interior code unchanged since R13 (venue geometry stable by
construction); this is the audit, the amp ruling, and the v3.0-FROZEN docs.

- Re-verified vs the current tree (D's continuity + RY-fix + real instrument GLBs landed):
  contract byte-identical (caps 8/8/12, deckY/riserY, gigKeys, determinism), drawSweep <=350
  (worst 161), gig-night draws pub 60 / band_room 52 / rsl 71 <=350. Release-law confirmed:
  D's rng namespace is disjoint from C's ctx.stream; no golden can move.
- guitar_amp ruling (LANE_C_PUB §3): NOT blessed as-is. D's pose (stage.z+0.15, down-stage)
  interpenetrates the bass rig by 0.29 m; move it up-stage-right ~ (stage.w*0.36, deckY,
  stage.z - stage.d*0.22), clear of the bass + C's primitive ampStacks. Full amp unification
  = v3.1. Cosmetic + ?gigs opt-in -> not a tag blocker. C->D handshake.
- Docs freeze: LANE_C_PUB.md + LANE_C_AUDIO.md marked v3.0-FROZEN; RY §0 reframed past-tense
  (the +Z fleet was fixed at source in D's buildFigure, R13 - no per-consumer flips); budgets
  updated to R14; in-flight handoffs removed.
- Comment-only code (zero runtime/golden/rng change): fixed stale R12 inline return-shape
  comments (watchPoints 6..8 -> 8/8/12; stage bandPoses[3] -> [4] + riserY) in interiors.js +
  layout.js; documented the standalone gigKey hand-build. theme.js untouched.

Note for E/F: settled gig-night interior is ~182k tris (E instruments 14k each x5); F's smoke
reports ~34k measuring before the async GLBs load. Interior draw gate passes; v3.1 LOD candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:40:42 +10:00
m3ultra
82b711f962 Lane C R13 (v3.0-beta, the district): band_room + rsl archetypes, 4-piece band, RY audit (?gigs=1)
- theme.js: band_room (small/scuzzy tin shed) + rsl (carpet-and-flock club) venue recipes,
  reusing the pub machinery via a new per-kind venueSpec (deck size/height + crowd cap).
- layout.js: parametrized placeStage/placeWatchPoints; 4th `drums` pose seated on the riser
  (debt #3 — the band is now a 4-piece across all venues), stage.riserY + per-pose y.
- interiors.js: robust gig-<genreKey> resolution (gig.genreKey -> shop.genreKey -> kind
  default) matching E's renamed manifest beds (debt #1); API doc for the 4-piece + riserY.
- LANE_C_PUB.md: 3-kind venue contract + pinned ry convention and the empirically-verified
  keeper/browser back-to-front finding + source-normalise fix for Lane D (debt #4).

Verified fresh (seed 20261990 + 90-build soak): watch caps 8/8/12, gigKeys
gig-pubrock/gig-grunge/gig-covers, determinism 0, path/carve 0, gig-night worst RSL 237 draws
asset-free (16 rigs) / 77 GLB <=350, leak geo0/tex0. Shot: docs/shots/laneC/rsl_gig_r13.jpg.
Lane F: bump flags_check band==3 -> ==4 (tools/flags_check.py:674,794) — RED by design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:05:03 +10:00
m3ultra
05e020e3b8 Lane C: round-12 pub venue — archetype + watchPoints + audio.gigKey (?gigs=1)
v3.0-alpha band room. New pub recipe (venue:true): stage + PA + amps at the back wall (new stage/paSpeaker/ampStack fittings, GLB slots stage/pa_speaker/amp for E), bar counter (keeper), sticky carpet, blocked back door = green room.

buildInterior returns for venues: room.watchPoints[] (6-8 seeded audience poses {x,z,ry,dance} facing the stage, dance ~1/3, counter.stand ry convention); room.stage {x,z,w,d,deckY,frontZ,bandPoses[3]} for Lane D's band; room.audio.gigKey ('gig-'+genreKey) set when Lane F passes opts.gig.

Flags-off byte-identical: venue code runs only for recipe.venue on its own seed sub-streams; non-pub rooms have watchPoints:[], stage:null, unchanged placement/audio.

Verified: 40-seed pub sweep (0 throws/carves/path-fails, 8 watchPoints, dance 0.38) + 60-room mixed soak (0 det/path fails, leak geo0/tex0, worst 19ms). Non-pub unchanged. Interface for D/F: docs/LANES/LANE_C_PUB.md. scaffold + manifest green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:59:48 +10:00