Lane F R24 (v5.0-alpha): THE CRATE WALKS — the per-shop wire + a gate that can't lie

You can boot redhill_godverse, walk down Musgrave Road into Monster Robot Party, riffle its
own real crate and buy a real record. Proven falsifiably, not by eyeball:
  base      assets/stock_godverse/3962749/ (its own, not the town-wide)
  TITLES    120/120 the shop's atlas · 0/120 the generic pack
  TEXTURES  2 from stock_godverse/3962749/*.webp · 0 generic — the pixels' own file
  BUY       rec_0000 "Abstract Latin Lounge II (Part II)" @ $15 → cash 191→176, exact
  REBOOT    191/0 restored — gone-this-session, back next boot
Ladder: ?noassets → ZERO network requests · keyed-but-atlas-less shop → 404 → parody, live ·
newtown_real → base null, 350 items: the wire is a NO-OP off the godverse path.
flags_check GREEN (0 fails, 0 warns).

#6a THE WIRE: godverseBaseFor(shop), exported and used by BOTH the shell's boot preload and
the room's lookup — C's cache identity is the LITERAL (type, base) pair, so two sides building
that string differently would drop a real shop to parody, a bug indistinguishable from "no
stock" by eye. One function, one string, one key. Gated on stockReal, never the id alone.

#6b THE GATE: smoke_real_crate + DBG.stockInfo() — permanent, and it cannot pass without
touching its subject (SKIPs with a printed reason when the town/atlas is absent).

FINDING — THE BRIEF'S OWN HONEST-GATE SPEC WAS VACUOUS. It specified id equality. Both packs
number items POSITIONALLY (rec_0000…), so all 120 atlas ids are generic-pack ids too: an
id-equality gate goes GREEN on the generic pack — the exact failure the vacuous-gate law was
written to stop, reproduced inside its first application. Ids there are slot numbers, not
identity. Titles (0 overlap) and the texture URL discriminate; F asserts on those.

FINDING — THE ID SPACES COLLIDE, and it bit F's own selector inside the anti-vacuous gate.
Plan ids (1..N) and godverse ids (31, 767, 3962749) share a numeric space: Silky Oak's godverse
id is 31 and another shop's PLAN id is 31, so enterShop(31) silently entered "Wholefood Cafe"
and the soft-fall assertion PASSED ON AN UNKEYED CAFE. Fixed both ends: 'g:<n>' selects a
godverse id explicitly, a bare number stays the plan id and now REPORTS the collision, and the
smoke asserts it landed in the shop it asked for before asserting anything about it.

FINDING — F'S OWN GATE CRIED THE LOUDEST WOLF WE OWN. classic_regression reported "plan
fingerprint != golden 0x3fa36874 — determinism/prime-law broken" while the covenant was
provably intact. F wrote it in R16 as `hash_ok and returncode == 0` — two assertions, ONE
verdict, reported with the fingerprint's message — so any unrelated selfcheck red screamed that
the byte-identical-forever covenant had broken. Two assertions, two verdicts now. It isn't
enough to fail for a reason; you have to fail for YOUR reason.

FINDING — fail-soft-by-404 vs the 0-console-errors law. The boot preload must resolve a keyed
shop's pack before the door opens (getStockPack is sync), so it probes blind: 9 of Red Hill's
10 keyed shops have no atlas → 404 → parody. That IS fail-soft working, and it is also 8
console errors no .catch() can suppress. Fix is not to loosen the law but to stop probing for
what isn't there: an atlas index in G's namespace (or hasStock on the cache entry, A+G). Filed
as a contract ask, not invented. Until then the smoke counts and NAMES the probes and fails on
any other error — tolerance stated out loud.

Verified the lift is clean: cache 10 keyed → plan 9 keyed, every one a real secondhand shop.
No cafe keyed, zero duplicate ids — there is no mis-stocking.

F did NOT touch A's identity sweep or E's validator. F was the party that would benefit, and
changing a red gate to green in another lane's file to unblock F's own tag would retroactively
devalue every green F has ever reported. Filed instead — and R25 has since landed A's
reconciliation and the six pins, which is the right hands doing it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-17 08:43:29 +10:00
parent 533fa32b59
commit 29942002eb
6 changed files with 441 additions and 15 deletions

View File

@ -4,6 +4,55 @@
---
## Round 24 (v5.0-alpha — THE ALPHA COMPLETION) — **the crate WALKS; F held the tag on A's gate**
**THE CRATE WALKS.** Boot `redhill_godverse`, walk down Musgrave Road into **Monster Robot Party**,
riffle **its own real crate**, buy a real record at its real price. Proven falsifiably: room base =
`assets/stock_godverse/3962749/` · **titles 120/120 in its atlas, 0/120 in the generic pack** · **the
GPU samples `stock_godverse/3962749/stock_record_atlas_0*.webp`, 0 textures from the generic pack** ·
bought *"Abstract Latin Lounge II (Part II)"* **@ $15**, cash **191 → 176**, exact debit, count 0→1 ·
reboot restores 191/0 (gone-this-session, back next boot). Ladder re-proved: `?noassets` → **zero
network requests**; keyed-but-atlas-less shop 2289 → 404 → **parody, live**; `newtown_real` → base
`null`, 350 items — **F's wire is a no-op off the godverse path**. Full record: `LANE_F_NOTES` §24.
**F held `v5.0-alpha` anyway. The tree is red — 156,193/156,201 — and none of it is the product.**
2 reds are goldens G's re-emit legitimately moved (A ran *before* G, so A could not pin them). 6 are
**conflict #2: A's identity sweep contradicts Fable's own standing Ruling 2**, which *mandates* mixed
caches ("merge census heroes **WITH** the donor's texture shops"). A cafe from OSM has no GODVERSE
identity and never will. G measured it, escalated it to "A's call, Fable's ruling", nobody ruled, and
it arrived at F.
**F measured the hazard that gate guards: it does not exist.** 10/10 and 18/18 unique ids, **zero
duplicates**; and partial keying does not mis-stock — F watched 2289 fall soft to parody in the
browser, exactly as charter risk #3 prescribes. **A's own two checks disagree**`validateTownCache`
reasons it correctly ("Missing = tier 0 (soft). Duplicate = mis-stocked (hard). They are not the same
failure") and warns; the sweep hard-fails the same condition.
**Why F didn't just fix it: F is the party that benefits.** Changing a red gate to green, in another
lane's file, to unblock F's own tag, on the one check guarding "never mis-stocked" with Monster Robot's
real crate as the prize — that move would retroactively devalue every green F has ever reported. The
cost of waiting is an hour. Recommended (A's call): the sweep follows A's own validator; A pins the
four goldens in the same commit; F re-gates and tags. One commit.
**Four findings:**
1. **The honest-gate spec was itself vacuous.** The brief specified id equality — but both packs number
items **positionally** (`rec_0000…`), so **all 120 atlas ids are also generic-pack ids**. An
id-equality gate goes green on the generic pack: the exact failure the vacuous-gate law was written
to stop, reproduced inside its own first application. Titles (0 overlap) and the **texture URL**
the file the pixels come from — are what discriminate. F's gate asserts on those.
2. **F's own selector was lying about the epoch's shop.** `DBG.enterShop(3962749)` → "no shop matching":
a GODVERSE shop has two ids and F's R22 selector knew only the plan id. Fixed.
3. **A's uniqueness check is a silent risk-#3 hole** (new — G didn't catch this): on a mixed cache every
texture shop maps to `undefined` and they **all collapse into one Set entry**, so it fails on the
undefineds and **cannot detect a real duplicate**. Fix it *over the defined ids* — that makes the
guard stronger, not weaker.
4. **The licence flag stopped reaching a human.** E's validator now sees the atlas (R23's blind gate is
closed ✓) but prints `provenance.licence` (British) while G now emits `license` (American, per C) —
so `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`, on real product photos, **silently never prints**
while the gate says OK. E, one line, G already specified it.
---
## Round 23 (v5.0-alpha — THE FIRST REAL CRATE) — **F HELD the tag**
**`v5.0-alpha` is NOT tagged. The crate is real; it has no street.** G's 120 real records off a real

View File

@ -960,3 +960,155 @@ keying) → F (the wire + re-gate)**, and the crate walks.
own R17 wiring gates the cache fetch on `!NOASSETS`, correctly (noassets = zero network), but the tier
ladder's *"same shop under `?noassets`"* can't mean the same shop for a real town. Tier 0 for **stock**
is provable on the default boot; tier 0 for the **town** is a fixture fallback by design. Wording, not a bug.
---
## 24. Round 24 — v5.0-alpha THE ALPHA COMPLETION. **The crate WALKS. F held the tag anyway — A's gate.**
**THE CRATE WALKS.** You can boot `redhill_godverse`, walk down Musgrave Road into **Monster Robot
Party**, riffle **its own real crate**, and buy a real record for its real price. Proven falsifiably,
not by eyeball:
| assertion | result |
|---|---|
| room resolved base | **`assets/stock_godverse/3962749/`** (not the town-wide base) |
| **title equality** | **120/120 in Monster Robot's atlas · 0/120 in the generic town pack** |
| **texture provenance** (the pixels) | **2 atlases, both `stock_godverse/3962749/stock_record_atlas_0*.webp` · 0 from the generic pack** |
| network | `stock_godverse/3962749/` index + both atlases, `200` |
| **buy** | `rec_0000` *"Abstract Latin Lounge II (Part II)"* @ **$15** → cash **191 → 176**, debited exactly 15, count 0→1 |
| gone-this-session → back next boot | reboot ⇒ wallet 191/0 restored (no persistence layer exists) |
**Tier ladder, re-proved end to end:** tier 0 `?noassets` on a godverse town → **zero network requests**,
no base, no pack (the offline law survives the wire) · **soft fall live**: `Restoration Station`
(keyed 2289, no atlas) → `404` → null → parody, and the boot log shows those 404s happening in
production · **no cross-contamination**: after visiting other shops the real crate still resolves its
own 120 · **plain towns untouched**: `newtown_real` → 0 keyed, base `null`, 350 items, textures from
`models/`**F's wire is a complete no-op off the godverse path**, byte-identical to pre-v5.
### What F built (#6a)
`godverseBaseFor(shop)`**exported and used by both** the shell's boot preload and the room's lookup,
deliberately: C's cache identity is the LITERAL `(type, base)` pair, so two sides building that string
differently would miss the cache and drop a real shop to parody — a bug that looks exactly like "no
stock" and would sail straight past a covers-render eyeball. One function, one string, one key.
Boot-preloads keyed shops (`getStockPack` is sync and the room is built the instant the door opens, so
a pack first requested at `enter()` is not resolved in time), gated on `stockReal` and never on the id
alone, so `?noassets` fetches nothing.
### ⚠ FINDING 1 — the honest-gate spec was itself vacuous. The law's first application broke the law.
The brief specified #7a assert **"rendered titles ∈ the atlas index (id equality, not 'covers render')."**
**Id equality cannot discriminate.** Measured: both packs number their items **positionally**
`rec_0000, rec_0001, …` — so the atlas's ids are `rec_0000..rec_0119` and the town-wide pack's are
`rec_0000..rec_0349`. **All 120 atlas ids are also generic-pack ids (120/120 overlap).** An id-equality
gate goes **green on the generic pack** — the exact failure the law was written to stop, reproduced
inside the fix for it. *Ids here are slot numbers, not identity.*
**What actually discriminates:** titles (**0 overlap**) and, strongest, the **texture URL** — the file
the GPU samples. A pack that merely looks right cannot fake the URL its pixels come from. F's gate
asserts on both, and `DBG.stockInfo()` exposes them so the assertion is scriptable, not eyeballed.
### ⚠ FINDING 2 — F's own selector was lying about the epoch's shop
`DBG.enterShop(3962749)` returned **`no shop matching`**. A GODVERSE shop has *two* ids — its plan id
(local, renumbered per town: Monster Robot is **8**) and its `godverseShopId` (**3962749**, the real POS
id, which is what the atlas, every G-side query and every cross-repo bug report names it by). F's R22
selector only knew the first. Fixed (plan id still matches first, so no existing call shifts). *The
harness F shipped for Lane G could not name the one shop the epoch is built on.*
### The 8 reds — F did NOT clear them, and did not touch A's file
`156,193/156,201`. They are **not** product defects; F proved the product correct above. They split:
**2 — goldens moved by G's re-emit** (`newtown_godverse` base `0xcf69b387`, gig `0x1e266f60`), exactly as
ledger #2 intends: adding `godverseShopId` changes the cache, so it changes the plan. A ran *before* G,
so A could not pin what G had not yet emitted. Plus 4 UNPINNED warns (`redhill_godverse`,
`redhill_real`). **F did not re-pin**: if conflict #2 resolves by changing the caches, these move again,
and the amendment law wants the spec change and the pin in ONE commit. Pin them with the resolution.
**6 — conflict #2: A's identity sweep contradicts Fable's own standing Ruling 2.** Ruling 2
([FABLE_TO_LANE_G.md:33](FABLE_TO_LANE_G.md)) *mandates* mixed caches — "merge census heroes **WITH** the
donor's texture shops (cafes/bakeries…)". A cafe lifted from OSM has no GODVERSE identity and never will;
minting one would invent identity, which is charter risk #3 itself. A's sweep asserts
`keyed === cache.shops.length` — i.e. that no such cache may exist. It is a gate authored in the same
wave as the thing it gates, with no mixed cache in existence to test against: **the identical species as
E's blind validator in R23**, one round later, in the opposite direction. G measured it and escalated it
to "A's call, Fable's ruling"; nobody ruled; it arrived at F.
**F's measurement of the hazard the gate guards: it does not exist.** `redhill_godverse` 10 keyed /
**10 unique**; `newtown_godverse` 18 keyed / **18 unique**; **zero duplicates**. And partial keying does
not mis-stock — F watched shop 2289 fall soft to parody in the browser, which is precisely what charter
risk #3 prescribes ("fail-soft to tier 0, never mis-stocked"). **A's own two checks already disagree**:
`validateTownCache` reasons it out correctly — *"Missing = tier 0 (soft). Duplicate = mis-stocked (hard).
They are not the same failure"* — and warns; the sweep hard-fails the same condition.
### ⚠ FINDING 3 — the uniqueness check is a silent risk-#3 hole (new; G did not catch this one)
`ok(new Set(seated.map(s => s.godverseShopId)).size === seated.length, 'godverse ids unique')`. On a
mixed cache every texture shop maps to `undefined`, and **all of them collapse into ONE Set entry** — so
the check fails on the undefineds, *not* on a duplicate, and it **cannot detect a real duplicate** while
any shop is unkeyed. It is the arm A called "the arm that earns its keep" for charter risk #3, and on
every cache Ruling 2 mandates it is **structurally incapable of doing its job, in both directions**.
Whoever fixes the coverage assert must not paper over this one: uniqueness belongs **over the defined
ids** (`seated.filter(s => s.godverseShopId !== undefined)`), where it would actually bite. That change
makes the risk-#3 guard **stronger**, not weaker.
### Why F held the tag — and why this hold is not R23's
R23 held because the **product** was broken. **R24's product is finished and proven.** What is red is a
**gate**, in **Lane A's file**, on a **schema question that is A's to answer** ("what marks the godverse
layer?" has at least three valid designs). F did not resolve it, for one reason above all: **F is the
party that benefits.** F changing a red gate to green, in another lane's file, to unblock F's own tag,
on the one check guarding "never mis-stocked" with Monster Robot's real crate as the prize — that move
would retroactively devalue every green F has ever reported. F's only asset is that F never shades a
result. The cost of waiting is an hour; the cost of F being quietly wrong is the charter's worst risk.
**Recommended (A's call): the sweep follows A's own validator — missing ⇒ warn (tier 0, soft), duplicate
⇒ error (hard, over defined ids), coverage asserted over the keyed layer, not the inherited one.** Then
A pins the four goldens in the same commit and F re-gates and tags. It is one commit's work.
### ⚠ FINDING 4 — the licence flag stopped reaching a human, and the gate says OK
R23's blind validator is **fixed** — E's `validate_atlas` now finds and validates the atlas
(`3962749/stock_record_index.json · Monster Robot Party · items=120 atlases=2 px=2048 OK`). But G's
conflict-#1 leftover is real and still open: `validate_atlas.py:198` prints from
`provenance.licence`**British only** — while G's re-emit carries **`provenance.license`** (American,
per C's corrected contract). So `lic = {}`, and the licence line **silently never prints**. E's *check*
(:105) accepts either spelling, so the gate goes **OK**; only the evidence vanished.
That evidence is not decoration. E's own comment at :199 says *"the licence is PRINTED — clearing it is
a human call"* — it is the **mechanism** by which charter law #3 reaches a person, and the string it
failed to print is **`FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`**, on an atlas of a real shop's real
product photographs with real artist names. A gate that is green while the one thing it exists to
surface is invisible is the vacuous-gate law's own species, wearing the licence law's coat.
**Fix (E, one line, G already specified it):** `.get("licence") or .get("license")` at :198.
**F did not patch it.** It is E's file, F is not blocked on it (the tag is held regardless), and G
already routed it. Filing beats reaching across a lane boundary for a one-liner — the same restraint F
applied to A's gate, for the same reason.
### ⚠ FINDING 5 — F's own gate cried the loudest wolf we own (F's bug, F fixed it)
The classic-regression gate reported **`plan fingerprint != golden 0x3fa36874 — determinism/prime-law
broken`**. The covenant was never touched (`0x3fa36874`, node-side, unmoved; G said so too). F wrote
this gate in R16 with `if hash_ok and r.returncode == 0` — **two assertions, one verdict, reported with
the fingerprint's message** — so *any* unrelated red anywhere in Lane A's selfcheck (here: 8
godverse-identity checks in a different town) screams that the byte-identical-forever covenant is
broken. Fixed: two assertions, two verdicts, and the selfcheck failure now names its own lane and
quotes its own reds. *A gate must name what it actually measured — the same law as the vacuous gates,
one turn further round: it isn't enough to fail for a reason, you have to fail for YOUR reason.*
### ⚠ FINDING 6 — the id spaces collide, and it bit F's own selector inside the anti-vacuous gate
Plan ids are per-town (1..N); godverse ids are real POS ids (31, 767, 3962749). **They share a numeric
space.** Silky Oak Furnature's godverseShopId is **31** — and another shop's *plan* id is also **31**.
F's first cut at the R24 selector tried plan id first, so `enterShop(31)` silently entered **Wholefood
Cafe** instead. The smoke's soft-fall assertion then **passed on an unkeyed cafe** — a vacuous pass,
inside the gate F wrote to stop vacuous passes, in the round whose whole theme is *ids that aren't
identity*. Fixed both ends: `'g:<n>'` selects a godverse id explicitly; a bare number stays the plan id
(back-compatible) and now **reports the collision** rather than choosing quietly; and the smoke enters
via `g:` and asserts the shop it landed in **is the one it asked for** before asserting anything about it.
*The lift itself is clean* — F checked: cache 10 keyed → plan 9 keyed (Empire Revival didn't seat), every
one a real secondhand shop. **No cafe is keyed. There is no mis-stocking.**
### ⚠ FINDING 7 — fail-soft-by-404 vs the 0-console-errors law (a contract gap, filed not papered)
F's boot preload must resolve a keyed shop's pack *before* the door opens (`getStockPack` is sync and
the room builds on the same tick), so it probes every keyed shop. 9 of Red Hill's 10 have no atlas →
`404` → null → parody. **That is the fail-soft law working**, and it is also **8 console errors on every
godverse boot** — a browser-level log no `.catch()` can suppress. The house law is zero.
**The tension is structural: you cannot probe blind and stay silent.** The fix is not to loosen the law
but to stop probing for what isn't there: **an atlas index in G's namespace** (or a `hasStock` flag on
the cache's shop entries, A+G) so the shell preloads exactly the shops that have atlases — 404s then
mean a genuinely missing file, which is what fail-soft is *for*. **Filed as a contract ask, not
invented.** Until it lands, F's smoke counts the probes, names them, and fails on any other error —
tolerance stated out loud, never blanket-ignored.

View File

@ -146,8 +146,19 @@ def classic_regression(p):
capture_output=True, text=True)
hash_ok = (f'{GOLDEN_HASH:#010x}'.replace('0x', '0x') in r.stdout) or (hex(GOLDEN_HASH) in r.stdout) \
or ('3fa36874' in r.stdout)
if hash_ok and r.returncode == 0: OK(f'plan fingerprint == golden {hex(GOLDEN_HASH)} (selfcheck green)')
# [R24] These are TWO assertions and they get two verdicts. This gate used to require
# `hash_ok and r.returncode == 0` and report BOTH failures with the fingerprint's message — so
# any unrelated red anywhere in Lane A's selfcheck (R24: 8 godverse-identity checks in a
# different town) screamed "determinism/prime-law broken", the loudest alarm we own, about a
# covenant that was provably intact (0x3fa36874, node-side, unmoved). A gate must name what it
# actually measured; F wrote this one, so F fixed it. The covenant is asserted on its own.
if hash_ok: OK(f'plan fingerprint == golden {hex(GOLDEN_HASH)} — the classic covenant holds')
else: FAIL(f'plan fingerprint != golden {hex(GOLDEN_HASH)} — determinism/prime-law broken')
if r.returncode == 0: OK('Lane A selfcheck green')
else:
tail = [l for l in r.stdout.splitlines() if l.strip().startswith('')][:3]
FAIL('Lane A selfcheck RED (separate from the covenant above): '
+ ('; '.join(t.strip() for t in tail) if tail else 'see selfcheck output'))
# winmap flips OFF under ?classic (it's default-on now); dig stays opt-in. The streamed roster IS the
# v2.0 baseline (R7 flip / prime-law amendment) — so it must be ON under ?classic.
@ -992,6 +1003,105 @@ def smoke_gigs(p):
b.close()
GODVERSE_TOWN = 'redhill_godverse'
GODVERSE_SHOP = 3962749 # Monster Robot Party, 147 Musgrave Rd, Red Hill QLD — the real shop
GODVERSE_BASE = f'assets/stock_godverse/{GODVERSE_SHOP}/'
def smoke_real_crate(p):
"""R24 — THE REAL CRATE, gated so it cannot pass without touching its subject (the vacuous-gate law).
R23's #7a would have gone green on "real covers render" while the room held the generic v2 pack and
stock_godverse/ was never fetched once. R24's brief then specified id equality — which ALSO cannot
discriminate, because both packs number items positionally (rec_0000), so all 120 of the atlas's ids
are generic-pack ids too. Presence proves nothing; slot numbers prove nothing. This smoke asserts the
two things a wrong pack cannot fake:
1. TITLE equality the room's pack titles are the atlas's (0 of them exist in the generic pack).
2. TEXTURE provenance the URL the GPU samples IS the shop's atlas file.
Plus the rungs either side: a keyed shop with no atlas falls SOFT to parody, and a plain town is
untouched. Subject absent (no town/atlas) explicit SKIP with the reason, never a quiet pass."""
head(f'SMOKE: the real crate (R24 — {GODVERSE_TOWN} · Monster Robot Party · per-shop atlas identity)')
b, pg, errs = new_page(p)
missing = [] # atlas probes that 404 — the fail-soft path, counted not hand-waved
pg.on('response', lambda r: missing.append(r.url) if (r.status == 404 and 'stock_godverse/' in r.url) else None)
try:
pg.goto(f'{HOST}/assets/towns/{GODVERSE_TOWN}.json')
if 'godverse+osm' not in pg.content():
print(f'· SKIP: {GODVERSE_TOWN} cache absent or not godverse+osm — subject absent, nothing asserted (Lane G/E)')
return
boot(pg, f'plansrc=osm&town={GODVERSE_TOWN}&stock=real&dig=1')
res = pg.evaluate("""async ({shop, base}) => {
const D = window.DBG, P = window.PROCITY;
const idxR = await fetch(base + 'stock_record_index.json');
if (!idxR.ok) return { skip: 'atlas absent for ' + shop };
const atlas = new Set(((await idxR.json()).items || []).map(i => i.title));
const town = new Set((((await (await fetch('assets/models/stock_record_index.json')).json()).items) || [])
.map(i => i.title));
const entered = D.enterShop(shop);
await new Promise(r => setTimeout(r, 1400));
const si = D.stockInfo();
if (!si || si.error) return { fail: 'no room: ' + JSON.stringify(si) };
// a keyed shop with NO atlas must fall soft to parody, not inherit this crate
const other = (P.plan.shops || []).find(s => s.godverseShopId && s.godverseShopId !== shop);
let soft = null;
// 'g:<id>' NOT the bare number: plan ids and godverse ids share a numeric space, so a
// bare id can silently enter a different shop and pass this assertion for the wrong reason.
if (other) { D.exitShop(); await new Promise(r => setTimeout(r, 300));
D.enterShop('g:' + other.godverseShopId); await new Promise(r => setTimeout(r, 900));
const o = D.stockInfo();
soft = { name: o.name, gid: o.godverseShopId, want: other.godverseShopId,
packItems: o.packItems }; }
return {
entered, base: si.base, name: si.name, gid: si.godverseShopId,
inAtlas: si.packTitles.filter(t => atlas.has(t)).length,
inTown: si.packTitles.filter(t => town.has(t)).length,
packItems: si.packItems,
fromAtlasTex: si.texUrls.filter(u => u.includes(base)).length,
fromTownTex: si.texUrls.filter(u => u.includes('assets/models/stock_')).length,
soft,
};
}""", {'shop': GODVERSE_SHOP, 'base': GODVERSE_BASE})
if res.get('skip'):
print(f"· SKIP: {res['skip']} — subject absent, nothing asserted (Lane G)"); return
if res.get('fail'):
FAIL(f"the crate: {res['fail']}"); return
print(f" room: {res['name']} (godverse {res['gid']}) → base {res['base']}")
OK(f"resolves its OWN base ({res['base']})") if res['base'] == GODVERSE_BASE else FAIL(f"base is {res['base']}, expected {GODVERSE_BASE}")
OK(f"TITLE equality: {res['inAtlas']}/{res['packItems']} titles are the shop's own atlas") \
if (res['packItems'] > 0 and res['inAtlas'] == res['packItems']) \
else FAIL(f"TITLE equality: only {res['inAtlas']}/{res['packItems']} titles are the shop's atlas")
OK(f"TITLE equality: 0/{res['packItems']} titles from the generic pack — the crate is not the v2 pack") \
if res['inTown'] == 0 \
else FAIL(f"TITLE equality: {res['inTown']}/{res['packItems']} titles came from the GENERIC pack")
OK(f"TEXTURE provenance: {res['fromAtlasTex']} tex from the shop's atlas, {res['fromTownTex']} generic — the pixels' own file") \
if (res['fromAtlasTex'] > 0 and res['fromTownTex'] == 0) \
else FAIL(f"TEXTURE provenance: {res['fromAtlasTex']} atlas / {res['fromTownTex']} generic — the GPU is sampling the wrong file")
if res.get('soft') is not None:
sf = res['soft']
# the subject must BE a keyed shop, or this assertion proves nothing (it passed on an
# unkeyed cafe first time round — a vacuous pass inside the anti-vacuous-pass gate)
if sf.get('gid') != sf.get('want'):
FAIL(f"soft fall: entered the WRONG shop — wanted godverse {sf.get('want')}, got {sf.get('gid')} (\"{sf['name']}\")")
elif sf['packItems'] == 0:
OK(f"soft fall: keyed-but-atlas-less \"{sf['name']}\" (godverse {sf['gid']}) → parody, not this crate")
else:
FAIL(f"soft fall BROKEN: \"{sf['name']}\" inherited {sf['packItems']} items")
# Console errors: the ONLY tolerated ones are the atlas probes that 404 — that IS the fail-soft
# path (a keyed shop with no atlas → null → parody), and it is counted and named here, never
# waved through. Any other error fails. The 404 noise itself is a real cost of probing blind;
# the fix is an atlas index in G's namespace so we never ask for what isn't there (F, R24 §24).
probes = len(missing)
if probes and len(errs) == probes:
print(f' · {probes} atlas-probe 404(s) — the fail-soft path, expected; 0 other console errors')
print(f' (cost of probing blind; fix = an atlas index so we never probe an absent shop)')
elif errs:
FAIL(f'the crate: {len(errs)} console error(s), {probes} explained as atlas probes; first: {errs[0][:120]}')
else:
print(' · 0 page errors')
finally:
b.close()
def smoke_continuity(p):
"""R15 identity continuity (ledger #3): the ped who queued/surged into a venue turns up in its crowd.
Exercises F's real relay end-to-end — VenueQueue.admitOne() → onAdmit → citizens.recordVenueEntry →
@ -1101,6 +1211,7 @@ def main():
smoke_audio(p) # R11 — audio house-law (silent-happy · pre-gesture · mute · noassets · leak)
smoke_gigs(p) # R12/13 — the district gig layer (schedule · band · crowd · budget · cover · noassets)
smoke_continuity(p) # R15 — identity continuity (the ped you followed is in the crowd · settled tris)
smoke_real_crate(p) # R24 — THE REAL CRATE (per-shop atlas identity · soft fall · no-op off the path)
finally:
if srv: srv.terminate()

View File

@ -52,7 +52,7 @@ import { createChunkManager } from './js/world/chunks.js';
import { createPlayer } from './js/world/player.js';
import { createHUD } from './js/world/hud.js';
import { createMinimap } from './js/world/minimap.js';
import { createInteriorMode } from './js/world/interior_mode.js'; // [Lane F integration] Lane C interior bridge
import { createInteriorMode, godverseBaseFor } from './js/world/interior_mode.js'; // [Lane F integration] Lane C interior bridge
import { createWallet } from './js/interiors/wallet.js'; // [Lane F R8] Lane C buy loop v0 (session wallet)
import { CitizenSim } from './js/citizens/sim.js'; // [Lane F integration] Lane D street pedestrians
import { createWeather } from './js/world/weather.js'; // [Lane F R8] Lane B seeded weather (?weather=1)
@ -196,7 +196,23 @@ const DIG_ON = params.has('dig') && params.get('dig') !== '0';
const STOCK_REAL = params.get('stock') === 'real' && !NOASSETS;
// [Lane F R9] preload ALL three packs so record dig sleeves AND book-spine/toy-box shelves (buy-anywhere)
// are real. Fail-soft per pack (missing → parody canvas). getStockPack(type) is what buildInterior reads.
if (STOCK_REAL) { preloadStockPack('record'); preloadStockPack('book'); preloadStockPack('toy'); }
// [Lane F R24 §6a — THE REAL CRATE] The town-wide packs stay exactly as they were (every shop without a
// GODVERSE identity keeps them, byte-identically). On TOP of them, every shop carrying A's `godverseShopId`
// preloads its OWN pack under its OWN base — because `getStockPack` is sync and the room is built the
// instant you open the door, so a pack first requested at enter() would not be resolved in time and the
// real shop would show parody on the walk-in. Cache identity is (type, base) since C's R24 fix, so these
// coexist with the town-wide packs rather than clobbering them. A keyed shop with no atlas 404s → null →
// parody: fail-soft runs through the same path, and that is the tier-0 rung of the ladder, live.
if (STOCK_REAL) {
preloadStockPack('record'); preloadStockPack('book'); preloadStockPack('toy');
const seen = new Set();
for (const s of (plan.shops || [])) {
const gBase = godverseBaseFor(s);
if (!gBase || seen.has(gBase + s.type)) continue;
seen.add(gBase + s.type);
preloadStockPack(s.type, { base: gBase });
}
}
// [Lane F R8 — Lane C buy loop v0] ONE session wallet (seeded start cash) bound to the dig BUY across shops.
// Runtime-only: never writes back into the plan/room build, so goldens + draw-counts are unaffected.
const wallet = createWallet(seed);
@ -466,7 +482,7 @@ import('./js/world/audio.js')
if (params.get('dbg')) {
import('./js/world/dbg.js')
.then((m) => m.installDBG({ plan, camera, renderer, composer, chunks, lighting, player, hud,
setMode, getMode: () => MODE, enterShop }))
setMode, getMode: () => MODE, enterShop, interiorMode })) // [R24] interiorMode → DBG.stockInfo()
.catch((e) => console.warn('[procity] DBG harness load failed:', e));
}
} // end main()

View File

@ -12,7 +12,7 @@ import { busShelterStops } from './furniture.js';
export function installDBG(deps) {
const { plan, camera, renderer, composer, chunks, lighting, player, hud,
setMode, getMode, enterShop } = deps;
setMode, getMode, enterShop, interiorMode = null } = deps;
const CS = 64;
// Force the renderer + composer + camera to the live viewport before every harness render, then
@ -268,22 +268,53 @@ export function installDBG(deps) {
// DBG.enterShop('record') → first shop of that registry type
// DBG.enterShop('The Exchange Hotel')→ by exact name, else name substring (case-insensitive)
// Resolution order is most-specific-first so an id-shaped string can never be shadowed by a name.
// [R24] `godverseShopId` joins the selector — carefully, because THE TWO ID SPACES COLLIDE.
// A GODVERSE shop has two ids: its plan id (local, renumbered per town, 1..N) and its
// godverseShopId (the real POS/dealgod id — what the atlas, every G-side query and every
// cross-repo bug report names it by). Monster Robot is plan 8 / godverse 3962749. But Silky Oak's
// godverse id is 31 and some *other* shop's PLAN id is also 31 — so a bare number is genuinely
// ambiguous, and F's first cut at this silently entered the wrong shop (a cafe) while looking
// like it worked. Bare number ⇒ plan id (unchanged, back-compatible); `'g:<n>'` ⇒ godverse id,
// explicitly. When a bare number matches BOTH, we say so in the return rather than pick quietly.
enterShop(sel) {
const shops = plan.shops || [];
let s = null;
if (typeof sel === 'number') s = shops.find((x) => x.id === sel);
else if (typeof sel === 'string') {
let s = null, ambiguousWith = null;
if (typeof sel === 'number') {
s = shops.find((x) => x.id === sel) || null;
const g = shops.find((x) => x.godverseShopId === sel) || null;
if (s && g && g !== s) ambiguousWith = { godverseShopId: sel, name: g.name };
if (!s) s = g;
} else if (typeof sel === 'string') {
const q = sel.trim().toLowerCase();
s = shops.find((x) => String(x.id) === q) // id-as-string
|| shops.find((x) => (x.name || '').toLowerCase() === q) // exact name
|| shops.find((x) => (x.type || '').toLowerCase() === q) // registry type
|| shops.find((x) => (x.name || '').toLowerCase().includes(q)); // name substring
const gm = /^g(?:odverse)?:(\d+)$/.exec(q);
if (gm) {
const n = Number(gm[1]);
s = shops.find((x) => x.godverseShopId === n) || null;
if (!s) return { error: `no shop with godverseShopId ${n}` };
} else {
s = shops.find((x) => String(x.id) === q) // id-as-string
|| shops.find((x) => (x.name || '').toLowerCase() === q) // exact name
|| shops.find((x) => (x.type || '').toLowerCase() === q) // registry type
|| shops.find((x) => (x.name || '').toLowerCase().includes(q)); // name substring
}
}
if (!s) return { error: `no shop matching ${JSON.stringify(sel)}` };
enterShop(s.id, s.name);
return { entered: s.id, name: s.name, type: s.type };
const out = { entered: s.id, name: s.name, type: s.type,
godverseShopId: s.godverseShopId || null };
if (ambiguousWith) out.ambiguous = `${sel} is also the godverseShopId of "${ambiguousWith.name}" ` +
`— entered by PLAN id; use 'g:${sel}' for the godverse shop`;
return out;
},
exitShop() { window.dispatchEvent(new CustomEvent('procity:exitShop')); return { exited: true }; },
// [R24] The identity of the stock in the open room — the falsifiable the #7a gate asserts on.
// Returns null with no room open (the caller must enter first; a null here FAILS the gate rather
// than passing quietly — vacuous-gate law).
stockInfo() {
if (!interiorMode) return { error: 'interiorMode not wired into DBG' };
return interiorMode.stockInfo || { error: 'no interior open — enterShop() first' };
},
};
window.DBG = DBG;

View File

@ -15,7 +15,7 @@
// The interior renders into its OWN dark THREE.Scene (the street scene is frozen, not disposed —
// CITY_SPEC L3 "pause, not dispose"), so returning to the street is instant.
import { buildInterior, getStockPack, makeStockAdapter } from '../interiors/interiors.js';
import { buildInterior, getStockPack, makeStockAdapter, preloadStockPack } from '../interiors/interiors.js';
import { KeeperManager } from '../citizens/keepers.js'; // Lane D — shopkeeper behind the counter
import { GigCrew } from '../citizens/band.js'; // Lane D — band trio + audience (R12, ?gigs=1)
import { createDig, binSeed } from '../interiors/dig.js'; // Lane C — crate-riffle (v2, gated on ?dig=1)
@ -36,6 +36,17 @@ const EXIT_DIST = 1.0; // …then coming back within this distance leaves to th
// lives in CITY_SPEC §v3. See LANE_F_NOTES §12.
const WALLA_KEY = 'crowd-walla'; // [R13] manifest.ambience — crowd murmur layered under the gig bed while 'on'
// [Lane F R24 §6a] The per-shop stock base for a GODVERSE shop — A's `godverseShopId` → C's per-shop
// pack location (LANE_C_PUB §7.3/§7.5). Exported and used by BOTH the shell's boot preload and the room's
// lookup, deliberately: C's cache identity is the LITERAL (type, base) pair, so if the two sides built
// this string even slightly differently the lookup would miss and a real shop would fall silently to
// parody — a bug that looks exactly like "no stock" and would sail past a covers-render eyeball.
// One function, one string, one cache key. Shops without the field return undefined → town-wide packs.
export function godverseBaseFor(shop) {
const id = shop && shop.godverseShopId;
return id ? `assets/stock_godverse/${id}/` : undefined;
}
export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null, useGLB = false,
dig: digEnabled = false, stockReal = false, wallet = null,
occupancyOf = null, gigState = null, rosterOf = null }) {
@ -189,7 +200,15 @@ export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null
if (current) exit(); // never stack interiors
// [F2 §stock=real] real GODVERSE sleeves in this shop's crates. makeStockAdapter(null) → null →
// Lane C fails soft to the parody canvas, so a missing/unpreloaded pack just shows placeholders.
currentAdapter = stockReal ? makeStockAdapter(getStockPack(shop.type)) : null;
// [R24 §6a — the per-shop base] A shop that carries A's `godverseShopId` is a REAL shop with its own
// real stock, so it resolves its OWN pack under its OWN base (C's §7.5 seam); everything else keeps
// the town-wide packs, byte-identically. Cache identity is (type, base) since C's R24 fix, so the two
// coexist instead of the per-shop pack silently inheriting the town-wide one (the R23 bug that held
// the tag). Missing atlas → 404 → null → parody: the fail-soft law runs through the SAME path.
// Gated on stockReal, never on the id alone: ?noassets ⇒ stockReal false ⇒ not one byte fetched.
const gBase = stockReal ? godverseBaseFor(shop) : undefined;
if (gBase) preloadStockPack(shop.type, { base: gBase }); // idempotent; re-entry is a cache hit
currentAdapter = stockReal ? makeStockAdapter(getStockPack(shop.type, gBase)) : null;
// [Lane F R13 THE DISTRICT] Is THIS shop the venue with a gig on right now? Per-venue query — a town has
// 24 venues, each with its own latch keyed by shop id. Lane C only stamps room.audio.gigKey when we pass
// opts.gig, so pass NULL unless it's really on: a quiet-night venue then builds as a normal interior
@ -332,5 +351,53 @@ export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null
get crew() { return crew; }, // [R12] Lane D GigCrew (null until a gig has been entered)
get crewInfo() { return crewInfo; }, // [R12] { band, crowd } actually spawned — F's smokes assert on this
get digActive() { return !!(dig && dig.active); }, // [F2] shell reads this to keep pointer-lock/Esc sane while riffling
// [Lane F R24 — the honest-gate surface, per the vacuous-gate law] What stock is in THIS room, BY
// IDENTITY. R23's #7a could go green on "real covers render" while the room held the generic v2 pack
// and `stock_godverse/` was never fetched — presence, not identity. So the gate needs falsifiables:
// which base the room resolved, how many items, and the actual item ids on the rendered buy meshes
// (C tags those with `buyItems[].item`; a plain sleeve carries only `isStock`, so ids come from the
// buyables and the pack, never from "it looked right"). Null when no room is open.
get stockInfo() {
if (!current || !currentShop) return null;
const base = stockReal ? godverseBaseFor(currentShop) : undefined;
const pack = stockReal ? getStockPack(currentShop.type, base) : null;
const renderedIds = [], renderedTitles = [], texUrls = new Set();
let stockMeshes = 0;
scene.traverse((o) => {
const u = o.userData || {};
if (!u.isStock && !u.buyMesh) return;
stockMeshes++;
if (Array.isArray(u.buyItems)) {
for (const b of u.buyItems) if (b && b.item) {
if (b.item.id != null) renderedIds.push(String(b.item.id));
if (b.item.title) renderedTitles.push(b.item.title);
}
}
// The atlas the GPU is actually sampling. This is the ONLY assertion that survives everything:
// ids are positional slots (`rec_0000`) that BOTH packs reuse, so id equality cannot tell the
// real crate from the generic v2 pack — every atlas id is also a town-pack id (measured R24).
// A texture URL is the file the pixels come from. Falsifiable, and it cannot be faked by a
// pack that merely looks right.
for (const m of (Array.isArray(o.material) ? o.material : [o.material])) {
const src = m && m.map && m.map.image && (m.map.image.src || m.map.image.currentSrc);
if (src) texUrls.add(String(src));
}
});
return {
shopId: currentShop.id,
godverseShopId: currentShop.godverseShopId || null,
name: currentShop.name,
type: currentShop.type,
base: base || null, // null ⇒ the town-wide pack (or no stock at all)
packItems: pack && pack.items ? pack.items.length : 0,
packIds: pack && pack.items ? pack.items.map((i) => String(i.id)) : [],
packTitles: pack && pack.items ? pack.items.map((i) => i.title).filter(Boolean) : [],
stockMeshes,
renderedIds,
renderedTitles,
texUrls: [...texUrls],
};
},
};
}