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:
parent
533fa32b59
commit
29942002eb
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
// 2–4 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],
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user