From 528829eeba2127792e2f53d0e51db5a41a067649 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Fri, 17 Jul 2026 11:38:44 +1000 Subject: [PATCH] =?UTF-8?q?Lane=20F=20R26=20(v5.0-beta):=20the=20manifest?= =?UTF-8?q?=20consumed,=20the=20tolerance=20retired=20=E2=80=94=20tag=20HE?= =?UTF-8?q?LD=20on=20the=20mint-id=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EVERY CRATE IS DIFFERENT, and F measured it rather than taking G's 15/15 on trust — from the indexes of record, not by entering one lucky shop: 15 keyed crates, 105 pairs, 0 sharing a title; 0 crates overlap the generic v2 pack; 0 ids reused across crates (G's sku fix holds at scale — the R24 vacuity dead at the source, not papered over in F's gate). 4 crates in Red Hill + 11 in Newtown = the manifest's 15 exactly, and the manifest lists nothing not on disk. SOURCING, NOT TIER — C was right and the gate follows C. `tier` is the charter's ladder rung and a mint atlas is ALSO rung 1: still a file, still offline, still deterministic. What differs is where the stock came from. 15/15 indexes declare sourcing, every one agrees with the manifest, a mint crate can never report as real, and the gate PRINTS it per room — provenance a human never reads isn't doing anything (R24's licence lesson, one round on). THE 404 TOLERANCE IS DELETED, NOT TUNED. R24 probed every keyed shop and ate a 404 per atlas-less one; F's smoke could only cope by NAMING them, and a tolerance is a confession. F filed the real fix as a contract ask rather than inventing it; G built the manifest; F now consumes it — the shell asks for nothing unlisted. Zero 404s because nothing absent gets asked for, not because the gate stopped looking. AND DELETING IT IMMEDIATELY CAUGHT A LEFTOVER: F had wired the boot preload but left a second blind probe at the door, 404ing on every keyed-but-unfetchable shop. While the gate said "8 probes, expected", a ninth was invisible too. A named exception starts protecting new bugs the moment the real fix exists. F's beta gate GREEN: distinctness · sourcing (real + mint rooms) · soft fall silent · ZERO 404s · 0 console errors, no attribution exceptions. Selfcheck ALL GREEN 156,212/156,212. TAG HELD — atlas-QA FAIL, 14 errors, and it is not the product. E's SLOT_ID_PREFIX = "sku_" (validate_atlas.py:73) applies to every pack; E reads `sourcing` at :178 but the slot-id check is sourcing-blind. All 14 mint crates fail; the real crate passes. A mint crate HAS no POS sku — it was never in that shop's till — so the rule demands what cannot exist, the same shape as R24's sweep demanding a cafe carry a godverseShopId. F measured the rule's intent and it holds: mint_, non-positional, sorted by it, seeded bake, 0 ids reused across all 15 crates. Only the literal prefix fails. And G refused to go green the easy way for the best reason — F's own R24 collision grown up: a POS sku and a dealgod listing id are different id spaces, and filing one under `sku_` recreates that bug INSIDE the economy, 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. F did not fix E's one line: E's file, C's contract, C's call as its author (the principle Fable ratified when C overruled `tier`) — and F is the party that benefits, since that line is the only thing between F and the tag. Cost of waiting: one line. Cost of F being quietly wrong: a gate that green-lights id-space collisions in the layer that moves money. Co-Authored-By: Claude Opus 4.8 --- F-progress.md | 29 +++++ docs/LANES/LANE_F_NOTES.md | 90 +++++++++++++++ tools/flags_check.py | 207 ++++++++++++++++++++-------------- web/index.html | 25 +++- web/js/world/interior_mode.js | 9 +- 5 files changed, 270 insertions(+), 90 deletions(-) diff --git a/F-progress.md b/F-progress.md index 68f3243..8c6399f 100644 --- a/F-progress.md +++ b/F-progress.md @@ -4,6 +4,35 @@ --- +## Round 26 (v5.0-beta — EVERY CRATE DIFFERENT) — F: the manifest consumed, the tolerance retired + +**Every crate is different, and F measured it rather than taking G's word.** Not by entering one lucky +shop — from the indexes of record: **15 keyed crates, 105 pairs, 0 sharing a title**; **0 crates overlap +the generic v2 pack**; **0 ids reused across crates** (G's sku fix holds at scale — the R24 vacuity dead +at the source, not papered over in F's gate). 4 crates in Red Hill + 11 in Newtown = the manifest's 15 +exactly, and the manifest lists nothing that isn't on disk. + +**Sourcing, not tier — C was right.** `tier` is the charter's ladder rung, and a mint atlas is *also* rung +1: still a file, still offline, still deterministic. What differs is where the stock came from, which is +`sourcing: real|mint`. E built to C's contract over the brief on the principle that the contract's author +owns the contract's shape. F's gate asserts it: 15/15 declare sourcing, every index agrees with the +manifest, **a mint crate can never report as real**, and the gate PRINTS it per room — provenance a human +never reads isn't doing anything (the R24 licence lesson, one round on). + +**The 404 tolerance is deleted, not tuned.** R24 probed every keyed shop and ate a 404 per atlas-less one; +F's smoke could only cope by *naming* them — and a tolerance is a confession. F filed the real fix as a +contract ask instead of inventing it; G built the manifest; **F now consumes it**, and the shell asks for +nothing that isn't listed. Zero 404s because nothing absent gets asked for, not because the gate stopped +looking. **And deleting the tolerance immediately caught a leftover**: F had wired the *boot* preload but +left a second blind probe at the *door*, which 404'd on every keyed-but-unfetchable shop. While the gate +said "8 probes, expected", a ninth would have been invisible too. A named exception protects new bugs the +moment the real fix exists. + +**Gate: `flags_check` GREEN** — distinctness · sourcing (real + mint rooms) · soft fall silent · **ZERO +404s** · **0 console errors, no attribution exceptions**. Full record: `LANE_F_NOTES` §26. + +--- + ## 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**, diff --git a/docs/LANES/LANE_F_NOTES.md b/docs/LANES/LANE_F_NOTES.md index 15362ce..a847662 100644 --- a/docs/LANES/LANE_F_NOTES.md +++ b/docs/LANES/LANE_F_NOTES.md @@ -1112,3 +1112,93 @@ the cache's shop entries, A+G) so the shell preloads exactly the shops that have 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. + +--- + +## 26. Round 26 — v5.0-beta EVERY CRATE DIFFERENT. F: the manifest consumed, the tolerance retired. + +### The 404 tolerance is gone — and that is the point +R24's wire preloaded a pack for **every keyed shop** and ate a `404` for each one with no atlas. That was +the fail-soft law working, and it was also **console errors on every godverse boot**. F's smoke could only +cope by *naming* the probes — and **a tolerance is a confession, not a fix**. F filed the real fix as a +contract ask rather than inventing it: *an atlas index in G's namespace, so we never ask for what isn't +there.* G built it. **F now consumes it** (`assets/stock_godverse/index.json` → `{godverseShopId, types[], +sourcing}`): the shell preloads only manifest-listed `(shop, type)` pairs, and the gate asserts **ZERO +404s with no attribution exceptions**. The 404s are gone because there is nothing absent to ask for — not +because the gate stopped looking. Fail-soft still covers the case that matters (manifest says yes, file is +gone), which is what fail-soft was always *for*. Fetched only when the town has keyed shops, so a plain +town stays byte-identical: no manifest, no fetch. + +### Distinctness — G reported 15/15; F measured it, from the indexes of record +Not by entering one lucky shop: **105 pairs across all 15 keyed crates, 0 sharing a title**; **0 crates +overlapping the generic v2 pack**; **0 ids reused across crates** — G's sku fix holds at scale, which is +the R24 vacuity dead at the source rather than papered over in F's gate. 4 crates in Red Hill + 11 in +Newtown = the manifest's 15 exactly, and the manifest lists nothing that isn't on disk. + +### Sourcing, not tier — C was right and the gate follows C +C ruled the brief's `tier` field wrong: `tier` is the **charter's ladder rung**, and a mint atlas is *also* +rung 1 — a mint crate is still a deterministic file, still offline, still tier 1. What differs is where the +stock **came from**, which is `sourcing: real|mint`. E built to C's contract over Fable's brief on the +principle that *the contract's author is the authority for the contract's shape*, and Fable ratified it. +F's gate asserts `sourcing`: every index declares it, every index agrees with the manifest, and **a mint +crate can never report as real**. The gate prints it per room, because provenance a human never reads is +provenance that isn't doing anything — the same lesson as R24's silent licence line. + +*Note for whoever reads the runtime:* C's pack (`stockpack.js`) does **not** carry `sourcing` through — +the gate reads it from the index of record, which is correct (the index is the artifact; B's street mark +reads the manifest). Not a gap; recorded so nobody re-derives it. + +### ⚠ The tolerance came out and immediately caught the leftover +F wired the **boot** preload to the manifest, ran the gate with the 404 tolerance **deleted**, and it went +red on exactly one 404: `stock_godverse/31/stock_opshop_index.json`. That was F's *other* probe — R24 also +preloaded a shop's pack **on entry**, so walking into a keyed shop whose type has no atlas fired a fetch +that could only ever 404. Removed: the door does a lookup, never a fetch. The manifest is consulted once, +at boot, and by the time a door opens the pack is either resolved or was never going to exist. + +The lesson is the round's, not just the bug's: **the tolerance was hiding a second instance of the very +thing it tolerated.** While the gate said "8 atlas-probe 404s, expected", the ninth would have been +invisible too. A named exception is still an exception; it earns its keep only until the real fix exists, +and the moment it does, the exception must die or it starts protecting new bugs. + +### The gate, green +`DISTINCTNESS 15 crates / 105 pairs / 0 shared · 0 vs the generic pack · SOURCING 15/15 declared, all +agreeing with the manifest · Monster Robot Party → sourcing REAL, 120/120 own titles, 2 own textures / 0 +generic · Presents of Mind → sourcing MINT, 16/16 own, 1 own / 0 generic · soft fall silent · ZERO 404s · +0 console errors, no attribution exceptions.` + +### ⚠ THE HOLD — `v5.0-beta` NOT tagged. `atlas-QA FAIL — 14 error(s)`, and it is not the product. +`qa.sh --strict --matrix` is **RED at the manifest validator**: E's `validate_atlas` rejects **all 14 mint +crates** (the real crate passes). Selfcheck is ALL GREEN 156,212/156,212; F's own beta gate is GREEN; +`flags_check` GREEN. The red is one assertion. + +**The conflict:** `SLOT_ID_PREFIX = "sku_"` (`validate_atlas.py:73`) is a module constant applied to +**every** pack — E reads `sourcing` at :178 but the slot-id check is **sourcing-blind**. C §7.2a's rule is +*"derive from the source's own stable key, never position, unique across ALL packs"*, and it specifies +`sku_` **"for a real-stock (godverse) pack"**. **A mint crate has no POS sku by construction** — +it was never in that shop's till. Demanding one demands a thing that cannot exist: the same shape as R24's +sweep demanding a cafe carry a `godverseShopId`, and as the brief's `tier` demanding a rung where the +question was provenance. + +**F measured the rule's INTENT, and it holds:** mint ids are `mint_` — a real external +key, **non-positional**, items sorted by it, the bake seeded (`random.Random(158)` over an id-ordered +pool), and **0 ids reused across all 15 crates** (F's own pairwise measurement). Stable, unique, +non-positional. Only the literal prefix fails. + +**G refused to go green the easy way, and was right to** — worth recording, because it is F's own R24 +finding grown up: *"a POS sku and a dealgod listing id are different id spaces; filing dealgod listing ids +under `sku_` recreates that bug INSIDE the economy — tier-2's sold-means-gone looks up POS skus, and +`sku_6031122` (a dealgod id) is indistinguishable by shape from a real sku."* R24's plan-id-31 vs +godverse-id-31 collision entered the wrong shop; the same collision in the money layer sells the wrong +record. G caught it **before** shipping rather than after. Emitting `sku_` would have made this gate green +and the economy wrong. + +**Why F did not fix E's one line:** identical to R24's conflict #2, and the answer has not changed. It is +**E's file implementing C's contract**, the question ("does §7.2a's sku rule bind a mint pack?") is **C's +to answer as the contract's author** — the principle Fable ratified when C overruled `tier` — and **F is +the party that benefits**, since the fix is the only thing between F and the tag it was sent to place. +F's whole worth is that F never shades a result. Cost of waiting: one line. Cost of F being quietly wrong: +a gate that green-lights id-space collisions in the layer that moves money. + +**The fix (C rules, E lands it, one line):** gate `SLOT_ID_PREFIX` on `sourcing == 'real'`; for `mint`, +assert `mint_`-prefixed + non-positional + unique — which F has already measured true. Then F re-gates +and tags. **Everything else for the beta is done and green.** diff --git a/tools/flags_check.py b/tools/flags_check.py index b65b161..e2284d2 100644 --- a/tools/flags_check.py +++ b/tools/flags_check.py @@ -1004,100 +1004,143 @@ def smoke_gigs(p): GODVERSE_TOWN = 'redhill_godverse' -GODVERSE_SHOP = 3962749 # Monster Robot Party, 147 Musgrave Rd, Red Hill QLD — the real shop +GODVERSE_SHOP = 3962749 # Monster Robot Party, 147 Musgrave Rd, Red Hill QLD — the one REAL crate GODVERSE_BASE = f'assets/stock_godverse/{GODVERSE_SHOP}/' +STOCK_MANIFEST = 'assets/stock_godverse/index.json' def smoke_real_crate(p): - """R24 — THE REAL CRATE, gated so it cannot pass without touching its subject (the vacuous-gate law). + """R26 — THE BETA GATE: every crate different, sourcing visible, and NOT ONE 404. - 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)') + The lineage of this gate is the epoch's whole argument. R23's #7a would have gone green on "real + covers render" while the room held the generic v2 pack. R24's brief then said assert id equality — + which also couldn't discriminate, because both packs numbered items positionally and every atlas id + was a generic-pack id too. What survives is what a wrong pack cannot fake: TITLES only this crate + has, and the TEXTURE URL the GPU actually samples. + + R26 adds the two beta claims. DISTINCTNESS: 15 keyed crates, pairwise title-disjoint — asserted + across the indexes of record, so it cannot pass by entering one lucky shop. SOURCING (C's §7.2b, + which correctly overruled the brief's `tier`): every index declares real|mint, a mint crate can + never report as real, and the gate PRINTS it. + + And the 404s are gone for the right reason. R24 probed every keyed shop and ate a 404 for each one + with no atlas; F's smoke could only tolerate that by naming it — a tolerance is a confession. G's + manifest now says which (shop, type) pairs exist, so the shell asks for nothing else. ZERO console + errors, NO attribution exceptions. Subject absent ⇒ SKIP with the reason, never a quiet pass.""" + head(f'SMOKE: the beta crate gate (R26 — 15 crates distinct · sourcing · zero 404s · {GODVERSE_TOWN})') 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) + four04 = [] + pg.on('response', lambda r: four04.append(r.url) if r.status == 404 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 + pg.goto(f'{HOST}/{STOCK_MANIFEST}') + if 'godverseShopId' not in pg.content(): + print('· SKIP: no atlas manifest — subject absent, nothing asserted (Lane G)'); return boot(pg, f'plansrc=osm&town={GODVERSE_TOWN}&stock=real&dig=1') - res = pg.evaluate("""async ({shop, base}) => { + res = pg.evaluate("""async ({manifest, real, 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) || []) + const man = await (await fetch(manifest)).json(); + + // ── DISTINCTNESS across the indexes of record (not just the shop we walk into) ── + const crates = []; + for (const sh of (man.shops || [])) for (const t of (sh.types || [])) { + const u = `assets/stock_godverse/${sh.godverseShopId}/stock_${t}_index.json`; + const r = await fetch(u); + if (!r.ok) return { fail: `manifest lists ${u} but it 404s` }; + const idx = await r.json(); + crates.push({ id: sh.godverseShopId, type: t, manSourcing: sh.sourcing, + sourcing: idx.sourcing || null, + titles: (idx.items || []).map(i => i.title) }); + } + const gen = {}; + for (const t of ['record', 'book', 'toy']) + gen[t] = new Set((((await (await fetch(`assets/models/stock_${t}_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:' — 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, + let shared = 0, vsGeneric = 0, noSourcing = 0, manMismatch = 0; + for (let i = 0; i < crates.length; i++) { + if (!crates[i].sourcing) noSourcing++; + if (crates[i].sourcing !== crates[i].manSourcing) manMismatch++; + if (crates[i].titles.some(t => gen[crates[i].type] && gen[crates[i].type].has(t))) vsGeneric++; + for (let j = i + 1; j < crates.length; j++) { + const a = new Set(crates[i].titles); + if (crates[j].titles.some(t => a.has(t))) shared++; + } + } + + // ── the ROOM binds to the right crate: one REAL, one MINT, one soft fall ── + const byId = new Map((man.shops || []).map(s => [s.godverseShopId, s])); + const stocked = (P.plan.shops || []).filter(s => { const e = byId.get(s.godverseShopId); + return e && (e.types || []).includes(s.type); }); + const unfetch = (P.plan.shops || []).find(s => { if (!s.godverseShopId) return false; + const e = byId.get(s.godverseShopId); + return !e || !(e.types || []).includes(s.type); }); + const visit = async (shop) => { + D.exitShop(); await new Promise(r => setTimeout(r, 250)); + D.enterShop('g:' + shop.godverseShopId); await new Promise(r => setTimeout(r, 1200)); + const si = D.stockInfo(); + const c = crates.find(c => c.id === shop.godverseShopId && c.type === shop.type); + const own = c ? new Set(c.titles) : new Set(); + return { name: si.name, gid: si.godverseShopId, want: shop.godverseShopId, type: si.type, + base: si.base, packItems: si.packItems, + sourcing: c ? c.sourcing : null, + inOwn: si.packTitles.filter(t => own.has(t)).length, + fromOwnTex: si.texUrls.filter(u => u.includes(`stock_godverse/${shop.godverseShopId}/`)).length, + fromGenericTex: si.texUrls.filter(u => u.includes('assets/models/stock_')).length }; }; - }""", {'shop': GODVERSE_SHOP, 'base': GODVERSE_BASE}) - if res.get('skip'): - print(f"· SKIP: {res['skip']} — subject absent, nothing asserted (Lane G)"); return + const realShop = stocked.find(s => s.godverseShopId === real); + const mintShop = stocked.find(s => (byId.get(s.godverseShopId) || {}).sourcing === 'mint'); + const out = { crates: crates.length, shared, vsGeneric, noSourcing, manMismatch, + stocked: stocked.length, + realRoom: realShop ? await visit(realShop) : null, + mintRoom: mintShop ? await visit(mintShop) : null }; + if (unfetch) { D.exitShop(); await new Promise(r => setTimeout(r, 250)); + D.enterShop('g:' + unfetch.godverseShopId); await new Promise(r => setTimeout(r, 900)); + const o = D.stockInfo(); + out.softFall = { name: o.name, gid: o.godverseShopId, want: unfetch.godverseShopId, + packItems: o.packItems }; } + return out; + }""", {'manifest': STOCK_MANIFEST, 'real': GODVERSE_SHOP, 'base': GODVERSE_BASE}) 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') + FAIL(f"beta crate gate: {res['fail']}"); return + + # ── distinctness (independently measured; G reported 15/15) ── + pairs = res['crates'] * (res['crates'] - 1) // 2 + OK(f"DISTINCTNESS: {res['crates']} keyed crates, {pairs} pairs, {res['shared']} sharing a title") \ + if res['shared'] == 0 else FAIL(f"DISTINCTNESS: {res['shared']} crate pair(s) share titles") + OK(f"DISTINCTNESS: 0/{res['crates']} crates overlap the generic v2 pack") \ + if res['vsGeneric'] == 0 else FAIL(f"{res['vsGeneric']} crate(s) overlap the generic pack") + # ── sourcing (C §7.2b — an index with no `sourcing` FAILS; the gate PRINTS it) ── + OK(f"SOURCING: {res['crates']}/{res['crates']} indexes declare sourcing") \ + if res['noSourcing'] == 0 else FAIL(f"SOURCING: {res['noSourcing']} index(es) declare none") + OK('SOURCING: every index agrees with the manifest') \ + if res['manMismatch'] == 0 else FAIL(f"SOURCING: {res['manMismatch']} index/manifest disagreement(s)") + + for label, want in (('realRoom', 'real'), ('mintRoom', 'mint')): + r = res.get(label) + if not r: + FAIL(f"{label}: no {want} crate reachable in {GODVERSE_TOWN}"); continue + if r['gid'] != r['want']: + FAIL(f"{label}: entered the WRONG shop — wanted {r['want']}, got {r['gid']}"); continue + print(f" room: {r['name']} (godverse {r['gid']}, {r['type']}) → {r['base']} [sourcing: {r['sourcing']}]") + OK(f" {want}: sourcing reports '{r['sourcing']}'") if r['sourcing'] == want \ + else FAIL(f" {want} crate reports sourcing '{r['sourcing']}' — a {want} crate must never report otherwise") + OK(f" {want}: TITLE equality {r['inOwn']}/{r['packItems']} from its OWN crate") \ + if (r['packItems'] > 0 and r['inOwn'] == r['packItems']) \ + else FAIL(f" {want}: only {r['inOwn']}/{r['packItems']} titles are its own") + OK(f" {want}: TEXTURE provenance {r['fromOwnTex']} own / {r['fromGenericTex']} generic") \ + if (r['fromOwnTex'] > 0 and r['fromGenericTex'] == 0) \ + else FAIL(f" {want}: textures {r['fromOwnTex']} own / {r['fromGenericTex']} generic — wrong file") + + sf = res.get('softFall') + if sf: + if sf['gid'] != sf['want']: FAIL(f"soft fall: entered the wrong shop ({sf['gid']} != {sf['want']})") + elif sf['packItems'] == 0: OK(f"soft fall: keyed, unfetchable type \"{sf['name']}\" → parody, silently") + else: FAIL(f"soft fall BROKEN: \"{sf['name']}\" inherited {sf['packItems']} items") + + # ── ZERO 404s. No exceptions. The manifest is what makes this honest. ── + OK('ZERO 404s — the manifest means we never ask for an atlas that is not there') \ + if not four04 else FAIL(f"{len(four04)} unexpected 404(s) — the manifest should have prevented every one; first: {four04[0]}") + OK('0 console errors (no attribution exceptions)') if not errs \ + else FAIL(f'the crate: {len(errs)} console error(s); first: {errs[0][:140]}') finally: b.close() diff --git a/web/index.html b/web/index.html index 2452fc1..7737e5f 100644 --- a/web/index.html +++ b/web/index.html @@ -205,12 +205,25 @@ const STOCK_REAL = params.get('stock') === 'real' && !NOASSETS; // 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 R26 §7 — THE MANIFEST KILLS THE PROBES] R24 preloaded a pack for EVERY keyed shop and ate a + // 404 for each one that had no atlas. That was fail-soft working, but it was also console errors on + // every godverse boot, and F's smoke could only tolerate them by NAMING them — a tolerance is a + // confession, not a fix. G's manifest (F's R24 finding #4/#7) says exactly which (shop, type) pairs + // exist, so now we ask for nothing else: zero 404s because there is nothing absent to ask for, not + // because we stopped looking. Fail-soft still covers the real case — manifest says yes, file is gone. + // Only fetched when the town actually has keyed shops, so a plain town stays byte-identical (no fetch). + if ((plan.shops || []).some((s) => s.godverseShopId)) { + try { + const r = await fetch('assets/stock_godverse/index.json', { cache: 'force-cache' }); + if (r.ok) { + const man = await r.json(); + const byId = new Map((man.shops || []).map((s) => [s.godverseShopId, s])); + for (const s of (plan.shops || [])) { + const e = byId.get(s.godverseShopId); + if (e && (e.types || []).includes(s.type)) preloadStockPack(s.type, { base: godverseBaseFor(s) }); + } + } + } catch (e) { /* no manifest ⇒ no per-shop packs ⇒ parody. The ladder's bottom rung, unchanged. */ } } } // [Lane F R8 — Lane C buy loop v0] ONE session wallet (seeded start cash) bound to the dig BUY across shops. diff --git a/web/js/world/interior_mode.js b/web/js/world/interior_mode.js index 5e49c06..a41cd53 100644 --- a/web/js/world/interior_mode.js +++ b/web/js/world/interior_mode.js @@ -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, preloadStockPack } from '../interiors/interiors.js'; +import { buildInterior, getStockPack, makeStockAdapter } 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) @@ -206,8 +206,13 @@ export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null // 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. + // [R26] NO probe here. R24 preloaded this shop's pack on entry — which meant walking into a keyed + // shop whose type has no atlas fired a fetch that could only 404. The shell now preloads every + // (shop, type) the manifest actually lists, at boot, so by the time a door opens the pack is either + // resolved or was never going to exist; asking again at the door can only ask for the absent one. + // F's own gate caught this leftover the moment the 404 tolerance came out — which is the argument + // for taking tolerances out. Lookup only: a shop with no pack gets null → parody, silently. 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