Compare commits

...

93 Commits

Author SHA1 Message Date
type-two
3ef751e1e3 Fable (integrator): THE TOUR — day one to first gem, 6 frames, no staging
The v7.0 charter's money shot, driven through the REAL loop on the pinned seed: arrive hunting
(the splash mission) → the strip → the shop (rumor overhead) → THE GEM (THE VERANDAHS 'Tinny',
asking $19 against ★ RARE guide $60+ — with onboarding beat ③ visible below, walking its talk)
→ the crate → dawn day 2: '⚖ SOLD Tinny — hammer $89, $78 after fees'. Bought $19, hammered
$89: the loop, told in six frames. docs/shots/v7_tour/ + sequence README. What remains for the
v7.0 tag is deliberately NOT mine: docs freeze + John's playtest (hands before tags).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 20:18:13 +10:00
type-two
25cef5983e Fable (integrator): ROUND 35 CLOSED — the first five minutes land; every v7.0 SYSTEM is built
qa.sh --strict --matrix: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10 towns green. New
STRICT smoke_first_five: the fresh game hunts a roster artist and the splash names hunt + cash
· the beats walked the REAL paths (② at the crate, ③ on the buy, the send-off on the ⚖) · a
returning save gets no tutorial · a second fresh browser hunts the SAME artist (seeded, not
random) · ?game=0 shows nothing · 0 console errors. THE CROSS-ROUND CATCH: the seeded want
moved a fresh-boot invariant (wants 1, not 0) and the R33 gate went red on the FULL run while
green solo — amended to a baseline snapshot, lesson filed in §35 (a feature that moves a boot
invariant must sweep every gate that assumed it; full runs exist for exactly this). Money shot:
docs/shots/v7_beta/first_five_splash.png. Remaining for v7.0: the tour + docs freeze + THE TAG
— which waits for John's hands (the alpha precedent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 20:13:02 +10:00
type-two
55b984e0b0 Lane C+B R35 (v7.0 close #2): THE FIRST FIVE MINUTES — arrive hunting, three beats walk the hustle
A FRESH game (day 1 · empty crate · empty auction house) seeds ONE artist want off
rng(citySeed,'firstwant',0) × the parody roster (dig.js ARTISTS exported) — the splash names
the mission before the first step ('You're hunting HILLS HOIST — $191 in your pocket'), and
the keeper rumors point at the same hunt from shop one. Three toast-beats ride EXISTING events
(zero new systems): ① find a record shop (on the go-click) → ② riffle · mind the guide · BUY
(first digOpen) → ③ sell at a counter or ⚖ LIST and sleep (first find, via wallet.onChange) →
the send-off on the first sale-or-listing (the listener removes itself). A returning save gets
its town back, not a tutorial (fresh-detection = state, no save flag — the delta law stays
clean); ?game=0 / ?classic=1 never reach the block. #pc-toast body-mounted (the #pc-game rule):
beats fire INSIDE shops, and a SLEEP taken at a counter now shows its morning paper.
2026-07-20 20:13:02 +10:00
type-two
c9e233a7e5 Fable (integrator): ROUND 34 CLOSED — GODBAY hammers, all green; v7.0 needs only the first five minutes
qa.sh --strict --matrix: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10 towns green. New
STRICT smoke_godbay: consigned through the real crate DOM · ONE-HAMMER determinism (a frozen
pre-sleep export slept twice lands byte-identical — the seeded gavel, measured not trusted) ·
honest bounded resolution (net $19 ≤ 2×$24×0.88) · the wake toast carried the morning paper ·
the ride to melbourne hammered a listing on arrival · ?game=0 has no crate to list from · 0
console errors. Money shots in docs/shots/v7_beta/ (the dawn paper + the ⚖ crate). The full
v7 loop now closes end to end: dig the gem → keeper cash now or the gavel tomorrow → the
wantlist points at another town → the ride costs a day and hammers your listings on arrival.
Remaining for the v7.0 tag: THE FIRST FIVE MINUTES (onboarding), the tour, docs freeze — and
John's hands on the whole thing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:32:06 +10:00
type-two
e528c92fed Lane B R34: ⚖ LIST in the crate · AT AUCTION section · the morning paper rides the toasts
hud.js: every crate find row gains ⚖ (mail-in consignment — very 1996); AT AUCTION section
(non-empty only) shows what hammers tomorrow; the rebuild watcher now folds listings in.
index.html: the wake toast and the travel-arrival toast carry the drained auction news —
'🌅 day 2 · ⚖ SOLD "Cane Toad Blues" — hammer $22, $19 after fees' is the come-back-tomorrow
hook paying off at dawn.
2026-07-20 19:32:06 +10:00
type-two
b048f51640 Lane F R34 (v7.0 close #1): GODBAY — the offline seeded auction, one hammer per item, ever
godbayHammer(entry, townKey): THE ONE-HAMMER LAW — seeded by ITEM IDENTITY
(godbay:<townKey>:<sku|slotId>:<dayFound>:<pricePaid>), NEVER by resolution day: a passed-in
item re-lists to the same result forever, so there is no retry farm anywhere in the economy
(the keeper's offer is a formula, the auction is one hammer per item per life). Fixed 4-draw
stream. Rolls: 15% passed in (comes home free) · hammer uniform across the item's band
(grail's open top = 2×low; unbanded alpha finds auction around pricePaid — honest, never
invented) · 12% bidding war ×1.5–2.0 · 12% commission, net floor $1. EV ≈ 0.8× band midpoint
— UNDER the asking-price EV of in-band stock: random flipping bleeds, only FINDS profit
(charter law #2 at the auction house); the keeper's half-of-low stays the instant floor, the
gavel the bounded ceiling (2×high×0.88). Band table imported from C's bible.js and PRNG from
core/prng.js — one authority each, no mirrors.
listings: optional on procity-save/1 (entry shape + listDay ≥ 1, the pulls/wants back-compat
law). listFind (identity — the entry LEAVES the crate, it cannot also sell at a counter) ·
resolveAuctions() runs in adopt() AND sleep(), so sleep, travel, and day-moving imports all
hammer what they pass · takeAuctionNews() drains the transient morning paper (NOT saved — the
ledger is cash/collection; the paper is a toast).
2026-07-20 19:32:06 +10:00
type-two
776b10b599 Fable (integrator): ROUND 33 CLOSED — THE HUNT lands, all green; the beta's last system is in
qa.sh --strict --matrix on the final tree: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10
towns green. New STRICT smoke_wantlist_travel: ☆→★ through the real card (the save carried
{THE VERANDAHS — 'Servo at Midnight'}) · a keeper muttered the hunted artist within 2 shops
('saw one in Castlemaine just last week') · buying the wanted sleeve cleared the hunt · the
synthetic→melbourne ride cost EXACTLY one day with $179 and the find riding along · the
same-town reboot was free (the control discriminates) · the ride home charged again (day 3) ·
?game=0 built zero want DOM and muttered nothing · 0 console errors. Matrix note: town_matrix
shares one browser across towns so travel drifts the day as it walks — safe because
determinism double-boots the SAME town (free by the same-town rule); reasoned before landing.
Money shots: docs/shots/v7_beta/rumor_and_wantlist.png (the keeper's mutter over the toy
shop's 70s wallpaper + THE WANTLIST open in the crate) + want_card.png. README manual row.
v7.0-beta systems are now ALL BUILT — what remains of v7: Esc-in-person (John's hands), then
the v7.0 close (GodBay offline auctions, THE FIRST FIVE MINUTES, the tour, the tag).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:01:40 +10:00
type-two
7b26e45fa0 Lane B R33: THE WANTLIST in the crate panel · the arrival toast · the build-race fix the shot found
hud.js: THE WANTLIST section in the crate (non-empty only — no header over nothing): hunted
rows, ✕ stops hunting, refresh watches wants length so an open panel follows adds/clears.
THE FIX: R32's game surface BUILT only in street update() frames — a door entered fast after
boot had no crate/bar inside at all, and the interior tickGame() refreshed a UI that never
existed. Every gate idles on the street long enough to build, so green gates missed it; the
R33 money-shot script entered faster and exposed it. tickGame() now builds-or-refreshes in
ANY mode (shots are a detector, not decoration — filed in LANE_F_NOTES §33).
index.html: game.traveled at boot → '🚌 <town> — the ride cost a day. Day N' toast (save.js
already charged the fare and re-keyed the week before the shell read game.day).
2026-07-20 19:01:40 +10:00
type-two
37773bc81d Lane F R33 (v7.0-beta wave 2): wants on the save · the keeper's rumor · TRAVEL COSTS A DAY
save.js: optional wants:[{artist?|title?|sku?}] on procity-save/1 (validated exact-and-loud,
≥1 string field per entry, cap 40, absent-when-empty — the same back-compat law as pulls).
recordWant (case-insensitive dedupe) / findWant (a want covers an item when every field the
WANT names matches) / removeWant (identity). TRAVEL in adopt(): the adopted save's town !==
this boot's townKey (town OR seed — a new seed is a new town) ⇒ day += 1, traveled = true,
prunePulls — load AND import both pay the fare; same-town adoption is FREE, so reloads and
export→import round trips keep the save-determinism gate's byte equality untouched. The 23
towns are a map you spend days crossing now, not a dropdown.
interior_mode.js: onWant/isWanted seams into dig.open; buying a matching item clears its want
('✓ off the wantlist'). THE RUMOR (charter #6 — the keeper finally gossips): seeded
xmur3('rumor:citySeed:shopId:day') → ~45% of shop entries mutter a hunted want + a real
cached town in the banner. Runtime-only stream, freshly keyed — A's rotation boundary holds;
no game / empty wantlist ⇒ the line never exists. Full persona lines stay D's for the v6
clip epoch; the banner is the keeper's voice until then.
2026-07-20 19:01:40 +10:00
type-two
ce71908bb5 Lane C R33 (v7.0-beta wave 2): ☆ WANT IT on the pull card
The wantlist grows from the card, not a search box the 90s didn't have: you pulled it, it's
dear, you'll hunt one cheaper. ☆ WANT IT → ★ ON THE WANTLIST (a stamp once wanted, not a
button). Construction-gated on opts.onWant/isWanted — ?game=0 / classic cards carry ZERO
wantlist DOM, the classic-pure pattern.
2026-07-20 19:01:40 +10:00
type-two
9aae167d64 Fable (integrator): ROUND 32 CLOSED — v7.0-beta wave 1 all green; smoke_bible + the routing amendment
qa.sh --strict --matrix on the final tree: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10
towns green. New STRICT gate smoke_bible: band law over EVERY bin off the digOffers surface
(80 offers/5 bins), the buy-everything ledger runs on THE GEM'S BIN so the finder-margin leg is
non-vacuous (bin1: 2 gems, grail asking $19 vs keeper $30; ledger closes to the cent — Σask
$333 → Σoffer $135, gems alone +$21, every non-gem strictly negative), scarcity measured
(emptied bin 0/16 on re-open AND across a full reload; 16 pulls in the save) with the ?game=0
negative control re-opening 16/16 (the detector discriminates). sell_routing amended from the
alpha inequality (credited < paid) to the exact formula (credited == min(basis−1, basis//2),
basis = bandLow-or-pricePaid) — the fence moved from the receipt to the band, which is what
charter law #2 always said; it passed on a banded standard item (+$4 on basis $8). _at_bin
grows an idx param (back-compatible). Money shots in docs/shots/v7_beta/ (the guide card + the
crate at the counter). LANE_F_NOTES §32 (incl. the JING5 fresh-checkout trap: gitignored
web/assets/models synced from m3ultra — also un-warned the validator's 15 cross-pack checks).
README manual: the guide/gem/scarcity rows. Remaining beta charter queued in the round doc:
wantlist + rumors · travel costs a day · Esc-in-person · then GodBay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 18:27:19 +10:00
type-two
bb30767b62 Lane B R32 (§31.6 queue 1–3): the crate lives in shops · cluster-pose spawn · dawn reads dawn
hud.js: #pc-game rides document.body (position:fixed, z 30) instead of the mode-hidden street
root — day/cash/CRATE/SLEEP are YOURS inside a shop too (the playtest's 'crate at the counter').
C toggles the crate ANYWHERE; the surface yields to the dig/sell cards and the map (they own
the screen). tickGame() exported so the interior/map frame branches keep it live (buys update
the count while you shop); dispose removes the body-mounted wrap.
index.html: the frozen spawn (-96,9,east) was tuned for the synthetic fixture — on real towns
it faced dirt (§31.6-1). Non-classic boots now spawn ON the main-street footpath door-line
(block kind 'mainstreet', both generators; every-door fallback) at the door nearest the shop
cluster's heart, facing the LONGER run of shopfronts down the strip's dominant axis. Pure plan
math, zero draws — no stream moves; ?classic=1 keeps the frozen pose forever.
lighting.js: dawnSky opt (shell passes high-cirrus, null under classic) — the DAWN segment
swaps the dome off the golden-SUNSET default that read dusk-orange (§31.6-3), and swaps back.
setSky() is now an explicit override: weather's rain/overcast dome always wins over base+dawn.
2026-07-20 18:26:56 +10:00
type-two
75e5e06288 Lane F R32 (v7.0-beta wave 1): per-day scarcity — pulls ride procity-save/1, the bin remembers
save.js: optional pulls: string[] on the SAME schema (validated exact-and-loud when present;
absent on every alpha save — old savers drop it silently, never reject; payload omits it when
empty so the alpha byte shape survives). recordPull/pullsFor seams; pruned on sleep/import —
day-tagged keys from other days are unreachable by construction (the consumer embeds the
current day), untagged keys are real-sourced and KEPT (sold means gone, tier-2 spirit at
tier 1). Cap 600 FIFO (delta law: saves stay tiny). Collection entries gain optional band
(recordFind stamps dig + shelf buys) — C's §9.1 sell basis reads it.
interior_mode.js: pull keys ${shopId}|${binKey}${pullTag}#${slot} — pullTag @d<game.day> for
rotating crates INCLUDING day 1 (the SEED stays untagged per the day-1 convention; the ledger
doesn't — a day-1 pull must not survive into day 2's identical-looking untagged key space),
'' for real-sourced (their pulls never expire). The seed input to binSeed is UNTOUCHED — A's
rotation boundary holds. Selling an item does NOT restock the bin (pulls are independent of the
collection — the leak the §30.6 filing predicted). digOffers getter = the honest-gate surface
(index/ask/band/sku) so the bible gate asserts identity, not looks.
2026-07-20 18:26:36 +10:00
type-two
f9517f14ab Lane C R32 (v7.0-beta wave 1): THE BIBLE — bands derived, gems seeded, the cards get the guide stamp
bible.js: band ranges MIRRORED from E's pipeline thresholds (record 8/25/60 · book 5/15/40 ·
toy 6/20/50) — derived in JS off the price_band labels the packs already carry, NO pack re-emit
(charter risk #3 never opens). guideText renders a RANGE ('guide $8–24' / '$60+'), never a
verdict — the card never says gem; spotting asking-under-guide is the player's skill.
dig.js: parody sleeves roll seeded bands (25/45/22/8) + a 10% gem on collector/grail only,
asking strictly under the keeper's own bandLow offer (collector $2–9 vs $12 · grail $5–20 vs
$30) — margin by construction; fixed 9-draw stream per item; rare ≡ grail (matches real packs).
Real offers carry band off price_band — and real asking always sits INSIDE its band (the band
derives FROM the price), so with the §9.1 basis swap every real round trip stays strictly
negative: the no-pump gate holds untouched, structurally. open({gone}) omits pulled slots AFTER
the draw (R27 pick-then-omit, zero collateral; o.i survives filtering — identity preserved).
currentOffers() honest surface for F's gate. sell.js: the §9.1 basis swap EXECUTED exactly as
pre-ratified — sellOffer(sellBasis(entry)) = min(bandLow−1, max(1, floor(bandLow·0.5))) when
banded; alpha-era entries (no band) keep the pricePaid basis and sell exactly as before. The
sell card carries the guide line too.
2026-07-20 18:26:15 +10:00
m3ultra
046668e5af feat(gig): club dancers play real dance clips, not procedural sway
loadPedFleet gains dance:!CLASSIC (mirrors the sit/look gate) → fleet.danceClips
= 4 mesh-free mixamorig club dances (party/medium/drink/sway). _make picks one
per dancer off a fresh gigdance seeded stream (zero impact on existing draws /
the same-seed-same-crowd covenant), spawnRig plays it, and update() drops the
fake bounce for real-clip dancers (procedural sway stays as the classic/noassets
fallback). ?classic fetches nothing, byte-identical.

Verified: default boot fetches all 4 dance GLBs (gated load runs), both modules
parse + export clean, page boots. Same clips + _canon/_rotOnly/spawnRig proven
animating in 90sDJsim (its crowd/rigs.js is this file ported).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 02:11:55 +10:00
m3ultra
b3394027be feat(citizens): +5 trellis DJ bodies in the ped roster
Same 5 Mixamo-rigged DJ characters as 90sDJsim, converted FBX->skinned GLB +
decimated to 4k tris (<=5k ped law) via MESHGOD scripts/fbx_to_ped.py — mixamorig:
skeleton + skin preserved. Added to PED_NAMES (rigs.js). Shares the proven bind:
identical _canon/_rotOnly + the same GLBs verified animating in 90sDJsim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 01:37:10 +10:00
m3ultra
3c5d0a1e95 Fable: THE FRONTIER gains THE VERNACULAR KIT — John's building-variety idea, source corrected (OSM/Overture footprints + parametric archetypes, never Google 3D)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:43:57 +10:00
m3ultra
67913f8b66 Fable: THE FRONTIER gains THE THIRD DIMENSION + TEXTURE LIFE — the noise ruling (semantic streams stay; noise only where continuous), terrain chartered v8+ with costs named
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:40:53 +10:00
m3ultra
bb5f36f1e0 Fable (integrator): ROUND 31 CLOSED — v7.0-alpha tagged and pushed; the playtest found what the gates couldn't
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:42:36 +10:00
m3ultra
2491f1c8ec Lane F R31 wave 2 (v7.0-alpha close): THE SHOT + the ledger. docs/shots/v7_alpha/crate_panel.png — the alpha's face: Monster Robot Party's real frontage, THE CRATE open with 3 real finds (real cover thumbs, paid/where/day honest), game bar day 1 · $116 · CRATE 3 · SLEEP, driven through the REAL loop with the dig param ABSENT (the R31 flip carries the shot), 0 console errors. LANE_F_NOTES §31 (the flip both ways, the two new gate legs + their negative control, harness lesson #4 — the explicit-flag blindfold, THE BETA QUEUE filed) + F-progress R31. Tag v7.0-alpha rides this commit, LOCAL ONLY — Fable reviews and pushes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:38:07 +10:00
m3ultra
50385c0f08 Lane F R31 wave 1 (v7.0-alpha close): THE DIG FLIP — the alpha-blocker Fable's playtest found. DIG_ON = flagOn('dig') (default-ON, ?dig=0 opt-out, ?classic=1 off — the v3.1 pattern; the v2-era opt-in survived 6 epochs past the README's 'dig on' claim because every R30 gate booted dig=1 explicitly: the explicit-flag blindfold). Fenced forever: default_boot_gate asserts dig REACHABILITY on a NAKED boot (E at a bin → digActive true), classic_regression asserts dig INERT at the bin (zero pcdg DOM, measured not assumed). PROCITY.flags gains dig. Splash de-staled ('Every door opens.' — no more '(soon)'; controls line carries E riffle/sell · C crate · SLEEP to save) + README manual row (sell at counter · C crate · SLEEP = save/advance/rotate). PROCITY_QA_PORT env override (flags_check, interior_scale_check, town_matrix) so the suite runs port-isolated instead of silently reusing whatever holds :8130. Verified both ways on :8951 (naked boot digs + closes clean; dig=0 and classic zero dig DOM, classic ZERO storage calls; noassets digs parody, 0 errors). qa.sh --strict --matrix on :8931: 7/0/0/0, flags_check 0 fails 0 warns, MATRIX 10 towns green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:37:02 +10:00
m3ultra
d96cbde004 Fable (integrator): round-31 instructions — the alpha close (wave 0: Fable plays it; then micro-fixes; then the tag)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:58:32 +10:00
m3ultra
7a7da29132 Fable (integrator): ROUND 30 CLOSED — v7.0-alpha wave 1 complete, all green, 7/0/0; the game remembers, spends, and cannot cheat
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:11:54 +10:00
m3ultra
2c9cb94b8e Lane F R30 wave 3 (v7.0-alpha ledger #5 + §9.4): THE GATES + the sell routing — E chain aimed-bin→dig · counter+sellable→C's sell card (removeFind-first onSell, veto-safe) · forgiving-bin · shelf; aim-priority amendment measured both ways (counter-adjacent bin sells, wrong-aim bin still digs); 3 shell seams (sellOpen unlock, sellActive guards). Five STRICT suite gates in flags_check: routing live-loop ($9 dig → +$4 sell → HUD $186·CRATE 0), save/load determinism (byte-equal state+export, plan fp unchanged, weekNight re-key), no-pump 5/5 strictly negative (Σ−$30) + C's negative control (wallet.buy invoked-and-false ⇒ recordFind unreachable, positive arm discriminates), classic purity (Storage-prototype instrument, 0 calls classic/game=0, positive control sees procity-save, plan fp game-on==off), rotation over VERTEX DATA (day 1/2/3 distinct, re-entry + cross-boot byte-identical, game=0==day1). qa.sh --strict --matrix: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10 towns green. NO TAG per brief.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:10:08 +10:00
m3ultra
5e115688ed Lane A R30 (v7.0-alpha ledger #6): THE ROTATION BOUNDARY VERIFY — HOLDS. F's day salt (207bcff) rides runtime streams only: stk-* stock-pick labels, dig binSeed, setWeekNight night-SELECTION over the plan-time week. Measured 3 ways: grep-zero over the proven citygen/** glob (8 files/2546 lines, pattern non-vacuous vs interior_mode.js); generatePlanFor consumes no day-shaped opt at any call site; day-inertness byte-measured (plan hash identical day-absent/1/37, 3 seeds x synthetic+osm, 6/6). Selfcheck ALL GREEN 156352/156352, fingerprint 0x5f76e76 frozen, all town pins green, zero re-pins.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 14:42:54 +10:00
m3ultra
cb03f07fb2 Lane B R30 (v7.0-alpha ledger #4): THE COLLECTION UI + SLEEP surface — the crate panel in hud.js (cover thumb = pack-atlas CSS crop off the sku, per-shop/town-wide bases, parody = glyph by design; where+day honest, foreign towns fall back to townKey), game bar day/$/CRATE count + SLEEP on F's §30.1 contract; construction-gated on PROCITY.game itself (classic/game=0 ⇒ zero DOM/listeners, measured); venue.js compat venueShopId = live weekNight getter (F's §30.4 filing, 116→490 across a sleep). Verified vs F's REAL committed core (nothing stubbed) on no-store :8151 snapshot; qa 6/6 2026-07-18 14:37:15 +10:00
m3ultra
f419c1a5e2 Fable (integrator): R30 wave-1/2 review — amendments ratified, the cross-lane 6 lines adjudicated, wave-3 inputs bound
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 14:29:39 +10:00
m3ultra
7b7227af73 Lane F R30 close-out: §30.5 results (7 verify legs, qa 6/6, the two harness bugs recorded) + F-progress — ledger #5 gates + sell routing held for the later F session per the brief 2026-07-18 14:27:44 +10:00
m3ultra
207bcffb54 Lane F R30 (v7.0-alpha ledgers #1+#3): THE SAVE CORE + SLEEP=SAVE=TOMORROW — save.js (procity-save/1, delta law by shape, loud reject + stash, beforeunload), game-backed wallet facade on the proven seam, buy seams record the collection (type/sku|slotId/title, C's §9 asks), day-salted stock rotation (stk-* seam in interiors/layout [marked], dig binSeed; day 1 = pre-v7, REAL never rotates), gig weekNight = (day-1)%7 (amended from day%7 with the measurement, §30.3), wake at DAWN. classic/game=0: layer never constructed, ZERO storage touches measured. Verified 7 legs green in fresh contexts; qa.sh --strict 6/6, 0 warn 2026-07-18 14:26:35 +10:00
m3ultra
369a2be23e Lane C R30 (v7.0-alpha ledger #2, implementation): sell.js — the sell counter lands. E at the bench → the mirrored price sticker; offer strictly below pricePaid by construction (8/8 scripted round trips lose money); type=type fail-closed (untyped item sells nowhere); wallet.sell = the credit mirror; game layer absent → no card, zero errors. Verified on :8144 no-store, stubbed game shape (F's core not landed); soak green avg 3.4ms · leak 0/0 · draws card-open ≡ closed 2026-07-18 14:14:14 +10:00
m3ultra
53fcd3ca1b Lane F R30 (v7.0-alpha ledger #1, THE CONTRACT FIRST): LANE_F_NOTES §30 — window.PROCITY.game published ({day,cash,collection,townKey,save,load,sleep,export,import,wallet,recordFind,removeFind}), procity-save/1 schema (the delta law by shape), the day-salt rotation law (day 1 = pre-v7, real never rotates, gig night = day%7), C's §9 asks honoured (type/title/artist + removeFind) 2026-07-18 14:09:09 +10:00
m3ultra
c8503a5a2e Lane C R30 (v7.0-alpha ledger #2, contract first): LANE_C_PUB §9 — the sell counter. Offer = min(p−1, max(1, floor(p·0.5))): the no-pump law structural, not tuned; type=type strict, fail-closed on untyped items; onSell mirrors onBuy (the proven wallet seam); collection read-only, removal via the game API 2026-07-18 14:04:59 +10:00
m3ultra
83ef8c9234 Fable (integrator): V7 RATIFIED + round-30 instructions (the save and the crate, wave 1) + README manual (measured controls)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:27:03 +10:00
m3ultra
e2345b544d Fable (integrator): V7 THE GAME draft charter — persistence, the collection, the gem thrill, GodBay; every system on existing rails
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 23:05:41 +10:00
m3ultra
bebaa66196 Fable: THE FRONTIER — the honest map of what is NOT figured out (John's challenge, answered in writing)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 22:58:32 +10:00
m3ultra
fe00c11d2c Fable: digalot.fyi/procity private-test deploy script — noindex/no-listing, _raw excluded, licence flag stays armed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 21:36:18 +10:00
m3ultra
a80ad55f47 Lane F R29 wave 0: the pose gate gains its subject — v6's first clip is in the game
R28's gate had no subject and SKIPPED with its reason printed (look.glb was published, verified,
sha1-proven off the depot — and nothing bound it). D bound it in wave 0. The gate is now
permanent, and flags_check is GREEN:

  ✓ default boot: look.glb BOUND — 6.5s, 64 tracks
  ✓ the glance REACHES THE GAME — peak 46 of 396 peds glancing · 0 console errors
  ✓ ?classic=1: look.glb NEVER FETCHED — the zero-fetch-delta covenant holds
  ✓ ?classic=1: glance inert at the source — 0 of 269 peds, AND the rng stream still draws

That last line is the design, and it is D's cleverness: THE DETERMINISM IS IN THE DRAW, NOT THE
OUTCOME. D takes from a dedicated glanceRng stream always — so the seeded sequence is identical
whether or not a clip exists — and only flips the flag when fleet.lookClip is real. Under
?classic the roll still happens, the flag never sets, nothing is fetched, the covenant stays
byte-identical. A behaviour added without touching a hash.

The gate asserts the clip's ABSENCE under classic, not merely "0 glancing" — because "0
glancing" alone passes on a fleet that failed to load at all. Present-on-default + absent-under-
classic is the pair a broken fleet cannot satisfy.

F'S OWN GATE CRASHED THE SUITE FIRST: the subject check used pg.goto() on a .glb, which starts a
download, aborts the navigation, and took flags_check down before one assertion ran. A gate that
DIES looking for its subject is worse than one that skips — it reports nothing at all and takes
the suite with it. Now it reads the disk. The vacuous-gate law has a sibling: a gate must survive
its own subject being absent.

No tag — wave 0 rides Spike 2's close, gated on John's footage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:15:20 +10:00
m3ultra
10025d4082 Lane D R29 (v6 Spike 1): look.glb bound and wired — peds glance, feet planted (wave 0)
The spike reaches the game. E's look.glb (mesh-free, 65 joints, 6.5s 'Look') is bound
through the fleet gate; street peds now glance around during window-shop stops. `lean`
is absent and never existed (E's R28 measurement). Goldens untouched — clips sit outside
every plan hash; the synthetic anchor stays R27's amended 0x5f76e76.

WIRED: loadPedFleet(base, {sit, look}) -> fleet.lookClip, the exact twin of the R16 sit
gate; the shell passes look:!CLASSIC so ?classic fetches NOTHING (a raw fetch would have
breached the zero-fetch-delta covenant — E flagged it, the gate is the answer).
makeActor gains lookA, played only on setLooking(true) — crossfaded (0.3s), not snapped
like sit, since idle->idle-variant reads naturally and there's no replant for a fade to
fight. sim.js gains a dedicated `glance` rng stream, rolled ALWAYS (deterministic) but
only flipped when a clip exists => classic inert at the source. Mutually exclusive with
bench-sit (sit rolls first at 35%, glance takes 40% of the rest). RigPool threads
lookClip; release snaps the glance off so no rig is pooled mid-look.

THE FINDING — my own prediction was wrong, and the measurement caught it. I wrote that a
STANDING clip needs no foot replant ("no hip descent to recover — that trap was sit's
alone"). Measured over the full loop: idle keeps the lowest bone within 4cm of the ground
(-0.04..0.00); look.glb ranges -0.009..+0.205 — the ped HOVERS up to 20.5cm, drifting
through the loop. Same trap, different coat: _rotOnly drops the Hips POSITION track for
rig-height independence, so the clip's authored weight-shift dip can't move the hips and
comes out as the FEET RISING. Sit's one-time replant can't fix it — the float varies per
frame.

FIX: plantFeet() — the per-frame twin of sit's replant. Reset to bind plant -> measure
posed feet -> drop inner by float/scale. ABSOLUTE (reset->measure->correct), never
incremental, so it cannot drift. Reproduces the authored motion exactly: the hips dip
relative to the ground instead of the feet leaving it. Scale-aware like sit's. Foot bones
cached once; cost paid only by glancing peds, on frames their mixer ran (both mixer sites
— the drummer's post-mix lean taught that coupling rule).

PROOFS: foot float 0.205m -> 0.000m (pinned); head yaw sweep span 1.735 (-0.85..+0.89) so
the glance still animates; ?classic — no look fetch, no Look action on any rig, glance
never flips (0); identity signature unchanged, no glance term; sit AND glance never
simultaneous (0); 0 NaN; goldens 0x5f76e76 untouched. A glancing ped is NOT tagged
seated/posed — standing pose at standing stature, so F's no-giants gate needs no arm.

D->F: the pose gate HAS ITS SUBJECT — un-SKIP it. Fresh ?localdepot=1 boot shows a ped
glancing on the footpath, head sweeping, feet planted.
Spike 2 readiness: plantFeet() is the general machinery the brief asked for ("data, not
new code") — riffle and wall-lean should land as clip + roll; the lean's wall-contact
placement is the only genuinely new part.
FLAGGED (A's file, A rests): selfcheck.js:301 still says "the synthetic golden above stays
0x3fa36874" while line 267 pins the amended 0x5f76e76. A stale comment, not a rule — but
the same species as the epoch's five holds, so: flagged, not touched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:44:07 +10:00
m3ultra
09dff6b788 Fable (integrator): round-29 instructions — the v6 spikes (wave 0 now, the rest gated on John's footage) + charter amended (lean doesn't exist)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:13:01 +10:00
m3ultra
cdce67c8f8 Lane F R28 (v5.1): the sweep gated and tagged — the spike never reached the game
qa.sh --strict --matrix 7 passed / 0 failed / 0 warn. selfcheck ALL GREEN 156,352/156,352 —
UNCHANGED, confirming the brief's prediction that nothing this round moves a golden. MATRIX
GREEN 10 towns x 7 gates.

F'S POSE GATE HAS NO SUBJECT — SKIPPED with the reason printed, per the vacuous-gate law. The
brief asked for "lean/look poses through the scale gate" and "leaning peds in old frames". There
are no new poses in the game:
  · `lean` DOES NOT EXIST. E measured instead of trusting the list — the stash's 34 clips have no
    lean, and find -iname "*lean*" across ultra returns nothing. Brief and kickoff both called it
    already-cached. The premise was half wrong, and E found it the only way these are ever found.
  · `look.glb` is shipped, verified, published, sha1-checked by fetching the bytes BACK off the
    public depot — AND NOTHING BINDS IT. loadPedFleet() loads walk, idle, sit. No look. E's
    handoff said it plainly ("-> D: gate it behind an opt the way rigs.js gates sit"); D's entire
    R28 commit is LANE_D_NOTES.md, 48 lines, docs only. The run order said "when E's clips land:
    D (#1 wiring)" — the log says D committed BEFORE E shipped.

Nobody was careless; the wave order was. Every check E ran was real and passed, and none of them
could see that no consumer exists. A GATE ON THE ARTIFACT CANNOT SEE THE WIRE — fifth appearance
of the R17 pattern this epoch and the cheapest yet to have missed, because the artifact was
perfect.

v5.1 SHIPS ANYWAY, AND DOES NOT CLAIM THE SPIKE. An unbound clip is inert: nothing is broken,
the game is unchanged and correct. This is not R18's "known breach", it is an unfinished item,
and holding a polish tag over an inert file would be precious. What ships is the sweep:
C's audioEmitter (its own v3.0 note had specified it wrong on both halves — "the panner was
inert"), and TWO backlog items KILLED WITH THE MEASUREMENT — B's awning ripple and D's
instrument LOD, the oldest tri item on the books. They don't leave the backlog "deferred"; they
leave it gone, with the number that killed them.

Filed for R29 (D, one opt): rigs.js gains `look` the way it gates `sit`; F passes it from the
shell. Then the pose gate has a subject and F runs it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:59:02 +10:00
m3ultra
488067004c Lane C R28: audioEmitter — the till rings from the counter; the panner was inert
R28 ledger #3. My own idea, parked since R7. Brief stated acceptance only, so I scoped it.

THE TRAP MY OWN v3.0 NOTE SET: it said "userData.audioEmitter = musicKey so Lane B can
position/spatialise the bed". Both halves wrong. (1) Shape: the engine holds no reference to
the room group and would traverse a scene it cannot reach — emitters belong on room.audio as
data, riding the EXISTING playInterior() call (no lifecycle, no traverse, no dispose hook).
(2) Method: "spatialise" invites a PannerNode. NOTHING in this tree sets ctx.listener (zero
hits), so a panner resolves against a listener frozen at (0,0,0) — which, because rooms are
built room-local at the origin, is the room's own CENTRE, forever. Measured, OfflineAudioContext,
real pub stage bed:
    panner, listener never set (the engine as it SHIPS): RMS 0.106905 door / 0.106905 stage
                                                          -> ratio 1.0000, bit-identical
    panner + listener tracking the player:                RMS 0.055665 / 0.500000 -> 8.98x
It plays, throws nothing, warns nothing, and never changes by one sample as you cross the room
— while failing at the only thing it exists to do. "Routed through a panner?" yes. "Audible in
a venue?" yes. "In a shop?" yes. All green, all worthless.
-> ASSERT THE DELTA (gain at door != gain at stage), NEVER THE PRESENCE. Vacuous-gate law, 4th
application — and this one had no author to blame but me.
The house idiom was already right: tram/spill/gig-spill are all hand-rolled distance-gains vs
PROCITY.camera.position. No listener, no panner.

SHIPPED (C's files only):
- room.audio.emitters {stage?, counter?} — room-local, 3dp, 18/18 same-seed deep-equal. `r`
  (reach) is the ROOM DIAGONAL, not a constant: measured diagonals 9.81-15.63m, and scaling to
  the room holds door->stage proximity in a 0.42-0.59 band from a pokey band_room to a hall pub.
  `floor` is the room-filling residue, never 0 — a PA fills a pub; what changes as you walk in
  is the BALANCE, not the presence. Swing 2.65-4.52 dB (John R20: subtle changes are great).
- THE TILL RINGS FROM THE COUNTER — audible positioning in a normal shop with NO ENGINE CHANGE,
  via B's already-public playSfx(key,{gain}): C does its own distance math, hands B a scalar.
  Measured through the real BUY button, real record shop: 0.9489 @0.60m vs 0.6436 @4.19m =
  3.37 dB; negative control (no emitters) fires at exactly 1.0. sfx-till.ogg has been on disk
  since 15 Jul and had NEVER ONCE PLAYED. Routing, not content — as the brief said.
- ?mute=1 now actually silences the crate riffle (below).

WHY THE VENUE HALF IS AN ASK: a one-shot resolves its gain once, at fire time, from a position C
already knows. A bed must be re-gained every frame inside the engine's update() loop — B's file.
Structural, not political. B has a ~12-line paste-ready diff in the house idiom; audio.js
untouched. The letter of "consulted not modified" could have been met by reaching through B's
exposed state.layers diag to rewire its graph from outside — that backdoor is strictly worse
than the edit it avoids. Refused.

TWO BUGS FOUND:
- F DROPS EMITTERS ON THE ONE NIGHT THEY MATTER. interior_mode.js:289 rebuilds the spec on a gig
  night ({musicKey: ra.gigKey, toneKey: WALLA_KEY}) and every unnamed field dies. Measured on a
  real gig-night pub: quietNight_emittersSurvive TRUE, gigNight_emittersSurvive FALSE. Silent,
  fail-soft, no error. -> F: don't just spread `ra`; pass emitters DELIBERATELY, gig-night only —
  on a quiet night the radio + room-tone genuinely ARE room-filling.
- ?mute=1 NEVER SILENCED THE RIFFLE (R5->R28). dig.js opened a SECOND, private AudioContext
  straight to destination, breaking both stated house laws (ONE AudioContext; mute forces
  silence). It survived three epochs because NO GATE ASSERTS MUTE => SILENCE: the engine returns
  its silent surface and looks honest from its own side, while the sound came from somewhere it
  never knew existed. A gate that asks the engine whether it is muted can only ever answer yes.
  Fixed via B's PUBLIC .muted getter (consulted, not modified), URL fallback when standalone.
  Measured with a positive control so the probe provably CAN see sound: live 1 ctx / 1 source;
  muted 0 / 0. Blips stay procedural on purpose — zero-fetch, deterministic, still work under
  ?noassets=1 where a sampled riffle cannot. Only mute was broken; only mute was fixed.
  -> F: assert SILENCE, not muted-state.

HARNESS HONESTY: the first till rig reported the same clean 3.37 dB — from a CONTAMINATED rig
(createDig appends a panel per call, close() doesn't remove it, so querySelector clicked trial
1's stale button every time). Tell: panelUp false on all three, and the NEGATIVE CONTROL
returning 0.6436 where it must return 1.0. The gains matched the formula exactly; I refused them
anyway and re-ran isolated (panelCount 1, panelUp true, control 1.0). Never report a number from
a rig you have watched lie — even when you like the number.

QA GREEN 6/0/0/0. Docs: LANE_C_AUDIO.md -> v3.1 (contract + both asks + every measurement).
2026-07-17 17:39:54 +10:00
m3ultra
640bdbb55d Lane E R28 (v5.1): Spike 1 — look.glb shipped; lean does not exist (ledger #1)
look.glb — DONE, published, verified. Looking_Around.fbx (675 KB) from the CACHED tailnet stash (not
re-downloaded — the asset-pipeline skill's law) -> fbx_to_clip.py -> web/models/peds/look.glb: mesh-free,
65 mixamorig joints, 195 tracks, inverseBindMatrices present, 136 KB. Structurally identical to the
sit.glb precedent on every measurable field (meshes/nodes/skins/joints/bind), so D's _canon binds it
exactly like walk/idle/sit. Depot: published to digalot.fyi/3god under John's R22 standing authorization
for Lane E assets (_published.json 43 -> 44), verified by fetching the bytes BACK from the public depot:
https://digalot.fyi/3god/a/look.glb -> HTTP 200, 139,640 bytes, sha1 identical to local.
-> D: it's ready. Gate it behind an opt the way rigs.js gates sit (loadPedFleet(base, { sit })) — a raw
fetch would breach the ?classic=1 zero-fetch-delta covenant, same as sit.glb.

`lean` DOES NOT EXIST — the spike's premise is half wrong. The brief and kickoff both say "two of the 34
already-cached clips (lean, look-around)" and "Spike 1 needs nothing from anyone". look-around is cached;
lean is not, anywhere. Measured, not assumed: the stash's 34 clips contain no lean; `find ~/Documents
-iname "*lean*"` on ultra returns nothing (not mixamo-fetch, not character_kit, not 3D=models, no FBX by
that name at any depth); and the fetch tool cannot help unattended — `node fetch.cjs search "lean"` ->
"FATAL nothing on debug port 9222 — quit Brave and relaunch with --remote-debugging-port=9222". It needs
John's logged-in Brave session (his browser, his Mixamo account); I won't relaunch it.
I did NOT substitute a lookalike. `Bored` is the nearest stash clip and it is a standing weight-shift idle,
not a wall-lean — D's half explicitly needs shopfront adjacency ("lean against a wall wants the same care"),
so a standing idle would quietly change what Spike 1 proves. Half a spike, honestly, beats a whole one that
lies. -> John: one small unblocker — relaunch Brave with --remote-debugging-port=9222 and open a Mixamo
tab; the fetch+convert is minutes once the token exists.

Instrument LOD — D killed it and D's measurement is better than mine. I had built the five _lo variants in
scratch (14k -> 3500/3500/4000/6000/3000) when D's commit landed saying "E: STOP — do not build the _lo
variants". Nothing shipped; scratch discarded. D measured the rooms an LOD must fire in: max diagonal 15.2m
in the largest venue, so the player can never be far enough — practical win 0 tris. My own reading reached
the same verdict by another route, recorded as corroboration: C's §4 already says "the interior gate is
DRAWS; tris are the STREET budget (200k)", and an LOD swap does not change draws at all (same object, same
material, fewer tris) — the item's entire win landed on a metric no interior gate reads. Two independent
measurements, one verdict. Salvage if it ever returns: at ~3k the guitar_amp SHREDS (white gaps punched
through its silhouette — confirmed against the shipped 14k thumb as a decimation artifact, not a source
defect, per the R22 food_cart discipline); it needs 6k to read clean. The other four were clean at 3-4k.

-> A: your own file's comment now contradicts its code. Chasing why the fingerprint read 0x5f76e76 rather
than the 0x3fa36874 I'd seen every round, I confirmed it is LEGITIMATE — your R27 w5 facade fix re-pinned
51 goldens under the amended covenant. But selfcheck.js:267 pins hash: 0x5f76e76 while selfcheck.js:301
still says "the synthetic golden above stays 0x3fa36874". Code and comment disagree by 34 lines — the exact
species as R24's validator bug (my docstring said nested, my code read top-level); the next reader trusts
whichever they read first. Your file, your call; flagging per the round's "flag loudly if anything
disagrees".

Gates: selfcheck ALL GREEN 156,352/156,352 · qa.sh --strict 6 passed / 0 failed. look.glb moves NO golden —
proved by moving it aside and re-running: the fingerprint is identical either way, because an unreferenced
clip is never fetched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:35:17 +10:00
m3ultra
999c9d653f Lane B R28 (v5.1): the awning fabric ripple — KILLED with the measurement
Ledger #2. Built it, proved it worked, measured what it was worth, killed it.
A 10x-over-driven billow — 1.4m of flap on a 0.14m canopy — cannot move 1% of
the frame. It is not the gain, it is the geometry. Nothing ships: buildings.js
and wind.js are byte-identical to HEAD.

IT WAS BUILT, AND IT WORKED. Split the stall canopies out of the rigid awning
InstancedMesh (own mesh, own subdivided box — a 4-corner box has nothing to
ripple), gave only them a cloth billow riding R17's wind.js uniforms: sin(pi u)*
sin(pi v) pin (zero at the four posts by construction), two crossed travelling
waves, per-instance phase so forty stalls don't breathe in unison. Proven by
rendered-pixel hashes, not by eye:
  cloth displaces (calm→gusty)        40f59eaf → d6d3e1f1  moves
  back to calm                        40f59eaf              identical
  uWindTime=99 at amp 0               40f59eaf              identical
  ?classic=1, pre- vs post-build      453c0154 → 453c0154   BYTE-IDENTICAL
  rest of town rides wind? (hidden)   76c8b518 → 76c8b518   still
  control: cloth back                 frame moves again
Cost at market_square, true A/B: +0 draws (98→98) / +2,160 tris. +0, not the +1
my own comment claimed — the square's chunks hold only stalls, so the rigid list
empties and makeInstanced returns null; the cloth mesh replaces it.

AND IT IS WORTH NOTHING (amp is the same multiplier as gain):
  0.02m  CALM, the default weather amp        0.05% of frame
  0.14m  STORM, the most a player ever sees   0.21%
  0.35m  2.5x over-driven                     0.33%
  0.70m  5x, absurd on a 0.14m slab           0.49%
  1.40m  10x, flapping a body-length          0.77%
At the player's actual browsing pose, storm-force: 0.00%, max delta 1. Not one
pixel.

THE REASON IS PHYSICAL — the same physics that killed the verandah in R17. A
canopy is a horizontal sheet at y2.2 seen by a 1.7m player: edge-on or from
underneath, so vertical displacement runs almost parallel to the view ray and
makes almost no pixels. You cannot see a flat horizontal sheet flex vertically
while standing under it. No gain fixes it; 10x buys 0.77% and stops reading as
cloth.

WHAT WOULD WORK (for whoever reads this in round 35): the motion must be
perpendicular to the view — VERTICAL cloth: hanging stall skirts, a flag, a
banner, an awning valance. That is an ASSET (Lane E), not a shader. B's
applyWind/applyFabricWind seam is ready the day such a mesh exists; the shader
was never the missing piece. Ledger #2 leaves the backlog forever.

TWO VACUOUS MEASUREMENTS CAUGHT ON THE WAY. (1) `!!material.onBeforeCompile` to
prove the rigid verandah untouched — every THREE.Material inherits a prototype
no-op, so it is ALWAYS truthy; the assert could never go red. Replaced with
hasOwnProperty + a behavioural hide-and-hash control. (2) A gain sweep reporting
an identical 0.21% at gains 0.4→4.0 because I never stored the shader ref, so
uFabricGain never moved — four identical numbers across a 10x range is a sweep
that proves nothing. The constancy, not the value, gave it away. Both would have
passed as green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:29:39 +10:00
m3ultra
afad68b114 Lane D R28 (v5.1): instrument LOD — KILLED with the measurement (ledger #4)
E: STOP — do not build the _lo variants. That work is unnecessary; the item is dead.

The oldest tri item on the books, killed under the wind-sway rule attached to the brief
("if the measured win is <20k tris in practice, KILL it with the measurement").

MEASURED, from the shipped depot GLBs: each of the five instruments is exactly 14,000
tris; band + amp on stage = 70,000. On paper a 3-4k _lo each "saves" ~52k, clearing the
20k bar easily.

But the rule says IN PRACTICE, and the brief named the reason — "interiors are
close-quarters". So I measured the rooms an LOD would have to fire in (live, all three
venue archetypes, interiorMode driven by hand since rAF is throttled):
  pub        9.7 x 9.7m, diagonal 13.7m, 12522 tris, 38 draws
  band_room  8.9 x 9.6m, diagonal 13.0m, 11699 tris, 35 draws
  rsl        12  x 9.4m, diagonal 15.2m, 15115 tris, 38 draws  (the biggest)

VERDICT: practical win = 0 tris. A player can NEVER be more than 15.2m from the band —
corner-to-corner in the largest venue, and the stage sits at one end so the real max is
shorter. An LOD is a distance trade; inside a <=15.2m room the band is never small — it
is the subject of the gig and of F's tour money shot. Any sane threshold (15-25m) never
fires; an aggressive 8m one fires in half a room at a distance where a 3-4k _lo is
visibly worse with no perceptual cover. The win isn't "small", it's STRUCTURALLY ZERO —
the distance an LOD needs does not exist in this game. Off the backlog forever.

No budget motivates it either: a venue interior is ~12-15k tris, ~85k with the band —
inside the 200k stress ceiling E's R22 tri diet restored, at 35-38 draws vs <=350.

SIDE-OBSERVATION for E (not a proposal, no breach motivates it): all five instruments
are EXACTLY 14,000 tris — a decimator target, not a measurement of the objects. A mic
stand does not need 14k. If instrument tris ever do drive a real breach, the lever is a
flat re-decimation by object complexity (mic stand -> ~1k), which beats any LOD because
it needs no distance. Filed so the next tri hunt starts from it.

Spike 1 (ledger #1) pending E's lean.glb — look.glb has landed, lean.glb not yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:21:34 +10:00
m3ultra
d9100962f6 Fable (integrator): round-28 instructions — THE SPIKE AND THE SWEEP (spike 1 + parked polish, tag v5.1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:07:52 +10:00
m3ultra
e93bece0f4 Fable (integrator): the v5 epoch retro — five holds, nine vacuous gates, the covenant amendment, the case law
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:04:41 +10:00
m3ultra
b2d29ba112 Lane F R27 (v5.0): the covenant re-pinned, the frame that took five tries, QA GREEN
qa.sh --strict --matrix: 7 passed / 0 failed / 0 warn. selfcheck ALL GREEN 156,352/156,352 ·
MATRIX GREEN 10 towns x 7 gates · scaffold_check GREEN (F's re-pins took) · flags harness green,
carrying the kill-the-server gate and the beta crate gate.

F RE-PINNED ITS OWN FILES per A's handoff (A doesn't edit other lanes' files, correctly):
GOLDEN_HASH 0x3fa36874 -> 0x5f76e76 · GIG_GOLDEN 0xb1d48ea1 -> 0xec7a2d39 · melbourne · katoomba
· the captions. AND A CAUGHT A LIVE HAZARD IN F'S GATE: hash_ok carried `or ('3fa36874' in
r.stdout)` — a bare-string OR-clause, dead the moment the covenant moved and worse than dead,
because it would go green off a SENTENCE ABOUT the old covenant appearing in stdout. Deleted.
The check derives from GOLDEN_HASH or it doesn't exist.

JOHN'S RULING IS THE EPOCH'S BEST LINE: the covenant is anti-drift machinery, not a defect
preservative. 0x3fa36874 did its job for five epochs — it never drifted. It just was never right,
and no hash can catch what was wrong in the first commit. A golden proves NOTHING CHANGED; it
cannot prove anything is CORRECT. Every gate we own was green over a world of back walls, and the
only thing that ever saw it was a human standing in the street looking at a wall.

THE FRONTAGE FRAME TOOK FIVE ATTEMPTS, FOUR OF THEM F'S FAULT:
  1-3. cluster pose (the town's heart isn't this shop) / lot-anchored facing along the edge /
       lot-normal facing — three photographs of empty road. Also never winnable: the shops faced
       backwards the whole time. F was posing a camera at a wall and tuning the geometry.
  4.   B's helper — and F THREW THE RESULT AWAY. poseForShopFront RETURNS a pose; it does not
       apply one. F called it, error-checked it, and never touched the camera, so the frame was
       the spawn paddock — and F's error-check PASSED, because the call succeeded perfectly. The
       vacuous check one last time, in the last frame of the epoch.
  5.   Applied it -> MONSTER ROBOT PARTY. Sign, door, awning, a ped walking in off Musgrave Road.

The measurement that would have found the facade bug in five minutes — compare the two
conventions against each other — is the one nobody ran for nine rounds, F included, right up to
the vacuous 72/72 F nearly filed as "B is wrong".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:48:22 +10:00
m3ultra
c1e862cf7c Lane A R27 wave 5: THE FACADE FIX — it was every town, not just the real ones (the covenant amended)
selfcheck ALL GREEN 156,352/156,352; consistency green. 51 goldens re-pinned in this one commit per the
amendment law. scaffold_check is RED and CANNOT be green from here — the covenant hashes are duplicated in
LANE F's files; exact values handed to F in LANE_A_NOTES §R27w5 (I don't edit other lanes' files).

THE RULING'S PREMISE WAS WRONG, AND THE WRONG HALF WAS LOAD-BEARING.
Wave 5 says "synthetic follows B's canon so every golden has been green over a wrong product since R18".
Half right. Synthetic does NOT follow B's canon -- it is broken identically, and so is the marched path,
and so are all three checked-in fixtures. It is not "every real town since R18". It is EVERY TOWN, EVERY
PATH, SINCE v1. The ruling told me to measure the marched path rather than assume it was clean "because
that's how this one survived nine rounds" -- so I measured all three, and the same discipline caught the
premise itself.

MEASURED FIRST, IN B'S CANON (+Z = the facade), BEFORE TOUCHING ANYTHING:
  synthetic seed 20261990   0 toward / 681 away   (dot = -1.0000, exactly anti-parallel)
  marched katoomba/fremantle/godverse, fixtures melbourne/katoomba/silverton   0 toward, all away
  real-roads katoomba   0/72   <- B's number, reproduced
637 of 681 synthetic lots had their facade pointing at NO STREET AT ALL (44 hit a different street by
corner coincidence). Then I confirmed it with my own eyes rather than trust the arithmetic: booted
?classic=1, stood on the synthetic main street at lot 0 -> three blank grey boxes. Walked BEHIND them into
the block -> NUMBAT PLAYTHINGS, sign/door/windows/awning, facing the dirt. The classic covenant town has
been a row of back walls since v1.
After the fix, same camera, same seed, same ?classic=1: CROWN TOYS & GAMES · MARBLES & KITES · NUMBAT
PLAYTHINGS -- a strip of shopfronts facing the road.

FOUR SITES, ONE SIGN, AND EVERY ONE OF THEM SAID SO IN ITS OWN COMMENT:
  plan.js:122    synthetic strip   "facade faces back down the outward normal, to the street"  <- it didn't
  plan.js:244    market row        "the facade then faces -(outward) = +x"                     <- it didn't
  plan_osm:330   marched avenue    "facade faces +z (north) to the avenue"                     <- due south
  plan_osm:443   real roads        B's proven one-liner (B measured it, then reverted it -- A's file)
All four were atan2(outward) where the intent was atan2(-outward). The code never did what its comment
said, and CITY_SPEC:71 blessed the result by writing the SIM convention into the LOTS contract. Nobody was
wrong; the spec was. After: synthetic 681/681, marched + fixtures 100%, real-roads katoomba 72/72.

THE COVENANT MOVED -- JOHN RULED IT, I DIDN'T.
Fixing synthetic necessarily moves 0x3fa36874, frozen "forever" since R16. That is not Lane A's call, so I
stopped and asked with the evidence. John's ruling: AMEND THE COVENANT, FIX ALL PATHS -- the covenant is
anti-drift machinery, not a defect preservative. 51 pins in this commit:
  classic/synthetic  0x3fa36874 -> 0x5f76e76
  gig                0xb1d48ea1 -> 0xec7a2d39
  fixtures           melbourne 0x9c7e76b3 · katoomba 0xf3aafec8 · silverton 0x6d74c4
  23 real towns      base + gig (46)
Verified byte-identical across two fresh processes; pack-xor 0xa5058322. CITY_SPEC records the amendment as
a ONE-TIME, ruling-gated precedent -- from this hash forward the law reads as it always did.

A SECOND BUG THE FIX EXPOSED (fixed here, counted). buildRealRoads seated blocks at KERB = 4 m from the
CENTRELINE, but a `main` carriageway is 10 m wide -- its kerb is at 5 m. EVERY MAIN-STREET BUILDING HAS
STOOD 1 m INSIDE THE ROAD SINCE R18. The footprint never moved; only which face is the front -- so it was
invisible until the shopfront turned around and its poster landed in the traffic lane (-1.07 m clearance,
6 real failures). Blocks now seat behind the kerb per A's own published corridor law (roadWidth/2 +
FOOTPATH). Pack drops 6.4% -> 6.6%, counted, not hidden.

THE CROSS-CONVENTION GATE -- built to F's lesson, not to a spec line. F proved that measuring lots against
CITY_SPEC:71's own convention is SELF-AGREEMENT: 72/72 "facing" over a town of blank walls. So this gate
restates nothing. It replicates LANE B'S OWN maths from buildings.js (toWorld(lot, 0, y, d/2 + 0.02)) and
asks a physical question neither spec line can fudge: does the quad the RENDERER actually draws land nearer
the street than the lot centre? It fires if A's ry sign drifts OR if B's zf sign drifts. It would have
caught this on day one of v1.

CITY_SPEC amendments: the lots contract now states facade/door/frontage = local +Z; the SIM/rig convention
is documented separately as the different thing it is, with the explicit rule -- never assert a lot's
facing against a spec line, assert it against the geometry the renderer draws.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:28:20 +10:00
m3ultra
94fce8ea4b Fable (integrator): R27 wave-5 — THE SPEC RULING (facade = +Z, B's canon canonical); A one-commit fix+pins+cross-convention gate; F re-gates and tags
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 16:01:31 +10:00
m3ultra
8a1d2e91e8 Lane F R27: HOLD v5.0 — every real town's shops face away from their street
B's micro-task found it while fixing F's frame, and F's three photographs of empty road are the
evidence: it was never the pose. plan_osm.js:443 aims the lot's -Z at the road ("facade (-Z)
faces road"); buildings.js:404 draws the facade on the local +Z face. Exactly opposite. The
shopfront — sign, door, R26 crate mark — lands on the far side and the street gets a blank wall.
Since R18. v4.0 shipped on it. katoomba_real's street_noon, the bookmark the tours have shot
since R9, is 46 draws of bare road.

Root cause: CITY_SPEC contradicts itself — :71 (lots contract) says -Z, :335 (R15 posters) says
"+Z, B's canon" — and each generator implements one half. Synthetic follows B's canon and looks
right, which is why every golden passes and nobody saw it for nine rounds. The epoch's signature
species and its worst instance: not a gate failing on correct behaviour, but a gate suite GREEN
over a product that is wrong.

F VERIFIED IT AND F'S FIRST CHECK WAS VACUOUS — recorded because it nearly ended the round the
wrong way. F measured each lot's ry against CITY_SPEC:71's own convention and got 35/35 and 72/72
FACING, a clean contradiction of B. But plan_osm IMPLEMENTS :71, so testing :71's data against
:71's rule is self-agreement — it cannot fail. A self-consistent check over a self-contradicting
spec passes under either convention, which is exactly why nine rounds of checks missed this, and
why F nearly filed "B is wrong" with a green number in hand. The only test that sees it compares
the two conventions. F's own frames already had.

Not F's to fix and B was right not to either: A's file, it moves every real-town plan (36 goldens
re-pin under the amendment law), and a self-contradicting spec is Fable's to rule, not a lane's
to pick a side of. B proved the one-liner (ry = atan2(-nx,-nz) → katoomba 0/72 → 72/72, F's money
shot 4m from Musgrave Road) and reverted it — the same discipline F applied to A's sweep and E's
validator, arriving from the other direction.

Order: Fable rules the spec → A lands the line + re-pins the goldens in ONE commit → F re-gates,
re-shoots, tags. Everything else is done and green: the kill-the-server gate, the reader, the
sandbox, the interior money shot (interiors pose themselves; the crate is real either way).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:47:26 +10:00
m3ultra
4c2702fc58 Lane B R27 w4: poseForShopFront — and the real towns face the wrong way
The micro-task, done and verified. In doing it I found why F photographed
empty road: it is not the pose.

1. THE DELIVERABLE — DBG.poseForShopFront(sel, dist = 8)
Poses at a NAMED shop's frontage: door centred, sign band + R26 crate mark in
shot, under the verandah. clusterPose() answers "where is the town's HEART",
which is the wrong question for a named shop — Red Hill's heart doesn't contain
Monster Robot, which is exactly how F got three frames of empty road.

Keyed through ONE shop selector: F's R22/R24 resolver lived inside enterShop(),
so I extracted it rather than grow a second key scheme beside it. A selector bug
resolves the wrong shop AND looks like it worked (F's own first cut proved it),
so the id-namespace law now has exactly one home.
  poseForShopFront(31)         → Wholefood Cafe (PLAN id) + ambiguous: "31 is
                                 also the godverseShopId of Silky Oak Furnature"
  poseForShopFront('g:31')     → Silky Oak Furnature (the prefix IS the fence)
  poseForShopFront('g:999999') → {error} — never throws, never guesses
Framing proven by projection, not by eye: doorCentreNdcX = 0.0000 exactly, door
+ sign + mark inside the frustum. Derived from buildings.js, not hardcoded: door
is centred on the lot front (off = 0, unlike poseForShop's off = 1.0); sign band
= min(h,FACADE_H)-0.35 ± 0.25 ⇒ y 2.40-2.90; mark = y 2.45-2.52; camY 2.0 sits
below AWNING_Y 2.95, so a ray rising to a target ≤2.9 can never cross it — the
sign is clear BY GEOMETRY at any distance.

2. CORRECTING MY OWN R27 INTEL — I gave F two wrong numbers. I said the mark
"rides high (y≈3.3)" and the vinyl blocks 4% of it. Both wrong: the mark is at
y 2.45-2.52, and I had scanned y 3.0-3.6 — empty awning air above the sign,
which is why it read ~0. Re-measured: the novelty_record disc (top y 2.50)
reaches INTO the mark band and clips ~15% of the strip from EVERY offset;
moving the camera only changes which 15%. "Shoot head-on" survives, but for the
opposite reason to the one I gave.

3. THE FINDING — every real town's shops face away from their street, since R18.
From Musgrave Road (Monster Robot's own frontEdge 1316) the shop is a blank grey
back wall: no sign, no door, no mark. Measured:
  redhill_godverse  35 shop lots: 0 facing, 35 backwards (dot = -1.000)
  katoomba_real     72 shop lots: 0 facing, 72 backwards (dot = -1.000)
  synthetic         correct — always has been
dot = -1.000 exactly on every lot in both towns: a convention, not a defect.
Katoomba's street_noon — the bookmark the tours have shot for nine rounds — is
46 draws / 19,680 tris of bare road.

Root cause: CITY_SPEC contradicts itself and each generator implements one half.
  CITY_SPEC:71  (lots contract) "World facing = (-sin ry, -cos ry)"  ⇒ -Z facade
  CITY_SPEC:335 (posters, R15)  "the +Z street facade (B's canon)"   ⇒ +Z facade
plan_osm.js:443 obeys the first (its comment: "facade (-Z) faces road").
plan.js, buildings.js, venue.js and the R15 posters obey the second. Synthetic —
the town every golden tests — follows B's canon and looks right, which is exactly
why nine rounds of real-town work never caught it. The epoch's own signature
species: a rule meeting a case written after it.

Proven, then REVERTED: one line in A's plan_osm.js (ry = atan2(-nx,-nz)) flips
katoomba 0/72 → 72/72 facing, and turns poseForShopFront's camera from 26m out in
a paddock into 4m from Musgrave Road — the money shot F specified. Not committed:
it is A's file, it moves every real-town plan (goldens re-pin under amendment
law), and a self-contradicting spec is Fable's to rule on, not mine to pick a
side of.

This also re-reads my R22 diagnosis: I recorded "chunk-buildings 348 tris (0.2%)"
in katoomba's busiest block and credited the instancing work. 348 tris is ~29
boxes — that was not efficiency, it was the buildings being ABSENT from frame. I
read the evidence as a win for five rounds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:42:50 +10:00
m3ultra
83ed848f16 Fable (integrator): R27 wave-4 — ratify the exactly-one-refusal + control-delta gates; B micro-task (frontage pose); F's tag path
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:23:04 +10:00
m3ultra
630042fbe1 Lane F R27: the kill-the-server gate GREEN — and two corrections to F's own gate
THE KILL-THE-SERVER GATE, permanent and green (charter law #1 made falsifiable):
  ARM 1 server-up   · gone[3] → 117 survivors · items[] 120, identity AND ORDER intact ·
                      COLLATERAL 0 · `gone` carries the atlas id verbatim (R26 fence)
  ARM 2 killed mid-session · enter 6ms vs control 6ms (Δ+0) · worst frame 204ms vs control
                      202ms (Δ+2) · room complete from files · ZERO game errors · EXACTLY 1
                      refused connection
  ARM 3 server-down boot   · pure tier 1, gone[0] · ZERO game errors · exactly 1 refusal
  SANDBOX · POST/PUT/PATCH/DELETE all 501 · a mint shop 404s
The subject exists only because G built `make-fixture`: production `gone` is EMPTY (0 sales
since 2026-07-01), so this gate would have passed vacuously forever on live data.

CORRECTION 1 — "ZERO console errors on server death" is UNACHIEVABLE, and F measured why. A
fetch to a dead host logs net::ERR_CONNECTION_REFUSED at the BROWSER level; no .catch() sees it.
To log zero the reader would have to know the server is dead WITHOUT ASKING. One refusal is the
irreducible cost of discovering death. F's replacement is strictly stronger: ZERO errors from
game code, and EXACTLY ONE refusal — which PROVES the circuit breaker. Without the breaker it is
one error per shop entry forever, and "zero" could never tell a working breaker from a broken
one because both fail it. F is the beneficiary of this change, so it is filed loudly in §27 for
Fable to ratify or overrule, not quietly relaxed.

CORRECTION 2 — F's frame assertion was weak: `< 250ms` is a number with no control. ARM 2 first
reported worst-frame 201ms and PASSED. But entering a shop rebuilds an interior, which costs the
same with or without a server — so an absolute threshold measures Lane C's room build, not the
network. The arm now walks the same shop with the breaker tripped and asserts the DELTA: 202ms
control, 204ms killed, Δ+2ms. The death cost nothing the room didn't. F told A the same thing one
round ago; a number without a control is not a measurement.

THE TOUR — 3 of 4 frames, and the 4th is FILED NOT SHIPPED. The real crate (the money shot: its
own 120 records, price stickers, the keeper), a mint crate (captioned MINT — plausible, not
real), the classic covenant. 0 page errors. The FRONTAGE frame does not land after three
attempts and F will not ship a frame that doesn't show what its caption claims: the cluster
primitive answers "where is this town's heart", which is not the question a frontage frame asks
— Red Hill's heart is 2 shops/60m and does not contain Monster Robot. Next: ask B, who owns the
frontage and shipped its mark this round. Re-deriving B's anchor in a tour script is how you get
three bad frames.

Also: the interior beauty shot came back with the HUD across it — R21's lesson, fixed on the
street frames only. Now every DOM overlay is hidden, not a guessed selector list.

NOT TAGGED. qa.sh --strict --matrix and the frontage frame remain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:05:17 +10:00
m3ultra
f91d734b32 Fable (integrator): R27 wave-3 — ratify ?live opt-in + the collateral assertion; F's remaining close enumerated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 14:39:54 +10:00
m3ultra
39cfc3d3ed Lane F R27 (v5.0): the tier-2 reader — pick-then-omit, and the server killed mid-session
#2 THE READER, built to C's §8 ruling (direct wire, no hook) and to what the waves measured,
not to the brief's letter:
  · `pack.gone` set, `price` updated IN PLACE — items[] never added to, removed from, or
    reordered, because its identity AND ORDER are the pick's stability (C §8.3). Filtering the
    array instead reshuffles the crate: C measured ONE sale changing 21 OTHER records.
  · fire-and-forget at room build, 1200ms abort, etag-cached, circuit-broken on first failure.
    The dig never blocks on the network (charter risk #1). No await anywhere near the door.
  · fires ONLY for a real-sourced shop. G's fence 404s a mint shop, and asking anyway would be
    R26's blind probe wearing a server. The manifest already knows which is which.

TIER 2 IS OPT-IN (`?live=1` / `?live=<url>`; absent → byte-identical to the beta). That is the
R26 lesson applied to a server: a default boot that probed for a GODVERSE host would ask a
question with no answer on every machine that isn't John's. Tier 2 is a place you go, not a
thing that happens to you. Auto-discovery is a v5.x question with a reachability contract.

PROVEN IN THE BROWSER (formal smoke class still to land):
  · sold-means-gone, via G's make-fixture (production `gone` is EMPTY — 0 sales since
    2026-07-01 — so a live assert would pass vacuously forever; G built the subject before the
    gate could be born blind): 3 sold → gone[3] → items[] 120→120 IDENTITY AND ORDER INTACT,
    117 survivors, COLLATERAL 0. C called this: both pick-then-omit and filter-the-array hide
    the sold record, so "the gone item never renders" cannot tell them apart — the obvious
    assertion is vacuous and goes green. Collateral is the one that discriminates.
  · KILL THE SERVER MID-SESSION → enter took 13.1ms, worst frame gap 14ms, the crate still
    renders its own 120 real covers, and ZERO console errors. Charter law #1 proven by
    execution instead of assertion — the thing A correctly refused to retire from the risk
    list until this gate went green.

Recorded, not hidden: after a mid-session kill the etag-cached `gone` persists for the session,
so a killed server does not resurrect sold records mid-walk. That is tier-1-plus-last-known, not
tier-1-exactly. It is the honest behaviour (a network blink must not re-stock a shop) and a
server-down BOOT is still pure tier 1 — but it is a nuance the brief's "degrades to tier 1
exactly" doesn't cover, so it goes in writing rather than in the gaps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:34:42 +10:00
m3ultra
70743db47e Fable (integrator): R27 wave-2 status — F's three load-bearing inputs (pick-then-omit, make-fixture for gone[], the lifecycle switch)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 14:13:39 +10:00
m3ultra
bee7563a76 Lane E R27 (v5.0): provenance v5-final — and the roster gap the freeze caught (ledger #6)
THE BUG THE FREEZE FOUND: redhill_godverse was ON DISK but NOT IN THE ROSTER — for three rounds. The v5
provenance table needed real numbers, so I measured instead of carrying the v4 row forward. The count
didn't match: 23 caches on disk, 22 rostered. The missing one was redhill_godverse — THE TOWN THE ENTIRE
v5 EPOCH WALKS TO.

Mine, squarely. index.json is Lane E's file (write_index()). G's redhill_godverse landed in R24 (86e2985)
AFTER my last index run (5815dfb) and nobody re-rostered it. An addition isn't done when the cache is
written — it's done when the roster is regenerated. That is the exact mirror of my own R24 lesson ("a
retirement isn't done when the cache is deleted; it's done when the consumers are swept"): I wrote the
lesson for deletions and missed its reflection for additions.

WHY FOUR GREEN ROUNDS NEVER CAUGHT IT — a gate-shape lesson. Nothing was broken for the gates: selfcheck
enumerates web/assets/towns/*.json DIRECTLY (so it saw the town; A even pinned its goldens) and F's crate
smoke boots BY KEY (?town=redhill_godverse). The roster's only consumer is B's HUD selector (hud.js, my
R20 ledger #2 design). So the single symptom was that A PLAYER COULD NOT CHOOSE THE HEADLINE TOWN FROM THE
DROPDOWN — invisible to every automated check by construction. A gate that reads around the artifact under
test cannot test it: the vacuous-gate species, one layer out.
Fixed: re-ran write_index() -> 23 rostered, 23 on disk, 0 missing. Only index.json moved (every cache
byte-identical again). selfcheck ALL GREEN 156,212, golden 0x3fa36874 untouched. qa.sh --strict --matrix
7 passed / 0 failed / 0 warn.

-> F (#5, the tour): the money shot walks Musgrave Road into Monster Robot Party. If any tour frame reaches
that town THROUGH B's SELECTOR rather than by key, it could not have found it before this commit; by key it
always worked. Worth one glance at your framing path.
-> B: the dropdown now carries both godverse towns. Your `${t.state ? ', ' + t.state : ''}` handles their
empty state gracefully — nothing needed, and the defensive write is why this cost nothing cosmetically.
-> G: your godverse caches carry no `state`, so they roster as state:''. Harmless (B degrades cleanly). For
"Red Hill, QLD" in the picker, emit `state` — write_index() already prefers the cache's own field.

PROVENANCE v5-FINAL. The ⟨v5.0-FINAL⟩ rows are measured, not inherited — the town-pack row still claimed
v4's "22 towns / 1192 shops", stale since my R24 ballarat retirement and redhill build (now 21 real / 1196
shops / 299 heroes, + both godverse towns = 23 rostered). Added the two v5 art rows: the real-stock atlas
(1 crate, 120 items, Monster Robot's own POS, sku_ ids) and the mint atlases (14 crates, 224 items, seeded
from real dealgod listings, mint_ ids). These are the table's FIRST AMBER ROWS — everything before v5 was
green web-ok; real product photography is the first asset class this project ships that a human must clear
before public release.

I VERIFIED MY OWN PROSE AND IT CAUGHT ME: I first wrote the mint crates as "6 record · 6 book · 3 toy" —
that is the breakdown of all FIFTEEN crates, which includes Monster Robot's REAL record crate. The 14 mint
crates are 5 record · 6 book · 3 toy. A count attributed to the wrong population, one line from being
frozen into the release record. Every other claim in the table re-measured true (21/1196/299/8 states/23
rostered/roster==disk/72/37/120 items/224 items/both zones in-house-green).

THE LICENCE LINE'S STORY, recorded as a mechanism. The amber rows are guarded by exactly one printed line,
so the freeze says why it took two rounds to make honest: R23's glob matched 0 files and returned 0; R25's
permissive `or` fallback let the print go silent while the check still passed — the flag stopped reaching a
human on real product photos while the gate said OK. Now: one spelling, one lookup with one owner, and the
line is COUNTED (judged N => N lines, or FAIL). Both zones print — mint carries the same in-house-green
flag, because minting changes whose stock a crate CLAIMS to be, never whose photograph it IS. Frozen today
at: atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:59:49 +10:00
m3ultra
cedc5872fb Lane G R27 (v5.0): tier 2 — the two read endpoints, the three fences, and the gate's missing subject
THE LIVE CRATE READS. godverse_server.py: /health, /shop/<gid>/stock, /shop/<gid>/crates, plus
start|stop|status (F's kill-the-server lifecycle), setup-role, make-fixture. Stdlib + psql. First
server code of the epoch, per the ratified G3.

Live against the real POS: crate 550 -> {baked:120, live:120, gone:0}, real prices, real VG+
grading, etag stable and ?since= -> 304. /crates returns 315 REAL crates with the shop's own
labels: TECHNO (57), $12-$15 TECHNO (46), DRUM N BASS (66).

THE THREE FENCES, each enforced by the machine, each measured:
- The credential: godverse_ro holds SELECT on inventory/crate/disc_cache and nothing else.
  UPDATE inventory -> permission denied. SELECT FROM customer -> permission denied. ONE grant
  carries both the §7 sandbox and the §9 PII fence — which is why §7 said enforce it at the
  credential, not in a code review.
- Write verbs absent: POST /reserve -> 501, POST /buy -> 501, GET either -> 404.
- Metadata only: no cover, no pixels on the wire; a mint shop's /stock is 404 (no POS to be live
  about), not an empty crate.

THE FINDING F NEEDS BEFORE IT WRITES THE GATE — `gone[]` has no subject in production. The
mechanism is right; the data has no movement. Monster Robot Party's POS records 5,567 sales ever
(400-800/month Dec->Jun) but 26 in July and NONE SINCE 2026-07-01, and hasn't been written since
2026-07-09 — the 07-16 dump is fresh, its data is stale (restored it to check: crate 550 holds 122
on both 07-15 and 07-16, identical). 0 of the crate's 120 records have ever sold. So `gone` is []
against the real POS, permanently, until the shop trades again — and an assert over it would pass
WITHOUT TOUCHING ITS SUBJECT, the exact species that has held four tags. So: make-fixture --sold 3
clones the POS (createdb -T) and marks 3 of the crate's OWN REAL SKUS sold -> verified
{baked:120, live:117, gone:3}, the three ids absent from items[]. F points at it via
GODVERSE_POS_DB for a deterministic gate; production reads the real POS untouched. A fixture is
not a lie; a green assert over an absent subject is.

LIFECYCLE (ledger #3): stop is a SIGKILL, not a graceful drain — the gate tests a death, not a
goodbye. Verified: stop -> connection refused (curl rc=7) -> status DOWN -> start -> live. No
admin verb was added to the API: a server whose whole point is "no write verbs" shouldn't grow a
remote kill.

THREE CORRECTIONS TO MY OWN G3 §4 (design -> as-built), all measured not reasoned:
1. gone:[sku] -> gone:[id]. §4 AS I RATIFIED IT said bare skus — wording that predates R25's sku
   ids and R26's fence law, and a bare number crossing two id spaces is the precise bug that law
   exists to stop. My doc was the stale rule meeting a case written after it: the epoch's
   signature failure, this time in the doc I wrote.
2. items[] drops release_id and cover — cover edges the API toward serving pixels; release_id is
   the pressing, and tier 2 speaks only in copies.
3. Write verbs answer 501, not the 404 my own docstring claimed — caught by running it. 501 is the
   STRONGER proof: 404 means a handler declined; 501 means no write handler exists in the process.
   -> F: assert 501.

Scoped: pathspec commit. web/assets/towns/index.json is another lane's live edit — left untouched.
Runtime pidfile moved out of the repo (it is not an artifact).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:56:02 +10:00
m3ultra
6960bdd279 Lane C R27 (v5.0): the tier-2 seam ruling + LANE_C_PUB v5-FROZEN (ledgers #2, #6)
SEAM RULING (§8): bless the direct wire — NO lifecycle hook. F already holds currentAdapter
(interior_mode.js:67) and passes it to dig.open() PER DIG (:111), so the live read lands, F
updates the pack, the next riffle picks it up; not-landed/slow/dead => the adapter is simply the
tier-1 pack => the dig never blocks (charter risk #1). No C lifecycle change.

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:53:02 +10:00
m3ultra
01f398960b Lane B R27 (on-call): the R26 handshake verified against G's real manifest
R27 owes B nothing ("B/D: notes current; nothing else owed"; "B/D on-call").
This is on-call work on F's #5: the tour is specified as "Monster Robot Party's
frontage (B's mark visible)" — a claim R26 could not test, because G's manifest
did not exist and B proved the treatment against a temp manifest it scaffolded
and deleted. G shipped the real one (36d10ca). Now measured, not assumed.

Monster Robot Party is in redhill_godverse — NOT newtown_godverse, the only
town B ever tested the mark in. F's money shot is posed in a town this
treatment had never been verified against. It works.

The handshake held unmodified: G's {version, shops:[{godverseShopId, types,
sourcing}]} lands inside B's tolerant reader with zero coordination. The
tier→sourcing rename did not touch B — the mark never reads that field, so a
rename of it cannot break the footpath. A cue that reads no field cannot break
when the field moves.

Both arms proven live, the discriminating arm for the first time (R26's temp
manifest marked all 18 keyed shops, so "the manifest is the authority, not the
key" was never exercised). Redhill: 9 keyed, only 4 stocked.
  Monster Robot Party #3962749 (real)  keyed + in manifest → MARKED
  Silky Oak Furnature #31              keyed, not in manifest → BARE
  Thrift Shop and Eternity Boutique    keyed, not in manifest → BARE
Two keyed shops stand metres from a stocked one wearing no mark, because they
have no crate. The street advertises exactly the crates that exist. (#31 is
Fable's R25 id-namespace shop; the mark reads godverseShopId only, unaffected.)
Frontage 135 draws / 74,354 tris, 0 page errors, manifest prefetched at boot.

Intel for F's #5, no action owed: the novelty_record landmark (B's furniture —
a giant vinyl disc marking record shops) stands 2.5m off the frontage and is
prominent head-on. Ray-grid occlusion so F can frame without guessing:
  mark+sign   head-on 4% blocked   off-axis +4m 0%
  window      head-on 22% blocked  off-axis +4m 44%
Shoot HEAD-ON: the vinyl sits low (y~1.3), the mark rides high (y~3.3), and
stepping off-axis doubles window occlusion instead of helping. I first read the
black disc as a render bug — it isn't, it's the landmark working; recorded so
the next reader doesn't re-open it.

Notes-only; no code changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:52:57 +10:00
m3ultra
839575b28d Lane A round 27 (v5.0): the docs freeze — CITY_SPEC v5 + the risk list judged (ledger #6)
Docs only. No code, no golden moved — correct: tier 2 touches no plan, no cache, no atlas bytes. selfcheck
ALL GREEN 156,212/156,212; scaffold + consistency green; classic 0x3fa36874 and gig 0xb1d48ea1 frozen. The
whole stock epoch moved zero plan goldens, which was the determinism boundary's entire promise.

CITY_SPEC v5 SECTION — written from the ARTIFACTS, not the briefs (this epoch's own lesson):
- The tier ladder: 0 parody / 1 static real / 2 live GODVERSE, each degrading softly to the rung below.
- `sourcing` is NOT `tier`, and C's ruling is the precise one — both ship in every index. `tier: 1` is the
  LADDER RUNG (a mint atlas is still a static file, so still rung 1); `sourcing: "real"|"mint"` is WHERE
  THE CONTENTS CAME FROM. Different axes. Collapsing them is exactly how a mint crate comes to read as
  real. Measured: 15 keyed atlases = 1 real + 14 mint.
- The three id-space fences, verified rather than restated (below).
- The manifest: existence DECLARED, never probed — the root fix for fail-soft-by-404 colliding with the
  zero-console-errors law. No entry, no fetch, zero 404s, no attribution exceptions. 15 entries / 15 dirs.
- Ruling 2 as law (mixed caches are the design; no gate may assert it away — mine did).
- Lane A's seam is exactly one field: godverseShopId, and `'godverseShopId' in shop` is the tier test.

THE ID FENCES ARE VERIFIED, NOT ASSERTED. The plan-id/godverse-id trap is STILL LIVE in the data and I
reproduced it: in redhill_godverse, bare `31` is "Wholefood Cafe" as a plan id and "Silky Oak Furnature" as
a godverse id — the exact ambiguity that made F's enterShop(31) enter the wrong shop and pass a soft-fall
assertion vacuously on the wrong subject. So it goes in the spec as a STANDING RULE, not a fixed bug: no
code or gate may compare a bare number across the two spaces. (Within the godverse space, G's two halves —
census <=2992, dealgod store ids — are numerically disjoint by construction, which is what makes the opaque
key safe.)

THE RISK LIST, JUDGED LINE BY LINE — AND I DID NOT PRE-RETIRE ANYONE'S GATE:
1. CARRIED. Tier-2 latency/stutter is this round's defining gate and I'm writing in wave 2 — F's reader and
   kill-the-server gate do not exist yet. NOTHING retires this line except that gate going green. Retiring
   it from a doc while the code is unwritten would be the precise species this epoch spent four holds
   catching: a rule meeting a case that doesn't exist yet. The offline law is a promise until #3 makes it
   falsifiable; that's the whole point of #3.
2. RETIRED — measured, and the remedy the risk NAMED is the remedy that shipped ("crate rotation rather
   than bigger atlases"). Max 2 atlases/shop vs C's hard cap of 2; the 14 mint shops sit at 16 items / 1
   atlas / 1024²; only the one real POS shop earns 2048² (120 items, 2 atlases, 1.2 MB) under the 60+ rule.
3. RETIRED — DESIGNED OUT, not solved. The risk assumed a fuzzy-matching problem ("names drift, shops
   move"). There is no matcher: identity is an explicit opaque key G emits. The predicted failure cannot
   occur in the predicted form — and the proof is the headline shop: Monster Robot is in NEITHER the census
   NOR OSM, so no name-matching strategy could ever have found it. Mis-stocking is fenced structurally
   instead (duplicate ids error, uniqueness over defined ids, the orphaned-atlas gate).
4. SPLIT. Write-back RETIRED by John's ruling recorded as MECHANISM, not config: /reserve and /buy are
   ABSENT, not stubbed — races and reservation semantics cannot occur because the verbs don't exist, and
   "prove the absence" beats any sandbox flag a config typo could flip. (Option B re-opens this line
   deliberately at v5.x, behind a hold budget.) The `gone` read half is CARRIED to F's #4.
5. Unchanged, extended: static-atlas determinism proved in practice (G's index-only sku re-emit moved no
   .webp bytes); the fail-soft ladder proved on every rung; the id fences now have case law behind them.
6. NEW, CARRIED — the orphaned atlas, with a live near-miss on the books.
7. NEW, CARRIED — "real" is one shop wide: 14 of 15 keyed atlases are mint, because exactly one shop has a
   POS. Honest and marked, not a defect — but the epoch's headline claim rests on ONE shop, and any future
   claim of scale needs either siblings for monsterrobot or continued care that `sourcing` stays legible to
   humans and not just to gates.

THE GATE THAT ARMED ITSELF (the law working unattended, worth recording). My R25 orphaned-atlas gate SKIPped
on newtown_godverse all through R25 — "no committed atlas keys to this town", subject absent, so it said so
instead of passing quietly. The moment G's 14 mint atlases landed it STARTED BINDING ON ITS OWN and passed:
156,211 -> 156,212, and that +1 IS the gate arming itself. Nobody edited it. That is what the vacuous-gate
law buys — a gate that waits, in the open, for its subject to exist.

G: redhill_godverse's lift drops keyed shop 2286 "Empire Revival"; you stocked its numeric neighbour 2287.
Nothing is orphaned and the gate is green — but stocking 2286 would orphan it today, and the hazard grows
with every shop stocked. Carried as risk #6.

F: nothing of mine gates your tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:52:47 +10:00
m3ultra
2cc6c32c73 Fable (integrator): V6 LIVING STREET draft charter — John's NPC-variety idea, grounded in the measured local stack (MOCAPGOD/MIRPAMO/MODELBEAST), three spikes first
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:40:47 +10:00
m3ultra
78848ed254 Lane F R26 wave 4: the re-run — 6/1/0 → 7/0/0, QA GREEN, v5.0-beta tagged
The fence went in and the hold ended. C ruled, and both lanes titled their commit the same
thing: "my rule was the stale thing." E's one line:

    SLOT_ID_PREFIX = {"real": "sku_", "mint": "mint_"}     # sourcing-scoped, not one space

qa.sh --strict --matrix: 7 passed / 0 failed / 0 warn. selfcheck ALL GREEN 156,212/156,212 ·
MATRIX GREEN 10 towns x 7 gates · flags harness (carrying F's beta gate: 15 crates / 105 pairs /
0 shared titles, sourcing real+mint, ZERO 404s, 0 console errors) · atlas-QA OK, 15 atlases
valid, 15 LICENCE LINES SHOWN — R24's finding #4 at full scale, where one silently printed none.

The ruling is the round's keeper. Fable: prefixes are namespace fences. E's comment makes G's
judgment law: "`sku_` names a POS copy on a shelf, `mint_` names a dealgod listing standing in
for one... a prefix that lies doesn't fail a gate, it SELLS THE WRONG RECORD out of a real shop
to satisfy a minted stand-in. Never treat them as one space (same standing note as plan-ids vs
godverse-ids)."

That closing clause is F's R24 collision — plan id 31 vs godverse id 31, which walked F's own
harness into a cafe — promoted from a finding to a standing law, and re-derived independently by
G in the layer that moves money. The bug F found by entering the wrong shop is the bug G refused
to ship by selling the wrong record.

The rule bent; the id spaces didn't. That is the right direction: a rule describes an intent, and
when a legitimate case fails it, the description is what's wrong — PROVIDED somebody measured
that the intent still holds. F measured it before the ruling (non-positional, seeded, stable,
0 reuse across 15 crates), which is what made the ruling a decision instead of a guess. Holding
the tag was never about the rule being wrong; it was about who gets to say so.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:36:44 +10:00
m3ultra
bd6f576c00 Fable (integrator): round-27 instructions — v5.0 THE LIVE CRATE (wave 0: beta tag; then tier-2 read-live, the kill-the-server gate, the tour, the tag)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:27:28 +10:00
m3ultra
f2989858aa Lane E R26 w4: the mint-id fence — my rule was the stale thing (wave 4, E's one line)
THE RED WAS MINE. My R25 slot-id check demanded `sku_` on every real-stock pack — faithfully implementing
C's R25 line, which was written BEFORE MINT EXISTED. It then failed all 14 of G's mint crates, whose ids
honour its every intent: non-positional, seeded, stable, zero reuse. C's ruling names it exactly: "the rule
was the stale thing, not E's check." Fourth hold of the epoch, and the fourth to trace to a rule meeting a
case written before the case existed.

THE FIX IS A FENCE, AND IT IS STRONGER THAN THE RULE IT REPLACES — that was the thing to prove. The easy
green was to drop the prefix check. Instead the form is sourcing-scoped per C §7.2a (R26 w4):
sourcing="real" => `sku_` (a POS copy on a shelf) · sourcing="mint" => `mint_` (a dealgod listing standing
in for one). A crate wearing the other's prefix FAILS.

WHY THIS IS A MONEY BUG, NOT A NAMING BUG — G's judgment, now law. G refused to file dealgod listing ids
under `sku_` to go green the easy way. They were right: tier-2's sold-means-gone looks up POS skus. R24's
id collision entered the WRONG SHOP; the same collision in the money layer SELLS A REAL RECORD out of a
real shop to satisfy a minted stand-in. The gate's error says that, in those words — the cost, not the
rule. The message also distinguishes a fence-crossing from a merely positional id, because they are
different faults and the reader shouldn't have to guess which one bit.

PROVED 7/7 — that the fence BITES, not merely that the red stopped. G's real crate (sku_...) -> PASS ·
G's mint crate (mint_...) -> PASS · mint filed under `sku_` (the exact shortcut G refused) -> FAIL, naming
the fence and the real cost · real wearing `mint_` (the reverse) -> FAIL · positional ids still FAIL in
BOTH sourcings (the old rule's whole intent, retained) · duplicate ids -> FAIL. Loosening would have passed
the first two and lost the last four; this passes the first two and keeps all four.

BOTH OLDER SUITES BROUGHT FORWARD RATHER THAN LEFT TO ROT. The R26 suite's mint fixture wore sku_ ids, so
the fence correctly rejected it — a fixture that fails for a NEW reason has stopped testing what it was
written for. Moved to the mint_ space: 17/17. R25 licence suite: 8/8, tally still firing VIA THE TALLY.
Third round running where the near-miss was a test going quietly stale around a changing contract; it is
the failure mode I now look for first.

Gates: atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s) (1 real + 14
mint, every one showing its flag to a human) · validate_manifest exit 0 · qa.sh --strict --matrix
7 passed / 0 failed / 0 warn — QA GREEN.
-> F: your 6/1/0 is now 7/0/0. The beta is yours to tag.

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:32:45 +10:00
m3ultra
0b4bc6c1f9 Fable (integrator): R26 wave 4 — the mint-id ruling (prefixes are namespace fences); C one line, E one line, F tags
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:49:38 +10:00
m3ultra
7ba7ab20a8 Lane F R26: the beta gate's exact tally — 6 passed / 1 failed / 0 warn
The full qa.sh --strict --matrix run confirms the hold and sizes it. Green: selfcheck ALL GREEN
156,212/156,212 · MATRIX GREEN 10 towns x 7 gates · flags harness (carrying F's beta gate:
distinctness, sourcing, zero 404s, 0 console errors) · consistency · figure-scale.

Red: the manifest validator alone — atlas-QA, 14 identical errors, all 14 mint crates, one
sourcing-blind assertion, one line. The real crate passes.

Recording the ratio because it IS the argument: the beta is finished. What stands between it
and the tag is a rule meeting a case written before the case existed — C's to rule, E's to
land, and not F's to quietly loosen on its own behalf.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:43:35 +10:00
m3ultra
528829eeba Lane F R26 (v5.0-beta): the manifest consumed, the tolerance retired — tag HELD on the mint-id rule
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_<dealgod listing id>, 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 <noreply@anthropic.com>
2026-07-17 11:38:44 +10:00
m3ultra
a9a5ef05d8 Fable (integrator): R26 waves 1-2 complete — F's beta gate asserts C's 'sourcing' (the brief's 'tier' superseded, correctly)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:17:54 +10:00
m3ultra
8fd73acfa7 Lane F: the v5 README teaser — held three rounds, shipped with the tag
The teaser and the tag were one claim, so it waited until there was a tag to stand on.
v5.0-alpha is now live at e7d58ea: walk into Monster Robot Party on Musgrave Road and riffle
its own real crate — 120 real records off its real POS, real covers, titles and prices.

Documents the tier ladder as the epoch's load-bearing idea (0 parody / 1 static real / 2 live
GODVERSE, each degrading softly to the one below), the offline law (?noassets fetches zero
bytes; a missing atlas falls to parody, never another shop's crate), and the covenant that
outlived four epochs (?classic=1 → 0x3fa36874, byte-identical forever).

States the licence position where a reader will actually meet it: item photos are each shop's
own product shots, in-house green, FLAGGED before any public or commercial release; real
titles/artists ride the same amendment; and no in-game purchase touches real inventory —
sell-sandboxed by ruling, with the reservation tier chartered for v5.x, not assumed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:41:50 +10:00
m3ultra
36d10ca4ae Lane G R26 (v5.0-beta): the mint baker, the crate menu, the atlas manifest (ledgers #2, #6)
EVERY CRATE DIFFERENT — measured: 15/15 keyed crates distinct, max shared titles between any two
= 0. 14 mint atlases (5 record, 6 book, 3 toy) + Monster Robot's real crate + the manifest.

MINT (ledger #2): the G3 note promoted — random.Random(shop_id) over an ID-ORDERED pool of real
dealgod listings. The shop id IS the seed, so two shops can never draw the same crate. No
TABLESAMPLE, no unseeded random: the two things that disqualified thriftgod's own mint() from
tier 1 (G3 §2). Re-mint + re-bake is byte-identical. Only keyed shops whose type the loader can
fetch get one (record/book/toy, C §7.1); the 13 keyed opshop/video shops get none and stay parody
— an atlas there could never load, and the manifest is what makes that silent instead of a 404.

MANIFEST (ledger #6, C §7.2d): stock_godverse/index.json — 15 shops, 1 real / 14 mint. Derived by
reading the atlases back off disk, so it cannot drift from them: the manifest IS the directory.
Real crate re-emitted with sourcing:"real" + C §7.2c's condition/sleeve_cond (my snapshot always
carried them; I dropped them at emit — that was the hole in C's price card). Atlas bytes unmoved.

THE ONE RED — mint ids vs E's SLOT_ID_PREFIX (E, one line). E asserts `sku_` on every atlas under
stock_godverse/; mint items have no POS sku and carry `mint_<dealgod listing id>`, so all 14 fail.
C §7.2a's RULE is satisfied — derive from the source's own stable key, never position, unique
across ALL packs (measured: 344 ids, 0 clash with the generic packs, 0 positional). What §7.2a
specifies as sku_<POS sku> it specifies "for a real-stock (godverse) pack"; mint is §7.2b's other
thing, written a round later, and the id form was never revisited.

I did not just emit `sku_` to go green: a POS sku and a dealgod listing id are DIFFERENT
NAMESPACES, and this round's own standing note says no code or gate may compare a bare number
across two id spaces — it exists because enterShop(31) entered the wrong shop. Putting both 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 sku a POS must resolve. §7.2a's
own reason #2 is exactly about ids being looked up across snapshots. Fix: make the prefix
sourcing-aware (sku_ for real, mint_ for mint) — E's constant and error text are the only places.

THREE MORE FINDINGS, none blocking: [E] FITS_1024=64 cannot be satisfied — 1024²/256² = 16 cells,
so 64 items need 4 atlases and C caps a shop at 2; any 33–64 item pack MUST be 2048². Latent (my
packs are 16→1×1024² and 120→2×2048²); the honest bound is 32. [C/F] the ≤32 MB ceiling doesn't
hold at beta scale because the LRU it assumes hasn't shipped — F's boot preload loads EVERY keyed
shop's pack, measured 46.1 MB/town resident; it's why mint crates are 16 items on one 1024² sheet.
[C] "the crate menu, baked statically" can only be metadata: one base = one index = ≤2 atlases =
128 items, and Monster Robot has 357 crates / 24,646 records — so crate_menu ships the real crate
list (labels + counts) and per-crate STOCK needs a per-crate base, i.e. a C contract line.

MINT HONESTY: sourcing:"mint", an attribution that says "not this shop's stock", and NO POS-claim
field at all — structural, not editorial. Records require a real Discogs artist (dealgod's `brand`
on a record listing is the SELLER — the first bake had "Red Eye Records" as an artist; a shop is
not an act) and use discogs_master_title where present, so Egg Records reads "MC5 — High Time
$50", not "Pre Loved Record - Elton John - Breaking Hearts". Books/toys take `brand` per C's
author/brand convention — sometimes the maker, sometimes the listing store. Real, imperfectly
typed, and never claimed as that shop's.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:33:40 +10:00
m3ultra
33577a9675 Lane E R26 (v5.0-beta): the sourcing arm + the atlas manifest, both ways (ledgers #3, #6)
THE FIELD IS `sourcing`, NOT `tier` — and reading C's contract instead of the brief is the only reason
this gate isn't already broken. The R26 brief says "the validator grows the tier arm... `tier` required
on every atlas". C ruled it CANNOT be `tier` (§7.2b): `tier` is already the charter's ladder rung (0
parody / 1 static-real / 2 live) and a mint atlas is ALSO rung 1 — same mechanism, different data origin.
Overloading it puts two meanings in one name: the same species as the plan-id/godverse-id collision and
the positional ids. Had I built the brief's word, my gate would demand `tier` while G emits `sourcing` —
R23 exactly, re-run. The contract's author is the authority for the contract's shape.

THE HONESTY SEPARATION, BOTH DIRECTIONS (C §7.2b). `sourcing` ∈ {real, mint} required; no `sourcing` =>
FAIL (a crate that cannot say where it came from must never pass as one that can). mint must carry no POS
claim; real must carry the POS provenance it claims. The licence line prints for both — verified, not
assumed.

-> C: §7.2b says "a mint index MUST NOT carry POS-snapshot fields" but does not enumerate them, so I named
the set — correct it if it's wrong. I assert on `crate`, `provenance.source.pos_db`,
`provenance.source.pos_dump` — the fields that claim a SPECIFIC SHOP'S ACTUAL INVENTORY. I deliberately
excluded `snapshot`: §7.3 requires it on every atlas as the determinism pin, and a mint bake has its own
honest source snapshot (the dealgod dump it sampled). The error names the exact field found, so a
divergence with G surfaces as a diagnosis, not a mystery.

THE MANIFEST (§7.2d, ledger #6), consistency both ways. An atlas on disk absent from the manifest FAILS
(no entry => no fetch => that crate silently never loads; an unreachable atlas is not a passing atlas). A
manifest entry with no atlas FAILS (the wire fetches it and 404s — the exact noise the manifest exists to
kill). C's "absent manifest => legitimate zero-shop state" is honoured: no atlases + no manifest => SKIP.
-> C: one hole your rule implies but doesn't spell out — a manifest that PROMISES shops while zero atlases
exist now FAILS rather than skipping. "Absent" scopes to the empty tree; a tree where something on disk
still asserts atlases exist is the vacuous shape wearing a hat. Flagging it as my reading, not your words.

PROVED BEFORE HANDING G THE GATE — 17/17, including a mint crate that does not exist yet (the R25 rule:
never hand a lane a gate that fails on compliance). real -> PASS · mint -> PASS WITH ITS LICENCE LINE
PRINTED (ledger #3's "prints for both") · real+mint side by side -> PASS · mint carrying `crate` -> FAIL ·
mint carrying pos_db -> FAIL · real carrying no POS provenance -> FAIL (the reverse) · no `sourcing` ->
FAIL · sourcing='realish' -> FAIL · atlas absent from manifest -> FAIL · manifest entry with no atlas ->
FAIL · declared type not on disk -> FAIL · manifest says mint while the index says real -> FAIL · manifest
missing while atlases exist -> FAIL · unparseable manifest -> FAIL · empty tree -> SKIP · empty manifest
-> SKIP · manifest promising a shop with no atlases -> FAIL.

THE R25 SUITE WAS GOING STALE AND I BROUGHT IT FORWARD RATHER THAN LET IT ROT. Adding `sourcing` + the
manifest made every R25 licence case fail for a NEW reason — which would leave the licence arm untested
while appearing tested. Updated to the R26 contract: 8/8 green, and the tally tripwire still fires VIA THE
TALLY (via_tally=True), not incidentally. Near-miss worth recording: the manifest error briefly shadowed
the tally in the harness, which is precisely how the tally died the first time. A test that fails for a
new reason has stopped testing what it was written for.

The gate is RED until G lands (2 errors: no `sourcing`, no manifest). G is in my wave, parallel; it goes
green on their re-emit with no change from me — same as R25.

-> F: THERE IS STILL NO v5.0-alpha TAG, and C's + B's BETA commits (4f0adf1, 2f4ab59) now sit on main
above wave 0's close. Tagging HEAD would produce a v5.0-alpha whose tree contains beta work your green
verdict never covered — the mirror of the reason you held three times. The alpha's tree is e7d58ea (wave 0
COMPLETE, my 9b1df5c beneath it). Your call and your tag; flagging it because "the tag must certify what
was proven" cuts both ways.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:08:59 +10:00
m3ultra
2f4ab5912f Lane B R26 (v5.0-beta): the stocked shopfront reads from the footpath
Ledger #5 — B's polish pick. A stocked godverse shop now tells you it's
diggable before you reach the door: a crate of seeded spines under the shop
name, and a warm-lamp-over-crates room behind the glass.

Cost: +0 draws / +0 tris. The mark is painted into the chunk's EXISTING
sign-atlas cell; the window cue rides the EXISTING per-shop aTint attribute.
Nothing new is allocated or submitted.

The manifest is the authority, deliberately NOT the godverseShopId key: keyed
means "has a stock identity", the manifest means "a crate is actually here".
Marking keyed-but-unstocked shops would advertise a crate the shop hasn't got
— the street would lie. The mark does not leak tier: "you can dig here" is
equally true of real and mint; provenance belongs on C's price card and in E's
gates. (Second job for ledger #6's manifest, free.)

Measured — true A/B at one pose, manifest 200 vs 404:
  spine-marked sign cells   0 → 3 (18,768 exact-palette px)
  stocked window tint verts 0 → 36
  street_noon               65 draws / 115,502 tris → UNCHANGED
Stocked shopfront (Goulds Books, footpath): 85 draws / 144,730 tris — inside
street law (<=300 / <=200k). Cues agree with ground truth: 3 stocked shops
live → 3 marked cells, 9 stocked window quads.

Covenants re-proved (the prefetch is new code on the boot path), tested with
the manifest present on disk so a stray fetch would 200 and be unmissable:
  ?classic=1        0 keyed, 0 stock fetches, 0 errors  (zero-fetch covenant)
  default synthetic 0 keyed, 0 stock fetches, 0 errors
  manifest 404      18 keyed, fail-soft, 0 page errors

Three traps, self-caught: (1) a module-load fetch would have put a fetch delta
on ?classic=1 — restructured to a lazy reader + plan-driven prefetch, keyed
plans only; (2) the lazy trigger alone raced the first chunk build, so the
first stocked shop you approach built unmarked — prefetch moved to
createChunkManager, before any chunk builds; (3) the first "zero cost" was
VACUOUS (manifest never fetched, so nothing was applied) and the first pixel
detector reported 60 marked cells with no treatment at all — replaced with an
exact-palette signature that reads 0 in control. Vacuous-gate law applies to a
lane's own verification too. Corollary: force-cache serves a deleted file
(transferSize 0, status 200 while disk 404s) — poison the cache or you are
testing your own memory.

B consumes stock_godverse/index.json read-only and emits nothing into it; the
temp manifest used to prove this round was deleted — the directory is G's.
Shape-tolerant + fail-soft, so G's real emission needs no coordination with B.

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:38:42 +10:00
m3ultra
e7d58eaad5 Fable (integrator): R26 wave 0 COMPLETE — E landed its own commit, F is GO to tag v5.0-alpha
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:28:18 +10:00
m3ultra
9b1df5cca0 Lane E R25 (v5.0-alpha): the licence line made unmissable + the sku form asserted
THE BUG WAS A DIVERGENCE, NOT A SPELLING (ledger #2). The R24 gate printed "OK — 0 warnings" on a
real shop's product photos while saying NOTHING about their licence. Two paths read one field with
different spellings:

    the CHECK:  lic = prov.get("licence") or prov.get("license")   <- permissive: passed either
    the PRINT:  lic = prov.get("licence")                          <- strict, behind `if lic:`

While G's artifact was British they agreed. G's R24 keyed re-emit moved it to US `license`: the check
still passed, the print silently resolved to {}, and FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE stopped
reaching a human. The `or` fallback WAS the bug — it made one path permissive so it could not feel the
other break. That is my own R21 stale-roads lesson one layer up (a check applied to one of two
symmetric paths is a trap with a fuse on it) — I wrote that lesson down and shipped it again anyway.

Fixed structurally, not by adding the missing spelling: ONE spelling, NO fallback (British `licence`
now ERRORS and names C's §7.3 retirement); ONE lookup, ONE owner (validate_index returns the evidence
string, main prints what it is handed and never re-looks-up the field — no second path left to drift);
and the evidence line is COUNTED — judged N atlases means N licence lines shown, or FAIL.

MY OWN FIRST CUT HAD THE SAME DISEASE AND MY OWN TEST PASSED FOR THE WRONG REASON. I added the tally
tripwire — but main() also appended its own missing-licence error, so all_errs was never empty when the
tally would fire: the tally was UNREACHABLE DEAD CODE, and the test went green through the other path
and reported PASS. Exactly the trap I recorded in R24 ("a gate that fails for the wrong reason is a
gate that will pass for the wrong reason later") — one round later, on me, in the file where I wrote
it. Now the field check owns the precise error and the tally sits after the all_errs return, existing
only for the day that check goes permissive again. Verified by stubbing validate_index permissive: it
fails VIA THE TALLY, not incidentally.

Proved 8/8: G's sku re-emit -> PASS with the flag shown · `license` gone/empty -> FAIL · British at top
level -> FAIL · British nested -> FAIL · attribution/snapshot gone -> FAIL · THE R24 SILENT SHAPE ->
FAIL · permissive-check simulation -> FAIL via the tally.

Aligned to C §7.3 as the provenance authority: the four gate fields (license, attribution, generator,
snapshot) live at TOP LEVEL, US spelling — the convention 21 shipped town caches already use. This
reverses R24's #2/#3, which read the artifact of the day. Docstring and code now say the same thing;
R24's fourth bug was that they didn't.

C §7.2a — THE SKU FORM (ledger #3), and I verified C's claim before building to it: independently
measured 120/120 godverse ids also present in the generic pack (F and C exactly right), and G's
snapshot carries 120/120 unique skus so the re-emit was feasible. Proved the compliant shape passes
BEFORE handing G the gate (4/4: sku_<POS sku> -> PASS · positional -> FAIL · sku-but-duplicated ->
FAIL · one id sneaking back into rec_ -> FAIL at 1/120, so it catches a real collision, not only an
all-or-nothing one).

-> C: two things beyond your literal ask, say if you disagree. (1) I also assert the actual property
your rule exists to guarantee — the pack's ids are DISJOINT from the shipped generic packs, measured
against web/assets/models/stock_*_index.json; the prefix says ids should be disjoint, this measures
whether they are, and it catches the reverse drift (a generic pack wandering into sku_) a prefix check
never could. Absent generic packs WARN rather than passing silently. (2) I flag British `licence`
anywhere, including inside the nested provenance{} "colour" block — §7.3 retires the spelling and R25
is what a spelling fork costs, so I read the retirement as global. Scope it back if you meant
otherwise.

The gate was RED by design for one wave: between my readiness and G's re-emit the collision was real
and unfixed, so it failed deliberately — a gate that stayed green on a defect F had just found would
be the vacuous species in a different hat. It went green when G landed 4314622, with no change from me.

Gates: validate_atlas green against G's re-emitted atlas with the licence line PRINTED (the point of
the round); validate_manifest (qa gate 3) exit 0; qa.sh --strict 6 passed / 0 failed / QA GREEN.
-> F: this is the commit you held for — the tree now contains the code that produced your green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:16:49 +10:00
m3ultra
4a80ea8505 Fable (integrator): R26 amended — wave-0 status (F green, tag pending E) + the atlas manifest (F's finding #4, structural)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:54:27 +10:00
m3ultra
29942002eb 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>
2026-07-17 08:43:29 +10:00
m3ultra
533fa32b59 Fable (integrator): round-26 instructions — wave 0 closes the alpha, then v5.0-beta EVERY CRATE DIFFERENT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:43:25 +10:00
m3ultra
4314622c1b Lane G R25: sku-derived slot ids — the re-emit (ledger #3), index-only
Per C §7.2a: items[].id = "sku_<POS sku>" (e.g. sku_20260314070442), never positional. One line at
the emit — the snapshot already carried the sku, I was dropping it.

MEASURED: id overlap with the generic pack 0/120, was 120/120 (F's finding). 120/120 unique, all
sku_-prefixed, none left in the generic packs' rec_ namespace. Id equality discriminates again —
verified through the live loader, not just the file. Index-only as briefed: the two atlas .webp
files are sha-identical (bytes did not move), no town cache touched, no goldens moved. uv,
one-material-per-atlas batching, and shop-pack/town-pack coexistence (120/350) all still hold.

C's reason #2 is the one that outlives the gate: position is not identity. Sell one record out of
crate 550 and rec_0050 silently becomes a DIFFERENT record on the next bake — so a wallet
purchase, a saved dig, or above all tier-2's sold-means-gone would point at the wrong item. The
sku is the shop's own identity for that copy (release_id is the pressing; the sku is the copy). So
the positional ids were a latent correctness bug in the ratified §7 economy, not only a vacuous
gate. Fixed while there are two atlases to migrate instead of fifty.

Conflict #1 closed: E's R25 validator prints "LICENCE: ... FLAG BEFORE ANY PUBLIC/COMMERCIAL
RELEASE" against the top-level US `license` and counts it — atlas-QA OK, 1 licence line shown, 0
warnings. The flag guarding real product photos is unmissable. Nothing owed either way.

Scoped: committed by pathspec on main (G's first as a full lane). F's six staged files and E's
in-flight validate_atlas.py left exactly as their sessions have them.

-> F (your file, your call, not touched mid-flight): interior_mode.js:378's comment now reads
false — ids are no longer positional and no longer collide (0/120). Your gate stays canonical
regardless: a texture URL proves which file the GPU sampled, which id equality never could. Only
the rationale is stale; id equality is now available as a second discriminator if you want it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:28:44 +10:00
m3ultra
a493c0a13c Lane A round 25 (v5.0-alpha): the sweep reconciled with Ruling 2 + the six pins (ledger #1)
All 8 tree reds cleared. selfcheck ALL GREEN 156,211/156,211; scaffold + consistency green; classic
0x3fa36874 and gig 0xb1d48ea1 frozen. F: the tag is unblocked from my side, nothing of mine is pending.

SIX OF THE EIGHT REDS WERE MY OWN GATE ASSERTING RULING 2 AWAY. I built the R24 identity arm on an
assumption that was true when I wrote it and false the moment G shipped: that a godverse cache is ALL
census shops. Ruling 2 makes it MIXED -- a keyed GODVERSE layer plus an inherited OSM texture layer that
G measured to be load-bearing, not cosmetic (census-only Red Hill: 306 m spacing warn + a poster inside a
kerb). So newtown_godverse (18 keyed + 54 texture) and redhill_godverse (10 + 27) were HEALTHY TOWNS MY
GATE CALLED BROKEN -- it fired 54/72 on a good town. Keyed asserts now run PER-LAYER over the keyed subset
only; unkeyed texture shops raise nothing. The validator's "N/M missing" warn dies with it. The one honest
requirement left at that layer: a `godverse+osm` cache must have a NON-EMPTY godverse layer (else it's an
osm cache wearing the wrong `source`) -- still a warn, for the R24 charter reasons that still hold.

THE UNIQUENESS CHECK WAS BROKEN IN BOTH DIRECTIONS. `new Set(all ids).size === shops.length` collapses
every `undefined` into ONE Set entry, so it (a) failed structurally on any mixed cache -- 54 texture shops
became one entry -- and (b) could HIDE A REAL DUPLICATE behind that same collapse. Now over DEFINED ids
only. And per the vacuous-gate law I made it prove it bites rather than asserting that it does:
validateTownCache rejects duplicates at the front door, so a dup can never reach the plan that way -- the
test feeds one STRAIGHT TO THE LIFT (generatePlanOSM does not validate) and asserts the predicate catches
it, with a healthy mixed cache passing the same predicate. A check nobody can watch fail is not a check.

NEW -- THE ORPHANED-ATLAS GATE (my own R24 finding, now enforced instead of filed). The lift legitimately
drops shops (overlap-resolve, counted since R19). Dropping a shop that HAS a committed atlas means real
stock keyed to a shop that isn't in the town -- exactly the failure I filed in R24, and one F's #7b could
only catch in a browser at the end of a round. Now a node gate: for each godverse town, every
stock_godverse/<id>/ on disk belonging to that cache MUST be seated, else FAIL. Subject-aware -- it SKIPs
by name when a town has no atlas, rather than passing quietly.

It already found one, benign today: redhill's lift drops keyed shop 2286 "Empire Revival" (10 keyed in
cache -> 9 seated). No atlas, so nothing is orphaned and the gate passes -- but it prints, because a drop
must never be silent (the R19/R20 ruling). G: stocking Empire Revival at the beta would orphan it. Monster
Robot Party is seated (lot 8), which is the one that matters this round.

THE SIX PINS, cross-checked two ways before landing -- byte-identical across two fresh processes, AND
matching the three values G stated independently in 86e2985:
  newtown_godverse  base 0xcf69b387  gig 0x1e266f60   (moved by G's keyed re-emit)
  redhill_real      base 0x6046700f  gig 0x673785ee
  redhill_godverse  base 0xb2224939  gig 0xc6f80e18
No other golden moved -- the diff is exactly these six lines.

Two lines still print; both deliberate, neither pending:
  ⊘ SKIP newtown_godverse orphaned-atlas -- correct, the only committed atlas (3962749) keys to Red Hill.
  ⚠ redhill_godverse: 1 keyed shop dropped -- the honest disclosure above.

Committed strictly by pathspec: F's six staged files sit untouched, exactly as its session left them.

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:18:37 +10:00
m3ultra
2c1ae37160 Fable (integrator): round-25 instructions — the tag, earned (sweep vs Ruling 2, licence line, sku ids, F closes)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:14:02 +10:00
m3ultra
0608f9c8c1 Lane G R24: G3 §7 RATIFIED — John's write-back ruling, A then B (charter law #4 satisfied)
John ruled at the R24 kickoff: v5.0 ships on Option A (sandbox) — an in-game purchase never
touches real monsterrobot/Square inventory; Option B (reservation via the POS's existing
hold_expires_at) is CHARTERED as a v5.x upgrade behind a hold budget John sets; Option C is not
chartered and happens only as a deliberate business launch, if ever. Recorded inline in §7 with
the options preserved beneath it, since the trade-offs are the record of why A-then-B was chosen.

Written in as binding, because a one-line ruling isn't a mechanism: /reserve + /buy stay ABSENT,
not stubbed behind a flag (a sandbox a config typo can flip isn't a sandbox); the tier-2 reader
connects read-only, enforced at the credential — a role with SELECT on inventory/crate/disc_cache
and nothing else, which carries the §9 PII fence in the same grant; and the gate asserts the write
verbs 404 and the role's grants are read-only, because "we didn't call buy()" proves nothing —
the vacuous-gate law turned on ourselves.

Option B's preconditions are stated so it can't slide in quietly: the hold budget (John's number,
NOT YET SET — B is not startable until it is; a v5.x item, not a v5.0 blocker), the-shop-always-
wins on a race with an in-store Square sale, a staff-visible `held: GODVERSE` reason code, and
soak before ship. Doc header/§10 updated so the whole file tells one story: §7 ruled, the rest
still DESIGN pending both Fables' sign-off, no server code before that.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:33:27 +10:00
m3ultra
86e298531e Lane G R24 (v5.0-alpha): the crate's real street — redhill_godverse, the keyed re-emit, and two contract conflicts
THE SHOP HAS A STREET. redhill_godverse: Monster Robot Party seated on Musgrave Road (verified —
lot 8, type record, godverseShopId 3962749), 37 shops on E's 884 real Red Hill roads. Its
coordinate is John-supplied (Google Maps place /g/11g6rlbbc0 -> -27.4553791, 153.0076244) because
it is derivable from nothing we hold: OSM doesn't know the shop, the census doesn't either,
dealgod's stores/store_entity rows are NULL lat/lng, recordgod has no address. Verified inside E's
bbox and on Musgrave Road's own geometry; my own estimate was ~120 m out.

RE-EMIT (ledger #5 + #2): atlas keyed by godverseShopId with US `license` throughout (British
retired, 0 occurrences) — atlas-QA OK, 120 real records, byte-identical re-bake (the .webp bytes
did not move; only the index). newtown_godverse re-emitted with ids (18 keyed). godverse_town.py:
psycopg2 -> psql, EXTRA_SHOPS roster, and Fable's R22 Ruling 2 finally implemented.

RULING 2 WAS LOAD-BEARING, NOT COSMETIC (measured): census-only Red Hill is unshippable —
9 shops = 306 m spacing warn; +Monster Robot = 10 shops = 261 m warn AND a frontage poster seated
INSIDE edge 1037's kerb (-2.95 m); + the donor's texture layer (37) = no warn, poster passes.
261 m is worse than the 255 m that retired ballarat this round: the alpha's headline town would
have shipped flagged "scattered, not a high street, or retire the town". 9 donor heroes swapped
for census entries (18 in Newtown), counted per the ruling.

CONFLICT #1 — C §7.3 vs E's validator. C says lift provenance to TOP LEVEL, US `license`, British
retired, "exactly what E's validator reads". Measured: it is not. validate_atlas.py:98 reads
NESTED `provenance`; :105 reads `licence` British-first. Lifting-and-retiring as written FAILS the
gate; my R23 atlas already passed it untouched. Same species as the bug that held the tag — a doc
asserting what code does without running it. Resolved: E's check accepts `licence or license`, so
US-nested passes; the four also ride at top level (C's contract + the town-cache convention) off
one source of truth. -> E, one line: :198 PRINTS `licence` British-only while the check accepts
both, so C's mandated US spelling silently stops printing the licence flag — which :199's own
comment calls a human call. Gate passes; evidence line vanished. Fix: .get("licence") or .get("license").

CONFLICT #2 — Ruling 2 vs A's identity gate. NEEDS A RULING; it is why the tree is red (6 of 8).
Ruling 2 makes a godverse cache mixed: a keyed GODVERSE layer + an inherited OSM texture layer.
A's sweep asserts EVERY shop is keyed -> partial FAILs. Texture shops carry no godverseShopId
deliberately: an OSM milkbar has no GODVERSE identity, and minting one from its OSM id pushes a
third id space into a field that already carries two — A's validator calls a duplicate
mis-stocking, charter risk #3's nightmare with Monster Robot's crate as the prize. And A's own two
checks disagree: the validator says "Missing = tier 0 (soft). Duplicate = mis-stocked (hard). They
are not the same failure", while the sweep fails partial keying because it "mis-stocks the rest" —
conflating exactly those two. Proposed (A's call, Fable's ruling): assert coverage over the
godverse layer, not the inherited texture layer.

THE 8 RED CHECKS, ITEMISED: newtown_godverse base+gig goldens (2) moved by the re-emit exactly as
ledger #2 intends — A re-pins, base 0xcf69b387 gig 0x1e266f60; godverse identity x3 per godverse
town (6) = conflict #2. Nothing else. Classic covenant 0x3fa36874 intact. redhill_godverse pins:
base 0xb2224939. Verified live: the shop's 120-record pack and the town-wide 350 pack now COEXIST
through C's type+base fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:31:07 +10:00
m3ultra
5815dfbd58 Lane E R24 (v5.0-alpha): the crate's real street, the honest gate, and Ballarat retired
THE STREET (ledger #3). redhill_real: Red Hill, Brisbane — 36 shops (9 heroes + 27 texture,
-29 subsampled), 884 roads, median NN 73 m. **Musgrave Road is in roads[] (41 ways, primary +
residential)** — that was the point: Monster Robot Party's real shop now has its real street.
One bounded Overpass fetch under the standing authorization; raw snapshots in _raw/, ODbL, C's
mapping. This is G's donor for godverse_town.py -> redhill_godverse.

TWO HAZARDS HANDED TO G/A, both measured against the real geometry (notes R24 §1):
(a) The shop is NOT in OSM — no shop=music node, no name match anywhere in the bbox (checked the
raw snapshot, not the emitted cache). It cannot arrive via the fetch; G must inject id 3962749
from the census or the town ships without the shop the release is about.
(b) THE SEATING HAZARD: A's lift seats on the NEAREST edge, and from G's geocode three streets
are nearer than the addressed one — Zig Zag Street 38.6 m (residential), Craig Street 46.1 m,
Musgrave Road 51.6 m. The shop would seat on Zig Zag St and the headline would quietly become
false. Measured fix (G's call): the point on the real Musgrave centreline nearest G's geocode is
-27.455169, 153.006902 — 0 m to Musgrave, next candidate 43 m. Snaps the street, does NOT verify
the house number; I have no address data for where #147 sits along the road.

THE HONEST GATE (ledger #4). F's catch was right and there were FOUR bugs: the glob used the
published-contract name the runtime can never load (0 matches -> returned 0: it passed identically
whether atlases were absent, misnamed, or perfect); the code read provenance top-level while my own
docstring said nested (G nested it correctly — the gate would have failed G's real artifact);
licence/license; and a stale ceiling. Rebuilt on the "runtime is the authority" ruling: globs
stock_godverse/<id>/stock_<type>_index.json, type in {record,book,toy}. Vacuous-gate law implemented
literally — 0 matches + stray files FAIL (listing them); 0 matches + empty dir prints
"atlas-QA SKIP: no atlases present (0 files matched glob ...)"; plus an id equality
(shop.godverse_id == dir) so it proves it touched its subject. Proved on four cases: empty -> loud
SKIP, misnamed (F's exact bug) -> FAIL, unparseable -> FAIL, G's real atlas -> PASS (120 items,
2 atlases, licence printed). My own test found a fifth hole — the glob still matched the old
stock_shop_* name, so the misnamed case failed only incidentally; added a STOCK_TYPES name check
so it fails for the RIGHT reason. A gate that fails for the wrong reason will pass for the wrong
reason later.

BALLARAT — RETIRED, counted, and a DIFFERENT illness from Toowoomba (notes R24 §3). Toowoomba was
dead everywhere. Ballarat is alive exactly where the game isn't: 103 POIs, densest 400 m disc = 47
of them (Toowoomba's was 3) — but that disc is cafes/clothes/bakeries while the op-shops sit 854 m,
1042 m and 1709 m out. Heroes and density don't coincide, so no bbox holds both. Real builds at
every span: 1.0 km -> 8 shops/184 m/hub 4-8 but ZERO op-shops (a thrift town with no thrift shop);
2.0 km -> 12/186 m/3-12; 3.0 km as shipped -> 20/255 m/4-20. Every row worse than any shipped town.
THE BINDING CONSTRAINT WAS MINE, NOT OSM'S: with 2 heroes the 3x texture cap keeps 6 of 47 texture
POIs and the subsample is spatially blind, so survivors scatter at any span — part of the 255 m A's
warn caught was my own cap's fingerprint. Seeding texture near heroes fixes the class but moves
every town's golden -> filed as a v5.x proposal, not done in a release round.

I OVERSTATED MY R23 ADVICE TO A: the hub fraction is NOT a standalone discriminator — glebe (shipped,
healthy) reads 13% and redhill 14%; a hub threshold would false-flag both. Median NN is the robust
primary; hub is only comparable at equal shop counts (toowoomba 2/12 vs darwin 7/12 was fair because
both had 12). The raw-vs-seated calibration point stands.

METHODOLOGY, recorded because it nearly shipped: I picked Ballarat's span from an offline simulation
that predicted 68 m / hub 5-8, and had already written those numbers into a config comment as fact.
The real build returned 184 m / 4-8 — same counts, different sample. Never trust a re-implementation
of the pipeline's own selection; run the pipeline.

-> A (batched pins): ADD REAL_TOWN_GOLDENS['redhill_real']=0x6046700f and
REAL_TOWN_GIG_GOLDENS['redhill_real']=0x673785ee (the headline town currently only WARNS as
UNPINNED — ungated until you pin it). REMOVE ballarat_real 0xf0a9e8a1 / 0xf097c331 — dead config by
your own R23 note.
-> F: tools/qa/tour_shots.py still shoots town=toowoomba_real, which I deleted in R23 — the v4 tour
breaks on a missing town. Your file, your call. A retirement isn't done when the cache is deleted;
it's done when the consumers are swept.
-> C: built to the runtime + G's real artifact per the ruling; LANE_C_PUB §7 line 271 still names the
contradictory stock_shop_<id>_index.json. I reconcile when your corrected contract lands.

Determinism receipt: the retirement forced a full 21-town rebuild from cached raw (no refetches) and
every existing cache came back byte-identical — only ballarat, redhill and index.json moved. Pack:
21 real towns · 1196 shops · 299 heroes (22 rostered with G's newtown_godverse). Selfcheck ALL GREEN
149,696, synthetic golden 0x3fa36874 unchanged. qa.sh --strict --matrix: 7 passed / 0 failed, MATRIX
GREEN 10x7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:06:00 +10:00
m3ultra
1eb0b45bd0 Lane A round 24 (v5.0-alpha): the identity field — godverseShopId, published first (ledger #2)
The optional shop key that resolves `stock_godverse/<godverseShopId>/`. Schema STAYS v2 (optional field ⇒
every shipped cache still valid, the 21 osm caches untouched by construction). Published to
web/assets/towns/README.md — the doc G and E build to — plus CITY_SPEC. selfcheck ALL GREEN (+15 checks),
synthetic 0x3fa36874 frozen, NO golden moved.

THE BRIEF'S "THE POS SHOP ID IS THE ID — NO MAPPING TABLE" IS FALSE FOR THE ONE SHOP THIS ALPHA IS ABOUT.
G's own docstring says it: Monster Robot Party is NOT in thriftgod's census, so its id is its DEALGOD STORE
ID 3962749, while census shops use thriftgod ids (max 2992) -- two disjoint spaces, deliberately kept clear
of each other. Had I coded "godverseShopId == shop.id" or derived it from the census, the headline shop
could never be keyed. So it is an OPAQUE key: no derivation from `id`, no equality assert, no mapping table
(the R12 gigKey law -- one key, zero mapping; G emits, everyone reads).

THE FIELD HAD TO SURVIVE THE LIFT OR IT WAS DECORATION. plan_osm builds a FRESH shop object and copies
nothing unknown, and the runtime reads plan.shops[], never the cache -- so a cache-only field could never
reach F's per-shop base. It's now carried through, CONDITIONALLY: absent ⇒ the key isn't written ⇒
JSON.stringify byte-identical ⇒ every pinned golden holds until G re-emits (that re-emit IS the sanctioned
move). Proven end-to-end: a keyed cache puts `stock_godverse/3962749/` on the plan with census ids
(101..107) coexisting alongside it.

ARMS SPLIT BY HAZARD, NOT TIDINESS:
- malformed  -> ERROR. It names a path segment; "3962749" vs 3962749 is the same folder but different JSON,
  and that coercion drift is the exact species that broke C's atlas contract (licence/license).
- duplicate  -> ERROR. Two shops keyed to one atlas IS mis-stocking -- charter risk #3 forbids it by name.
- missing on a godverse cache -> WARN + fail-soft to tier 0.

RECORDED DEPARTURE (Fable's to overrule): ledger #2 says validateTownCache "requires" the field; I made
MISSING a warn. Three reasons. (1) The charter's determinism boundary is "world = seeded, frozen, gated;
stock = data tiers" -- an error inverts it, failing to load a TOWN because its STOCK identity is absent;
tier 0 must always boot. (2) Charter risk #3 names the remedy for identity gaps: "fail-soft to tier 0,
never mis-stocked" -- missing IS the soft case, duplicate is the hard one; they are different failures and
now get different arms. (3) Sequencing: newtown_godverse carries 0 ids today, it is G's to re-emit in wave
2 (their namespace, no census DB on m3), so a hard error would red the tree for C and E through all of wave
1 to say what the warn already says.
The requirement keeps its teeth where R23 taught us to put them -- the VACUOUS-GATE LAW: selfcheck FAILS on
partial keying (a half-keyed town silently mis-stocks the rest) and prints an explicit
"⊘ SKIP ... 0/18 shops carry godverseShopId. Subject absent ... binds the moment one id lands" at zero. It
names its subject, proves it touched it with a count, and cannot pass vacuously.

FILED FOR G BEFORE THEY START (measured in wave 1, so it costs them minutes not a wave): redhill_godverse
cannot contain Monster Robot from EITHER source. godverse_town.py takes roads from the OSM donor and shops
from the census, and the shop is in neither -- not in the census (G's R23 finding), and NOT in E's
redhill_real, which I checked the moment it landed: 36 shops, ZERO of type `record`, nothing matching
/monster|robot/. As written the adapter yields a Red Hill of census charity shops while the atlas keys to a
shop that isn't in the town -- F's #7b id-equality gate would fail at the end of the round. It must be
injected explicitly; the opaque-key design exists precisely to serve that case. Second trap on the same
path: redhill_godverse must still clear MIN_TOWN_SHOPS (6) from the census bbox or the cache is rejected
outright (hard error). E's donor is otherwise good: valid, no warns, 73.3 m median spacing.

HOUSEKEEPING: removed toowoomba's two stale pins -- E retired the cache in R23, and a golden for a town
that cannot load can never fire (the vacuous-gate law, one layer down). E's attempt also proved my R23
spacing metric is GAMEABLE: the re-bbox fixed the median (395.7 -> 89.4 m, "passes") while hub density fell
3/12 -> 2/12, the pack's worst. It is necessary, not sufficient -- D's hub test is the complement, and E
used both and refused to game it. Ballarat (255 m) is still flagged and still E's call; I pin what moves.

PINS NOT TAKEN YET, DELIBERATELY: redhill_real is untracked/in-flight (E still editing build_towns.py), and
redhill_godverse + the re-keyed newtown_godverse don't exist until wave 2. Pinning a moving file buys a
false green or a spurious red. The round's law says A pins these BATCHED -- that batch lands when E commits
and G's caches are in.

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:43:50 +10:00
m3ultra
0740b4697d Fable (integrator): round-24 instructions — the alpha completion (contract fix, identity field, redhill, honest gates) + two new laws
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 00:21:51 +10:00
m3ultra
57973e0540 Lane F R23 (v5.0-alpha): the alpha gate — HELD. The crate is real; it has no street.
v5.0-alpha is NOT tagged. #7a is not a gate that failed, it's a gate that cannot be run:
G's 120 real records are in the tree and byte-identically re-bakeable, and nothing in the
game can reach them. R18's law — release rounds don't ship known breaches.

GREEN (everything except the crate): qa.sh --strict --matrix 7 passed / 0 failed / 0 warn ·
MATRIX GREEN 10 towns x 7 gates · selfcheck 152,015/152,015 (was 161,300, E retired
toowoomba) · flags harness green, so ?classic=1 still holds through the merge · tier 0
?noassets zero fetches + zero console errors (the offline law holds) · tier 1 (v2
town-wide) resolves. Tier 1 (v5, per-shop) is NEVER FETCHED.

FINDING 1 — the atlas contract contradicts itself. LANE_C_PUB:271 publishes
stock_shop_<id>_index.json; C's own loader (stockpack.js:25) fetches
${base}stock_${type}_index.json. Incompatible. G built runtime-compatible (which is why
C's base-override check passed); E built contract-compatible (which is why E's validator
can't see it). Nobody was wrong — the contract was. F recommends amending to G's shape:
it's the only one the runtime can load, and it costs G nothing.

FINDING 2 — the gate built to catch that was blind and green. validate_atlas.py globs a
filename that can't exist at runtime, finds nothing, and returns 0 ("No atlases yet =>
clean pass"), which validate_manifest.py:158 propagates. It conflates "no atlases exist"
with "no atlases matched my glob" — the first is a clean pass, the second is the exact
failure it was written to catch. Pointed at G's index by hand, deeper drift surfaced:
E's line 68 reads provenance top-level while E's own docstring says nested (where G put
it), licence vs license, and a ceiling stale by one round.

FINDING 3 — #7a would have passed vacuously. Walked into The Vintage Record (real shop,
real road) via DBG.enterShop: the crates filled with real covers — from E's 350-item
town-wide v2 pack, not the crate. Zero requests to stock_godverse/, ever. Eyeballed as
written, the gate goes green and proves nothing. Two vacuous gates in one round, same
species: both test the presence of a thing rather than the identity of it.

R24 — three gaps, none F-only: A+G land a godverse_id (verified live: every godverse shop
reports null; the schema has no such field) -> E builds redhill_real (one Overpass fetch,
E's file, outward-facing — not F's to invent) -> C keys the pack cache by type+base
(today a hit returns before base is read, so two stocked shops collide and fail-soft
breaks) -> F passes the per-shop base and re-gates. F deliberately did not build its half:
untestable with no street and no identity field. Filed, not guessed.

Also: A's spacing warn earned on day one — ballarat now warns at 255 m, so the thin tail
has a second member. The README v5 teaser is held with the tag; they're one claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:05:44 +10:00
m3ultra
43b035aa2e Lane F R23: execute the mid-round merge of G's branch (charter law #5)
The merge amendment says G's branch merges mid-round once Fable-m3 reviews. The review
happened (the shared checkout was parked on lane-G-godverse for it) and C verified the
atlas contract from a temp worktree — deliberately pre-merge — and PASSED. But the merge
itself never executed: main had no web/assets/stock_godverse/** at all, so the round's
headline artifact was absent from the branch F is asked to gate and tag.

F executes it as the integration lane, last in the round with the tree in hand.
Disjointness verified before merging, not assumed:
  G since fork:    G-progress.md, docs/G3_ECONOMY_DESIGN.md, pipeline/_godverse/*,
                   pipeline/godverse_stock.py, web/assets/stock_godverse/3962749/*
  main since fork: C-progress.md, docs/LANES/LANE_C_PUB.md
  overlap:         none. Zero conflicts, as charter law #5 predicted.

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:42:30 +10:00
156 changed files with 266369 additions and 231432 deletions

View File

@ -4,6 +4,25 @@
plus the game shell. It runs on my hand-written fixture **and** auto-integrates with Lane A's
`generatePlan` (which landed mid-session). Everything below was verified live in a browser.
## Round 30 (v7.0-alpha ledger #4) — THE COLLECTION UI + SLEEP surface · qa GREEN 6/6
The crate panel + game bar in `hud.js` (my DOM-overlay patterns), on F's §30.1 `PROCITY.game`
contract — and verified against F's REAL committed core (`207bcff` landed mid-round; my test
snapshot diffed byte-identical to it — **nothing stubbed**, unlike C's wave-1 run).
- **Game bar** (bottom-right): `day N · $cash` · `CRATE n` (count in the standing HUD; opens the
panel, `C` toggles) · `☾ SLEEP``game.sleep()`, enabled any time (alpha).
- **Crate panel**: cover thumb (pack-atlas CSS crop off the entry's sku — per-shop godverse base or
town-wide; parody = type glyph by design), title/artist, `paid $p · where · day d`, newest first,
header totals. 50-row rebuild 0.9 ms; 0 draws (DOM-only).
- **Construction-gated on the game object itself** (lazy, R19-selector style): `?classic=1`/`?game=0`
⇒ zero game DOM, zero listeners, help line byte-identical, 0 console errors (measured, clean tabs).
`?noassets` ⇒ 0 stock fetches, placeholders stand.
- **SLEEP proven end-to-end**: day 1→2, weekNight 0→1, wake DAWN seg 0, `procity-save/1` on disk,
next boot loads it and the UI reads it. **venue.js §30.4 filing fixed as filed** — compat
`venueShopId` is now a live weekNight-keyed getter (116 → 490 across a sleep, measured).
- Full measurement table + honest notes: LANE_B_NOTES §30. Verified on a port-isolated no-store
:8151 snapshot server (not :8130/:8144 — Lane F was mid-flight in the shared tree).
## Round 9 (Fable's ROUND9 → Lane B) — v2 tour bookmarks + tram (`?tram=1`) · qa GREEN
Ship-v2.0 round. Two deliverables, both v2-law clean, `qa.sh --strict` GREEN, flags-off byte-identical.

View File

@ -3,7 +3,411 @@
*Status: **v1 complete & verified**. Standalone interiors library + test page. Every shop door opens
into a unique, seeded, themed interior, generated on demand in ~4ms, byte-identical every revisit.*
Last updated: 2026-07-16 (round 22) · owner: PROCITY-C · reviewer: Fable
Last updated: 2026-07-18 (round 30) · owner: PROCITY-C · reviewer: Fable
---
## Update 2026-07-18 (round 30, v7.0-alpha THE SAVE AND THE CRATE) — the sell counter: cash comes back, always less
R30 ledger #2 — contract first (LANE_C_PUB §9, its own commit), implementation second. F's save core
(ledger #1) had **not landed** when this was built: no `save.js`, no `window.PROCITY.game` anywhere in
the tree — so everything below is built to the R30-published shape and verified against a **stub** of
exactly that shape (noted loudly below).
### The contract (§9) — the one number and why
**`sellOffer(p) = min(p 1, max(1, floor(p × 0.5)))`, never < 0.**
- **0.5** is the secondhand-dealer margin — the keeper pays half of what he'll sticker it at.
- **`min(p 1, …)` is the no-pump law made STRUCTURAL:** the offer is strictly below `pricePaid` for
every `p ≥ 1`, so buy-then-immediately-sell loses ≥ $1 per round trip **by construction** — no
balance retune can break F's monotonic-loss gate without violating the contract. At `p = 1` the
offer is $0 and SELL disables (the keeper won't buy what he can't sticker).
- **Alpha basis = `pricePaid`** (every alpha buy is at asking price, so it IS the buy-side price the
player faced; there are no guide bands yet). **Beta swaps the basis to `bandLow`, not the shape.**
- Matching is **type = type, strict, fail-closed**: an untyped item sells NOWHERE (a matcher passing
untyped items would "match" everywhere — vacuous-gate law applied to a matcher).
### What shipped (C's files only)
- **`web/js/interiors/sell.js`** — `createSell()` + pure `sellOffer` / `sellableIn` / `nearCounter`.
DOM-only (zero draws, zero GPU), the dig's manila price-sticker mirrored to the left with a green
offer sticker; one item at a time, ` ` cycling, ✕/Esc; till rings from the counter emitter (R28
one-shot pattern). Same consumer pattern as `dig.js`: the single side-effect is `onSell(item,
offer)` — **C never mutates `game.collection`** (read-only law, §9.3).
- **`web/js/interiors/wallet.js`** — `wallet.sell(item, offer)`: the credit mirror of `buy()`.
This file never prices; the offer comes in from `sellOffer`.
- **`web/interior_test.html`** — E-at-counter routing (bin → dig · counter → sell · shelf → buy) gated
on `window.PROCITY.game` presence (the in-game classic-pure law, exactly); `?sellstub=1` stubs the
published game shape for verification until F's core lands.
### Verified (fresh context, no-store server on :8144 — NOT :8130, zero console errors throughout)
- **The sale:** record shop seed 1990, at the bench (`nearCounter` true at 0.9 m, false across the
room) → card `THE FIBROS — "Servo at Midnight" · you paid $24 · offer $12` → SELL → **cash 163 →
175 (+$12 < $24 paid)**, collection 4 → 3, sold item gone, toast + till fired.
- **Type fence:** stub carried 2 records + 1 book + 1 untyped item. Record shop cards **exactly the 2
records**; book shop cards **exactly the book** ($7 on $15); the untyped item cards **nowhere**.
- **The disabled edge:** the $1 record offers $0 → button reads NOT WORTH BUYING, disabled.
- **No-pump round-trip (C's mini-gate, precursor to F's #5):** buy-then-sell through the real wallet +
card path, prices {2,3,5,9,24,60,61,137} → **8/8 strictly negative** (1,2,3,5,12,30,31,69).
- **The gate discriminated first** (vacuous-gate law, proven live): my first loop pushed a 999-priced
item into the collection **without checking `wallet.buy` succeeded** — broke wallet, no debit, then
a +$499 "sale". A pump — from an item never paid for. **Finding for F below.**
- **Fail-soft:** boot with no stub (= no game layer) → E at the counter does nothing, `open()` false,
zero DOM, zero errors. `dispose()` leaves 0 panels/0 toasts in the DOM.
- **Budgets:** draws with the card open ≡ closed (16 ≡ 16 — DOM is zero-draw); `buildInterior` and all
room code untouched this round; 50-room soak re-run green in the same session: **avg 3.4 ms · worst
8.2 ms · leak geo 0 / tex 0 · determinism identical · worst draws 73 ≤ 350**.
### Findings / asks (→ F, for the save core + gates)
1. **The collection item needs `type`** (registry stock type, stamped at buy time). The R30 shape
(`{townKey, godverseShopId|shopId, sku|slotId, pricePaid, dayFound}`) doesn't carry it, and without
it **nothing is sellable anywhere** (my matcher is fail-closed on purpose). `title`/`artist` are
strongly asked too (B's collection UI needs them; my card falls back to the sku label — honest, ugly).
2. **`pricePaid` must only ever be written by a SUCCESSFUL debit.** The no-pump law's structure
assumes pricePaid was actually paid — my broken first loop showed a collection entry that skipped
the wallet debit converts straight into a money pump. F's no-pump gate should include this negative
control: a collection entry minted without a debit must be impossible (or the gate must fail).
3. **The removal handoff:** in-game, the sold item must leave the collection **via the game API**
`onSell` is where F calls it. My harness splices its own stub (its array, its right); C's modules
never touch game state. When F publishes the API name, §9.3 references it verbatim.
R28 §Lane C (ledger #3) — my own idea, parked since R7, brief states acceptance only. Wave 1, alongside E+B.
### The trap my own parked note set
v3.0's deferred note said: *"`userData.audioEmitter = musicKey` so Lane B can position/spatialise the
bed"*. **Both halves were wrong**, and the second one is dangerous:
1. **The shape.** The engine holds no reference to the room group — it would have to traverse a scene it
cannot reach. Emitters belong on `room.audio` as data, where they ride the **existing**
`playInterior(room.audio)` call: no new lifecycle, no traverse, no dispose hook, no F plumbing.
2. **The method.** "Spatialise" invites a `PannerNode`. **Nothing in this tree sets `ctx.listener`** (zero
hits, whole tree), so a panner resolves against a listener frozen at (0,0,0) — and since rooms are built
in room-local coords at the origin, that is *the room's own centre, forever*. Measured with
`OfflineAudioContext` on a real pub stage bed:
| wiring | RMS at door | RMS at stage | ratio |
|---|---|---|---|
| panner, listener never set (**the engine as it ships**) | 0.106905 | 0.106905 | **1.0000** |
| panner + listener tracking the player | 0.055665 | 0.500000 | 8.98 |
It **plays, throws nothing, warns nothing, and never changes by one sample as you cross the room**
while failing at the only thing it exists to do. *"Routed through a panner?"* yes. *"Audible in a
venue?"* yes. *"In a shop?"* yes. All green, all worthless. **The only assertion that can see this is a
DELTA: gain at the door ≠ gain at the stage.** Vacuous-gate law, 4th application — and this one had no
author to blame but me. There is no listener code anywhere to teach a lane otherwise.
**The house idiom was already right**: tram rumble (R=55), door spill (R=9), gig spill (R=26) are all
hand-rolled distance-gains against `PROCITY.camera.position`. No listener, no panner. Emitters match it.
### What shipped (C's files only)
- **`room.audio.emitters`** = `{ stage?, counter? }`, room-local, 3-dp, **18/18 same-seed deep-equal**.
`r` (reach) is the **room diagonal, not a constant** — measured diagonals 9.8115.63 m, and scaling to
the room keeps door→stage proximity in a tight **0.420.59** band from a pokey band_room to a hall pub.
`floor` is the room-filling residue, **never 0** — a PA fills a pub; what changes as you walk in is the
*balance*, not the presence. Swing **2.654.52 dB** (John R20: *subtle changes are great*).
- **The till rings from the counter***audible positioning in a normal shop with **no engine change***,
via B's already-public `playSfx(key,{gain})`: C does its own distance math, hands B a scalar. Measured
through the real BUY button in a real record shop: **0.9489 @ 0.60 m vs 0.6436 @ 4.19 m = 3.37 dB**;
negative control (no emitters) fires at exactly **1.0**. `sfx-till.ogg` had been on disk since 15 Jul and
had **never once played**. Routing, not content — exactly as the brief said.
- **`?mute=1` now silences the crate riffle.** See below.
**Why the venue half is an ask, not a ship:** a **one-shot** resolves its gain once, at fire time, from a
position C already knows. A **bed** must be re-gained every frame inside the engine's `update()` loop —
B's file. The split is structural, not political. B got a ~12-line paste-ready diff in the house idiom; C
did not touch audio.js. The letter of "consulted not modified" could have been satisfied by reaching
through B's exposed `state.layers` diag to rewire its graph from outside — **that backdoor is strictly
worse than the edit it avoids** (invisible to B, breaks on any change of B's). Refused.
### Two bugs found on the way
- **F drops emitters on the one night they matter.** `interior_mode.js:289` rebuilds the spec on a gig
night: `{ musicKey: ra.gigKey, toneKey: WALLA_KEY }` — every unnamed field dies. Measured on a real
gig-night pub: `quietNight_emittersSurvive: true`, **`gigNight_emittersSurvive: false`**. Silent, fail-
soft, no error. -> F: don't just spread `ra` — pass emitters **deliberately, gig-night only**, because on
a quiet night the radio and room-tone genuinely *are* room-filling.
- **`?mute=1` never silenced the riffle (R5 → R28).** `dig.js` opened a **second, private AudioContext**
straight to `destination`, breaking both of audio.js's stated laws (*ONE AudioContext*; *mute forces
silence*). It survived three epochs because **no gate asserts mute ⇒ silence**: the engine returns its
silent surface and looks honest *from its own side*, while the sound came from somewhere it never knew
existed. *A gate that asks the engine whether it is muted can only ever answer yes.* Fixed by reading
B's **public** `.muted` getter (consulted, not modified), URL fallback when standalone. Measured with a
positive control so the probe is provably able to see sound: live 1 ctx / 1 source; **muted 0 / 0**.
Blips stay procedural on purpose — zero-fetch, deterministic, and they still work under `?noassets=1`
where a sampled riffle cannot. Only mute was broken; only mute was fixed.
### Harness honesty (again)
First till rig reported a clean 3.37 dB — from a **contaminated harness**. `createDig` appends a panel per
call and `close()` doesn't remove it, so `querySelector` was clicking **trial 1's stale button** every
time. The tell was `panelUp: false` on all three trials, and the **negative control** returning 0.6436
where it must return 1.0. The gains happened to match the formula exactly — I still refused them and
re-ran isolated (`panelCount: 1`, `panelUp: true`, control **1.0** ✓). R27's lesson holds: *never report a
number from a rig you have watched lie, even when you like the number.* The control is what caught it.
QA GREEN (6/0/0/0). Docs: **LANE_C_AUDIO.md → v3.1** (contract + both asks + every measurement above).
---
## Update 2026-07-17 (round 27, v5.0 THE LIVE CRATE) — the tier-2 seam + LANE_C_PUB v5-FROZEN
R27 §Lane C (ledgers #2 + #6). Wave 1 — ran after F tagged `v5.0-beta` (`78848ed`, 7/0/0).
### The seam ruling (§8): bless the direct wire — but the *form* was the real question
Fable asked: publish a post-build hook, or bless the direct wire? **Answer: bless it — no lifecycle hook.**
F already holds `currentAdapter` (`interior_mode.js:67`) and passes it to `dig.open()` **per dig** (`:111`),
so the live read lands, F updates the pack, and the next riffle picks it up. Read not landed / slow / dead ⇒
the adapter is simply the tier-1 pack ⇒ the dig never blocks. No C lifecycle change.
**But the question underneath it — *how* `gone` applies — is C's, and the obvious wiring is wrong.**
`pickRealOffers` indexes `pack.items[(rr()*len)|0]`, so **filtering the array reshuffles the crate**.
Measured across 5 crates: **one record selling changed 21 OTHER records** (6/2/2/6/5 collateral). Pick-then-
omit — draw from the stable full list, drop gone *after* — has **0 collateral**: a crate with 3 sold shows
13, not a different 16. Selling one record must remove one record, not re-stock the shop.
**And the trap: both forms hide the sold record.** So F's planned sandbox assertion — "a real `gone` item
never renders" — **goes green on the wrong wiring**. It cannot see the 21. → **F: assert on collateral (the
survivors are unchanged across a `gone` delta), not absence.** That's the vacuous-gate law's third
application, and the cheapest to miss, because it passes.
**Landed** (`dig.js`): `pack.gone` (Set|array) omitted at pick time; **F's invariant published** — update
item *fields* in place (live price) freely, **never add/remove/reorder `items[]`**; that identity+order IS
the seeded pick's stability (the same lesson as R25's positional ids, one layer up).
**Determinism boundary sits exactly here:** server down ⇒ byte-identical tier 1 (gate-able); server up ⇒ the
crate legitimately changes — tier 2 is non-deterministic *by design*, so **no gate may assert dig determinism
with the server up**, and `pack.gone` never enters a seeded stream or a golden.
**Scoped honestly:** the bin fan is decorative (a different seeded pick from the dig's offers), so a sold
cover may linger there until rebuild — that's when I'd publish a post-build hook, and the epoch's claim
doesn't need it, because *the crate you riffle is true*.
**Verified** — pure-function measurement for the collateral claim, then end-to-end through the real dig's
public surface (pull → Esc → scroll → pull): sold record gone · **survivors 4/4 · collateral 0** · `items[]`
untouched at 120 · live in-place price shows on the next pull. *My first dig harness returned the same title
4× (I never un-pulled), so its "survivors" number was a harness artifact — I re-ran it properly rather than
report a metric I'd just watched lie.*
### Docs freeze (§6): LANE_C_PUB is **v5.0-FROZEN**, whole document
Marked, with the case law kept at the top rather than buried: three of this doc's amendments exist because a
rule of mine met a case written *after* it (§7's naming vs its own loader; §7.2a's `sku_` before mint) — and
both times the lane building to my rule took the red. The header now says it plainly: **when a line here
meets a case it didn't foresee, the line is the stale thing — amend it, don't bend the case.**
---
## Update 2026-07-17 (round 26 wave 4) — the mint-id fence: my rule was the stale thing
F's fourth hold traced to **my** §7.2a. I wrote "`sku_`-prefixed for real-stock packs" in R25 — **before mint
existed** — so it names one space where there are two. E implemented it faithfully and its validator did
exactly what I told it to: demand `sku_` on 14 mint crates. **The check wasn't wrong; my rule was.** Same
shape as R23, and I'll say it plainly: this is twice now that a contract line of mine met a case written
after it and the lane building to it took the red.
**Amended (§7.2a): the id form is sourcing-scoped, and the prefix IS the fence.**
| `sourcing` | id form | the space it names |
|---|---|---|
| `real` | `sku_<POS id>` | the shop's POS inventory — a physical copy on a shelf |
| `mint` | `mint_<dealgod listing id>` | a dealgod listing — a plausible stand-in, not that shop's stock |
**Why G was right to refuse the easy green.** Filing dealgod listing ids under `sku_` would have gone green
in one line and recreated the R24 collision **inside the economy layer** — where **tier-2's sold-means-gone
looks up POS skus**. R24's collision entered the *wrong shop*; the same collision in the money layer **sells
the wrong record** — a real copy, out of a real shop, to satisfy a minted stand-in. A prefix that lies costs
money, not a gate. Blessed as design: disjoint namespaces, and no gate, lookup or economy path may treat
them as one (same standing note as plan-ids vs godverse-ids).
**Verified against every crate in the tree before handing E the line:** 15 crates (1 real + 14 mint) —
**0 fails** under the amended rule (prefix matches `sourcing`, ids unique within pack), and **0 ids appear in
both spaces** across 344 distinct ids. The fence already holds in the data; only my wording didn't.
**→ E:** assert the form against `sourcing` (you already read it at line 178) — `sku_` for real, `mint_` for
mint; a crate wearing the other's prefix FAILS. That's **stronger** than the check it replaces, not looser.
**→ F:** 6/1/0 should flip to 7/0/0. Doc-only from C; no code, no atlas bytes, no goldens.
---
## Update 2026-07-17 (round 26, v5.0-beta EVERY CRATE DIFFERENT) — three contract lines + the price sticker
R26 §Lane C, wave 1 (ledgers #1, #4, #6). Contract lines published FIRST — G and E build to them.
### The brief said `tier`. It can't be `tier` — that name is taken twice (§7.2b)
G's atlases already emit **`tier: 1`** = the **charter's ladder rung** (0 parody · 1 static-real · 2 live),
and `kind` is already the stock type. **A mint atlas is also rung 1** — same mechanism (a statically baked
per-shop atlas); only the data's *origin* differs. Real-vs-mint is an **orthogonal axis, not a rung**.
Overloading `tier` would put two meanings in one name — the same species as the plan-id/godverse-id
collision that just became a standing note, and as R25's positional ids. **Ruling: top-level
`sourcing: "real" | "mint"`, REQUIRED; `tier` stays the numeric rung.** A mint index MUST NOT carry
POS-snapshot fields (the separation IS the honesty); gates read `sourcing`, and an index without it FAILS.
### `condition` isn't in the index — the ledger assumed it was (§7.2c)
Ledger #4 said the card gets "the fields the atlas index already carries (artist/title/price/**condition**)".
It carries artist/title/price/price_band — **not condition**. G's *snapshot* has `condition` + `sleeve_cond`
and drops them at emit, so `pickRealOffers` mapped `cond: ''` and the card rendered dangling separators
around a hole. That *was* the "database row". Ruled: `condition`/`sleeve_cond` are **optional** index fields,
emitted when the source has them; **no invented grades** on mint items — a plausible-but-fictional grade is
exactly what `sourcing` exists to prevent.
### The atlas manifest (§7.2d) — existence declared, never probed
`web/assets/stock_godverse/index.json` is the authority for which shops carry atlases; **no entry ⇒ no
fetch ⇒ zero 404s**, so the zero-console-errors law holds with no attribution exception. Shape + both-ways
consistency (atlas without manifest entry FAILS, and vice versa) in §7.2d. A tree with no godverse atlases
and no manifest is a legitimate silent zero-shop state, not an error.
### The price sticker (ledger #4, code)
The pull panel is now the shop's **price sticker**, not a data readout: manila card, slight rotation +
shadow, typewriter caps, italic title, a rubber-stamped condition grade, and **the round yellow $ sticker
the sleeve art already uses** (dig.js's own canvas language — the panel now echoes it instead of fighting
it). Fields compose only when present: **a missing field never leaves a stray separator.**
**Verified — five card shapes, both star branches, fresh context:**
| case | card |
|---|---|
| real grail | `$28` · Deep Dish · *Penetrate Deeper* · `VG+/VG` · **★ GRAIL** |
| real standard, graded | `$15` · `VG+` · `STANDARD` |
| real ungraded (today's index / mint) | `$15` · `COLLECTOR` — grade omitted cleanly |
| parody | `$15` · THE VERANDAHS · *Tinny* · `NM` · `1973 PUNK` |
| parody rare (seed 4) | `$16` · HILLS HOIST · *Servo at Midnight* · `VG` · `1973 POP` · **★ RARE** |
**The verification caught a defect in my own card:** a real *grail* rendered **"GRAIL ★ GRAIL"** — `s` carried
the price_band *and* `rare` derived from that same band. (Parody is immune: its `s` is a genre.) Fixed at the
mapping — grail becomes the ★ stamp and leaves `s` empty. And I did not leave the parody-rare branch on
assumption: scanned seeds until a rare pull appeared (seed 4) and asserted it says RARE, not GRAIL. An
unexercised branch is an unproven one.
**Budgets:** the card is DOM — zero draws, zero tris, no 3D change. Ceilings untouched.
**→ G:** `sourcing` + `condition`/`sleeve_cond` + the manifest. **→ E:** assert `sourcing` (absent ⇒ FAIL),
the mint/POS-field separation both ways, and manifest↔files both ways. **→ F:** consult the manifest before
fetching — that's what retires the 404 attribution exception.
---
## Update 2026-07-17 (round 25, the tag earned) — sku-derived slot ids (ledger #3, C's one line)
R25 §Lane C: one contract line, published first so G re-emits against it. **§7.2a added** — F's id-collision
finding made structural, while there are two atlases to migrate instead of fifty.
**The collision, measured:** both packs number items positionally from zero, so **all 120 godverse ids are
also generic-pack ids — 120/120 overlap.** An id-equality gate cannot discriminate which pack a rendered
item came from; that's exactly why the brief's own #7a spec was vacuous. (The vacuous-gate law's first
application broke the law — and my own R23 metric had the same disease. It's a persistent species.)
**The rule (§7.2a):** `items[].id` MUST derive from the source's stable key, never from position, and MUST
be unique across ALL packs. Real-stock packs: **`id = "sku_" + <POS sku>`** — G's snapshot already carries
`sku` per item (e.g. `20260314070442`), it's just dropped at emit, so this is index-only. Generic packs keep
`rec_NNNN` as their namespace; real-stock must not emit into it.
**I gave it two reasons, and the second is the one that outlives the gate.** The collision is the presenting
symptom. The deeper defect is that **position is not identity**: a positional id renumbers on every re-bake,
so selling one record out of crate 550 silently makes `rec_0050` a *different* record. Anything holding an
id across snapshots — a wallet purchase, a saved dig, a gate assertion, and above all **tier-2's
sold-means-gone** — would point at the wrong item. The POS sku is stable because it's the shop's own
identity for that copy (`release_id` identifies the pressing, not the copy). So this line isn't only a gate
fix; it's the identity tier-2 will need, bought now for free.
**Scope kept honest:** index-only, no atlas bytes, no town cache, no goldens. `id` isn't read by the loader
(§7.3) — the dig/buy consumers and the gates read it, so no C code changes. **→ G:** re-emit with `sku_`
ids. **→ E:** assert the form (present, unique within the pack, `sku_`-prefixed for real-stock).
*Process note:* F's six files were staged in the shared tree mid-flight; committed by explicit pathspec, so
F's session is untouched.
---
## Update 2026-07-17 (round 24, THE ALPHA COMPLETION) — the contract fix (ledger #1, the root cause)
R24 §Lane C. **The root cause was mine and it's closed.** F held the tag because my published atlas
contract and my own loader were mutually incompatible; the ruling — **the runtime is the authority** — is
now the section's first line. Published FIRST (half-day rule; G and E re-emit against it).
**What was actually wrong.** My R22 §7 invented `stock_shop_<godverseShopId>_index.json`. The loader
(`stockpack.js:25`) fetches `` `${base}stock_${type}_index.json` ``. **An index named per my contract can
never be fetched by my own code.** So G built runtime-compatible (correct), E built contract-compatible
(also correct — the contract said so), and E's validator globbed `stock_shop_*_index.json` → **zero files
→ vacuous pass**. Nobody was wrong; the contract was. I wrote it, so I own it.
**§7 rewritten against the code (LANE_C_PUB):**
- **Authority rule:** `stockpack.js` IS the contract; if the doc and the loader disagree, the doc is a bug.
- **Layout ratified as G shipped it:** `assets/stock_godverse/<godverseShopId>/stock_<type>_index.json`.
`type` is load-bearing (it picks the slot via `SLOT_FOR`: record→sleeve). `stock_shop_*` is **retired**
**E: the validator glob becomes `stock_godverse/**/stock_*_index.json`** (that's the vacuous gate's
actual cause).
- **Runtime-read vs runtime-ignored, stated exactly.** Reads `atlases[]` + `items[{atlas, uv}]` (uv origin
**top-left**; the loader flips V — don't pre-flip). Ignores `version/atlas_px/cell*/count/kind/tier/shop/
crate/provenance` entirely.
- **The provenance drift dies (§7.3):** since the runtime reads none of it, the authority is E's validator
+ the 23 shipped town caches — both use **top-level `license` (US), `attribution`, `generator`,
`snapshot`**. British `licence` retired; **G lifts+renames those four**, keeps its rich nested
`provenance{}` (source/sha256/determinism) as colour. `atlas_px` stays REQUIRED (E checks it against real
dims — a genuine falsifiable check). `cell*` informational: **C reads `uv`, never `cell`** — and don't
shrink the cell for VRAM, the dig renders covers close-up.
**Code landed (the `type+base` keying, F's filed item):**
- `stockpack.js`: cache identity is now `` `${base}|${type}` ``; `getStockPack(type, base = DEFAULT_STOCK_BASE)`.
- `interiors.js`: new **`opts.stockBase`** — the seam F wires (`buildInterior(shop, THREE, { stock:'real',
stockBase: 'assets/stock_godverse/<id>/' })`). Omitted ⇒ town-wide packs, byte-identical to today.
- Pre-v5 callers untouched (`getStockPack('record')` still means the town pack).
**Verified fresh — and I had to fix my own metric first.** R23 I claimed "8/8 atlas-backed"; that checked
`material.map` exists — **but a parody canvas has a map too**. That's the same species as the vacuous gate
I'd just been handed. Re-asserted falsifiably:
- **Every sleeve samples G's atlas FILE** — texture sources are exactly `stock_godverse/3962749/
stock_record_atlas_00.webp` / `_01.webp`; pack items ≡ index items **by id**. Titles are real
(*Yoshitoshi Artists In House We Trust / Deep Dish Penetrate Deeper*).
- **Negative control (what makes it a gate):** the parody room's stock textures are `(canvas, no src)` and
touch the godverse atlas **zero** times — the metric discriminates.
- **Collision FIXED:** shop pack (120 items) + town pack (350) coexist as distinct objects.
- **Fail-soft FIXED with a populated cache:** missing base ⇒ `null` ⇒ parody (the R23 correctness bug —
an unstocked shop inheriting a cached crate — is gone).
- Ceilings: **98 draws** with real covers via `opts.stockBase`; `pathOK`; 0 carves; **0 console errors**.
**Scoped, not slid:** the **LRU + dispose** (§7.6) lands at **v5.0-beta**, not here — with one stocked shop
there's no accretion, and safe eviction needs a room-exit hook (a live room holds the pack's shared
material), which arrives with F's per-shop preload. Written into the contract so it can't slide silently.
**→ F:** the seam is `opts.stockBase`; §7.5 has the call. **→ G:** layout ratified, re-emit provenance field
names only (§7.3). **→ E:** the glob (§7.1) is the vacuous gate's cause.
---
## Update 2026-07-16 (round 23, v5.0-alpha THE FIRST REAL CRATE) — the crate in the room (ledger #3)
R23 §Lane C. **Verdict: the contract holds — G's atlas needs no re-emit.** Verified against G's
`stock_godverse/3962749/` (120 real records from Monster Robot Party's DEEP HOUSE crate 550, 2 × 2048²,
cell 256²) in a fresh context. Ran **before** Fable's merge deliberately — if the contract failed, that
blocks the merge. It didn't.
**PASS — the first real crate renders in a PROCITY room:**
- **Resolves through the existing `stockAdapter` path** via a base override — no C code change needed for
the single-shop alpha. 120 items · 2 atlases · **8/8 stock meshes atlas-backed** (real covers). The
sleeves are legible on screen: *peperecordings · DEEPCHILD · "39" · mff FREAKS · The Angels Instinct feat
Charlie Ashcroft* — a real deep-house crate.
- **Ceilings hold:** **118 draws** with real covers (≤350 law), `pathOK`, 0 carves.
- **Fail-soft holds** (clean cache): missing atlas → `null` → parody (20 parody meshes, 110 draws); no
adapter (the `?noassets` shape) → parody, zero fetch. **0 console errors.**
- **G's index is compliant:** `items[]` sorted by id (my R22 determinism ask honoured), provenance present,
`uv ⊂ [0,1]`, 2 atlases = exactly my cap, `cell_w/cell_h` harmless (C reads `uv`, never `cell`).
**I got a number wrong, and the first real data caught it (LANE_C_PUB §7.2 CORRECTED).** My R22 ceiling
said "≤8 MB decoded" — derived from assuming a "typical shop" is ~16 items @1024². **A real record shop is
120 records.** At the contract's own 256² cell that's 2 × 2048² ≈ **32 MB**, 4× my figure. The *structural*
rules held (≤2 atlases; 2048² for 60+ items — G landed exactly on the cap); only the MB number was wrong,
because it described a shop that doesn't exist. **Corrected:** ≤2 atlases/shop @≤2048², **LRU = 1 shop's
atlas set resident ⇒ ≤32 MB peak** (still bounded; still far better than the 181 MB unbounded accretion E
measured). And explicitly: **don't shrink the cell below 256² to save VRAM** — the dig shows a cover
close-up; resolution is the one thing the player actually sees.
**G's ask #1 CONFIRMED — and worse than reported (LANE_C_PUB §8: C's spec, C's fix).** `stockpack.js` keys
`_packs`/`_resolved` by **`type` alone**, so `base` is **ignored on a cache hit**. Measured:
`preloadStockPack('record', {base:'…/3962749/'})` then `preloadStockPack('record', {base:'assets/models/'})`
**returned the identical object**. Consequences: (1) two stocked shops collide — the 2nd serves the 1st's
crate (blocks v5.0-beta, as G said); (2) **it breaks the fail-soft law** — an *unstocked* shop of a cached
type inherits that crate instead of parody. The second is a correctness bug, not a mix-up. Seam spec
published (cache key = `type + base`; per-shop preload on entry; LRU=1 + dispose; **C consumes whatever
identity field lands — C does not invent it**). **C owns and lands it** — the §7 promise coming due.
**Two gaps block F's gate #7a (neither is C's; both flagged):** (1) **the crate has no street**
`redhill_real` doesn't exist and monsterrobot isn't in any town's `shops[]` (G's ask #2 → E/Fable);
(2) the shell's `interior_mode.js:192` calls `getStockPack(shop.type)` with `base` fixed to
`assets/models/`, so it can't reach a per-shop pack until §8's seam + the identity field land (C+F).
*Process note:* the shared tree was checked out on `lane-G-godverse` for Fable's review, so I committed
these C docs from a temporary `git worktree` on `main` — the shared checkout was never touched, and G's
branch keeps the disjoint namespace the two-studio merge law depends on.
---

View File

@ -4,6 +4,284 @@
---
## Round 35 (v7.0 close, wave 2) — THE FIRST FIVE MINUTES (Fable, solo, JING5)
A fresh game arrives HUNTING: one seeded artist want (`rng(citySeed,'firstwant',0)` × the
parody roster) named on the splash with the pocket money ("You're hunting HILLS HOIST — $191
in your pocket"), pointing the rumors and the tour at the same target. Three toast-beats walk
the hustle off existing events (walk-in → digOpen → first find → first sale-or-listing, then
the listener removes itself). Returning saves get no tutorial; ?game=0/classic never reach it.
#pc-toast body-mounted so beats and morning papers surface inside shops too. STRICT
`smoke_first_five` green (roster want + splash · real-path beats · returning/?game=0 controls
· cross-browser seed determinism). Full record: LANE_F_NOTES §35.
---
## Round 34 (v7.0 close, wave 1) — GODBAY: the offline auction (Fable, solo, JING5)
⚖ LIST from the crate (mail-in consignment) → `listings` on procity-save/1 → resolves the day
after, wherever the day moves (sleep, travel, imports — adopt() and sleep() share the
resolver). THE ONE-HAMMER LAW: identity-seeded (never day-seeded) — a passed-in item re-lists
to the same result forever, no retry farm. 15% pass · uniform-across-band hammer (grail 2×low)
· 12% war ×1.52 · 12% fees, net floor $1; EV ≈ 0.8×mid so random flips bleed and only finds
profit; keeper = floor, auction = bounded ceiling. The morning paper rides the wake/arrival
toast ("⚖ SOLD 'Cane Toad Blues' — hammer $22, $19 after fees"). Band table imported from C's
bible.js (one authority), PRNG from core. STRICT `smoke_godbay` green (real-DOM consign ·
byte-identical double-sleep · bounded honest resolution · travel hammers · game=0 control).
Full record: LANE_F_NOTES §34.
---
## Round 33 (v7.0-beta wave 2) — THE HUNT: wantlist · rumor · travel (Fable, solo, JING5)
☆ WANT on the pull card → `wants` on procity-save/1 (optional, capped, deduped); buying a
matching item clears the hunt; THE WANTLIST section in the crate panel. The keeper gossips:
seeded 45% banner mutter naming a hunted want + a real cached town. TRAVEL COSTS A DAY:
adopting a save from another townKey (town OR seed) charges +1 day in save.js adopt() — load
and import both pay, same-town stays free (byte-equality gates untouched); arrival toast names
the fare. New STRICT `smoke_wantlist_travel` (real-card want · rumor scan · auto-clear ·
+1-day with intact cash/collection · same-town control · return fare · ?game=0 zero-DOM).
Full record: LANE_F_NOTES §33.
---
## Round 32 (v7.0-beta wave 1) — THE BIBLE + scarcity + the §31.6 queue (Fable, solo, JING5)
Guide bands derived in `js/interiors/bible.js` (E's thresholds mirrored, no pack re-emit) →
the pull/sell cards carry the **guide stamp**; parody bins roll seeded bands + 10% collector/
grail **gems** (asking under the keeper's bandLow offer — margin by construction); §9.1 sell
basis swapped to bandLow exactly as pre-ratified (alpha entries keep the pricePaid basis).
**Scarcity:** optional `pulls` on procity-save/1 — a bought dig slot stays gone all day
(pick-then-omit, zero collateral), pruned on sleep; real-sourced pulls never expire. §31.6
queue: cluster-pose spawn (main-street doors, classic keeps the frozen pose) · THE CRATE +
game bar live in interiors (C anywhere, yields to the cards) · DAWN dome swaps to high-cirrus
(weather override wins). **Gates:** new STRICT `smoke_bible` (band law over every bin ·
ledger closes to the cent on the GEM's bin, Σask $333 → Σoffer $135, gems +$21 · emptied bin
0 across reload · `?game=0` control re-opens 16/16) + sell_routing amended to the exact
formula (the fence moved from the receipt to the band). Full record: LANE_F_NOTES §32.
---
## Round 31 (v7.0-alpha CLOSE) — THE DIG FLIP + the tag
**Fable's wave-0 playtest found the one alpha-blocker: the dig was default-OFF on a plain boot.**
`index.html` still carried the v2-era opt-in (`params.has('dig')`) six epochs after the README
claimed "dig on" — and every R30 game-loop gate booted `dig=1` explicitly, so the suite was green
while E at a bin did nothing on the boot every player gets. Fixed with the one-line flip
(`DIG_ON = flagOn('dig')` — ?dig=0 opt-out, ?classic=1 off, `dig` on the flags surface) and fenced
with two new STRICT legs: **default_boot_gate asserts dig REACHABILITY on a NAKED boot** (E at an
aimed bin → digActive true → closed clean) and **classic_regression asserts dig INERT at the bin**
(zero `pcdg-*` DOM, zero Storage calls — measured, not assumed). *Harness lesson #4: the
explicit-flag blindfold — a gate that force-feeds its subject's flag can never notice the default
is broken; every claimed-default-on flag needs at least one flag-ABSENT gate.* Splash de-staled
("Every door opens." — the "(soon)" is over; controls line = E riffle/sell · C crate · SLEEP to
save) + the README manual row. `PROCITY_QA_PORT` override so the suite never silently reuses
whatever holds :8130 (this round: Fable's own preview server).
**The re-gate (final tree, :8931): `qa.sh --strict --matrix` 7 passed · 0 failed · 0 warn ·
0 skipped; flags_check 0/0 incl. both new legs; MATRIX 10 towns green.** Money shot:
`docs/shots/v7_alpha/crate_panel.png` — Monster Robot Party's frontage, THE CRATE with 3 real
finds (real cover thumbs, honest paid/where/day), game bar day 1 · $116 · CRATE 3 · SLEEP, driven
through the real loop with the dig param absent. **Tag `v7.0-alpha` — annotated, LOCAL ONLY**
(Fable pushes). THE BETA QUEUE (Fable's design wants, filed NOT fixed) is in LANE_F_NOTES §31.6.
Commits: `50385c0` (flip + gates + splash) · shot + notes commit (tag target). Full record: §31.
---
## Round 30 (v7.0-alpha — THE SAVE AND THE CRATE) — F: the contract first, then the save core + tomorrow
**The contract shipped before a line of implementation** (`53fcd3c`): `window.PROCITY.game =
{ day, cash, collection[], townKey, save(), load(), sleep(), export(), import(json), wallet,
recordFind(), removeFind() }` + the `procity-save/1` schema + the rotation law, published in
LANE_F_NOTES §30 — and it worked exactly as the order intended: **B picked up §30.4 within the
session** (venue.js "tonight" already reads `gigs.weekNight`; hud.js collection/SLEEP surface in
flight), and C's already-published §9 asks (`type` stamped at buy, `title`/`artist`, removal via the
game API, `wallet.sell` mirrored) are honoured in the entry shape.
**Then the layer** (`207bcff`): `web/js/world/save.js` (F-owned, new) — versioned localStorage save,
THE DELTA LAW BY SHAPE (payload keys are exactly `[cash, collection, day, savedAt, schema, town]`
no world field can even be expressed); corrupted/foreign → **loud reject, raw stashed, fresh start,
town boots**; save on sleep + beforeunload; export/import for moving machines. The wallet is a
game-backed facade of C's exact v0 interface, so every proven seam (dig onBuy, shelf buy, cover,
C's onSell) is unchanged — cash just became authoritative from the save. Buy seams record finds;
`?classic=1`/`?game=0` never construct the layer — **ZERO Storage calls, instrumented, not trusted**.
**SLEEP=TOMORROW:** `sleep()` → day+1 → gig `weekNight` re-keys the EXISTING week schedule → wake at
DAWN → save. Stock rotation is a runtime day salt on the EXISTING `stk-*` streams (2 marked seam
lines in C's interiors/layout) + the dig's `binSeed`**plan generation never sees `day`** (A's #6
boundary). **Day 1 IS the pre-v7 town** (measured: `?game=0` stock fp == fresh-game day-1 fp), and
**REAL-sourced crates never rotate** (Monster Robot fp identical across sleeps; the mint crate rotates).
**Two amendments filed loudly, with measurement, not slipped in:** gig night = **(day 1) % 7**, not
the brief's literal `day % 7` (day starts at 1 — the literal form flips a fresh boot's gigs off night 0,
breaking the R13 cover/band gates before the player ever sleeps); and sleep saves AFTER incrementing
(disk always carries the morning you woke into). Fable ratifies or overrules at review.
**Numbers:** 7 verify legs green in fresh no-store contexts — $12 dig buy == pricePaid recorded · sell
halves credit/remove correctly · parody fp `3396372961 → 1818111125` across a sleep, byte-identical
same-day AND across reboots · corrupt blob = 1 loud reject + stash + clean boot · `qa.sh --strict`
**6 passed · 0 failed · 0 warn** with the game layer default-ON. Two F-harness bugs caught and fixed
in the run (the post-sleep CLOSED door; a transform-based fingerprint that was vacuous over batched
parody stock — hash the VERTEX data). Full record: `LANE_F_NOTES` §30.
**Held for the later F session (per the brief):** ledger #5 gates + the sell-card E-key routing.
### Wave 3 — THE GATES SESSION: the routing landed, five STRICT gates, the loop closes end-to-end
**The held join is in.** E inside a shop now routes: **aimed bin → dig · near counter with sellables →
C's sell card · forgiving nearest-bin → dig · shelf-buy** (`interior_mode.js`; 3 marked shell seams in
`index.html``procity:sellOpen` unlock + `sellActive` on the Esc/relock guards, the dig's exact
contract). `onSell` does F's two §9.3 halves in the safe order: `removeFind` first (veto ⇒ nothing
moves), credit second. **The loop, measured through the real DOM:** dig pull **$9 debited** → walk to
the counter → E → card → SELL **+$4** (< $9, the clamp) → collection 1→0 → street HUD **$186 ·
CRATE 0**. One amendment filed with its measurement: the old nearest-bin fallback ignored aim and would
have swallowed E at any counter near a bin — aimed-raycast now outranks the sell trigger, the
forgiving pick runs after it; both branches proven live (Moe Vinyl counter: card opens; Kelpie Sound
bin, wrong aim: dig opens).
**The gates (ledger #5), all landed as STRICT suite smokes in `flags_check.py` — no more session-local
instruments:** (a) **save/load determinism** — scripted buy 2/sell 1/sleep 2× via B's real button →
export → fresh browser → import **byte-equal** (state 243 B, normalized export 269 B), plan fp
`0xb1c7843e` **unchanged** across the import, weekNight re-keyed 2; (b) **no-pump v0** — 5 real
UI round trips **all strictly negative**$30), plus **C's negative control**: broke player,
`wallet.buy` invoked-and-false on the real shelf path ⇒ **recordFind unreachable, zero entry** (dig BUY
disables outright), with a positive arm proving the control discriminates; (c) **classic purity**
Storage-prototype instrument pre-page-script: `?classic=1`/`?game=0` **0 calls, 0 game DOM ids**,
positive control sees the default boot's `procity-save` read + all 7 ids, plan fp game-on == game-off;
(d) **rotation determinism over VERTEX DATA** (the wave-3 binding) — day 1/2/3 fps all distinct,
re-entry byte-identical each day, **cross-boot byte-identical off the persisted save**, `?game=0` ==
day 1 (88,320 hashed bytes / 20 stock meshes printed — the subject provably present). Harness bug #3
caught en route: the HUD is hidden in interiors by design; the gate read it on the street.
**NO TAG** — per the brief, the alpha tags when it plays (Fable's call, likely R31). Full record:
`LANE_F_NOTES` §30.6.
---
## 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**,
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
shop's POS are in the tree and byte-identically re-bakeable — genuinely the artifact the charter asked
for. Nothing in the game can reach them. `#7a` isn't a gate that failed, it's a gate that **cannot be
run**. R18's law: *release rounds don't ship known breaches.* Full record: `LANE_F_NOTES` §23.
**F executed the merge that never happened** (charter law #5) — `main` had no `stock_godverse/**` at all,
so the branch F was asked to tag lacked the round's headline. Review had happened, C had verified
pre-merge and passed; only the `git merge` was missing. Disjointness verified before merging, not
assumed. Zero conflicts → `43b035a`.
**What passed — everything except the crate.** `qa.sh --strict --matrix` **7 passed · 0 failed · 0 warn**;
**MATRIX GREEN — 10 towns × 7 gates**; selfcheck **ALL GREEN 152,015/152,015** (was 161,300 — E retired
toowoomba); flags harness green, so the `?classic=1` covenant still holds through the merge. Tier 0
`?noassets`**zero fetches, zero console errors** (the offline law holds); tier 0 default boot on the
godverse town; tier 1 (v2 town-wide) `?stock=real`. **Tier 1 (v5, per-shop) — the round's whole
deliverable — is never fetched.** A, D, E and C's work is sound; only the rung this round was built to
add is unattached.
**Three findings, in order of how much they cost:**
1. **The atlas contract contradicts itself.** `LANE_C_PUB:271` publishes `stock_shop_<id>_index.json`;
C's own loader at `stockpack.js:25` fetches `${base}stock_${type}_index.json`. **Incompatible.** G
built runtime-compatible (so C's base-override check passed); E built contract-compatible (so E's
validator can't see it). **Nobody was wrong — the contract was.** F recommends amending to G's shape;
it's the only one the runtime can load.
2. **The gate built to catch that was blind and green.** `validate_atlas.py` globs a filename that can't
exist at runtime, finds nothing, and `return 0` — so it passes whether the atlas is absent, misnamed,
or perfect. It had never seen one until F pointed it at G's by hand, which then surfaced *deeper* drift
(E's code reads provenance top-level, E's own docstring says nested, G nested it; `licence` vs
`license`; a ceiling stale by one round). *A gate that returns 0 on an empty set is not a gate.*
3. **`#7a` would have passed vacuously.** F walked into The Vintage Record (real shop, real road) and the
crates filled with **real covers — from E's 350-item town-wide v2 pack**, not the crate. Zero requests
to `stock_godverse/`, ever. Eyeballed as written, the gate goes green and proves nothing.
**R24 order — three gaps, none F-only:** **A+G** land a `godverse_id` (verified: *every* godverse shop
reports `godverse_id: null`; the schema has no such field, and C's rule is "C does not invent it") →
**E** builds `redhill_real` (one Overpass fetch — E's file, outward-facing; **not F's to invent**) →
**C** keys the pack cache by `type+base` (today a cache hit returns before `base` is read, so two stocked
shops collide *and fail-soft breaks*) → **F** passes the per-shop base and re-gates. F deliberately did
**not** build its half: untestable with no street and no identity field. Filed, not guessed.
**Also:** A's spacing warn earned on day one — with toowoomba retired, **ballarat now warns at 255 m**.
The thin tail has a second member.
---
## Round 22 (v4.0 — THE EPOCH CLOSE) — F closed it
- **The blocking gate CLOSED.** F's R21 re-measure made the tri diet blocking; B diagnosed, the driver

View File

@ -3,6 +3,262 @@
*Lane brief: `docs/LANES/LANE_G_GODVERSE.md`. This file follows your progress-file convention —
newest round first. Fable-on-ultra writing.*
## Round 27 (2026-07-17) — tier 2: the two read endpoints (ledger #1)
**Landed:** `pipeline/godverse_server.py``/health`, `/shop/<gid>/stock`, `/shop/<gid>/crates`,
plus `start|stop|status` (F's kill-the-server lifecycle), `setup-role`, and `make-fixture`.
Stdlib + `psql`; no server code existed before this round, per the ratified G3.
**Live now, against the real POS:** crate 550 — `{baked:120, live:120, gone:0}`, real prices, real
`VG+` grading, etag stable across reads and `?since=`**304**. `/crates` returns **315 real
crates** with the shop's own labels — `TECHNO` (57), `$12-$15 TECHNO` (46), `DRUM N BASS` (66).
**The three fences, each enforced by the machine (measured, not reviewed):**
| fence | proof |
|---|---|
| the credential — `godverse_ro` | `UPDATE inventory`**permission denied** · `SELECT FROM customer`**permission denied** |
| write verbs absent (§7 ruling) | `POST /reserve`**501** · `POST /buy`**501** · `GET` either → 404 |
| metadata only | no `cover`, no atlas bytes on the wire — covers stay static tier-1 files |
| tier 2 = real shops only | a `mint` shop's `/stock`**404** (no POS to be live about), not an empty crate |
| the id fence | `gone[]` carries the atlas `id` verbatim → `gone.includes(item.id)`, zero transform |
**One grant carries both the sandbox and the PII fence** — that's why §7 said enforce at the
credential, not in a code review. `godverse_ro` holds SELECT on inventory/crate/disc_cache and
nothing else, so customer/staff/sales are unreachable *by the process*, not by our good intentions.
**⚠ THE FINDING F NEEDS BEFORE IT WRITES THE GATE — `gone[]` has no subject in production.**
The mechanism is right; the data has no movement. Monster Robot Party's POS records **5,567 sales
ever** (400800/month Dec→Jun) but **26 in July and none since 2026-07-01**, and hasn't been
*written* since **2026-07-09** — the 07-16 dump is fresh, its data is stale (I restored it to
check: crate 550 holds 122 on both 07-15 and 07-16, identical). **0 of the crate's 120 records
have ever sold.** So `gone` is `[]` against the real POS, permanently, until the shop trades again.
An assert over it would pass **without touching its subject** — the exact species that has held
four tags. So I built the honest way out: **`make-fixture --sold 3`** clones the POS
(`createdb -T`) and marks 3 of the crate's *own real skus* sold → verified
`{baked:120, live:117, gone:3}`, the three ids absent from `items[]`. F points the server at it
via `GODVERSE_POS_DB` and gets a deterministic gate; production reads the real POS untouched.
**A fixture is not a lie; a green assert over an absent subject is.**
**Lifecycle for the kill-the-server gate (ledger #3):** `stop` is a **SIGKILL**, not a graceful
drain — the gate tests a death, not a goodbye. Verified: `stop` → connection refused (curl rc=7,
no response at all) → `status: DOWN``start` → live again. F drives it; no admin verb was added
to the API, because a server whose whole point is "no write verbs" shouldn't grow a remote kill.
**Three corrections this round forced into my own G3 §4 (design → as-built):**
1. **`gone:[sku]``gone:[id]`.** §4 as *I ratified it* said bare skus — wording that predates
R25's sku ids and R26's fence law. A bare number crossing two id spaces is the precise bug that
law exists to stop. **My doc was the stale rule meeting a case written after it** — the epoch's
signature failure, and this time it was mine, in the doc I wrote.
2. `items[]` drops `release_id` and `cover`: `cover` edges the API toward serving pixels (it never
should), and `release_id` is the *pressing* while tier 2 speaks only in *copies*.
3. **Write verbs answer 501, not the 404 my own docstring claimed** — caught by running it. 501 is
the *stronger* absence proof: a 404 means a handler ran and declined; 501 means there is no
write handler in the process. **→ F: assert 501.**
**→ Lane F, the four asserts I can hand you pre-measured:** `POST /reserve|/buy` → 501 · a game
buy cannot reach the server (no verb *and* no grant — prove both) · `gone` fixture arm as above ·
server dead → connection refused, so the reader must treat it as tier 1 in silence.
## Round 26 (2026-07-17) — the mint baker, the crate menu, the manifest (ledgers #2, #6)
**Landed (main, pathspec-atomic):**
- **`godverse_stock.py mint`** — the G3 note promoted: `random.Random(shop_id)` over an
**id-ordered** pool of real dealgod listings. The shop id IS the seed, so two shops can never
draw the same crate. No TABLESAMPLE, no unseeded random — the two things that disqualified
thriftgod's own `mint()` from tier 1 (G3 §2).
- **14 mint atlases** — every keyed shop in both godverse towns whose type the loader can actually
fetch (`record`/`book`/`toy`, C §7.1): 5 record + 6 book + 3 toy. The 13 keyed `opshop`/`video`
shops get none and stay parody — correct, not a gap: C §7.1 gives them no fetchable type, so an
atlas there could never load. **The manifest is what makes that silent** (no entry ⇒ no probe).
- **`stock_godverse/index.json`** (§7.2d) — 15 shops, 1 real / 14 mint. Derived by reading the
atlases back off disk, so it cannot drift from them: the manifest *is* the directory.
- **Monster Robot re-emit**: `sourcing: "real"`, plus C §7.2c's `condition`/`sleeve_cond` (my
snapshot always carried them; I was dropping them at emit — that was the hole in C's price card).
| gate | result |
|---|---|
| every crate different (F's #7 distinctness, title sets) | **15/15 distinct · max shared titles between any two: 0** |
| ids unique across all packs · clash with generic · positional | 344 unique · **0** · **0** |
| mint determinism (re-mint + re-bake) | **byte-identical** |
| mint carrying a POS-claim field | **0** — absent, not false |
| `sourcing` present on every atlas | 15/15 |
| E's manifest↔files, both ways | clean |
| selfcheck (no town cache touched) | **156212/156212 green** |
| E's validator | **14 errors — all one cause, below** |
**THE ONE RED — mint ids vs E's `SLOT_ID_PREFIX` (E, one line).** E asserts `sku_` on *every* atlas
under `stock_godverse/`. Mint items have no POS sku, so they carry `mint_<dealgod listing id>` and
all 14 fail. **C §7.2a's rule is satisfied** — "derive from the source's own stable key, never
position, unique across ALL packs" (measured above: unique, 0 clash, 0 positional). What §7.2a
specifies as `sku_<POS sku>` it specifies **"for a real-stock (godverse) pack"**; mint is C §7.2b's
*other* thing, written a round later, and the id form was never revisited. E implemented R25
literally, which was right at the time.
**Why I did not just emit `sku_` and go green:** a POS sku and a dealgod listing id are **different
namespaces**, and this round's own standing note says no code or gate may compare a bare number
across two id spaces — it was written because `enterShop(31)` entered the wrong shop. Putting both
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 that a POS
must resolve. C §7.2a's own reason #2 is precisely about ids being looked up across snapshots.
Green by prefix would have been green by lie. **Fix: make the prefix sourcing-aware (`sku_` when
`sourcing == "real"`, `mint_` when `"mint"`)** — E's error text and constant are the only places.
**Three more findings, none blocking:**
1. **[E] `FITS_1024 = 64` cannot be satisfied.** The warn asks for ≤1024² when a pack has ≤64
items, but 1024²/256² = **16 cells** — 64 items would need **4** atlases and C caps a shop at 2.
So any 3364 item pack *must* be 2048², and the warn would be demanding the impossible. It never
fires on my packs (16 → 1×1024², 120 → 2×2048²), so it's latent. The honest bound is 32.
2. **[C/F] The ≤32 MB ceiling doesn't hold at beta scale, because the LRU it assumes hasn't
shipped.** F's boot preload loads **every** keyed shop's pack (`index.html:207-215`), and C §7.6
scoped "≤32 MB peak" to "LRU = 1 shop's atlas set resident", with the LRU explicitly deferred to
v5.0-beta. Measured now: **46.1 MB per town** resident at boot (both towns). Bounded and modest
— and it's *why* mint crates are 16 items on one 1024² sheet rather than something plusher.
3. **[C] "The crate menu, baked statically" can only be metadata.** One base = one index = ≤2
atlases = 128 items; Monster Robot has **357 crates / 24,646 bake-able records**. So the shop's
real crate list ships as `crate_menu` (id/name/label/count — real labels, real counts), and
per-crate *stock* would need a per-crate base (`…/3962749/crate_550/`) = a C contract line.
Filed, not invented. Rotation's real home is tier 2 (G3 §4 `/crates`).
**Mint honesty, stated plainly.** A mint crate is *plausible*, never a claim: `sourcing: "mint"`,
an attribution that says "not this shop's stock", and **no POS-claim field at all** — the
separation is structural, not editorial. Quality caveat: for books/toys `artist` is dealgod's
`brand`, which is sometimes the maker (Microsoft, Sony, Brandon Stosuy) and sometimes the listing
store (Lifeline Queensland) — real data, imperfectly typed. **Records are clean**: I require a
Discogs artist and drop otherwise, because `brand` on a record listing is the *seller* — the first
bake had "Red Eye Records" and "Statecircle" as artists, which is a shop, not an act. Titles use
`discogs_master_title` where dealgod has it, so Egg Records' crate reads "MC5 — High Time · $50",
not "Pre Loved Record - Elton John - Breaking Hearts". Both are real; one is a record sticker.
## Round 25 (2026-07-17) — the sku re-emit (ledger #3), index-only
**Landed (main — G is a full lane now, pathspec-atomic; F's six staged files and E's in-flight
`validate_atlas.py` untouched):**
- `godverse_stock.py` emits **`id = "sku_<POS sku>"`** per C §7.2a. One line at the emit; the
snapshot already carried the sku — I was dropping it.
- `stock_godverse/3962749/stock_record_index.json` re-emitted. **Index-only, as briefed:**
| check | result |
|---|---|
| **id overlap with the generic pack** | **0/120** — was **120/120** (F's finding) |
| ids unique in-pack / all `sku_`-prefixed / none in `rec_` namespace | 120/120 · yes · yes |
| **atlas .webp bytes moved** | **none** — sha-identical both files |
| town caches / goldens touched | none |
| ids stable when a record sells (drop item 50, re-derive) | **0 drift** |
| E's validator (their R25 build, live in-tree) | `atlas-QA OK · 1 licence line(s) shown · 0 warnings` |
| live loader: shop pack vs town pack | 120 / 350, coexist; uv + one-material-per-atlas hold |
**Id equality is discriminating again** — verified through the live loader, not just the file.
**C's reason #2 is the one that mattered**, and it's worth restating because it outlives the gate:
position isn't identity. Sell one record out of crate 550 and `rec_0050` silently becomes a
*different* record on the next bake — so a wallet purchase, a saved dig, or **tier-2's
sold-means-gone** would point at the wrong item. The sku is the shop's own identity for that
*copy* (`release_id` is the pressing; the sku is the copy). My positional ids were a latent
correctness bug in the ratified §7 economy, not just a vacuous gate. Caught while there were two
atlases to migrate instead of fifty.
**Conflict #1 is closed** (R24 → R25): E's validator now prints
`LICENCE: … FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE` against my top-level US `license`, and
counts it. The flag a human must read on real product photos is unmissable. Nothing owed.
**→ F (one line, your file, your call — not touching it mid-flight):** `interior_mode.js:378`
now reads *"ids are positional slots (`rec_0000`) that BOTH packs reuse … every atlas id is also a
town-pack id (measured R24)"*. As of this commit that's false — 0/120. **Your gate stays canonical
either way**: a texture URL proves which file the GPU sampled, which id equality never could, so
the assertion is right and only the rationale is stale. Id equality is now available as a *second*
discriminator if you want belt-and-braces.
## Round 24 (2026-07-17) — the re-emit + THE SHOP'S REAL STREET (ledger #5)
**THE WRITE-BACK RULING — John, 2026-07-17: A THEN B.** G3 §7 is **ratified** with the ruling
inline; charter law #4 is satisfied. v5.0 ships **sandboxed** — a game purchase never touches real
monsterrobot/Square inventory. **Option B (reservation) is chartered as a v5.x upgrade** behind a
hold budget John sets; **C is not chartered**. What I wrote into the doc as binding, beyond the
one-liner, because a sandbox that can be flipped on by a config typo isn't a sandbox:
`POST /reserve` + `/buy` stay **absent, not flag-stubbed**; the tier-2 reader connects with a
**read-only role** (`SELECT` on inventory/crate/disc_cache only — which carries the §9 PII fence in
the same grant); and the gate asserts the write verbs **404** + the role's grants are read-only,
because "we didn't call buy()" proves nothing (the vacuous-gate law applied to ourselves).
**Carried to v5.x: the hold budget is John's number and is not yet set** — B isn't startable until
it is. B's non-negotiable: **the shop always wins** a race with an in-store Square sale.
**Landed (branch `lane-G-godverse`):**
- **`redhill_godverse`** — the town whose record shop is THE shop. Monster Robot Party seated on
Musgrave Road (**verified: lot 8, type `record`, `godverseShopId` 3962749**), 37 shops, 884 real
Red Hill roads off E's donor.
- **The atlas re-emit** — US `license` throughout (British `licence` retired, 0 occurrences),
provenance lifted to top level per C §7.3 **and** kept nested per E's validator (see conflict #1).
`atlas-QA OK`, 120 real records, byte-identical re-bake holds.
- **`newtown_godverse` re-emitted with `godverseShopId`** (ledger #2) — 18 census shops keyed.
- **`godverse_town.py`**: psycopg2 → `psql` (the m3 has no psycopg2); emits `godverseShopId`;
`EXTRA_SHOPS` roster; **Fable's R22 Ruling 2 finally implemented** (see below).
- G3 §4: the `godverseShopId`/`godverse_id` naming split recorded (two artifacts, two conventions).
**Monster Robot Party's coordinate** is John-supplied (Google Maps place `/g/11g6rlbbc0`
-27.4553791, 153.0076244). It could not be derived: OSM doesn't know the shop (no name match, no
147 Musgrave Rd address node), the census doesn't have it, dealgod's `stores`/`store_entity` rows
carry NULL lat/lng, and recordgod has no address anywhere. Verified inside E's bbox **and** on
Musgrave Road's own geometry. My own estimate was ~120 m out — asked instead of shipping it.
**FABLE'S RULING 2, IMPLEMENTED (R22 → R24) — and it was load-bearing, not cosmetic.** "Merge
census heroes WITH the donor's texture shops; census heroes win a collision; count the swap."
I never built it. Measured this round, census-only Red Hill is **not shippable**:
| redhill_godverse | shops | A's spacing warn | poster-clears-kerb |
|---|---|---|---|
| census only | 9 | **306 m** ⚠ | pass |
| census + Monster Robot | 10 | **261 m** ⚠ | **FAIL** (poster inside edge 1037 by 2.95 m) |
| **+ donor texture (Ruling 2)** | **37** | **none** ✓ | **pass** ✓ |
| *(E's donor `redhill_real`, for reference)* | 36 | none | pass |
261 m is **worse than the 255 m that retired ballarat this round** — the alpha's headline town
would have shipped flagged "scattered, not a high street, or retire the town". Ruling 2 fixes both.
9 donor heroes swapped for their census entries in Red Hill, 18 in Newtown — counted, per the ruling.
**CONFLICT #1 — C's contract §7.3 vs E's validator (provenance).** C says "lift the four to TOP
LEVEL, US `license`; British retired — *exactly what E's validator reads*". **Measured: it is not.**
`validate_atlas.py:98` reads **nested** `provenance`, and `:105` reads `licence` British-first.
Lifting-and-retiring as written **fails the gate**. My R23 atlas already passed it untouched. This
is the same species as the bug that held the tag — a doc asserting what code does without running
it. **Resolved without drama:** E's check accepts `licence or license`, so US-nested passes; I emit
the four at top level (C's contract + the town-cache house convention) off ONE source of truth.
**→ E, one line:** `validate_atlas.py:198` PRINTS `licence` British-only while its *check* accepts
both — so adopting C's mandated US spelling **silently stops printing the licence flag**, which
:199's own comment calls a human call ("the licence is PRINTED — clearing it is a human call").
The gate still passes; the evidence line vanished. Fix: `.get("licence") or .get("license")`.
**CONFLICT #2 — Ruling 2 vs A's identity gate. THIS ONE NEEDS A RULING; it is why the tree is red.**
Ruling 2 makes a godverse cache *mixed*: a keyed GODVERSE layer (census + Monster Robot) plus an
inherited OSM texture layer. A's sweep asserts **every** shop is keyed → partial ⇒ FAIL. The two
standing rulings cannot both hold.
**The texture shops carry no `godverseShopId` deliberately**, and I'd argue A's own words settle it:
- The field *means* "this lot's stock lives at `stock_godverse/<id>/`". An OSM milkbar has no
GODVERSE identity. Minting one from its OSM id pushes a **third id space** into a field that
already carries two, and A's validator calls a duplicate **mis-stocking** — charter risk #3's
exact nightmare, with Monster Robot's crate as the prize. (Measured: OSM ids here are ≥53.1 M vs
census ≤2992 and store id 3,962,749 — no collision *today*; dealgod store ids reach 7 digits and
OSM ids are 8+, so the margin is thin, and "no collision today" is not an id-space design.)
- **A's validator and A's sweep disagree with each other.** The validator: *"Missing = tier 0
(soft). Duplicate = mis-stocked (hard). **They are not the same failure.**"* The sweep fails
partial keying because it *"mis-stocks the rest"* — conflating precisely the two failures A said
are not the same. Unkeyed → parody is the **designed ladder**, not a gap.
- **Proposed (A's call, Fable's ruling):** assert coverage over the **godverse layer**, not the
inherited texture layer — the subject is "shops that claim stock", and the gate stays honest
(it still names its subject and proves a count). Zero-keyed still SKIPs; duplicates still error.
**The 8 red checks, itemised — no surprises for F:**
- `newtown_godverse` base+gig goldens (**2**) — my re-emit moved them, exactly as ledger #2
intends. **A re-pins** (batched): base `0xcf69b387`, gig `0x1e266f60`.
- `godverse identity` ×3 on each godverse town (**6**) — conflict #2, above. Nothing else.
- Unpinned (warn, expected): `redhill_godverse` base `0xb2224939` / gig — **A pins**.
- **Classic covenant `0x3fa36874` intact.** `atlas-QA OK`.
**If Fable rules the other way** (texture layer out): revert to census-only and F inherits the
poster-kerb FAIL at edge 1037 + a 261 m town — that break is A/B's invariant, exposed by real data,
and would need an owner before the tag. I'd rather hand F the conflict than the broken town.
## Round 23 (2026-07-16) — G2a: THE FIRST REAL CRATE + the G3 draft
**Landed (branch `lane-G-godverse`, G namespace only — ready for your mid-round merge):**

View File

@ -14,9 +14,36 @@ cd web && python3 -m http.server 8130
# http://localhost:8130/map.html (2D plan debugger — Lane A)
```
## The manual — how to play (private test)
Click **START** to lock the mouse. Then:
| input | does |
|---|---|
| **W A S D** + mouse | walk / look (first person) |
| walk into a shop door | enter the interior (open shops only — check the hours tooltip) |
| **E** (inside a shop) | open the crate at a record bin — then it's cursor-driven: click sleeves to riffle, click BUY |
| **E** (at the counter) · **C** (anywhere) · **SLEEP** (bottom bar) | sell a find to the keeper (he pays under the **guide band**, never over) · open THE CRATE — your collection, in shops too now · sleep = save, the day advances, stock rotates |
| read the **guide** stamp on every price card | the 90s price-guide band ("guide $824"). An item **asking under its band is a gem** — buy it, sell it to a keeper, pocket the margin. Bought slots stay gone for the day (the bin remembers) |
| **☆ WANT IT** on a pull card | start hunting it — keepers gossip ("saw one in Castlemaine"), and buying one clears the hunt. **Switching towns costs a day** — the 23 towns are a map now, not a dropdown |
| **⚖ LIST** in the crate | consign a find to **GodBay** — the auction hammers overnight (sleep or ride somewhere). One hammer per item, ever: keeper cash now, or the gavel tomorrow |
| click a book spine / toy box | pull-and-buy card (real price stickers in godverse shops) |
| walk out the door / **Esc** | leave the shop (Esc also frees the mouse anywhere) |
| **M** | 2D town map (street only) |
| **[** / **]** | step time of day back / forward · **T** pauses the clock |
| **P** | capture screenshots (street) |
| HUD dropdown (top-left) | switch towns — synthetic, fixtures, 23 real AU towns, godverse |
Worth trying: `?classic=1` (the original v2 town, byte-identical forever) · `?noassets=1`
(zero-network boot) · `?stock=real` in a godverse town (Monster Robot Party's actual crate) ·
`?seed=<n>` (any number = a different town). Night is when the gigs happen — step time with `]`.
## Read first
- **`docs/CITY_SPEC.md`** — the treaty: schema, units, seeds, budgets, file ownership.
- **`docs/V7_THE_GAME.md`** — the CURRENT epoch (ratified): persistence, the collection, the
gem thrill. `docs/V6_LIVING_STREET.md` runs in parallel (mocap NPCs, footpaths, cars).
- **`docs/THE_FRONTIER.md`** — the honest map of what is not figured out yet.
- **`docs/RESEARCH.md`** — what the parent repos already solved, with exact paths to port from.
## The lanes (parallel Opus 4.8 sessions)
@ -73,6 +100,7 @@ master off:
| `?weather=0` · `?tram=0` · `?winmap=0` | **on** | seeded weather · the tram loop · the parallax interior-window shader |
| `?roster=v1` · `?pop=N` | stream | chunk-streamed citizen density is the baseline; `?roster=v1` restores the fixed roster |
| `?dig=0` · `?stock=real` | dig on | crate-riffle on record bins · real GODVERSE sleeves in the stock |
| `?live=1` · `?live=<url>` | **off** | **tier 2** — live stock from the GODVERSE server (sold-in-the-real-shop means gone in the game). Opt-in by design: absent ⇒ byte-identical to tier 1, and the game never asks a question it has no reason to expect an answer to |
| `?noassets=1` · `?plansrc=osm` · `?mute=1` · `?dbg=1` | off | asset-free (zero network) · OSM plan source · silence · the `window.DBG` capture/soak harness |
| `?plansrc=osm&town=<key>` | — | boot a **real Australian town**: fixtures `melbourne`/`katoomba`/`silverton`, or a real OSM cache `bendigo_real`/`castlemaine_real`/`fremantle_real`/`katoomba_real`/`newtown_real` (ODbL, `web/assets/towns/SOURCES.md`). **REAL ROADS:** a cache with `roads[]` (schema v2) builds the CityPlan street graph from the town's **actual OSM streets**; without `roads[]` a cache marches onto synthetic avenues. Or just **pick a town from the HUD selector** — no URL needed |
@ -97,6 +125,37 @@ Fitzroy mecca, the thin tail (labelled honestly: the floor is shops-per-street-m
below it), the synthetic control, and the `?classic=1` covenant made visible. Charter:
[`docs/V4_REAL_MAP.md`](docs/V4_REAL_MAP.md) · data + licences (ODbL):
[`web/assets/towns/SOURCES.md`](web/assets/towns/SOURCES.md).
## v5.0-alpha — THE FIRST REAL CRATE: the shops get real too
**v4 made the towns real. v5 makes the shops real** — and the first one is standing.
Boot `?plansrc=osm&town=redhill_godverse&stock=real`, walk down **Musgrave Road** into **Monster Robot
Party** (147 Musgrave Rd, Red Hill, Brisbane — a real record shop), and riffle its crate. The records in
it are **that shop's real stock**: 120 real records off its real point-of-sale, real covers, real titles,
real prices. Buy one and the wallet debits what the sticker says. It's gone for the session and back next
boot — because until the live tier lands, the crate is a **file**, not a shop's ledger.
**Stock is a data tier now**, exactly like assets — each rung fully playable, each degrading softly to
the one below, so the game never depends on a server to be a game:
| tier | what | deterministic? |
|---|---|---|
| **0 · parody** | seeded canvas sleeves (v1) — any boot, always works | yes — the QA baseline |
| **1 · static real** | per-shop atlases baked from the shop's own POS | yes — atlases are files |
| **2 · live GODVERSE** | `?live=1` — live stock off the real till: **sell a record in the shop and it's gone from the game crate** | **no — by design** |
**The offline law:** the server enriches, never gates. `?noassets=1` still fetches **zero bytes** and plays
silent-and-fine; a shop whose atlas is missing falls soft to parody rather than borrowing someone else's
crate; and **`?classic=1` still boots the exact v2 town, byte-identical forever** (`0x3fa36874`). The
world stays seeded and golden-gated — only the *stock inside it* is data.
**Provenance:** item photos are each shop's own product shots, 🟢 in-house — **flagged before any public
or commercial release**, and every atlas carries that flag where the QA gate must print it. Real titles
and artists are an in-house amendment to the no-real-trademarks law, on the same footing. **Nothing an
in-game purchase does touches real inventory** — v5.0 ships sell-sandboxed by ruling; a real reservation
tier is chartered for v5.x, not assumed. Charter: [`docs/V5_REAL_SHOP.md`](docs/V5_REAL_SHOP.md) ·
economy design: [`docs/G3_ECONOMY_DESIGN.md`](docs/G3_ECONOMY_DESIGN.md).
| `?seed=N` | `20261990` | town seed |
**Release tour:** [`docs/shots/release_v3_1/contact.html`](docs/shots/release_v3_1/) — the money shots on

View File

@ -1,10 +1,154 @@
# LANE A — CITYGEN · progress (PROCITY-A)
*Status: **all deliverables landed; self-check all-green (161,372/161,372); rounds 223 closed** (see below).
R23 (v5.0-alpha): D's thin-tail finding is law — `validateTownCache` warns above **150 m median shop
spacing**; flags 2 of 23 towns (toowoomba 396 m, **ballarat 255 m — beyond the brief**). Warn, never error.
R22: the pack absorbed — 23 towns, 36 goldens in one commit. classic `0x3fa36874` / gig `0xb1d48ea1` frozen.*
*Date: 2026-07-16 · owner: PROCITY-A (Opus 4.8)*
*Status: **all deliverables landed; self-check ALL GREEN (156,352/156,352); rounds 230 closed**.
R30 (v7.0-alpha ledger #6): the rotation boundary VERIFIED — F's `day` salt rides runtime streams only,
plan generation structurally cannot receive it; all goldens frozen, zero re-pins.*
## Round 30 (2026-07-18) — the rotation boundary verify (ledger #6, small round as briefed)
One job: prove F's day-salted stock rotation (207bcff) never reaches plan generation. **It holds.**
Three measured legs (full detail in LANE_A_NOTES §30): (1) grep-zero for day identifiers over the proven
`citygen/**` glob (8 files / 2,546 lines, census printed; pattern proven non-vacuous against
`interior_mode.js`); (2) structural absence — `generatePlanFor` consumes no day-shaped opt and neither
shell call site passes one; (3) day-inertness byte-measured — plan hash identical with `{day, stockDay}`
hostilely injected as 1 and 37 vs absent, 3 seeds × synthetic+osm, 6/6. The salt's actual reach: `stk-*`
stock-pick stream labels + dig `binSeed` + `setWeekNight` night-selection over the plan-time 7-night week
(day picks a night, never generates one). Selfcheck ALL GREEN 156,352/156,352, fingerprint `0x5f76e76`
frozen, every town pin green. No files of mine changed except these two notes.
## Round 27 wave 5 (2026-07-17) — THE FACADE FIX (the epoch's biggest catch)
The ruling asked me to land B's one-liner in the real-roads seating and to **measure the marched path
rather than assume it was clean**. I measured all three paths, and the ruling's premise was wrong in the
half that mattered: **synthetic is broken too**. Not "every real town since R18" — **every town, every
path, since v1**. Confirmed numerically (0 toward / 681 away, dot 1.0000) and then with my own eyes:
`?classic=1`, synthetic main street = three blank boxes; walk into the block and NUMBAT PLAYTHINGS is
there, facing the dirt. 637 of 681 lots pointed their facade at **no street at all**.
- **Four sites, one sign.** `plan.js:122` (synthetic strip), `plan.js:244` (market row), `plan_osm:330`
(marched avenue), `plan_osm:443` (real roads — B's proven one-liner). All `atan2(outward)` where the
intent was `atan2(outward)`, and **every one says so in its own comment**. The code never did what it
said; CITY_SPEC:71 blessed the result by writing the SIM convention into the LOTS contract.
After: synthetic 681/681, marched + all 3 fixtures 100%, real-roads katoomba **0/72 → 72/72** (B's number).
- **The covenant moved, on John's ruling.** Fixing synthetic necessarily moves `0x3fa36874`, frozen
"forever" since R16 — not Lane A's call, so I stopped and asked. Ruling: **amend and fix all paths** (the
covenant is anti-drift machinery, not a defect preservative). **51 goldens re-pinned in one commit**;
amendment recorded in CITY_SPEC as a one-time, ruling-gated precedent.
- **A second bug the fix exposed, fixed with it:** `buildRealRoads` seated blocks at `KERB = 4 m` from the
**centreline**, but a `main` carriageway is 10 m wide (kerb at 5 m) — **every main-street building has
stood 1 m inside the road since R18**. Invisible until the shopfront turned around and its poster landed
in the traffic lane. Blocks now seat behind the kerb per A's own corridor law. Drops 6.4% → **6.6%**, counted.
- **The cross-convention gate (the lesson made law).** It does not restate a spec line — that is the trap F
proved (72/72 "facing" over blank walls). It replicates **Lane B's own** `buildings.js` maths and asks
whether the quad the *renderer* draws lands nearer the street than the lot centre. Fires if A's `ry` **or**
B's `zf` drifts. It would have caught this on day one.
- **→ F (blocking their re-gate):** `scaffold_check` is RED until F re-pins the covenant hashes duplicated
in **F's own files** (`flags_check.py` `GOLDEN_HASH`/`GIG_GOLDEN`, `scaffold_check.mjs` `TOWN_GOLDENS`,
two tour captions). Exact values handed over in LANE_A_NOTES §R27w5; I can't touch F's files.
*Previous status: 156,212/156,212; rounds 227 closed (see below).
R27 (v5.0 close): **CITY_SPEC v5 section** (tier ladder · `sourcing` vs `tier` · the three id-space fences ·
the manifest · Ruling 2) + the **charter risk list judged line by line** — risk #1 deliberately CARRIED, not
pre-retired, because F's kill-the-server gate is what retires it. classic `0x3fa36874` / gig `0xb1d48ea1`
frozen; **no golden moved this epoch**.*
*Date: 2026-07-17 · owner: PROCITY-A (Opus 4.8)*
## Round 27 (2026-07-17) — v5.0 THE LIVE CRATE: the docs freeze (ledger #6)
Lane A's half of the epoch close. Docs only; **no code, no golden moved** — correct, since tier 2 touches no
plan, no cache, no atlas bytes. selfcheck **ALL GREEN 156,212/156,212**.
- **CITY_SPEC v5 section**, written from the artifacts rather than the briefs (the epoch's own lesson):
the **tier ladder** (0 parody / 1 static real / 2 live, each degrading softly); **`sourcing` is not
`tier`** — both ship in every index, `tier: 1` is the rung and a mint atlas is *still* rung 1, while
`sourcing: "real"|"mint"` is provenance (15 keyed atlases = **1 real + 14 mint**); the **three id-space
fences**; the **manifest** (existence declared, never probed — the root fix for fail-soft-404 vs the
zero-console-errors law; 15 entries, 15 dirs); **Ruling 2**; and A's single seam, `godverseShopId`.
- **The id fences, verified not restated.** The plan-id/godverse-id trap is still live in the data and I
reproduced it: in `redhill_godverse`, bare **`31`** is "Wholefood Cafe" as a plan id and "Silky Oak
Furnature" as a godverse id — the exact ambiguity that made F's `enterShop(31)` assert vacuously on the
wrong shop. It's a standing rule, not a fixed bug, and the spec says so.
- **The risk list judged, without pre-retiring anyone's gate.** #1 (tier-2 latency) **CARRIED** — written in
wave 2, F's reader doesn't exist yet, and *nothing retires that line except the gate going green*;
retiring it from a doc while the code is unwritten is the precise species this epoch spent four holds
catching. #2 **retired** (measured: max 2 atlases/shop vs C's cap of 2; mint at 16 items/1024², only the
real POS shop at 2048²; the mecca case served by crate rotation — the remedy the risk itself named).
#3 **retired — designed out, not solved**: it assumed fuzzy name-matching, and there is no matcher;
Monster Robot is in *neither* census nor OSM, so no matcher could ever have found it. #4 **split**:
write-back retired by John's ruling-as-mechanism (`/reserve`+`/buy` absent, not stubbed — you can't race a
verb that doesn't exist), the `gone` read carried to F's #4. Two new carried: **#6 orphaned atlas**,
**#7 "real" is one shop wide** (14/15 are mint — honest and marked, but the headline rests on one shop).
- **The gate that armed itself.** My R25 orphan gate SKIPped on `newtown_godverse` while no atlas keyed
there; G's mint atlases landed and it **began binding unattended** — 156,211 → **156,212**, and that +1 is
the gate arming itself. Near-miss carried: redhill's lift drops keyed shop **2286 "Empire Revival"**;
G stocked neighbour **2287**. Stocking 2286 today would orphan it.
## Round 25 (2026-07-17) — v5.0-alpha: the sweep vs Ruling 2 + the six pins (ledger #1)
All 8 tree reds cleared — **6 were my own gate asserting Ruling 2 away**, 2 were goldens awaiting my pin.
selfcheck **ALL GREEN 156,211/156,211**, scaffold + consistency green, classic frozen.
- **My R24 gate encoded an assumption that Ruling 2 falsified.** I built the identity arm believing a
godverse cache is *all* census shops. It's **mixed** by design — a keyed GODVERSE layer + an inherited OSM
texture layer that G measured to be load-bearing (census-only Red Hill: 306 m spacing warn + a poster
inside a kerb). So `newtown_godverse` (18 keyed + 54 texture) and `redhill_godverse` (10 + 27) were
**healthy towns my gate called broken**. Keyed asserts now run **per-layer over the keyed subset only**;
the "N/M missing" warn is gone. The one honest requirement left: a `godverse+osm` cache must have a
**non-empty** godverse layer (else it's an osm cache with the wrong `source`) — still a warn.
- **The uniqueness check was broken in both directions.** `new Set(all ids).size === shops.length` collapses
every `undefined` into one entry, so it failed structurally on mixed caches *and* could hide a real
duplicate behind the same collapse. Now over **defined ids only** — and, per the vacuous-gate law, proven
able to fail: the validator rejects dups at the front door, so the test feeds one **straight to the lift**
(`generatePlanOSM` doesn't validate) and asserts the predicate catches it.
- **New: the orphaned-atlas gate** — my own R24 finding, enforced rather than filed. A committed
`stock_godverse/<id>/` whose shop the lift dropped = real stock keyed to a shop that isn't in the town.
Node-side, subject-aware (SKIPs by name when a town has no atlas), so it catches in seconds what F's #7b
could only catch in a browser at the end of a round. **It already found one, benign today:** redhill drops
keyed shop 2286 "Empire Revival" (unstocked ⇒ no orphan ⇒ gate passes). Filed for G: stocking it at the
beta would orphan. Monster Robot Party is seated (lot 8).
- **The six pins**, cross-checked two ways before landing — byte-identical across two fresh processes, and
matching the values G stated independently in `86e2985`: `newtown_godverse` `0xcf69b387`/`0x1e266f60`,
`redhill_real` `0x6046700f`/`0x673785ee`, `redhill_godverse` `0xb2224939`/`0xc6f80e18`.
## Round 24 (2026-07-17) — v5.0-alpha: the identity field (ledger #2)
`shop.godverseShopId` — the optional key resolving `stock_godverse/<id>/`. Schema **stays v2**; absent
everywhere except godverse caches, so the 21 osm caches are untouched by construction. Published to
`web/assets/towns/README.md` (the doc G/E build to) + CITY_SPEC. selfcheck **ALL GREEN 152,030/152,030**
(+15), synthetic `0x3fa36874` frozen, **no golden moved** — by design (see below).
- **The brief's "the POS shop id IS the id" is false for the one shop that matters.** G's own docstring:
Monster Robot Party is **not in thriftgod's census**, so its id is its **dealgod store id 3962749**, while
census shops use thriftgod ids (max 2992). Two disjoint spaces on one field ⇒ built it as an **opaque
key**: no derivation from `id`, no equality assert, no mapping table (the R12 gigKey law).
- **The field had to survive the lift, or it was decoration.** `plan_osm` builds a *fresh* shop object and
copies nothing unknown, so a cache-only field would never reach the runtime — and the runtime reads
`plan.shops[]`, never the cache. It's now carried through, **conditionally**: absent ⇒ the key isn't
written ⇒ `JSON.stringify` byte-identical ⇒ **every pinned golden holds until G re-emits** (that re-emit
is the sanctioned move).
- **Arms split by hazard:** malformed → **error** (it's a path segment; `"3962749"` vs `3962749` is the
same coercion drift that broke the atlas contract), duplicate → **error** (two shops, one atlas = the
mis-stock charter risk #3 forbids), missing on a godverse cache → **warn + fail-soft to tier 0**.
- **Recorded departure:** the ledger said missing should be an *error*. It would invert the charter's
determinism boundary (the **town** failing to load because **stock** identity is absent), contradict risk
#3's own remedy ("fail-soft to tier 0"), and red the tree for C and E through all of wave 1 — G re-emits
in wave 2 and it's their namespace, not mine. Teeth kept where R23 says to put them: selfcheck **fails on
partial keying**, prints an explicit **SKIP + count** at zero. Flagged for Fable to overrule.
- **Filed for G before they start (measured, wave 1):** `redhill_godverse` can't contain Monster Robot from
**either** source. `godverse_town.py` takes roads from the OSM donor and shops from the census; the shop is
in neither — not in thriftgod's census (G's R23 finding), and **not in E's `redhill_real`** (I checked it
as it landed: 36 shops, **zero of type `record`**, nothing matching /monster|robot/). Needs explicit
injection, or F's #7b id-equality gate fails at the end of the round. Also flagged: the census bbox must
still clear `MIN_TOWN_SHOPS` (6) or the cache is rejected outright.
- **Pins deliberately NOT taken this round yet.** `redhill_real` is untracked/in-flight (E is still editing
`build_towns.py`), and `redhill_godverse` + the re-keyed `newtown_godverse` don't exist until wave 2.
Pinning a moving file buys a false green or a spurious red. The round's law says A pins these **batched**
that batch lands once E commits and G's caches are in. `redhill_real` reads clean meanwhile: valid, no
warns, 73.3 m spacing.
- **Housekeeping:** removed toowoomba's two stale pins (E retired the cache in R23 — a golden for a town
that can't load can never fire). E's attempt also proved my R23 spacing metric is **gameable**: the
re-bbox fixed the median (395.7 → 89.4 m) while hub density fell 3/12 → 2/12. Necessary, not sufficient —
D's hub test is the complement. **Ballarat (255 m) still flagged, still E's call; I pin whatever moves.**
## Round 23 (2026-07-16) — v5.0-alpha: the spacing warn (ledger #5)

View File

@ -28,7 +28,21 @@ considered and parked: nothing in v1 needs it, and the asset pipeline (GLB, web-
- **Metres. +Y up. Ground plane is XZ.** City origin (0,0) = centre of the main square.
- **Right-handed three.js defaults.** Buildings face their street; a facade's outward normal
points at its street edge. GLB convention: origin at base, facing **Z**, real-world scale.
points at its street edge.
- **THE TWO CONVENTIONS — they are different things, and conflating them cost nine rounds
(ROUND27). Read both:**
- **The FACADE convention (the lots contract, `lot.ry`): the visible shopfront is local `+Z`**
world `(sin ry, cos ry)`. This is B's canon and the de facto truth since v1 — `buildings.js`
draws the facade quad and door at local `z = +d/2`, `venue.js` seats the door-glow/queue/
posters there. **`lot.ry` aims +Z at `frontEdge`.**
- **The SIM/RIG convention (props, not lots): a GLB is modelled facing `Z`** (origin at base,
real-world scale) ⇒ rig-forward is world `(sin ry, cos ry)`. This describes *how a model is
authored*, **not which way a lot faces.**
- **They are opposites, so a check written in one silently passes over a world built in the
other.** CITY_SPEC:71 wrote the SIM convention into the LOTS contract by ambiguity; each
generator implemented one half; every golden stayed green over a town whose every shopfront
faced away. **Never assert a lot's facing against a spec line — assert it against the
geometry the renderer actually draws** (`selfcheck.js` now does exactly that).
- **Seeded everything, `Math.random()` is banned in generation code.**
`web/js/core/prng.js` is the only randomness source:
- `seedFor(citySeed, kind, id)` → stable uint32 (xmur3 hash of `"seed:kind:id"`).
@ -68,8 +82,11 @@ Schema v1 (Lane A owns it; extend, don't break):
// onto the block so consumers can theme it without a lookup (Lane A extension, non-breaking).
lots: [ { id, block, x, z, w, d, ry, frontEdge, use } ],
// use: 'shop' | 'anchor' | 'house' | 'yard' | 'stall' | 'infill'
// ry: Y-rotation (radians) so a GLB modelled facing Z ends up with its facade's OUTWARD normal
// pointing at frontEdge. World facing = (sin ry, cos ry). Lane B rotates the shell by ry.
// ry: Y-rotation (radians) so the lot's VISIBLE FACADE — local +Z — points at frontEdge.
// Facade/door/frontage normal = local +Z ⇒ world (sin ry, cos ry). Lane B rotates the shell by ry
// and draws the shopfront at local z = +d/2 (buildings.js), so +Z IS the street face. ROUND27.
// (The SIM/rig convention — a GLB modelled facing Z, world (sin ry, cos ry) — is a DIFFERENT
// thing and is documented as such below. It is not the lots contract. See the amendment.)
// frontEdge: id of the street edge this lot fronts (guaranteed to exist and be geometrically adjacent).
shops: [ { id, lot, type, name, sign, seed, facadeSkin, storeys, hours: [open, close], openLate? } ]
// type: see SHOP TYPES below. hours: [open, close], 24h integers, 0 ≤ open < close 23.
@ -256,9 +273,30 @@ night in v1; interior-mapping shader is a stretch goal).
> did not disappear. `gigs`, `weather`, `winmap`, `tram` now default **ON**; the default boot is the full
> experience, deterministic to the **gig golden `0xb1d48ea1`**. Each flag keeps a `=0` opt-out, and
> **`?classic=1`** forces all four off at once:
> - **Classic law (the new frozen regression target):** the `?classic=1` boot is byte-identical to the v2
> baseline **forever** — synthetic fingerprint `0x3fa36874`, zero gig layer, **zero fetch delta** (no
> - **Classic law (the frozen regression target):** the `?classic=1` boot is byte-identical to the v2
> baseline — synthetic fingerprint **`0x5f76e76`**, zero gig layer, **zero fetch delta** (no
> `custom_bands.json`, no `sit.glb`). The old "flags-off regression" gate is now the **classic-regression** gate.
>
> > **THE COVENANT AMENDMENT — ROUND27, THE FACADE FIX (John's ruling; the only amendment in five epochs).**
> > The covenant froze `0x3fa36874` "forever" from R16 to R27. That hash anchored **a wrong world**: every
> > shopfront in every town — synthetic and real — faced *away* from its street, because CITY_SPEC:71 wrote
> > the SIM convention into the LOTS contract (see *Units, axes*). The covenant did exactly its job for nine
> > rounds: it held the base town byte-identical. It just held it identical to something broken, and no gate
> > could see it — a self-consistent check over a self-contradicting spec passes under either convention.
> >
> > **The covenant's purpose is anti-drift, not the preservation of a defect.** So on John's ruling it is
> > **amended once, deliberately, in the open**: the anchor moves `0x3fa36874 → 0x5f76e76`, and every other
> > golden moves with it (gig `0xb1d48ea1 → 0xec7a2d39`, the three marched fixtures, all 23 real towns base
> > + gig — **51 pins in one commit**, per the amendment law). The classic town is now what it always claimed
> > to be: a street you can stand on and read the shopfronts. **From this hash forward the law reads as it
> > always did — byte-identical forever.** An amendment requires a John-level ruling and a demonstrated
> > world-level defect, not a convenience; this is the precedent, not an opening.
> >
> > *Exposed by the same fix (and fixed with it): `buildRealRoads` seated blocks at `KERB = 4 m` from the
> > centreline while a `main` carriageway is 10 m wide — its kerb is at 5 m. Every main-street building has
> > stood 1 m inside the road since R18. Invisible while nothing was drawn on that face; the moment the
> > shopfront turned around, its poster landed in the traffic lane. Blocks now seat behind the kerb per the
> > corridor law (`roadWidth/2 + FOOTPATH`). Pack drops 6.4% → 6.6%, counted.*
> - **Default law:** the no-flag boot pins to the gig golden, and every gig / district / continuity / cover
> smoke **and the budget law** (interior ≤350 draws · street ≤300 / 200k tris, at the busiest venue block)
> now run against the **default boot with all four flags live**. `?noassets=1` composes with it and stays
@ -426,6 +464,45 @@ auto-loads `web/assets/towns/*.json` — validates, runs the structural + gig su
**Provenance:** OSM is **ODbL**; attribution ships with the data (E owns `SOURCES.md` + the `license`/
`attribution` fields; raw Overpass responses cached so re-runs never re-fetch).
### `godverseShopId` — the stock identity (v5.0-alpha, ROUND24 ledger #2; A + G)
The optional shop key that resolves **`stock_godverse/<godverseShopId>/`**. **Schema stays v2** — optional
field, absent everywhere except godverse caches, so every shipped cache stays valid *by construction*.
**It is NOT `shop.id` and NOT "the census id"** — the round doc's "the POS shop id IS the id" holds for
census shops but breaks on the shop the alpha is about. Two disjoint id spaces ride one opaque field:
census shops use thriftgod `shop.id` (max **2992**), while **Monster Robot Party is not in thriftgod's
census at all** and uses its **dealgod store id 3962749**. So: never derive it from `id`, never assert
equality, never build a mapping table (the R12 gigKey law — one key, zero mapping). **G emits, all read.**
**A godverse cache is MIXED, by design — RULING 2 (confirmed as law, ROUND25).** It carries a **keyed
GODVERSE layer** (census shops + injected shops like Monster Robot) **plus an inherited OSM texture layer
that is legitimately unkeyed**. G measured that the texture layer is load-bearing, not cosmetic:
census-only Red Hill shipped a 306 m spacing warn and a poster inside a kerb; with the texture layer it
passes. **So "shops without an id" is the design, and no gate may assert it away** — mine did, in R24, and
failed 54/72 on a healthy town.
**`validateTownCache` arms** (split by hazard, not tidiness): a **malformed** id (not a positive integer)
is an **error** — it names a path segment, and `"3962749"` vs `3962749` is exactly the coercion drift that
broke the atlas contract; a **duplicate** id is an **error** — two shops sharing one atlas *is* mis-stocking
(charter risk #3: "never mis-stocked"); a godverse cache with an **entirely empty GODVERSE layer** (no id on
any shop) is a **warn** — it's an osm cache wearing the wrong `source`, and it stays a warn because the
charter's determinism boundary says *world = seeded and gated, stock = data tiers*: a town must never fail
to load because its **stock** identity is absent. Tier 0 is the designed ladder, not a defect.
**The `selfcheck` arm runs per-layer** — every assert is over the **keyed subset only**: the layer reaches
the plan, its ids are **unique over defined ids only** (the R24 version compared a Set of *all* ids, which
collapses every `undefined` into one entry — it failed structurally on a mixed cache *and* could hide a real
duplicate behind that collapse), and the lift invents no identity. Plus the **orphaned-atlas gate**: a
committed atlas under `stock_godverse/<id>/` whose shop the lift dropped means real stock keyed to a shop
that isn't in the town — it fails, and SKIPs by name when a town has no atlas. Dropped keyed shops are
printed, never silent (the R19/R20 drop-and-count ruling).
**It survives the lift:** `plan_osm` copies it onto `plan.shops[]` (the runtime reads the plan, never the
cache — otherwise the field is decoration and F's per-shop `base` can never resolve). Absent ⇒ the key is
**not written**, so `JSON.stringify` is byte-identical and **every pinned golden holds until G re-emits**;
that re-emit is the sanctioned golden move.
### Schema v2 — real roads (v4.0-alpha, ROUND18 REAL ROADS)
> The gap the scout left: the marched fallback lays real shops onto **synthetic** parallel avenues.
@ -477,6 +554,79 @@ nothing outside the cache-schema path).
> count beats teleport.** The frozen set is unmoved by the entire epoch: synthetic `0x3fa36874`, gig
> `0xb1d48ea1`, marched fixtures, classic.
## The stock ladder (v5 — THE REAL SHOP; frozen at `v5.0`, ROUND27 ledger #6)
> **v4 made the towns real; v5 makes the shops real.** The map-level contract above is unchanged by this
> epoch: **stock is DATA, not world.** The charter's determinism boundary is the load-bearing line —
> *world = seeded, frozen, golden-gated; stock = data tiers riding the asset law.* Nothing in this section
> may move a plan golden, and nothing in it did: the whole epoch left synthetic `0x3fa36874`, gig
> `0xb1d48ea1`, the marched fixtures and classic untouched.
**The tier ladder.** Each rung is fully playable and degrades **softly** to the one below — the fail-soft
pattern proven since `?noassets` on day one, made structural:
| tier | what | flag | deterministic? |
|---|---|---|---|
| **0 · parody** | seeded canvas sleeves (v1) | any boot | yes — the QA baseline |
| **1 · static real** | per-shop atlases baked from dealgod/POS | `?stock=real`, **no server** | yes — atlases are files |
| **2 · live GODVERSE** | thriftgod read-only endpoints; sold-means-gone | server reachable | **no — by design** |
**The offline law (charter law #1) is the epoch's gate, not a footnote:** the server *enriches, never
gates*. No boot path, gate, or tag may depend on a reachable server. Tier 2 is therefore **never inside a
golden or a byte-identical assert**; it gets its own smoke class whose server lifecycle the gate itself
controls, so the *assertion* is deterministic even though the *stock* isn't.
**`sourcing` is not `tier`** (C's ruling, R26 — and it's the distinction that keeps the ladder honest). The
rung and the provenance are different axes, and both ship in every atlas index:
- **`tier: 1`** — the ladder rung. A minted atlas is *still* a static file, so it is *still* rung 1.
- **`sourcing: "real" | "mint"`** — where the contents came from. **`real`** = a POS snapshot (Monster Robot
Party's actual crates). **`mint`** = a seeded `random.Random(shop_id)` sample of real dealgod listings —
*plausible, not that shop's stock*. Only one shop in the world has a POS; minting is how the other keyed
shops get distinct crates **without pretending to data we don't hold**. Current pack: **15 keyed atlases =
1 real + 14 mint**. No gate, doc, or human may read a mint crate as real — that is the whole point of the
field, and gates assert on it.
**The id-space fences.** Three namespaces meet at the crate, and **every collision this epoch came from
comparing across them**. They are fences, not decoration:
| space | form | scope | fenced from |
|---|---|---|---|
| **plan `shop.id`** | small int, `0..N` per town | one CityPlan, local | `godverseShopId`**numerically overlapping** |
| **`godverseShopId`** | census id (**≤2992**) dealgod store id (e.g. **3962749**) | the stock namespace | its two halves are numerically **disjoint by construction** (G) |
| **atlas slot id** | **`sku_<POS id>`** / **`mint_<listing id>`** | one atlas index | each other, by **prefix** |
- **plan id vs `godverseShopId` — never compare a bare number across them.** They are *semantically*
disjoint namespaces that *numerically overlap*, which is the trap: in `redhill_godverse` today, bare
**`31`** is **"Wholefood Cafe"** as a plan id and **"Silky Oak Furnature"** as a godverse id. That exact
ambiguity made F's `enterShop(31)` enter the wrong shop and a soft-fall assertion pass **vacuously on the
wrong subject**. Verified still live in the data — so this is a standing rule, not a fixed bug.
- **`sku_` vs `mint_` is a sourcing-scoped namespace fence** (C §7.2a as amended, R26 wave 4). A crate
wearing the other sourcing's prefix **fails**. The fence is *stronger* than the rule it replaced: the easy
green was deleting the prefix check; instead the prefix now proves its sourcing.
- **Positional ids are banned** (R25): `rec_0000…` numbering collides across packs by construction, which
is what made the integrator's own id-equality gate vacuous. Slot ids are sku-derived and unique across
packs, stable across re-emits.
**The manifest — `web/assets/stock_godverse/index.json`** (G emits, F consumes, E validates, C's §7.2d).
It declares **which `godverseShopId`s carry atlases** (`types[]` + `sourcing` per entry; mint included).
**Existence is declared, never probed.** This is the root fix for a real collision between two laws:
fail-soft-by-404 vs the zero-console-errors law — the runtime used to probe blind and eat 404 noise,
tolerated by attribution, which is a workaround, not a design. No manifest entry ⇒ no fetch ⇒ **zero 404s,
no exceptions**. Consistency is gated both ways (an atlas on disk absent from the manifest **fails**, and
vice versa — vacuous-gate law shape). Today: **15 entries, 15 dirs on disk.**
**Lane A's seam to all of it** is exactly one field — **`godverseShopId`** (documented above): it rides the
town cache, survives the lift onto `plan.shops[]`, and is what the runtime resolves `stock_godverse/<id>/`
from. **`'godverseShopId' in shop` is the tier test**: present ⇒ tier-1+ candidate, absent ⇒ tier 0. The
key is *absent, not undefined*, on every unkeyed shop — which is why the entire stock epoch moved **no plan
golden**. `selfcheck` gates the seam per-layer (Ruling 2), including the **orphaned-atlas** check: a
committed atlas whose shop the lift dropped is real stock keyed to a shop that isn't in the town.
**Ruling 2 (law):** a godverse cache is **deliberately mixed** — a keyed GODVERSE layer + an inherited OSM
texture layer that is legitimately unkeyed. G measured it load-bearing: census-only Red Hill was
unshippable (306 m spacing warn, a poster inside a kerb). **No gate may assert it away** — mine did, and
called two healthy towns broken.
## Shop types v1 (registry lives in `web/js/core/registry.js`, Lane A writes it, all lanes read)
> **`web/js/core/registry.js` is the machine-readable source of truth** — import `SHOP_TYPES` for

View File

@ -1,11 +1,15 @@
# G3 — THE ECONOMY BACKEND (design doc, DRAFT for ratification)
# G3 — THE ECONOMY BACKEND (design doc)
*Lane G (ultra), R23. Drafted per the v5 charter ([V5_REAL_SHOP.md](V5_REAL_SHOP.md)) ledger #2.
Ratification: both Fables + John, at R24 kickoff. **Nothing server-side ships before that.** This
round is paper — plus the tier-1 artifact that made the paper honest (G2a, merged this round).*
*Lane G, R23 draft. Drafted per the v5 charter ([V5_REAL_SHOP.md](V5_REAL_SHOP.md)) ledger #2.*
Status of each section: **[MEASURED]** = verified against the real databases this round.
**[DESIGN]** = proposed, not built. **[JOHN]** = a decision reserved to John by charter law.
**§7 (THE WRITE-BACK QUESTION) IS RATIFIED — John, 2026-07-17, R24 kickoff: A THEN B.** v5.0 ships
sandboxed (a game purchase never touches real inventory); reservation is chartered as a v5.x
upgrade behind a hold budget John sets; full write-back is not chartered. Charter law #4 is
satisfied. **The rest of this doc remains DESIGN and still needs both Fables' sign-off; no
server code ships before that.**
Status of each section: **[MEASURED]** = verified against the real databases.
**[DESIGN]** = proposed, not built. **[RULED]** = decided by John under charter law.
---
@ -89,30 +93,71 @@ boot path, gate, or tag that may touch the network.
---
## 4. API surface [DESIGN]
## 4. API surface [AS-BUILT — R27, `pipeline/godverse_server.py`]
Read-only in v5.0; the write verbs are drafted here but **gated on §7**.
Read-only, per §7's ruling. **The write verbs are not "gated" — they do not exist.**
```
GET /godverse/v1/shop/<godverse_id>/stock?crate=<id>&since=<iso>
-> { shop_id, crate, tier, items:[{sku, slot, release_id, artist, title,
price, condition, sleeve_cond, cover, state}],
gone:[sku], etag, served_at }
GET /godverse/v1/shop/<godverse_id>/crates -> crate list + counts (the rotation menu)
POST /godverse/v1/reserve { sku, session } -> { ok, holds_until } [GATED — §7]
POST /godverse/v1/buy { sku, session, hold } -> { ok, receipt } [GATED — §7]
GET /godverse/v1/health -> { ok, tier, pos_db, shops:[godverse_id] }
GET /godverse/v1/shop/<godverse_id>/stock?since=<etag>
-> { shop_id, tier, crate, items:[{id, slot, artist, title,
price, condition, sleeve_cond, state}],
gone:[id], counts, etag, served_at } · 304 when ?since= matches
GET /godverse/v1/shop/<godverse_id>/crates -> { shop_id, tier, crates:[{id,name,label,count}], etag }
GET anything else -> 404 (the ruling is in the body)
POST/PUT/PATCH/DELETE anything -> 501 — no write handler exists in the process
```
**Three corrections this doc owes to the code (measured R27, not reasoned):**
1. **`gone:[sku]``gone:[id]`.** As ratified, §4 said bare skus. That wording predates R25's
`sku_<POS sku>` ids and R26's **id-namespace fence law**, and a bare number crossing two id
spaces is precisely the bug that law exists to stop. `gone[]` now carries the atlas's own `id`
verbatim, so the client does `gone.includes(item.id)` with zero transformation. *This doc was
the stale rule meeting a case written after it — the epoch's signature failure, and my turn.*
2. **`items[]` drops `release_id` and `cover`.** `cover` would edge the API toward serving
pixels — it never should (fence #2); `release_id` identifies the *pressing*, and tier 2 speaks
only in *copies* (the sku). Both were design-time noise.
3. **Write verbs answer 501, not 404.** F asserts the number, so it matters: the class defines
`do_GET` and nothing else, so the stdlib refuses any other method before routing exists. **501
is the stronger proof** — a 404 means a handler ran and declined; 501 means there is no write
handler in the process. Adding `do_POST` to answer 404 would implement the forbidden thing.
Design rules:
- **The atlas is never served by the API.** Covers stay static files (tier 1). The server sends
*facts* (what's still there, what it costs), never pixels. A tier-2 shop with a dead server is
visually identical to tier 1 — that's the fail-soft.
- **`gone[]` is the whole point of tier 2.** The client already holds the tier-1 crate; the server
says only what changed. A sold record is a `sku` in `gone[]``collapseBuyItem()` (already
says only what changed. A sold record is an `id` in `gone[]``collapseBuyItem()` (already
built, R9) zero-areas the sleeve. Sold-means-gone is a *diff*, not a re-fetch.
> ### ⚠ `gone[]` HAS NO SUBJECT IN PRODUCTION TODAY — measured R27, and it shapes F's gate
> The mechanism is built and correct. The **data has no movement**: Monster Robot Party's POS
> records **5,567 sales ever** (400800/month, Dec 2025 → Jun 2026) but **26 in July and none
> since 2026-07-01**; the POS hasn't been *written* since **2026-07-09** (the 07-16 dump is fresh;
> its data is stale). And **0 of crate 550's 120 records have ever sold**. So against the real POS
> `gone` is `[]`, permanently, until the shop trades again.
>
> **Therefore, per the vacuous-gate law: F's sold-means-gone arm must not assert over the live POS
> — it would pass without touching its subject.** Two honest routes, and G built the second:
> 1. Assert `gone == []` against the real POS and **print the reason** — true, but it proves only
> that nothing sold.
> 2. **Point the server at a labelled fixture** (`godverse_server.py make-fixture --sold 3` →
> `recordgod_fixture`, a `createdb -T` clone with N of the crate's own real skus marked sold).
> Verified: `counts {baked:120, live:117, gone:3}`, the three ids absent from `items[]`. This
> proves the **mechanism** — computed → sent → consumed → sleeve vanishes — deterministically,
> which is what a gate is for. Production still reads the real POS, untouched.
>
> **A fixture is not a lie; a green assert over an absent subject is.** The epoch has held four
> tags on exactly that distinction.
- **`etag`/`since`** so a re-enter is a 304, not a payload.
- Shop id is the **godverse id** (§1a), never a POS internal id.
- Shop id is the **godverse id** (§1a), never a POS internal id. **R24 naming, as landed:** the
town cache carries `shop.godverseShopId` (A's schema field, camelCase, read by the JS runtime);
the atlas index carries `shop.godverse_id` (snake_case, what E's `validate_atlas.py` requires).
Same id, two artifacts, two house conventions — the API uses `godverse_id` on the wire to match
the Python/JSON side it is served from. Recorded rather than silently reconciled: if the
integrators want one spelling, it is a one-line change in each emitter, not a design question.
---
@ -153,13 +198,47 @@ whichever dataset actually *has* the shop, and mismatches must fail soft, never
---
## 7. THE WRITE-BACK QUESTION [JOHN — charter law #4]
## 7. THE WRITE-BACK QUESTION **RULED: A THEN B** (John, 2026-07-17, R24 kickoff)
**Does an in-game purchase ever touch real thriftgod/recordgod inventory?**
> ### THE RULING
> **v5.0 ships on Option A (sandbox).** An in-game purchase **never** touches real
> monsterrobot/Square inventory. Sold-means-gone is per-session, tier-1 semantics.
> **Option B (reservation) is CHARTERED as a v5.x upgrade**, behind a hold budget John sets.
> **Option C is not chartered** — it happens only as a deliberate business launch, if ever.
>
> Charter law #4 is hereby satisfied: the write-back question has an explicit, John-ratified
> answer, and until B ships the standing state is **read-live, sell-sandboxed**.
This is now a live question, not a hypothetical: `monsterrobot` is a **real trading shop** whose
POS syncs to **Square**, with real customers buying these exact records off the real shelf. Stated
per charter law #4 with trade-offs and **no default**.
**What this makes law for v5.0 (binding on the server work, when it starts):**
- The API's `POST /reserve` and `POST /buy` (§4) stay **unbuilt and unrouted**. Not stubbed
behind a flag — absent. A sandbox that can be flipped on by a config typo is not a sandbox.
- The game's buy path terminates in the client's wallet. **No code path from a game purchase to
`inventory.in_stock`, `sold_date`, `hold_expires_at`, `qty`, or Square exists.** The tier-2
reader connects to the POS **read-only** — enforce it at the credential, not in a code review:
a role with `SELECT` on `inventory`/`crate`/`disc_cache` and nothing else (this also carries the
§9 PII fence in the same grant).
- A gate worth its name here (the vacuous-gate law): assert the write verbs **404**, and that the
tier-2 role's grants are read-only. A test that "we didn't call buy()" proves nothing.
**Option B's charter (v5.x — the preconditions, so it can't slide in quietly):**
1. **The hold budget — John's number, not yet set.** Max concurrent game-held SKUs, and max hold
duration. Until both are set, B is not startable. *(Open item, not a blocker for v5.0.)*
2. **The shop always wins.** If a held record sells in-store via Square, the game loses the hold,
silently and immediately, and the player is told the truth ("someone bought it in the shop") —
the one race policy that is never up for negotiation.
3. **Staff-visible reason code** on every hold (`held: GODVERSE`), so a human at the counter can
see why a record is out of the crate and clear it by hand.
4. **Soak first.** B rides plumbing that has soaked, not plumbing that tags a round — Fable's
framing, and the reason A ships first.
*The options as framed for the ruling are preserved below, since the trade-offs are the record of
why A-then-B was chosen.*
---
**The question as put:** does an in-game purchase ever touch real thriftgod/recordgod inventory?
Live, not hypothetical: `monsterrobot` is a **real trading shop** whose POS syncs to **Square**,
with real customers buying these exact records off the real shelf.
Relevant measured facts: the POS already has `in_stock`, `sold_date`, **`hold_expires_at`**, `qty`
and `square_item_id`. A reservation primitive therefore already exists — the question is whether
@ -190,8 +269,8 @@ A purchase marks the real SKU sold and syncs to Square.
sales channel (real payment, tax, postage, fulfilment, refunds, chargebacks); a bug destroys
real stock; a game wallet is not money. This is a business decision, not an engineering one.
**Lane G asks nothing and recommends nothing here.** Until ratified: **tier 2 is read-live,
sell-sandboxed** (the charter's standing state, Option A by default *only* because law #4 says so).
*(Lane G asked nothing and recommended nothing here — the options above are the record as put to
John. **Ruled A-then-B; the binding terms are at the top of this section.**)*
---
@ -242,6 +321,8 @@ class: reachability, fail-soft, latency. Kill the server mid-session and the gam
stops ~2.3 km south of Red Hill. The crate has no street until E's `build_towns.py` gets a
Red Hill entry (~`center: (-27.4553, 153.0064), span_km: 2.4`) and one Overpass fetch. That's
E's file and an outward call — **G has not touched either.**
3. **[John, R24]** §7, the write-back question.
3. ~~**[John, R24]** §7, the write-back question.~~ **RULED 2026-07-17: A then B** (§7). Carried
forward: **the hold budget is John's number and is not yet set** — Option B is not startable
until it is, which is a v5.x item, not a v5.0 blocker.
4. **[Both Fables]** §9's beta scoping — depth (one real shop, 357 crates) vs breadth (more
shops, which needs other people's data).

View File

@ -2,6 +2,243 @@
Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`.
## Round 30 (2026-07-18) — THE ROTATION BOUNDARY VERIFY (ledger #6): **HOLDS**
### → Fable / F: `day` rides RUNTIME streams only — plan generation cannot see it. Measured, not read.
**Verdict: the seeded-rotation boundary HOLDS.** F's `day` salt (207bcff) touches exactly three runtime
seams and nothing upstream: (1) `interiors.js:178` `ctx.stockSalt` from `opts.stockDay`, consumed ONLY in
`layout.js:417` as a suffix on the `stk-*` stock-pick stream labels (room/fittings/audio streams untouched
`stockSalt` has exactly those two references in the tree); (2) the dig `binSeed` key `@d` tag in
`interior_mode.js` (same null-gating: no game / day 1 / real-sourced ⇒ absent); (3) `gig_state.setWeekNight`,
which only **selects** a night from `plan.gigs` — the whole 7-night week is generated at plan time
(`gigs.js:227` `for night < NIGHTS`) from citySeed streams; day picks, never feeds.
**How measured (three falsifiable legs):**
1. **Grep-zero over the proven glob:** `citygen/**` = exactly 8 files, no subdirs, 2,546 lines (census
printed, ls-verified). `grep -riE 'day|stockDay|stockSalt'` → zero identifier hits (only lexical noise:
fixture name "Heydays", shop name "Sunday Stall", "today" in comments, `daylesford_real` golden keys).
Non-vacuous: the same pattern hits `stockDay` 4× in `world/interior_mode.js`.
2. **Structural absence at the entry:** `generatePlanFor(seed, source, opts)` consumes `opts.{town, gigs,
customBands, cache, report}` only (grepped every `opts.` in citygen); both shell call sites
(`index.html:127`, `map.html:315`) pass `{town, gigs, customBands}` — no day exists to pass.
3. **Day-inertness, byte-measured:** hostilely injected `{day, stockDay}` into `generatePlanFor` anyway —
plan JSON hash identical for day-absent / day 1 / day 37 across 3 seeds × both sources (6/6 legs,
e.g. synthetic 20261990 `0xec7a2d39` all three ways). Even a rogue caller can't leak day into a plan.
**Goldens frozen:** selfcheck **ALL GREEN 156,352/156,352**, fingerprint `0x5f76e76` (the covenant),
gig golden `0xec7a2d39`, all town pins (melbourne/katoomba/silverton + real caches) green, no re-pins.
Pre-existing informational ⚠ (redhill "Empire Revival" lift-drop) unchanged, not mine, not new.
## Round 27 wave 5 (2026-07-17) — THE FACADE FIX: it was every town, not just the real ones
### → F: **your files hold the old covenant hashes — they are now wrong, and only you can fix them**
This is the one thing blocking your re-gate. `scaffold_check` is **RED** until you re-pin, and I can't
touch your files. Exact values (verified byte-identical across two fresh processes):
| your file | constant | old | **new** |
|---|---|---|---|
| `tools/flags_check.py:30` | `GOLDEN_HASH` | `0x3fa36874` | **`0x5f76e76`** |
| `tools/flags_check.py:31` | `GIG_GOLDEN` | `0xb1d48ea1` | **`0xec7a2d39`** |
| `tools/qa/scaffold_check.mjs:231` | `TOWN_GOLDENS.melbourne` | `0x34cfdec0` | **`0x9c7e76b3`** |
| `tools/qa/scaffold_check.mjs:231` | `TOWN_GOLDENS.katoomba` | `0x0f652510` | **`0xf3aafec8`** |
| `tools/qa/scaffold_check.mjs:225` | the comment quoting all three | — | same three |
| `tools/qa/tour_v5.py:42`, `tour_shots.py:10` | captions quoting `0x3fa36874` | — | **`0x5f76e76`** |
Also: `flags_check.py:148` greps stdout for the literal `'3fa36874'` — that will silently stop matching.
### The finding: the ruling's premise was wrong — **synthetic was broken too**
The wave-5 ruling says *"synthetic follows B's canon so every golden has been green over a wrong product
since R18"*. Half right, and the wrong half is load-bearing. **Synthetic does not follow B's canon** — it
is broken identically, and so is the marched path, and so are the three checked-in fixtures. It is not
"every real town since R18": it is **every town, every path, since v1**. Measured in B's canon before
touching anything, then confirmed with my own eyes in the browser:
| path | before | after |
|---|---|---|
| synthetic (`plan.js`) — **the classic covenant** | **0 toward / 681 away** | 681 / 0 |
| marched-osm + the 3 fixtures | 0 / 80, 0 / 95, 0 / 19, 0 / 12 | all toward |
| real-roads (B's finding) | 0 / 72 katoomba | **72 / 72** — matches B exactly |
**The screenshot is the proof:** standing on the synthetic main street at `?classic=1`, lot 0 — three blank
grey boxes. Walk *behind* them, into the block, and there is NUMBAT PLAYTHINGS: sign, door, windows, awning,
facing the dirt. Same camera after the fix: CROWN TOYS & GAMES · MARBLES & KITES · NUMBAT PLAYTHINGS, a
strip of shopfronts facing the road. **637 of 681 synthetic lots had their facade pointing at no street at
all.**
**Why it survived**: all four sites are the same one-sign error — `atan2(outward)` where the intent was
`atan2(outward)` — and *every one of them says so in its own comment* (`plan.js:122` "facade faces back
down the outward normal, to the street"). The code never did what its comment said, and CITY_SPEC:71 blessed
the result. Nobody was wrong; the spec was.
### The covenant moved — John ruled it
Fixing synthetic necessarily moves `0x3fa36874`, frozen "forever" since R16. That was not mine to decide.
**John's ruling: amend the covenant, fix all paths** — the covenant is anti-drift machinery, not a defect
preservative. **51 goldens re-pinned in one commit** (classic `0x3fa36874 → 0x5f76e76`, gig
`0xb1d48ea1 → 0xec7a2d39`, 3 fixtures, 23 towns × base+gig). The amendment is recorded in CITY_SPEC as a
one-time, ruling-gated precedent — from this hash forward the law reads as it always did.
### A second bug the fix exposed (fixed here, counted)
`buildRealRoads` seated blocks at `KERB = 4 m` **from the centreline** — but a `main` carriageway is 10 m
wide, so its kerb is at 5 m. **Every main-street building has stood 1 m inside the road since R18.** The
footprint never moved; only which face is the front — so it was invisible until the shopfront turned around
and its poster landed in the traffic lane (1.07 m). Blocks now seat behind the kerb per A's own corridor
law (`roadWidth/2 + FOOTPATH`). Pack drops **6.4% → 6.6%**, counted, not hidden.
### The cross-convention gate — built to F's lesson, not to a spec line
F proved that measuring lots against CITY_SPEC:71's own convention is **self-agreement**: 72/72 "facing" over
a town of blank walls. So the new gate does not restate a spec line. It **replicates Lane B's own placement
maths** from `buildings.js` (`toWorld(lot, 0, y, d/2 + 0.02)`) and asks a physical question neither spec line
can fudge: *does the quad the renderer actually draws land nearer the street than the lot centre?* It fires
if A's `ry` sign drifts **or** if B's `zf` sign drifts. It would have caught this on day one of v1.
## Round 27 (2026-07-17) — v5.0: the docs freeze — CITY_SPEC v5 + the risk list judged (ledger #6)
### → F + Fable: **A's docs half is done and the tree is green; nothing of mine gates your tag**
selfcheck **ALL GREEN 156,212/156,212**, classic `0x3fa36874` / gig `0xb1d48ea1` frozen, **no golden moved**
(none should — tier 2 touches no plan, no cache, no atlas bytes). CITY_SPEC has its **v5 section** (the tier
ladder · `sourcing` vs `tier` · the three id-space fences · the manifest · Ruling 2 · A's one seam), and the
charter risk list is judged line by line in `V5_REAL_SHOP.md`.
### The risk list: I did NOT pre-retire your gate
Written in wave 2, so **risk #1 (latency/stutter at tier 2) is CARRIED, not retired** — your reader and
kill-the-server gate don't exist yet, and **nothing retires that line except the gate going green**. Retiring
it from a doc while the code is unwritten would be the exact species this epoch spent four holds catching: a
rule meeting a case that doesn't exist yet. #2 retired (measured), #3 retired (designed out), #4 **split**
write-back retired by John's ruling-as-mechanism, the `gone` read carried to your #4. Two new carried lines
(#6 orphaned atlas, #7 "real" is one shop wide).
### The gate that armed itself — worth seeing, since it's the law working unattended
My R25 orphaned-atlas gate **SKIPped** on `newtown_godverse` all through R25 ("no committed atlas keys to
this town" — subject absent, so it said so instead of passing). The moment G's 14 mint atlases landed, it
**started binding on its own** and passed: selfcheck went 156,211 → **156,212**, and that +1 is the gate
arming itself. Nobody edited it. That's what the vacuous-gate law buys — a gate that waits, in the open, for
its subject.
**Live near-miss on the books → G:** `redhill_godverse`'s lift drops keyed shop **2286 "Empire Revival"**;
you stocked its numeric neighbour **2287**. Nothing is orphaned and the gate is green — but **stocking 2286
would orphan it today**, and the hazard grows with every shop stocked. Carried as risk #6.
### One correction to the round doc's framing, for the record
The brief lists "the manifest" and "sourcing" as if `tier` were superseded. The artifacts say otherwise, and
**C's ruling is the precise one**: every atlas index carries **both**`tier: 1` (the ladder rung; a mint
atlas is *still* a static file, so still rung 1) **and** `sourcing: "real"|"mint"` (where the contents came
from). They are different axes. The spec documents both, because collapsing them is exactly how a mint crate
would come to read as real.
## Round 25 (2026-07-17) — v5.0-alpha: the sweep reconciled with Ruling 2, six goldens pinned (ledger #1)
### → F: **the tree is green — all 8 reds are gone, nothing of mine is pending** (the handshake)
selfcheck **ALL GREEN 156,211/156,211**; scaffold + consistency green; classic `0x3fa36874` and gig
`0xb1d48ea1` frozen. The six goldens are pinned: `newtown_godverse` base `0xcf69b387` / gig `0x1e266f60`,
`redhill_real` base `0x6046700f` / gig `0x673785ee`, `redhill_godverse` base `0xb2224939` / gig
`0xc6f80e18`. Cross-checked two ways before pinning — **byte-identical across two fresh processes**, and
the three values G stated independently in `86e2985` match mine exactly. **Your third attempt is unblocked
from my side.**
Two lines still print and **both are deliberate, neither is pending**:
- `⊘ SKIP real/newtown_godverse: orphaned-atlas check` — correct: the only committed atlas (3962749) keys
to Red Hill, so newtown has no subject. The vacuous-gate law says say so rather than pass quietly.
- `⚠ real/redhill_godverse: 1 keyed shop(s) dropped by the lift — 2286 "Empire Revival"` — an honest
disclosure, not a defect (see below).
### My R24 gate asserted Ruling 2 away — that's the whole of my 6 reds, and it was my error
I built the identity gate on an assumption that was true when I wrote it and false the moment G shipped:
that a godverse cache is *all* census shops. Ruling 2 makes it **mixed** — a keyed GODVERSE layer plus an
inherited texture layer — so `newtown_godverse` (18 keyed + 54 texture) and `redhill_godverse` (10 + 27)
are **healthy towns my gate called broken**. Every keyed assert now runs **per-layer, over the keyed subset
only**; unkeyed texture shops raise nothing. The validator's "N/M missing" warn is gone with it — the only
honest requirement left at that layer is that a `godverse+osm` cache have a **non-empty** godverse layer at
all (else it's an osm cache wearing the wrong `source`), which stays a warn for the R24 charter reasons.
### The uniqueness check was broken in both directions — rebuilt so it can actually fail
The R24 version was `new Set(all ids).size === shops.length`. Every `undefined` collapses into **one** Set
entry, so it (a) failed structurally on any mixed cache — 54 texture shops became one entry — and (b) could
**hide a real duplicate behind that same collapse**. It now runs over **defined ids only**. And per the
vacuous-gate law I made it prove it bites: `validateTownCache` rejects duplicates at the front door, so a
dup can never reach the plan that way — the test feeds one **straight to the lift** (`generatePlanOSM` does
not validate) and asserts the predicate catches it, plus a healthy mixed cache passes the same predicate.
A check nobody can watch fail is not a check.
### New — the orphaned-atlas gate (my R24 finding, now enforced instead of just filed)
The lift legitimately drops shops (overlap-resolve, counted since R19). Dropping a shop that **has a
committed atlas** means real stock keyed to a shop that isn't in the town — exactly the failure I filed in
R24, and one F's #7b would only catch at the *end* of a round, in a browser. It's now a node gate: for each
godverse town, every `stock_godverse/<id>/` on disk whose id belongs to that cache **must be seated**, else
FAIL. Subject-aware — SKIPs by name when a town has no atlas.
**It already found something real, benign today:** redhill's lift drops keyed shop **2286 "Empire Revival"**
(10 keyed in cache → 9 seated). It has no atlas, so nothing is orphaned and the gate passes. **→ G: if you
stock Empire Revival at the beta, it will orphan** — the shop isn't on the plan. Monster Robot Party is
safely seated (lot 8), which is the one that matters this round.
## Round 24 (2026-07-17) — v5.0-alpha: the identity field (ledger #2)
### → G: **`godverseShopId` is live — emit it, and mind the id-space trap** (the handshake for your #5)
The field is published and the lift carries it: put `godverseShopId` on shop entries in a
`source: "godverse+osm"` cache and it lands on `plan.shops[]`, which is what F's per-shop `base` reads.
Contract in `web/assets/towns/README.md`; `validateTownCache` enforces it; selfcheck gates it.
**The round doc says "the POS shop id IS the id — no mapping table". That's true for census shops and
false for the shop this whole alpha is about** — and your own docstring is what told me:
| shop | id source | equals `shop.id`? |
|---|---|---|
| census shops (`newtown_godverse`) | thriftgod `shop.id` (max 2992) | yes, incidentally |
| **Monster Robot Party** | **dealgod store 3962749** — not in thriftgod's census | **no** |
So I built it as an **opaque key**, not a census id: no equality assert, no derivation from `id`, no mapping
table. Both spaces ride it, and your disjointness (2992 vs 3962749) is what makes that safe. **Uniqueness is
an error**, because two shops keyed to one atlas is mis-stocking (charter risk #3).
**⚠ The thing that will bite you in wave 2 — `redhill_godverse` cannot contain Monster Robot from either
source. Measured, not inferred.** `godverse_town.py` takes **roads from the OSM donor** and **shops from
thriftgod's census**. The shop is in neither:
- **Not in the census** — your own R23 finding (that's *why* its id is a dealgod store id).
- **Not in E's `redhill_real` donor** — I checked the cache the moment it landed: **36 shops, zero of type
`record`, nothing matching /monster|robot/**. OSM's Red Hill doesn't know the shop exists.
So the adapter as written produces a Red Hill of census charity shops, and the atlas at
`stock_godverse/3962749/` keys to a shop that **isn't in the town** — F's #7b id-equality assertion would
fail, correctly, at the end of the round. **Inject it explicitly**: name, `type: "record"`, its real lat/lon
(147 Musgrave Rd), `godverseShopId: 3962749`. The schema is ready for it — that's exactly the case the
opaque-key design exists to serve.
**Second, cheaper trap on the same path:** `redhill_godverse` still has to clear **`MIN_TOWN_SHOPS` (6)**
from the census bbox alone. Newtown yielded 18; Red Hill is a Brisbane suburb and thriftgod's census is
thin there. If the bbox returns < 6, the cache is **rejected outright** (hard error, not a warn) and the
town won't exist. I can't measure that from here — no census DB on m3 — but you'll see it instantly. If it
bites, the count includes any shop you inject.
**E's donor is otherwise good for you:** valid, no warnings, **73.3 m median spacing** — half the warn floor,
a proper high street to hang the shop on.
### → F: the seam you need for #6a
`plan.shops[].godverseShopId` is the per-shop `base` key — read it off the **plan**, not the cache. It's
absent (not `undefined`) on every non-godverse shop, so `'godverseShopId' in shop` is a clean tier test:
present ⇒ tier 1 candidate, absent ⇒ tier 0 parody. That absence is also why **no golden moved this round**.
### → Fable: one recorded departure from the ledger, and why
Ledger #2 says `validateTownCache` **requires** the field on godverse shops. I implemented **missing → warn**
(malformed and duplicate → hard errors). Three reasons, and it's yours to overrule:
1. **The charter's determinism boundary***world = seeded, frozen, gated; stock = data tiers*. An error
inverts it: the **town** would fail to load because its **stock** identity is absent. Tier 0 must boot.
2. **Charter risk #3 names the remedy** — identity gaps "fail-soft to tier 0, never mis-stocked". Missing
*is* the soft case; duplicate is the hard one. They're different failures and now get different arms.
3. **Sequencing**`newtown_godverse` carries 0 ids today, it's G's to re-emit in wave 2, and I can't do
it here (their namespace; no census DB on m3). A hard error would red the tree for C and E through all
of wave 1 to say what the warn already says.
The requirement still has teeth, in the place R23 taught us to put them: selfcheck **fails on partial
keying** and prints an explicit **SKIP with the count** at zero — it cannot pass vacuously.
### Also: E's toowoomba retirement showed my spacing metric is gameable (worth knowing)
E tried the re-bbox before retiring and found it **fixed my metric while making the town worse** — median NN
395.7 → 89.4 m ("passes") but hub density 3/12 → 2/12, the pack's worst. My warn is **necessary, not
sufficient**: it catches scatter, and it can be satisfied by tightening the box around a scatter. D's hub
test is the complement, and E used both. Retired pins removed from both golden maps (a golden for a deleted
town can never fire — the vacuous-gate law one layer down). **Ballarat (255 m) is still flagged and still
E's call this round; if it moves, I pin it.**
## Round 23 (2026-07-16) — v5.0-alpha: the spacing warn (D's thin-tail finding as law)
### → E: **the warn is live — and it flags TWO towns, not one** (the handshake for your #4)

View File

@ -1,5 +1,343 @@
# LANE B — NOTES (measured)
## Round 30 (v7.0-alpha ledger #4) — THE COLLECTION UI + the SLEEP surface: **shipped, verified against F's REAL core (nothing stubbed)**
**The crate is on screen: cover thumb · title/artist · price paid · where + day found; count, day and
cash in the standing HUD; SLEEP saves and wakes tomorrow. All of it built on `PROCITY.game` exactly as
§30.1 publishes it — and by verify time none of it was a stub: F committed the save core (`207bcff`)
mid-round, and every game-layer file my test server carried is byte-identical to that HEAD (diffed,
not assumed). Lane C stubbed because F hadn't landed; B landed after F did — the wave order worked.**
### What ships (`web/js/world/hud.js` — my file, my DOM-overlay patterns; +8 lines in `venue.js`, F's §30.4 filing)
- **The game bar** (bottom-right): `day N · $cash` readout + `CRATE n` (the standing-HUD count, opens
the panel; `C` toggles it on the street) + `☾ SLEEP``game.sleep()` — enabled any time (alpha;
home-base gating is beta). The shell's `onDay` hook owns the wake toast; the bar just re-reads the
contract getters.
- **The crate panel**: newest find first; each row = cover thumb, title/artist (C's §30.1 fallback
chain title → sku → slotId), `paid $p · <where> · day d`. Header totals finds + $ spent. *Where* =
the shop's name off the plan when the entry's `townKey` is this boot's, else the town part of its
townKey (`redhill godverse`) — honest, never guessed.
- **Cover thumbs = the pack atlas, cropped in CSS.** The entry's `sku` resolves against its pack index
(per-shop `assets/stock_godverse/<godverseShopId>/` when set, else the town-wide `assets/models/`),
and the row's 44 px thumb is `background-image` + `background-size: 100/du% × 100/dv%` +
`background-position: u0/(1du)` off the item's `uv` rect (origin top-left = CSS's origin, no flip —
the WebGL V-flip in stockpack.js is exactly the transform NOT to apply here). Zero canvas, zero
texture decode beyond the browser's own; B keeps its own tiny index-only fetch cache (`base|type`,
read-only GETs — can't fight stockpack's texture cache). Parody finds (slotId, no sku) wear a type
glyph by design (§30.4); so does any missing pack / unknown sku / remote (`depot:`/http) atlas.
- **Construction-gated like the R19 selector, but on the game itself:** the HUD builds NOTHING at
`createHUD` — a lazy check (every 30th frame) builds the surface on first sight of a truthy
`window.PROCITY.game` (the shell constructs the game after the HUD). `?classic=1` / `?game=0`
`game` is null forever ⇒ **none of this DOM, the KeyC listener, or the help-line suffix ever
exists** — the gate is the game object, not a re-derivation of the flags.
- **`venue.js` — F's §30.4 filing, fixed as filed:** the compat `venueShopId` ("tonight's venue") was
frozen at night-0's gig; now a live getter keyed to `PROCITY.gigs.weekNight` (0 pre-game/day-1 ⇒
exact pre-v7 value). No consumer of the field exists in-tree today (grepped), so the getter is pure
forward-correctness for whoever reads "tonight" next.
### Measured (fresh contexts, port-isolated no-store server :8151 — NOT :8130/:8144 — serving a code
### snapshot + assets symlink, so F's mid-flight edits couldn't shift under the test; snapshot later
### diffed clean against F's landed commits)
| check | result |
|---|---|
| `?classic=1` / `?game=0` (clean tab) | `game: null` · game DOM **0/5 ids** · help line byte-identical · **0 console errors** |
| default boot | bar lazily built, `day 1 · $191` (wallet-backed start), CRATE 0 |
| 3 finds via the REAL `game.recordFind` (godverse sku · town-wide sku · parody slotId) | CRATE 3 · head `3 finds · $92 spent` · thumbs from **both** pack bases (`stock_godverse/158/`, `models/`) — real cover art on screen (screenshotted), parody = ♪ · exactly 4 fetches (2 index + 2 atlas) |
| foreign-town entry via the REAL `game.import()` | row reads `paid $25 · redhill godverse · day 1` (townKey fallback) · missing sku ⇒ placeholder, 0 errors |
| sell seam (`game.removeFind`) | 4 → 3: bar + open panel both follow within one poll (≤6 frames) |
| **SLEEP** (button) | day 1→2 · weekNight 0→1 · wake DAWN 06:30 seg 0 · `procity-save/1` on disk with day 2 + 3 entries · shell toast `🌅 day 2` · cash untouched · **`venueShopId` re-keyed 116 → 490** (the §30.4 fix, proven behaviourally) |
| next boot (same profile) | the saved game LOADS and the bar/panel read it — persistence proven from the UI side too |
| `?noassets` + a real-sku find | **0** stock/atlas fetches (asset law) · placeholder stands · 0 errors |
| cost | 50-row full rebuild **0.9 ms** · first 3-row open 2.1 ms · DOM-only = 0 draws (C proved the class; unchanged) |
**Honest notes:** (1) the four `THREE.PointerLockControls` console errors seen mid-drive are the
automation browser refusing the Pointer Lock API on my splash click — reproduced click-free at zero,
absent on every clean-tab boot; environment, not code. (2) F's splash ("walk into town") was new in
the tree mid-round and sat over my first screenshot — noted, F's surface, untouched. (3) Beta polish,
not done: foreign-town rows could carry real display names via E's towns index (the selector already
fetches it) — one map at build time, when the money-shot pass comes.
---
## Round 28 (v5.1) — ledger #2, the awning fabric ripple: **KILLED with the measurement**
**Built it, proved it worked, measured what it was worth, and killed it. A 10×-over-driven billow —
1.4 m of flap on a 0.14 m canopy — cannot move 1% of the frame. It is not the gain; it is the
geometry. The backlog item is closed permanently, and the reason names what *would* work.**
R17 shipped tree sway and parked this with a physical reason: a posted verandah is a structural roof
and does not flutter. R28 asked for the half R17 left — the stalls' striped canopies really are cloth,
and they shared the rigid awning InstancedMesh, so neither could move without the other. That part was
all true, and the split worked. The cloth just turned out to be invisible.
### It was built, and it worked
Split the canopies out of the rigid mesh (own `InstancedMesh`, own subdivided box — a 4-corner box has
nothing to ripple), gave only them a cloth billow riding R17's existing `wind.js` uniforms: a
`sin(πu)·sin(πv)` pin (zero at the four corner posts by construction, peak mid-panel), two crossed
travelling waves, per-instance phase off world position so forty stalls don't breathe in unison.
Proven by **rendered-pixel hashes**, not by eye:
| proof | result |
|---|---|
| the cloth actually displaces (calm vs gusty) | `40f59eaf``d6d3e1f1`**moves** |
| returning to calm reproduces the calm frame | `40f59eaf`**identical** |
| `uWindTime = 99` at amp 0 | `40f59eaf`**identical** (amp 0 ⇒ `+= …*0.0`, the R17 pattern) |
| **`?classic=1`, same fixed pose, pre- vs post-change build** | **`453c0154``453c0154` — byte-identical** |
| rigid verandahs / rest of town ride the wind? | cloth+trees hidden: `76c8b518``76c8b518`**still** |
| …and the control proves I hid the right thing | cloth restored: frame **moves** again |
Cost at `market_square`, true A/B: **+0 draws** (98 → 98) / **+2,160 tris**. +0, not the +1 I'd written
in my own comment — the square's chunks hold *only* stalls, so the rigid list empties, `makeInstanced`
returns null, and the cloth mesh replaces it rather than adding to it.
### And it is worth nothing — the sweep (amp is the same multiplier as gain)
| peak billow | frame changed |
|---|---|
| **0.02 m — CALM, the default weather amp** | **0.05%** |
| **0.14 m — STORM, the most any player ever sees** | **0.21%** |
| 0.35 m (2.5× over-driven) | 0.33% |
| 0.70 m (5×, absurd on a 0.14 m slab) | 0.49% |
| **1.40 m (10×, a canopy flapping a body-length)** | **0.77%** |
At the player's actual browsing pose, storm-force: **0.00%, max channel delta 1**. Not one pixel.
**The reason is physical, and it is the same physics that killed the verandah in R17.** A canopy is a
horizontal sheet at y 2.2 viewed by a 1.7 m player: you see it edge-on or from underneath, so a
*vertical* displacement runs almost parallel to the view ray and produces almost no pixel change. You
cannot see a flat horizontal sheet flex vertically while standing under it. No gain fixes that — 10×
buys 0.77%, and anything that large stops reading as cloth and starts reading as a trampoline.
**What would work, if wind in the market is ever wanted (for whoever reads this in round 35):** the
motion has to be *perpendicular* to the view, which means **vertical cloth** — hanging stall skirts, a
flag, a banner, an awning valance. That is an **asset** (Lane E), not a shader. B's `applyWind` /
`applyFabricWind` seam is ready for it the day such a mesh exists; the shader was never the missing
piece. **Ledger #2 leaves the backlog forever.**
### Kept from the round
Nothing shipped: `buildings.js` and `wind.js` are byte-identical to HEAD. Shipping an invisible ripple
would have cost 2,160 tris and a second draw path in the covenant town to move 0.05% of a calm frame.
*The R17 rule was "ships or dies". It died — and it died with numbers, which is the only way an item
this old stops coming back.*
**Two vacuous measurements caught on the way (the epoch's own disease, still catching me):** (1) a
`!!material.onBeforeCompile` check to prove the rigid verandah was untouched — every `THREE.Material`
inherits a prototype no-op, so it is **always** truthy; the assert could never go red, and I replaced it
with `hasOwnProperty` plus a behavioural hide-and-hash control. (2) a gain sweep that reported the
identical 0.21% at gains 0.4→4.0 because I never stored the shader ref, so `uFabricGain` never moved —
**four identical numbers across a 10× range is a sweep that proves nothing**, and it was the constancy,
not the value, that gave it away. Both would have passed as green.
---
## Round 27 wave 4 — `poseForShopFront` shipped · **and every real town's shops face away from the street**
**The micro-task is done and verified. In doing it I found why F photographed empty road: it is not
the pose. Every shopfront in every OSM town has faced away from its street since R18, and the tours
have been photographing the backs of buildings for nine rounds. Root cause is a CITY_SPEC that
contradicts itself. The fix is one line in A's `plan_osm.js` — proven, then reverted, because it
isn't mine and it needs a ruling.**
### 1. The deliverable — `DBG.poseForShopFront(sel, dist = 8)`
Poses at a NAMED SHOP's frontage: door centred, sign band + R26 crate mark in shot, under the verandah.
`clusterPose()` answers "where is the town's HEART", which is the wrong question for a named shop —
Red Hill's heart doesn't contain Monster Robot, which is exactly how F got three frames of empty road.
Keyed through **one shop selector**: F's R22/R24 resolver lived inside `enterShop()`, so I extracted it
rather than grow a second key scheme beside it — a selector bug resolves the wrong shop *and looks like
it worked* (F's own first cut proved that), so the id-namespace law now has exactly one home.
Verified live, and this is the case that matters:
| call | resolves to | |
|---|---|---|
| `poseForShopFront(31)` | **Wholefood Cafe** (plan id 31) | + `ambiguous:` "31 is also the godverseShopId of Silky Oak Furnature — use 'g:31'" |
| `poseForShopFront('g:31')` | **Silky Oak Furnature** (godverse id 31) | the prefix IS the fence |
| `poseForShopFront('g:999999')` | `{error}` | never throws, never guesses |
Framing proven by **projection**, not by eye — `doorCentreNdcX = 0.0000` (exactly), door / sign / mark
all inside the frustum. Derived from buildings.js rather than hardcoded: door is centred on the lot
front (so `off = 0`, unlike `poseForShop`'s `off = 1.0`); sign band is `min(h,FACADE_H)0.35 ± 0.25`
**y 2.402.90**; the mark is **y 2.452.52**; `camY = 2.0` sits below the `AWNING_Y = 2.95` verandah, so
a ray rising to a target ≤ 2.9 can never cross it — the sign is clear *by geometry*, at any distance.
### 2. Correcting my own R27 intel to F — I gave F two wrong numbers
My R27 note said the mark "rides high (y ≈ 3.3)" and the vinyl landmark blocks only 4% of it. **Both
wrong.** The mark is at y 2.452.52; I had ray-scanned y 3.03.6, which is empty awning air *above the
sign* — that is why it read ~0. Re-measured on the true band: the `novelty_record` disc (top y **2.50**)
reaches **into** the mark band and clips **~15% of the strip from every offset** — moving the camera only
changes *which* 15%, so there is nothing to dodge. The recommendation (**shoot head-on**) survives, but
for the opposite reason to the one I gave: not because the vinyl is clear of the mark, but because it
never isn't, and the repeating spines read through it anyway.
### 3. THE FINDING — real-town shopfronts face the paddock, not the street
Standing on Musgrave Road (Monster Robot's own `frontEdge: 1316`) and looking at the shop returns a
**blank grey back wall**: no sign, no door, no windows, no mark. Measured, not eyeballed:
| town | shop lots w/ frontEdge | facing their street | **180° backwards** |
|---|---|---|---|
| `redhill_godverse` | 35 | **0** | **35** (dot = 1.000) |
| `katoomba_real` | 72 | **0** | **72** (dot = 1.000) |
| synthetic (default) | — | — | none — looks right, always has |
dot = **1.000 exactly**, every lot, both towns: a convention, not a defect. Katoomba's `street_noon`
the bookmark the release tours have shot for nine rounds — is **46 draws / 19,680 tris of bare road**.
**Root cause: CITY_SPEC contradicts itself, and the two generators each implement one half.**
- **CITY_SPEC:71** (the lots contract): *"ry: … a GLB modelled facing Z ends up with its facade's OUTWARD
normal pointing at frontEdge. **World facing = (sin ry, cos ry)**"* → **Z is the facade.**
- **CITY_SPEC:335** (posters, R15): *"seated on the **+Z street facade** (**B's canon**: buildings.js facade +
venue.js door/queue/camera are all +Z; `lot + (sin ry, cos ry)·(d/2+ε)`)"* → **+Z is the facade.**
`plan_osm.js:443` obeys the first (`ry = atan2(nx, nz)`, its comment says *"facade (Z) faces road"*).
`plan.js` (synthetic), `buildings.js`, `venue.js` and the R15 posters obey the second. Synthetic — the
town every golden and every one of us tests — follows B's canon and looks correct, which is precisely
why nine rounds of real-town work never caught it. **This is the epoch's own signature species: a rule
meeting a case written after it.** The +Z canon is the de-facto standard (v1→v5, every synthetic golden,
posters R15 ledger #1); `plan_osm.js` is the lone dissenter.
**Proven, then reverted.** One line in A's file — `ry = Math.atan2(-nx, -nz)` — flips katoomba from
**0/72 facing → 72/72 facing**, and turns B's `poseForShopFront` camera from **26 m out in a paddock into
4 m from Musgrave Road**: the money shot F specified, shopfront + mark + kerb + a citizen at the door.
I did not commit it. It is A's file, it moves every real-town plan (⇒ real-town goldens re-pin under
amendment law), and a spec that contradicts itself is Fable's to rule on, not mine to pick a side of.
**This also re-reads my own R22 diagnosis.** I recorded "chunk-buildings **348 tris** (0.2%)" in katoomba's
busiest venue block and credited the atlas/instancing work. 348 tris is ~29 boxes: that was not efficiency,
it was the buildings being **absent from the frame**, and I read the evidence as a win. The number was
right; my reading of it was wrong, for five rounds.
Files: `web/js/world/dbg.js` (the helper + the extracted selector). No other lane's file touched.
---
## Round 27 (on-call) — the R26 handshake **verified against G's real manifest**; intel for F's #5 money shot
R27 owes B nothing (*"B/D: notes current; nothing else owed"*, *"B/D on-call"*). This is on-call work: F's
tour (#5) is specified as *Monster Robot Party's frontage, **B's mark visible*** — a claim my R26 verification
could not actually test, because G's manifest didn't exist yet and I proved the treatment against a **temp
manifest I scaffolded and deleted**. G has since shipped the real one (`36d10ca`). Claim now measured, not
assumed.
**Monster Robot Party is in `redhill_godverse`, not `newtown_godverse`** — the only town B ever tested the
mark in. F's money shot is posed in a town this treatment had never been verified against. It works.
### The R26 handshake held, unmodified
G's real emission is `{version:1, shops:[{godverseShopId, types, sourcing}]}` — 15 entries, lands inside B's
tolerant reader (`.shops`, objects carrying `godverseShopId`) with **zero coordination**, exactly as the R26
note promised. **The `tier` → `sourcing` rename (Fable's waves-1-2 ruling) did not touch B**: the mark
deliberately never reads that field, so a rename of it cannot break the footpath. *The "don't leak tier"
call paid a dividend it wasn't designed for — a cue that reads no field cannot break when the field moves.*
### Both arms proven live — the discriminating arm for the first time
R26's temp manifest marked **all 18** keyed shops, so "manifest is the authority, not the key" was never
actually exercised. G's real manifest makes it load-bearing: redhill has **9 keyed, only 4 stocked**.
Measured at Monster Robot's frontage (live chunk set), ground truth vs render:
| shop | keyed | in manifest | rendered |
|---|---|---|---|
| **Monster Robot Party #3962749** (`real`) | ✓ | ✓ | **marked** ✓ |
| Silky Oak Furnature #31 | ✓ | ✗ | **bare** ✓ |
| Thrift Shop and Eternity Boutique #908 | ✓ | ✗ | **bare** ✓ |
Two keyed shops stand metres from a stocked one wearing no mark, because they have no crate. **The street
advertises exactly the crates that exist.** (#31 is the same shop as Fable's R25 id-namespace bug — its
godverse id collides with a plan id; the mark reads `godverseShopId` only, so it is unaffected.)
Frontage: **135 draws / 74,354 tris**, 0 page errors, manifest prefetched at boot.
### Intel for F's #5 (no action needed from B)
The **`novelty_record` landmark** (B's furniture — a giant 0x1a1a1a vinyl disc on a post, marking record
shops) stands 2.5 m off Monster Robot's frontage and is **prominent in the head-on frame**. Measured
occlusion by ray-grid, so F can frame without guessing:
| band | head-on (8 m) | off-axis (+4 m) |
|---|---|---|
| **mark + sign** | **4%** blocked | 0% |
| window glass | 22% blocked | **44%** blocked |
**Shoot head-on.** The vinyl sits low (y≈1.3) and the mark rides high (y≈3.3), so it never meaningfully
occludes the mark; stepping off-axis *doubles* the window occlusion instead of helping. A giant vinyl record
in the frame of a record-shop money shot is a feature, not a blemish. *(I first read the black disc as a
render bug — it isn't; it's the landmark doing its job. Recorded so the next reader doesn't re-open it.)*
No code changed this round; notes-only.
---
## Round 26 (Fable's ROUND26 → Lane B) — #5 the polish pick: **the stocked shopfront reads from the footpath**
**Shipped. A stocked godverse shop now tells you it's diggable before you reach the door — a crate of
spines under the sign, and a warm-lamp-over-crates room behind the glass. Cost: +0 draws / +0 tris.**
Pick rationale: of the three offered (signage/window treatment · awning ripple · tram-stop tri diet), this
one is the beta's own theme — EVERY CRATE DIFFERENT is worth nothing if the street doesn't say which shops
*have* a crate. (The tri diet is likely moot after E's R22 decimation; the ripple is a vibe with no beta
dependency. Both stay on the list.)
### The authority question — the manifest, not the key
The mark keys off **`stock_godverse/index.json` membership**, deliberately *not* off `godverseShopId`.
Keyed only means "this shop has a stock identity"; the manifest means "a crate is actually here". Marking
keyed-but-unstocked shops would advertise a crate the shop hasn't got — **the street would lie, the one
thing this epoch keeps refusing to do**. This is ledger #6's manifest (G emits, F consumes) doing a second
job for free: it is already the truth of who carries an atlas.
The mark **does not leak tier**. "You can dig here" is equally true of `real` and `mint`; provenance
belongs on C's price card and in E's gates, not shouted from the awning.
### What it is (both halves free)
| cue | how | cost |
|---|---|---|
| **crate mark** — a row of seeded spines under the shop name | painted into the chunk's **existing** sign-atlas cell (`drawSign` → `drawCrateMark`), 7-colour palette, `shop.seed` LCG so a shop's crate is stable | **0 draws / 0 tris** (same 2048² canvas, same 1 draw/chunk) |
| **window cue** — warm lamp over crates, not a generic 90s wall | rides the **existing** per-shop `aTint` attribute (`roomTint` → `STOCKED_ROOM_TINT`), `?winmap` parallax path | **0 draws / 0 tris** (no new attribute, no new material) |
### Measured (newtown_godverse, 18 keyed = 18 stocked under a temp manifest, since deleted)
**True A/B at one pose — manifest 200 vs manifest 404:**
| | control (404) | treatment (200) |
|---|---|---|
| spine-marked sign cells | **0** | **3** (18,768 exact-palette px) |
| stocked window tint verts | **0** | **36** |
| `street_noon` | 65 draws / 115,502 tris | **65 draws / 115,502 tris** |
**+0 draws / +0 tris** — with the treatment *proven on*, not merely assumed. Stocked shopfront (Goulds
Books, footpath pose): **85 draws / 144,730 tris**, inside street law (≤300 / ≤200k).
Consistency check against ground truth: 3 stocked shops in the live chunk set (Dorrigo Hotel, Old Wares,
Goulds Books) → **3 marked cells, 9 stocked window quads** (3 shops × ~2.6 windows). Cues agree.
### Covenants (re-proved — the prefetch is new code on the boot path)
| boot | keyed | `stock_godverse` fetches | errors |
|---|---|---|---|
| `?classic=1` (zero-fetch covenant) | 0 | **0** | 0 |
| default synthetic (`?dbg=1`) | 0 | **0** | 0 |
| `newtown_godverse`, manifest 404 | 18 | 1 (404, eaten) | **0** |
Tested with the manifest **present on disk** — the harshest form: a stray fetch would have returned 200
and been unmissable.
### Three traps this round (all self-caught, all recorded)
1. **The module-load fetch would have broken the classic covenant.** First draft fetched the manifest at
module load → fires on *every* boot → a console 404 on the synthetic default and a **fetch delta to
`?classic=1`**, whose covenant is *zero fetch*. Restructured to lazy `ensureStockIndex()` +
plan-driven `prefetchStockIndex(plan)`, called from `createChunkManager`**keyed plans only**.
2. **The lazy trigger alone raced the first chunk build.** With only the per-chunk trigger, boot showed
`manifestRequested: []` (spawn chunks had no keyed shops); the manifest only fetched after walking to a
keyed shop — meaning **the first stocked shop you approach would build unmarked**. Fixed by prefetching
at chunk-manager construction, before any chunk builds. Verified: manifest now fetched at boot.
3. **A vacuous "zero cost" and a junk detector.** First measurement read +0/+0 — because the manifest was
never fetched (stale `chunks.js` + a cached 404), so *nothing had been applied*. Numbers identical
because the feature was off. Then the pixel detector reported **60 "marked" cells with no treatment at
all** — it was counting ordinary busy sign cells. Replaced with an exact-palette signature (the spine
colours no registry sign uses): control **0**, treatment **3**. *Vacuous-gate law applies to a lane's
own verification, not just to gates — a green that can't go red proves nothing.*
Corollary recorded: **`force-cache` serves a deleted file** (`transferSize: 0, status: 200` while disk
404s) — an A/B against disk state is not an A/B. Poison the cache (`cache:'reload'`) or you are testing
your own memory.
### Handshake for G / F / C (manifest shape)
`buildings.js` reads the manifest **fail-soft and shape-tolerantly**: a root array, or `.shops` /
`.entries` / `.atlases`, of bare ids or of objects carrying `godverseShopId` | `id`. Any other shape, a
404, or `?noassets`**no treatment, no error, no console noise**. G's real emission (ledger #6) needs no
coordination with B — if it lands in any of those shapes, the footpath lights up on its own. **B consumes
`index.json` read-only and emits nothing into `stock_godverse/**`** — the temp manifest used to prove this
round was deleted; the directory is G's.
Files: `web/js/world/buildings.js` (the two cues + the lazy fail-soft reader), `web/js/world/chunks.js`
(one line: the keyed-plan prefetch).
---
## Round 22 (Fable's ROUND22 → Lane B) — #1a the `venue_night` tri diagnosis → **OWNER: LANE E**
**Verdict: the driver is the four GLB street-furniture props. 149,161 of 163,015 tris = 91.5% of the

View File

@ -1,10 +1,11 @@
# LANE C — interior audio contract (v3.0-FROZEN) → for Lane F + Lane B
# LANE C — interior audio contract (v3.1) → for Lane F + Lane B
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane F + Lane B. Describes the shipped state; changes
> require a version bump.
> **v3.1** · 2026-07-17 (R28) · durable contract for Lane F + Lane B. Amends v3.0-FROZEN: adds
> `room.audio.emitters` (§Emitters) and retires the v3.0 deferred `userData.audioEmitter` note — that
> note proposed a shape its own consumer could not use. **The line was the stale thing.**
*`buildInterior` returns `room.audio = { musicKey, toneKey }`, seeded per shop. Lane F's interior_mode just
plays it; Lane B's audio.js resolves the keys → files.*
*`buildInterior` returns `room.audio = { musicKey, toneKey, gigKey?, emitters }`, seeded per shop. Lane F's
interior_mode just plays it; Lane B's audio.js resolves the keys → files.*
> **Scope:** this doc covers the **base interior audio** (the seeded music bed + room-tone every shop
> returns). The **gig audio**`room.audio.gigKey`, the live-band bed, canonical form `gig-<genreKey>`
@ -17,6 +18,7 @@ plays it; Lane B's audio.js resolves the keys → files.*
room.audio = {
musicKey, // string | null — an interior MUSIC bed key in manifest.audio.music, or null (no music)
toneKey, // string — an interior ROOM-TONE key in manifest.audio.ambience (scope:'interior')
emitters, // object — WHERE keys sound from (R28, §Emitters). Never null; may be {}.
}
```
- **Keys, not files.** `buildInterior` stays synchronous and asset-free — it only *names* what should
@ -39,6 +41,58 @@ room.audio = {
Verified (9 types × 8 seeds): 0 bad keys, 0 nondeterminism, every key resolves + matches its manifest
`types` membership; dedicated-music types are stable, non-music types vary on/off by seed.
## Emitters — WHERE a key sounds from (R28)
```js
room.audio.emitters = {
stage?: { x, y, z, r, floor }, // VENUE ONLY — the band + the crowd sound from here
counter?: { x, y, z, r, floor }, // the till
}
```
Room-local metres, 3-dp, deterministic (18/18 same-seed deep-equal across 6 types × 3 seeds).
**Absent emitter ⇒ room-filling**, which stays the right default for a shop radio (the v3.0 call, kept).
- **`r` is the REACH** — the distance at which the emitter has decayed to `floor`. It is the **room
diagonal**, not a constant. Measured over 8 types × 4 seeds: diagonals run **9.8115.63 m**, and scaling
`r` to the room holds the door→stage proximity in a consistent **0.420.59** band from a pokey
band_room to a hall pub. A fixed radius would make the same walk mean different things in different rooms.
- **`floor` is the room-filling RESIDUE, never 0.** A pub PA fills the room; what changes as you walk in is
the **balance**, not the presence. `stage` 0.35, `counter` 0.15 (a till is a point source, a PA is not).
- **The curve** (both consumers use exactly this):
```js
const d = Math.hypot(e.x - cam.x, e.z - cam.z); // emitters and the interior camera are
const prox = Math.max(0, Math.min(1, (e.r - d) / e.r)); // BOTH room-local — no transform
const gain = e.floor + (1 - e.floor) * prox; // never 0
```
Measured swing door→stage: **2.654.52 dB** across pub / band_room / rsl. Audible, not a new game — the
wide rooms swing least because the stage genuinely *is* nearer the door there. (John, R20: *"subtle
changes are great … the player should notice the town feels more alive, not that the game changed."*)
### DO NOT use a PannerNode. It is inert here. (measured)
Nothing in the tree sets `ctx.listener` — zero hits, whole tree. A `PannerNode` therefore resolves against
a listener frozen at **(0,0,0)**, and because rooms are built in **room-local coords at the origin**, that
is *the room's own centre, forever*. Measured via `OfflineAudioContext`, a stage bed in a real pub:
| wiring | RMS at the door | RMS at the stage | ratio |
|---|---|---|---|
| **panner, listener never set** (the engine as it ships) | 0.106905 | 0.106905 | **1.0000** |
| panner + listener tracking the player | 0.055665 | 0.500000 | 8.98 |
The naive build **plays, throws nothing, warns nothing, and never changes by one sample as you cross the
room** — while doing the one thing the feature exists to prevent. Every plausible gate goes green on it:
*"is it routed through a panner?"* yes. *"is it audible in a venue?"* yes. *"in a shop?"* yes.
**-> The only assertion that can see this is a DELTA: the bed's gain at the door ≠ its gain at the stage.**
Assert the change, not the presence. (Vacuous-gate law, 4th application.)
This trap was set by **this document's own v3.0 note** — *"`userData.audioEmitter = musicKey` so Lane B can
position/spatialise the bed"* — which invites exactly the panner, in a codebase with no listener code to
teach otherwise. Both halves of that note were wrong: the *shape* (the engine holds no reference to the
room group and cannot traverse a scene it can't reach) and the *method*. It is retired.
**The house idiom is already correct**: every spatial effect B ships — tram rumble (R=55), door spill
(R=9), gig spill (R=26, low-passed through the wall) — is a hand-rolled distance-gain against
`PROCITY.camera.position`. No listener, no panner. Emitters are the same shape as what already works.
## Lane F wiring (interior_mode)
```
on enter(room):
@ -50,9 +104,92 @@ on exit / dispose(room):
Nothing before the first gesture; `?mute=1`/`?noassets=1` short-circuit before any fetch. `room.audio`
is plain data on the return — no new lifecycle, no dispose hook needed from Lane C's side.
## Deferred idea (v3.1+, not in the frozen contract)
A `places`-tagged visual source ("the music comes from *there*") — e.g. a radio/hifi mesh with
`userData.audioEmitter = musicKey` so Lane B can position/spatialise the bed. Only the record shop has a
fitting for it today (`listeningCorner`); milk-bar/video would need a new radio prop. A tidy v3.1 item: the
`userData.audioEmitter` hook + the two props. v3.0 plays the bed non-spatially (room-filling), which is the
right default for an interior anyway.
---
# R28 — what C shipped, and the two asks
**C shipped (this round, C's files only):**
1. `room.audio.emitters` — the data above. Rides the **existing** `playInterior(room.audio)` call: no new
lifecycle, no traverse, no dispose hook.
2. **The till rings from the counter** (`dig.js`) — *audible positioning in a normal shop, with no engine
change*, via B's already-public `playSfx(key, {gain})`: C does its own distance math and hands B a
scalar. Measured through the real BUY button in a real record shop — **0.9489 at the counter (0.60 m)
vs 0.6436 at the door (4.19 m), 3.37 dB**; negative control (`emitters` omitted) fires at exactly
**1.0**. `sfx-till.ogg` has been on disk since 15 Jul and had **never once played**. Routing, not content.
3. **`?mute=1` now actually silences the crate riffle** — see the house-law fix below.
**Why the venue half is an ask and the shop half wasn't:** a **one-shot** resolves its gain once, at fire
time, from a position C already knows — so C can do it alone. A **bed** must be re-gained every frame from
inside the engine's `update()` loop. That loop is B's file. The split is structural, not political.
## ASK → Lane B (audio.js) — ~12 lines, paste-ready, house-idiomatic
Gives the round its venue half: *the band comes from the stage, not from the whole room.*
```js
// near the other interior state:
let iEmit = null, iMusicG = 0.5, iToneG = 0.35;
async function playInterior(spec) {
// … existing body …
iEmit = spec.emitters || null; // [R28] Lane C — where the beds sound from (gig night only)
iMusicG = mEntry?.gain ?? 0.5;
iToneG = tEntry?.gain ?? 0.35;
swapBed(L.iMusic, spec.musicKey || null, mEntry || null, iMusicG, 1.0);
swapBed(L.iTone, spec.toneKey || null, tEntry || null, iToneG, 1.0);
}
function stopInterior() { iEmit = null; ramp(L.iMusic.g.gain, 0, 0.7); ramp(L.iTone.g.gain, 0, 0.7); }
// in update(), alongside the tram/spill blocks — the SAME idiom, no panner, no ctx.listener:
if (!street && iEmit && iEmit.stage) {
const e = iEmit.stage, d = Math.hypot(e.x - _cam.x, e.z - _cam.z);
const k = e.floor + (1 - e.floor) * Math.max(0, Math.min(1, (e.r - d) / e.r));
ramp(L.iMusic.g.gain, (L.iMusic.src ? iMusicG : 0) * k, 0.25);
ramp(L.iTone.g.gain, (L.iTone.src ? iToneG : 0) * k, 0.25);
}
```
Emitters are room-local; the interior camera is room-local. Same space, no transform. Your call entirely —
C has not touched audio.js.
**Second, smaller ask (not this round):** expose the sfx bus or the ctx so `dig.js` can retire its private
AudioContext (below). C will not open that seam unilaterally.
## ASK → Lane F (interior_mode.js) — 1 line, and it is load-bearing
`interior_mode.js:289` rebuilds the audio spec on a gig night and **drops every field it doesn't name**:
```js
const gigAudio = gigOn && ra.gigKey ? { musicKey: ra.gigKey, toneKey: WALLA_KEY } : ra;
```
Measured against a real gig-night pub: `quietNight_emittersSurvive: true`, **`gigNight_emittersSurvive:
false`**. So emitters reach B on every night *except the one night the stage emitter exists for* — silently,
fail-soft, no error. Don't just spread `ra`: the split is real and should be **deliberate**, because on a
quiet night the radio and the room-tone genuinely *are* room-filling.
```js
// [R28] On a gig night BOTH beds come from the stage — music = the band, tone = the crowd in front of it.
// On a quiet night the radio + room-tone fill the room, so no emitter is passed. Deliberate, not a spread.
const gigAudio = gigOn && ra.gigKey
? { musicKey: ra.gigKey, toneKey: WALLA_KEY, emitters: ra.emitters }
: { musicKey: ra.musicKey, toneKey: ra.toneKey };
```
**Also, when you next touch `openDig`:** pass `emitters: current.audio.emitters` to `dig.open()` and the
till becomes positional. Optional — omitted, it still rings, just un-positioned. Never required.
## House-law fix — `?mute=1` did not silence the crate riffle (R5 → R28)
`dig.js` opened a **second, private AudioContext** wired straight to `destination`, breaking both of
audio.js's stated laws (*ONE AudioContext*; *`?mute=1` forces silence*). The riffle sang right through
`?mute=1` and never saw B's master gain. **It survived three epochs because no gate asserts mute ⇒
silence** — the engine returns its silent surface and looks honest from its own side, while the sound came
from somewhere the engine never knew existed. *A gate that asks the engine whether it is muted can only
ever answer yes.*
Fixed in `dig.js` by reading B's **public** `.muted` getter (consulted, not modified), falling back to the
URL when there is no engine (standalone `interior_test.html`). Measured, with a positive control so the
instrument is provably able to see sound:
| | AudioContexts built | sounds started |
|---|---|---|
| live (mute off), after a riffle | 1 | 1 |
| **muted, same riffle** | **0** | **0** |
The blips stay **procedural** on purpose: zero-fetch, deterministic, and they still work under
`?noassets=1` where a sampled riffle cannot. Only mute was broken; only mute was fixed.
**-> F: assert SILENCE, not muted-state.** The honest gate is *"no second AudioContext exists / no source
starts under `?mute=1`"* — with a positive control proving the probe can see a sound when one is played.

View File

@ -1,4 +1,11 @@
# LANE C — interiors contract (venues + real-town class mapping) → Lanes A/D/E/F
# LANE C — interiors contract (venues · real-town mapping · real stock) → Lanes A/D/E/F/G
> **v5.0-FROZEN · 2026-07-17 · the whole document.** Every section below is the shipped, measured state of
> Lane C's contracts across five epochs. Changes from here are version-bumped amendments, recorded in the
> marker list — the same discipline §0§5 have carried since v3.0. **Case law worth keeping:** three of this
> doc's amendments exist because a rule of mine met a case written *after* it (§7's naming vs the loader,
> §7.2a's `sku_` before mint existed) — the lane building to the rule took the red both times. When a line
> here meets a case it didn't foresee, **the line is the stale thing.** Amend it; don't bend the case.
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane D + Lane F. §0§5 describe the *shipped*
> venue state; changes require a version bump (CITY_SPEC amendment law). Verified R13 (build) + R14 (audit).
@ -7,6 +14,24 @@
> (ledger #2, the density widening; C→E handshake). The frozen venue contract (§0§5) is unchanged.
> **v4.0 amendment (2026-07-16):** **§7 added — the G2a per-shop stock-atlas contract** (C's interior
> draw/tex ceilings, answering Lane G + E's review). §0§6 unchanged.
> **v5.0-alpha REWRITE (2026-07-17, R24 ledger #1):** **§7 rewritten against the code — the runtime is the
> authority.** My R22 wording invented an index name my own loader could never fetch; that contradiction
> held F's tag. §7 now states what `stockpack.js` actually implements, extended per-shop, and the
> `type+base` cache keying is landed. **G's R23 atlas layout is ratified** (G re-emits provenance field
> names only, §7.3). §0§6 unchanged.
> **v5.0-alpha amendment (2026-07-17, R25 ledger #3):** **§7.2a added — slot ids must be sku-derived,**
> unique across packs and stable across re-emits (F's id-collision finding, made structural). Index-only;
> no atlas bytes, no goldens. §0§6 unchanged.
> **v5.0-beta amendment (2026-07-17, R26 ledger #1/#4/#6):** **§7.2b `sourcing` (real|mint — NOT `tier`,
> which is already the ladder rung), §7.2c `condition` for the price card, §7.2d the atlas manifest**
> (existence declared, never probed — kills the 404 attribution exception). All index/manifest-only:
> no atlas bytes, no town caches, no goldens. §0§6 unchanged.
> **v5.0-beta amendment (2026-07-17, R26 wave 4):** **§7.2a's id form is now SOURCING-SCOPED** —
> `sku_<POS id>` for real, `mint_<listing id>` for mint. **Prefixes are namespace fences**, not decoration:
> my R25 line was written before mint existed and read as one space where there are two. Doc-only.
> **v7.0-alpha amendment (2026-07-18, R30 ledger #2):** **§9 added — the sell-counter contract** (the
> no-pump law made structural: `offer(p) = min(p1, max(1, floor(p·0.5)))` — strictly below the buy side
> for every p ≥ 1, by construction). §0§8 unchanged.
*The district ships **three** venue archetypes behind `?gigs=1``pub`, `band_room`, `rsl`. Lane A
converts a chosen shop to a venue kind in place (`shop.type = kind`); C keys the interior recipe off
@ -223,55 +248,302 @@ mapping for the caches is this table.)
---
## 7. G2a — per-shop stock atlases: C's interior ceilings (v4.0, ledger #3) → Lane G + Lane E
## 7. Real-stock packs — THE RUNTIME IS THE AUTHORITY (v5.0-alpha, R24 ledger #1) → Lanes E/F/G
*Answering Fable's three priors + E's half (LANE_E_NOTES). **Verdict: all three priors hold — atlas-per-shop
is the right shape, and it's strictly BETTER for draws than today's type-packs.** The binding constraint is
**VRAM, not draws**, and E's dispose ask is granted: it's my seam and I own the fix.*
> **This section was the root cause of R23's held tag and has been rewritten against the code.**
> My R22 wording invented an index name (`stock_shop_<godverseShopId>_index.json`) that **my own
> loader can never fetch** — so G built runtime-compatible, E built contract-compatible, and E's
> validator glob matched **zero files** (the vacuous gate). Nobody was wrong; the contract was.
> **Rule going forward: `web/js/interiors/stockpack.js` is the contract. If this doc and the loader
> disagree, the loader is right and this doc is a bug.** G's R23 atlas was already correct — it is
> hereby ratified as the layout, and **G re-emits only the provenance field names (§7.3).**
**1. Draw ceiling — ONE atlas per shop (hard cap 2).**
Real stock batches to **one draw per atlas** (shared material per atlas + per-item UV baked into geometry →
`batch.js` merges). A room shows exactly one shop's stock, so a per-shop atlas = **1 atlas = 1 draw** — better
than a type-pack, which can hit several atlases per room. **Rule: 1 atlas/shop; hard cap 2** (each extra = +1
draw, and the R9 buyable-shelf path wants one atlas per shelf to stay one-mesh/one-draw). A 60+ item shop
spills to the shared type-pack rather than fragmenting the room (E's (b) — agreed). *Headroom: `?stock=real`
worst rooms today are record 41 / book 58 / toy 57 draws against the ≤350 law; my R22 23-town soak worst is
159. Draws are not the constraint.*
### 7.1 File layout (loader-exact)
The loader is `preloadStockPack(type, { base })` → it fetches **`<base>stock_<type>_index.json`**.
- **`type``record` | `book` | `toy`** — and it is *not* cosmetic: `type` picks the stock slot
(`SLOT_FOR`: record→`sleeve`, book→`spine`, toy→`box`). **A record shop's per-shop pack must be
type `record`**, or the adapter feeds nothing.
- **Town-wide packs** (unchanged): `assets/models/stock_<type>_index.json`.
- **Per-shop packs (v5): `assets/stock_godverse/<godverseShopId>/stock_<type>_index.json`** — the base
is the per-shop directory; the filename is the same. *(This is exactly G's R23 layout.)*
- **RETIRED:** `stock_shop_<id>_index.json` — never loadable; do not emit, do not glob for it.
**→ Lane E:** `validate_atlas.py`'s glob must become `stock_godverse/**/stock_*_index.json`
(the current `stock_shop_*_index.json` is why it passed on 0 files).
**2. Texture/VRAM ceiling — the real limit. E's dispose ask: GRANTED, and it's mine to land.**
E's audit is correct and the cause is a deliberate C design note (`stockpack.js:11`): atlas textures are
"shared/cached in the pack (**NOT per-room**), like depot GLBs". That is **right for 3 bounded type-packs**
and **wrong for unbounded per-shop atlases** (23 towns × N shops). The ceiling:
- **Resident: ≤2 shop atlases** (the room you're in + 1 for re-entry), **≤8 MB decoded**.
- **Atlas size: ≤1024² for a typical shop** (16 items @256² cell); **2048² only for a 60+ item shop**, and
prefer spilling to the type-pack. At 1024² RGBA ≈ 4 MB → ≤2 resident ≈ **8 MB** (vs E's measured **181 MB**
resident today — ~20× better, and bounded instead of accreting).
- **C lands the LRU cap + dispose in `stockpack.js`** (evicted shop's atlas texture + material disposed).
Type-packs keep today's shared cache (bounded, reused across rooms of a type) — unchanged.
- **Scheduling:** today's 181 MB accretion is **pre-existing**, and `?stock=real` is **opt-in**
(`index.html:196` — `params.get('stock')==='real' && !NOASSETS`), so it cannot touch the default boot and is
**not a v4.0-tag blocker**. I land the cap as **C's first v4.x item — before G2a ships**. G: build to the
ceiling; the loader will honour it.
### 7.2 Fields the RUNTIME reads (absence ⇒ pack `null` ⇒ parody)
- **`atlases: string[]`** — resolved against the **same base**; `depot:`/`https:` entries pass through.
- **`items: [...]`, non-empty** — each item needs:
- **`atlas`** — must be one of `atlases` (items are grouped by it so a bin batches to one draw).
- **`uv: [u0,v0,u1,v1]`, origin TOP-LEFT** — the loader flips V itself (`uvRect`). Do not pre-flip.
- Read by the dig/buy consumers (not by the loader): **`id`, `title`, `artist`, `price`, `price_band`**.
**3. Index schema — E's (c) confirmed VERBATIM, keyed per shop.**
`stock_shop_<godverseShopId>_index.json` = `{version, atlas_px, cell, atlases[], items:[{id,title,artist,price,
price_band,atlas,uv:[u0,v0,u1,v1]}]}`. `stockpack.js` consumes it unchanged (it already groups items by atlas
and shares a material per atlas). **UV origin stays top-left (image-natural) — C flips V for WebGL** (R7 law,
unchanged). Book cell 176×256 portrait is fine: **C reads `uv`, never `cell`**.
### 7.2a Slot ids — unique across packs, stable across re-emits (v5.0-alpha, R25 ledger #3)
**Rule: `items[].id` MUST derive from the source's own stable key — never from position — and MUST be
unique across ALL packs.** The shipped generic packs keep `rec_NNNN` as their namespace — **a real-stock
pack must not emit into it.**
**4. Fail-soft + `?noassets` — unchanged law, confirmed.**
Index/atlas missing → `getStockPack` → null → **parody canvas + one warn** (R7, proven since). `?noassets`
fetches zero. A godverse town under `?noassets` stays silent-and-fine. `?stock=real` opt-in ⇒ G2a cannot
regress the default or classic boots.
> **AMENDED v5.0-beta (R26 wave 4) — the id form is SOURCING-SCOPED, and the prefix IS a namespace fence.**
> R25's line said "`sku_`-prefixed for real-stock packs". I wrote that **before mint existed**, so it reads
> as one space when there are two. E implemented my rule faithfully; **the rule was the stale thing, not
> E's check.** The form now follows §7.2b's `sourcing`:
>
> | `sourcing` | id form | the space it names |
> |---|---|---|
> | `"real"` | **`sku_<POS id>`** (e.g. `sku_20260314070442`) | the shop's **POS** inventory — a physical copy on a shelf |
> | `"mint"` | **`mint_<dealgod listing id>`** (e.g. `mint_1382327`) | a **dealgod listing** — a plausible stand-in, not that shop's stock |
>
> **These are disjoint namespaces and no gate, lookup, or economy path may treat them as one.** G refused
> to file dealgod listing ids under `sku_` to go green the easy way, and that judgment is the rule now:
> **tier-2's sold-means-gone looks up POS skus.** R24's id collision entered the *wrong shop*; the same
> collision in the money layer **sells the wrong record** — a real one, out of a real shop, to satisfy a
> minted stand-in. A prefix that lies costs money, not a gate.
> *(Same standing note as plan-ids vs godverse-ids: one name, one space — never compare across.)*
**5. Determinism — one ask back to G.**
The seeded pick per bin is C's and stays: same `shop.seed` → same crate contents, byte-identical revisits.
That indexes into `items[]`, so **G must emit `items[]` in a deterministic order** (sort by `id`) — otherwise
a re-emit silently reshuffles every crate. Same discipline as E's byte-identical cache re-runs.
**Why — two reasons, both measured:**
1. **Positional ids collide by construction.** Both packs number from zero, so **all 120 godverse ids are
also generic-pack ids — 120/120 overlap (measured R25)**. An id-equality gate therefore cannot
discriminate which pack a rendered item came from: that is what made the brief's own #7a spec vacuous.
F caught it and replaced it with title-sets + texture-URL provenance (now the canonical honest-gate
pattern for stock). Sku ids make id-equality discriminating again — permanently, and while there are
two atlases to migrate instead of fifty.
2. **Position is not identity — and this one outlives the gate.** A positional id renumbers on every
re-bake: sell one record out of crate 550 and `rec_0050` silently becomes a *different* record. Anything
holding an id across snapshots — a wallet purchase, a saved dig, a gate assertion, and above all
**tier-2's sold-means-gone** — would then point at the wrong item. The POS sku is stable precisely
because it is the shop's own identity for that record: the same principle that makes `godverseShopId`
the POS shop id. *(`release_id` identifies the pressing, not the copy — the sku is the copy.)*
**6. Buy-anywhere (R9) — compatible, no change.**
`buildBuyableShelf` merges per-item-addressable covers from ONE atlas → one draw (`noBatch`). A per-shop atlas
satisfies that naturally (better than the type-pack's seeded `atlasPool` pick). Parody = not buyable stays.
**Scope: index-only.** `id` is not read by the loader (§7.3) — the dig/buy consumers and the gates read it.
**No atlas bytes, no town cache, no golden moves.**
**→ Lane E (amended R26 w4):** assert the form **against `sourcing`** (you already read it at line 178) —
`id` present · **unique within the pack** · and the prefix matching its space: `sourcing:"real"``sku_`,
`sourcing:"mint"``mint_`. **A crate wearing the other's prefix FAILS** — that's the fence doing its job,
and it's a stronger check than the one it replaces, not a looser one.
**7. Parody law — E's (d), unchanged and not C's to relax.** Metadata deterministically parody-transformed;
cover art real; prices real-or-seeded.
### 7.2b `sourcing` — real vs mint, and why it is NOT `tier` (v5.0-beta, R26 ledger #1)
The beta needs every keyed crate marked as **actual POS inventory** or **seeded plausibility**, so no gate,
doc or human mistakes one for the other. The brief called that field `tier`. **It can't be `tier`: that name
is already taken, twice over.**
- G's atlases already emit **`tier: 1`** = the **charter's ladder rung** (0 parody · 1 static-real · 2 live
GODVERSE). A mint atlas **is also rung 1** — same mechanism (a statically baked per-shop atlas); only the
*data's origin* differs. And `kind` is already the stock type (`record`).
- So real-vs-mint is an **orthogonal axis**, not a rung. Overloading `tier` would put two meanings in one
name — the same species as the plan-id/godverse-id collision now standing-noted, and as the positional
ids in §7.2a. One name, one meaning.
**Rule: top-level `sourcing: "real" | "mint"`, REQUIRED on every real-stock atlas index.** `tier` stays the
numeric ladder rung, unchanged.
- **`"real"`** — baked from that shop's own POS snapshot (Monster Robot Party). The index carries its
`snapshot` provenance (§7.3).
- **`"mint"`** — a deterministic seeded sample (`random.Random(shop_id)`) standing in for a keyed shop with
no POS. Plausible, never claimed as that shop's stock. **A mint index MUST NOT carry POS-snapshot fields**
(the separation IS the honesty — E asserts both directions).
- **Gates read `sourcing`** (vacuous-gate law: an index with no `sourcing` FAILS). A mint crate can never
report as real. The dig card's treatment is C's call — see §7.2c.
### 7.2c Price-card fields — `condition` (v5.0-beta, R26 ledger #4)
The dig card is a **record-shop price sticker**, and a sticker's fields are artist / title / **condition** /
price. `condition` is **not in the index today** — G's snapshot carries `condition` + `sleeve_cond` per item
and drops them at emit, so `pickRealOffers` currently maps `cond: ''` and the card renders dangling
separators around a hole. That's the "database row" the ledger wants gone.
**Rule: `condition` (and optionally `sleeve_cond`) are OPTIONAL index item fields; emit them when the source
has them.** Index-only; the loader never reads them (§7.3). C's card renders whatever is present and omits
what isn't — a missing field must never leave a stray separator. **Mint items:** emit `condition` only if the
sampled listing genuinely carries one; a plausible-but-invented grade is exactly the kind of fiction
`sourcing` exists to prevent.
### 7.2d The atlas manifest — existence is declared, never probed (v5.0-beta, R26 ledger #6)
Fail-soft-by-404 collides with the zero-console-errors law: the runtime probes per-shop atlases blind and
eats the 404 noise (8 in F's R24 gate run), tolerated by attribution — a workaround, not a design.
**Rule: `web/assets/stock_godverse/index.json` is the authority for which shops carry atlases. No entry ⇒ no
fetch.** Shape:
```json
{ "version": 1,
"shops": [ { "godverseShopId": 3962749, "types": ["record"], "sourcing": "real" },
{ "godverseShopId": 31, "types": ["record"], "sourcing": "mint" } ] }
```
- **`godverseShopId` is a number in the GODVERSE namespace** — never compare it to a plan id (standing note).
- **→ F:** consult the manifest before fetching; no entry ⇒ no probe ⇒ **zero 404s, no attribution
exception**. A shop in the manifest whose fetch still fails keeps the existing fail-soft → parody.
- **→ G:** emit it alongside the atlases (mint entries included).
- **→ E:** manifest↔files consistency **both ways** — an atlas on disk absent from the manifest FAILS, and a
manifest entry with no atlas FAILS.
- **Manifest absent entirely** (a tree with no godverse atlases): that is a legitimate zero-shop state — the
wire probes nothing and stays silent. Not an error.
### 7.3 Fields the runtime IGNORES — and the provenance ruling (the drift dies here)
`version`, `atlas_px`, `cell`/`cell_w`/`cell_h`, `count`, `kind`, `tier`, `shop`, `crate`,
`provenance`**`stockpack.js` reads none of them.** So the authority for their shape is not C's
loader but **E's `validate_atlas.py`**, and the house convention set by 23 shipped town caches:
- **Provenance lives at TOP LEVEL: `license`, `attribution`, `generator`, `snapshot`** — US spelling
`license`, exactly what E's validator reads and what every town cache already uses.
**British `licence` is retired. → Lane G: rename + lift those four to top level.**
- Rich detail **may** stay nested under `provenance{}` (G's `source` / `snapshot_sha256` /
`determinism` block is good — keep it). The four gate fields are the contract; the block is colour.
- **`atlas_px` REQUIRED** — E's validator checks it against the atlas's real pixel dimensions. That is
a genuine falsifiable check; keep it.
- **`cell` / `cell_w` / `cell_h` are informational.** **C reads `uv`, never `cell`** — so 256², a
176×256 portrait book cell, anything: all fine. **Do not shrink the cell to save VRAM** — the dig
renders a cover close-up; resolution is the one thing the player actually sees.
### 7.4 Cache identity = **(type, base)** — landed R24
`stockpack.js` keys `_packs`/`_resolved` by `` `${base}|${type}` ``; `getStockPack(type, base =
DEFAULT_STOCK_BASE)`. Keying by `type` alone made `base` a **no-op on a cache hit** (measured R23:
the 2nd `preloadStockPack('record', …)` returned the identical object). That was two bugs:
1. two stocked shops collide — the 2nd serves the 1st's crate; **and**
2. **it defeated fail-soft** — an *unstocked* shop of an already-cached type inherited that crate
instead of falling back to parody. (A correctness bug, not a mix-up.)
Both fixed and re-measured (§7.7). Pre-v5 callers are unaffected: no `base` ⇒ the town-wide pack.
### 7.5 The seam (→ Lane F, ledger #6a)
```js
buildInterior(shop, THREE, { stock: 'real', stockBase: `assets/stock_godverse/${shop.godverseShopId}/` })
```
`opts.stockBase` omitted ⇒ the town-wide packs (today's behaviour, byte-identical). Preload the shop's
pack on entry with the same base. **C consumes whatever identity field A/G land (`godverseShopId`) —
C does not invent it.**
### 7.6 Ceilings (C's, unchanged from the R23 correction)
- **Draws:** one atlas = **one draw**; **≤2 atlases per shop** (G's 120-record crate lands on the cap);
a 60+ item shop may use 2048²; spill beyond 2 → the town-wide pack. Room law ≤350 stands.
- **VRAM:** **≤2 atlases @≤2048²; LRU = 1 shop's atlas set resident ⇒ ≤32 MB peak.**
*(R23 correction: my "≤8 MB" assumed a ~16-item "typical shop". A real record shop is 120 records.)*
- **The LRU + dispose is C's, and it lands at v5.0-beta**, not here: with ONE stocked shop there is no
accretion, and safe eviction needs a room-exit hook (a live room holds the pack's shared material) —
that seam arrives with F's per-shop preload. Stated so it can't slide silently.
### 7.7 R24 verification — falsifiable, per the new vacuous-gate law
Fresh context, against G's merged `stock_godverse/3962749/` (120 real records):
- **Every sleeve samples G's atlas file** — texture sources are exactly
`stock_godverse/3962749/stock_record_atlas_00.webp` / `_01.webp`; pack items ≡ index items **by id**.
*(Not "covers render": a parody canvas also has a `.map`. The negative control below is what makes
this a gate — I had to fix my own R23 metric to satisfy the law.)*
- **Negative control:** the parody room's stock textures are `(canvas, no src)` and touch the godverse
atlas **zero** times — the metric discriminates.
- **Collision fixed:** shop pack (120 items) and town pack (350) coexist as distinct objects.
- **Fail-soft with a POPULATED cache:** missing base ⇒ `null` ⇒ parody (the R23 bug is gone).
- **Ceilings:** 98 draws with real covers via `opts.stockBase`; `pathOK`; 0 carves; 0 console errors.
---
## 8. Tier-2 live enrichment — the seam (v5.0, R27 ledger #2) → Lane F
**Ruling: bless the direct wire — no new lifecycle hook. But the `gone` *application form* is C's, and it's
landed in `dig.js`.** The question wasn't hook-vs-no-hook; it was *how* `gone` applies.
**1. Lifecycle — no hook needed.** F already holds `currentAdapter` (`interior_mode.js:67`) and passes it to
`dig.open()` **per dig** (`:111`) — a fresh read every time a crate is opened. When the live read lands, F
updates the pack; the next dig picks it up. If the read hasn't landed, hasn't finished, or failed, the
adapter is simply the tier-1 pack — **the dig never blocks on the network** (charter risk #1) and no C
lifecycle changes.
**2. The form — pick-then-omit, NEVER filter-the-array.**
`pack.gone` = a `Set`/array of ids the live read reports sold. C picks from the **full, stable `items[]`**
and omits gone ids **after** the draw.
- **Why (measured, R27, 5 crates):** filtering `items[]` first **reshuffles the crate** — every pick indexes
by position, so **one sale changed 21 OTHER records**. Pick-then-omit: **0 collateral**. A crate with 3
sold shows 13, not a different 16.
- **Both forms hide the sold record** — so **“the gone item never renders” cannot tell them apart.** → **F:
the sandbox proof (#4) must assert on *collateral* (the survivors are unchanged across a `gone` delta),
not just absence. The obvious assertion is vacuous here.** (Vacuous-gate law, third application — and the
cheapest one to get wrong, because it goes green.)
- **Proven in the real dig:** sold record gone · survivors **4/4** kept · **collateral 0** · `items[]`
untouched (120).
**3. F's invariant — `items[]` identity and order ARE the pick's stability.**
- **Do:** update item *fields* in place (live `price`) — free, no reshuffle; the card reads `it.price` at
pull time, so a live price shows on the next pull.
- **Never:** add, remove, or reorder `items[]`. That is what `pack.gone` exists for.
**4. The determinism boundary sits exactly here.** Server down ⇒ no `gone`, no live fields ⇒ the dig is
tier 1, deterministic and gate-able. Server up ⇒ the crate legitimately changes as records sell — tier 2 is
non-deterministic **by design** (charter law #2). So **no gate may assert dig determinism with the server
up**, and no live value may enter a seeded stream or a golden: `pack.gone` is consumed at pick time and
never persisted.
**5. The bin fan is decorative, not inventory.** The room's visible sleeves are built at `buildInterior` from
a *different* seeded pick than the dig's offers — a fan, not the crate's contents. A sold record's cover may
linger in the fan until the room rebuilds. If we ever want the fan to honour `gone`, **that** is when I'd
publish a post-build hook; it is not needed for the epoch's claim, because the crate you *riffle* is true.
---
## 9. The sell counter (v7.0-alpha, R30 ledger #2) → Lanes F/B
**The mirror of the buy card.** Walk to the keeper's counter holding items the shop trades in → a sell
card (the same manila price-sticker treatment as the dig's pull panel): item, OFFER, SELL. Module:
`web/js/interiors/sell.js` — DOM-only (zero draws, zero GPU resources), same consumer pattern as
`dig.js` (C ships the interaction; F owns the in-game input/mode hook).
### 9.1 THE OFFER — the no-pump law, structural (the multiplier and why)
```js
sellOffer(pricePaid) = min(pricePaid 1, max(1, floor(pricePaid × 0.5))) // and never < 0
```
- **The multiplier is 0.5** — the classic secondhand-dealer margin (a keeper pays about half of what
he'll sticker it at; he has to eat too). It keeps selling *meaningful* (half your money back beats a
full crate of duds) without being an income source.
- **The `min(pricePaid 1, …)` clamp is the law made structural, not tuned:** the offer is **strictly
below what you paid for every `pricePaid ≥ 1`** (at `p = 1` the offer is $0 and the SELL button
disables — the keeper won't buy what he can't sticker; at `p = 2` the naive `max(1, floor(p/2)) = 1`
would meet the clamp anyway — the clamp exists so no future retune of the multiplier can ever push an
offer to break even). **Buy-then-immediately-sell therefore loses ≥ $1 per round trip, always**
F's no-pump gate (`R30 ledger #5`, monotonic cash loss) holds **by construction**, and a change to
this formula that breaks that property is a contract violation, not a balance pass.
- **Alpha basis = `pricePaid`.** The alpha carries no guide-band data (the BIBLE bands are beta), and
in the alpha every buy happens at the shop's asking price — so `pricePaid` IS the buy-side price the
player faced. **Beta swaps only the basis, not the shape:** when E's guide bands land, the offer
becomes `min(bandLow 1, max(1, floor(bandLow × 0.5)))` — below the band's *low* edge by
construction, so a gem bought under band still nets its finder margin only against the band, never
against the keeper. The formula's clamp survives the swap untouched.
### 9.2 What the keeper buys (alpha: type = type, strict)
- `sellableIn(shopType, item)``item.type === shopType`. A record shop buys records, a book shop
books — **any** shop of the type (per-keeper taste, haggling, pawn-buys-everything: all beta, not
built).
- **An item with no `type` is NOT sellable — fail-closed** (vacuous-gate law: a matcher that passes an
untyped item would "match" everywhere). **→ Lane F ASK:** stamp `type` (the registry stock type:
`record`/`book`/`toy`/…) on every collection item at buy time — the R30 published shape
(`{townKey, godverseShopId|shopId, sku|slotId, pricePaid, dayFound}`) doesn't carry it, and without
it nothing is sellable anywhere. Carrying `title`/`artist` too is strongly asked (B's collection UI
needs them; my card falls back to the sku/slotId label when absent — ugly but honest).
### 9.3 The seam — money like buying, removal via the game API only
- **C consumes `window.PROCITY.game.collection` READ-ONLY.** The card never splices, reorders, or
mutates it (same species of law as §8.3's `items[]`).
- **`onSell(item, offer)`** is the single side-effect callback — the exact mirror of the dig's
`onBuy(offer)` (the proven wallet seam). The **consumer** (F) does both halves: credit the wallet,
remove the item from the collection **through the game API**. `onSell` returning `false` = keeper
veto: card unchanged, nothing moves.
- `wallet.sell(item, offer)` is provided (C-owned `wallet.js`, mirrors `buy()`): credits `offer`,
drops the matching v0-inventory entry, notifies. F binds it exactly like it binds `wallet.buy`.
- Till ring: the sale rings `'till'` from the counter emitter (the R28 one-shot pattern, same
distance-gain math as the dig's) — silent-and-happy with no engine / `?mute` / `?noassets`.
### 9.4 The trigger + the card
- **Where:** within **`SELL_DIST = 2.0 m`** of `room.counter.pose` (bench centre, room-local — the
frame the interior camera walks in). Helper exported: `nearCounter(room, pos, dist?)`.
- **Suggested E routing for F** (matches my harness): aimed bin → dig (unchanged) · else near counter
with ≥ 1 sellable → sell card · else shelf-buy. Pointer unlocks while the card is up (dig precedent).
- **The card:** one item at a time — artist/title (or the sku/slotId fallback), the round OFFER
sticker, SELL, ` ` cycling when more than one sellable, ✕/Esc closes. `open()` returns `false`
(no card, no DOM) when nothing is sellable.
### 9.5 Fail-soft (the classic-pure law, C's half)
No game layer (`?classic=1` / `?game=0``window.PROCITY.game` absent) → the consumer never opens
the sell UI, **and** `open()` itself returns `false` on a missing/empty item list — zero errors, zero
DOM, zero localStorage. `createSell()` allocates DOM only; `dispose()` removes every node + listener.
### 9.6 API (loader-exact, per the §7 rule: the code is the contract)
```js
import { createSell, sellOffer, sellableIn, nearCounter, SELL_MULT, SELL_DIST } from './js/interiors/sell.js';
const sell = createSell();
sell.open({ shopType, shopName, items, getCash, onSell, onClose, emitters }) // → bool (false = nothing sellable)
sell.close(); sell.dispose(); sell.active // mirrors dig.js
```

View File

@ -6,6 +6,118 @@ _rotOnly/head-bone normalize/upgradeStreetPeople). Measurements on the M3 Ultra
---
## ROUND 29 — v6 SPIKE 1: `look.glb` bound and wired — peds glance (wave 0)
*PROCITY-D, 2026-07-17. The spike finally reaches the game: E's `look.glb` (mesh-free, 65 joints, 6.5 s
'Look') is bound through the fleet gate and street peds now glance around during window-shop stops. `lean`
is not here and never was — E measured the stash (R28). Goldens untouched (clips sit outside every plan
hash); the synthetic anchor is R27's amended `0x5f76e76`, unchanged by me.*
### What shipped
- **`rigs.js``loadPedFleet(base, { sit, look })`** → `fleet.lookClip`, the exact twin of the R16 sit gate.
The shell passes `look: !CLASSIC`, so **`?classic` fetches nothing** — a raw fetch would have breached the
zero-fetch-delta covenant (E flagged this; the gate is the answer).
- **`makeActor({ …, lookClip })`** → a `lookA` action **played only on `setLooking(true)`**, so walkers,
placeholders and `?classic` never advance it. `setLooking` **crossfades** (0.3 s) rather than snapping the
way sit does — idle → idle-variant reads naturally, and there's no discrete replant for a fade to fight.
- **`sim.js`** — a dedicated `glance` rng stream (`rng(citySeed,'glance',id)`), rolled **always** (deterministic)
but only flipped when a clip exists ⇒ classic inert at the source. **Mutually exclusive with the bench-sit**:
the sit rolls first at 35 %, the glance takes 40 % of what's left. `RigPool` threads `lookClip`; release
snaps the glance off so no rig is pooled mid-look.
### The finding — I predicted this needed no re-plant, and the measurement said otherwise
I wrote in my own first draft that a *standing* clip needs no foot re-plant ("no hip descent to recover —
that trap was sit's alone"). **Wrong, and the numbers caught it:**
| lowest bone over the full loop | idle clip | **look.glb, no plant** |
|---|---|---|
| min → max | 0.04 → 0.00 (span **4 cm**) | 0.009 → **+0.205** (span **21 cm**) |
The ped **hovers up to 20.5 cm** (`mixamorigLeftToe_End` clean off the ground), drifting through the loop. It
is the sit trap wearing a different coat: `_rotOnly` drops the Hips POSITION track (rig-height independence),
so the clip's authored weight-shift **dip cannot move the hips — it comes out as the feet rising instead**.
Sit's one-time replant cannot fix it because **the float varies frame to frame**.
**Fix: `plantFeet()` — the per-frame twin of sit's replant.** Reset to the bind plant → measure the posed
feet → drop `inner` by float / scale. **Absolute (reset→measure→correct), never incremental, so it cannot
drift.** It reproduces the authored motion *exactly*: the hips dip relative to the ground instead of the feet
leaving it. Scale-aware like sit's. Foot bones cached once; cost paid **only** by glancing peds, and only on
frames their mixer actually ran (both mixer sites — the drummer's post-mix lean taught me that coupling rule).
### Proofs
| | before | after |
|---|---|---|
| foot float over the loop | **0.205 m** | **0.000 m** (pinned) |
| head yaw sweep (the glance itself) | — | **span 1.735** (0.85 → +0.89) |
| `?classic`: look fetch / `Look` action / glance flips | — | **none / none / 0** — inert at the source |
| identity signature | `-1,-1#0:6:0:1.777:1.171:1:-1` | **unchanged — no `glance` term** |
| sit ∧ glance simultaneously | — | **0** (mutually exclusive) |
| NaN | 0 | **0** |
| goldens | `0x5f76e76` | **`0x5f76e76`** untouched |
A glancing ped is **not** tagged `procitySeated`/`procityPosed` — it is a standing pose at standing stature,
so F's no-giants gate needs no exemption arm for it.
### Handshakes
- **D → F:** **the pose gate has its subject** — un-SKIP it. A fresh `?localdepot=1` boot shows a ped glancing
on the footpath, head sweeping, feet planted (screenshot taken). Classic/`?noassets` untouched.
- **Spike 2 readiness:** `plantFeet()` is the general machinery the brief wanted ("data, not new code") — the
riffle and the wall-lean will land as **clip + roll**, not new pose code. The lean's wall-contact placement
is the only genuinely new thing it will need.
- **Stale line spotted (A's file, A rests):** `selfcheck.js:301` still says *"the synthetic golden above stays
0x3fa36874"* while line 267 pins the R27-amended `0x5f76e76`. A comment, not a rule — but it is the same
species as the epoch's five holds (a line outliving the case that wrote it), so: flagged, not touched.
---
## ROUND 28 — v5.1 THE SWEEP: instrument LOD — **KILLED with the measurement** (ledger #4)
*PROCITY-D, 2026-07-17. The oldest tri item on the books (v3.1-era). The brief attached the wind-sway rule:
**"if the measured win is <20k tris in practice, KILL the item with the measurement and it leaves the
backlog forever."** It does not survive. **E: do not build the `_lo` variants — that work is now unnecessary.***
### The measurement
Instrument tri counts, parsed straight from the shipped depot GLBs (`web/assets/models/`):
| instrument | tris |
|---|---|
| electric_guitar | 14,000 |
| bass_guitar | 14,000 |
| drum_kit | 14,000 |
| guitar_amp | 14,000 |
| mic_stand | 14,000 |
| **band + amp, all on stage** | **70,000** |
On paper that clears the bar: a 34k `_lo` each would "save" ~52k. **But the rule says *in practice*, and the
brief named the reason — "interiors are close-quarters."** So I measured the rooms an LOD would have to fire in
(live, all three venue archetypes, `interiorMode` driven by hand since rAF is throttled):
| venue | room | **max diagonal** | room tris (no band) | draws |
|---|---|---|---|---|
| pub — The Exchange Hotel | 9.7 × 9.7 m | 13.7 m | 12,522 | 38 |
| band_room — The Vinyl Shed | 8.9 × 9.6 m | 13.0 m | 11,699 | 35 |
| rsl — Wangaratta RSL (biggest) | 12 × 9.4 m | **15.2 m** | 15,115 | 38 |
### The verdict: practical win = **0 tris**. KILLED.
**A player can never be more than 15.2 m from the band** — that is corner-to-corner in the *largest* venue, and
the stage sits at one end, so the real stage-to-player max is shorter still. An LOD is a *distance* trade: it
pays when the subject is small on screen. Inside a ≤15.2 m room the band is never small — it is the subject of
the gig, and of F's tour money shot. Any sane swap threshold (1525 m) **never fires**; an aggressive 8 m one
would fire only in half a room, at a distance where a 34 k `_lo` is *visibly* worse with no perceptual cover.
The win is not "small" — it is **structurally zero**, because the distance the LOD needs does not exist in this
game. **Per the wind-sway rule: killed, off the backlog forever.**
No budget motivates it either: a venue interior is ~1215 k tris, ~85 k with the band — comfortably inside the
200 k stress ceiling E's R22 tri diet restored, at 3538 draws against a ≤350 ceiling.
### Side-observation for E (NOT a proposal — no breach motivates it)
All five instruments are **exactly 14,000 tris**, which is a decimator target, not a measurement of the objects:
a **mic stand does not need 14 k**. If instrument tris ever *do* become the driver of a real breach, the lever is
a **flat re-decimation by object complexity** (mic stand → ~1 k), which beats any LOD because it needs no
distance — not a `_lo` swap. Filing the observation so the next tri hunt starts from it; **not** asking for work.
---
## ROUND 23 — v5.0-alpha: the GIG_RANGE chunk-neighbour fix (ledger #6, the carried v4.x item)
*PROCITY-D, 2026-07-16. My own filed finding, finally fixed — post-release, as Fable scheduled. Hot path,

View File

@ -1,5 +1,400 @@
# LANE E — cross-lane notes
## Round 28 — Spike 1: half of it shipped, half of it doesn't exist ⟨v5.1⟩
**1. `look.glb` — DONE, published, verified.** `Looking_Around.fbx` (675 KB, from the cached tailnet stash,
not re-downloaded — the skill's law) → `fbx_to_clip.py`**`web/models/peds/look.glb`**: mesh-free, **65
`mixamorig` joints, 195 tracks, inverseBindMatrices present, 136 KB** — structurally identical to the
`sit.glb` precedent on every field I can measure (`meshes/nodes/skins/joints/bind`), so D's `_canon` binds
it exactly like walk/idle/sit. Depot: published to `digalot.fyi/3god` under John's **R22 standing
authorization for Lane E assets** (`_published.json` 43 → 44). **→ D: it's ready.**
**2. 🚩 `lean` DOES NOT EXIST — the spike's premise is half wrong, and only John can unblock it.** The
brief and the kickoff both say "two of the 34 already-cached clips (lean, look-around)" and "Spike 1 needs
nothing from anyone". **`look-around` is cached. `lean` is not — anywhere.** Measured, not assumed:
- The stash's 34 clips contain no lean (`Agreeing · Bored · Breathing_Idle · Counting · … · Looking_Around
· … · Yawn`).
- `find ~/Documents -iname "*lean*"` on ultra: **nothing** — not in `mixamo-fetch`, not in `character_kit`,
not in `3D=models`, no FBX by that name at any depth.
- The fetch tool can't help unattended: `node fetch.cjs search "lean"`
`FATAL nothing on debug port 9222 — quit Brave and relaunch with --remote-debugging-port=9222`. It needs
**John's logged-in Brave session** (his browser, his Mixamo account) — **I won't relaunch it.**
**→ John, one small unblocker (adds to your two):** relaunch Brave with `--remote-debugging-port=9222`,
open a Mixamo tab, and either run `node fetch.cjs search "lean"` yourself or tell me and I'll fetch +
convert it in minutes (the path is proven twice now). **I did NOT substitute a lookalike.** `Bored` is the
nearest stash clip and it's a standing weight-shift idle, not a wall-lean — D's half explicitly needs
shopfront adjacency ("`lean` against a wall wants the same care" as the scale-aware traps), so a standing
idle would quietly change what Spike 1 proves. Half a spike, honestly, beats a whole one that lies.
**3. Instrument LOD — D killed it, and D's measurement is better than mine.** I'd built the five `_lo`
variants in scratch (14k → 3500/3500/4000/**6000**/3000) before D's commit landed saying *"E: STOP — do not
build the _lo variants."* **Nothing shipped**; scratch discarded. D measured the rooms an LOD would have to
fire in — **max diagonal 15.2 m in the largest venue, so the player can never be far enough: practical win
0 tris.** That's decisive in a way my argument wasn't. My own reading agreed on the verdict by a different
route, and it's worth recording as corroboration: **C's §4 already says "the interior gate is *draws*; tris
are the *street* budget (200k)"** — and an LOD swap doesn't change draws *at all* (same object, same
material, fewer tris). So the item's entire win landed on a metric no interior gate reads. Two independent
measurements, one verdict. **The oldest tri item is dead, correctly.**
*(One salvage worth keeping if it ever returns: at the brief's ~3k the **guitar_amp shreds** — white gaps
punched through its silhouette, confirmed against the shipped 14k thumb as a decimation artifact and not a
source defect, the R22 `food_cart` discipline. It needs **6k** to read clean. The other four were clean at
34k.)*
**4. 🚩 → A: your own file's comment now contradicts its code.** Investigating why the fingerprint read
`0x5f76e76` instead of the `0x3fa36874` I'd seen every round, I confirmed it's **legitimate** — your R27 w5
facade fix re-pinned 51 goldens under the amended covenant. But `selfcheck.js:267` pins
`hash: 0x5f76e76` while **`selfcheck.js:301` still says "the synthetic golden above stays 0x3fa36874"**.
Code and comment disagree by 34 lines. That's the exact species as R24's validator bug (my docstring said
nested, my code read top-level) — the next reader trusts whichever they read first. Your file, your call;
flagging per the round's "flag loudly if anything disagrees".
**5. Gates.** selfcheck **ALL GREEN 156,352/156,352** · `qa.sh --strict` **6 passed / 0 failed**. `look.glb`
moves **no golden** — proved by moving it aside and re-running: fingerprint identical either way, because an
unreferenced clip is never fetched. The `?classic=1` covenant is untouched: like `sit.glb`, a raw fetch
would breach the zero-fetch-delta rule, so **D must gate `look` behind an opt** the way `rigs.js` gates
`sit` (`loadPedFleet(base, { sit })` → the same shape for `look`). Depot-live verified by fetching the
bytes back: `https://digalot.fyi/3god/a/look.glb`**HTTP 200, 139,640 bytes, sha1 identical to local**.
## Round 27 — provenance v5-final, and the roster gap the docs freeze caught ⟨v5.0⟩
**1. THE BUG THE FREEZE FOUND: `redhill_godverse` was on disk but NOT in the roster — for three rounds.**
The v5 provenance table needed real numbers, so I measured instead of carrying the v4 row forward. The
count didn't match: **23 caches on disk, 22 rostered.** The missing one was **`redhill_godverse` — the
town the entire v5 epoch walks to.**
**Mine, squarely.** `index.json` is Lane E's file (`write_index()`). G's `redhill_godverse` landed in R24
(`86e2985`) *after* my last index run (`5815dfb`), and nobody re-rostered it. **An addition isn't done when
the cache is written — it's done when the roster is regenerated.** That's the exact mirror of my own R24
lesson (*"a retirement isn't done when the cache is deleted; it's done when the consumers are swept"*) —
I wrote the lesson for deletions and then missed its reflection for additions.
**Why four green rounds never caught it — worth reading, because it's a gate-shape lesson.** Nothing was
broken *for the gates*: `selfcheck` enumerates `web/assets/towns/*.json` **directly** (so it saw the town
and A even pinned its goldens), and F's crate smoke boots **by key** (`?town=redhill_godverse`). The
roster's *only* consumer is **B's HUD selector** (`hud.js`, my R20 ledger #2 design). So the single symptom
was that **a player could not choose the headline town from the dropdown** — invisible to every automated
check by construction. A gate that reads around the artifact under test cannot test it: the same species as
the vacuous-gate law, one layer out. **Fixed:** re-ran `write_index()` → **23 rostered, 23 on disk, 0
missing**; only `index.json` moved (every cache byte-identical again); selfcheck **ALL GREEN 156,212**,
golden `0x3fa36874` untouched; `qa.sh --strict --matrix` **7/0/0**.
**→ F: this matters for #5, the tour.** The money shot walks Musgrave Road into Monster Robot Party. If any
tour frame reaches that town **through B's selector** rather than by key, it could not have found it before
this commit — by key it always worked. Worth one glance at your framing path.
**→ B:** the dropdown now carries both godverse towns. Your `${t.state ? ', ' + t.state : ''}` handles their
empty `state` gracefully — no change needed, and the defensive write is why this cost nothing cosmetically.
**→ G:** your godverse caches carry no `state`, so they roster as `state:''`. Harmless today (B degrades
cleanly). If you want "Red Hill, QLD" in the picker, emit `state` in the cache — my `write_index()` already
prefers the cache's own field, so it needs nothing from me.
**2. Provenance v5-final (ledger #6).** The ⟨v5.0-FINAL⟩ rows are measured, not inherited: the town pack
row was still claiming **v4's "22 towns / 1192 shops"** — stale since my R24 ballarat retirement and redhill
build (now **21 real / 1196 / 299 heroes**, plus both godverse towns = 23 rostered). Added the two v5 art
rows: the **real-stock atlas** (1 crate, 120 items, Monster Robot's own POS) and the **mint atlases** (14
crates, 224 items, seeded from real dealgod listings). **These are the table's first 🟡 rows** — everything
before v5 was 🟢 web-ok; real product photography is the first asset class this project ships that a human
must clear before public release.
**3. The licence line's story, written down as a mechanism (ledger #6).** The 🟡 rows are guarded by exactly
one printed line, so the freeze records why it took two rounds to make honest: R23's glob matched 0 files
and returned 0; R25's **permissive `or` fallback let the print go silent while the check still passed**
the flag stopped reaching a human on real product photos while the gate said OK. Now: one spelling, one
lookup with one owner, and the line is **counted** (judged N ⇒ N lines, or FAIL). **Both zones print**
mint carries the same in-house-green flag, because minting changes whose stock a crate *claims* to be,
never whose photograph it *is*. Frozen today at **15 crates, 15 flags, every one in front of a human.**
## Round 26 wave 4 — the mint-id fence: my check was faithful, the rule was stale ⟨v5.0-beta⟩
**The red was mine and the fix is C's ruling.** My R25 slot-id check demanded `sku_` on every real-stock
pack — faithfully implementing C's R25 line, which was **written before mint existed**. It then failed all
14 of G's mint crates, whose ids honour its every intent: non-positional, seeded, stable, zero reuse. C's
ruling names it exactly: *"the rule was the stale thing, not E's check."* Fourth hold of the epoch, and
the fourth to trace to **a rule meeting a case written before the case existed.**
**The fix is a fence, and it is STRONGER than the rule it replaces — that was the thing to prove.** The
easy green was to drop the prefix check. Instead the form is now sourcing-scoped per C §7.2a (R26 w4):
`sourcing:"real"``sku_` (a POS copy on a shelf) · `sourcing:"mint"``mint_` (a dealgod listing
standing in for one). **A crate wearing the other's prefix FAILS.**
**Why this is a money bug and not a naming bug — G's judgment, now law.** G refused to file dealgod listing
ids under `sku_` to go green the easy way. They were right: **tier-2's sold-means-gone looks up POS skus.**
R24's id collision entered the *wrong shop*; the same collision in the money layer **sells a real record
out of a real shop to satisfy a minted stand-in.** The gate's error says that, in those words — the cost,
not the rule.
**Proved 7/7 — that the fence bites, not merely that the red stopped.** G's real crate (`sku_…`) → PASS ·
G's mint crate (`mint_…`) → PASS · **mint filed under `sku_` (the exact shortcut G refused) → FAIL**, naming
the fence and the real cost · real wearing `mint_` (the reverse) → FAIL · **positional ids still FAIL in
BOTH sourcings** (the old rule's whole intent, retained) · duplicate ids → FAIL. Loosening would have
passed the first two and lost the last four; this passes the first two and keeps all four.
**Both older suites brought forward rather than left to rot.** The R26 suite's mint fixture wore `sku_`
ids, so the fence correctly rejected it — a fixture that fails for a *new* reason has stopped testing what
it was written for. Moved to the `mint_` space: **17/17**. R25 licence suite: **8/8**, tally still firing
**via the tally**. This is the third round running where the near-miss was a test going quietly stale
around a changing contract; it is the failure mode I now look for first.
**Gates:** `atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s)` (1 real +
14 mint, every one showing its flag to a human) · `validate_manifest` exit 0 · **`qa.sh --strict --matrix`
7 passed / 0 failed / 0 warn — QA GREEN.** F's 6/1/0 is now 7/0/0. **→ F: the beta is yours to tag.**
## Round 26 — the sourcing arm + the atlas manifest ⟨v5.0-beta⟩
**1. The field is `sourcing`, NOT `tier` — and reading C's contract instead of the brief is the only
reason this gate isn't already broken (ledger #3).** The R26 brief says *"the validator grows the tier
arm… `tier` required on every atlas"*. **C ruled it cannot be `tier`** (§7.2b): `tier` is already the
charter's ladder rung (0 parody / 1 static-real / 2 live) and **a mint atlas is also rung 1** — same
mechanism, different data origin. Overloading it would put two meanings in one name: the same species as
the plan-id/godverse-id collision and the positional ids. Had I built the brief's word, my gate would have
demanded `tier` while G emitted `sourcing`**R23 exactly, re-run**. The contract's author is the
authority for the contract's shape. **Built to C §7.2b.**
**2. The honesty separation, asserted both directions.** `sourcing ∈ {real, mint}` required (vacuous-gate
law: no `sourcing` ⇒ FAIL — a crate that can't say where it came from must never pass as one that can).
Then: **mint must carry no POS claim, real must carry the POS provenance it claims.**
**→ C: §7.2b says "a mint index MUST NOT carry POS-snapshot fields" but does not enumerate them, so I
named the set — please correct it if it's wrong.** I assert on `crate`, `provenance.source.pos_db`,
`provenance.source.pos_dump` — the fields that claim *a specific shop's actual inventory*. I deliberately
**excluded `snapshot`**: §7.3 requires it on every atlas as the determinism pin, and a mint bake has its
own honest source snapshot (the dealgod dump it sampled). If a mint bake legitimately needs any field in
my set, say so and the set changes. The error names the exact field found, so a divergence shows up as a
diagnosis rather than a mystery.
**3. The manifest (ledger #6), consistency both ways + one hole C's rule implies but doesn't spell out.**
`stock_godverse/index.json` per §7.2d. An atlas on disk absent from the manifest FAILS (no entry ⇒ no
fetch ⇒ that crate silently never loads — an unreachable atlas is not a passing atlas); a manifest entry
with no atlas FAILS (the wire fetches it and 404s — the exact noise the manifest exists to kill). C's
"absent manifest ⇒ legitimate zero-shop state" is honoured: **no atlases + no manifest ⇒ SKIP, not an
error.** But **a manifest that PROMISES shops while zero atlases exist now FAILS** rather than skipping —
C's rule scopes "absent" to the empty tree, and a tree where something on disk still asserts atlases exist
is the vacuous shape wearing a hat. Flagging it since it's my reading, not C's words.
**4. Proved before handing G the gate — 17/17, including a mint crate that does not exist yet.** The R25
rule (never hand a lane a gate that fails on compliance) applied: I hand-built G's likely R26 emit and a
seeded mint index and ran them. Real → PASS · **mint → PASS with its licence line printed** (ledger #3's
"prints for both" — verified, not assumed) · real+mint side by side → PASS · mint carrying `crate` → FAIL ·
mint carrying `pos_db` → FAIL · **real carrying no POS provenance → FAIL** (the reverse direction) · no
`sourcing` → FAIL · `sourcing:'realish'` → FAIL · atlas absent from manifest → FAIL · manifest entry with
no atlas → FAIL · declared type not on disk → FAIL · **manifest says mint, index says real → FAIL** ·
manifest missing while atlases exist → FAIL · unparseable manifest → FAIL · empty tree → SKIP · empty
manifest → SKIP · manifest promising a shop with no atlases → FAIL.
**5. The R25 suite was going stale and I brought it forward rather than let it rot.** Adding `sourcing` +
the manifest meant every R25 licence case started failing for a NEW reason — which would have left the
licence arm *untested while appearing tested*. Updated to the R26 contract; **8/8 still green, and the
tally tripwire still fires VIA THE TALLY** (`via_tally=True`), not incidentally. Worth noting the near-miss:
the manifest error briefly shadowed the tally in the harness, which is precisely how the tally died the
first time — a test that fails for a new reason has stopped testing what it was written for.
**6. The gate is RED until G lands, by design (2 errors: no `sourcing`, no manifest).** Same as R25: G is
in my wave, parallel. It goes green when G's re-emit + manifest land, with no change from me.
**→ F: one thing before the alpha tag.** There is still **no `v5.0-alpha` tag**, and C's + B's **beta**
commits (`4f0adf1`, `2f4ab59`) are now on `main` ahead of wave 0's close. Tagging `HEAD` would produce a
`v5.0-alpha` whose tree contains beta work your green verdict never covered — the mirror of the reason you
held three times. The alpha's tree is **`e7d58ea`** (wave 0 COMPLETE, my `9b1df5c` beneath it). Your call
and your tag; flagging it because "the tag must certify what was proven" cuts both ways.
## Round 25 — the licence line, made unmissable + the sku form ⟨v5.0-alpha⟩
**1. The bug was not a spelling — it was a DIVERGENCE, and the fallback was the culprit (ledger #2).**
The R24 gate printed `OK — 0 warnings` on a real shop's product photos while saying **nothing** about
their licence. Two paths read one field with different spellings:
```
the CHECK: lic = prov.get("licence") or prov.get("license") ← permissive: passed either spelling
the PRINT: lic = prov.get("licence") ← strict: British only, behind `if lic:`
```
While G's artifact was British they agreed. **G's R24 keyed re-emit moved it to US `license`** — the
check still passed, the print silently resolved to `{}`, and `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`
stopped reaching a human. **The `or` fallback WAS the bug**: it made one path permissive so it could not
feel the other path break. This is **my R21 stale-roads lesson one layer up** — *a check applied to one of
two symmetric paths is a trap with a fuse on it* — and I wrote that lesson down and still shipped it
again. Fixed structurally, not by adding the missing spelling:
- **One spelling, no fallback.** British `licence` now ERRORS and names C's §7.3 retirement.
- **One lookup, one owner.** `validate_index` returns the evidence string; `main` prints what it is
handed and never re-looks-up the field. There is no second path left to drift.
- **The evidence line is COUNTED.** Judged N atlases ⇒ N licence lines shown, or FAIL.
**2. My own first fix had the same disease, and my own test passed for the WRONG REASON.** I added the
tally tripwire — then `main()` *also* appended its own missing-licence error, so `all_errs` was never
empty when the tally would fire: **the tally was unreachable dead code.** The test went green through the
other path and reported PASS. That is exactly the trap I recorded in R24 (*"a gate that fails for the
wrong reason is a gate that will pass for the wrong reason later"*) — one round later, on me, in the very
file where I wrote it. The fix: the field check owns the precise error; the tally sits *after* the
`all_errs` return and exists **only** for the day that check goes permissive again. Verified by stubbing
`validate_index` permissive — it now fails **via the tally** (`via_tally=True`), not incidentally.
**Proved, 8/8:** G's re-emit → PASS with the flag shown · `license` gone / empty → FAIL · British at top
level → FAIL · British nested → FAIL · `attribution` / `snapshot` gone → FAIL · **the R24 silent shape →
FAIL** · permissive-check simulation → FAIL via the tally.
**3. C §7.2a — the sku form asserted, and I verified C's claim myself before building to it (ledger #3).**
Independently measured: **120/120 godverse ids also exist in the generic pack** — F and C are exactly
right, and an id-equality gate cannot discriminate while that holds. G's snapshot carries **120/120 unique
skus**, so the re-emit was feasible, and I proved the compliant shape passes **before** handing G the gate
(4/4: `sku_<POS sku>` → PASS · positional → FAIL · sku-but-duplicated → FAIL · one id sneaking back into
`rec_` → FAIL at 1/120, so it can catch a *real* collision, not just an all-or-nothing one).
**→ C: two things I did beyond your literal ask — say if you disagree.**
- Your ask was "id present, unique within the pack, `sku_`-prefixed". I also assert the **actual property
your rule exists to guarantee**: the pack's ids are **disjoint from the shipped generic packs**, read
from `web/assets/models/stock_*_index.json`. The prefix says ids *should* be disjoint; this measures
whether they *are*, and it catches the reverse drift (a generic pack wandering into `sku_`) that a
prefix check never could. If the generic packs are absent it **warns** rather than passing silently.
- I flag British `licence` **anywhere**, including inside the nested `provenance{}` "colour" block. Your
§7.3 retires the spelling and R25 is what a spelling fork costs, so I read the retirement as global. If
you meant the block to stay G's business, say so and I'll scope it to the four gate fields.
**4. The gate was RED by design for one wave, and that was the point.** Between my commit-readiness and
G's re-emit the collision was real and unfixed, so `validate_atlas` failed — deliberately. A gate that
stayed green on a defect F had just found would be the vacuous species wearing a different hat. It went
green the moment G landed `4314622`, with no change from me.
## Round 24 — the alpha completion: redhill, the honest validator, ballarat ⟨v5.0-alpha⟩
**1. `redhill_real` — the crate's real street (ledger #3). BUILT.** One bounded Overpass fetch under the
standing authorization. **Red Hill, Brisbane** · centre `-27.4553, 153.0064` · span 2.4 km · **36 shops
(9 heroes + 27 texture, 29 subsampled) · 884 roads** · median NN 73 m, hub 5/36. **Musgrave Road is in
`roads[]` — 41 ways, kinds `[primary, residential]`.** That was the round's point: Monster Robot Party's
real shop now has its real street. Raw snapshots in `_raw/`, ODbL, C's mapping, same discipline as the
pack. **→ Lane G: this is your donor** for `godverse_town.py redhill_real redhill_godverse`.
**→ G AND A: TWO HAZARDS ON THIS DONOR. Please read before you seat the shop.**
> **(a) The shop is NOT in OSM.** There is no `shop=music` node and no "Monster Robot"/"monsterrobot"
> anywhere in the Red Hill bbox — I checked the raw snapshot, not just the emitted cache. It **cannot**
> arrive via my fetch; **G must inject it from the census** (id `3962749`) into `shops[]`. If G assumes
> the donor already carries it, the town ships without the one shop the release is about.
>
> **(b) THE SEATING HAZARD — from G's geocode, the shop seats on the WRONG STREET.** A's lift seats each
> shop on its **nearest edge**. Measured against the real `roads[]` geometry, from G's geocode
> `-27.4553, 153.0064`:
>
> | distance | street | kind |
> |---|---|---|
> | **38.6 m** | **Zig Zag Street** | residential ← **A would seat it HERE** |
> | 46.1 m | Craig Street | residential |
> | 51.6 m | **Musgrave Road** | primary ← the real address (147 Musgrave Rd) |
>
> Three streets are nearer than the addressed one, so Monster Robot Party lands on Zig Zag Street and the
> round's headline quietly becomes false. A street-address geocode sitting 52 m off its own centreline is
> itself the tell — the pin is roughly the block, not the shopfront.
>
> **The measured fix (E's recommendation, G's call):** the point on the real Musgrave Road centreline
> nearest G's geocode is **`-27.455169, 153.006902`**. Seating there: **0 m to Musgrave Road, next
> candidate 43 m away** — unambiguous, with margin. It moves the shop 51.6 m; the residual error *along*
> the street is G's geocode error and is below the game's resolution. **Caveat, stated plainly:** this
> snaps to the right street, it does not verify the house number — I have no address data to confirm
> where #147 sits along Musgrave Rd.
>
> **The robust fix (A's call, likely v5.x):** a godverse shop knows its addressed street; A's lift could
> prefer a name-matching edge over the merely-nearest one. That fixes the whole class, not this one shop.
> The snap above is enough to ship the alpha honestly.
**2. `validate_atlas` — made honest (ledger #4). F's catch was right; there were FOUR bugs, not one.**
The R23 gate could pass without ever touching its subject — the vacuous-gate law's exact species. Fixed:
| # | bug | why it mattered |
|---|---|---|
| 1 | globbed the **published-contract** name (`stock_shop_<id>_index.json`) — a name the runtime can *never* load | matched **0 files** and returned 0. It passed identically whether atlases were absent, misnamed, or perfect |
| 2 | the **code read provenance top-level** while **my own docstring said nested** | G nested it (correctly). The gate would have failed G's real, correct atlas |
| 3 | `license` vs **`licence`** | same — a spelling fork silently failing a valid artifact |
| 4 | ceiling stale by one round | drifted from C's §7 |
Per the R24 **"the runtime is the authority"** ruling the gate now globs the path the loader fetches:
`stock_godverse/<godverseShopId>/stock_<type>_index.json`, `type ∈ {record, book, toy}`. **The vacuous-gate
law is implemented literally:** 0 matches **+ stray files** ⇒ **FAIL**, listing the unmatched paths; 0
matches **+ a truly empty dir** ⇒ `atlas-QA SKIP: no atlases present (0 files matched glob …)`, exit 0 —
it skips **loudly, with the glob printed**, so a silent pass can't hide. It also asserts identity
(`shop.godverse_id` == the directory name) — a falsifiable id equality, per the law's "prove it touched
its subject".
**Proved, not asserted** — four cases: empty ⇒ SKIP (loud, exit 0) · **misnamed (F's exact bug) ⇒ FAIL**
· unparseable ⇒ FAIL · **G's real atlas ⇒ PASS**: `3962749/stock_record_index.json — Monster Robot Party
— items=120 atlases=2 px=2048 OK`, licence printed, 0 warnings. **My own test found a fifth hole:** the
glob still matched the old `stock_shop_*` name, so the misnamed case failed only *incidentally* — I added
a `STOCK_TYPES` name check, and it now fails **for the right reason**. A gate that fails for the wrong
reason is a gate that will pass for the wrong reason later.
**→ Lane C:** built to the runtime + G's real artifact per the ruling. `LANE_C_PUB §7` **line 271** still
names the contradictory `stock_shop_<godverseShopId>_index.json` — when your corrected contract lands I'll
reconcile; if it names anything other than the above, the gate and the loader disagree again.
**3. Ballarat — RETIRED (my call, counted). A different illness from Toowoomba.** Full rationale in
`SOURCES.md` and the commented config entry. The short version, because the distinction matters:
**Toowoomba was dead everywhere. Ballarat is alive exactly where the game isn't.** It has 103 POIs and a
genuinely dense core (densest 400 m disc = **47** POIs; Toowoomba's was **3**) — but that disc is
cafés/clothes/bakeries, while the op-shops sit 854 m / 1042 m / 1709 m out. Heroes and density don't
coincide, so no bbox holds both:
| span | shops | heroes | median NN | hub ≤160 m | op-shops |
|---|---|---|---|---|---|
| 1.0 km (the live core) | 8 | 2 | 184 m | 4/8 (50%) | **0** ← a thrift town with no thrift shop |
| 2.0 km | 12 | 3 | 186 m | 3/12 (25%) | 1 |
| 3.0 km (as shipped) | 20 | 5 | 255 m | 4/20 (20%) | 2 |
Every row is worse than any shipped town (darwin 68 m, glebe 60 m, redhill 73 m, katoomba 22 m).
**→ A, D: the binding constraint was MINE, not OSM's — and it's a real finding.** With 2 heroes the 3×
texture cap keeps 6 of 47 texture POIs, and **the subsample is spatially blind** (deterministic hash rank
over osmId). So in any hero-poor/texture-rich town the survivors scatter *no matter how tight the box*
**part of the 255 m A's warn caught was my own cap's fingerprint, not Ballarat's.** Seeding texture near
heroes would fix the class, but it moves **every** town's golden → I'm filing it as a **v5.x proposal**,
not doing it in a release round.
**→ A: I OVERSTATED MY R23 ADVICE — please don't encode it as a threshold.** I told you the hub fraction
is the discriminator. It isn't, on its own: **glebe — shipped and healthy — reads 7/52 (13%)**, and
redhill reads 5/36 (14%). A hub threshold would false-flag both. What actually held up:
- **median NN is the robust primary** (dead: toowoomba 396 m, ballarat 255 m; alive: 2277 m).
- **hub is only comparable at equal shop counts** — toowoomba 2/12 vs darwin 7/12 was a fair comparison
*because both had 12 shops*; hub% otherwise scales with town size and morphology.
- The R23 calibration point still stands and still matters: **your `validateTownCache` sees raw cache
lat/lon; D's numbers are seated (post-lift)** — darwin is 27 m seated but **68.3 m raw**, so a threshold
read off D's table will false-warn darwin.
**4. A methodology note I'm recording because it nearly bit me.** I simulated the re-bbox offline to pick
Ballarat's span; the sim predicted **68 m / hub 5-8** and I had already written those numbers into a
config comment as fact. The real build returned **184 m / 4-8***same* counts (2 heroes + 6 texture),
*different sample*: my re-implementation of the hash subsample picked a dense six where the pipeline picks
a spread six. **Never trust a re-implementation of the pipeline's own selection — run the pipeline.** The
sim's numbers would have shipped as a claim about a town nobody would have re-measured.
**5. Determinism receipt.** The ballarat retirement forced a full 21-town rebuild from the cached raw
snapshots (no refetches). **Every existing cache came back byte-identical** — only ballarat (deleted),
redhill (new) and `index.json` moved. Pack now **21 real towns · 1196 shops · 299 heroes**, 22 rostered
with Lane G's `newtown_godverse`. Selfcheck **ALL GREEN, 149,696 checks**, synthetic golden `0x3fa36874`
unchanged (the count moves with the roster: ballarat's 1144-edge graph out, redhill's 884 in).
**6. → A: THE GOLDEN PINS ARE YOURS AND MY ROUND MOVED THEM (the batched pin, ledger law).** Your own
selfcheck printed the exact values — no round trip needed:
```
REAL_TOWN_GOLDENS['redhill_real'] = 0x6046700f // ADD (new town, currently UNPINNED)
REAL_TOWN_GIG_GOLDENS['redhill_real'] = 0x673785ee // ADD
REAL_TOWN_GOLDENS['ballarat_real'] = 0xf0a9e8a1 // REMOVE (cache deleted — dead config)
REAL_TOWN_GIG_GOLDENS['ballarat_real'] = 0xf097c331 // REMOVE
```
The ballarat pins are now exactly what **your R23 note** called the toowoomba pin: *"a golden for a town
that cannot load is dead config: it can never fire, so it can never fail — same species as the vacuous-gate
law, one layer down."* Your words, my retirement, same fix. And note the mirror on the other side:
`redhill_real` currently **warns** (`⚠ base UNPINNED`) rather than failing, so **the round's headline town
is ungated until you pin it** — an unpinned town is the same vacuous species pointing the other way. Your
warn-with-the-hash design is what made this a two-line handoff instead of a bug hunt; worth keeping.
**7. → F: an R23 orphan I created and owe you.** `tools/qa/tour_shots.py` (your v4.0 release tour) still
shoots `plansrc=osm&town=toowoomba_real` for its `toowoomba_thintail` frame — I deleted that cache in R23
and never swept the consumers, so **re-running the v4 tour now breaks on a missing town.** It's your file
and it's a shipped historical artifact (the frame *was* the honest thin-tail proof), so I'm flagging
rather than editing: either re-point the frame at a surviving thin town or retire it with a note. Same
class, lower stakes: `web/js/citygen/gigs.js:134` names ballarat/toowoomba in a comment as thin-town
examples — stale prose only, no behaviour. **The general lesson for me: a retirement isn't done when the
cache is deleted; it's done when the consumers are swept.**
## Round 23 — v5.0-alpha: the atlas validator + the toowoomba call ⟨v5.0-alpha⟩
**1. `validate_atlas` — the G2a format gate (ledger #4).** `pipeline/validate_atlas.py`, wired into
@ -412,12 +807,52 @@ loader wiring (`spawnRig`/fleet). Bench-sit loiter (Fable's stretch goal) reuses
| Audio pack | 28 | 100% procedural numpy synthesis (`gen_audio.py`) | oscillator/noise render → ffmpeg OGG+M4A | 🟢 CC0-equiv, parody-safe |
| Stock packs | 3 (350/311/273) | GODVERSE `dealgod` Postgres — **real** cover art | `build_stock_pack.py`: parody-transform metadata + atlas | 🟢 real-art / parody-metadata |
| Ped clip **⟨v3.1⟩** | `sit.glb` (E R16; + pre-existing `walk`/`idle` from D/90sDJsim) | Adobe **Mixamo** `Sitting_Idle.fbx` | `fbx_to_clip.py` (Blender, mesh-strip) | 🟢 Mixamo royalty-free (motion only, mesh stripped) |
| Town pack **⟨v4.0-FINAL⟩** | **22 real AU towns, all 8 states/territories** — 1192 shops + real street geometry (`roads[]`) | **OpenStreetMap** via Overpass API — bounded per-town bbox; raw shops+roads snapshots committed in `_raw/` | `build_towns.py`: fetch → dedupe/suburb-fill/parody → C's §6 class map → 3× texture cap (drops counted) → v2 cache + roads | 🟢 **ODbL 1.0** (© OSM contributors; `web/assets/towns/SOURCES.md` + `license`/`attribution` on every cache) |
| Godverse town *(Lane G's, listed for completeness)* | `newtown_godverse` — 18 shops, rostered in E's `index.json` | GODVERSE/thriftgod shop census + OSM road geometry | `pipeline/godverse_town.py` (**Lane G owns this provenance**) | 🟢 ODbL 1.0 + GODVERSE census (G's own `license`/`attribution` fields) |
| Ped clip **⟨v5.1⟩** | `look.glb` (E R28 — v6 Spike 1) — mesh-free, 65 `mixamorig` joints, 195 tracks, 136 KB; structurally identical to `sit`/`walk`/`idle` | Adobe **Mixamo** `Looking_Around.fbx` (675 KB) — from the **cached tailnet stash** (`johnking@ultra:~/Documents/mixamo-fetch/out/`), not re-downloaded | `fbx_to_clip.py` (Blender, mesh-strip) — the R16 path, unchanged | 🟢 Mixamo royalty-free (motion only, mesh stripped) — same zone as walk/idle/sit |
| Ped clip **⟨v5.1⟩ NOT BUILT** | `lean.glb`**the source does not exist** (see R28 §1) | — | — | — |
| Town pack **⟨v5.0-FINAL⟩** | **21 real AU towns, all 8 states/territories****1196 shops** (299 heroes) + real street geometry (`roads[]`) | **OpenStreetMap** via Overpass API — bounded per-town bbox; raw shops+roads snapshots committed in `_raw/` | `build_towns.py`: fetch → dedupe/suburb-fill/parody → C's §6 class map → 3× texture cap (drops counted) → v2 cache + roads | 🟢 **ODbL 1.0** (© OSM contributors; `web/assets/towns/SOURCES.md` + `license`/`attribution` on every cache) |
| Godverse towns *(Lane G's, listed for completeness)* **⟨v5.0-FINAL⟩** | `newtown_godverse` (72 shops) · **`redhill_godverse` (37)** — the town whose record shop is THE shop. Both rostered in E's `index.json` (**23 total**) | GODVERSE/thriftgod shop census + OSM road geometry; `redhill_real` is E's donor | `pipeline/godverse_town.py` (**Lane G owns this provenance**) | 🟢 ODbL 1.0 + GODVERSE census (G's own `license`/`attribution` fields) |
| Real-stock atlas **⟨v5.0-FINAL⟩** | **1 crate, 120 items**, 2×2048² webp — Monster Robot Party (`3962749`) | **the real POS** (`recordgod`) — crate 550 "DEEP HOUSE"; covers are **the shop's own product shots**, cached by dealgod | `pipeline/godverse_stock.py` (**Lane G owns**) — `snapshot`(DB-side, sha256-pinned) → `pack`(pure) | 🟡 **in-house-green — FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE** · `sourcing:"real"` · ids `sku_<POS id>` |
| Mint atlases **⟨v5.0-FINAL⟩** | **14 crates, 224 items**, 14×1024² webp — keyed hero shops with **no POS** (5 record · 6 book · 3 toy across both godverse towns; the 6th record crate is the real one above) | **real dealgod listings** (the sellers' own product shots) — a *seeded sample*, `random.Random(shop_id)`, deterministic | `godverse_stock.py` mint baker (**Lane G owns**) — same bake path, sampled source | 🟡 **in-house-green — same flag** · `sourcing:"mint"` · ids `mint_<listing id>` · **never claimed as that shop's stock** |
**GLB fleet = 33 manifest (23 fit + 10 furniture) + 5 `_hi` = 38 published.** Only external spend ever:
~$0.64 (fal-Hunyuan, R5). Everything else is $0 / on-device (electricity only).
### ⟨v5.0-FINAL⟩ The licence line's story — why one printed line is a provenance mechanism
**The only 🟡 in this table is the v5 stock art, and the flag is the whole control.** Charter law #3 says
clearing photos for public release is **a human call** — so the gate does not pattern-match the licence
and rule on it. It **prints it**, every run, for every atlas, and a human decides. That makes one printed
line the mechanism the amber rows depend on, which is why it earned two rounds of work:
- **R23** — the gate globbed a name the runtime can never load: **0 files matched, returned 0**. It passed
identically whether atlases were absent, misnamed, or perfect. (The vacuous-gate law came from this.)
- **R25 — the line went silent and the gate still said OK.** Not a typo: the **check** read
`licence or license` (permissive) while the **print** read `licence` only. G's re-emit moved to US
`license`; the check passed, the print resolved to `{}`, and `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`
**stopped reaching a human on a real shop's product photos**. *The fallback WAS the bug* — it let one
path stay permissive so it could not feel the other break.
- **The fix is structural, not a spelling.** One spelling, no fallback (British `licence` now ERRORS,
naming C's §7.3 retirement) · **one lookup, one owner** (`validate_index` returns the evidence string;
`main` prints what it is handed and never re-looks-up the field — no second path left to drift) · and
**the line is COUNTED**: judged N atlases ⇒ N licence lines shown, or FAIL. A gate that can say OK while
hiding the flag cannot exist by construction.
**Today, frozen:** `atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s)`.
Fifteen crates, fifteen flags, every one in front of a human. **Both zones print** — the mint rows carry the
same in-house-green flag because a seeded sample of real dealgod listings is still **the sellers' own
product shots**. Minting changes whose stock it *claims* to be, never whose photograph it *is*: that is why
`sourcing` is honesty and the licence is unchanged by it.
### ⟨v5.0-FINAL⟩ The id-space fences — provenance carried in the id itself
Two disjoint namespaces, blessed as design (C §7.2a, R26 w4), asserted by `validate_atlas`:
**`sku_<POS id>`** names a physical copy on Monster Robot's shelf · **`mint_<listing id>`** names a dealgod
listing standing in for one. A crate wearing the other's prefix **FAILS**. This is not naming hygiene:
tier-2's sold-means-gone looks up POS skus, so a `mint_` id filed under `sku_` doesn't fail a gate — it
**sells a real record out of a real shop to satisfy a minted stand-in**. G refused that shortcut when it
would have gone green the easy way, and that judgment is the fence. *(Same standing note as
plan-ids vs godverse-ids: one name, one space — never compare across.)*
### Audio inventory — 28 assets (`web/assets/audio/`, committed; contract = `manifest.audio`)
All procedural, seeded, $0, parody-safe; OGG/Opus + M4A/AAC; beds+music loudnorm 16 LUFS, SFX peak-norm.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,108 @@
# PROCITY — Round 24 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **R23 verdict: THE HOLD WAS RIGHT.** F held the `v5.0-alpha` tag on a round
where every suite was green — `qa.sh --strict --matrix` 7/7, MATRIX 10×7, selfcheck 152,015 —
because the round's headline could not be reached: G's 120 real records (Monster Robot Party's
actual crate 550, off the real POS) have **no street** (the shop isn't in any town cache), **no
identity** (`godverse_id: null` on every godverse shop — the schema has no such field), and **no
compatible contract** (C's published atlas naming and C's own runtime loader are mutually
incompatible — an atlas named per the contract can never be loaded by the code meant to load
it). G built runtime-compatible; E built contract-compatible. **Nobody was wrong; the contract
was.** And two gates would have passed without touching their subject — E's validator returns 0
on an empty glob, and #7a-as-written goes green on the *generic* v2 pack's real covers with zero
fetches to `stock_godverse/` ever. "Release rounds don't ship known breaches" — F applied the
law to its own tag. This is the discipline working at the exact moment it matters most.
Round 24 is **THE ALPHA COMPLETION** — the three gaps closed by their owners, the two process
fixes made law, and the tag earned. Tag **`v5.0-alpha`** at close (F, and only when #7a runs
honestly).
## Two process fixes, now law (from R23's findings)
1. **THE VACUOUS-GATE LAW (all lanes, immediately):** a gate must FAIL — or explicitly SKIP with
a printed reason — when its subject is absent. A gate that can pass without touching its
subject is not a gate. "No atlases yet ⇒ clean pass" and "real covers render" were the same
species: presence-of-a-thing checked instead of the-right-thing. Every new gate states what
its subject is and proves it touched it (a fetch log line, a matched-file count, an id
equality — something falsifiable).
2. **Charter law #5 amendment — F executes gating merges.** The mid-round merge was Fable's per
the charter and Fable was never invoked — the round's headline sat unmerged while five lanes
built around it. F proved the fix in the act: verify namespace disjointness empirically, then
execute. Going forward: **when a G deliverable is gating, Lane F executes the merge as part
of its integration duty** (disjointness verified, not assumed); Fable reviews at the round
boundary as before. One less cross-Mac synchronization point.
## The ledger
1. **The contract fix (C — the root cause is yours to close).** Your published atlas contract
and your own `stockpack.js:25` loader disagree. Ruling: **the runtime is the authority**
rewrite the contract to what the loader family actually implements, extended per-shop:
per-shop atlases live under a per-shop `base` (`stock_godverse/<godverseShopId>/`), index
named as the loader expects, fields named as the loader reads them (the `licence`/`license`
and nested-vs-top-level provenance drift dies here — pick what the code reads, document it,
one truth). Plus F's filed item: **key the pack cache by `type+base`** so a per-shop pack
and the town-wide pack coexist. Publish the corrected contract FIRST (half-day rule — G and
E both re-emit against it).
2. **The identity field (A, with G).** The town-cache schema grows an optional
**`godverseShopId`** on shop entries (schema stays v2 — optional field, absent everywhere
except godverse caches; `validateTownCache` requires it on `source: "godverse+osm"` shops).
G's adapter emits it from the census (the POS shop id IS the id — no mapping table). A: the
validator arm + selfcheck assert; G: re-emit `newtown_godverse` with ids.
3. **The street (E).** Build **`redhill_real`** — one bounded Overpass fetch under the standing
authorization — so Monster Robot Party's real shop has its real street. Same discipline as
the pack (raw snapshot, ODbL, spans, C's mapping). Hand the donor to G.
4. **The validator, made honest (E).** Fix `validate_atlas.py` per the vacuous-gate law: an
empty match set prints `SKIP: no atlases present (0 files matched glob <g>)`; a present-but-
unparseable or present-but-unmatched atlas FAILS; field names align with C's corrected
contract (your own docstring and your own check currently disagree — F caught it). Then run
it against G's re-emitted atlas and gate on the result. Also: the **ballarat** curation
(A's spacing warn caught it at 255 m — re-bbox onto its strip or retire, counted, your call).
5. **The re-emit + the second town (G, on ultra).** Against C's corrected contract and A's
identity field: re-emit the Monster Robot atlas keyed by `godverseShopId`, run the adapter
on E's `redhill_real` donor → **`redhill_godverse`** (the town whose record shop is THE
shop). Update the G3 doc §4 if the corrected contract changes any API field naming. Your
deliverables are gating → F merges mid-round per the amended law.
6. **The wire + the honest gate + the tag (F, last).**
a. Pass the per-shop `base` through the stock path (the seam you deliberately didn't build
against a three-quarters spec — the spec is now whole).
b. **#7a, honestly:** boot `redhill_godverse` + `?stock=real`, enter Monster Robot Party via
your selector, and assert on falsifiables: network log shows fetches under
`stock_godverse/<id>/`, rendered titles ∈ the atlas index (id equality, not "covers
render"), buy debits, tier-1 gone-this-session semantics. Then the ladder re-proof
(tier 0 zero-fetch / missing-atlas soft-fall / tier 1) and the frozen classic + default
gates.
c. `qa.sh --strict --matrix` green (with E's honest validator now IN the gate set), notes
§24, the held README v5 teaser ships WITH the tag (they're one claim, as you said), tag
**`v5.0-alpha`**, push.
7. **On-call:** B (standing light-round offer holds — R25 gets you first pick of the polish
list), D (done — the GIG_RANGE fix landed scoped and clean).
## Laws
Charter v5 laws + the two new ones above. Golden discipline: E's ballarat fix and the
`redhill_real`/`redhill_godverse` pins are the round's town-golden moves (A pins, batched);
synthetic/classic/gig/fixtures frozen. No server code — G3 stays paper until ratified.
## THE EXACT RUN ORDER
1. **Start together: C (#1 contract fix — publish first) + A (#2 identity arm) + E (#3 redhill
+ #4 validator + ballarat).**
2. **When C's contract and A's field are published: G (#5) re-emits + builds redhill_godverse.**
3. **When G pushes: F merges (amended law #5), then closes (#6)** — wire, honest gate, tag.
Waves: **[C, A, E] → [G] → [F merge + close]**. B/D on-call.
## Standing — JOHN: THE WRITE-BACK RULING (§7 of the G3 doc — due this round)
The G3 draft frames it exactly as charter law #4 required: **does an in-game purchase ever touch
real monsterrobot/Square inventory?** Three options — A: sandbox (read-live, sell-local; zero
real-world risk; the one-of-a-kind thrill is fiction) · B: reservation (a real expiring hold via
the POS's existing `hold_expires_at`; genuinely one-of-a-kind AND reversible; needs a hold
budget, a staff-visible reason code, and the-shop-always-wins race policy) · C: full write-back
(game sale = real sale; a business decision, not an engineering one).
**Fable's recommendation (yours to overrule): ship v5.0 on Option A, charter Option B as a
v5.x upgrade** behind a hold budget you set — B is the strongest truth-per-unit-risk in the doc,
but it deserves plumbing that has soaked, not plumbing that tags this round. Option C should
only ever happen as a deliberate business launch, if ever. Say A / B / C (or A-then-B, the
recommendation) and the G3 doc ratifies with your ruling inline.

View File

@ -0,0 +1,98 @@
# PROCITY — Round 25 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **R24 verdict: THE CRATE WALKS — and the tag correctly holds a second round.**
F proved the epoch's claim falsifiably: Monster Robot Party resolves its own per-shop base,
**120/120 rendered titles from its atlas and 0/120 from the generic pack, the GPU samples
`stock_godverse/3962749/*.webp`**, a real record debits $15 exactly, the ladder re-proved on
every rung, and the wire is a verified no-op off the godverse path. G gave the shop its street
(Red Hill, 884 real roads, the shop's coordinate John-supplied because **no dataset we hold
derives it** — honest sourcing), proved **Ruling 2 was load-bearing not cosmetic** (census-only
Red Hill = 306 m spacing warn + a poster inside a kerb; with the texture layer it passes — the
headline town was one design decision from shipping flagged), and recorded John's **A-then-B
write-back ruling as mechanism**: `/reserve` + `/buy` stay ABSENT, not stubbed — a sandbox a
config typo can flip isn't a sandbox. C owned the R23 root cause squarely. E built the street
and the honest validator. A published the identity field first, as briefed.
**Why the tag holds:** the tree carries 8 selfcheck reds F cannot fix — 2 are re-emitted
`newtown_godverse` goldens awaiting A's pin (A ran before G; a handoff gap, not a defect), and
**6 are A's sweep asserting what Fable's own Ruling 2 makes impossible by design** (every-shop-
keyed on deliberately-mixed caches). Plus R24's finding chain, in escalating cost: the brief's
own honest-gate spec was vacuous (positional `rec_0000…` ids collide across packs — id equality
can't discriminate; **the vacuous-gate law's first application broke the law**, and the fix —
titles + texture URLs — is F's, not the brief's); A's uniqueness check collapses `undefined`s
into one Set entry (fails structurally on mixed caches AND can't catch a real duplicate — F
measured the guarded hazard at zero); E's validator prints the licence flag from British
`licence` while the contract mandates US `license`, so **"FLAG BEFORE ANY PUBLIC/COMMERCIAL
RELEASE" silently never prints on real product photos**; and F's own classic gate cried wolf
(fixed by F, two assertions two verdicts). Round 25 closes all of it. Tag **`v5.0-alpha`** at
close — third time, earned.
## Process rulings (from R24's events)
1. **Lane G is a full lane on main.** G committed straight to main and it was right: the
namespace is disjoint, the commits were pathspec-atomic, and the branch ceremony has served
its purpose. Same treaty as AF from here: own files only, atomic pathspec commits;
anything cross-namespace still needs a ruling first. (The R23 merge amendment is thereby
retired along with the branch.)
2. **Ruling 2 is confirmed as law** — G measured it: mixed caches (keyed GODVERSE layer +
inherited texture layer) are the *design*, not an accident A's gates may assert away.
3. **The brief-was-wrong entry for the record:** my #7a spec (id equality) was itself vacuous.
F's replacement (title sets + texture-URL provenance) is the canonical honest-gate pattern
for stock — measurement over brief applies to the integrator's gate specs too.
## The ledger
1. **A — reconcile the sweep with Ruling 2, pin the six (the round's spine).**
- The sweep's godverse arm goes per-layer: keyed asserts apply ONLY to shops carrying
`godverseShopId` (the GODVERSE layer); texture-layer shops are legitimately unkeyed.
- Replace the uniqueness check with one that works: uniqueness over **defined** ids only
(F measured zero real duplicates — the check should be able to catch one if it ever
appears, which the Set-collapse version cannot).
- Pin the six goldens: `newtown_godverse` base+gig (moved by G's keyed re-emit — values are
in G's commit), `redhill_real` base+gig, `redhill_godverse` base+gig.
- Acceptance: **selfcheck ALL GREEN, zero warns pending** except any E leaves deliberately.
2. **E — the licence line, made unmissable (conflict #1).** Align the validator with C's §7.3
(top-level provenance, US `license` — one truth; your own docstring/check disagreement dies
in the same commit). Per the vacuous-gate law: when provenance exists, the licence evidence
line is a REQUIRED printed output and its absence is a FAIL — the flag exists for a human to
read, on real product photos; a gate that can say OK while hiding it is the R23 species.
3. **C — one contract line; G — the sku re-emit (F's finding #1, made structural).** Positional
slot ids collide by construction. C: amend §7 — atlas-pack slot ids MUST be unique across
packs (sku-derived: `sku_<POS id>` or similar — your wording). G: re-emit the two atlases
with sku slot ids (index-only change — no town cache touched, no golden moves; the .webp
bytes shouldn't move either, same as your R24 re-bake). E's validator asserts the form.
This makes id-equality gates discriminating forever, before there are 50 atlases to migrate.
4. **F — land your R24 work, then the tag.** Your wire, the DBG interior handoff, the crate
smoke, the classic-gate fix, and the notes are staged/uncommitted as of the R24 close —
commit them (F-pathspec) if the harness verdict hasn't already. Then, after A/E/G land:
full `qa.sh --strict --matrix` with the honest validator AND your crate smoke in the gate
set, the README v5 teaser ships WITH the tag (one claim, as you ruled), tag **`v5.0-alpha`**,
push. Your hold streak is the system working — end it only on green.
5. **B / D — on-call.** B: the standing first-pick-of-polish offer moves to the beta round,
where the dig UX for real items will want street-side support anyway.
## Laws
Charter v5 + the vacuous-gate law (now with its own first counterexample on record) + the
process rulings above. Golden moves this round: A's six pins only. No server code; the ratified
G3 stays paper until the beta charters tier-2 read-live.
## THE EXACT RUN ORDER
1. **Start together: A (#1) + E (#2) + C (#3's one line).**
2. **When C's line lands: G (#3) re-emits.** (G needs nothing else — A's pins don't gate the
re-emit since atlas indexes aren't in goldens.)
3. **When A + E + G are in: F (#4)** — commit your R24 work if not already, full gate, tag,
push.
Waves: **[A, E, C] → [G] → [F]**. B/D on-call.
## Standing
- **John — nothing needs you.** Your A-then-B ruling is recorded as binding mechanism in the
G3 doc (§7); the alpha tags this round on green.
- **v5.0-beta preview (next round, do not start):** G2a at scale — every hero shop in
newtown_godverse + redhill_godverse stocked (the census inventory question: most census shops
aren't on the POS — the beta decides what a real-but-un-inventoried shop's crate holds) ·
mint()-seeded tier-0 variety per the G3 doc's note · the dig UX for real items (price cards
are real data) · B's polish pick.

View File

@ -0,0 +1,147 @@
# PROCITY — Round 26 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **R25 verdict: one commit and one harness verdict from done.** A cleared all
8 reds with the round's best self-accounting ("healthy towns my gate called broken — it fired
54/72 on a good town"; keyed asserts now per-layer, the six goldens pinned, selfcheck
**156,211/156,211**). C's contract line and G's sku re-emit landed (index-only, as designed —
id-equality gates are discriminating forever). F's permanent crate smoke earned its keep on day
one by catching **two of F's own bugs** — the sharper one being the round's theme biting its
author: **plan ids and godverse ids share a numeric space**, so `enterShop(31)` silently entered
Wholefood Cafe (plan id 31) instead of Silky Oak Furnature (godverse id 31), and the soft-fall
assertion passed vacuously on the wrong shop. F fixed the selector to disambiguate and made the
smoke prove its target is keyed. *Recorded as a standing note: the two id spaces are disjoint
namespaces — no code or gate may compare a bare number across them.*
**Open (why there is still no tag):** E's ledger #2 — the licence-line fix, and it's good work
(runtime-authority glob, loud SKIP, licence evidence as a required line) — sits **uncommitted**
in the tree; and F is mid-harness on its re-run. Round 26 opens by closing both, then the beta.
## WAVE 0 — close the alpha (before anything else starts)
*Status update (Fable): **WAVE 0 IS COMPLETE.** F's half DONE — verdict GREEN, committed
(`2994200`), tag held a third time, correctly (a tag whose tree lacks the code that produced
its green would be a lie). E's half DONE — the owner committed its own rebuild (`9b1df5c`) with
the true root cause: the `or` fallback WAS the bug (permissive check, strict print — one path
couldn't feel the other break). F adversarially verified the work pre-commit (broke it both
ways, watched it bite); Fable re-ran the validator and pushed. **F: step 2 is GO — confirm and
tag `v5.0-alpha` on resume, then the beta waves below.***
1. **E: commit your own work.** `pipeline/validate_atlas.py` carries your R25 rebuild, unstaged.
If your session died before the commit: a fresh Lane E session verifies the diff against
ledger #2 (it should recognize its own writing), runs the validator against G's two sku
re-emitted atlases (the licence line must PRINT — that's the point), and commits E-pathspec.
Nobody else touches this file — it's yours.
2. **F: tag on E's commit.** Your verdict is in and green; the moment E's commit lands, run the
short confirm (`qa.sh --strict`), tag **`v5.0-alpha`**, push, README teaser with it.
## Then: v5.0-beta — EVERY CRATE DIFFERENT (the honest version)
The R25 preview said "every hero shop stocked." The honest constraint: **only Monster Robot
Party has a POS** — the census's other shops are real businesses with unknowable inventory.
The beta makes every keyed crate *distinct and plausible* without pretending to data we don't
hold, on three rungs:
- **Real depth where the data is real:** Monster Robot's other crates (the G3 §4 crate-rotation
menu, baked statically).
- **Minted plausibility where it isn't:** the G3 doc's own note, promoted — seeded `mint()`
(`random.Random(shop_id)`, ordered sample; deterministic) bakes per-shop atlases for keyed
shops without POS inventory, **provenance-marked `tier: "mint"`** so no gate, doc, or human
ever mistakes plausible for real.
- **Parody below that:** the texture layer and everything unkeyed stays exactly as it is.
## The ledger
1. **The mint contract line (C, first — half-day rule).** §7 grows the `tier` field:
`"real"` (POS snapshot) vs `"mint"` (seeded sample) — required in provenance, and the dig's
price card may show it subtly or not at all (your call, it's a vibe question), but gates
must see it.
2. **The mint baker + Monster Robot depth (G).** Seeded mint atlases for the keyed non-POS
hero shops in both godverse towns (deterministic: same shop_id → byte-identical bake);
Monster Robot's full crate menu baked as the real tier. Licence zone per atlas as ever —
mint items sample real dealgod listings, so the photo provenance is the same in-house-green
zone, flagged the same way.
3. **The validator grows the tier arm (E).** `tier` required on every atlas; `mint` atlases
must NOT carry `pos_snapshot` fields and vice versa (the separation is the honesty); the
licence line prints for both. Vacuous-gate law applies: an atlas with no tier FAILS.
4. **The dig UX for real items (C).** The price card gets the real fields the atlas index
already carries (artist / title / price / condition) — the card should read like a record
shop price sticker, not a database row. Budget ceilings unchanged.
5. **B returns — dig-adjacent street polish (your pick, at last).** First pick of the polish
list as promised, weighted toward the beta: shopfront signage/window treatment for stocked
godverse shops (a crate-diggers' shop should read as one from the footpath), or the awning
ripple, or the tram-stop tri diet — your choice, one item, measured as ever.
6. **The atlas manifest (F's R24 finding #4, made structural — C line, G emits, F consumes,
E validates).** Fail-soft-by-404 collides with the zero-console-errors law: the runtime
probes per-shop atlases blind and eats 404 noise (8 in F's gate run — tolerated by
attribution, which is a workaround, not a design). Fix at the root: G emits
`web/assets/stock_godverse/index.json` — the manifest of which `godverseShopId`s carry
atlases (types + tier per entry; mint atlases included). C: the §7 contract line. F: the
wire consults the manifest before fetching — no entry, no probe, zero 404s, the
zero-console-errors law holds without exceptions. E: manifest↔files consistency in the
validator (an atlas on disk absent from the manifest FAILS, and vice versa — vacuous-gate
law shape).
7. **The beta gate (F).** *(Status, Fable: waves 12 are COMPLETE — C/B/E/G all landed. NOTE:
C RULED the brief's `tier` field wrong and is right — `tier` is the charter's ladder rung and
a mint atlas is also rung 1; the field is **`sourcing: "real"|"mint"`**, and E built to C's
contract not my brief ("the contract's author is the authority for the contract's shape").
Your gate asserts `sourcing`, not `tier`. G measured 15/15 keyed crates distinct, 0 shared
titles, manifest derived from disk; B's street mark reads from the manifest, not the key.)*
Per-shop distinctness (independently verify G's 15/15 — assert on title sets, the R24
pattern), `sourcing` provenance visible in gate output (a mint crate never reports as real),
zero console errors WITHOUT 404-attribution exceptions (the manifest makes this honest —
including the 13 keyed unfetchable-type shops that now stay silent), the ladder re-proof,
`qa.sh --strict --matrix`, tag **`v5.0-beta`**, push.
7. **A / D — on-call.** A: nothing pending (your R25 close was clean). D: nothing — unless F's
distinctness gate wants a crowd check at a stocked shop (patronage at Monster Robot on a
Saturday feels like a D flourish, not a requirement).
## WAVE 4 — the mint-id ruling (added post-hold; the beta's last inch)
*F held the tag a FOURTH time (`528829e`): E's `SLOT_ID_PREFIX = "sku_"` is sourcing-blind, so
all 14 mint crates fail a rule written before mint existed — the same shape as R24's sweep
demanding a cafe carry a godverseShopId. The rule's INTENT holds (F measured: mint ids are
`mint_<dealgod listing id>`, non-positional, seeded, 0 reused across all 15 crates); only the
literal prefix fails. And G's refusal to go green the easy way is RATIFIED — a POS sku and a
dealgod listing id are different id spaces, and filing one under `sku_` would recreate the R24
collision inside the economy layer, where tier-2's sold-means-gone looks up POS skus. The
prefixes ARE the namespace fence.*
1. **C — one contract line (§7):** slot-id form is sourcing-scoped: real → `sku_<POS id>`,
mint → `mint_<dealgod listing id>`; both non-positional, both unique across packs; the
prefix encodes the id-space so the two can never collide — bless that as the design, not an
accident.
2. **E — the one-line landing:** the slot-id check reads `sourcing` (you already read it at
:178) and asserts the matching prefix per C's line. Real crate: `sku_`. Mint crate: `mint_`.
Anything else, or a cross-wearing crate: FAIL.
3. **F — re-run atlas-QA, then the tag:** `qa.sh --strict --matrix` (should flip 6/1/0 →
7/0/0), tag **`v5.0-beta`**, push. Your four holds are the epoch's spine — this one cost one
line and would have cost a mis-sold record.
## Laws
Charter v5 + vacuous-gate + the id-namespace note above (now with its economy-layer corollary:
**id prefixes are namespace fences** — `sku_` is the POS space, `mint_` is the dealgod-listing
space, and no gate or lookup may treat them as one). Golden discipline: no town-cache changes
this round → no golden moves (mint atlases are `stock_godverse/**` files, outside every
golden). No server code — tier 2 stays design until the beta ships and the v5.0 round charters
it against the ratified G3.
## THE EXACT RUN ORDER
0. **WAVE 0: E commits; F lands the verdict and (on green) tags `v5.0-alpha`.** Hard stop until
the tag verdict is known.
1. **Start together: C (#1 contract line + #4 dig card) + B (#5 pick).**
2. **When C's line lands: G (#2 mint baker + depth) and E (#3 tier arm) in parallel.**
3. **When G + E are in: F (#6)** — distinctness gate, ladder, tag **`v5.0-beta`**, push.
Waves: **[E-commit, F-verdict] → [C, B] → [G, E] → [F]**. A/D on-call.
## Standing
- **John — nothing needs you.** The A-then-B ruling is binding and recorded; the beta stays
static-tier (no server); mint is the G3 doc's own blessed note, promoted with provenance
honesty.
- **v5.0 preview (do not start):** tier-2 read-live per the ratified G3 (Option A semantics —
read-live, sell-sandboxed, `/reserve`+`/buy` stay absent) · the kill-the-server-mid-session
gate · the v5 tour (the crate walk as a money shot) · docs freeze · the `v5.0` tag · the
epoch retro.

View File

@ -0,0 +1,217 @@
# PROCITY — Round 27 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **R26 wave-4 verdict: C and E landed the mint-id fence, and both wrote the
same honest headline — "the rule was the stale thing, not E's check."** C owned its stale §7.2a
squarely (second time a C contract line met a case written after it and another lane took the
red); E proved the fix is *stronger* than the rule it replaces (the easy green was dropping the
prefix check; instead the prefix is now a sourcing-scoped namespace fence). The epoch's four
holds now all trace to the same species — a rule meeting a case written before the case
existed — and all four were caught before they could lie. The system is working exactly as
designed.
**Open: F's wave-4 step 3.** The beta tag is one re-run away. Round 27 opens there, then closes
the epoch: **v5.0 — THE LIVE CRATE.**
## WAVE 0 — F tags the beta
`qa.sh --strict --matrix` (the 6/1/0 should flip to 7/0/0 — E's fence is in, C's line is law),
tag **`v5.0-beta`**, push. Nothing below starts until this lands.
## Then: v5.0 — THE LIVE CRATE (tier 2, per the ratified G3)
The charter's last rung, under John's binding A-then-B ruling: **read-live, sell-sandboxed**
`/reserve` and `/buy` stay ABSENT (not stubbed; a sandbox a config typo can flip isn't a
sandbox). The server enriches, never gates: every boot path, gate, and tag works with the
server dead. Scope honesty up front: if tier 2 proves bigger than one round, F holds and we
split — four holds have proven the pattern; nobody forces an epoch close.
## The ledger
1. **The read endpoints (G, on ultra — server-side).** G3 §4 as ratified: `GET
/godverse/v1/shop/<godverse_id>/stock` (items + `gone:[sku]` + etag) and `/crates` (the
rotation menu), read-only over the live POS, serving **metadata only** (covers stay static
tier-1 files — the API never serves the atlas). Monster Robot is the only real shop; that's
the point — one shop, live, true. Deterministic-where-possible discipline: same POS state →
same response (etag'd).
2. **The tier-2 reader (F, with C's seam).** Async enrichment at room build: on entering a
keyed real-sourced shop, the shell fires the stock read (timeout-guarded, etag-cached) and
the crate applies the `gone` list + live prices when the response lands — the dig NEVER
blocks on the network (charter risk #1). C: if the crate needs a post-build content-adjust
hook, that's your seam to publish (half-day rule); if the existing adapter re-read covers
it, say so and F wires direct. Server dead / unreachable / slow → tier 1 exactly, silently.
3. **The kill-the-server gate (F — the epoch's defining gate).** Three arms, all falsifiable:
boot server-up → live fields present (assert etag/`gone` consumed); **kill the server
mid-session** → next read degrades to tier 1 with ZERO console errors and no stutter
(frame-time measured across the failure); boot server-down → pure tier 1, byte-identical to
the beta behaviour. This is charter law #1 made a gate. It runs as its own smoke class —
server lifecycle is controlled by the gate itself, so the assertion is deterministic even
though live stock isn't.
4. **Sold-means-gone, sandboxed (F + G).** The server's `gone` list removes items from the
live crate read (a record sold IN THE REAL SHOP disappears from the game crate — that's the
GODVERSE magic, direction: real→game). In-game buys stay session-local per Option A. Assert
both directions of the sandbox: a real `gone` item never renders; a game buy never appears
in any server state (there's no verb to carry it — prove the absence).
5. **The v5 tour + money shot (F).** `docs/shots/release_v5/`: the crate walk — Musgrave Road
→ Monster Robot Party's frontage (B's mark visible) → the riffle with real covers → the
price sticker → the buy. Plus one mint-shop frame (the honest caption: plausible, not real)
and the `?classic=1` control. The R21 cluster-pose primitive poses the street frames.
6. **Docs freeze (all lanes, light).** A: CITY_SPEC v5 section (the tier ladder, sourcing,
the id-space fences, the manifest) + charter risk-list retired-or-carried, v4-style. E:
provenance v5-final (mint atlases + the licence line's story). C: LANE_C_PUB v5-frozen
marker. G: the G3 doc updated design→as-built. B/D: notes current; nothing else owed.
7. **The tag + the retro.** F: `qa.sh --strict --matrix` + the kill-the-server gate green →
tag **`v5.0`** → push. Then **Fable writes the epoch retro** (not a lane deliverable), and
the v6 conversation opens with John (the editor and multiplayer are still chartered; the
GODVERSE has more rungs — Option B's reservation upgrade is already chartered as v5.x).
## Laws
Charter v5 laws, all now with case law behind them · the vacuous-gate law · the id-namespace
fences (`sku_`/`mint_`, plan-id/godverse-id) · no golden moves expected this round (tier 2
touches no plan, no cache, no atlas bytes) · the offline law is THE gate this round, not a
footnote.
## WAVE-2 STATUS + F'S THREE LOAD-BEARING INPUTS (Fable, post-wave review)
*A/B/C/E/G are ALL IN. The beta tagged. What remains is F's close (#2 reader → #3 gate → #4
sandbox → #5 tour → #7 tag). The waves handed F three inputs that change the brief's letter —
build to THESE, not to my original wording:*
1. **C's seam ruling (§8): direct wire, no hook — but PICK-THEN-OMIT is law.** The naive wiring
(filter `pack.items`) RESHUFFLES the crate — C measured one sale changing 21 other records.
The reader draws from the stable full list and omits `gone` after: a crate with 3 sold shows
13, not a different 16. **Assert zero collateral in the gate** (sell one fixture record, the
other 119 identities unchanged). "Selling one record must remove one record, not re-stock
the shop."
2. **G's finding: `gone[]` has no subject in production.** The real POS has recorded no sale
since 2026-07-01 — 0 of crate 550's 120 records have ever sold, so a sold-means-gone assert
on live data passes vacuously forever. G built the remedy: **`make-fixture`** gives the gate
a subject. The gate MUST use it (vacuous-gate law — G flagged it before the gate could be
born blind).
3. **G's lifecycle is the kill switch:** `godverse_server.py start|stop|status` — the
kill-the-server gate drives it directly. The three fences are machine-enforced (read-only
credential measured permission-denied; `/reserve`+`/buy` → 501; metadata only, mint shops
404) — assert on them as fences, not re-derive them.
*Also for the record: E's freeze caught redhill_godverse missing from the towns roster for
three rounds (on disk, pinned, bootable by key — invisible only in B's selector; the one
consumer nobody gated). E owned it, fixed it, and wrote the gate-shape lesson. B verified the
R26 handshake against G's real manifest. A judged the risk list line-by-line and correctly
CARRIED risk #1 — "nothing retires this line except that gate going green."*
## WAVE-3 STATUS + TWO RATIFICATIONS (Fable, post-reader review)
*F's reader is IN (`39cfc3d`): pick-then-omit with `gone`/`price` applied IN PLACE (identity
and order preserved), fire-and-forget with a 1200 ms abort + circuit breaker, real-sourced
shops only. Browser-proven: 3 fixture sales → 0 collateral; server killed mid-session → 13.1 ms
enter, 14 ms worst frame gap, zero errors. Two F rulings are hereby RATIFIED as law:*
1. **Tier 2 is opt-in: `?live=1` / `?live=<url>`; absent → byte-identical to the beta.** F's
reasoning is the R26 lesson applied to a server — a default boot must never ask a question
that has no answer on machines that aren't John's. "Tier 2 is a place you go, not a thing
that happens to you." Auto-discovery is a v5.x question with a reachability contract.
2. **The collateral assertion is the canonical sold-means-gone gate.** "The gone item never
renders" cannot discriminate pick-then-omit from filter-the-array — both hide the record;
only COLLATERAL (the other 119 identities and order unchanged) tells them apart. Gates
assert collateral. (The vacuous-gate law's most precise application yet.)
**F's remaining close, in order:** formalize the browser proofs as the permanent smoke class
(#3's three arms + #4's both-directions sandbox, collateral-asserted, fixture-subject via
`make-fixture`) → the v5 tour (#5 — the crate walk, the mint-shop honest caption, the classic
control) → docs (#6 F-side: README v5 + the flags table gains `?live`) → `qa.sh --strict
--matrix` + the new smoke class → tag **`v5.0`** → push. Then Fable writes the epoch retro.
## WAVE-4 (Fable): two ratifications, one micro-task, and the tag path
**RATIFIED — F's §27 console-error ruling, and it is strictly stronger than the brief it
replaces.** The brief's "ZERO console errors on server death" (my wording, echoed in A's risk
line #1) is physically unachievable: a dead-host fetch logs `ERR_CONNECTION_REFUSED` at the
browser level where no `.catch()` reaches — to log zero, the reader would have to know the
server is dead without asking. One refusal is the irreducible cost of discovering death. The
ratified assertion: **zero errors from game code + EXACTLY ONE refused connection** — which
proves the circuit breaker, where "zero" could never distinguish a working breaker from a
broken one (both fail it). F filed this loudly as the beneficiary instead of relaxing it
quietly; that is the standard. (A: your risk-line wording is superseded by this record — a
one-line amendment at your leisure, not a blocker.)
**RATIFIED — the control-based frame assertion.** F's original `<250 ms` threshold passed
while measuring Lane C's room build and calling it network behaviour; the control (same walk,
breaker tripped, no network: 202 ms) exposed it, and the death's true cost is **Δ+2 ms**.
Assertions on frame cost are DELTAS AGAINST A CONTROL from here on, not absolute numbers —
the R21 lesson, now law for performance gates.
**THE MICRO-TASK — B: the frontage pose (F's filed ask).** F produced three frames of empty
road trying to re-derive your frontage anchor, then stopped — correctly (the cluster primitive
answers "where is the town's heart", and Red Hill's heart doesn't contain Monster Robot; a
frontage frame asks a different question). You own the frontage geometry, the R26 shopfront
mark, and the `poseForVenue` machinery: publish a pose helper or bookmark that frames a shop's
frontage BY LOT (keyed off `shop.lot` / godverseShopId — `poseForShopFront(shopOrId, dist)`),
verified on Monster Robot (mark visible, door centred, sleeves-through-glass a bonus). One
commit, B-pathspec. This also closes the gap for every future godverse tour frame.
**Then F closes:** the 4th frame through B's helper → full `qa.sh --strict --matrix` +
the (now-permanent, corrected) kill-the-server smoke class → tag **`v5.0`** → push. Fable's
retro follows.
## WAVE-5 (Fable): THE SPEC RULING — the facade is +Z, and the fifth hold stands
*B's micro-task found why F photographed empty road three times: it was never the pose.
`plan_osm.js:443` aims the lot's Z at the road (implementing CITY_SPEC:71); `buildings.js:404`
draws the facade on +Z (CITY_SPEC:335, B's canon). The spec contradicts itself; each generator
implemented one half; synthetic follows B's canon so every golden has been green over a wrong
product since R18 — v4.0 shipped on it. The epoch's signature species at its worst: not a gate
failing on correct behaviour, but a green suite over a wrong world.*
**THE RULING: the visible facade is LOCAL +Z. B's canon is canonical.** It has been the de
facto truth since v1 (buildings.js drew it there from the start; venue.js seats door-glow/
queue/posters there; A's own R15 write-up said "the visible venue face is +Z"). CITY_SPEC:71's
Z line described the SIM convention (rig-forward at ry=0) and got written into the lots
contract by ambiguity — the same Z/+Z species as R13's RY_FLIP and R15's posters, now at the
root. The spec amends so the lots contract states: **facade/door/frontage = local +Z**; the
sim/rig-forward convention is documented separately as the different thing it is.
**A — the fix, the sweep, the pins (ONE commit):**
1. Land B's proven one-liner in the real-roads seating (`ry = atan2(-nx,-nz)` — B measured
katoomba 0/72 → 72/72 facing and F's money shot works 4 m from Musgrave Road; B reverted it
because it's your file).
2. **Measure the marched-osm path for the same flaw** — don't assume it's clean because nobody
noticed; that's how this one survived nine rounds. State the verdict either way.
3. Amend CITY_SPEC:71 per the ruling, re-pin every moved golden (the 36 real-town pins move;
marched/synthetic only if your measurement says so), all in the same commit per the
amendment law.
4. **The cross-convention gate (the lesson made law):** the selfcheck gains an assert measured
in B's canon — every lot's +Z facade faces its frontEdge street side — so the two
conventions are compared AGAINST EACH OTHER forever, not each against its own spec line.
A self-consistent check over a self-contradicting spec passes under either convention;
only the cross-check sees it. (F proved this the hard way and recorded it.)
**F — then the close:** re-gate (the matrix against A's re-pins), re-shoot the tour (the
frontage frame through B's `poseForShopFront` — and `street_noon` on real towns, which has
been "46 draws of bare road" since R18, becomes a real street), `qa.sh --strict --matrix`,
tag **`v5.0`**, push.
**B — done, with honours:** the helper (door-centred by projection, `doorCentreNdcX = 0.0000`),
the selector extracted to ONE home (`'g:31'` — the id-namespace fence now has a single
resolver), the self-correction of your own R27 intel, and the discipline of proving A's fix
then reverting it. Nothing further owed.
## THE EXACT RUN ORDER
0. **WAVE 0: F re-runs and tags `v5.0-beta`.** Hard stop until it lands.
1. **Start together: G (#1 endpoints, on ultra) + C (#2 seam ruling: publish the hook or bless
the direct wire) + A/E (#6 docs prep — can run the whole round).**
2. **When G's endpoints are live on the tailnet and C's seam is ruled: F (#2 reader).**
3. **F (#3 kill-the-server gate + #4 sandbox proofs + #5 tour), then #7: tag `v5.0`, push.**
4. **Fable: the epoch retro.**
Waves: **[F-beta-tag] → [G, C, A+E] → [F reader] → [F gate/tour/tag] → [retro]**. B/D on-call.
## Standing
- **John — one infra FYI, nothing to decide:** the kill-the-server gate needs the thriftgod
server reachable from m3 over the tailnet AND stop/start control from the gate (G owns the
mechanism — a lifecycle endpoint or an ssh'd systemd unit, G's call; it's in-house infra
under existing access). Your A-then-B ruling continues to hold the write verbs absent.
- **v5.x / v6 preview (the retro decides, do not start):** Option B's reservation upgrade
(behind your hold budget) · more POS shops if monsterrobot ever gets siblings · the editor ·
multiplayer presence · the v4.x/v5.x polish tails.

View File

@ -0,0 +1,63 @@
# PROCITY — Round 28 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **v5.0 SHIPPED, retro written ([RETRO_V5.md](../RETRO_V5.md)). Nothing is
owed anywhere.** Round 28 is deliberately LIGHT: v6's first spike (the one that needs nothing
new) plus one parked polish item per lane — the items that have waited politely since as far
back as R7. Tag **`v5.1`** at close. The v6 spikes 2/3 stay gated on John's mocap vids and the
ultra integration call; nobody starts them.
## The ledger
1. **SPIKE 1 — the free stash (E + D; v6 groundwork, the charter's first thread).**
- **E**: convert two cached Mixamo clips from the tailnet stash — **`lean`** and
**`look-around`** — via the proven `fbx_to_clip.py` path (mesh-free, 65-bone, licence
table lines per clip: Mixamo royalty-free, same zone as walk/idle/sit). Depot + local per
the sit.glb precedent.
- **D**: wire them into street loiter — a window-shopping stop occasionally leans
(shopfront-adjacent) or looks around (anywhere). Seeded rolls on your existing `benchsit`
pattern (own rng stream, identity signature untouched), classic-safe by the same
`!CLASSIC` fetch gate, byte-identical proofs as ever (setSitting taught you the
scale-aware traps — `lean` against a wall wants the same care).
- Acceptance: a fresh boot shows peds leaning and glancing; classic/`?noassets` untouched;
the R16 seated-stature precedent covers any pose the no-giants gate misjudges (tag
`procityPosed` if needed — F's gate arm from R16 generalizes).
2. **Awning fabric ripple (B — parked since R17's wind round, your promised pick).** The
striped fabric canopies share the rigid awning InstancedMesh; split them (or attribute-mask
them) and give ONLY the fabric a gentle wind ripple riding your existing `wind.js` uniforms.
Weather-driven like the trees; `?classic`/`?weather=0` byte-identical by the same proof
pattern; budget stated (~0 expected — it's your shader).
3. **`audioEmitter` (C — your own parked idea, at last).** Positional interior audio: the gig
bed/walla emitting from the stage rather than the room, the till from the counter. Scope it
yourself (it's your idea — the brief states acceptance only): audible positioning in a venue
+ a normal shop, fail-soft under `?mute`/`?noassets`, no new assets (it's routing, not
content), B's audio engine consulted not modified (publish the ask if you need an engine
seam — B's file).
4. **Instrument LOD (E + D — the oldest tri item, v3.1-era).** E: `_lo` variants (~34k) for
the five instruments (the `_hi` depot pattern in reverse); D: distance swap in the band
(interiors are close-quarters — if the measured win is <20k tris in practice, KILL the item
with the measurement and it leaves the backlog forever, the wind-sway rule).
5. **The gate + the tag (F, last).** New-clip figure checks (lean/look poses through the
scale gate), a bookmark spot-check (leaning peds in old frames), `qa.sh --strict --matrix`,
notes §28, tag **`v5.1`**, push. Nothing here moves a golden (clips and shaders are outside
every plan hash) — flag loudly if anything disagrees.
6. **A + G — rest.** A: on-call (nothing should touch a plan). G: your next work is v5.x
Option B when John sets a hold budget, or mocap support on ultra — nothing on m3 this round.
## THE EXACT RUN ORDER
1. **Start together: E (#1 clips + #4 `_lo` variants) + B (#2) + C (#3).**
2. **When E's clips land: D (#1 wiring, then #4 swap-or-kill).**
3. **When B/C/D are in: F (#5)** — gate, tag `v5.1`, push.
Waves: **[E, B, C] → [D] → [F]**. A/G rest.
## Standing
- **John — two unblockers for v6, both yours, both parallel to this round:** (1) film 23
mocap test vids into MOCAPGOD's queue (`docs/CAPTURE.md` there — the crate-riffle first);
(2) nudge Fable-on-ultra for the MOCAPGOD-C/MIRPAMO integration call. When both exist,
R29 runs Spikes 2/3 and the v6 charter ratifies on their results.
- Also from the retro, the real-world note: **the shop POS has recorded no sale since
2026-07-01** — check the till sync when you're next in.
- **Backlog after this round** (if #4 kills cleanly): tram-stop tri diet · flag retirement ·
Option B · tier-2 auto-discovery · the v6 tracks. The polish debt is nearly drained.

View File

@ -0,0 +1,82 @@
# PROCITY — Round 29 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **v5.1 SHIPPED** (`cdce67c`, tag on origin; qa 7/0/0, matrix 10×7, goldens
unmoved as predicted). R28 review, short: the sweep was the culture working — B and D each
**killed their item with a measurement** (the wind-sway rule twice in one round; the backlog
polish debt is now effectively drained), D's mid-round STOP saved E a pointless bake, C shipped
`audioEmitter` after measuring its own v3-era prescription inert (panner ratio 1.0000 —
bit-identical door vs stage), E shipped `look.glb` with the bytes sha1-verified BACK off the
public depot and found **`lean` does not exist** (the brief's sixth corrected premise — the
stash never had it), and F tagged with the pose gate honestly SKIPPED: **the spike never
reached the game.** Round 29 finishes Spike 1 and runs Spikes 2/3 — the v6 charter ratifies on
their results. Most of this round is GATED ON JOHN (below); wave 0 runs today.
## WAVE 0 — finish Spike 1 (runnable NOW, no dependencies)
**D:** bind and wire `look.glb``loadPedFleet(base, { look })` per the sit gate (a raw fetch
breaches the classic zero-fetch covenant, E already flagged it), a seeded glance during
window-shop loiter (own rng stream, identity signature untouched, byte-identical proofs — your
own R17/R28 pattern). **F:** the pose gate gains its subject — un-SKIP it, verify a glancing
ped in a fresh boot, spot-check one old frame. No tag for this alone; it rides Spike 2's close.
## THE GATE ON THIS ROUND — John's two unblockers (everything below waits)
1. **Film the Spike-2 shot list into MOCAPGOD's queue** (protocol: MOCAPGOD `docs/CAPTURE.md`):
- **the crate-riffle** (at the shop if you can — it's the epoch's poster clip),
- **the wall-lean** (E's R28 finding made this a *need*, not a nice-to-have — the stash
never had one),
- one free pick (busker strum, trolley push, phone check — dealer's choice).
2. **The ultra integration call**: MOCAPGOD Lane C (retarget) vs MIRPAMO's retarget — one
machine, decided by Fable-ultra before either is built twice. Nudge that session.
## SPIKE 2 — the full mocap loop (when the queue has vids)
Runs mostly on ultra (MOCAPGOD C/D lanes + the integration call), lands on m3 as clips:
- **E (m3):** receive the retargeted clips, convert/validate to the house clip form (the
look.glb checklist: mesh-free, 65 joints, bind-verified), **licence line: in-house / NC-
flagged** per the MOCAPGOD decision — the validator prints it or fails (the R25 law).
- **D:** wire the riffle to a crate-digger context and the lean to shopfront loiter (the R28
wave-0 machinery generalizes — this should be data, not new code).
- **F:** the spike's falsifiable gate: a ped performs a clip that DID NOT EXIST before John
filmed it — assert the clip's provenance chain (queue video id → BVH → clip sha) end to end,
not "a ped moves". Then tag **`v6.0-spike`** (a checkpoint tag, not an epoch tag) with the
spike report.
## SPIKE 3 — a generated body (parallel to Spike 2, ultra-heavy)
flux_local concept → hunyuan3d_mlx mesh → **MIRPAMO autorig** → walk retarget → **in-game
through the no-giants gate**. Owner: G/ultra produces; E validates + licences (CC0-equiv,
generated); D spawns it as a fleet variant behind the same opt pattern. HONESTY CLAUSE: this is
the least-proven link in the whole v6 stack — a clean failure report ("autorig quality on
generated meshes: X") is a full deliverable and shapes the charter as much as a success.
## After the spikes — the ratification
Fable amends `V6_LIVING_STREET.md` DRAFT → ratified (or re-scoped) on the spike reports, and
the v6 rounds proper get chartered: clips at scale · personas · street discipline (footpaths +
occasional cars) · bodies. **A, B, C rest this round** (A: nothing touches a plan; B/C: your
R28 kills closed your queues) — you're first up in v6 proper, where street discipline (B: cars;
A: crossings data) and persona interiors (C) are the headline tracks.
## Laws
Unchanged, plus the R28 additions to the case law: **a kill with a measurement is a full
deliverable** (two more this round — the backlog never carries a zombie again) and **a
wishlist is not an inventory** (E measured the stash instead of trusting a four-epoch-old
list; briefs cite measured stock only).
## THE EXACT RUN ORDER
0. **Now: D + F (wave 0, Spike 1 closes).**
1. **John: film the vids + nudge ultra (the gate).**
2. **When clips flow: E → D → F (Spike 2), G/ultra → E → D (Spike 3, parallel).**
3. **F: the spike gate + `v6.0-spike` checkpoint tag.**
4. **Fable: charter ratification → v6 rounds proper.**
## Standing
- **John — the two unblockers above are the whole critical path.** Everything m3-side is one
small D+F wave and then waiting on footage of you leaning on a wall. The till-sync note from
the retro stands too (no POS sale recorded since 2026-07-01).
- **Backlog after R28's kills:** tram-stop tri diet · flag retirement (policy, not urgent) ·
Option B (your hold budget) · tier-2 auto-discovery. Thinner than it has ever been.

View File

@ -0,0 +1,120 @@
# PROCITY — Round 30 lane instructions (from Fable, integrator)
Date: 2026-07-17 · **V7 THE GAME is RATIFIED** ([docs/V7_THE_GAME.md](../V7_THE_GAME.md) —
read it first, it is the epoch's truth). Round 30 opens **v7.0-alpha: THE SAVE AND THE CRATE**
— wave 1 of the smallest set that makes one town play as a complete small game. *(Housekeeping:
the phantom R30 that two lanes were spun up for never had a brief — that was Fable's miss, now
fixed. If your session finds prior R30 work anywhere, it doesn't exist; this document is R30.)*
## The ledger (v7.0-alpha wave 1)
1. **THE SAVE CORE (F — the foundation everything consumes; publish the contract FIRST).**
- `web/js/world/save.js` (F-owned): versioned localStorage save implementing **the delta
law** — cash · owned items (each = `{townKey, godverseShopId|shopId, sku|slotId, pricePaid,
dayFound}` — the sku IS the identity, per the R25/26 fences) · current town · **day
number** · schema `procity-save/1`. Save on SLEEP (see #3) and on `beforeunload`;
export/import as JSON (John moves machines).
- **The published contract** (`window.PROCITY.game`): `{ day, cash, collection[], save(),
load(), sleep(), export(), import(json) }` — B builds UI on this, C reads it at the
counter. Publish the shape in LANE_F_NOTES §30 in your FIRST commit, implementation after.
- **Laws wired from birth:** `?classic=1` → NO save layer constructed, zero localStorage
touches (the covenant stays a pure v2 boot); `?game=0` opt-out; a corrupted/foreign save
→ rejected loudly, fresh start, the town NEVER breaks (delta law: world state is not in
the save, so it can't).
2. **THE SELL COUNTER CONTRACT (C — publish first, implement second).** Walk to a keeper's
counter with items → a sell card (mirror of the buy card): item, offer price, SELL.
**Offer = below the buy-side band, structurally** (the no-pump law — C picks the multiplier,
documents it in LANE_C_PUB §9). v7.0-alpha sells at any shop of the item's type (record
shop buys records); the fancy stuff (per-keeper taste, haggling) is beta+. C consumes
`PROCITY.game.collection` read-only; the debit/credit goes through the wallet exactly like
buying (the proven seam).
3. **SLEEP = SAVE = TOMORROW (F + B).** v7.0-alpha's day-advance: the HUD gains a SLEEP action
(B's surface; enabled any time for the alpha — home-base gating is beta) → `game.sleep()`:
save, `day+1`, **stock rotation** (the parody/mint pick streams re-seed as
`rng(citySeed, shopId, 'stock', day)` — F threads `day` into the existing stock seeding;
REAL stock never rotates, it's real), gig night = `day % 7` (plugs into the existing week
schedule), wake at dawn (segment 0).
4. **THE COLLECTION UI (B — after F's contract lands).** A collection panel in hud.js (your
selector/HUD patterns): cover thumb, title/artist, price paid, where + day found; count in
the standing HUD; the SLEEP button + day/cash readout. Money-shot-grade is beta; readable
and correct is alpha.
5. **THE GATES (F, last).**
- **Save/load determinism:** play a scripted session (buy 2, sell 1, sleep 2×) → export →
fresh boot → import → assert byte-equal game state AND the world untouched (fingerprint
unchanged — the delta law's falsifiable form).
- **The no-pump gate v0:** script buy-then-immediately-sell N times → assert monotonic cash
LOSS (the spread is the house's). The full adversarial playtest-bot is beta.
- **Classic purity:** `?classic=1` → zero localStorage reads/writes (measure, don't trust),
no HUD additions, fingerprint frozen.
- **Rotation determinism:** same seed, same day → byte-identical shop stock; day N ≠ day
N+1 (actually different, not just asserted different).
- `qa.sh --strict --matrix` green throughout. **No tag** — the alpha tags when it plays
(likely R31, after the loop closes end-to-end).
6. **A — the rotation boundary verify (small).** F threads `day` into stock seeding — A
verifies it rides RUNTIME streams only: plan generation never sees `day`, all goldens
frozen, selfcheck proves it. One note, one line. **D, E, G — rest** (E's guide-band bake
and G's GodBay open in beta; D's rumors likewise. If footage lands in the MOCAPGOD queue
meanwhile, v6's Spike 2 takes priority for E/D — the epochs interleave, Fable sequences).
## Laws
The charter's five (delta · no-pump · game-money · classic-pure · seeded-rotation) + everything
inherited. No goldens move this round (rotation is runtime; A verifies the boundary). The
vacuous-gate law applies to every new gate above — each names its subject and proves it touched
it.
## THE EXACT RUN ORDER
1. **Start together: F (save core — contract in first commit) + C (sell contract first,
implementation second).**
2. **When F's contract is published: B (collection UI + SLEEP surface).** F continues
(sleep/rotation threading) in parallel — disjoint files.
3. **When F + B + C are in: F gates (#5), A verifies the boundary (#6).**
4. Fable reviews; R31 charters the rest of the alpha (or its close, if it plays).
Waves: **[F, C] → [B, F-cont] → [F-gate, A]**. D/E/G rest unless footage arrives.
## WAVE-1/2 REVIEW NOTES (Fable, mid-round — the agents' reports are the record)
**RATIFIED — F's two amendments, both measured:** (1) gig night = **(day 1) % 7** — the
brief's literal `day % 7` flips a fresh boot (day 1) off night 0 and breaks the R13 cover/band
gates before the first sleep; the brief was wrong, the measurement wins. (2) **sleep saves
AFTER incrementing** — disk always carries the morning you woke into.
**ADJUDICATED — the cross-lane 6 lines (F edited C's `interiors.js`/`layout.js` for the
`stockDay` salt):** SANCTIONED, this instance — because all three conditions held: the owner
reviewed and ratified on the record BEFORE the commit ("rides my own sub-stream discipline,
absent ⇒ byte-identical"), the edit is marked and cited in the commit, and it is minimal.
**Without all three, a cross-lane edit gets reverted on sight.** The law stands: write the
ask, not the edit — C folds the seam into LANE_C_PUB at its next session; carried to the
epoch retro as a process note.
**WAVE-3 INPUTS (bind these into the gates session):** the rotation gate must hash **vertex
data, not transforms** (F measured its transform fingerprint vacuous over batched parody
stock); C's money-pump **negative control** (an item minted into the collection without a
successful debit must be impossible — assert `recordFind` only fires on `wallet.buy` truthy);
the **E-key sell routing** (§9.4 dig → sell → shelf) is F's held item and lands in the wave-3
session BEFORE the gates run against it.
## ROUND CLOSED (Fable, at the wave-3 review) — **v7.0-alpha wave 1: COMPLETE, ALL GREEN**
Ten commits, five lane-agent sessions, zero holds, zero cross-lane incidents beyond the one
adjudicated. The loop is real and gated: dig → buy → the crate remembers → counter → E → sell
(strictly at a loss) → sleep → tomorrow rotates the mint crates and never the real one → the
save survives the reboot and the UI reads it. `qa.sh --strict --matrix` **7/0/0, MATRIX 10
towns**, selfcheck 156,352, covenant frozen, the day provably cannot enter the world.
**Fed to R31 (the alpha close — charter it after John PLAYS):** the beta rulings queue
(scarcity-per-day pull records · guide bands · per-keeper taste · foreign-town display names
via E's index) · F's harness lesson #3 (the HUD hides in interiors by design) · the design
fact that re-buying a rotated sleeve is a strict money loser (correct, filed). **The alpha
tags when it plays — John's hands on the real URL are the acceptance environment.**
## Standing
- **John — nothing blocks this round.** The deploy to digalot.fyi/procity still waits on your
VPS-access pick (grant me the ssh rule / run the script yourself) — worth doing THIS round:
testing the save loop in a real browser on the real URL is exactly the alpha's acceptance
environment. And the MOCAPGOD queue still wants your footage whenever.
- **The drive question stands:** say "run it" and Fable spawns these lanes as subagents in the
wave order above, reviews between waves, and reports once at the end.

View File

@ -0,0 +1,53 @@
# PROCITY — Round 31 instructions (from Fable, integrator)
Date: 2026-07-18 · **R30 closed all green** (7/0/0, matrix 10 towns, the loop live and gated).
Round 31 is **the v7.0-alpha CLOSE**: play it, fix the friction, tag it. Charter stage check
([V7_THE_GAME.md](../V7_THE_GAME.md)): the alpha = persistence + collection + sell-to-keeper +
sleep/day + rotation, "ONE town plays as a complete (small) game" — every system landed in R30;
what remains is the PLAYING and the honesty about what it feels like.
## WAVE 0 — THE PLAYTEST (Fable, in person)
Fable boots the game in a real browser and plays the loop AS A PLAYER, not as a gate: arrive,
find a shop, dig, buy, check the crate, sell at a counter, sleep, verify tomorrow feels like
tomorrow, reboot, confirm the town remembered. Deliverable: **the friction list** — every
"why can't I", every confusing beat, every moment the game fails to explain itself — split
into (a) alpha-blocking friction (bugs/confusion that would embarrass the tag) and (b) beta
input (design wants, John's ruling queue). Honest numbers where they apply. *John's own
playtest remains the beta charter's real input — Fable's hands are the alpha's acceptance
stand-in, and the tag notes it.*
## WAVE 1 — micro-fixes (only if wave 0 surfaces alpha-blockers)
Assigned to owning lanes as tightly-scoped agents, same treaty. Nothing speculative — only
what the playtest actually hit.
## WAVE 2 — the tag (F)
Money shot: the crate panel with real finds (the collection IS the alpha's face). Fresh
`qa.sh --strict --matrix` on the final tree. Tag **`v7.0-alpha`** (annotated, the playtest
verdict in the message), push main + tag. The README gains one line in the manual: sell at
the counter with E, sleep to save.
## ROUND CLOSED (Fable) — **v7.0-alpha TAGGED AND PUSHED** (`2491f1c`)
Wave 0: Fable played the loop end to end — dig $10 → sell $5 (the spread felt) → keep a $12
NM soul record → THE CRATE → full reload → **the town remembered** → SLEEP → day 2. Verdict:
**the loop plays.** The playtest found the alpha-blocker every green gate had missed: **the
dig was default-OFF on a plain boot** (v2-era opt-in, six epochs past the README's claim; every
R30 gate booted `dig=1` explicitly — harness lesson #4, THE EXPLICIT-FLAG BLINDFOLD: a gate
that boots its own flags cannot see a broken default). Waves 1+2: the flip (classic-safe,
fenced with a naked-boot reachability assert + negative control), the de-staled splash, the
money shot (Monster Robot's real crate, 3 real finds), qa 7/0/0 + MATRIX 10 towns on
port-isolated servers (a second fix the round forced: the suite no longer silently reuses
whatever holds :8130). **The beta queue is filed in LANE_F_NOTES §31.6** — spawn-faces-the-
strip, crate-at-the-counter, dawn sky, THE BIBLE, per-day scarcity. The beta charters on
John's own playtest whenever he walks the strip.
## Standing
- **John — the alpha's real acceptance is you:** play it (locally now, or grant the VPS ssh
and Fable ships digalot.fyi/procity same hour). Your friction list charters the beta. The
queued beta rulings await: per-day scarcity (pull records in the save) · guide bands (the
BIBLE) · keeper taste · foreign-town names via E's index.
- v6 footage: the MOCAPGOD queue is still empty; Spike 2 wakes whenever you film.

View File

@ -0,0 +1,61 @@
# PROCITY — Round 32 instructions (from Fable, integrator)
Date: 2026-07-20 · **R31 closed** — v7.0-alpha tagged and pushed; the loop plays. Round 32 opens
**v7.0-beta — THE THRILL, wave 1**: the guide bands land ([V7_THE_GAME.md](../V7_THE_GAME.md)
stage 2), scarcity rides the save, and the alpha playtest's friction queue (LANE_F_NOTES §31.6)
gets its cosmetic items. **Solo round:** John asked for direct progress; Fable executes all lanes
in one session on JING5, same treaty, same gates — lane ownership is honoured in the commit
messages, not the parallelism.
## The design rulings this round encodes (from the ratified charter + §31.6 queue)
1. **THE BIBLE derives, it is not baked.** E's band thresholds (build_stock_pack.py:88-94 —
record 8/25/60 · book 5/15/40 · toy 6/20/50) become a tiny JS mirror
(`js/interiors/bible.js`). No pack re-emit, so risk #3 (re-baked atlases orphaning owned
skus) never opens. The guide shows a BAND ("guide $2559"), never a verdict — spotting the
gem stays the player's skill.
2. **Gems live in the parody bins.** Real/mint packs derive `price_band` FROM `price`, so real
asking is always inside its band → with the §9.1 basis swap (sellOffer basis = bandLow),
every real round trip stays strictly negative and **the R30 no-pump gate holds untouched,
by construction**. Procedural stock gains seeded bands + a seeded under-band gem roll —
the only place asking can sit under the guide, which is exactly the op-shop fantasy.
3. **Scarcity is what makes the gem loop safe** (§31.6 item 6, ruled WITH the bands as filed):
a bought dig slot enters `pulls` in the save (optional field, procity-save/1 — old saves
stay valid, old loaders drop the field without rejecting) and that bin omits it after the
draw (the proven `pack.gone` pick-then-omit seam — zero collateral). A gem is profit ONCE;
tomorrow's rotation re-keys the bin. Growth is bounded by finds, not arbitrage (charter
law #2's real meaning).
4. **§31.6 items 13:** spawn becomes a computed cluster-pose facing the shop strip (classic
keeps the frozen v2 pose); THE CRATE surfaces inside interiors (C key — the §31.6
"crate at the counter" want, hidden while dig/sell cards own the screen); DAWN gets a
cool sky dome swap (classic + weather-override fenced).
## Gate additions (F)
`smoke_bible` in flags_check.py: guide stamp on the pull card (real + parody) · offer < bandLow
structurally · a seeded parody gem exists and its round trip is positive ONCE (then the slot is
gone — re-open the bin and it stays gone; reload and it stays gone) · negative control:
`?game=0` re-opens the same bin with the slot PRESENT (the detector discriminates — scarcity is
the save's doing, not the dig's). Classic purity untouched (no new module constructs under
`?classic=1` / `?dig=0`).
## ROUND CLOSED (Fable) — ALL GREEN
`qa.sh --strict --matrix`: **7 passed · 0 failed · 0 warn · 0 skipped**, MATRIX 10 towns green.
`smoke_bible`: 80 parody offers across 5 bins all band-lawful; bin1 holds 2 seeded gems (grail
asking $19 vs keeper $30); the buy-everything ledger closes to the cent (Σask $333 → Σoffer
$135, gems alone +$21, every non-gem strictly negative); the emptied bin stays empty across a
full reload (16 pulls in the save) and `?game=0` re-opens it 16/16 (the control discriminates);
0 console errors. sell_routing now asserts the exact §9.1 formula and passed on a BANDED item
(standard, basis $8 → +$4). Money shots: `docs/shots/v7_beta/guide_card.png` (the guide stamp —
"1974 COUNTRY guide $824" beside the $24 sticker) + `crate_at_the_counter.png` (THE CRATE open
inside a shop, game bar riding bottom-right). Environment note: JING5's gitignored
`web/assets/models/` was absent (fresh-checkout trap, now §32.3 in LANE_F_NOTES) — synced from
m3ultra, which also un-warned the manifest validator's 15 cross-pack checks. Full record:
LANE_F_NOTES §32. Shipped: bible.js + guide stamps + parody bands/gems (C) · pulls on the save
+ gone-wiring + digOffers surface (F) · crate-in-interiors + cluster-pose spawn + dawn sky (B)
· smoke_bible + the routing amendment (F) · README/docs.
**The beta's remaining charter** (next rounds): the WANTLIST + seeded rumors (D's keepers
finally gossip) · travel costs a day (23 towns become the map) · §31.6-5 (dig Esc under real
hands) · then v7.0 = GodBay offline auctions (G) + onboarding + the tour.

View File

@ -0,0 +1,52 @@
# PROCITY — Round 33 instructions (from Fable, integrator)
Date: 2026-07-20 · **R32 closed all green** (7/0/0, matrix 10 towns — THE BIBLE + scarcity live).
Round 33 is **v7.0-beta wave 2 — THE HUNT**: the wantlist, the rumor, and travel that costs a
day ([V7_THE_GAME.md](../V7_THE_GAME.md) system #6, the last unbuilt beta system). Solo round
again (John's "keep going"), same treaty, lane ownership honoured in the commits.
## The design rulings this round encodes
1. **A want is something you SAW.** The wantlist grows from the pull card (☆ WANT IT — you
pulled it, it's dear, you'll hunt one cheaper) — not from an abstract search box the 90s
didn't have. Buying a matching item clears its want automatically (the hunt ends when
you're holding it). Wants ride the save (`wants: [{artist?|title?|sku?}]`, optional field,
same procity-save/1 back-compat law as `pulls`).
2. **The keeper finally gossips** (charter: "D's citizens finally gossip for a reason").
Seeded per (citySeed, shop, day): ~45% of shop entries mutter a hunted want + a real cached
town in the banner — flavor and a travel nudge, honest about being neither promise nor
stock lookup (town-wide packs mean any town CAN surface the record eventually). Runtime
stream, freshly keyed — no plan/golden moves. Full persona lines remain D's when the v6
clip work lands; the banner is the keeper's voice until then.
3. **Travel = the townKey changing hands.** Booting a save in a different town (different
plansrc/town/**seed** — a new seed is a new town) costs exactly one day, applied in
save.js `adopt()` so load and import both pay it, and pulls prune on the way. Same-town
reloads and export→import round trips stay free — the save-determinism gate's byte
equality survives untouched. The town selector IS the bus; the arrival toast names the
fare. The 23 towns are now a map you spend days moving across, not a dropdown.
## Gate (F)
`smoke_wantlist_travel` (STRICT): ☆→★ through the real card → the save carries the want ·
a keeper names the hunted artist within the open shops (seeded scan) · buying the wanted
sleeve auto-clears it · synthetic→melbourne costs exactly one day with cash/collection intact
· same-town reboot is FREE (the control) · the ride home costs a day again · `?game=0` builds
zero want DOM and mutters nothing.
## ROUND CLOSED (Fable) — ALL GREEN
`qa.sh --strict --matrix`: **7 passed · 0 failed · 0 warn · 0 skipped**, MATRIX 10 towns green
(the shared-context day drift travel introduces across matrix towns was reasoned through
before landing: determinism double-boots the SAME town, so no gate sees it). The new gate's
run: want carried {THE VERANDAHS — "Servo at Midnight"} · rumor after 2 shops ("🗣 THE
VERANDAHS? saw one in Castlemaine just last week") · buy cleared the want · travel day 1→2
with $179 and the find riding along · same-town free · return trip day 3 · game=0 clean ·
0 console errors. Full record: LANE_F_NOTES §33.
Also caught and fixed here: the R32 crate-in-shops surface only BUILT in street frames — a
door entered fast after boot had no game surface inside (the money-shot script entered faster
than any gate does and exposed it; tickGame() now builds-or-refreshes in any mode). Money
shots: `docs/shots/v7_beta/rumor_and_wantlist.png` + `want_card.png`.
**What remains of v7:** §31.6-5 (dig Esc in person — John's hands) · then the v7.0 close:
GodBay offline auctions (G's lane), THE FIRST FIVE MINUTES (onboarding), the tour, the tag.

View File

@ -0,0 +1,57 @@
# PROCITY — Round 34 instructions (from Fable, integrator)
Date: 2026-07-20 · **R33 closed all green** — every v7.0-beta system is built. Round 34 opens
**the v7.0 close: GODBAY, the offline auction** ([V7_THE_GAME.md](../V7_THE_GAME.md) system #4:
"list an item, a SEEDED offline auction resolves NEXT DAY — the come-back-tomorrow hook").
Offline tier FIRST per the proven ladder (G3's live rails are v7.x; this round is the seeded
brain in the game layer, G's later live tier must match these laws). Solo round (John's "keep
rolling").
## The laws (GODBAY v0 — named now, gated below)
1. **Mail-in consignment.** You list from THE CRATE (⚖ LIST on a find) — the 90s answer
(classifieds + the post). The entry leaves the collection into `listings` (you cannot also
sell it at a counter), `listDay` stamped. Resolution happens when the day moves past
`listDay` — sleep, travel, or a day-moving import all count (adopt() and sleep() both run
the resolver).
2. **THE ONE-HAMMER LAW (no retry farming).** The hammer is seeded by ITEM IDENTITY —
`godbay:<townKey>:<sku|slotId>:<dayFound>:<pricePaid>` — NOT by the resolution day. A
passed-in item re-lists to the SAME result, forever. There is no re-roll anywhere in the
economy: the keeper's offer is a formula, the auction is one hammer per item, per life.
3. **The house always eats.** Hammer rolls uniform across the band (grail's open top = 2×low);
~12% chance of a bidding war (×1.52.0); ~15% passed in (returns to the crate, free);
12% commission, net floor $1. Expected value ≈ 0.8× band midpoint — UNDER the asking-price
EV of in-band stock, so random flipping bleeds and only FINDS profit (charter law #2's
spirit at the auction house: growth bounded by finds, the keeper's instant half-of-low
remains the coward's floor and the auction the gambler's ceiling — bounded at 2×high×0.88).
4. **Delta law / classic purity unchanged.** `listings` is an optional procity-save/1 field
(same back-compat law as pulls/wants); no game layer ⇒ no listings, no DOM, no news.
5. **The morning report.** Resolution queues transient news ("⚖ SOLD 'Sunburnt' — hammer $38,
$33 after fees"); the shell folds it into the wake/arrival toast. News is NOT saved — it's
the morning paper, not the ledger (cash/collection already carry the truth).
## Gate (F): `smoke_godbay` (STRICT)
List through the real crate DOM (C → ⚖ LIST) → entry moves collection→listings, the save
carries it · sleep → resolved: listings empty, cash+net OR item returned, news queued, net
within [1, 2.2×bandHigh] when sold · DETERMINISM: a pre-sleep export imported twice sleeps to
byte-identical cash/collection both times (the seeded hammer, measured not trusted) · travel
resolves too (list → ride to melbourne → resolved on arrival) · `?game=0` builds zero ⚖ DOM ·
0 console errors.
## ROUND CLOSED (Fable) — ALL GREEN
`qa.sh --strict --matrix` on the final tree: **7 passed · 0 failed · 0 warn · 0 skipped**,
MATRIX 10 towns green. `smoke_godbay`: consigned through the real crate DOM (collection 2→1,
listDay stamped) · ONE-HAMMER determinism (a frozen pre-sleep export slept twice → byte-
identical) · honest bounded resolution (SOLD net $19 on a standard record, ≤ 2×$24×0.88) ·
the wake toast carried "⚖ SOLD 'Cane Toad Blues' — hammer $22, $19 after fees" · the ride to
melbourne hammered a listing on arrival · `?game=0` has no crate to list from · 0 console
errors. Design note vs the brief: the band table is IMPORTED from C's bible.js and the PRNG
from core/prng.js (one authority each — the drafted mirror died in review). Money shots:
`docs/shots/v7_beta/godbay_morning_paper.png` (dawn day 2, the paper in the toast, the crate's
⚖ row) + `godbay_at_auction.png`. Full record: LANE_F_NOTES §34.
**What remains of v7.0:** THE FIRST FIVE MINUTES (onboarding — arrive with a want, three
toast-beats) · the tour ("day one to first gem") · docs freeze · the tag. Then John's playtest
charters what's next.

View File

@ -0,0 +1,36 @@
# PROCITY — Round 35 instructions (from Fable, integrator)
Date: 2026-07-20 · **R34 closed all green** (GODBAY hammers). Round 35 is **THE FIRST FIVE
MINUTES** ([V7_THE_GAME.md](../V7_THE_GAME.md) system #7 — "smallest system, biggest
stranger-facing payoff"), the last unbuilt v7.0 system before the tour/freeze/tag.
## What ships
1. **You arrive with a want.** A FRESH game (day 1, empty crate, empty auction house) seeds ONE
artist want off `rng(citySeed,'firstwant',0)` × C's parody roster (ARTISTS now exported) —
so the splash, the keeper rumors, and the tour all point at the same hunt on a given seed.
The splash names it: "You're hunting HILLS HOIST — $191 in your pocket." A returning save
gets its town back, not a tutorial; ?game=0 / classic never reach the block.
2. **Three toast-beats walk the hustle** (fresh games only): ① find a record shop / E at a
crate (on walk-in) → ② riffle · mind the guide · BUY (first digOpen) → ③ sell at a counter
or ⚖ LIST and sleep (first find banked) → the send-off ("that's the whole hustle") on the
first sale-or-listing. Driven off existing events + wallet.onChange — zero new systems.
3. **Toasts surface in every mode**#pc-toast moved to document.body (the #pc-game body-mount
rule): beats fire INSIDE shops, and a SLEEP taken at a counter now shows its morning paper.
## Gate (F): `smoke_first_five` (STRICT)
Fresh game: 1 seeded want from the roster + the splash names hunt AND cash · beats ②/③/send-off
through the REAL paths (E at crate / dig buy / crate ⚖) · a returning save gets NO tutorial
(splash line hidden, no ② on dig) · a second fresh browser hunts the SAME artist (seeded, not
random) · ?game=0 shows nothing · 0 console errors.
## ROUND CLOSED (Fable) — ALL GREEN
`qa.sh --strict --matrix`: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10 towns green
(final tree). Gate run: hunts HILLS HOIST with $191, beats walked through the real dig/buy/list
paths, returning-save and ?game=0 controls clean, second fresh browser hunts the same artist,
0 console errors. Money shot: `docs/shots/v7_beta/first_five_splash.png`.
**v7.0 remaining:** the tour ("day one to first gem") · docs freeze · the tag — and the tag
waits for John's playtest (the alpha precedent: hands before tags).

122
docs/RETRO_V5.md Normal file
View File

@ -0,0 +1,122 @@
# PROCITY v5 — THE REAL SHOP: the epoch retro
*Fable (m3, integrator), 2026-07-17, written after the `v5.0` tag (`b2d29ba`) per ROUND27 §7.
Epoch span: R23 → R27. Tags: `v5.0-alpha``v5.0-beta``v5.0`. The first two-studio epoch:
Lane G (ultra) went from merged guest to the lane that shipped the epoch's server.*
## What shipped, in one breath
The tier ladder, whole: **344 real-or-minted items across 15 keyed crates in two godverse
towns**, every crate distinct (105 pairs, 0 shared titles), sourcing-honest (`real`/`mint` as
law, prefixes as namespace fences); Monster Robot Party's actual crate 550 riffled and bought
in-game off its actual POS; **tier 2 live** — read-only endpoints over the real till, fences
enforced at the DB credential, `/reserve`+`/buy` absent by John's A-then-B ruling; and the
epoch's defining proof: **a real HTTP server killed mid-session costs the game Δ+2 ms and
exactly one refused connection** — the offline law as an executable, not a sentence. Plus the
finale nobody ordered: **every shopfront in every town finally faces its street.**
## The five holds — the epoch's spine
Every hold was correct; every hold traced to the same species (a rule or contract meeting a
case written before the case existed); every hold was released by its owner, never by the
beneficiary:
1. **R23** — the crate was real but had no street, no identity, no compatible contract; the
gate built to catch it was blind-green, and #7a as briefed would have passed vacuously.
2. **R24→25** — E's rebuild sat uncommitted; tagging would have produced a tag whose tree
lacked the code that produced its green.
3. **R26** — the mint-id rule demanded a POS sku from crates that never saw a till; G refused
the easy green because colliding id spaces in the economy layer eventually sells the wrong
record.
4. **R27 (reader)** — "zero console errors on server death" was physically impossible; the
replacement (exactly one refusal) is strictly stronger because it proves the breaker.
5. **R27 (facade)** — the fifth hold found the oldest bug in the project.
## The finale: the facade, and the covenant amendment
B's frontage helper — a micro-task — exposed that `plan_osm` aimed lots' Z at the street
while `buildings.js` draws facades on +Z. The wave-5 ruling named it "every real town since
R18" and ordered A to *measure the marched path rather than assume it clean*. **A's measurement
caught the ruling's own premise: it was EVERY town, EVERY path, since v1** — all four seating
sites wrote `atan2(outward)` under comments that said `-outward`; 637 of 681 synthetic lots
faced no street at all; the classic covenant town was a row of back walls, and A proved it the
only way left — standing in the street, looking at NUMBAT PLAYTHINGS facing the dirt.
**John ruled the covenant amendment**, and his ruling is the epoch's best line: *the covenant
is anti-drift machinery, not a defect preservative.* `0x3fa36874` did its job for five epochs —
it never drifted. It was never right, and no hash can catch what was wrong in the first commit.
**A golden proves nothing changed; it cannot prove anything is correct.** 51 goldens re-pinned
in one commit; F re-pinned its own files per A's handoff (lane boundaries held to the last);
and A caught, in passing, a bare-string OR-clause in F's classic gate that would have gone
green off a *sentence about* the old covenant. The cross-convention gate is now in the
selfcheck so the two conventions are compared against each other forever.
## The epoch's signature species: the vacuous gate
Named as law in R24, then found NINE times — including three inside the law's own
applications: the empty-glob validator, the covers-render #7a, my id-equality gate spec
(positional ids collide), F's selector entering the wrong shop, the silent licence line, the
impossible zero-errors assert, F's 72/72 self-agreement (testing a spec line's data against
the same line), F's attempt-4 error-check (the call succeeded; the pose was never applied),
and the stdout OR-clause. The case law that came out of it is the epoch's real export:
- **A gate proves it touched its subject** (fetch log, matched-file count, id equality —
something falsifiable) or it is not a gate.
- **The collateral assertion**: when correct and broken implementations both hide the obvious
evidence, assert on what discriminates (the other 119 records unchanged).
- **Cross-convention checks**: two subsystems implementing "the same" convention are compared
against each other, never each against its own spec line.
- **Control-deltas for performance**: an absolute threshold measures the wrong thing politely.
- **A named exception starts protecting new bugs the moment the real fix exists** (F's 404
tolerance — deleted, and the deletion caught a ninth invisible probe).
- **Id prefixes are namespace fences**; plan ids, godverse ids, POS skus and listing ids are
four spaces, and no bare number crosses them.
## What worked — keep doing
1. **Restraint under benefit.** F declined to touch A's sweep, E's validator, and E's
uncommitted file across three separate holds where one edit would have unblocked its own
tag; B proved the facade one-liner and reverted it; A handed F's re-pins to F instead of
editing F's files. The party that benefits never releases its own hold.
2. **Ownership sentences.** "The red was mine" (E) · "my rule was the stale thing" (C) ·
"it fired 54/72 on a good town" (A) · "four of the five attempts were F's fault" (F).
Nobody defended; everybody measured.
3. **The contract's author is the authority for the contract's shape** — and when contract
and runtime disagree, **the runtime is the authority** (R24's root-cause ruling).
4. **Machine-enforced fences beat reviewed promises**: the read-only credential (measured
permission-denied), 501s for absent verbs, metadata-only responses.
5. **The two-studio pattern matured**: G went from branch-merged guest (R23) to full lane on
main (R25) to shipping the epoch's server (R27), with John's rulings recorded as mechanism.
## What cost us — fixed or fenced
1. **Comments and specs are not measurements.** All four facade sites' comments described the
correct behaviour above code doing the opposite, for 27 rounds. The cross-convention gate
is the fix; the lesson is older: nothing is true because a document says it.
2. **Consistency masquerades as correctness.** Every subsystem agreed with itself, so every
gate was green over a wrong world; only a human in the street saw it. Money shots and
walk-arounds are QA, not decoration — John's browser stroll started the v6 charter, and
A's street-level eyeball confirmed the covenant bug.
3. **My briefs carried five corrected errors this epoch** (the `tier` field, the id-equality
gate, the zero-errors assert, the "wire on entry" default that F correctly made opt-in,
and the wave-5 premise). All five were caught by the contract-first channel or by
measurement before they became bugs. Briefs state acceptance; lanes own method; the
integrator is not exempt from measurement-over-brief.
## Carried forward
- **v5.x**: Option B reservation (chartered, behind John's hold budget) · tier-2
auto-discovery (needs a reachability contract) · more POS shops if monsterrobot gains
siblings · **a shop-ops note for John, not a code item: the POS has recorded no sale since
2026-07-01 — G proved the dump is fresh but the data is stale; worth checking the till's
sync before the next live demo.**
- **v4.x tail** (unchanged): thin-tail clustering floor · seating overflow tail · instrument
LOD · tram-stop tri diet · awning ripple · audioEmitter · flag retirement.
- **v6 — THE LIVING STREET**: charter drafted (`docs/V6_LIVING_STREET.md`), spikes gated on
John's mocap vids (the MOCAPGOD queue is still empty) and the MOCAPGOD-C/MIRPAMO
integration call on ultra.
*— Fable. Twenty-seven rounds, eleven tags, five correct holds, one amended covenant. The game
now sells a record that is really in the crate, in a shop that is really on the street, in a
town that is really in Australia — and for the first time since v1, the shops are facing you
when you walk up the road.*

116
docs/THE_FRONTIER.md Normal file
View File

@ -0,0 +1,116 @@
# PROCITY — THE FRONTIER (what is genuinely not figured out)
*Fable (m3), 2026-07-17. John's challenge, verbatim: "there is heaps more to figure out — all
the systems are set you say?" He's right. This doc is the honest map: what 29 rounds built is
the WORLD and the PIPELine; the GAME — the reason a stranger would play for an hour — is
mostly unfigured. Nothing here is chartered; it's the space the next epochs get picked from.
Sibling docs: V2_IDEAS (the old parking lot), V6_LIVING_STREET (chartered, in flight).*
## What is actually solid (don't re-litigate)
Procedural + real towns · enterable interiors · citizens with identity · gigs · weather/tram ·
the dig with real/mint/parody stock · the tier ladder + offline law · the two-studio pipeline ·
the QA culture. The platform is genuinely done-ish. Everything below stands ON it.
## THE BIG ONE — it isn't a game yet
You walk, you dig, you buy. Then you close the tab and it never happened. The whole loop that
makes digging MEAN something is unbuilt:
1. **Persistence.** No saves. The wallet resets, the collection vanishes, "gone this session"
is the only memory. (localStorage fits the seeded-everything law trivially — the world
regenerates from seed; only the DELTA needs saving: cash, owned items, day. This is days of
work, not an epoch, and it unlocks everything below.)
2. **The collection.** You buy records and they go... nowhere. No crate at home, no album/
shelf UI, no "look what I found in Katoomba." The collection IS the trophy case of the
whole game and it does not exist.
3. **The thrill loop.** thriftgod's `mint()` gem bands exist as DATA (price bands, gems,
CBD-distance curation) and drive NOTHING. No underpriced gem in a dusty crate, no "is this
worth more than they're asking", no sell/flip loop, no wantlist hunting across towns. The
entire reason op-shopping is exciting in real life is un-simulated.
4. **Why today matters.** No day structure — no opening hours pressure (hours exist! nothing
makes you race them), no end-of-day, no "the gig starts at 9, the shops shut at 5, choose."
The clock is scenery.
5. **The player.** No hands, no home, no inventory UI beyond a number, no character. Who are
you in this town?
6. **Onboarding.** A stranger at digalot.fyi/procity gets pointer-lock and silence. The first
five minutes are unfigured entirely.
## Chartered but unbuilt (in flight or waiting)
- **v6 LIVING STREET** — spikes gated on footage; then clips-at-scale, personas, footpaths +
occasional cars, generated bodies.
- **Option B** — real reservation holds (John's hold budget).
- **Tier-2 auto-discovery** — needs a reachability contract.
- **The editor/override layer** (V2_IDEAS, still the best long-game idea: hand-author any shop
interior over the procedural base).
- **Multiplayer presence** (see who's digging the same town).
## Unowned design questions (nobody has even sketched these)
- **What is a session?** 20 minutes? A day-cycle? A town per sitting?
- **The economy loop:** buy low → ??? → profit. Sell where? To whom? GodBay (thriftgod's
auction sim) is sitting right there as the obvious answer and no design connects them.
- **Cross-town play:** 23 towns exist; nothing makes you travel. Regional wantlists? A gem
rumor system? The tram/train BETWEEN towns?
- **The gig layer as gameplay:** you can attend a gig; you can't book one, play one, or care
about one. 90sDJsim overlap sits unexplored (DJ the venue?).
- **Music identity:** procedural beds exist; a game this specific deserves a sound.
- **The 90s-ness:** it's in the fabric (video shops, milk bars) but no system USES the era —
no phone-free navigation puzzles, no street-press gig guide, no mixtapes.
## THE THIRD DIMENSION + TEXTURE LIFE (filed 2026-07-18 — John's Perlin question, answered from the code)
**The ruling that frames this section:** world *structure* stays on the semantic hash-stream
system (`prng.js` — `seedFor(citySeed, kind, id)`, independently-keyed streams per entity).
That architecture is why 31 rounds of features never moved a golden: a draw added to one
stream shifts no other. Perlin answers a different question — a *continuous* value over
space/time — and it enters the toolbox only where the question really is continuous. When it
does, it seeds from the same citySeed keying: deterministic, gate-able, no exceptions.
Candidates, ranked by honest cost:
1. **TERRAIN / ELEVATION — the headline, epoch-scale, chartered as a v8+ candidate.**
Everything is flat, which is *wrong for the real map*: Katoomba is a mountain town rendered
as a billiard table. Per the real-map DNA, the primary source is REAL elevation (SRTM /
contour data for each town bbox, the same discipline as the roads fetch) — noise is only
the infill between samples, never the shape of a real hill. **The cost is named now so
nobody charters it lightly:** terrain touches everything — roads follow grades, buildings
need foundations and steps, footpaths slope, the chunk streamer and the walkers and every
budget meet a Y axis, and the classic covenant needs a flat-forever fence. This is an
epoch with an alpha/beta/close arc of its own, not a round.
2. **Sky / cloud domes — cheap, near-term (v6/v7 polish).** Skies are baked textures today; a
seeded FBM dome gives every day its own weather-shaped sky, composes with the existing
weather states, and is one lane's item (B, lighting).
3. **Wear & grime — cheap, high-charm (E/C canvas painters).** Seeded value-noise for sleeve
ring-wear, facade sun-fade, footpath staining — per-item, deterministic, and exactly the
90s patina the parody sleeves deserve. Pairs with the beta's price-guide work (a worn
sleeve *reading* worn next to its condition grade).
4. **Crowd ebb (LIVING STREET track, D).** A slow 1D noise curve modulating street density
across the day — mornings trickle, arvo swells — richer than uniform draws, one stream,
identity-safe.
5. **THE VERNACULAR KIT (filed 2026-07-18, John's idea, source corrected) — buildings that
feel real.** The town's massing is same-y boxes; the fix is **1520 parametric Aussie
vernacular archetypes** (Queenslander, Federation cottage, terrace row, weatherboard
shopfront + verandah, deco corner pub, brick milk bar, fibro shack, tin warehouse…) with
seeded knobs — width, storeys, verandah depth, roof pitch, parapet, tint, and the wear
noise from item #3 above. **Source ruling: NOT Google Maps 3D** (licence-fatal — the one
thing the provenance spine must never touch — and photogrammetry mush at house scale
anyway). Instead: (a) **OSM/Overture building footprints + heights** for the real towns —
ODbL, the licence and pipeline we already ship, one more fetch class under the standing
authorization; (b) the kit designed from Street View *reference* (looking is not
extracting) and built parametrically on B's existing instanced-attribute machinery, which
already varies tint/hours per instance. Real footprints place real shapes on real streets;
the kit + knobs cover everything unfootprinted. Pairs with terrain as the two halves of a
future "the streetscape gets real" epoch — but unlike terrain, the kit is INCREMENTAL
(archetype by archetype, budget-measured, classic fenced), so it could also drip in as
B/E polish rounds. Goldens untouched either way (plan hashes don't see meshes); budgets
re-measured per archetype.
## The honest sequencing opinion (Fable's, not a ruling)
The private-test deploy is the design instrument: John walks the game cold and writes down
every "why can't I..." — that list IS the v7 charter input. My candidate for v7 once v6's
spikes land: **THE GAME (persistence + collection + the thrill loop + a day that matters)**
it's the smallest set that turns the platform into something a stranger plays twice.

View File

@ -81,3 +81,52 @@ plus this epoch's own:
5. **Settled non-risks:** the dig/wallet/stockAdapter machinery (shipped v2, soaked 16 rounds);
static-atlas determinism (E's byte-identical pipeline discipline); the fail-soft ladder
(proven pattern from `?noassets` day one).
### Retired-or-carried at the v5.0 close (ROUND27 ledger #6 — Lane A, line by line)
Verdicts against what shipped, not against the plan. Written in **wave 2**, so tier-2 items that F's reader
and kill-the-server gate will decide are **carried, not pre-retired** — the gate retires them, not this doc.
1. **CARRIED — and it is this round's defining gate, not a footnote.** The only risk here the epoch has not
yet measured. The reader (#2) and the kill-the-server gate (#3) don't exist as I write this; **nothing
retires this line except that gate going green** — boot server-up (live fields consumed), kill mid-session
(degrade to tier 1, zero console errors, frame-time measured across the failure), boot server-down (pure
tier 1). Until then the charter's offline law is a promise, and the whole point of #3 is that a promise
isn't a gate. *(If tier 2 proves bigger than one round, F holds and this line simply carries — four holds
have proven nobody forces an epoch close.)*
2. **RETIRED — the remedy the risk named is the remedy that shipped.** It predicted "pagination/crate
rotation rather than bigger atlases", and that is exactly what happened: the mecca case is served by the
**crate-rotation menu**, not by growing the sheet. Measured across all **15 keyed atlases**: max **2
atlases/shop** against C's hard cap of 2; the 14 mint shops sit at **16 items / 1 atlas / 1024²** and only
the one real POS shop earns 2048² (**120 items, 2 atlases, 1.2 MB**) under C §7.2's 60+ item rule. E's
validator enforces the ceilings from the committed files alone. Total: 344 items.
3. **RETIRED — the risk was designed out, not solved.** It assumed a **fuzzy-matching** problem ("names
drift, shops move") and demanded mismatches be counted. There is no matcher: identity is an **explicit
opaque key** (`godverseShopId`) that G emits, so there is nothing to mis-match. The predicted failure
couldn't occur in the predicted form — and the case that would have broken any name-matcher proves it:
**Monster Robot Party is in neither the census nor OSM**, so no matching strategy could ever have found
it; it is keyed by injection. Mis-stocking is fenced structurally instead: duplicate ids **error**,
uniqueness runs over defined ids, and the **orphaned-atlas gate** fails if a stocked shop isn't seated.
4. **SPLIT — the write half RETIRED by ruling, the read half CARRIED to F's #4.** *Write-back:* retired, and
retired properly — John's **A-then-B** ruling is recorded as **mechanism, not config**: `/reserve` and
`/buy` are **ABSENT, not stubbed**. Race conditions and reservation semantics cannot occur because the
verbs do not exist; "prove the absence" is a stronger guarantee than any sandbox flag, since a sandbox a
config typo can flip isn't a sandbox. *(Option B's reservation upgrade is chartered as v5.x, behind a
hold budget — it re-opens this line deliberately, when it is built.)* *Read (`gone`, real→game):* carried
— F's #4 asserts both directions this round.
5. **Unchanged, and extended by what the epoch settled:** static-atlas determinism proved in practice
(byte-identical re-bakes, including G's index-only sku re-emit that moved no `.webp` bytes); the
fail-soft ladder proved on every rung; and the **id-space fences** (`sku_`/`mint_`, plan-id vs
godverse-id) now have case law behind them rather than good intentions.
6. **NEW, CARRIED — the orphaned atlas.** The lift legitimately drops shops (overlap-resolve, counted since
R19); dropping one that carries a **committed atlas** means real stock keyed to a shop that isn't in the
town. Gated in `selfcheck` since R25 and **currently green** — and the gate armed itself this round
without anyone touching it: it SKIPped on `newtown_godverse` while no atlas keyed there, and **began
binding the moment G's mint atlases landed**. Live near-miss on the books: `redhill_godverse`'s lift drops
keyed shop **2286 "Empire Revival"**; G stocked its numeric neighbour **2287** instead. Carried because
the hazard grows with every shop stocked — **stocking 2286 would orphan it today.**
7. **NEW, CARRIED — "real" is one shop wide.** 14 of 15 keyed atlases are **mint**; exactly one is a POS
snapshot, because exactly one shop has a POS. That is honest and marked (`sourcing`), not a defect — but
the epoch's headline claim rests on **one** shop, and any future claim of scale needs either more POS
shops (v5.x: "if monsterrobot ever gets siblings") or continued care that `sourcing` stays legible to
humans, not just gates.

98
docs/V6_LIVING_STREET.md Normal file
View File

@ -0,0 +1,98 @@
# PROCITY v6 — THE LIVING STREET (epoch charter — DRAFT for John)
*Fable (m3), 2026-07-17. John's idea, from a browser walk down the strip: the crowd is
believable but narrow — one walk, one idle, one sit, everyone jaywalks, nobody owns the street.
Status: DRAFT — v5.0 (THE LIVE CRATE) is still closing; this charters the v6 conversation the
retro was already going to open. Applies to PROCITY first and 90sDJsim by depot (the Lane-G
two-studio pattern, proven).*
## The vision
The street stops being 24 clones who walk anywhere and becomes a town of *people*: personas
with their own gaits, errands and manners; footpaths used like footpaths; the odd car easing
down the main street; a busker outside the record shop doing a move John actually performed in
his actual shop. **Every asset generated, rigged, animated and shipped on our own machines —
zero cloud, zero subscriptions, everything owned.**
## The stack (measured today, not aspirational)
| piece | state | what it gives us |
|---|---|---|
| **mixamo-fetch** (tailnet source box) | **34 clips cached, only 3 wired** (walk/idle/sit) | the free stash, already in hand — lean, look-around, phone, talk, carry… |
| **MOCAPGOD** (ultra) | Lanes 0/A/B **DONE**: phone video → HSMR/SKEL → BVH, MPS-verified, ~1.6× realtime, mixamorig name-map shipped | film John → skeleton motion. Lanes C (retarget) + D (clip factory) remain |
| **MIRPAMO** (both Macs) | repo live: **a local offline Mixamo** — autorig (landmark → mixamorig fit → bone-heat weights) + retarget → `character@clip.glb` | new rigged BODIES from any mesh + clip retargeting, no Adobe |
| **MODELBEAST** (m3ultra :8777) | flux_local (Apache, free) · **hunyuan3d_mlx local** · trellis_mac · sf3d · mflux_image_edit · seedvr2 | concept art → 3D meshes → texture/outfit variants, $0 |
| **3GOD depot** | live, no-Draco law, two-studio consumption proven (Lane G) | one publish, every game (PROCITY · 90sDJsim · festifun) consumes |
| **PROCITY runtime** | ped fleet (19 rigged, `_canon` mixamorig), sit.glb clip pattern proven (R16), persona hooks half-exist (loiter/dance/bench/patronage) | the body the assets walk into |
| **A's street exports** | `roadWidth/vergeBand/poleOffset` per edge — **shipped R15, barely consumed** | the footpath law is one D constraint away, not a new system |
**MOCAPGOD Lane C and MIRPAMO's retarget are the same machinery** — an integration decision
for Fable-ultra before either builds it twice.
## THE SPIKES (do these FIRST — cheap, each kills the biggest unknown in one thread)
The pipeline test John asked for, as three end-to-end spikes. No epoch work until these run.
1. **SPIKE 1 — the free stash (motion, zero generation).** ~~Wire 2 of the 34 cached clips
(`lean`, `look-around`)~~ **AMENDED R28: `lean` does not exist in the stash** — E measured
the 34 instead of trusting the wishlist (the brief's premise was half wrong). `look.glb` is
shipped + depot-verified (R28); D's wiring closes in R29 wave 0. The lean moves to Spike
2's shot list — John films a wall-lean. *Proves:* the clip bank expands cheaply, and
wishlists are not inventories. Risk: ~zero (confirmed).
2. **SPIKE 2 — the full mocap loop (the one that matters).** John films ONE move on a phone —
**the crate-riffle, at Monster Robot** (the exact clip D's notes flagged "bespoke, won't be
on Mixamo" two epochs ago) → MOCAPGOD ingest → BVH → retarget (MOCAPGOD-C or MIRPAMO —
the integration call) → clip GLB → depot → **a ped riffles a crate on Musgrave Road,
in-game**. *Proves:* film-yourself-to-NPC works end to end. Needs: MOCAPGOD Lane C/D
sessions on ultra + John's test vids in the queue (it's empty — that's the ask).
3. **SPIKE 3 — a generated body (the stretch).** flux_local concept ("older shopper with
trolley, 90s AU high street") → hunyuan3d_mlx mesh → **MIRPAMO autorig** → walk retarget →
in-game past the no-giants gate. *Proves (or honestly fails):* autorig quality on generated
meshes — the least-proven link, which is exactly why it's a spike and not a plan line.
## The epoch tracks (after v5.0 tags + the retro + the spikes report)
1. **CLIPS** — wire the 34; fetch more free categories; the MOCAPGOD bespoke set (riffle,
till-operation, busker strum, lean-on-wall, trolley-push, phone-check). Licence rail: the
HSMR/SKEL NC-risk (MOCAPGOD Decision 1) means mocap-derived clips stay **in-house** until
reviewed — same discipline as the item photos, same flag line.
2. **PERSONAS (D)** — a seeded archetype layer per citizen: {body variant, clip set, speed
band, loiter/dance/bench biases, errand routes (pick a shop, walk there, dwell, move on —
patronage formalized into visible purpose)}. The persona is DATA riding the existing sim;
determinism per citizen id; identity signatures unchanged.
3. **STREET DISCIPLINE (D + B + A)** — the footpath law: peds walk the `vergeBand`, cross at
nodes (zebra-preferred on real roads — B's crossings already render); **occasional cars**:
seeded parked cars (instanced, furniture pattern) + 13 movers on the mains (the tram
pattern: deterministic path-followers, ~2 draws each, stated budgets). v0 non-interacting:
cars don't yield, peds don't jaywalk — both true by construction because each stays in its
band.
4. **BODIES (E + MIRPAMO)** — outfit/texture variety on the existing fleet first (
mflux_image_edit reskins — free variety, zero rig risk), MIRPAMO-autorigged newcomers
second, per Spike 3's verdict.
## Laws (inherited + this epoch's)
Everything behind **`?streetlife=1`** (cars possibly split as `?traffic=1` — F's call at
wiring), default-OFF until soaked, `?classic=1` byte-identical as ever · budgets stated per
system (cars and new rigs are draw/tri line items) · determinism from citySeed + citizen id ·
asset law (every new clip/body rides the manifest + depot + `?noassets` fail-soft) · licence
law (LICENSES.md per clip source: Mixamo royalty-free / MOCAPGOD in-house-NC-flagged /
generated CC0-equiv) · vacuous-gate law (a persona gate proves a persona DID something, not
that one exists) · measurement over brief.
## Sequencing (the honest order)
1. **Now:** v5.0 closes per ROUND27 — nothing here touches it.
2. **In parallel, zero collision (different repos):** MOCAPGOD Lanes C/D run on ultra ·
John drops 23 test vids in the MOCAPGOD queue · a MIRPAMO autorig smoke on one
hunyuan3d_mlx mesh.
3. **After the v5 retro:** the spikes run as v6 wave 1; this charter ratifies (or amends)
on their results; the tracks become rounds.
## John — the asks that unblock the spikes
1. **Film the test vids** (Spike 2 blocks on an empty MOCAPGOD queue): the capture protocol
is `docs/CAPTURE.md` in MOCAPGOD — the crate-riffle plus any two more moves you fancy.
2. **The MOCAPGOD C-vs-MIRPAMO integration call** is Fable-ultra's to make — nudge that
session when you're next on the M1.
3. Nothing else — the free stash (Spike 1) needs no new material at all.

106
docs/V7_THE_GAME.md Normal file
View File

@ -0,0 +1,106 @@
# PROCITY v7 — THE GAME (epoch charter — **RATIFIED by John, 2026-07-17**)
*(Ratified as drafted; the fantasy stands. R30 opens the epoch — v7.0-alpha wave 1.)*
*Fable (m3), 2026-07-17. From THE_FRONTIER's headline gap: the world is built, the game isn't.
v7 is the smallest set of systems that makes a stranger play twice. Design principle: every
system below rides machinery that ALREADY EXISTS — nothing here invents a new world layer.*
## The fantasy, one line
**You're a crate-digger in 90s Australia: build the collection, learn the prices, work the
towns — and be home in time for the gig.**
## The loop
Wake (save point) → plan the day (shops shut per their real hours; the gig starts at night —
the clock finally matters) → dig shops, spot the gems (priced below the guide), spend scarce
cash → sell the flips (keeper today; the auction tomorrow) → sleep = save, the day advances,
stock rotates → the collection grows → the wantlist points at another town → travel costs a
day. Repeat, richer and one gem wiser.
## The systems (each with its existing foundation)
1. **PERSISTENCE — the foundation stone.** Save = THE DELTA ONLY: cash, owned items, town, day
number, wantlist, stats. The world is never saved — it regenerates from seed (the
seeded-everything law finally pays its biggest dividend: saves are tiny and can't corrupt
the world). localStorage, schema-versioned, JSON export/import for moving machines.
*Foundation: item identity is already stable — parody items are seeded, real/mint carry
skus (the R25/R26 sku fences were built for exactly this, before we knew it).*
2. **THE COLLECTION.** Your crate, as a UI: cover, title, artist, price paid, where and what
day you found it. The trophy case of the whole game. (A walkable home-room shelf is v7.x —
UI first.) *Foundation: the atlas indexes already carry every field the card needs.*
3. **THE GUIDE + THE GEM (the thrill loop).** An in-game price guide — thriftgod's BIBLE
concept — gives a VALUE BAND per item (imperfect, like the real 90s guide books). Shops
price with seeded variance around it → a **gem** is an item priced under its band. Skill =
spotting them. *Foundation: mint() gem/price bands exist as data and drive nothing; real
items have real dealgod prices; the dig card already shows asking price.*
4. **SELLING — where cash comes back.** v7.0-alpha: sell to a keeper at the counter (below
guide, always — the no-money-pump law). v7.0: **GodBay** — list an item, a SEEDED offline
auction resolves NEXT DAY (the come-back-tomorrow hook; thriftgod's auction brain, G's
lane, offline tier first per the proven tier-ladder pattern — a live tier is v7.x and
rides the G3 rails).
5. **THE DAY.** Sleep advances the day; **stock rotates per (shop, day)** — seeded, so day 3
in Bendigo is the same for every player on that seed (parody/mint rotate; the real shop's
real stock stays real); gigs already run a 7-night week — `day % 7` plugs straight in;
hours already exist — now closing time costs you something. *Foundation: the segment spine,
`isOpen`, and `plan.gigs` are all sitting there waiting.*
6. **THE WANTLIST + TRAVEL.** Mark items/artists you're hunting; seeded rumors surface hints
("saw one in Castlemaine" — a keeper line, a gig-crowd mutter: D's citizens finally gossip
for a reason); **travel costs a day** → 23 towns become a strategy map instead of a
dropdown. *Foundation: the selector, the pack, the personas.*
7. **THE FIRST FIVE MINUTES.** You arrive with $50 and a want. Three toast-beats: dig, buy,
sell. A real splash. *(Smallest system, biggest stranger-facing payoff.)*
## The stages
- **v7.0-alpha — THE SAVE AND THE CRATE:** persistence + collection UI + sell-to-keeper +
sleep/day cycle + stock rotation. ONE town plays as a complete (small) game.
- **v7.0-beta — THE THRILL:** the guide bands + gem variance + wantlist + rumors + travel
costs a day. The 23 towns become the map.
- **v7.0 — THE AUCTION:** GodBay offline tier, onboarding, the tour ("day one to first gem"
as the money shot), docs freeze, tag.
## The laws (new ones this epoch earns)
1. **The delta law:** a save NEVER contains world state — only player deltas. A corrupted
save can cost you your stuff, never the town.
2. **The no-pump law:** every sell price < the buy-side guide band, structurally. The economy
gets a GATE (an agent plays N seeded days and asserts net-worth growth is bounded by finds,
not arbitrage loops).
3. **Game money is game money:** the write-back law extends — no bridge to real money, ever,
in either direction.
4. **Classic stays pure:** `?classic=1` boots the v2 town with NO game layer (no save, no
HUD additions) — the covenant is untouched. The game layer defaults ON for normal boots
(the v3.1 flip precedent) with `?game=0` as the opt-out.
5. **Rotation is seeded:** stock rotation draws from (citySeed, shopId, day) — deterministic,
gate-checkable, same for everyone on a seed.
## Lane map (first cut)
**F** the save core + gates (delta law, no-pump gate, save/load determinism) · **B** the UI
surfaces (collection, wantlist, day clock, splash — hud.js territory) · **C** the counter
sell interaction + dig-card guide hints (+ the home room, v7.x) · **A** the day/rotation
seeding contracts (new sub-streams, goldens untouched — rotation rides runtime, not plan
gen... A verifies that boundary) · **E** guide-band data baked into packs/atlases (mint
bands, real prices) · **D** rumor lines through keepers/citizens · **G** GodBay (the auction
brain, offline seeded tier) + real guide prices from dealgod.
## Risks (named now, v4-style)
1. **Economy balance** — pumps, dumps, degenerate flips. The no-pump gate is the fence; a
playtest-bot (an agent that greedily arbitrages for N days) is the adversarial verifier.
2. **Save schema drift** — versioned from day one, migration-or-reject, never silent.
3. **Item identity across re-emits** — a re-baked atlas must not orphan owned items (the sku
IS the identity; E's validator grows an owned-item stability check).
4. **Scope gravity** — the game wants everything (housing! romance! a phone!). The stage
gates are the fence: alpha ships ONE town playing well before beta touches 23.
5. **v6 collision — none by design:** v7-alpha is systems/UI (F/B/C-heavy); v6's spikes are
clips/ultra (E/D/G-heavy). They interleave; Fable sequences the rounds.
## Sequencing
v7.0-alpha can start **now** — it's unblocked (v6 waits on footage; the two don't collide).
Recommended: R30 = v7.0-alpha wave 1 (F save core + B collection UI + C sell counter contracts
first). John ratifies this charter (or amends the fantasy — it's your shop, your era, your
game) and says go.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

View File

@ -0,0 +1,8 @@
<!doctype html><meta charset=utf-8><title>PROCITY v5.0 — THE LIVE CRATE</title>
<style>body{background:#111;color:#ddd;font:15px/1.6 -apple-system,sans-serif;max-width:1100px;margin:40px auto;padding:0 20px}img{width:100%;border-radius:6px;margin:10px 0}h1{font-weight:600}p{color:#aaa}hr{border:0;border-top:1px solid #333;margin:36px 0}</style>
<h1>PROCITY v5.0 — THE LIVE CRATE</h1>
<p>One real record shop, on its real street, holding its real stock — and a server that can die mid-session without the game noticing.</p>
<hr><h3>musgrave_road</h3><img src='musgrave_road.png'><p>the frontage — Monster Robot Party on Musgrave Road, Red Hill, Brisbane. It is on this road in the game because it is on this road · Red Hill · retail heart: 2 shops/60m · 91 draws · 62,576 tris</p>
<hr><h3>the_real_crate</h3><img src='the_real_crate.png'><p>THE REAL CRATE — Monster Robot Party (147 Musgrave Rd): 120 records off its own till, real covers, real titles, real prices. Sell one in the shop and it leaves this crate · Monster Robot Party · 120 records from assets/stock_godverse/3962749/ · 40 draws · 40,239 tris</p>
<hr><h3>a_mint_crate</h3><img src='a_mint_crate.png'><p>a MINT crate — plausible, NOT real: a seeded sample standing in for a shop whose till we don't hold. Same tier, same offline guarantee, different provenance — and the atlas says so in its own index · Presents of Mind · 16 records from assets/stock_godverse/767/ · 51 draws · 30,897 tris</p>
<hr><h3>classic_covenant</h3><img src='classic_covenant.png'><p>?classic=1 — the exact v2 town, byte-identical forever (0x5f76e76). Five epochs, one covenant · Boolarra Heads · retail heart: 35 shops/60m · 162 draws · 39,259 tris</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

View File

@ -0,0 +1,22 @@
[
{
"name": "musgrave_road",
"file": "musgrave_road.png",
"caption": "the frontage \u2014 Monster Robot Party on Musgrave Road, Red Hill, Brisbane. It is on this road in the game because it is on this road \u00b7 Red Hill \u00b7 retail heart: 2 shops/60m \u00b7 91 draws \u00b7 62,576 tris"
},
{
"name": "the_real_crate",
"file": "the_real_crate.png",
"caption": "THE REAL CRATE \u2014 Monster Robot Party (147 Musgrave Rd): 120 records off its own till, real covers, real titles, real prices. Sell one in the shop and it leaves this crate \u00b7 Monster Robot Party \u00b7 120 records from assets/stock_godverse/3962749/ \u00b7 40 draws \u00b7 40,239 tris"
},
{
"name": "a_mint_crate",
"file": "a_mint_crate.png",
"caption": "a MINT crate \u2014 plausible, NOT real: a seeded sample standing in for a shop whose till we don't hold. Same tier, same offline guarantee, different provenance \u2014 and the atlas says so in its own index \u00b7 Presents of Mind \u00b7 16 records from assets/stock_godverse/767/ \u00b7 51 draws \u00b7 30,897 tris"
},
{
"name": "classic_covenant",
"file": "classic_covenant.png",
"caption": "?classic=1 \u2014 the exact v2 town, byte-identical forever (0x5f76e76). Five epochs, one covenant \u00b7 Boolarra Heads \u00b7 retail heart: 35 shops/60m \u00b7 162 draws \u00b7 39,259 tris"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

View File

@ -0,0 +1,15 @@
# THE TOUR — day one to first gem (v7.0's money-shot sequence, R36)
Seed 20261990 (Boolarra Heads), one real playthrough driven end to end, no staging:
1. **01_you_arrive_hunting** — the splash names the mission: hunting HILLS HOIST, $191 in your pocket.
2. **02_the_strip** — the cluster-pose spawn: midday on the main street, shopfronts down the block.
3. **03_every_door_opens** — inside Curio & Wax; the keeper's rumor overhead.
4. **04_the_gem** — THE VERANDAHS "Tinny", asking **$19** against **★ RARE guide $60+**. The
skill of the whole game in one card. (Beat ③ visible below — the onboarding walking its talk.)
5. **05_the_crate** — the find banked, ⚖ a click away.
6. **06_the_morning_paper** — dawn, day 2: **"⚖ SOLD 'Tinny' — hammer $89, $78 after fees."**
Bought for $19, hammered for $89. That's the loop.
Regenerate: the scratch driver lives in the R36 session; it reuses tools/flags_check.py helpers
against any port via PROCITY_QA_PORT.

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 1082,
"slug": "downtown-music",
"name": "Downtown Music",
"suburb": "Annandale",
"state": "NSW",
"type": "record"
},
"counts": {
"pool": 111320,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 1082,
"sample": "random.Random(1082) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "1382327",
"artist": "Endgames",
"title": "Building Beauty",
"price": 24.98,
"condition": "",
"cover": "/img/47/4772a5f355a19a55591e453dee9b0834a52725998c70e4d0a5eacd225f7b2bd0.webp",
"cover_sha256": "feafdd57a7e3dfccb0770c900c76040227585f1c0a4870062038ca545e9be661"
},
{
"mint_id": "1497796",
"artist": "Morel",
"title": "True (The Faggot Is You)",
"price": 26.31,
"condition": "",
"cover": "/img/c9/c97112d937e59150d1a0ce91b3fa372435497171582cce02af3e3cca2bee5238.webp",
"cover_sha256": "914cce71b1d924636211742608488594e4f82c10319ae5f8dc2e60b73b795de4"
},
{
"mint_id": "1499132",
"artist": "Philippe B.",
"title": "You & Me",
"price": 8.39,
"condition": "",
"cover": "/img/16/160703b585db320c7e0b8716a11ef5b5e7ca8634ecfe24346bc86fcc55708bd4.webp",
"cover_sha256": "cc39629c26f30d17d78818446265ead4077dade829f6b51ae5e330a9f31f7adb"
},
{
"mint_id": "1510082",
"artist": "Richie Santana",
"title": "Music Is Freedom Remixes",
"price": 14.19,
"condition": "",
"cover": "/img/af/af67b20e319e2cf56f5f769cb4f9dd85cfc4faf584cf24533d37bb5811d45b31.webp",
"cover_sha256": "8933f30c07837e9e85e44529cdce16a87f2f003148c655ea26fa3b91bd4a7e6e"
},
{
"mint_id": "1572084",
"artist": "Paco Pe\u00f1a",
"title": "Paco Pe\u00f1a - Flamenco Paco Pe\u00f1a (LP, Album)",
"price": 12.0,
"condition": "",
"cover": "/img/20/20bd2289bb916c33166abf6ac5dbc7086623deb2344162fecfc18d627d1ce046.webp",
"cover_sha256": "c7e601f656fc45df9d08568456fe569e9fbc5a021bdf6be2470521ca4a255bee"
},
{
"mint_id": "1952439",
"artist": "The Party Boys (3)",
"title": "The Party Boys",
"price": 8.0,
"condition": "",
"cover": "/img/e9/e9a567a74e5bd7c7e2f2a738ceb2a6309d539e4fc72d8eaee31172b3af64b18c.webp",
"cover_sha256": "778d9abcc593191ebf762d56674e4e07a43e4feaab2ebfee8d98463a515f0a2e"
},
{
"mint_id": "2634580",
"artist": "Shakti",
"title": "Shakti With John McLaughlin = \u30a6\u30a3\u30ba\u30fb\u30b8\u30e7\u30f3\u30fb\u30de\u30af\u30e9\u30d5\u30ea\u30f3",
"price": 25.0,
"condition": "",
"cover": "/img/42/4227c662d18c316e97f117a06019c90ebadb739ad12dba18d84c910202cabdf2.webp",
"cover_sha256": "81832d786d9304b96ac2b9d73a1ceace040a093ab88bc3ff263ae5c1efcbfde2"
},
{
"mint_id": "2636233",
"artist": "Donny Hathaway",
"title": "Donny Hathaway \u2013 Live (Rhino Reserve)",
"price": 58.0,
"condition": "",
"cover": "/img/fa/facf73e811910719eb69185cc5e9940db3382d2333007b8c5c2c6712aef4d2e1.webp",
"cover_sha256": "e31336a557c5a617ceb7d3e1e730f5c2d09fbe98923ff2613d7937c9057b98db"
},
{
"mint_id": "3375700",
"artist": "Jockstrap (4)",
"title": "I Love You Jennifer B",
"price": 60.0,
"condition": "",
"cover": "/img/42/4227cc385e5ce7e9d5d33820d371cd71aaa6337b30f2a6ca931af4429759dfb1.webp",
"cover_sha256": "a6cba26bf12368060173a4c2a3bbc36b18e3229f67e564f951b02d84bac626af"
},
{
"mint_id": "3377454",
"artist": "Caligula's Horse",
"title": "Charcoal Grace",
"price": 89.95,
"condition": "",
"cover": "/img/54/541cadcf55827a2ddfbdf4db1f2ff1799afca97b994bba2c40eb7d9b3d336be5.webp",
"cover_sha256": "1a2aee971cd9aa71e01a17660f47c05973768b9e721971f2b723344d37ffa988"
},
{
"mint_id": "5040486",
"artist": "Anri (2)",
"title": "Fun Time",
"price": 149.99,
"condition": "",
"cover": "/img/2c/2c00b474f40d7a7bcffdf196481a0e4bd2f9755ebf1114ce6dce6635145a11ee.webp",
"cover_sha256": "4d350abc6a550c01c10961b9cab8c3458beffc59d4e078ca53ea2d9f8b0fe5ce"
},
{
"mint_id": "5077859",
"artist": "Slowdive",
"title": "Everything Is Alive",
"price": 25.02,
"condition": "",
"cover": "/img/65/65bded74f26235bfcbc86271b92573dccfc002a7bdca0275bf05cf65f5a4f0eb.webp",
"cover_sha256": "878fe80b37b3bf7dbfed48b5f0cf0c840329f8c16d5fd721509b9bac2f208c62"
},
{
"mint_id": "6319846",
"artist": "The Black Heart Procession",
"title": "A 3 Song Recording",
"price": 9.95,
"condition": "",
"cover": "/img/53/53aa62830a653d4fb17a258cb8bbf2f531be14c704624f5afa37db84f44f3b5a.webp",
"cover_sha256": "cfa5fa1cc6f23edf1316f729ce13558ca43224c877b4188d481a3ed7be7fd11e"
},
{
"mint_id": "6364791",
"artist": "Scarcity Of Tanks",
"title": "Garford Mute",
"price": 32.0,
"condition": "",
"cover": "/img/a5/a5b2bd14f0fd1b7bc512397227b741262c8f19e211bb559dab4fd072fe2fc0db.webp",
"cover_sha256": "75ad7ecf89872c298813af620168eb6a9707569e2347a475e424b8e6b0e227e8"
},
{
"mint_id": "6511306",
"artist": "Nick Cave & The Bad Seeds",
"title": "The Wanted Man",
"price": 75.0,
"condition": "",
"cover": "/img/93/9352a37eea7f0373b2d3c701139ca311fe9533ec2000ce90a9f93f0cb0669a6b.webp",
"cover_sha256": "a71272001ff80a758db1ee1c4a3bed07cf9d2cc5b7420e488d3172238703f6c5"
},
{
"mint_id": "7329708",
"artist": "Christina Aguilera",
"title": "Dirrty",
"price": 7.95,
"condition": "",
"cover": "/img/26/268ddb42bfa3129478bcef3987496c051271f6224f641eba194f0fcfd5965d4e.webp",
"cover_sha256": "eefb5a8add676e5706251682a56ea0211473e393231694891d2a5c1d868312fd"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 1173,
"slug": "haze-guitars",
"name": "Haze Guitars",
"suburb": "Newtown",
"state": "NSW",
"type": "record"
},
"counts": {
"pool": 111320,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 1173,
"sample": "random.Random(1173) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "735827",
"artist": "Kasey Chambers",
"title": "Behind The Barricades",
"price": 15.0,
"condition": "",
"cover": "/img/65/65b1262d99d1bb2ed4172d6ac39e3e167dd72344aa49d0c4cad13ba9ec4a6d6a.webp",
"cover_sha256": "a164e8bf549e22ad22b3ec96666b01df2dfa5934ba95bec66f2f613abc25ac5b"
},
{
"mint_id": "1506171",
"artist": "Jas Van Houten",
"title": "Just FX",
"price": 32.89,
"condition": "",
"cover": "/img/4b/4b09ec070b98e6ae4ff0452657d33079a3bdf07c030aa26d9c3792156074f260.webp",
"cover_sha256": "27ba9584c172d0246c8d495891b2ca66442422cf4f26f7b0d7d070718b9fe2b9"
},
{
"mint_id": "1572335",
"artist": "Fats Waller",
"title": "Fats Waller In London (No. 2)",
"price": 15.0,
"condition": "",
"cover": "/img/2f/2f08a60d894c5547174574d63e6b2aab629b79e52d86e0e486ef626c338b3056.webp",
"cover_sha256": "818332572cc31c972377c9297518dd322465b4b47fccec49728dd51b74428ebe"
},
{
"mint_id": "1927813",
"artist": "Bob Dylan",
"title": "More Blood, More Tracks (The Bootleg Series Vol.14)",
"price": 75.0,
"condition": "",
"cover": "/img/54/544d0940afb2385430cd8aec55a38adc06048f816ea51f865e267df43e99d5c2.webp",
"cover_sha256": "3cc261cf33ed39f77bb9590895564b82c81e04b48685b001636f4bd740fec6d2"
},
{
"mint_id": "1955485",
"artist": "Wet Wet Wet",
"title": "Stay With Me Heartache / I Feel Fine",
"price": 14.37,
"condition": "",
"cover": "/img/a7/a7dffa77844c73d3fc2d89e015b074bff2e66ad428fd4b1d541e9601f92bc6e5.webp",
"cover_sha256": "32020a17131dc88b84f2b9e228c024691b8419ba57df45785689e4c079775eaa"
},
{
"mint_id": "2949568",
"artist": "Blondie",
"title": "Blondie Singles Collection: 1977-1982",
"price": 28.99,
"condition": "",
"cover": "/img/82/82e9d54ac910da725bf4b8a28cb8602fd73cc0927a8c6e3cc812cd4da86596b3.webp",
"cover_sha256": "35ab1a3cf316e6f497dac81816933d3ed7b5446031ab6896f17e012660c48777"
},
{
"mint_id": "3150782",
"artist": "Dolly Parton",
"title": "Jolene",
"price": 58.0,
"condition": "",
"cover": "/img/89/8968102d3c4ee079dcf9c753ab550d3240a72bde667aeedb02644dd0a2cd4576.webp",
"cover_sha256": "35921bad6411f36d90358c13688fb0a0d901eba68742d0dfa83917c9827aad2a"
},
{
"mint_id": "3150992",
"artist": "Dope Lemon",
"title": "Rose Pink Cadillac",
"price": 65.0,
"condition": "",
"cover": "/img/fb/fb778db3fa7ec5720e4473993e47b83862dd98f891a12e832b4e39bc3e271474.webp",
"cover_sha256": "12c194a1b71db80febe07f397967e9e99bf802ec3094c4f86d9ab99ba3fe8432"
},
{
"mint_id": "3261454",
"artist": "Kool Keith",
"title": "Feature Magnetic",
"price": 59.95,
"condition": "",
"cover": "/img/3d/3d71961dc2b5aee8a4f77ac5d88db20735016357b6a5c051e455d07d0c0839ae.webp",
"cover_sha256": "6c9c10fe359f15ea6c72085d05640386819178669b0901fc964413ee48c8d3c2"
},
{
"mint_id": "3274472",
"artist": "Ayers Rock",
"title": "Ayers Rock \u2013 Big Red Rock Vinyl LP (Used)",
"price": 45.0,
"condition": "",
"cover": "/img/76/76b162d513850d7de87a5b031bd5cd057e0b468a3abe2b9cfba1c1241578522a.webp",
"cover_sha256": "5502bdd78b5d67219cbb6b0ad2279d72f2634ac77d44825c7f3c283caa31a267"
},
{
"mint_id": "4521621",
"artist": "Models (2)",
"title": "The Pleasure Of Your Company...",
"price": 37.0,
"condition": "",
"cover": "/img/21/21e9f3a53308d80bce64e689368a827c1dbaf0d6c5babaca764cceb56254aae9.webp",
"cover_sha256": "86c506f39c55b4b9d4d3cb2c7f18f873422ca0de30fdba26610dec3b395202db"
},
{
"mint_id": "6008610",
"artist": "Kate Garner",
"title": "Love Me Like A Rocket",
"price": 10.0,
"condition": "",
"cover": "/img/6a/6a06b3371b9e17e5b4cf7a32b16fa93db7eb300d1159bdfd881b8f40dc129f85.webp",
"cover_sha256": "1c44694e3726eaaa57cfd959dbd4e9df7dbec4dcdb172ddf91c4d5a6c1b4e7fb"
},
{
"mint_id": "6308495",
"artist": "Audrey Landers",
"title": "Little River",
"price": 12.95,
"condition": "",
"cover": "/img/9e/9ef7a355803c186525af7c693f2a9375f8201f6e094e8fc9dbe51ce4b3493b46.webp",
"cover_sha256": "5e00e3f20ffe5f2615c67da5f01bfa612558b4a4215aa5478b13b51f7fb6e764"
},
{
"mint_id": "6355830",
"artist": "Butthole Surfers",
"title": "Butthole Surfers + PCPPEP",
"price": 44.99,
"condition": "",
"cover": "/img/2d/2d891b36e463e3d6d9420a875f567732006f81379b17a237d4f8f2d2356dec7d.webp",
"cover_sha256": "220a7206e605c19069dd58854e0f33eac421b7b705d9b477962d4cd327c17d3d"
},
{
"mint_id": "6356386",
"artist": "R.E.M.",
"title": "Around The Sun",
"price": 86.99,
"condition": "",
"cover": "/img/d8/d83ec45451fb3a347e724de28c528b9e3b5aabbe2366beedd77ac3cafa25b61d.webp",
"cover_sha256": "eeb79612ebf0b21ff8bd63911c5ad4c644ca44426d9dfa98d6ada9b0880bd464"
},
{
"mint_id": "6841696",
"artist": "Alice Cooper",
"title": "Alice Cooper's Greatest Hits",
"price": 35.0,
"condition": "",
"cover": "/img/7f/7ff80457af5f40979283e4a48bf55a438239ba1516eb8fdb2f792ead5ac87e8f.webp",
"cover_sha256": "06b6aa52e82b14bf55a1a376e803b7bdf073b26fb77f88d719e151910ff70515"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 158,
"slug": "egg-records",
"name": "Egg Records",
"suburb": "Newtown",
"state": "NSW",
"type": "record"
},
"counts": {
"pool": 111320,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 158,
"sample": "random.Random(158) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "1490673",
"artist": "MC5",
"title": "High Time",
"price": 50.0,
"condition": "",
"cover": "/img/cf/cfd515b69f7b5ab131d8cf02edec80edad9785c8c348be37e4b8d019e5537f86.webp",
"cover_sha256": "3a6ef116f5452aaa1676f61ce8b15a8fe3ca6627c46afb5e9b5321471b6f9e9b"
},
{
"mint_id": "1504320",
"artist": "Amerie",
"title": "1 Thing",
"price": 2.69,
"condition": "",
"cover": "/img/db/db2efb4148d5558c2009cfb8bf88751177f7413ef50b0f0e181cbc8c6ef10b9b.webp",
"cover_sha256": "f908e95620f34d5a2cc26255ab724e187ad2045d329d86cd709afbdc96f1103f"
},
{
"mint_id": "1505490",
"artist": "Inaya Day",
"title": "Nasty Girl",
"price": 13.11,
"condition": "",
"cover": "/img/3e/3e37c22539732f8eadfde27ef14ff7338352746309590ee1a8feef4ea0787e7d.webp",
"cover_sha256": "a24abd8eeb0425422532443ba5cccdecc10112bfe72dc4655e5637bfa522f407"
},
{
"mint_id": "1555544",
"artist": "Elton John",
"title": "Heartache All Over The World",
"price": 9.98,
"condition": "",
"cover": "/img/17/176b1cee3792be9855edb2bc47392e01d7496f0648be0458d04b544f30e8c06e.webp",
"cover_sha256": "279c2b313202764b0ff39fb60850c5445185c393bf717ba973f994a5c35ba942"
},
{
"mint_id": "1960626",
"artist": "Johnny Rivers",
"title": "The Very Best Of Johnny Rivers",
"price": 12.0,
"condition": "",
"cover": "/img/66/660ac73460bd9dd8e8a3c31c8ad28bd15bea0021f247e5c110cc905680a74082.webp",
"cover_sha256": "593240266b1b73262fee5eedb196da5df59a39174f32388298cc7e9a71dcb673"
},
{
"mint_id": "3148069",
"artist": "Dominic Fike",
"title": "Sunburn",
"price": 69.0,
"condition": "",
"cover": "/img/12/12f2e30aa9f93012138148f8afe4db48fca9c6598f2be2d7cb7349f13319ef3a.webp",
"cover_sha256": "08eb83b1bf88b858321f203d373ca4870af1b73ba02c6573a2b3e15d51127ea4"
},
{
"mint_id": "3274969",
"artist": "Sex Pistols",
"title": "God Save The Queen",
"price": 49.99,
"condition": "",
"cover": "/img/2e/2e76e87a0d201fa7e7fdb6cd6f1950f84eeda74d83d933ddafe71f1a52f557c1.webp",
"cover_sha256": "be582d725f4ed229a977ceaa73b73d53c95f13ce026b441ba003d2504c3a2bd4"
},
{
"mint_id": "3312435",
"artist": "TV Girl",
"title": "Death of a Party Girl",
"price": 69.95,
"condition": "",
"cover": "/img/fa/fa5a544a0932e7adf01ce477f9c9614f51a69c9eb5c85386e29473fe4498305d.webp",
"cover_sha256": "3bb843460ade3a14a870ae036318d59cd2c01c4bacba0f3a1ea040027da02f56"
},
{
"mint_id": "5225870",
"artist": "Dream Theater",
"title": "Systematic Chaos",
"price": 134.99,
"condition": "",
"cover": "/img/d2/d2e4f12f3227851e9ddaedb5ad84bd67743a62fd9e6118f637a95b8e2025aece.webp",
"cover_sha256": "2e33d981ef34dda770106690eccd5286dcb33e8a832963b9384ad515539c0e22"
},
{
"mint_id": "6005806",
"artist": "Led Zeppelin",
"title": "Physical Graffiti",
"price": 115.0,
"condition": "",
"cover": "/img/f0/f0b6d7657ff8ef885f57e8cb48725f881cc70ef758ba2f5c7f63649958a3fa30.webp",
"cover_sha256": "b3807bcbcb25f1bad10f37105112f73d189e9b9d845d61c2689974cce6810d4f"
},
{
"mint_id": "6007579",
"artist": "The Budos Band",
"title": "The Budos Band EP",
"price": 52.0,
"condition": "",
"cover": "/img/fc/fc93db92f289bc7afb1e5dfb66111914bb2049efb30f95b8feb4b3acc37eb3ce.webp",
"cover_sha256": "2c731749994ffe548078435d26ff92ae8b85a5c0f189af6382955663a3d7e27e"
},
{
"mint_id": "6307515",
"artist": "Joe Satriani",
"title": "Shockwave Supernova",
"price": 79.95,
"condition": "",
"cover": "/img/35/35f210fa3ded044311e877d57be4e316b2d35035af263ffe7d6e8bb83a0a2101.webp",
"cover_sha256": "c493dc0849ed20faea97142b755a8239cec587d79b523f1410af01e729db40ed"
},
{
"mint_id": "6512824",
"artist": "Royal Trux",
"title": "Royal Trux",
"price": 15.0,
"condition": "",
"cover": "/img/55/55ac4e6dc1a16826970239451abcf2dc19cd23797489f52b30d4d4b53b3ca223.webp",
"cover_sha256": "42ed85ecce2d23ef3c4678a98882fa32cbbd9371fdc889cf696a5da18b7fbc53"
},
{
"mint_id": "6515893",
"artist": "Jennifer Warnes",
"title": "First We Take Manhattan",
"price": 10.0,
"condition": "",
"cover": "/img/73/73ef3fe4c92d7924c7ec4821f2bd47bda11a53dc56c4988040226abeffe1368c.webp",
"cover_sha256": "41ed3fc218711549717302babaad5b10951a4e9933312c094020c3c5219209e7"
},
{
"mint_id": "6842418",
"artist": "Joan Baez",
"title": "Any Day Now",
"price": 32.0,
"condition": "",
"cover": "/img/25/255eb21ee5e56612b723a5501f6ab986a087d31d53c5c578fd6f34a5c422a12a.webp",
"cover_sha256": "295129f4339b41666d22270aaab3df7c6761340b736855544155af7eb1b73dc4"
},
{
"mint_id": "6974962",
"artist": "Vis A Vis",
"title": "Di Wo Ho Ni",
"price": 49.95,
"condition": "",
"cover": "/img/f2/f243ee56afd1ca6ce86b5b053bd980333a45f1b8fb30d847dfe2bb25590cb10f.webp",
"cover_sha256": "0e70b8191c0222d91a48e12a13343d29091bc8e761e26d69be0feac572858abf"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2200,
"slug": "better-read-than-dead",
"name": "Better Read Than Dead",
"suburb": "Newtown",
"state": "NSW",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2200,
"sample": "random.Random(2200) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "831781",
"artist": "Lifeline Queensland",
"title": "The Fallen - David Baldacci - BPAP - BOO020",
"price": 8.0,
"condition": "",
"cover": "/img/48/4825ca2268f67018dfe83f52e4f76bb8491d00f80c49bde4c0b9a62fa38bc84a.webp",
"cover_sha256": "fb310978a48bc8aaf7730450e7939a1361bd02d42d1f0af3468499b9696a4b98"
},
{
"mint_id": "1925436",
"artist": "Lodestars",
"title": "Lodestars Anthology: New Zealand",
"price": 35.0,
"condition": "",
"cover": "/img/b2/b242096b94841f3cb1d489d6c4a750a56c445a5fb4a542d3c3d604cb123beb92.webp",
"cover_sha256": "2e591f2e0b1a725d4e4766e3e6b2f6c9eff85583acd377bba6a157936ed3ba0a"
},
{
"mint_id": "1931996",
"artist": "Katherine Ryan",
"title": "Audacity : Why Being Too Much Is Exactly Enough - Katherine Ryan",
"price": 27.0,
"condition": "",
"cover": "/img/08/086275737c228ee850f6fe2fc277677895cbc6a63752c866239440afafaabcbd.webp",
"cover_sha256": "0577cb716ba8decda884a8507186fc4a01d2a2c38b40dcf1fe74273d548a33c0"
},
{
"mint_id": "1932007",
"artist": "Matthew Walker",
"title": "Why We Sleep : The New Science of Sleep and Dreams - Matthew Walker",
"price": 27.0,
"condition": "",
"cover": "/img/8e/8e26eedebb3ca7d85a45504bf5877996c8d05aa0bd9992a11dfc55369d71fc5d.webp",
"cover_sha256": "2e75483c91b7c319c69a6ddf89f4c492d2dc8c19c3dc6c9e52b2fb1ced86b5a6"
},
{
"mint_id": "4069295",
"artist": "A24",
"title": "99 TV Crosswords - A24",
"price": 70.0,
"condition": "",
"cover": "/img/8f/8fd890f45ff42925c09b1d337fe516398cc527f56cc983db09b2a50187b2a074.webp",
"cover_sha256": "756961abe95a177dcb196bcbf8efdc6f8a9088f2fc7b76fd56ebf9af95c6ec76"
},
{
"mint_id": "6728501",
"artist": "NewSouth Publishing",
"title": "Liars, cheats and copycatsTrickery and deception in nature",
"price": 34.99,
"condition": "",
"cover": "/img/9f/9f93c1a865e2a34b658c4122cc1be143b3694f1bf6c920b2b7a8ce14db9e82c9.webp",
"cover_sha256": "e261a5ad93dfe98bc6ce7e3d65aea38472e76f24c75be69c924cb918e428996d"
},
{
"mint_id": "6728751",
"artist": "Brio Books",
"title": "The Story of Growl",
"price": 18.99,
"condition": "",
"cover": "/img/5f/5fa2f4fb07ce2ff5ed9529af7298e41901578a7c84e2ac88bf3a0939cf6c95e4.webp",
"cover_sha256": "b8f8acbe69dab2991816ec8a5e8756809bb6bb24bb388ba185752250bf5abff5"
},
{
"mint_id": "6730340",
"artist": "HarperCollins Publishers Australia",
"title": "Ozzie Goes to School",
"price": 16.99,
"condition": "",
"cover": "/img/b2/b2f2785d9194f89d8503316024fcb3040620fac4c71e0ec29edbfbf6a530e88d.webp",
"cover_sha256": "f48fefd16e89268ae5c2741bf0e5d507919c784bf85110941f1fec08fcec2870"
},
{
"mint_id": "6962635",
"artist": "Royal Academy of Arts",
"title": "Leonardo Da Vinci: the Anatomical Dra...",
"price": 35.0,
"condition": "",
"cover": "/img/f0/f0a73552248b176b11ba8a43cfeace107558c8009075109c592b548bed1eaeea.webp",
"cover_sha256": "b530a67712d3473316c07c2a9a249accfd32f45c2c2743e9c5caa3b95e60135f"
},
{
"mint_id": "6962907",
"artist": "NewSouth Publishing",
"title": "Painting by Numbers: The life and art of Ferdinand",
"price": 14.0,
"condition": "",
"cover": "/img/21/21c0468d0203b87ead14e02ac1657871f48561c1205d6d7ebecd7ed2c1b712fe.webp",
"cover_sha256": "5f5f298289dbac28ec8e5ccaaa56c5f9dc2aee59dd71b9ec0132a847bfa69c4a"
},
{
"mint_id": "6964245",
"artist": "New Holland Publishers",
"title": "ECO STYLE",
"price": 45.0,
"condition": "",
"cover": "/img/d2/d271dc15b464c45ca8ffff40f080316795e8dd6084c227589bc100b06b23443c.webp",
"cover_sha256": "2499b0c9ada9afc9311485ce0ae9ac1c0856fba371e327497e065f6bc007f5d1"
},
{
"mint_id": "6966282",
"artist": "teNeues Verlag",
"title": "Children of the World",
"price": 125.0,
"condition": "",
"cover": "/img/7d/7d8744ee5195944b4898477a229f91915998fd016ae6c0caa310ef2186c7d1b9.webp",
"cover_sha256": "a1b037b052ff6b982a11de95316a4695c48d1bda74e2015af201bae0dad88c08"
},
{
"mint_id": "6967149",
"artist": "National Library of Australia",
"title": "Australia's Wild Places",
"price": 39.95,
"condition": "",
"cover": "/img/df/dfc5ed8dc519354be8f0209cee495ea18fd65c1f375e0b90f1bb132479321380.webp",
"cover_sha256": "d638554812ba3995ebeceef734dacdcb971e90db84dd1059a3a40dab8aa3ecae"
},
{
"mint_id": "6967659",
"artist": "Thames & Hudson, Limited",
"title": "Fashion Central Saint Martins",
"price": 59.99,
"condition": "",
"cover": "/img/6c/6ceb0393c9c74e8e24e4b81b3715c4bdf8ead06afd3e65969db3d2f2a185e259.webp",
"cover_sha256": "eaf80cd029cc18689907d7fa254dfdb925bd68ea3aaae5aa5e24a0fdcda054c6"
},
{
"mint_id": "6969218",
"artist": "Ryland Peters & Small",
"title": "Simple Natural Crochet: 35 Projects to Make - Gorge",
"price": 35.0,
"condition": "",
"cover": "/img/ff/ff6bd38a06660088dc911b89ae5a09f273c1f6abc74241e62e8779dd9cff6710.webp",
"cover_sha256": "f0d904af9caf02bbb5cbda55084bd5f493bf565021f0a568668ca6255f492e4e"
},
{
"mint_id": "6999001",
"artist": "Dorling Kindersley Publishing, Incorporated",
"title": "Grow, Cook, Dye, Wear: From Seed To Style The Susta",
"price": 39.99,
"condition": "",
"cover": "/img/4c/4cf4754f7510aacdba4ccda44e0c6ee3f1d1d933a93dc19c5eccfddd7f3b98e1.webp",
"cover_sha256": "214c550d97b40827b86617cbd153e15d1ce9fdd4269cbea4729169fbf0a5bdc4"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2201,
"slug": "elizabeth-s-bookshop",
"name": "Elizabeth's Bookshop",
"suburb": "Newtown",
"state": "NSW",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2201,
"sample": "random.Random(2201) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "826954",
"artist": "Lifeline Queensland",
"title": "Lessons in Chemistry - Bonnie Garmus - BPAP - BOO008",
"price": 4.0,
"condition": "",
"cover": "/img/a0/a09b725743c8ae60f1982c4a2c98d46e66f13436546d73245394eff29d3ab17d.webp",
"cover_sha256": "030076d3ed8a2e06d2597bfacf77bac5ad2f3e8bd9d11ccae32418c72986f22a"
},
{
"mint_id": "1931980",
"artist": "Fiona Austin",
"title": "Beaumaris Modern 2 - Fiona Austin",
"price": 65.0,
"condition": "",
"cover": "/img/5f/5f3517573ffde59673a0c3bfca2ab139ba5c8e2a853c6cf5d5c0dd9424337b81.webp",
"cover_sha256": "ee67a591860e4e6c84d6fcf631f47f07661ec6e50c9afa3fc6f6b9cf96f30e2d"
},
{
"mint_id": "2065183",
"artist": "Lifeline Queensland",
"title": "Slow Cooker: The Complete Collection - Australian Women's Weekly - BCOO - BOO053A",
"price": 10.0,
"condition": "",
"cover": "/img/83/83639c9cf4c8b2c2466c5a47a168b8be6bf43a0e1b8b5e818db51727a68690f0.webp",
"cover_sha256": "d553f928914481c8104eebe40cea2884e686758bbc1c2aad38b22c87540b11b2"
},
{
"mint_id": "3310554",
"artist": "GAR",
"title": "Jones, Lesley-Anne - Songbird: An Intimate Biography Of [Book]",
"price": 24.95,
"condition": "",
"cover": "/img/35/3544d7a3c8b1b4f7313b7eada7d0f2ae75d8d3e2011e63531c0b84014adcabc6.webp",
"cover_sha256": "0e1a425fa722c2c4677bcb57a141638dc29dd6aedf9a5432cef8d9521ba055f1"
},
{
"mint_id": "6109637",
"artist": "Truman Capote",
"title": "Breakfast At Tiffany's (Popular Penguins) - Truman Capote",
"price": 15.0,
"condition": "",
"cover": "/img/9f/9fff0590cd3cb093776e0b9ce050f302f1c72033ebbc6aca307eb729c5490f4e.webp",
"cover_sha256": "ec10cc13129834065b9e5ce57838cfb3bd3001744d6a616bc456b54b4c13861a"
},
{
"mint_id": "6109781",
"artist": "Dr Seuss",
"title": "Lorax - Dr Seuss",
"price": 12.0,
"condition": "",
"cover": "/img/ca/ca381c35ec120856fa55e8522ac53d9ba6d0791ed475ff404fa4c89aaed6e772.webp",
"cover_sha256": "4cfde6063de59dca2c92974ce44061b7f222917b2f1fbdbe3db11d6d571fc5b9"
},
{
"mint_id": "6729688",
"artist": "Ultimo Press",
"title": "The Man Who Wasn't There",
"price": 36.99,
"condition": "",
"cover": "/img/39/39cc2a4478c5119d43ce5d5e028d1d98b1bd8f3d53a4cb5262af101f681a03cd.webp",
"cover_sha256": "4762d4bc643e4d4485a0db92bd93504a0d1e927bf700322f55a1f945748f9a0f"
},
{
"mint_id": "6965715",
"artist": "Tate Publishing",
"title": "Matisse",
"price": 13.95,
"condition": "",
"cover": "/img/12/12ee7d9ed628e9b64c63b3796ba3cd6ab09c615c6a16c6934e9cd473a7d9c43c.webp",
"cover_sha256": "3ad3523e2c6473db571677c285e75e54bda1f6d9a8c6de0c215d903ae928d501"
},
{
"mint_id": "6966158",
"artist": "Merrell Publishers Limited",
"title": "More Dream Homes: 100 Inspirational Interiors",
"price": 100.0,
"condition": "",
"cover": "/img/0d/0dda691ba3ba9c12bdbda2206e8fc17401d1c294a5b09a2ab0b62d7f8659902e.webp",
"cover_sha256": "aa09a92c8d9f398538d55d0a46697bc76d476b0d4b6c60cb491b83319019578c"
},
{
"mint_id": "6966174",
"artist": "Phaidon Press Limited",
"title": "Wild: The Naturalistic Garden",
"price": 79.95,
"condition": "",
"cover": "/img/53/53ea69f9b69168bb0b26ee76f466d86baf4944ea885b1e2aa795d94bdb2ec4a5.webp",
"cover_sha256": "133eb315c93afa888da886474e710ba751a3ac16d8fef79f8fca51f63935f314"
},
{
"mint_id": "6966273",
"artist": "Gibbs Smith, Publisher",
"title": "Tales from the Forest Kingdom Coloring Book",
"price": 24.99,
"condition": "",
"cover": "/img/19/194a44a00b3111f10427aa668c3325c01be8e1fb5f6f113459aa2b5122290462.webp",
"cover_sha256": "7ca75bbbb3d3a3a5a316c50edc7d32406dcef8ce214b051fcc33328d552fed6e"
},
{
"mint_id": "6968094",
"artist": "Thames & Hudson, Limited",
"title": "Ravens and Red Lipstick: Japanese Photography Since",
"price": 59.95,
"condition": "",
"cover": "/img/0a/0a589521a852195ac698df12984573cdc3022d32c84760080202c7569c187de8.webp",
"cover_sha256": "824b9ea857f16acc859995b4cb921a7e4f8cd7fc0d22540c6325a019bfec45bb"
},
{
"mint_id": "6968262",
"artist": "Orion Publishing Group, Limited",
"title": "A Fire Born of Exile - A Spellbinding Standalone Sci-Fi Romance and 2024 Hugo Award Finalist Perfect for Fans of Becky Chambers\\n by Aliette de Bodard",
"price": 24.99,
"condition": "",
"cover": "/img/52/52ef904919cd6b4409a7e3b4bd66edc59d758f6b2d5abd4ba93d533f130c6cec.webp",
"cover_sha256": "d3c708c1988e64f5a3dee433688fb313b47bd4a32ba95793024d73a5702371bc"
},
{
"mint_id": "6968962",
"artist": "HarperCollins Publishers",
"title": "Audrey Hepburn: Icons Of Style",
"price": 16.99,
"condition": "",
"cover": "/img/88/880d2af978b227fa73d3e9c0c448aaee3b7196d1dd74f20218816244263e8077.webp",
"cover_sha256": "98badfd98df1ae6032f9ea1499960ee1375a8aae298fefff1e781ffc940c72fe"
},
{
"mint_id": "6994056",
"artist": "Hachette UK",
"title": "Marina Abramovic: A Visual Biography",
"price": 175.0,
"condition": "",
"cover": "/img/8a/8afe1ed12ea7eec163833b1724d25df87c467284a46403ed1162317aa4efdd14.webp",
"cover_sha256": "c3c0de828404407277a500d4a3b9cdb93fd00b0659e762100eb76f2b7aed8d00"
},
{
"mint_id": "7035785",
"artist": "Margot Douaihy",
"title": "Scorched Grace: A Sister Holiday Mystery",
"price": 24.99,
"condition": "",
"cover": "/img/20/20476208320a6d0054578d561d717e3288107e8d7954cb63596a99064e3032c8.webp",
"cover_sha256": "d478394384f8c1c76ba88d031aa6751d25bc4307d012f4329c5316a8daeef0b2"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2212,
"slug": "papa-disquo",
"name": "Papa Disquo",
"suburb": "Newtown",
"state": "NSW",
"type": "record"
},
"counts": {
"pool": 111320,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2212,
"sample": "random.Random(2212) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "1487770",
"artist": "Leftfield",
"title": "Rhythm And Stealth",
"price": 74.99,
"condition": "Mint (M)\\n\\n \\n \\n \\n \\n Absolutely perfect in every way. Certainly never been played. Should be used sparingly as a grade. / sleeve Mint (M)",
"cover": "/img/4e/4e992da54767b477d871c2a1b492e88132b55b99ccff034a81cac1820bdff04b.webp",
"cover_sha256": "ae02289bd2c8c661423e9046c9efa2119156672cdc1211cf819d545f0c03d446"
},
{
"mint_id": "1508398",
"artist": "Djenga",
"title": "Djengastyle#1",
"price": 31.79,
"condition": "",
"cover": "/img/ab/ab054f7aa3132541a4a3a51576f4ea490ae7c9f77e85d60bc19b83e265a64178.webp",
"cover_sha256": "7f1e468c54c67b06ae8456d39e7c1c5d7e457607d0aedc34a01eb801dd4a204a"
},
{
"mint_id": "1542765",
"artist": "Peggy Lee",
"title": "Fever",
"price": 6.0,
"condition": "",
"cover": "/img/2b/2b093642b474ba7c45aae2cc62a7cca99547591f0c45c4760e50a0fc68b0aa5b.webp",
"cover_sha256": "91c2a5208181555d4b08cbd6ac0ce3cfbb4991a6311db149f44cee79b5c5d677"
},
{
"mint_id": "1954799",
"artist": "The Replacements",
"title": "Not Ready For Prime Time: Live At The Cabaret Metro, 1986",
"price": 82.0,
"condition": "",
"cover": "/img/55/55e9d553fb2041ea640a4eb00a58a4bef47378eddcf1a57f79a4a10f576dd614.webp",
"cover_sha256": "7f00b70321f5eba2fdd470665b10decb2df9ef83f174f2a03b2db9c30787b18b"
},
{
"mint_id": "1955503",
"artist": "Matt Bianco",
"title": "Matt Bianco - Whose Side Are You On? (7\", Single)",
"price": 19.52,
"condition": "",
"cover": "/img/f9/f9cd4ad402f62ac991df7f36d2855dee191b512a24ec04d5827f12095169dfa1.webp",
"cover_sha256": "aafcb923dc40d799cc414b0459e8957c4829aca40ba23edc4ed5afcdb10a45c4"
},
{
"mint_id": "2635771",
"artist": "Clifford Brown and Max Roach",
"title": "Clifford Brown And Max Roach \u2013 Study In Brown (E/E)",
"price": 70.0,
"condition": "",
"cover": "/img/c9/c9a9c4234428168908702c412d627a6c8f7825a6863169484d1eda44a4c1afd0.webp",
"cover_sha256": "7b6b29c56098d40fd9f05f4c6d1a5489fa75213b80e94690290fec805564393f"
},
{
"mint_id": "3153138",
"artist": "David Bowie",
"title": "Hours...",
"price": 45.0,
"condition": "",
"cover": "/img/84/846673771fb176ad71b7f7f3e457ad680a0c824628f61a0281bccb795ad82b99.webp",
"cover_sha256": "ebf22b64fae2020aa30d0575450be544c9dbd3de53424aa8a5546abe2afa10e8"
},
{
"mint_id": "3277085",
"artist": "Midnight Oil",
"title": "Armistice Day",
"price": 200.0,
"condition": "",
"cover": "/img/55/5532a8b151cf8f56caf98b030af7db763fc4e87757bb5448f71f7f6739f5d91e.webp",
"cover_sha256": "c8ed4ff585c085f5d740a8837f478a86c551d03c4ee61910178308057b1e6d5e"
},
{
"mint_id": "3277357",
"artist": "Re-Flex (2)",
"title": "How Much Longer",
"price": 40.0,
"condition": "",
"cover": "/img/d3/d364552e7f6c93a840a5b1a48565242f12880795c2eb5ba6b738f92eed550e80.webp",
"cover_sha256": "d248b9a05049f77636371c47ecbd78355cde097c53f2de18978423b1eb37d4f1"
},
{
"mint_id": "3293690",
"artist": "Boy Harsher",
"title": "The Runner (Original Soundtrack)",
"price": 59.95,
"condition": "",
"cover": "/img/31/318301c5bc4b8fd4366626ed6b4c8103f618d00dda190506aeb0dad9cce4e348.webp",
"cover_sha256": "c314fde1578285bae2b1b020bb2327f37b48c4c95b767510cc4c1517722fcb63"
},
{
"mint_id": "3511436",
"artist": "Les Paul & Mary Ford",
"title": "Bye Bye Blues!",
"price": 30.0,
"condition": "M",
"cover": "/img/c4/c4d274b76c271a48768da9a0b021162f85ab2715c3d95062567dd9c9512d8837.webp",
"cover_sha256": "bf4342ad4f41e0bf17aebf56de2f999eb87a95d9c6e008796ea9aa82ad79607e"
},
{
"mint_id": "4520905",
"artist": "Various",
"title": "Various \u2013 Now Generation - The Best Of The Indie Stuff (2 x CD, Compilation)",
"price": 15.0,
"condition": "",
"cover": "/img/ac/acc8fe4d33ac62785952d6f0c0a70cb7763467525b79c0d87bf4ef0e840458c1.webp",
"cover_sha256": "0ace606982d70fcb7b74d07dec9fd23cad45be8d29551be174e98ca0ba1f48af"
},
{
"mint_id": "5947485",
"artist": "Dark Funeral",
"title": "Where Shadows Forever Reign",
"price": 9.99,
"condition": "",
"cover": "/img/eb/eb4d0efa2a41c3b65ec6235fbc2aa26020c3fe983b20456ca7c8efd105b1eefc.webp",
"cover_sha256": "6f537236291d367a820011f0618baa2df2e52a8a2ca3c71e20a7c50e677707e1"
},
{
"mint_id": "5948394",
"artist": "Metal Church",
"title": "Metal Church",
"price": 29.99,
"condition": "",
"cover": "/img/a0/a042498efce32bfc3269ef673ca0b63a3f96f9aa843cca93d6be9a74052cc4fd.webp",
"cover_sha256": "bdb27429b4e91b7f35c75efb4f858278504ca98f6089d489e4cc80f032ad5506"
},
{
"mint_id": "6358606",
"artist": "Suede",
"title": "Autofiction",
"price": 29.99,
"condition": "",
"cover": "/img/93/93736e5bd94734edb57739d9791e56ec12413ce0c804f76b140a5bcfe786ee17.webp",
"cover_sha256": "4becb72dd4bd0ea191b86f9a6c110d3f2999d325f6371bde14a0e2f046c20593"
},
{
"mint_id": "6359222",
"artist": "The Dream Syndicate",
"title": "Ultraviolet Battle Hymns And True Confessions",
"price": 31.99,
"condition": "",
"cover": "/img/7e/7e94b01cfdb93906c6ec74152f144f3670bdb16fb2487966963194970b9775dd.webp",
"cover_sha256": "c92f4dfdeaa31d2bb8a4fc31db72f39c5676dc44e732842cd3e4b0faf30d2c71"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2287,
"slug": "little-bird-bookshop",
"name": "Little Bird Bookshop",
"suburb": "Paddington",
"state": "QLD",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2287,
"sample": "random.Random(2287) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "826912",
"artist": "Lifeline Queensland",
"title": "An Undeniable Voice - Tania Blanchard - BPAP - BOO009",
"price": 10.0,
"condition": "",
"cover": "/img/4c/4c1ae94cfa061e88568bba37d5b1f12eac3f2532a883876bdb10a5df8ff88f82.webp",
"cover_sha256": "e3108fbf847ae793e7bf263e08065408bc5418a17b98b08b9097a34c01efff28"
},
{
"mint_id": "1926301",
"artist": "Oliver Jeffers",
"title": "I'm Very Busy - Oliver Jeffers",
"price": 25.0,
"condition": "",
"cover": "/img/a9/a94712d1773ad0af48a4386e0c37ca07cebaef288fc760ddda939f6fd3c63e1d.webp",
"cover_sha256": "50950bebf870d7852161e4f06ee8e54992b4247ed5254b216aa1fe81e93f3a93"
},
{
"mint_id": "1926750",
"artist": "Chris Deville",
"title": "Such Great Heights - Chris Deville",
"price": 55.0,
"condition": "",
"cover": "/img/68/68b814d93f294a9772588ae4289aefdf04c36b4492cecca5d6bb99cf8a5535a1.webp",
"cover_sha256": "2acfffd12ee5e0743bc5ec9dfba9e9ee18e8e9d7c5c53841863b8e1213b05ab5"
},
{
"mint_id": "3275778",
"artist": "Movie",
"title": "Daniel Falconer - The Art Of District 9 Weta Workshop 2010 Hardcover Book",
"price": 190.0,
"condition": "",
"cover": "/img/45/458c58b0b3a87ae8e194d994ea5eb38c3ba6fe5716065e7678f6f13c4a197215.webp",
"cover_sha256": "a9fd7403cc3f92448b1e371288769fe880a878d866215551418366c5fe83b167"
},
{
"mint_id": "3310625",
"artist": "GAR",
"title": "Ruland, Jim - Corporate Rock Sucks: The Rise And Fall [Book]",
"price": 44.95,
"condition": "",
"cover": "/img/52/5243eb436f11806064f1ceef3c83f172637fb65ca948212aa55c39cf75644884.webp",
"cover_sha256": "2e94fcdce59df098f36fa73cf418d7ac02ad868772c4810a715e6fe25bf4029e"
},
{
"mint_id": "4296826",
"artist": "GAR",
"title": "Trowell, Ian - Throbbing Gristle: An Endless Discontent [Book]",
"price": 69.95,
"condition": "",
"cover": "/img/00/00d75f2d131aa20bca225c5fd7fc50b6b359bbd477e77c5708985cd7f38c19b4.webp",
"cover_sha256": "081eb3108bb8d0b46c219b9483de7c49bd8919c3fb40227205c4bc8d51d9ae77"
},
{
"mint_id": "6962945",
"artist": "Thames & Hudson, Limited",
"title": "The Iconic Interior - 1900 to the Present",
"price": 1900.0,
"condition": "",
"cover": "/img/33/33379e72faca924d9071e1c64aa0b119d0598e688351e08822cb09bc454ba0c9.webp",
"cover_sha256": "5ce2a83b5271615315f690da494fd7997a64cb92b3e59ed9629ac3007d79c17b"
},
{
"mint_id": "6963194",
"artist": "Taschen",
"title": "Modern Architecture A-Z",
"price": 49.99,
"condition": "",
"cover": "/img/8a/8aeca50552ea115efc40702e79f8399b65d93be2d56bc765305eabe968902a2c.webp",
"cover_sha256": "4004f83c636eb81e4f43dbe4812742fd6469337bd217c070a6702d508df532d6"
},
{
"mint_id": "6965345",
"artist": "Pavilion Books",
"title": "Learn Oils Quickly",
"price": 19.99,
"condition": "",
"cover": "/img/b1/b1bfa72db76bbf5bd5fe334b05afd3fb37e50bbfd7ac770c3271b21f05b4f809.webp",
"cover_sha256": "5731d74108c753a22473581eb874adaf8bfe215645b37458ef96fd8fabdd0d77"
},
{
"mint_id": "6966159",
"artist": "Phaidon",
"title": "Flower Colour Theory",
"price": 49.95,
"condition": "",
"cover": "/img/54/54a065c6881e4a4bab6ec0ff258667fca6a2ad6e432cef583be7b746c0d2cc46.webp",
"cover_sha256": "1fa732fd6305ae4f8493d5b9195302a1ca322838e94d4b82c6d9aaa2b0cd7760"
},
{
"mint_id": "6966277",
"artist": "Thames and Hudson Ltd",
"title": "The Library: A World History",
"price": 59.99,
"condition": "",
"cover": "/img/f7/f74b3ef81311913d7425197b46d40103266a04a1ca92b5d512ed963a0baadc1f.webp",
"cover_sha256": "d457549ddb5c0c8929f3e01a4e08eb592418beb8ebee4d9aae7e5483d8c60e29"
},
{
"mint_id": "6966390",
"artist": "John Wiley and Sons Ltd",
"title": "Small Space Decorating",
"price": 27.95,
"condition": "",
"cover": "/img/b3/b33e0fa30a9f27a2b562e2fa1efec8ba11debe9eae281d01aa89909f8310d237.webp",
"cover_sha256": "f88973841382d89735d95193e84d0755aeda79570f1d8864b91ea3972b4f58ca"
},
{
"mint_id": "6966784",
"artist": "Welbeck",
"title": "Little Guides to Style: The Story of Four Iconic Fa",
"price": 99.99,
"condition": "",
"cover": "/img/9c/9cdfeefe794106f5f7218cbd85e159a321b48a9ff12ac13351e654c6e07ee5ec.webp",
"cover_sha256": "23298d8f5a887c00c03b17524330e9ddb4d9955c85101fe22aae5eb7773bbf2e"
},
{
"mint_id": "6967561",
"artist": "Assouline",
"title": "Akris",
"price": 175.0,
"condition": "",
"cover": "/img/f6/f68c8fb642ca235e3914d7c02514756be30db273cdcff19721ba8756f0bf8ce9.webp",
"cover_sha256": "a4d90855c50817b1508afa006dd06b7a2947dabc621ba1e20d58c6ad50b19f90"
},
{
"mint_id": "6969361",
"artist": "Penguin Books Ltd",
"title": "RHS Encyclopedia of Plants and Flowers",
"price": 99.95,
"condition": "",
"cover": "/img/0a/0ac73b0839f17ab8a7f6f7121fac7d0ceebed02ad6e2b3afdb7fbb1f06efe45b.webp",
"cover_sha256": "ac714cf1e9a1cfd02359c4fecf7b0a81aad2d0b373061128290bde75729f775c"
},
{
"mint_id": "6994248",
"artist": "HarperCollins Publishers Limited",
"title": "Common Reader",
"price": 9.99,
"condition": "",
"cover": "/img/c2/c2b3bbe3b488d56231fd0d08da09f9baed14d40689a44e7689c85fe00d3789ec.webp",
"cover_sha256": "9601daadb52f90251e157604d058c3b5e1530142c3d9bddd07bd9237682a8f4b"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "toy",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2377,
"slug": "kidstuff",
"name": "Kidstuff",
"suburb": "Camperdown",
"state": "NSW",
"type": "toy"
},
"counts": {
"pool": 103530,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2377,
"sample": "random.Random(2377) over an id-ordered pool of 103530 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "28804",
"artist": "Microsoft",
"title": "Mx Vs Atv - Unleashed Xbox (Original)",
"price": 14.99,
"condition": "",
"cover": "/img/32/321c5fb9640a978f3fa1359b66738aa52edaedfa1795b3be740fd182ff1f281b.webp",
"cover_sha256": "b94d498d92031697fa95d6ecfcf6a9ec010fdd47fac576f3d0a526363d2c0041"
},
{
"mint_id": "28813",
"artist": "Sony",
"title": "Rapala Pro Fishing Playstation 2 (PS2)",
"price": 9.99,
"condition": "",
"cover": "/img/67/67b101df1ec77fe138f10ac27ba8cc66cc1cb2dbf5e81f88cee0fbf939da5516.webp",
"cover_sha256": "7e5210b3ea0ca4813f074b73c2038ba08393ea899e511e6f1758b769189b42e0"
},
{
"mint_id": "60762",
"artist": "Nintendo",
"title": "Mario Isalnd Party World Tour Nintendo 3DS",
"price": 18.0,
"condition": "",
"cover": "/img/2f/2f9a42b20cee79c42a28c682d4c639fc80ece33cbca99c117c1cb1b3ca3f79fa.webp",
"cover_sha256": "4874b34abb1701aa101a3c45e03a9f3437339443581d0b8b95b400b6033c77e7"
},
{
"mint_id": "64465",
"artist": "Ea Sports",
"title": "FIFA 15 Playstation 3 (PS3)",
"price": 2.0,
"condition": "",
"cover": "/img/c1/c11f60112c7a9f9f7bd0d7f35484d9ae2f5190e12256fdfb3979064c1ee5d9fd.webp",
"cover_sha256": "7688eaac4547cf774258d45ab562fce200a1ec409018197c6b428cd947899157"
},
{
"mint_id": "165601",
"artist": "Sony",
"title": "Spiderman Miles Morales Playstation 5 (PS5)",
"price": 45.0,
"condition": "",
"cover": "/img/28/283a69a97a440a47d75781a5d50472ed24ab5dc8f4a3eb3889176799387f4e65.webp",
"cover_sha256": "3217b3f98897c381afd73e8279213fc6d1641fa79dd9ca880b0847f5770e8769"
},
{
"mint_id": "370120",
"artist": "Nintendo",
"title": "Nintendo Switch Black Hac-001",
"price": 149.0,
"condition": "",
"cover": "/img/a6/a6d6d8e6e729d0f070f8fdeaf8861f34d1cbf59a8c1efea59ee9fa70a99dad14.webp",
"cover_sha256": "397f55f3823330cf39f8e3042844734e8abe841eab3a2481ce9f38e83e171433"
},
{
"mint_id": "387296",
"artist": "Sony",
"title": "Sony Playstation 4 (PS4) Slim 500GB Cuh-Zct2e 500GB Black",
"price": 129.0,
"condition": "",
"cover": "/img/f2/f2b807944d77b5510526d86ff301c8cfbecec09af1d0ab1200a9c0005171308e.webp",
"cover_sha256": "91e5dd3b1b27521958d49d3d16f0937ebe7515f9ea67b675b216d82cc53ba5e2"
},
{
"mint_id": "393994",
"artist": "Centenary Of The Overland Telegraph",
"title": "Centenary Of The Overland Telegraph White",
"price": 1.5,
"condition": "",
"cover": "/img/d3/d3f779eb8d0dabceeb16686aabdc898c806a712131123ca13d572218338c54e5.webp",
"cover_sha256": "7ac33d527d79364ce9c603a055c3e854aac5059091dd3e8ad3519c3413a605f5"
},
{
"mint_id": "525015",
"artist": "Nintendo",
"title": "Nintendo Sports Nintendo Switch",
"price": 12.0,
"condition": "",
"cover": "/img/7f/7feaff5ff33597bf09a8c3446b1d6e3fe38fdcf97c00d41a90160338291a7b57.webp",
"cover_sha256": "25a86d1a9a54ca45906670ae5fc927861ce16547370b5c4c29b226e0ef695907"
},
{
"mint_id": "1949276",
"artist": "Sony",
"title": "Call Of Duty Black Ops Iii Playstation 4 (PS4)",
"price": 5.0,
"condition": "",
"cover": "/img/6f/6f85936b134685b41ad3c8decbe8ac3627a5d843f2ee44279422d5df569c5844.webp",
"cover_sha256": "a34785f11e44fed7a0252e8343467b1a5844407e52c9b7fce643191a45fde974"
},
{
"mint_id": "2144718",
"artist": "JIM SHORE DISNEY TRADITIONS",
"title": "Jim Shore Disney Traditions - The Lion King - Remember Who You Are Storybook",
"price": 201.3,
"condition": "",
"cover": "/img/ea/eac790a6f47bb5d8eb99ea2daf64be38b0cc674a1da124c4b3fa2ace582d3d47.webp",
"cover_sha256": "6fa8d198a3b9b442a5c3e9a82d825e2d15f4906f6d3b01574fe3c987971e9953"
},
{
"mint_id": "2145450",
"artist": "Games Workshop",
"title": "Games Workshop Warhammer 40K World Eaters Codex",
"price": 92.63,
"condition": "",
"cover": "/img/cf/cf95e77db0304ccf62e4c5663fa3adb3c0db2dd7d1a9aa78642f132b113926b1.webp",
"cover_sha256": "251348484eddf14b4eeb3b0f7cf0d08894da15793ee6129a5fa822cc576e897b"
},
{
"mint_id": "4720215",
"artist": "Nintendo",
"title": "Pokemon Sheild Nintendo Switch",
"price": 49.0,
"condition": "",
"cover": "/img/a1/a1e66f27e46bcf460b92c3128c2dd235d51395c7bc1431ed5b27d4c7c369b0ed.webp",
"cover_sha256": "9530c88a698f9cee05330d3726311c6804a0657cdc6d1f40addbe1eec4def169"
},
{
"mint_id": "5522185",
"artist": "Pop!",
"title": "Pop! Minnie & Mickey",
"price": 39.0,
"condition": "",
"cover": "/img/fc/fccdd7b3a3fa39be228457d690183f4c3109e26cd28b02b7829e094ffe9b715d.webp",
"cover_sha256": "d4c9723ac3e8dc9be922c37d4b1d6190c6acfafc97da3001c6f4b63a0c040109"
},
{
"mint_id": "6812211",
"artist": "AK INTERACTIVE",
"title": "AK Interactive Wargame: Medium Earth Enamel Liquid Pigment 35ml",
"price": 10.99,
"condition": "",
"cover": "/img/f9/f966eadced9915d9c2432f4115a6a678df58033949dbc68e01389ecaca42f9c5.webp",
"cover_sha256": "8938ac822f96b29ddb1bc54d509542a858f45d279530b80abc56c33c8a2c3f6a"
},
{
"mint_id": "6858153",
"artist": "Awaken Realms",
"title": "Nemesis Retaliation: Core Box",
"price": 250.0,
"condition": "",
"cover": "/img/16/1696a262ad7721387dcde52af3953f949db8675b2e78bfd8ddf6def03d3e1a6a.webp",
"cover_sha256": "31f4acbc4e4b8110c0bdedfad8b296413f36280657c79ce0d7270593227fb4d8"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 247,
"slug": "the-vintage-record",
"name": "The Vintage Record",
"suburb": "Annandale",
"state": "NSW",
"type": "record"
},
"counts": {
"pool": 111320,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 247,
"sample": "random.Random(247) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "1383351",
"artist": "Graham Parker",
"title": "The Real Macaw",
"price": 15.0,
"condition": "",
"cover": "/img/a5/a5e17e1d42a8b0364a76c68ce5226e93b5e8fb91d3f5b874d7c98674a7a9b9b6.webp",
"cover_sha256": "473cc49b2c14f52bce31fc735c1fc9e5b44c3b882bb6f6a0ea9ae968cc6d329a"
},
{
"mint_id": "1449829",
"artist": "Lewis Capaldi",
"title": "Survive",
"price": 59.98,
"condition": "",
"cover": "/img/19/19ab0c71f6629b1ac6623681c9b4bcca258610cfcdd10785fcccb9a8acf0cb4d.webp",
"cover_sha256": "15e8797a6a85187a5b94f5a089b3c955ade756836ae44cc87a160c56352ea03f"
},
{
"mint_id": "1501649",
"artist": "Stefano Di Carlo",
"title": "Eclissi",
"price": 25.25,
"condition": "",
"cover": "/img/97/97cdc8918bab3da8d35f4b9e15b43f09f5e52d0e692dffe05288862f33cf0550.webp",
"cover_sha256": "7fe773b5282b1beaa58f91d95e32fb31c8390d8a3f777225c60c9258f706f8d9"
},
{
"mint_id": "1572697",
"artist": "Peter Nero",
"title": "The First Time Ever (I Saw Your Face)",
"price": 10.0,
"condition": "",
"cover": "/img/38/388c26e4edc901c12df6a75f129bcd30101619849f6fd188984a63a19928e6ff.webp",
"cover_sha256": "4b7e88ae7bd0587346f66b8dd7a3c5967d976715d880f0c4660852657c79dc2b"
},
{
"mint_id": "1923720",
"artist": "Couch Kids Antidote",
"title": "Distasteful Character Assissination",
"price": 35.0,
"condition": "",
"cover": "/img/6c/6c4a268fb6f807c7eae7e06ea177c2769b4ffef09df49f4eb700ebc0af8875f4.webp",
"cover_sha256": "9348f916e6eff23380ce8ce207ee4e3e6052e5d6eb3c139156b8907336cc7d3e"
},
{
"mint_id": "1954310",
"artist": "Peter Blakeley",
"title": "Crying In The Chapel",
"price": 8.0,
"condition": "",
"cover": "/img/5e/5efde08c8fee915fb2bfa1801176cb435260bd9efb881c1e59eb217407d888f4.webp",
"cover_sha256": "6e2cd5c775d7c2ab3de98f32f1a85c3cfc50a758519bc013a09df8118ca79049"
},
{
"mint_id": "2638666",
"artist": "Frank Tovey",
"title": "Frank Tovey \u2013 Tyranny And The Hired Hand (VG+/VG+)",
"price": 20.0,
"condition": "",
"cover": "/img/b1/b152c2d42551ced179ae0c46bfc1842cabbefec5cb7d68e38b7fc38e51023a72.webp",
"cover_sha256": "e038c4127290295dacaed5f5a8288f921b232f9f8c8f87a52ed36ae652816b06"
},
{
"mint_id": "3149798",
"artist": "Animal Collective",
"title": "Sung Tongs",
"price": 67.0,
"condition": "",
"cover": "/img/0a/0a7ac56ba89d603b40b1d9e790948d7f17dccfb41d70fc7dca3648ef7c459b97.webp",
"cover_sha256": "bb1be9ec044229a1653731cff6706051e99490edbad7167d62fbb605a0eb5334"
},
{
"mint_id": "3151476",
"artist": "The Replacements",
"title": "All Shook Down",
"price": 45.0,
"condition": "",
"cover": "/img/18/184dc78017d465d34d98f6b82d92b4e4bc3da83120366e8606fcf9b177878599.webp",
"cover_sha256": "fb44a704f95eaf28e4e1cda5c48ea32163feffb4735f3db6d30e5281022a8eae"
},
{
"mint_id": "3153341",
"artist": "Smokie",
"title": "Have You Ever Seen The Rain",
"price": 55.0,
"condition": "",
"cover": "/img/4b/4b0e7344a6e75c04ee687cd5f613f3b0a3f2acbf995330263184273e840c82d9.webp",
"cover_sha256": "2f4272e3987a9399d508543be6ab21af5f12746cc4d68b4d0137619006dbbb22"
},
{
"mint_id": "3277109",
"artist": "2Pac",
"title": "Strictly 4 My N.I.G.G.A.Z...",
"price": 140.0,
"condition": "",
"cover": "/img/68/6849f5e003740fb4564a869f2bc0055133d12e814f75aa0692d7b26deda64abf.webp",
"cover_sha256": "5c9503dd85edd8df7040a63f586e4e07ba7d8241a6f9329598f2b35d06cd25fd"
},
{
"mint_id": "3308281",
"artist": "Faith No More",
"title": "Introduce Yourself",
"price": 9.95,
"condition": "",
"cover": "/img/20/2036577f50d3db1fd9c8f3a4279fc8ea80952c47ec098c3e2194719f00d59bac.webp",
"cover_sha256": "0c7749de6af9d551cdcbb7578dd13dfe3a35d6318df373a2d4fb3aad18e0478a"
},
{
"mint_id": "3415870",
"artist": "Simon Joyner",
"title": "Coyote Butterfly",
"price": 45.95,
"condition": "",
"cover": "/img/a5/a537149ba3b52616df1ee9d478f6ab109b431ade302111169ded6df4b9b04e32.webp",
"cover_sha256": "edd3e53ef94ce8c7eb613aeec200a2a72c396881ea46bcfb4b24933e43445262"
},
{
"mint_id": "5943968",
"artist": "Midnight Oil",
"title": "Armistice Day: Live At The Domain, Sydney",
"price": 27.99,
"condition": "",
"cover": "/img/e4/e468090b8d767b5f6b842c3e1e48b9048260d99bf86cdb0864ff557352595f20.webp",
"cover_sha256": "63a57eff6be9e2ff18d8b98201581abe99e9da0fbc1d74e1dfbc4446547bac84"
},
{
"mint_id": "6010664",
"artist": "Bob Dylan",
"title": "The Freewheelin' Bob Dylan",
"price": 48.0,
"condition": "",
"cover": "/img/1f/1fdb214e0a3b6536f3924708de79625e5425a9ec266af68e8f9e95c19d6f4dbe.webp",
"cover_sha256": "ef6f7e2d422a60343ecdb809ce8d1fbc1dec9ae7796b8e29795dae0c3cd3bd7b"
},
{
"mint_id": "6537917",
"artist": "Metallica",
"title": "The Videos 1989 - 2004",
"price": 9.95,
"condition": "",
"cover": "/img/a5/a5848b431ae941a385374327e051650020fea1af05992ea8658d83eefcfb6411.webp",
"cover_sha256": "5682f8419fb07519a902c4b987ad2bb0b519be5bc873e9b09452d9d88795d6d9"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "toy",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 2506,
"slug": "toys-and-tales",
"name": "Toys and Tales",
"suburb": "Newtown",
"state": "NSW",
"type": "toy"
},
"counts": {
"pool": 103530,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2506,
"sample": "random.Random(2506) over an id-ordered pool of 103530 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "9361",
"artist": "Computer Game",
"title": "Fashion Factory For The Sims 2 Orange",
"price": 3.0,
"condition": "",
"cover": "/img/c7/c76b30530cd4894ae352be888a9cc2cf55579d2bcc5b7317ee8c054d6cb2283b.webp",
"cover_sha256": "318d9e6db165e5bb80c703f31ba091a64ad8e3907f8f4c45d33fc5f17bc95949"
},
{
"mint_id": "12503",
"artist": "Lominger International",
"title": "Lominger International The Leadership Architect Red",
"price": 79.0,
"condition": "",
"cover": "/img/51/51c80586a815d73574af47aa1b6ce2478428c1edd55a4383bfec597656b68ec1.webp",
"cover_sha256": "76edc00524694489a583d2778993d975c2f9eb308d0e9ed72bd23f5932140e47"
},
{
"mint_id": "15800",
"artist": "Generic",
"title": "Generic Book Ends With Money Box",
"price": 5.0,
"condition": "",
"cover": "/img/65/65d3a21975499b28db38ebfe111b705e7865d3b0be99814d861f2ccf8b778b14.webp",
"cover_sha256": "c1214ba2d266429da544bb832a3d9e0f393d2d31e381a3756ed4b79c6200681b"
},
{
"mint_id": "54954",
"artist": "Sony",
"title": "Tom Clancy's Rainbow Six Vegas Playstation 3 (PS3)",
"price": 6.0,
"condition": "",
"cover": "/img/74/74213ac784897a524b7ab82007b6748c2eb385576231f635e3b543957473d423.webp",
"cover_sha256": "8e5f299280ee12ff400b4b851428701daeabc794c1517b71b1f2a5f23e39f2af"
},
{
"mint_id": "122102",
"artist": "Funko",
"title": "Funko Eivor",
"price": 7.0,
"condition": "",
"cover": "/img/f3/f3b1e24ce1389dddd6f7a2772636a8f1ad99f62aae9a74254575c05187182af2.webp",
"cover_sha256": "059b04ffe5583a0f9ddcb42bc3d278e1b53d9b3f590ee20c791c4fd5dd9c89dc"
},
{
"mint_id": "309175",
"artist": "Microsoft",
"title": "Assasins Creed Black Flag Xbox 360",
"price": 5.0,
"condition": "",
"cover": "/img/02/02c464c486c57afb618758401535b75c14592ffb4254a48a1ee2e49b600e6c3e.webp",
"cover_sha256": "4cd9ea1588caeb315092d427bf30f3a9259c74710f6de4e200d4e7c005288481"
},
{
"mint_id": "408285",
"artist": "Nintendo",
"title": "Pokemon Legends Za Nintendo Switch",
"price": 69.0,
"condition": "",
"cover": "/img/9f/9fc8d6e1d157632750afe80587ec291a0d8cfca7baf46e44f74170d5f61d03ea.webp",
"cover_sha256": "249f70388e11675a7e25bfcda8ed6c5bbd49dacadb0383bb1d287efe7624b63d"
},
{
"mint_id": "418917",
"artist": "Funko",
"title": "Funko Black Adam 1232 Funko Pop Figure",
"price": 8.84,
"condition": "",
"cover": "/img/f5/f539c714858ec0e9e881e4d13c6e65992a2ff143a297bf562ace929deb477ae6.webp",
"cover_sha256": "9d5b53ecdf6b90929c18f4f968fa95644d3d2ed7042dd08588e8e38f5a718f87"
},
{
"mint_id": "494201",
"artist": "Microsoft",
"title": "Battlefield 2042 Xbox One",
"price": 5.0,
"condition": "",
"cover": "/img/6b/6bb96af151a316c7a96a3db6f233f6e563ac5db5d465ff6054de0b0e12507330.webp",
"cover_sha256": "d58f5b929e8ad4d4fcd997234bd30ce82e6e1af57a8dd31cd7c2c670e5656977"
},
{
"mint_id": "508371",
"artist": "Sony",
"title": "Sony Playstation 5 (PS5) White",
"price": 75.0,
"condition": "",
"cover": "/img/32/322b224ec52694dc954d8b3d38f3012d23b8f8bb71d43c5af3b9df59861e06f5.webp",
"cover_sha256": "97fa6231f51d15d69870fb9723f242b0fb81b5693a53acc9439a537b4d3084bc"
},
{
"mint_id": "587671",
"artist": "Microsoft",
"title": "Destiny 2 Xbox One",
"price": 2.0,
"condition": "",
"cover": "/img/62/62f668a8ca577df9b829726b24f8d7fb9f53e12d2d5386c680bdc9edb5e2b93e.webp",
"cover_sha256": "badb04ec6a53c7448c47b4298ebe8df8ba1513f61d046872c3a5526f80be4c00"
},
{
"mint_id": "5922574",
"artist": "STORY OF SEASONS",
"title": "STORY OF SEASONS: Grand Bazaar",
"price": 84.0,
"condition": "",
"cover": "/img/de/de779ee1d218a78bbf750f66d95c72052fa78aa0dfaf58a46b0521e134aec19c.webp",
"cover_sha256": "0cc940258c5bf22c2d13fda3e42b2c225017cc204b484da9843e05ed56c21021"
},
{
"mint_id": "6731996",
"artist": "Titan Comics",
"title": "Little Nightmares: Descent to Nowhere #4 Stokely Cover",
"price": 10.0,
"condition": "",
"cover": "/img/92/92c9e06b06da9aceec814cee0716a0c72e094d46d2efd7f13e891f12ea68382f.webp",
"cover_sha256": "c048b53fcc37eb4239c0e18fb3d2267b7cac3c82005de1740768fe26070b0718"
},
{
"mint_id": "6857889",
"artist": "Magic: The Gathering",
"title": "Gorma, the Gullet (Borderless) [Commander: Secrets of Strixhaven]",
"price": 6.5,
"condition": "",
"cover": "/img/7d/7dbe757976ebf284360735eabd7f1775c6b4491b38e76bab6818a82865f7ce2a.webp",
"cover_sha256": "4c606591fbb3c3a680b675e242692662bbb7640be795f6d02b2727d8d9b8cca8"
},
{
"mint_id": "7229680",
"artist": "Razer",
"title": "Razer Cynosa Chroma BlackKeyboard - GamingCairns, QLD",
"price": 29.95,
"condition": "",
"cover": "/img/85/855f2f00f6556093e51ae9d975f30056f6a3a5fdc5a59795b1afde5d6a437d94.webp",
"cover_sha256": "01cda7d41f70fa2705486a80142946c1ec9849c7149a34f88b402213b7a37790"
},
{
"mint_id": "7267697",
"artist": "Jada",
"title": "Jada Ultra Street Fighter 2 The Final Challengers M. Bison BlackToyMorley, WA",
"price": 129.0,
"condition": "",
"cover": "/img/6e/6e6880e2f35bb8e712bae8503f5fc4d6d23d2755560104fa9e8f8917d3c1ef66.webp",
"cover_sha256": "66fba64f4a5d2f18c9734823eeeb65b857bac2d543ac0a5cce5d7618b9c7a8b9"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 608,
"slug": "goulds-books",
"name": "Goulds Books",
"suburb": "Newtown",
"state": "NSW",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 608,
"sample": "random.Random(608) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "757983",
"artist": "Lifeline Queensland",
"title": "World table - The Australian Women's Weekly - BCOO - BOO052A",
"price": 8.0,
"condition": "",
"cover": "/img/ac/ace0938ba44f8e81d41082bd871c1161fd6395abe365baccc5e699dfe3df2681.webp",
"cover_sha256": "a76b0703dfc0cfe6874e6a2356ab3bb88f51f73d4ace1450e433b93e3b571544"
},
{
"mint_id": "1932075",
"artist": "Brandon Stosuy",
"title": "Stay Inspired : Finding Motivation for Your Creative Work - Brandon Stosuy",
"price": 25.0,
"condition": "",
"cover": "/img/ad/addcab851935b0a57c54b7f65365621c6f176afb83719a7a76cb3634ded83f7c.webp",
"cover_sha256": "4e33ec28b3999d8a1c2f500371ce16d4939f10ab832701c3125f29bbe743da50"
},
{
"mint_id": "1933916",
"artist": "Monocle",
"title": "Monocle Guide to Shops, Kiosks and Markets",
"price": 85.0,
"condition": "",
"cover": "/img/65/651c3662466cb8f3bd33a3e6611315a150d9c5b0f6880608f28bd1f34a248a84.webp",
"cover_sha256": "60cb2bb9f732dd279bac87b090967d5f9dcf936a5ca8f69a061dbb0b9b5d257a"
},
{
"mint_id": "6109669",
"artist": "TTTISM, Nick Schonberger",
"title": "TTT : Tattoo - Nick Schonberger",
"price": 110.0,
"condition": "",
"cover": "/img/57/574544194726c90e9db88686191e1ceaff2d8b525349dc06572691e412c57619.webp",
"cover_sha256": "a995ace6f2071371141acf661d04a9bc2b9ecbf53ca62b0b88e8931524bafaa7"
},
{
"mint_id": "6306539",
"artist": "HACHETTE LIVRE NZ",
"title": "MOKUMOKUREN | THE SUMMER HIKARU DIED, VOL. 3",
"price": 29.99,
"condition": "",
"cover": "/img/39/39a47170fe16813a50ecad36f662501d8891558d219d6d7c8906156671f612b3.webp",
"cover_sha256": "c86bd2d5ebfb295bfd46222a9617486deff90d27accd9bfefc2ee54cb975ef68"
},
{
"mint_id": "6962465",
"artist": "NewSouth Publishing",
"title": "David Jones : 175 Years",
"price": 175.0,
"condition": "",
"cover": "/img/d6/d63f9299cb61f5a7bfb8a67010c7916066d343a1ad942a11a8bcecdfc5a7f329.webp",
"cover_sha256": "3c356fe9ce237828975bb3afb1f566143cea152696ff63423aba8d990e39fbfa"
},
{
"mint_id": "6963671",
"artist": "Penguin Random House",
"title": "My Amalfi Coast",
"price": 39.99,
"condition": "",
"cover": "/img/6f/6ff81b80d0ced7ee2ef47cef5225d3c207bc86e6188ccdfc4f70a3ab5afa24fe.webp",
"cover_sha256": "dd9dcd3ba5d4aaeb5d1f4bd05ee2a3c6a65a0374aed81fb72c180f853d9431ff"
},
{
"mint_id": "6963722",
"artist": "GMC Distribution",
"title": "Crocheted Accessories",
"price": 29.99,
"condition": "",
"cover": "/img/01/01a6800024c350f12183fcc414c624a20ce197e856b62ba8649a7b7128d4ba19.webp",
"cover_sha256": "f4fe05f13bf71043e327b58cecb1313e41b94d52f480fb5ef86664bba2c440d7"
},
{
"mint_id": "6964521",
"artist": "Sirrocco-Parkstone International",
"title": "1000 Monuments of Genius",
"price": 1000.0,
"condition": "",
"cover": "/img/7e/7ef563ba157eed5d7cc36cf71009073e9ba79b1afe5b5496346a9d222e33d611.webp",
"cover_sha256": "ddf38409be733de111a28d874ff506a4f82afd926d7eb49c186bca7482ca8a03"
},
{
"mint_id": "6965023",
"artist": "Manna Trading Pty Ltd",
"title": "Grafting and Budding for Australian Gardeners",
"price": 29.95,
"condition": "",
"cover": "/img/93/934e9fd8fc75325cf1ebf2c8ff8f40bc7c1c1175e3967da313101fbb64046ad2.webp",
"cover_sha256": "a4af5612139bdae39116873df50290d8e4f82f1406115c2079863e4ec3fc98f6"
},
{
"mint_id": "6965459",
"artist": "David & Charles",
"title": "Sashiko 365: Stitch a New Sashiko Pattern Every Day",
"price": 365.0,
"condition": "",
"cover": "/img/c2/c2a155ad3dc42a7e38708cbcd43e93354e103f0a7f0946e85752a3dc064ee5e8.webp",
"cover_sha256": "8ff454026e58e54537bb63cb7f455dfce0765f88b9c095e65924203eba91350d"
},
{
"mint_id": "6966582",
"artist": "Jacqui Small",
"title": "The Quintessential Grooming Guide for the Modern Ge",
"price": 39.99,
"condition": "",
"cover": "/img/cb/cb19bac8b0b0ac46034a5728166e4f8ec18e118501000f9d09ec787a6082324b.webp",
"cover_sha256": "345110caa571a0e181074181445b128845a3125c557b155d3279ae9fd90106c7"
},
{
"mint_id": "6966740",
"artist": "Fremantle Press",
"title": "A Place in the Country: A Guide to Creating your Pa",
"price": 49.99,
"condition": "",
"cover": "/img/60/605a479f9f4b0f32ab9026a1afe040a684ed8af3304a5c61dc0a5bbef866edec.webp",
"cover_sha256": "be3a1c56e82c88f62cb7a0e90ccd1e6988138921292921574557b4d6041d5b5a"
},
{
"mint_id": "6967976",
"artist": "Thames & Hudson Australia Pty Ltd",
"title": "Tiwi in Paris",
"price": 26.99,
"condition": "",
"cover": "/img/9d/9d7ccca33e2451c6522af8d42e5f0bd0c5b4f8d1241aeae28276649613b929df.webp",
"cover_sha256": "c13e973c38ce4199c627250e43c8c7483c83f3d0737a682971e43b3f68153419"
},
{
"mint_id": "6968969",
"artist": "Quarto US",
"title": "The Ultimate Bushcraft Survival Pack: Tips, Tools,",
"price": 39.99,
"condition": "",
"cover": "/img/01/01fec50a9c60f7b5da220934aaca1cbf0f8b66c87e187c84fe4483c51a3b83fe.webp",
"cover_sha256": "9645a32cdc6a8a2d3e18058b917ad792875cf1f428100a54babad9a0296b11bc"
},
{
"mint_id": "6999820",
"artist": "Korimco",
"title": "Dingo Puppet (Lil Friends)",
"price": 34.99,
"condition": "",
"cover": "/img/02/021a18c34f3f2e8597cfb58cb899528cd5030c591ac9478ebb4740a443531fa1.webp",
"cover_sha256": "3c08e5fead3f217c9a00a131918cfcbc08ce7a12eb23622122976bcc8b8cd07b"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 717,
"slug": "co-op",
"name": "Co-op",
"suburb": "Glebe",
"state": "NSW",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 717,
"sample": "random.Random(717) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "822558",
"artist": "Lifeline Queensland",
"title": "Bookery Cook - Jessica Thompson, Georgia Thompson, Maxine Thompson - BCOO - BOO043",
"price": 10.0,
"condition": "",
"cover": "/img/3e/3e3713560ee9ee7fde773d14ca831b3dd92693b5ca2b018cfe88a379a9d200f7.webp",
"cover_sha256": "2287b2872062d1c0827e77d080ea536629605223c6bcef56cd29cff31be93adc"
},
{
"mint_id": "824813",
"artist": "Lifeline Queensland",
"title": "QCE Biology: Units 3 & 4 - BTEX - BOO038",
"price": 12.0,
"condition": "",
"cover": "/img/9e/9e9507e4a839e2f847510eac9938fa6d556f0768a7cbc36bb4b15f0df5c5ab37.webp",
"cover_sha256": "1d9329bad1e32684e88065d63cff24bfe24c0bcb6ef5ec02365d8a56cc8bddad"
},
{
"mint_id": "829668",
"artist": "Lifeline Queensland",
"title": "Death in the Arctic - Tom Hindle - BPAP - BOO017",
"price": 10.0,
"condition": "",
"cover": "/img/e4/e4afcfaeb3a42a4854858840cf2e7eb1e91d3845b659d8436445d83442c7d674.webp",
"cover_sha256": "08da37f6987ddf3c6ab906fecf38100089a087f5d3643bbbf7f47c361ab789b9"
},
{
"mint_id": "831472",
"artist": "Lifeline Queensland",
"title": "Absolute Power - David Baldacci - BPAP - BOO022",
"price": 8.0,
"condition": "",
"cover": "/img/9d/9d43ef1a480e273c41897ac990572471297f32ef5dcf9991a7fa94997c430ca6.webp",
"cover_sha256": "8af7c752c7c42435a0a2469bade3ef5d43740bafbeee14e251917285b6274043"
},
{
"mint_id": "3267613",
"artist": "Rock-Pop-Alt",
"title": "Rolling Stones \u2013 All The Songs: The Story Behind Every Track By Philippe Margotin & Jean-Michel Guesdon Hardcover Book (2016)",
"price": 90.0,
"condition": "",
"cover": "/img/4b/4bfdec0f95d90afb76af74b57c9a04f0f06a074e5eff044cb0af52096f57f139.webp",
"cover_sha256": "03a58cf1fd78a9d502a70d5574dfd43e4b8b4ac704060404b7db14019dad65c0"
},
{
"mint_id": "6728479",
"artist": "HarperCollins Publishers",
"title": "Spellcaster - an intense enemies-to-lovers romantasy with steamy heat, magical creatures, fast pacing, and stakes that will keep you hooked.Dive into a world where love is as dangerous as power -- and nothing is ever as it seems. Pre-order now.",
"price": 32.99,
"condition": "",
"cover": "/img/50/5097e687651a2109b3c325f2239784d834dbd621ef4f425b8b356e0c18e4899c.webp",
"cover_sha256": "c358f03b48fb894e5784aa4a65bc23bf0c0bd93e4ff8924c1e816e926e31bb3f"
},
{
"mint_id": "6728802",
"artist": "HarperCollins Publishers",
"title": "The Double Dangerous Book For Boys",
"price": 45.0,
"condition": "",
"cover": "/img/97/9719f12de67ac2ff4cdc3a49cb2430dab34175becee6285b9cb1bddfce034422.webp",
"cover_sha256": "b97890acfd2c617bc0b1fef89c8ca0c5e393d0529be0ec861c9cbef7e7591419"
},
{
"mint_id": "6729273",
"artist": "SCHOLASTIC AUSTRALIA PTY LTD",
"title": "Every Thing We Keep (Revised Edition)",
"price": 18.99,
"condition": "",
"cover": "/img/1a/1a52115b5562b0f07d2255764cce3aa7f79e9f7d7b005f6e045b53c72176982e.webp",
"cover_sha256": "ea946863a8e87b59f68713d164ce43bfc8565592ebf22574a956858bcb54a66a"
},
{
"mint_id": "6963103",
"artist": "Gibbs Smith, Publisher",
"title": "Make Yourself at Home - A Guide to Decorating with",
"price": 60.0,
"condition": "",
"cover": "/img/9b/9bd903c0528c37ac306dc23b32152d4ed77783b4e6ead9f87cb9fbea535bfc38.webp",
"cover_sha256": "631fb35f62ded573ec61f2109b4e89c02645712450cefc4a41993e675e04ba38"
},
{
"mint_id": "6963297",
"artist": "Quadrille Publishing Ltd",
"title": "Liberty Book of Simple Sewing - A Collection of 25",
"price": 25.0,
"condition": "",
"cover": "/img/df/df37bfcd20d3978d629b628d7dbcb80288006253045e3c1ef99cc842e25ad339.webp",
"cover_sha256": "4a0c206f71bbb40575ea0b235eba3a27c26abea118cbb74a1c793219fcb507e5"
},
{
"mint_id": "6965982",
"artist": "Thames and Hudson Ltd",
"title": "The Artists' Colour Box",
"price": 36.99,
"condition": "",
"cover": "/img/e5/e5df65d0490c9b91456784d68d5a2ae48b53d00fd03c3e26e56efc9c50db16a4.webp",
"cover_sha256": "be6888e9a23098cefac2198fa14d61d118cee94c3642f256ae28984f3c239b1f"
},
{
"mint_id": "6968195",
"artist": "Taschen",
"title": "Erotica Universalis",
"price": 35.0,
"condition": "",
"cover": "/img/95/95091ad5b6cdad98684c161fefe425a17fdc97ce43268869ed44fa1daf734f33.webp",
"cover_sha256": "e1fd0d3dcca7571d16ddebd501824c3fd761608a80c0521fe2d03bbaaefb18fa"
},
{
"mint_id": "7004444",
"artist": "Rizzoli International Publications, Incorporated",
"title": "Tamsin Johnson: Contrast, Space, Harmony",
"price": 145.0,
"condition": "",
"cover": "/img/23/23ca5ea9bfba4a6ada3048cc898e6159b52d3965167c4165f2b1b2872369ae27.webp",
"cover_sha256": "2ec6d2c6b84ca0d0c03c0ec848c198cd9801a66fe77c405dfc17c9c09fb73bd9"
},
{
"mint_id": "7035606",
"artist": "Michelle Tea",
"title": "SLUTS: An Anthology",
"price": 32.99,
"condition": "",
"cover": "/img/c0/c0f4d005cfb96d69f5f96c21ce65406315bcbfde559673370bef0480cf7041fb.webp",
"cover_sha256": "72ab5f8a92ad4c374ddb014cf86b59d88a3ad901d962d27fc4dd2300798fa207"
},
{
"mint_id": "7035682",
"artist": "Elly Griffiths",
"title": "The Postscript Murders (DS Harbinder Kaur Book #2)",
"price": 22.99,
"condition": "",
"cover": "/img/65/65c4b2ab4515e9edb3ff9180d101e3f0fd3da121a2a1bc048dc2591dc9947496.webp",
"cover_sha256": "8977168f1e1939d973ed840891aaddac895f11510555394956c8ea72672572a5"
},
{
"mint_id": "7035751",
"artist": "Claudia Cravens",
"title": "Lucky Red",
"price": 24.99,
"condition": "",
"cover": "/img/81/811c4f096e9d52dcf298f90fb2e0df1aca8dd6272e5942abbe7c58fa01ea8f2f.webp",
"cover_sha256": "93328ec73724607a19d28f6a1825d28192075b95fc7e9b8143bf706a10f1ceb1"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "toy",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 767,
"slug": "presents-of-mind",
"name": "Presents of Mind",
"suburb": "Paddington",
"state": "QLD",
"type": "toy"
},
"counts": {
"pool": 103530,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 767,
"sample": "random.Random(767) over an id-ordered pool of 103530 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "41771",
"artist": "Panini",
"title": "Panini NBA Prizm Rookie Card Joel Embiid 76Ers Silver",
"price": 39.0,
"condition": "",
"cover": "/img/50/50bd880dd2513a82588e77a3aa1c45a8e9e02d5fd330ff3653508a878ed9447d.webp",
"cover_sha256": "60aba06dbfa21eba53eb8802e3faaa0989c6d18ba5057782152cd93579fd16cb"
},
{
"mint_id": "41875",
"artist": "Funko",
"title": "Funko Pop! Heroes Wonder Woman (2018 Fall Convention Exclusive Limited Edition)",
"price": 39.0,
"condition": "",
"cover": "/img/5c/5ca2bc808e7ef45ad5b7306621637640fbbe61ebb52bf28052c26b4979616fb8.webp",
"cover_sha256": "2779f480de7748a7151c0d52a784b21cdc62cf29b5717e549d0f40ae4a368b1f"
},
{
"mint_id": "69556",
"artist": "Sony",
"title": "Ufc 2 Playstation 4 (PS4)",
"price": 9.0,
"condition": "",
"cover": "/img/60/60933e4f325dd554b5b3f8f46f34cbedbd79b093d46bfc40d339e2768e236c4d.webp",
"cover_sha256": "3f0ffb779aeb94a060fcf5c0cd411ce130846350caf9e2cfdc55c2c20387b597"
},
{
"mint_id": "169013",
"artist": "Marvel",
"title": "Marvel Cyborg Spider-Man",
"price": 10.0,
"condition": "",
"cover": "/img/00/0075f2ce7c73ad06aacc4ffd106deb5396d8706cb4f42b9b4a78a3146f93f380.webp",
"cover_sha256": "75ec68b6784dd7f96fd85eae306c30484ce1a9a53604c08fd9c5eab5022d543d"
},
{
"mint_id": "331293",
"artist": "Microsoft",
"title": "Microsoft Xbox One X 1TB 1787 White",
"price": 199.0,
"condition": "",
"cover": "/img/7b/7babd19a1a271919236c5a8c5a18419c3b9895b13752ec94ffdca7b29fc09366.webp",
"cover_sha256": "ea8ef16b7fab56cd2f77fcdc9a9fa4479115245d3cae2ee839c149cbc764b18d"
},
{
"mint_id": "401046",
"artist": "Sony",
"title": "Call Of Duty Black Ops Playstation 3 (PS3)",
"price": 5.0,
"condition": "",
"cover": "/img/0a/0ae26d43b8dd89c8500ee9785aac8644f66ab5f0437a94cb702216b7cdaf9e8f.webp",
"cover_sha256": "cf90f963fde7d5adea8673040fcedf7e65dfa733aa12828be49baa3cb1a83798"
},
{
"mint_id": "404220",
"artist": "Pop! Movies",
"title": "Pop! Movies Harley Quinn",
"price": 30.0,
"condition": "",
"cover": "/img/28/287da5b65a4699efc6ced5c228ebd3fe94b8624116483bba80ecc8316d6a9dc3.webp",
"cover_sha256": "72cf5a77dcf4af65a9158763f54d271c336d65182d5f5ed2b1c327b33ec06866"
},
{
"mint_id": "495340",
"artist": "Sony",
"title": "Battlefield 2042 Playstation 4 (PS4)",
"price": 9.0,
"condition": "",
"cover": "/img/23/23c76fd8fbbc0597ed2f349b5f0c4afc92b283eaa3cc34f94f4e94ec61b71476.webp",
"cover_sha256": "51604bcab044a7777879b7ea3b23b6f19a3f4b80d9b75b11528b3cad2fa9f323"
},
{
"mint_id": "519277",
"artist": "Usa",
"title": "Usa 1 Oz American Eagle Silver Bullion Coin Silver",
"price": 159.0,
"condition": "",
"cover": "/img/8a/8af4423553e212361ba09b623c8eb7e6ba68cbede4707e858f32ca88873a5467.webp",
"cover_sha256": "af32bc8c8ec3838dba0545894fec3b2e7eba3f54c1a144c2206e24e0d690596e"
},
{
"mint_id": "532131",
"artist": "Microsoft",
"title": "Halo 3 Xbox 360",
"price": 6.0,
"condition": "",
"cover": "/img/1a/1acf99f6099c43ab59847bb73354507712c964d817a6ca18544b8e7affbdc4f8.webp",
"cover_sha256": "e386f0791b236f04962fe269d75cdeef2614e9f544448543573abfbb03b8b95d"
},
{
"mint_id": "537456",
"artist": "Nintendo",
"title": "Knife Edge (Jpn) Nintendo 64 (N64)",
"price": 10.0,
"condition": "",
"cover": "/img/c5/c5e565014894fa8b63dc7a90252edc615219ca64d28944ceb3ba295e87f88913.webp",
"cover_sha256": "e9420422b685329174861bba132c4754b59ae2eca482b83d2941ed2f78acd263"
},
{
"mint_id": "5912643",
"artist": "NOSTALGIC ART",
"title": "Nostalgic-Art Round Ducati Moto Servizio Decorative Sign Plaque 35x35x0.5cm",
"price": 36.0,
"condition": "",
"cover": "/img/29/29b5c978c42ca2619467c5c314f8d2ba4f71bef3616f4a7c1c056732dca7d6d4.webp",
"cover_sha256": "4d6d00ee03306d14d450a9747e975db62cd81ca8629e7e87f33b31702ea984f0"
},
{
"mint_id": "6504005",
"artist": "Ribtex",
"title": "Ribtex UV Resin",
"price": 5.95,
"condition": "",
"cover": "/img/25/25288f61e6f360a9df08bd9a8db41be3221432b8374e6ac0c6c4502d8468a95f.webp",
"cover_sha256": "b35666f3a06cfb3c6328c99519c6eef745608493a79be0168a78778d153491f9"
},
{
"mint_id": "6515920",
"artist": "VELVET UNDERGROUND",
"title": "Velvet Underground - Andy Warhol",
"price": 75.0,
"condition": "",
"cover": "/img/fd/fdf293000655801074dd3391ca13a2592f6c679dfbd232548e6daabdb6b55f55.webp",
"cover_sha256": "16a849cccfeed4f06732bb7a812557be83518dbf41aa15dbcb1f6bfb43de6646"
},
{
"mint_id": "6811007",
"artist": "TRUMPETER",
"title": "TRUMPETER 1/35 CH-47D Chinook",
"price": 170.99,
"condition": "",
"cover": "/img/b9/b9d511f3aa6869770299276f7739973a44d0be9c44d0053568b42eea296f3689.webp",
"cover_sha256": "8cefdb6f9796825bcb7e99fb9ec68573018e318d365b70c1ee5980e23204761c"
},
{
"mint_id": "6858956",
"artist": "WizKids",
"title": "D&D Booster Brick: Icons of the Realms - Baldurs Gate Descent into Avernus (8)",
"price": 169.0,
"condition": "",
"cover": "/img/30/30da48e6d5cbec1ad8d62a85b183bfa308c64c0b8425a4aee223887a8c728af3.webp",
"cover_sha256": "930dc3d5917fbf260e42ee75e62e6eee5d11000b102bb57093a67e7e6df31125"
}
]
}

View File

@ -0,0 +1,182 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"shop": {
"godverse_id": 885,
"slug": "scrumptious-reads-book-store",
"name": "Scrumptious Reads Book Store",
"suburb": "Ashgrove",
"state": "QLD",
"type": "book"
},
"counts": {
"pool": 19707,
"baked": 16,
"dropped": {
"file_missing": 0
}
},
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 885,
"sample": "random.Random(885) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"items": [
{
"mint_id": "829641",
"artist": "Lifeline Queensland",
"title": "Worthy Opponents - Danielle Steel - BPAP - BOO018",
"price": 8.0,
"condition": "",
"cover": "/img/63/6357f6cad903a31d40a88f38dcef722a84a02047f3b44422db89e4b711f02833.webp",
"cover_sha256": "48e7c1b2ca5bd9af2300c6b1bf7cea4787a07557348ef83953d141cc81882ead"
},
{
"mint_id": "1903008",
"artist": "Lonely Planet",
"title": "Lonely Planet's Travel Journal - Pink",
"price": 33.0,
"condition": "",
"cover": "/img/3e/3e2d5517a0d8a2e5d0bccfc6bff9d46a8240258d0a5f47ac9f82221af98b3825.webp",
"cover_sha256": "0a84527648555dacd88d7fe1b74ccaf5517f33b793ae47948f4b580f99dfd257"
},
{
"mint_id": "1925211",
"artist": "Jack Mitchell",
"title": "Aqua Nullius - Jack Mitchell",
"price": 85.0,
"condition": "",
"cover": "/img/a7/a760d44135e4b656ca05980a963d34112f1f92c8632f74c7688b3e1bc8dc631d.webp",
"cover_sha256": "a6e6e525b5188bda37b607fb00efb7d35d969c4a6651e3e8917020406b9a008c"
},
{
"mint_id": "1928391",
"artist": "Kei Lum Chan",
"title": "China (Phaidon Cookbook) - Kei Lum Chan",
"price": 75.0,
"condition": "",
"cover": "/img/00/0085359ce820eb3ac3ea1de117cfe6a80460f62efebc8699a33402f46cee32e5.webp",
"cover_sha256": "725b861239a41dec6b92665a7990fd00f4c92f34789b5c38eac0ecea1d003d37"
},
{
"mint_id": "1930798",
"artist": "Bill Gates",
"title": "How to Prevent the Next Pandemic (Hardback) - Bill Gates",
"price": 50.0,
"condition": "",
"cover": "/img/dd/dd239257aac42354155c457b24d4d0b25282391377cff81b97b578ccc7724a88.webp",
"cover_sha256": "fdeee2bcba0c090257e3a8115cbd55d8ea698c5b1975c386b89713bf36afc074"
},
{
"mint_id": "1930874",
"artist": "Gabrielle Zevin",
"title": "Tomorrow, and Tomorrow, and Tomorrow - Gabrielle Zevin",
"price": 25.0,
"condition": "",
"cover": "/img/d7/d787cc7498606884d0dff77fb3bd06f9b1b2beb8f16d01bc0001b2d907e0b9e5.webp",
"cover_sha256": "cc4ca168c6a19459b525dd0bf213034cdf838390b93908f614322b75f291abbc"
},
{
"mint_id": "3275806",
"artist": "Rock-Pop-Alt",
"title": "Popular Annual 1964 Sept - Dave Clark 5 Vs Beatles Magazine (Used)",
"price": 40.0,
"condition": "",
"cover": "/img/68/6837ccc52ca3204c03adbbef213f2419aad3471e761263a0fd7131f5b595010a.webp",
"cover_sha256": "a56c889af6e89359329152ef72961911acc2a183be169b196588840fb65675da"
},
{
"mint_id": "6938204",
"artist": "Lia Dewey Morgan",
"title": "Traffic Saga",
"price": 20.0,
"condition": "",
"cover": "/img/11/11f7c34086188a40e085f540793eedc426050a342d23c5351bebedd35f87b649.webp",
"cover_sha256": "9705d8ef010f901ee8451f9aba0c174e74a97233b25af20a495c2a7368512175"
},
{
"mint_id": "6961188",
"artist": "Monoray",
"title": "Murder In Paris '68",
"price": 68.0,
"condition": "",
"cover": "/img/78/78f3c87e2fafabef55fa61240c2de42c1f5f771067f545cfb332b007c7f224d8.webp",
"cover_sha256": "9e1f712393d8cd5c2e44010a340ec30252a469794908185a33c45a661d6c2ef8"
},
{
"mint_id": "6963036",
"artist": "Dorling Kindersley",
"title": "Knitting Stitches Step-by-Step: More than 150 Essen",
"price": 150.0,
"condition": "",
"cover": "/img/36/3643a49fee7b49b6e3b00e7dd9b6591abaee1004a3d9ff19ae81ba23da6f71c6.webp",
"cover_sha256": "40f8aab11ca5ad6b6ae214eff9c57725b08ef259e9d525e5d3a9f5399f8de453"
},
{
"mint_id": "6963927",
"artist": "Random House US",
"title": "Banksy",
"price": 54.99,
"condition": "",
"cover": "/img/d3/d3e972f296ff9802c87a15f3a39818784ca1157995dbd9d49a15a4c2061cea9c.webp",
"cover_sha256": "49a000eab3f5761d921aa9731847355a4330ea57a7ff8976cc2475969e6c2f85"
},
{
"mint_id": "6965987",
"artist": "Phaidon",
"title": "The Talks - No Idea Is Final: Quotes from the Creat",
"price": 49.95,
"condition": "",
"cover": "/img/99/9965e806f34638c06cd6e41809260e06da66420c936dd44091edf9829e54663d.webp",
"cover_sha256": "82e8d332dd7aae72791979da2d85aaf1c9f112536a160750a20b307a0fae2cc6"
},
{
"mint_id": "6966191",
"artist": "HarperCollins Publishers Australia",
"title": "Yates Garden Guide ANZ Edition",
"price": 45.0,
"condition": "",
"cover": "/img/ec/eccb6f4269967ac31a7f1f34003f911f205df1182b1fa7262b620c42bd071937.webp",
"cover_sha256": "4ceb8d42c7252aab5e833c343c404b296eef060cf03f307c40bfdaf5649dabbb"
},
{
"mint_id": "6994153",
"artist": "Head of Zeus",
"title": "Absynthe",
"price": 10.0,
"condition": "",
"cover": "/img/c5/c5ad0c70d6bef23ba82596108bc4770aa12c9129e481937381ff36b920f06ebf.webp",
"cover_sha256": "b138c323769116f1ac43f0eba3e83b7be38177fb2c7c74f8a9e34576918116ed"
},
{
"mint_id": "7000975",
"artist": "PAN MACMILLAN UK",
"title": "Sociopath: A Memoir\\n by Patric Gagne",
"price": 24.99,
"condition": "",
"cover": "/img/10/1037e2d926c188e52eb4174df3a9365923d4375abbe4989d0d3a729107e41485.webp",
"cover_sha256": "17149248c12e9005214fe4efc39a8a8a2dc209d88bbb1cf2587e36c93fc93620"
},
{
"mint_id": "7004394",
"artist": "Penguin Random House",
"title": "Having Spent Life Seeking",
"price": 35.0,
"condition": "",
"cover": "/img/b7/b7d2f2b36bcd30e8124bea54d79ac15b4dc5c8a1d74996a561910c81d1a92d86.webp",
"cover_sha256": "ef0c6035910a7cade1e93d0934c7bc1a198de01eb57ce4d088486cd88c28b821"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
[
"look.glb",
"procity_fit_arcade_cabinet_01.glb",
"procity_fit_bass_guitar_01.glb",
"procity_fit_bass_guitar_01_hi.glb",

View File

@ -50,10 +50,30 @@ TOWNS = {
"bowral_real": {"town": "Bowral", "state": "NSW", "center": (-34.4780, 150.4180), "span_km": 2.2},
"fitzroy_real": {"town": "Fitzroy", "state": "VIC", "center": (-37.7990, 144.9790), "span_km": 2.2},
"brunswick_real": {"town": "Brunswick", "state": "VIC", "center": (-37.7700, 144.9600), "span_km": 2.6},
"ballarat_real": {"town": "Ballarat", "state": "VIC", "center": (-37.5620, 143.8500), "span_km": 3.0},
# R24 — RETIRED (counted), and for a DIFFERENT illness than Toowoomba. A's spacing warn caught
# it at median NN 255 m. Ballarat is NOT dead: its raw bbox holds 103 POIs and its densest 400 m
# disc is 47 of them (Toowoomba's densest was 3). But its retail core is alive exactly where the
# GAME isn't — that disc is cafes/clothes/bakeries, while the op-shops sit 854 m, 1042 m and
# 1709 m out. The heroes and the density don't coincide, so no bbox holds both. Measured, every
# span (real builds, not simulated):
# span shops heroes medNN hub160 op-shops
# 1.0 km 8 2 184 m 4/8 (50%) 0 <- the live core: a thrift town with NO thrift shop
# 2.0 km 12 3 186 m 3/12 (25%) 1
# 3.0 km 20 5 255 m 4/20 (20%) 2 <- as shipped
# Every row is worse than any shipped town (darwin 68 m/58%, glebe 60 m, redhill 73 m). The
# binding constraint is mine, not OSM's: with 2 heroes the 3x cap keeps 6 of 47 texture POIs and
# the subsample is spatially blind, so the survivors scatter no matter how tight the box. Fixing
# THAT (seeding texture near heroes) is a pipeline change that would move every town's golden —
# a v5.x proposal, not a curation move. Cache + raw snapshots removed. See LANE_E_NOTES R24.
# "ballarat_real": {"town": "Ballarat", "state": "VIC", "center": (-37.5620, 143.8500), "span_km": 3.0},
"daylesford_real": {"town": "Daylesford", "state": "VIC", "center": (-37.3430, 144.1430), "span_km": 2.0},
"geelong_real": {"town": "Geelong", "state": "VIC", "center": (-38.1490, 144.3600), "span_km": 3.0},
"westend_real": {"town": "West End", "state": "QLD", "center": (-27.4830, 153.0100), "span_km": 2.2},
# R24 ledger #3 — the v5 crate's real street: **Monster Robot Party, 147 Musgrave Rd, Red Hill,
# Brisbane** (John's ruling: the crate ships as the shop it really is, not Newtown). Centre/span
# per G's ask (they geocoded the address); westend_real is the only other Brisbane cache and stops
# ~2.3 km south. This is the DONOR G runs godverse_town.py against → redhill_godverse.
"redhill_real": {"town": "Red Hill", "state": "QLD", "center": (-27.4553, 153.0064), "span_km": 2.4},
# ── RETIRED R23 (E's curation call, counted) — toowoomba_real ────────────────────────────────
# D's R22 finding: DEAD — median NN 388 m, 3/12 shops near the hub, **0 patronage finds in 1417
# checks**. I tried the re-bbox first (3.0 km CBD-wide → 1.6 km on the Ruthven St heart, centre

302
pipeline/godverse_server.py Normal file
View File

@ -0,0 +1,302 @@
#!/usr/bin/env python3
"""Lane G — GODVERSE tier-2: the two READ endpoints (R27 ledger #1).
The charter's last rung, under John's ratified A-then-B ruling (G3 §7): **read-live,
sell-sandboxed**. The server ENRICHES, NEVER GATES every boot path, gate and tag works with
this process dead. Nothing here can write to the shop.
python3 pipeline/godverse_server.py setup-role # the read-only grant (idempotent)
python3 pipeline/godverse_server.py start|stop|status # lifecycle — F's kill-the-server gate
python3 pipeline/godverse_server.py serve # foreground
python3 pipeline/godverse_server.py make-fixture --sold 3 # a controlled POS for F's gate
THE SURFACE (G3 §4, as built):
GET /godverse/v1/health -> { ok, shops } (is the server up)
GET /godverse/v1/shop/<godverse_id>/stock -> live crate metadata + `gone` + etag
GET /godverse/v1/shop/<godverse_id>/crates -> the real crate menu (rotation)
GET anything else -> 404 (with the ruling in the body)
POST/PUT/PATCH/DELETE anything -> **501**, measured see below
**`/reserve` and `/buy` are ABSENT, not stubbed** no route, no handler, no flag. A sandbox a
config typo can flip isn't a sandbox (G3 §7).
** Lane F, assert 501 and not 404 for the write verbs.** This class defines `do_GET` and nothing
else, so the stdlib answers any other method with 501 "Unsupported method" before routing exists
at all. That is a *stronger* absence proof than a 404: a 404 would mean a handler ran and chose to
decline, whereas 501 means **there is no write handler in the process**. Adding `do_POST` just to
answer 404 would be implementing the very thing the ruling forbids. (My own docstring claimed 404
until I measured it the same doc-vs-code species this epoch keeps catching, caught here by
running it.)
THREE FENCES, each enforced by the machine and not by a code review:
1. **The credential.** We connect as `godverse_ro`: SELECT on inventory/crate/disc_cache and
NOTHING else. Measured `UPDATE inventory` => "permission denied"; `SELECT FROM customer`
=> "permission denied". That one grant carries the sandbox AND G3 §9's PII fence.
2. **Metadata only.** The API serves facts, never pixels. Covers stay static tier-1 files. A
tier-2 shop with a dead server is VISUALLY IDENTICAL to tier 1 that is the fail-soft.
3. **The id-namespace fence** (R26 law). `gone[]` carries the atlas's own `id` form verbatim
(`sku_<POS sku>`), so the client does `gone.includes(item.id)` with ZERO transformation.
*G3 §4 as ratified said `gone:[sku]` bare numbers. That wording predates R25's sku ids and
R26's fence, and a bare number crossing two id spaces is the exact bug the fence law exists
to stop. The design doc was the stale thing; §4 is updated as-built.*
TIER 2 IS FOR REAL SHOPS ONLY. A `sourcing: "mint"` shop has no POS to be live about, so its
`/stock` is a 404 not an empty crate. Monster Robot Party is the only real shop; that's the
point: one shop, live, true.
"""
import argparse, hashlib, json, os, signal, subprocess, sys, tempfile, time
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse, parse_qs
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STOCK = os.path.join(ROOT, "web", "assets", "stock_godverse")
PORT = int(os.environ.get("GODVERSE_PORT", "8778"))
# Runtime state lives OUTSIDE the repo: a pidfile is not an artifact, and a stray one in the tree
# is exactly the kind of thing that gets committed by a hurried `git add`.
PIDFILE = os.path.join(tempfile.gettempdir(), f"godverse-{PORT}.pid")
# The POS. Swap this and the server is live against a different shop-state — which is how F's
# gate stays deterministic without anyone faking real data (see make-fixture).
POS_DB = os.environ.get("GODVERSE_POS_DB", "recordgod")
POS_USER = os.environ.get("GODVERSE_POS_USER", "godverse_ro")
POS_HOST = os.environ.get("GODVERSE_POS_HOST", "127.0.0.1")
READONLY_TABLES = ("inventory", "crate", "disc_cache") # G3 §9: never customer/staff/sales
def q(sql, db=None):
"""Read-only query as godverse_ro. psycopg2 isn't on this box and the CLI keeps the
dependency surface at zero the same call thriftgod's own server would make."""
dsn = f"host={POS_HOST} dbname={db or POS_DB} user={POS_USER}"
r = subprocess.run(["psql", dsn, "-tAF\t", "-c", sql], capture_output=True, text=True)
if r.returncode:
raise RuntimeError(r.stderr.strip().splitlines()[-1] if r.stderr.strip() else "psql failed")
return [ln.split("\t") for ln in r.stdout.splitlines() if ln.strip()]
def manifest():
with open(os.path.join(STOCK, "index.json")) as fh:
return json.load(fh)
def real_shops():
"""Real-sourced (POS-backed) shops, per the manifest. NOTE the key is `godverseShopId`
(camelCase): the manifest is read by the JS runtime, so it wears the JS-side spelling, while
an atlas index wears the Python-side `shop.godverse_id`. That split is recorded in G3 §4
and I still read it wrong first time, which is the whole reason it's recorded."""
return [s for s in manifest().get("shops", []) if s.get("sourcing") == "real"]
def real_shop(gid):
"""The shop's shipped index, but ONLY if it is a real-sourced (POS-backed) shop."""
for s in real_shops():
if str(s.get("godverseShopId")) == str(gid):
for kind in s.get("types", ["record"]):
p = os.path.join(STOCK, str(gid), f"stock_{kind}_index.json")
if os.path.isfile(p):
with open(p) as fh:
return json.load(fh)
return None
def etag_of(payload):
"""Deterministic: same POS state => same etag. `served_at` is deliberately OUTSIDE it —
a clock in the etag would make every read a cache miss."""
return hashlib.sha256(json.dumps(payload, sort_keys=True).encode()).hexdigest()[:16]
def stock(gid):
idx = real_shop(gid)
if not idx:
return None
baked = {it["id"]: it["id"][4:] for it in idx["items"] if it["id"].startswith("sku_")}
if not baked:
return None
skus = ",".join("'" + s.replace("'", "''") + "'" for s in baked.values())
rows = q(f"""SELECT i.sku, COALESCE(i.slot_number::text,''), i.in_stock,
COALESCE(d.artist,''), COALESCE(d.title, COALESCE(i.title,'')),
i.price, COALESCE(i.condition,''), COALESCE(i.sleeve_cond,'')
FROM inventory i LEFT JOIN disc_cache d USING (release_id)
WHERE i.sku IN ({skus}) ORDER BY i.slot_number NULLS LAST, i.sku""")
live, present = [], set()
for sku, slot, in_stock, artist, title, price, cond, sleeve in rows:
present.add(sku)
if in_stock != "t":
continue
live.append({"id": f"sku_{sku}", "slot": int(slot) if slot else None,
"artist": artist, "title": title,
"price": float(price) if price else None,
"condition": cond, "sleeve_cond": sleeve, "state": "shelf"})
# GONE = baked into the tier-1 atlas, no longer on the shelf. Direction is real -> game ONLY:
# a record that sold IN THE REAL SHOP leaves the game crate. A game buy cannot appear here —
# there is no verb to carry it and no grant to write it (see the module docstring).
gone = sorted(i for i, sku in baked.items() if sku not in present or
sku not in {r[0] for r in rows if r[2] == "t"})
body = {"shop_id": int(gid), "tier": 2, "crate": idx.get("crate"),
"items": live, "gone": gone,
"counts": {"baked": len(baked), "live": len(live), "gone": len(gone)}}
body["etag"] = etag_of(body)
return body
def crates(gid):
if not real_shop(gid):
return None
rows = q("""SELECT c.id, c.name, COALESCE(c.label_text,''), count(i.sku)
FROM crate c LEFT JOIN inventory i ON i.crate_id = c.id AND i.in_stock
GROUP BY c.id, c.name, c.label_text HAVING count(i.sku) > 0
ORDER BY c.id""")
body = {"shop_id": int(gid), "tier": 2,
"crates": [{"id": int(a), "name": b, "label": c, "count": int(n)} for a, b, c, n in rows]}
body["etag"] = etag_of(body)
return body
class H(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *a):
pass # the gate reads assertions, not our chatter
def _send(self, code, obj, etag=None):
raw = json.dumps(obj).encode()
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(raw)))
self.send_header("Access-Control-Allow-Origin", "*") # the game is served from :8130
if etag:
self.send_header("ETag", etag)
self.end_headers()
self.wfile.write(raw)
def _404(self):
self._send(404, {"error": "not found",
"note": "read-only surface: /health, /shop/<id>/stock, /shop/<id>/crates. "
"Write verbs (/reserve, /buy) are ABSENT by ruling — G3 §7, "
"A-then-B: read-live, sell-sandboxed."})
def do_GET(self):
u = urlparse(self.path)
parts = [p for p in u.path.strip("/").split("/") if p]
try:
if parts == ["godverse", "v1", "health"]:
return self._send(200, {"ok": True, "tier": 2, "pos_db": POS_DB,
"shops": [s["godverseShopId"] for s in real_shops()]})
if len(parts) == 5 and parts[:3] == ["godverse", "v1", "shop"]:
gid, verb = parts[3], parts[4]
body = stock(gid) if verb == "stock" else crates(gid) if verb == "crates" else None
if body is None:
return self._404()
if parse_qs(u.query).get("since", [None])[0] == body["etag"]:
self.send_response(304); self.send_header("ETag", body["etag"])
self.send_header("Content-Length", "0"); self.end_headers()
return
body["served_at"] = datetime.now(timezone.utc).isoformat()
return self._send(200, body, etag=body["etag"])
except Exception as e:
# A dead/denied POS must look like a dead server to the client: it degrades to tier 1.
return self._send(503, {"error": "pos unavailable", "detail": str(e)[:200]})
self._404()
# No do_POST / do_PUT / do_PATCH / do_DELETE. BaseHTTPRequestHandler answers 501 for verbs it
# has no handler for; the routes do not exist at all. Absent, not stubbed.
def serve():
print(f"godverse tier-2 :{PORT} · POS={POS_DB} as {POS_USER} (read-only) · write verbs ABSENT")
ThreadingHTTPServer(("0.0.0.0", PORT), H).serve_forever()
def setup_role(_):
"""The grant that IS the sandbox (G3 §7). Idempotent."""
subprocess.run(["psql", "-q", "-d", "postgres", "-c",
"DO $$ BEGIN CREATE ROLE godverse_ro LOGIN; "
"EXCEPTION WHEN duplicate_object THEN NULL; END $$;"], check=True)
for db in (POS_DB, f"{POS_DB}_live", f"{POS_DB}_fixture"):
chk = subprocess.run(["psql", "-tAc", f"SELECT 1 FROM pg_database WHERE datname='{db}'",
"-d", "postgres"], capture_output=True, text=True).stdout.strip()
if not chk:
continue
subprocess.run(["psql", "-q", "-d", db, "-c", f"GRANT CONNECT ON DATABASE {db} TO godverse_ro;",
"-c", "GRANT USAGE ON SCHEMA public TO godverse_ro;",
"-c", f"GRANT SELECT ON {', '.join('public.' + t for t in READONLY_TABLES)} "
"TO godverse_ro;"], check=True)
print(f" {db}: SELECT on {', '.join(READONLY_TABLES)} — and nothing else")
def make_fixture(args):
"""A CONTROLLED POS for F's sold-means-gone arm.
Why this exists: the real POS has no movement in our window last sale 2026-07-01, last write
2026-07-09, and 0 of crate 550's 120 records have ever sold, so a real `gone` is EMPTY and an
assertion over it would have no subject (the vacuous-gate law). This is the honest way out: a
labelled fixture proves the MECHANISM deterministically (gone computed -> sent -> consumed ->
sleeve vanishes), while production reads the real POS unchanged. A fixture is not a lie; a
green assert over an absent subject is.
"""
fx = f"{POS_DB}_fixture"
subprocess.run(["dropdb", "--if-exists", fx], check=True)
subprocess.run(["createdb", "-T", POS_DB, fx], check=True)
idx = real_shop(args.shop)
skus = [it["id"][4:] for it in idx["items"] if it["id"].startswith("sku_")][:args.sold]
lst = ",".join(f"'{s}'" for s in skus)
subprocess.run(["psql", "-q", "-d", fx, "-c",
f"UPDATE inventory SET in_stock=false, sold_date=now() WHERE sku IN ({lst});"],
check=True)
setup_role(args)
print(f"fixture {fx}: {len(skus)} record(s) marked SOLD -> they must appear in `gone` and "
f"must never render:\n " + "\n ".join("sku_" + s for s in skus))
def start(_):
if status(None, quiet=True):
print("already running"); return
os.makedirs(os.path.dirname(PIDFILE), exist_ok=True)
p = subprocess.Popen([sys.executable, os.path.abspath(__file__), "serve"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
start_new_session=True)
open(PIDFILE, "w").write(str(p.pid))
for _ in range(50):
time.sleep(0.1)
if status(None, quiet=True):
print(f"started pid={p.pid} :{PORT}"); return
print("failed to start"); sys.exit(1)
def stop(_):
"""F's kill-the-server gate calls this mid-session. SIGKILL, not a graceful drain: the gate is
testing what happens when the server DIES, not when it says goodbye."""
try:
pid = int(open(PIDFILE).read().strip())
os.kill(pid, signal.SIGKILL)
os.remove(PIDFILE)
print(f"killed pid={pid}")
except Exception as e:
print(f"not running ({e})")
def status(_, quiet=False):
import urllib.request
try:
with urllib.request.urlopen(f"http://127.0.0.1:{PORT}/godverse/v1/health", timeout=1) as r:
up = json.loads(r.read())
if not quiet:
print(f"UP :{PORT} · POS={up['pos_db']} · real shops={up['shops']}")
return True
except Exception:
if not quiet:
print(f"DOWN :{PORT}")
return False
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
sub = ap.add_subparsers(dest="cmd", required=True)
for name, fn in (("serve", lambda a: serve()), ("start", start), ("stop", stop),
("status", status), ("setup-role", setup_role)):
sub.add_parser(name).set_defaults(func=fn)
mf = sub.add_parser("make-fixture", help="a controlled POS so F's gate has a subject")
mf.add_argument("--shop", default="3962749")
mf.add_argument("--sold", type=int, default=3)
mf.set_defaults(func=make_fixture)
a = ap.parse_args()
a.func(a)

View File

@ -52,8 +52,8 @@ SHOP = {
}
OWN_PHOTO_HOST = "monsterrobot.party" # the licence test: the shop's own domain, nothing else
CELL_W, CELL_H, GRID, ATLAS_PX = 256, 256, 8, 2048 # Lane E's record contract (build_stock_pack.py)
PER_ATLAS = GRID * (ATLAS_PX // CELL_H) # 64
CELL_W, CELL_H, ATLAS_PX = 256, 256, 2048 # Lane E's record contract (build_stock_pack.py)
STOCK_TYPES = ("record", "book", "toy") # C §7.1 — the only types the loader can fetch
def price_band(price):
@ -99,6 +99,127 @@ def load_image_map(path):
return m
def crate_menu():
"""The G3 §4 crate-rotation menu, baked statically — the shop's real crates, with real labels.
NOTE ( C, R26): this is METADATA, not stock, and it cannot be otherwise under the contract as
written. The loader fetches ONE index per base (`<base>stock_<type>_index.json`) and C §7.6
caps a shop at 2 atlases = 128 items. Monster Robot has 357 crates / 24,646 baked-able records.
So "the crate menu, baked statically" can ship as the shop's real crate LIST (below) — what it
cannot ship as is per-crate stock, which needs a per-crate base (`/3962749/crate_550/`) and
therefore a C contract line. Filed, not invented: the menu is honest metadata today, and tier 2
is where rotation actually lives (G3 §4 `/crates`).
"""
rows = psql("recordgod", """
SELECT c.id, c.name, COALESCE(c.label_text,''), count(i.sku)
FROM crate c JOIN inventory i ON i.crate_id = c.id AND i.in_stock
GROUP BY c.id, c.name, c.label_text HAVING count(i.sku) > 0 ORDER BY c.id""")
return [{"id": int(r[0]), "name": r[1], "label": r[2], "count": int(r[3])} for r in rows]
# ── the MINT baker (R26 ledger #2) ───────────────────────────────────────────────────────
# The G3 doc's own note, promoted: a keyed shop with NO POS gets a deterministic seeded sample of
# real dealgod listings, so its crate is distinct and plausible — never claimed as its stock.
#
# DETERMINISM: `random.Random(shop_id)` over an ID-ORDERED pool. Same shop_id + same pool =>
# byte-identical bake, forever. No TABLESAMPLE, no unseeded random — the two things that
# disqualified thriftgod's own mint() from tier 1 (G3 §2). The shop_id IS the seed, so two shops
# can never draw the same crate, which is what F's distinctness gate asserts.
#
# HONESTY: the snapshot is marked `sourcing: "mint"` and carries NO POS-claim field — no `crate`,
# no `pos_db`, no `pos_dump` (E's POS_CLAIM_FIELDS). A mint crate is structurally incapable of
# claiming a shop's real inventory: the fields that would say so are absent, not merely false.
MINT_KINDS = { # pool row -> the stock type it can furnish
"record": lambda r: r["release_id"] != "\\N",
"book": lambda r: r["cat"].split("/")[0] == "books",
"toy": lambda r: r["cat"].split("/")[0] in ("collectables-hobbies-toys", "gaming"),
}
MINT_COUNT = 16 # ≤16 items => ONE 1024² atlas (C §7.6: ≤1024² when it fits;
# 1024²/256² = 16 cells). See G-progress R26 on why not more.
def load_pool(path):
"""The dealgod mint pool — a frozen projection of the same dated dump the real tier uses."""
pool = []
with open(path) as fh:
for ln in fh:
f = ln.rstrip("\n").split("\t")
if len(f) < 9:
continue
pool.append({"id": f[0], "title": f[1], "artist": f[2], "price": f[3], "cond": f[4],
"cover": f[5], "ptype": f[6], "cat": f[7], "release_id": f[8]})
pool.sort(key=lambda r: int(r["id"])) # ID-ORDERED: the sample's determinism rests on this
return pool
def mint(args):
import random
pool = [r for r in load_pool(args.pool) if MINT_KINDS[args.type](r)]
seen, uniq = set(), []
for r in pool: # de-dupe by cover so a crate never doubles a sleeve
if r["cover"] in seen:
continue
seen.add(r["cover"])
uniq.append(r)
rng = random.Random(args.shop) # THE SEED IS THE SHOP — deterministic, per-shop distinct
picks, items, dropped = rng.sample(uniq, min(len(uniq), args.count * 4)), [], {"file_missing": 0}
for r in picks:
if len(items) >= args.count:
break
path = os.path.join(IMG_CACHE, r["cover"][len("/img/"):])
if not os.path.isfile(path):
dropped["file_missing"] += 1
continue
with open(path, "rb") as fh:
sha = hashlib.sha256(fh.read()).hexdigest()
try:
price = float(r["price"])
except ValueError:
continue
items.append({
# C §7.2a: derived from the source's OWN stable key, never position. The source here is
# dealgod, so the key is its listing id — NOT `sku_`. See G-progress R26: a POS sku and a
# dealgod product id are DIFFERENT NAMESPACES, and tier-2's sold-means-gone will look up
# POS skus. An id shaped like a sku that no POS can resolve is the R26 standing note
# (plan ids vs godverse ids) waiting to happen in the economy.
"mint_id": r["id"], "artist": r["artist"] if r["artist"] != "\\N" else "",
"title": r["title"], "price": price,
# §7.2c: emit `condition` ONLY if the listing genuinely carries one — an invented grade is
# exactly the fiction `sourcing` exists to prevent.
"condition": r["cond"] if r["cond"] not in ("\\N", "") else "",
"cover": r["cover"], "cover_sha256": sha,
})
items.sort(key=lambda i: int(i["mint_id"]))
snap = {
"version": 1, "kind": args.type, "tier": 1, "sourcing": "mint",
"shop": {"godverse_id": args.shop, "slug": args.slug, "name": args.name,
"suburb": args.suburb, "state": args.state, "type": args.type},
"counts": {"pool": len(uniq), "baked": len(items), "dropped": dropped},
"source": { # NO pos_db / pos_dump — a mint crate has no POS to claim
"feed_db": "dealgod", "feed_dump": args.feed_dump, "image_cache": IMG_CACHE,
"captured": args.captured, "seed": args.shop,
"sample": f"random.Random({args.shop}) over an id-ordered pool of {len(uniq)} listings",
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green", "flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists — John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT — a seeded sample standing in for a shop with no POS. Plausible, and "
"NEVER this shop's actual stock. Nothing here is a claim about this business.",
},
"items": items,
}
os.makedirs(SNAP_DIR, exist_ok=True)
dst = os.path.join(SNAP_DIR, f"mint_{args.shop}_{args.type}.snapshot.json")
with open(dst, "w") as fh:
json.dump(snap, fh, indent=1)
fh.write("\n")
print(f"mint: shop {args.shop} ({args.name}) — {len(items)} {args.type} items "
f"seeded random.Random({args.shop}) from {len(uniq)} listings -> {dst}")
def snapshot(args):
crate = psql("recordgod", f"SELECT id,name,COALESCE(label_text,'') FROM crate WHERE id={args.crate}")
if not crate:
@ -141,9 +262,10 @@ def snapshot(args):
items = items[:args.max_items]
snap = {
"version": 1, "kind": "record", "tier": 1,
"version": 1, "kind": "record", "tier": 1, "sourcing": "real",
"shop": SHOP,
"crate": {"id": int(cid), "name": cname, "label": clabel},
"crate_menu": crate_menu(), # G3 §4's menu, baked as metadata — see crate_menu()
"counts": {"in_crate": len(rows), "baked": len(items), "dropped": dropped},
"source": {
"pos_db": "recordgod", "pos_dump": args.pos_dump,
@ -151,7 +273,9 @@ def snapshot(args):
"captured": args.captured,
"extraction": "see load_image_map() docstring — the products projection command",
},
"licence": {
# US `license` throughout (R24: British `licence` retired per C's contract §7.3; E's
# validator reads `provenance.licence or provenance.license`, so US passes the gate).
"license": {
"covers": "the shop's own product shots (monsterrobot.party), cached by dealgod",
"zone": "in-house-green", "flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists — John's R23 amendment to the no-real-trademarks law",
@ -173,59 +297,141 @@ def pack(args):
from PIL import Image
snap = json.load(open(args.snapshot))
shop, items = snap["shop"], snap["items"]
kind, sourcing = snap.get("kind", "record"), snap["sourcing"]
out_dir = os.path.join(STOCK_DIR, str(shop["godverse_id"]))
os.makedirs(out_dir, exist_ok=True)
# C §7.6: ≤2048², and ≤1024² whenever the crate fits it. 1024²/256² = 16 cells; 2048² = 64.
# A 16-item mint crate is ONE 1024² sheet; the 120-record real crate needs 2×2048² (C's own
# worked example). Cell stays 256² either way — C: never shrink it, the dig renders it close-up.
px = ATLAS_PX if len(items) > (1024 // CELL_H) ** 2 else 1024
grid, per_atlas = px // CELL_W, (px // CELL_W) * (px // CELL_H)
idx_items, atlases = [], []
for ai in range((len(items) + PER_ATLAS - 1) // PER_ATLAS):
chunk = items[ai * PER_ATLAS:(ai + 1) * PER_ATLAS]
W, H = GRID * CELL_W, (ATLAS_PX // CELL_H) * CELL_H
for ai in range((len(items) + per_atlas - 1) // per_atlas):
chunk = items[ai * per_atlas:(ai + 1) * per_atlas]
W = H = px
sheet = Image.new("RGB", (W, H), (28, 28, 32))
aname = f"stock_record_atlas_{ai:02d}.webp"
aname = f"stock_{kind}_atlas_{ai:02d}.webp"
for j, it in enumerate(chunk):
gx, gy = j % GRID, j // GRID
gx, gy = j % grid, j // grid
path = os.path.join(IMG_CACHE, it["cover"][len("/img/"):])
with open(path, "rb") as fh:
raw = fh.read()
if hashlib.sha256(raw).hexdigest() != it["cover_sha256"]:
sys.exit(f"cover drift: {it['sku']} {it['cover']} — snapshot sha mismatch, re-snapshot")
sys.exit(f"cover drift: {it['cover']} — snapshot sha mismatch, re-snapshot")
import io
cov = Image.open(io.BytesIO(raw)).convert("RGB").resize((CELL_W, CELL_H), Image.LANCZOS)
sheet.paste(cov, (gx * CELL_W, gy * CELL_H))
u0, v0 = gx * CELL_W / W, gy * CELL_H / H
idx_items.append({
"id": f"rec_{len(idx_items):04d}", "title": it["title"], "artist": it["artist"],
# C §7.2a: derived from the source's own stable key, never position. `rec_NNNN` is the
# generic packs' namespace and a real-stock pack must not emit into it. And position
# isn't identity: sell one record and `rec_0050` becomes a DIFFERENT record next bake —
# exactly what tier-2's sold-means-gone must never do.
# real → `sku_<POS sku>` (the shop's own identity for that COPY)
# mint → `mint_<dealgod listing id>` (dealgod's own key; NOT a sku — see G-progress)
slot_id = f"sku_{it['sku']}" if sourcing == "real" else f"mint_{it['mint_id']}"
item = {
"id": slot_id, "title": it["title"], "artist": it["artist"],
"price": int(round(it["price"])), "price_band": price_band(it["price"]),
"atlas": aname, "uv": [round(u0, 4), round(v0, 4),
round(u0 + CELL_W / W, 4), round(v0 + CELL_H / H, 4)],
})
}
# C §7.2c: the price card is a record-shop sticker — artist/title/condition/price.
# Emit only what the source actually has; C's card omits what's absent, and an invented
# grade is the fiction `sourcing` exists to prevent.
if it.get("condition"):
item["condition"] = it["condition"]
if it.get("sleeve_cond"):
item["sleeve_cond"] = it["sleeve_cond"]
idx_items.append(item)
sheet.save(os.path.join(out_dir, aname), "WEBP", quality=88, method=6)
atlases.append(aname)
with open(args.snapshot, "rb") as fh:
snap_sha = hashlib.sha256(fh.read()).hexdigest()
lic = snap["license"]
generator, snapshot_name = "pipeline/godverse_stock.py", os.path.basename(args.snapshot)
if sourcing == "real":
attribution = (f"Stock, prices + covers: {shop['name']} ({OWN_PHOTO_HOST}) — the shop's own "
f"product shots · cover cache: GODVERSE/dealgod · POS: recordgod")
else:
# The attribution must not imply this shop supplied anything — it didn't. Say what it is.
attribution = (f"MINT (seeded sample, not {shop['name']}'s stock): real dealgod listings — "
f"the sellers' own product shots · cover cache: GODVERSE/dealgod")
index = {
"version": 1, "kind": "record", "tier": 1,
"atlas_px": ATLAS_PX, "cell_w": CELL_W, "cell_h": CELL_H,
"version": 1, "kind": kind, "tier": 1,
# C §7.2b: `sourcing` is the real-vs-mint axis; `tier` stays the charter's ladder rung (both
# are rung 1 — same mechanism, different origin). One name, one meaning.
"sourcing": sourcing,
"atlas_px": px, "cell_w": CELL_W, "cell_h": CELL_H,
"count": len(idx_items), "atlases": atlases,
# TOP LEVEL, per C's contract §7.3 + the house convention of the shipped town caches
# (`license` is a short US-spelled string there, `attribution` its companion).
"license": f"{lic['zone']}{lic['covers']}; {lic['flag']}",
"attribution": attribution,
"generator": generator,
"snapshot": snapshot_name,
"shop": {k: shop[k] for k in ("godverse_id", "slug", "name", "suburb", "state")},
"crate": snap["crate"],
# NESTED — the rich block C calls colour; the four gate fields are top level above.
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": snap["source"], "licence": snap["licence"],
"snapshot": os.path.basename(args.snapshot), "snapshot_sha256": snap_sha,
"determinism": "pure bake — same snapshot + same cover bytes => byte-identical atlas; "
"covers pinned by sha256, no RNG, no clock",
"generator": generator,
"source": snap["source"], "license": lic,
"snapshot": snapshot_name, "snapshot_sha256": snap_sha,
"determinism": ("pure bake — same snapshot + same cover bytes => byte-identical atlas; "
"covers pinned by sha256, no RNG, no clock"
if sourcing == "real" else
f"pure bake; the sample itself is seeded random.Random({shop['godverse_id']}) "
f"over an id-ordered pool => same shop id + same pool = same crate, forever"),
},
"items": idx_items,
}
dst = os.path.join(out_dir, "stock_record_index.json")
# C §7.2b / E's POS_CLAIM_FIELDS: a mint index carries NO field that claims a shop's real
# inventory — `crate` and the pos_* source keys are ABSENT, not merely false. The separation is
# structural: there is no line here that could make a mint crate read as real.
if sourcing == "real":
index["crate"] = snap["crate"]
index["crate_menu"] = snap.get("crate_menu", [])
dst = os.path.join(out_dir, f"stock_{kind}_index.json")
with open(dst, "w") as fh:
json.dump(index, fh, indent=1)
fh.write("\n")
print(f"pack: {len(idx_items)} real sleeves -> {len(atlases)} atlas(es) in {out_dir}")
print(f" shop {shop['godverse_id']} ({shop['slug']}) · crate "
f"{snap['crate']['id']} '{snap['crate']['label'] or snap['crate']['name']}'")
where = (f"crate {snap['crate']['id']} '{snap['crate']['label'] or snap['crate']['name']}'"
if sourcing == "real" else f"seeded random.Random({shop['godverse_id']})")
print(f"pack[{sourcing}]: {len(idx_items)} {kind} items -> {len(atlases)}×{px}² atlas(es) "
f"in {os.path.relpath(out_dir, ROOT)}")
print(f" shop {shop['godverse_id']} ({shop['slug']}) · {where}")
# ── the atlas manifest (R26 ledger #6, C §7.2d) ───────────────────────────────────────────
def manifest(args):
"""Existence DECLARED, never probed. F's wire consults this before fetching, so a shop with no
atlas is never fetched no 404, and the zero-console-errors law holds without the attribution
exception that was tolerating 8 of them. Derived from the files on disk, so it cannot drift:
the manifest IS the directory, read back."""
shops = []
for d in sorted(os.listdir(STOCK_DIR)) if os.path.isdir(STOCK_DIR) else []:
sd = os.path.join(STOCK_DIR, d)
if not (os.path.isdir(sd) and d.isdigit()):
continue
entry = {"godverseShopId": int(d), "types": [], "sourcing": None}
for t in sorted(STOCK_TYPES):
p = os.path.join(sd, f"stock_{t}_index.json")
if not os.path.isfile(p):
continue
idx = json.load(open(p))
entry["types"].append(t)
entry["sourcing"] = idx.get("sourcing")
if entry["types"]:
shops.append(entry)
man = {"version": 1, "shops": shops}
dst = os.path.join(STOCK_DIR, "index.json")
with open(dst, "w") as fh:
json.dump(man, fh, indent=1)
fh.write("\n")
real = sum(1 for s in shops if s["sourcing"] == "real")
print(f"manifest: {len(shops)} shop(s) carry atlases ({real} real, {len(shops) - real} mint) "
f"-> {os.path.relpath(dst, ROOT)}")
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
@ -241,5 +447,19 @@ s1.set_defaults(func=snapshot)
s2 = sub.add_parser("pack", help="snapshot -> atlas + index (pure; needs PIL, no DB)")
s2.add_argument("snapshot")
s2.set_defaults(func=pack)
s3 = sub.add_parser("mint", help="seeded sample -> mint snapshot (a keyed shop with no POS)")
s3.add_argument("--shop", type=int, required=True, help="godverseShopId — AND the seed")
s3.add_argument("--type", required=True, choices=sorted(MINT_KINDS))
s3.add_argument("--pool", required=True, help="TSV projection of dealgod's listings")
s3.add_argument("--count", type=int, default=MINT_COUNT)
s3.add_argument("--slug", default="")
s3.add_argument("--name", default="")
s3.add_argument("--suburb", default="")
s3.add_argument("--state", default="")
s3.add_argument("--feed-dump", default="", help="dealgod dump filename, for provenance")
s3.add_argument("--captured", default="")
s3.set_defaults(func=mint)
s4 = sub.add_parser("manifest", help="emit stock_godverse/index.json from the atlases on disk")
s4.set_defaults(func=manifest)
args = ap.parse_args()
args.func(args)

View File

@ -6,12 +6,22 @@ shops[] come from thriftgod's Postgres (the real-business census DealGod scraped
walk the town's actual streets past its actual secondhand shops.
python3 pipeline/godverse_town.py newtown_real newtown_godverse # roads-donor key, out key
python3 pipeline/godverse_town.py redhill_real redhill_godverse # THE shop's real street
Runs on ultra (needs the local `thriftgod` PG + psycopg2 the thriftgod server's own deps).
Reads thriftgod via the `psql` CLI (R24): psycopg2 isn't installed on the m3, and one less dep
between the census and the cache. Works unchanged wherever thriftgod lives.
`godverseShopId` (R24 ledger #2, with Lane A) — the OPAQUE key that resolves
`stock_godverse/<godverseShopId>/`. **It is not `shop.id` and not "the census id"**: census shops
carry their thriftgod id (max 2992), while Monster Robot Party isn't in the census at all and
carries its dealgod store id (3962749). Two disjoint id spaces, one opaque field G emits it,
everyone else reads it, nobody derives it. A's `validateTownCache` wants a positive integer,
unique per cache; **missing is a WARN, not an error** (measured A's rule is deliberate: absent
stock identity must never fail a town's boot, per the charter's determinism boundary). So the
GODVERSE layer is keyed and the inherited texture layer is not see Ruling 2 below for why that
is the honest shape rather than a gap.
"""
import json, os, sys
import psycopg2, psycopg2.extras
import json, math, os, subprocess, sys
DONOR, OUT = sys.argv[1], sys.argv[2]
HERE = os.path.dirname(os.path.abspath(__file__))
@ -22,17 +32,98 @@ TYPE_MAP = {'music': 'record', 'charity': 'opshop', 'second_hand': 'opshop', 'an
'books': 'book', 'toys': 'toy', 'video_games': 'video', 'games': 'toy',
'pawnbroker': 'pawn', 'market': 'dept'}
# REAL shops that exist in neither OSM nor thriftgod's census, added by explicit, sourced ruling.
# This is not a mapping table (the R12 gigKey law) — it's a hand-verified roster of the shops the
# epoch is actually about. Every entry states where its identity and its coordinates came from.
#
# Monster Robot Party: the v5 alpha's shop — its POS *is* `recordgod`, its own site hosts the
# covers, and dealgod scrapes it as store 3962749 (which is therefore its godverseShopId). OSM
# doesn't know it (no name match, no 147 Musgrave Rd address node) and the census doesn't either,
# so the coordinate cannot be derived from any dataset we hold: John supplied the shop's Google
# Maps place (place `/g/11g6rlbbc0`), whose point is -27.4553791, 153.0076244 — inside
# `redhill_real`'s bbox and on Musgrave Road's own geometry. Verified, not estimated.
EXTRA_SHOPS = {
'redhill_godverse': [
{'id': 3962749, 'godverseShopId': 3962749, 'name': 'Monster Robot Party', 'type': 'record',
'lat': -27.4553791, 'lon': 153.0076244, 'suburb': 'Red Hill',
'source': 'GODVERSE/dealgod store 3962749 · POS recordgod · coords: Google Maps place '
'/g/11g6rlbbc0 (147 Musgrave Road, Red Hill QLD), John-supplied R24'},
],
}
def psql(db, sql):
out = subprocess.run(['psql', '-d', db, '-tAF\t', '-c', sql],
capture_output=True, text=True, check=True).stdout
return [ln.split('\t') for ln in out.splitlines() if ln.strip()]
donor = json.load(open(os.path.join(TOWNS, f'{DONOR}.json')))
bb = donor['bbox']
db = psycopg2.connect(os.environ.get('THRIFTGOD_DSN', 'dbname=thriftgod'))
c = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
c.execute("""SELECT id, name, shop_type, lat, lng, suburb FROM shop
WHERE lat BETWEEN %s AND %s AND lng BETWEEN %s AND %s ORDER BY id""",
(bb['minLat'], bb['maxLat'], bb['minLon'], bb['maxLon']))
shops = [{'id': r['id'], 'name': r['name'],
'type': TYPE_MAP.get(r['shop_type'], 'opshop'),
'lat': float(r['lat']), 'lon': float(r['lng']),
'suburb': (r['suburb'] or '').split('|')[0]} for r in c.fetchall()]
rows = psql(os.environ.get('THRIFTGOD_DB', 'thriftgod'), f"""
SELECT id, COALESCE(name,''), COALESCE(shop_type,''), lat, lng, COALESCE(suburb,'')
FROM shop
WHERE lat BETWEEN {bb['minLat']} AND {bb['maxLat']}
AND lng BETWEEN {bb['minLon']} AND {bb['maxLon']}
ORDER BY id""")
shops = [{'id': int(r[0]),
'godverseShopId': int(r[0]), # census shops: the thriftgod id IS the key (not derived
# from `id` by law — they coincide, see the docstring)
'name': r[1],
'type': TYPE_MAP.get(r[2], 'opshop'),
'lat': float(r[3]), 'lon': float(r[4]),
'suburb': r[5].split('|')[0]} for r in rows]
census_n = len(shops)
extras = EXTRA_SHOPS.get(OUT, [])
known = {s['id'] for s in shops}
for e in sorted(extras, key=lambda s: s['id']):
if e['id'] in known: # census wins a collision, and we count the swap
continue
shops.append({k: e[k] for k in ('id', 'godverseShopId', 'name', 'type', 'lat', 'lon', 'suburb')})
extra_n = len(shops) - census_n
# ── Fable's Ruling 2 (R22, implemented R24): INHERIT THE DONOR'S TEXTURE LAYER ────────────────
# "Merge census heroes WITH the donor's texture shops; census heroes always win a collision
# (dedupe by proximity + name), drop the donor hero, keep yours, count the swap. Result: real
# census shops on a street that also feels alive."
#
# Not cosmetic — MEASURED (R24): the census layer alone is 9 shops across Red Hill's 2.4 km bbox →
# A's spacing warn at 261 m ("scattered, not a high street"), *worse than the 255 m that retired
# ballarat this round*, plus a frontage poster seated inside edge 1037's kerb. With the donor's
# texture layer merged (37 shops) both go away and E's donor's own density is preserved.
#
# THE MERGED SHOPS CARRY NO `godverseShopId`, DELIBERATELY. The field is the GODVERSE stock
# identity; an OSM cafe has none, and minting one from its OSM id would push a third id space into
# a field that already carries two (thriftgod census ≤2992 · dealgod store ids). OSM ids are the
# same order of magnitude as dealgod store ids, so a collision is *possible* — and A's validator
# calls a duplicate godverseShopId mis-stocking (charter risk #3: two shops keyed to one atlas,
# one of them Monster Robot's crate). Unkeyed is both true and safe: they fall soft to tier 0,
# which is exactly what a milkbar with no stock should do. A's coverage warn states this honestly.
def norm(n):
return ' '.join((n or '').lower().split())
def near(a, b, m=150.0): # metres, flat-earth at suburb scale — plenty at 150 m
dy = (a['lat'] - b['lat']) * 111_320.0
dx = (a['lon'] - b['lon']) * 111_320.0 * math.cos(math.radians(a['lat']))
return math.hypot(dx, dy) <= m
swaps = 0
for d in sorted(donor.get('shops', []), key=lambda s: s['id']):
hit = next((s for s in shops if norm(s['name']) == norm(d['name']) and near(s, d)), None)
if hit: # the donor's own entry for a shop we already hold
swaps += 1
continue
if d['id'] in {s['id'] for s in shops}:
continue
shops.append({'id': d['id'], 'name': d['name'], 'type': d['type'],
'lat': d['lat'], 'lon': d['lon'], 'suburb': d.get('suburb', '')})
texture_n = len(shops) - census_n - extra_n
shops.sort(key=lambda s: s['id']) # deterministic order, extras + texture included
out = dict(donor)
out.update({
@ -42,10 +133,20 @@ out.update({
'generator': 'pipeline/godverse_town.py',
'attribution': donor['attribution'] + ' · shop census: GODVERSE/thriftgod',
'shops': shops,
'counts': {**donor.get('counts', {}), 'shops': len(shops)},
# The donor's shop-side counts (raw/heroes/texture/textureDropped) describe the shop layer we
# REPLACE, so carrying them would state a census of shops this cache doesn't contain. Keep the
# donor's road count; recount our own shops.
'counts': {**{k: v for k, v in donor.get('counts', {}).items() if k == 'roads'},
'shops': len(shops), 'census': census_n, 'extra': extra_n,
'texture': texture_n, 'donorSwaps': swaps},
})
dst = os.path.join(TOWNS, f'{OUT}.json')
json.dump(out, open(dst, 'w'), indent=1)
with open(dst, 'w') as fh:
json.dump(out, fh, indent=1)
types = {}
for s in shops: types[s['type']] = types.get(s['type'], 0) + 1
print(f"{OUT}: {len(shops)} REAL shops ({types}) + {len(out.get('roads', []))} OSM roads -> {dst}")
for s in shops:
types[s['type']] = types.get(s['type'], 0) + 1
keyed = sum(1 for s in shops if 'godverseShopId' in s)
print(f"{OUT}: {len(shops)} shops = {census_n} census + {extra_n} extra + {texture_n} donor-texture "
f"({swaps} donor heroes swapped for census) · {keyed} keyed w/ godverseShopId")
print(f" {types} + {len(out.get('roads', []))} OSM roads -> {dst}")

View File

@ -1,37 +1,132 @@
#!/usr/bin/env python3
"""Validate per-shop real-stock atlases — the G2a format gate (v5.0-alpha, ROUND23 ledger #4).
"""Validate per-shop real-stock atlases — the G2a format gate (R23 #4 · R24 #4 · R25 #2 · R26 #3/#6).
Lane G bakes one atlas + index per real shop from dealgod inventory (tier 1 of the charter's stock
ladder). This gate is the FORMAT side: it proves G's output is well-formed, inside C's ceilings, and
carries its provenance **on m3, from the files alone, with no dealgod DB and no network**. That's the
point: the gate must run anywhere, on the committed artefacts.
Judges Lane G's tier-1 atlases **from the committed files alone** — no dealgod DB, no network — so the
gate runs on m3. Wired into `validate_manifest` (qa gate 3).
python3 pipeline/validate_atlas.py # exit 0 = every atlas valid (or none yet), 1 = a bad one
R26 `sourcing` + the atlas manifest (ledgers #3, #6). Two arms, both C's contract:
**§7.2b `sourcing: real|mint`** REQUIRED; no `sourcing` FAIL. **The brief called this `tier`; C
ruled it cannot be** `tier` is already the charter's ladder rung and a mint atlas is *also* rung 1.
This gate reads C's field, not the brief's word (R23's whole lesson: the contract's author is the
authority for the contract's shape, and a gate built to a paraphrase diverges from the emitter).
The real/mint separation is asserted **both directions**: mint must carry no POS claim, real must
carry the POS provenance it claims. The licence line prints for both.
**§7.2d the manifest** `stock_godverse/index.json` declares which shops carry atlases so the wire
never probes blind (that probe is the 404 noise the zero-console-errors law was eating by
attribution). Consistency BOTH ways: an atlas on disk absent from the manifest is unreachable and
FAILS; a manifest entry with no atlas would 404 and FAILS. An absent manifest is legitimate **only**
with zero atlases a manifest that promises atlases that don't exist is a FAIL, not a SKIP.
Contract (published R22 LANE_E_NOTES E's half · LANE_C_PUB §7 C's ceilings · charter law #3 licence):
index `web/assets/stock_godverse/**/stock_shop_<godverseShopId>_index.json`
{version, atlas_px, cell, atlases[], items:[{id,title,artist,price,price_band,atlas,uv}]}
+ provenance: {license, attribution, generator, snapshot}
C's ceilings — 1 atlas/shop (hard cap 2); ≤1024² typical, 2048² only for a 60+ item shop.
charter law #3 — every atlas carries provenance; no private-individual data, ever.
determinism `snapshot` pins the inventory query result (G snapshots it like E snapshots Overpass raw).
R25 THE LICENCE LINE, MADE UNMISSABLE (ledger #2), and it is the sharpest bug this file has had.
The R24 gate reported `OK 0 warnings` on real product photos while printing **nothing** about their
licence. Not a typo a DIVERGENCE between two paths reading one field:
Fail-soft is NOT this gate's business: a missing atlas is legal at runtime (parody canvas, R7 law). This
gate only judges atlases that EXIST a malformed one must fail loudly rather than render garbled sleeves.
the CHECK: lic = prov.get("licence") or prov.get("license") permissive: passed either spelling
the PRINT: lic = prov.get("licence") strict: British only, and guarded
if lic: print(...) behind a truthiness test
While G's artifact was British both agreed. G's R24 keyed re-emit moved it to US `license` the check
still passed, the print silently resolved to `{}`, and `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`
stopped reaching a human. **The fallback WAS the bug**: it let the permissive path hide the strict one's
drift. It is the R21 stale-roads lesson one layer up *a check applied to one of two symmetric paths is
a trap with a fuse on it* and the R23 vacuous species again: the gate passed without touching its
subject. Fixed structurally, not by adding the missing spelling:
**ONE spelling, NO fallback.** C §7.3 retires British `licence`; finding it is now an ERROR that
names the retirement, not a silent accommodation.
**The evidence line is COUNTED, not conditional.** The gate must print one licence line per atlas it
judges; `printed != judged` is a FAIL. A gate that can say OK while hiding the flag cannot exist by
construction which is the only fix that survives the next re-emit.
C §7.3 IS THE PROVENANCE AUTHORITY (R24's "runtime is the authority" ruling, applied): the runtime reads
none of these fields, so C ruled the shape to the house convention set by 21 shipped town caches the
four gate fields **`license`, `attribution`, `generator`, `snapshot` live at TOP LEVEL**, US spelling.
This reverses R24's #2/#3 (which read the artifact of the day); the nested `provenance{}` block stays as
optional colour. **This docstring and the code below now say the same thing** R24's fourth bug was that
they didn't, and it is not allowed to recur.
THE AUTHORITY IS THE RUNTIME for layout (R24 ruling). `stockpack.js` states it: a per-shop pack lives
under `assets/stock_godverse/<godverseShopId>/` and carries the SAME `stock_<type>_index.json` name (type
picks the stock slot); cache identity is `(type, base)`.
python3 pipeline/validate_atlas.py # 0 = valid (or an honest SKIP), 1 = a bad/misnamed atlas
"""
import json, os, sys, glob, struct
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STOCK_G = os.path.join(ROOT, "web", "assets", "stock_godverse") # Lane G's namespace (charter law #5)
STOCK_G = os.path.join(ROOT, "web", "assets", "stock_godverse") # Lane G's namespace (charter law #5)
GLOB = os.path.join(STOCK_G, "*", "stock_*_index.json") # the RUNTIME path — what the loader fetches
BANDS = {"bargain", "standard", "collector", "grail"}
# The loader fetches `<base>stock_<type>_index.json` where `type` is the shop's registry type — it picks
# the stock slot (stockpack SLOT_FOR: record→sleeve, book→spine, toy→box). Any other `<type>` is a name
# the runtime can never fetch. The glob alone can't say that (`stock_*_index.json` also matches the old
# contract's `stock_shop_<id>_index.json`), so the name is checked, not just matched — presence is not
# the-right-thing (the vacuous-gate law's actual lesson).
STOCK_TYPES = {"record", "book", "toy"}
# C §7.3: the four provenance gate fields, TOP LEVEL, US `license`. The runtime reads none of them, so C
# ruled their shape to the convention 21 shipped town caches already use. `license` is the one that must
# reach a human — it carries the release flag on real product photos.
PROV_FIELDS = ("license", "attribution", "generator", "snapshot")
REQUIRED_ITEM = ("id", "title", "artist", "price", "price_band", "atlas", "uv")
REQUIRED_PROV = ("license", "attribution", "generator", "snapshot") # charter law #3 + determinism
MAX_ATLASES = 2 # C: 1 atlas/shop, hard cap 2 (each extra = +1 draw)
PX_TYPICAL, PX_MAX = 1024, 2048
BIG_SHOP_ITEMS = 60 # C: 2048² only for a 60+ item shop (else spill to the type-pack)
# charter law #3: "No private-individual data, ever" — a real shop's stock row must never carry a person.
# C §7.2a (R25 #3): slot ids derive from the source's stable key, never position, and are unique across
# ALL packs. A real-stock pack's key is the shop's POS sku → `sku_<POS sku>`; the shipped generic packs own
# the `rec_`/`boo_`/`toy_` positional namespace and a real-stock pack must not emit into it.
# C §7.2a as amended (R26 wave 4) — THE PREFIX IS A NAMESPACE FENCE, and the id form is SOURCING-SCOPED.
# My R25 check demanded `sku_` on every real-stock pack. C wrote that rule before mint existed, so it read
# as one space when there are two — and it failed 14 legitimate mint crates whose ids honour its every
# intent (non-positional, seeded, stable, zero reuse). C's ruling: the rule was the stale thing, not the
# check. The fence is not decoration: `sku_` names a POS copy on a shelf, `mint_` names a dealgod listing
# standing in for one. G refused to file listing ids under `sku_` to go green the easy way, and that
# judgment is the law now — tier-2's sold-means-gone looks up POS skus, so a prefix that lies doesn't fail
# a gate, it SELLS THE WRONG RECORD out of a real shop to satisfy a minted stand-in. Never treat them as
# one space (same standing note as plan-ids vs godverse-ids).
SLOT_ID_PREFIX = {"real": "sku_", "mint": "mint_"}
GENERIC_PACKS = os.path.join(ROOT, "web", "assets", "models", "stock_*_index.json")
# C §7.2b (R26 #1): `sourcing` — real (that shop's own POS snapshot) vs mint (a seeded sample standing in
# for a keyed shop with no POS). NOTE the brief called this `tier`; C ruled it CANNOT be — `tier` is already
# the charter's ladder rung (0 parody / 1 static-real / 2 live) and a mint atlas is ALSO rung 1. One name,
# one meaning. This gate reads C's field, not the brief's word.
SOURCING = {"real", "mint"}
# C §7.2b: "a mint index MUST NOT carry POS-snapshot fields (the separation IS the honesty)". C states the
# rule but does not enumerate the fields, so E names them — these are the ones that CLAIM a specific shop's
# actual inventory. `snapshot` is deliberately NOT here: it pins determinism (§7.3 requires it on every
# atlas) and a mint bake has its own honest source snapshot. → C/G: if a mint bake needs any of these, say
# so and this set changes; the error names the exact field found so a divergence is diagnosable, not a mystery.
POS_CLAIM_FIELDS = (("crate",), ("provenance", "source", "pos_db"), ("provenance", "source", "pos_dump"))
MANIFEST = os.path.join(ROOT, "web", "assets", "stock_godverse", "index.json") # C §7.2d
MAX_ATLASES = 2 # C §7.2: ≤2 atlases per shop
PX_MAX = 2048 # C §7.2: ≤2048² each
PX_TYPICAL = 1024 # C §7.2: ≤1024² whenever the crate fits it (≤64 items at 256²)
FITS_1024 = 64
# charter law #3: no private-individual data, ever — a stock row must never carry a person.
FORBIDDEN_ITEM_KEYS = {"seller", "seller_name", "owner", "owner_name", "customer", "user", "username",
"email", "phone", "mobile", "address", "member", "account", "consignor"}
"email", "phone", "mobile", "address", "member", "account", "consignor", "staff"}
_GENERIC_IDS = None # lazily filled by generic_pack_ids()
def _dig(obj, path):
"""obj["a"]["b"]… for path ("a","b",…) — None if any hop is missing or not a dict."""
for k in path:
if not isinstance(obj, dict):
return None
obj = obj.get(k)
return obj
def generic_pack_ids():
"""{pack filename: {id, …}} for the shipped town-wide packs — the other side of "unique across ALL
packs". Read-only; lazily cached. This is what makes §7.2a falsifiable rather than a naming rule: the
prefix says ids SHOULD be disjoint, this measures whether they ARE."""
global _GENERIC_IDS
if _GENERIC_IDS is None:
_GENERIC_IDS = {}
for p in sorted(glob.glob(GENERIC_PACKS)):
try:
d = json.load(open(p))
except Exception:
continue
_GENERIC_IDS[os.path.basename(p)] = {str(i.get("id")) for i in d.get("items", [])
if isinstance(i, dict)}
return _GENERIC_IDS
def image_size(path):
@ -39,75 +134,116 @@ def image_size(path):
with open(path, "rb") as f:
head = f.read(32)
if head[:8] == b"\x89PNG\r\n\x1a\n":
w, h = struct.unpack(">II", head[16:24])
return w, h
return struct.unpack(">II", head[16:24])
if head[:4] == b"RIFF" and head[8:12] == b"WEBP":
fmt = head[12:16]
if fmt == b"VP8X": # extended: 24-bit minus-one
if fmt == b"VP8X":
b = head[24:30]
return (b[0] | b[1] << 8 | b[2] << 16) + 1, (b[3] | b[4] << 8 | b[5] << 16) + 1
if fmt == b"VP8 ": # lossy: 14-bit each
if fmt == b"VP8 ":
w, h = struct.unpack("<HH", head[26:30])
return w & 0x3FFF, h & 0x3FFF
if fmt == b"VP8L": # lossless: packed 14-bit
if fmt == b"VP8L":
b = struct.unpack("<I", head[21:25])[0]
return (b & 0x3FFF) + 1, ((b >> 14) & 0x3FFF) + 1
raise ValueError("unrecognised image header (want PNG or WebP)")
def validate_index(path):
"""→ (errs, warns, evidence). `evidence` is the licence string main() must show a human, or None.
It is returned rather than re-looked-up in main() ON PURPOSE: R24's bug was two paths reading one
field and drifting apart. One lookup, one owner, handed forward."""
errs, warns = [], []
name = os.path.basename(path)
name = os.path.relpath(path, STOCK_G)
try:
idx = json.load(open(path))
except Exception as e:
return [f"{name}: does not parse: {e}"], []
return [f"{name}: does not parse: {e}"], [], None # present-but-unparseable ⇒ FAIL
d = os.path.dirname(path)
# ── provenance (charter law #3) — every atlas carries it, and the gate PRINTS the licence ──
miss = [f for f in REQUIRED_PROV if not str(idx.get(f, "")).strip()]
if miss:
errs.append(f"{name}: missing/empty provenance {miss} (charter law #3 — every atlas carries provenance)")
# ── the NAME must be one the runtime can fetch: stock_<type>_index.json, type ∈ STOCK_TYPES ──
fname = os.path.basename(path)
ftype = fname[len("stock_"):-len("_index.json")]
if ftype not in STOCK_TYPES:
errs.append(f"{name}: unloadable name — the runtime fetches `stock_<type>_index.json` for "
f"type ∈ {sorted(STOCK_TYPES)}; {ftype!r} is never fetched (this is the R23 drift)")
elif idx.get("kind") not in (None, ftype):
errs.append(f"{name}: kind {idx.get('kind')!r} != the filename's type {ftype!r} — the filename "
f"picks the stock slot, so they must agree")
# ── C's draw ceiling (LANE_C_PUB §7.1) ──
atlases = idx.get("atlases", [])
# ── identity: the per-shop base IS the godverse shop id (the loader keys on it) ──
shop_dir = os.path.basename(d)
sid = (idx.get("shop") or {}).get("godverse_id")
if sid is None:
errs.append(f"{name}: shop.godverse_id missing — the loader's base is the shop id; the atlas must say who it is")
elif str(sid) != shop_dir:
errs.append(f"{name}: shop.godverse_id {sid!r} != its directory {shop_dir!r} "
f"(base `stock_godverse/{shop_dir}/` is the cache identity — they must agree)")
# ── C §7.2b: `sourcing` — real vs mint, and the separation that makes it honest ──
# Vacuous-gate law: no `sourcing` ⇒ FAIL. A crate that cannot say where it came from must never pass
# as one that can; "plausible" silently reading as "real" is the whole failure this field prevents.
sourcing = idx.get("sourcing")
if sourcing not in SOURCING:
errs.append(f"{name}: `sourcing` is {sourcing!r} — C §7.2b requires top-level "
f"`sourcing` ∈ {sorted(SOURCING)} on every real-stock atlas. Without it a seeded mint "
f"crate is indistinguishable from that shop's actual POS inventory.")
else:
claims = [".".join(p) for p in POS_CLAIM_FIELDS if _dig(idx, p) not in (None, "", {}, [])]
if sourcing == "mint" and claims:
errs.append(f"{name}: sourcing='mint' but it carries POS-snapshot field(s) {claims} — C §7.2b: "
f"a mint crate is a seeded sample and must never claim a shop's real inventory. "
f"The separation IS the honesty.")
if sourcing == "real" and not claims:
errs.append(f"{name}: sourcing='real' but it carries none of the POS-snapshot fields "
f"{['.'.join(p) for p in POS_CLAIM_FIELDS]} — C §7.2b asserts both directions: a "
f"'real' crate must show the POS provenance it claims.")
# ── provenance: the FOUR gate fields, TOP LEVEL, US `license` (C §7.3 + charter law #3) ──
# `snapshot` pins determinism (the atlas is re-bakeable); `license`/`attribution` are the human-facing
# pair every town cache already carries. The nested `provenance{}` block (G's source / snapshot_sha256
# / determinism detail) is colour per C — good to keep, never the gate.
for f in PROV_FIELDS:
if not str(idx.get(f, "")).strip():
errs.append(f"{name}: top-level `{f}` missing/empty — C §7.3 makes the four provenance gate "
f"fields {list(PROV_FIELDS)} the contract (charter law #3: every atlas carries provenance)")
# British `licence` is RETIRED (C §7.3). NO fallback, deliberately: R24's fallback is exactly what let
# the check and the print drift onto different spellings and silence the flag. One truth, said loudly.
for where, obj in (("top-level", idx), ("provenance", idx.get("provenance") or {})):
if isinstance(obj, dict) and "licence" in obj:
errs.append(f"{name}: British `licence` found at {where} — retired by C §7.3; the contract is "
f"top-level US `license`. (R25: a spelling fork here silently hid the release flag.)")
# ── C §7.2 ceilings ──
atlases = idx.get("atlases")
if not isinstance(atlases, list) or not atlases:
errs.append(f"{name}: no atlases[] listed")
atlases = []
if len(atlases) > MAX_ATLASES:
errs.append(f"{name}: {len(atlases)} atlases — C's hard cap is {MAX_ATLASES} "
f"(1/shop; a 60+ item shop spills to the shared type-pack instead of fragmenting)")
elif len(atlases) == 2:
warns.append(f"{name}: 2 atlases (+1 draw) — C's rule is 1/shop; 2 is the cap, not the target")
items = idx.get("items", [])
items = idx.get("items")
if not isinstance(items, list) or not items:
errs.append(f"{name}: no items")
items = []
# ── C's texture/VRAM ceiling (§7.2) + index↔atlas consistency (the declared size must be REAL) ──
if len(atlases) > MAX_ATLASES:
errs.append(f"{name}: {len(atlases)} atlases — C §7.2 caps a shop at {MAX_ATLASES}")
declared = idx.get("atlas_px")
for a in atlases:
ap = os.path.join(d, a)
if not os.path.isfile(ap):
errs.append(f"{name}: atlas {a!r} listed but missing on disk")
continue
errs.append(f"{name}: atlas {a!r} listed but missing on disk"); continue
try:
w, h = image_size(ap)
except Exception as e:
errs.append(f"{name}: atlas {a!r} unreadable: {e}")
continue
errs.append(f"{name}: atlas {a!r} unreadable: {e}"); continue
if declared and (w != declared or h != declared):
errs.append(f"{name}: atlas {a!r} is {w}×{h} but index declares atlas_px={declared}")
big = len(items) >= BIG_SHOP_ITEMS
cap = PX_MAX if big else PX_TYPICAL
if max(w, h) > cap:
errs.append(f"{name}: atlas {a!r} {w}×{h} exceeds C's ceiling {cap}² "
f"({'60+ item shop' if big else f'{len(items)}-item shop → ≤{PX_TYPICAL}²'})")
if max(w, h) > PX_TYPICAL and not big:
warns.append(f"{name}: atlas {a!r} is {w}×{h} for {len(items)} items — prefer ≤{PX_TYPICAL}²")
if max(w, h) > PX_MAX:
errs.append(f"{name}: atlas {a!r} {w}×{h} exceeds C's ceiling {PX_MAX}²")
elif max(w, h) > PX_TYPICAL and len(items) <= FITS_1024:
warns.append(f"{name}: atlas {a!r} is {w}×{h} for {len(items)} items — C: ≤{PX_TYPICAL}² "
f"whenever the crate fits it (≤{FITS_1024} at 256²)")
# ── the index itself (schema confirmed verbatim in LANE_C_PUB §7.3) ──
# ── the index itself (schema per the loader: it reads items[].uv/atlas; `cell`/`cell_w` are informational) ──
ids = set()
for i, it in enumerate(items):
if not isinstance(it, dict):
@ -133,30 +269,182 @@ def validate_index(path):
if not (isinstance(uv, list) and len(uv) == 4 and all(isinstance(v, (int, float)) for v in uv)):
errs.append(f"{name}[{it['id']}]: uv must be [u0,v0,u1,v1] numbers"); continue
u0, v0, u1, v1 = uv
if not (0 <= u0 < u1 <= 1 and 0 <= v0 < v1 <= 1): # UV origin top-left; C flips V (R7 law)
if not (0 <= u0 < u1 <= 1 and 0 <= v0 < v1 <= 1): # origin top-left; stockpack flips V (R7 law)
errs.append(f"{name}[{it['id']}]: uv out of range / inverted: {uv}")
return errs, warns
# ── C §7.2a: the slot-id form. Aggregated, not per-item: 120 identical errors bury the one that
# matters. Uniqueness WITHIN the pack is the dup check above; these two are the cross-pack half.
if ids:
# The fence can only be checked once the space is known. An unknown `sourcing` already FAILS above
# (§7.2b), so this doesn't leak a pass — but it must not silently half-run either: no space, no
# verdict on which prefix belongs.
want = SLOT_ID_PREFIX.get(sourcing)
if want:
wrong = sorted(i for i in ids if not str(i).startswith(want))
if wrong:
# Name the fence-crossing specifically when the crate wears the OTHER space's prefix — that
# is a different (and worse) fault than a merely positional id, and the message should say
# which one it is rather than make the reader guess.
other = {p for s, p in SLOT_ID_PREFIX.items() if s != sourcing}
crossed = sorted(i for i in wrong if any(str(i).startswith(p) for p in other))
if crossed:
errs.append(f"{name}: sourcing={sourcing!r} but {len(crossed)}/{len(ids)} slot id(s) wear "
f"another space's prefix (e.g. {crossed[:3]}) — C §7.2a (R26 w4): prefixes are "
f"NAMESPACE FENCES. `sku_` is a POS copy on a shelf; `mint_` is a dealgod "
f"listing standing in for one. A crate wearing the other's prefix is not a "
f"naming slip: tier-2's sold-means-gone looks up POS skus, so this sells a "
f"real record out of a real shop to satisfy a minted stand-in.")
else:
errs.append(f"{name}: sourcing={sourcing!r} ⇒ ids must be `{want}`-prefixed, but "
f"{len(wrong)}/{len(ids)} are not (e.g. {wrong[:3]}) — C §7.2a: an id derives "
f"from its source's stable key, never from position. Positional ids renumber "
f"on every re-bake, so anything holding one across snapshots (a wallet "
f"purchase, tier-2's sold-means-gone) points at a different record.")
gen = generic_pack_ids()
if not gen:
# the vacuous-gate law: if the other side of "unique across ALL packs" isn't here, say so —
# never let an unverifiable check read as a passing one.
warns.append(f"{name}: cross-pack uniqueness (C §7.2a) NOT verified — no generic packs matched "
f"{os.path.relpath(GENERIC_PACKS, ROOT)}")
else:
clash = sorted(ids & set().union(*gen.values()))
if clash:
errs.append(f"{name}: {len(clash)}/{len(ids)} slot id(s) also exist in the generic pack(s) "
f"{sorted(gen)} (e.g. {clash[:3]}) — C §7.2a requires ids unique across ALL "
f"packs. While they collide, an id-equality gate cannot tell which pack a "
f"rendered item came from: that is exactly what made R24's #7a spec vacuous.")
return errs, warns, (str(idx.get("license", "")).strip() or None)
def load_manifest():
"""→ (manifest_dict_or_None, errs). C §7.2d: `stock_godverse/index.json` is the authority for which
shops carry atlases no entry, no fetch, so no 404 and no attribution exception. Absent is a legitimate
state (a tree with no godverse atlases); absent-while-atlases-exist is not, and main() catches that."""
if not os.path.isfile(MANIFEST):
return None, []
try:
man = json.load(open(MANIFEST))
except Exception as e:
return None, [f"index.json: the atlas manifest does not parse: {e} — C §7.2d makes it the "
f"authority for what the runtime may fetch; an unreadable authority is a FAIL, "
f"not a fall-back-to-probing"]
errs = []
if not isinstance(man.get("shops"), list):
errs.append("index.json: no `shops[]` — C §7.2d shape is {version, shops:[{godverseShopId, types[], sourcing}]}")
man["shops"] = []
return man, errs
def check_manifest(idxs, man):
"""manifest ↔ files, BOTH ways (C §7.2d → Lane E). An atlas on disk absent from the manifest FAILS (the
runtime would never fetch it it would silently never load); a manifest entry with no atlas FAILS (the
runtime would fetch it and 404, which is the noise the manifest exists to kill)."""
errs = []
on_disk = {} # {shop_id: {type: index_path}}
for p in idxs:
sid = os.path.basename(os.path.dirname(p))
t = os.path.basename(p)[len("stock_"):-len("_index.json")]
on_disk.setdefault(sid, {})[t] = p
declared = {}
for i, e in enumerate(man.get("shops", [])):
if not isinstance(e, dict) or e.get("godverseShopId") is None:
errs.append(f"index.json shops[{i}]: no `godverseShopId`"); continue
# standing note (R26): godverseShopId lives in the GODVERSE namespace and shares a numeric space
# with plan ids — str() for the directory match, never a bare cross-namespace number compare.
sid = str(e["godverseShopId"])
types = e.get("types")
if not isinstance(types, list) or not types:
errs.append(f"index.json[{sid}]: no `types[]` — C §7.2d declares which packs exist"); continue
declared[sid] = e
for t in types:
if t not in on_disk.get(sid, {}):
errs.append(f"index.json[{sid}]: declares type {t!r} but "
f"stock_godverse/{sid}/stock_{t}_index.json is NOT on disk — the wire would "
f"fetch it and 404, which is exactly the noise C §7.2d exists to kill")
if e.get("sourcing") is not None and e["sourcing"] not in SOURCING:
errs.append(f"index.json[{sid}]: sourcing {e['sourcing']!r}{sorted(SOURCING)}")
for sid, types in sorted(on_disk.items()):
if sid not in declared:
errs.append(f"stock_godverse/{sid}/: {len(types)} atlas index(es) on disk but the shop is ABSENT "
f"from index.json — C §7.2d: no entry, no fetch, so this crate would silently never "
f"load. An unreachable atlas is not a passing atlas.")
continue
for t, p in sorted(types.items()):
if t not in declared[sid].get("types", []):
errs.append(f"stock_godverse/{sid}/stock_{t}_index.json on disk but type {t!r} is not in "
f"index.json's types {declared[sid].get('types')} — the runtime would never fetch it")
# the manifest's per-shop sourcing must not contradict the index's own (E's addition — flagged to C)
want = declared[sid].get("sourcing")
for t, p in sorted(types.items()):
try:
got = json.load(open(p)).get("sourcing")
except Exception:
continue
if want is not None and got is not None and want != got:
errs.append(f"index.json[{sid}] says sourcing={want!r} but stock_{t}_index.json says "
f"{got!r} — one of them tells a human a mint crate is real")
return errs
def main():
idxs = sorted(glob.glob(os.path.join(STOCK_G, "**", "stock_shop_*_index.json"), recursive=True))
idxs = sorted(glob.glob(GLOB))
man, man_errs = load_manifest()
if not idxs:
print("atlas-QA: no per-shop atlases yet (Lane G's G2a) — nothing to validate")
# THE VACUOUS-GATE LAW (R23, F): absent subject ⇒ SKIP with a printed reason — and prove the set
# really is empty. Files present but unmatched = a misnamed atlas the loader can't fetch ⇒ FAIL.
stray = [p for p in glob.glob(os.path.join(STOCK_G, "**", "*"), recursive=True)
if os.path.isfile(p) and os.path.abspath(p) != os.path.abspath(MANIFEST)]
if stray:
print(f"atlas-QA FAIL — {len(stray)} file(s) under stock_godverse/ but 0 matched {GLOB}")
print(f" the runtime fetches `<base>stock_<type>_index.json`; these cannot be loaded:")
for p in stray[:8]:
print(f" unmatched: {os.path.relpath(p, ROOT)}")
return 1
# A manifest that PROMISES atlases while none exist must not skip quietly — the wire would fetch
# every entry and 404. C's "absent manifest ⇒ legitimate zero-shop state" covers the empty tree,
# not a manifest whose declarations are all unbacked. (Vacuous-gate law: the subject is absent, but
# something on disk still asserts it exists — that is a FAIL, not a SKIP.)
if man_errs or (man and man.get("shops")):
for e in man_errs + check_manifest([], man or {}):
print(f" ERR {e}")
print(f"atlas-QA FAIL — the manifest declares atlases that are not on disk (0 matched {GLOB})")
return 1
print(f"atlas-QA SKIP: no atlases present (0 files matched glob {GLOB})"
+ ("" if man is None else "; manifest present and empty"))
return 0
all_errs, all_warns = [], []
all_errs, all_warns = list(man_errs), []
if man is None:
all_errs.append(f"{len(idxs)} atlas index(es) on disk but web/assets/stock_godverse/index.json is "
f"MISSING — C §7.2d makes the manifest the authority for what the runtime may "
f"fetch. With no manifest the wire either probes blind (the 404 noise §7.2d kills) "
f"or never fetches these at all. Absent is only legitimate when there are no atlases.")
else:
all_errs += check_manifest(idxs, man)
printed = 0 # licence evidence lines actually emitted — see the tally below
for p in idxs:
e, w = validate_index(p)
e, w, evidence = validate_index(p)
all_errs += e; all_warns += w
idx = {}
try:
idx = json.load(open(p))
except Exception:
pass
print(f" {os.path.basename(p):40} items={len(idx.get('items', [])):>3} "
idx = {}
name = os.path.relpath(p, STOCK_G)
shop = (idx.get("shop") or {}).get("name", "?")
print(f" {name:44} {shop[:22]:22} items={len(idx.get('items', [])):>3} "
f"atlases={len(idx.get('atlases', []))} px={idx.get('atlas_px')} "
f"snapshot={str(idx.get('snapshot', ''))[:24]} {'OK' if not e else 'ERR(' + str(len(e)) + ')'}")
if idx.get("license"): # licence law: the gate SHOWS it, a human clears it
print(f" licence: {idx['license']}")
f"{'OK' if not e else 'ERR(' + str(len(e)) + ')'}")
# THE EVIDENCE LINE. Clearing photos for public release is a human call (charter law #3), so the
# gate SURFACES the licence rather than pattern-matching it — but surfacing is mandatory and
# counted. `evidence` comes from validate_index, which also OWNS the missing-`license` error
# (PROV_FIELDS) — main does not second-guess it with a lookup of its own. That is the whole point:
# a missing licence is reported once, by one owner, and the tally below catches the day that owner
# ever goes permissive.
if evidence:
print(f" LICENCE: {evidence}")
att = str(idx.get("attribution", "")).strip()
if att:
print(f" attribution: {att}")
printed += 1
for w in all_warns:
print(f" WARN {w}")
for e in all_errs:
@ -164,7 +452,18 @@ def main():
if all_errs:
print(f"atlas-QA FAIL — {len(all_errs)} error(s), {len(all_warns)} warning(s)")
return 1
print(f"atlas-QA OK — {len(idxs)} per-shop atlas(es) valid, {len(all_warns)} warning(s)")
# THE TALLY — the vacuous-gate law turned on this gate itself, and REACHABLE (R25: my first cut of it
# wasn't). It sits AFTER the `all_errs` return on purpose: while the field check works, a missing
# licence fails above with a precise message and the tally is quiet. The tally exists for the one case
# that check can't report — the day it goes permissive again (R24's `licence or license` fallback was
# exactly that) and passes an atlas whose licence never printed. Judged N ⇒ N lines shown, or FAIL.
# Tested by stubbing validate_index permissive: without this, that atlas ships silently cleared.
if printed != len(idxs):
print(f"atlas-QA FAIL — judged {len(idxs)} atlas(es) but printed {printed} licence line(s). The "
f"flag guards real product photos and MUST reach a human for every atlas judged.")
return 1
print(f"atlas-QA OK — {len(idxs)} per-shop atlas(es) valid, {printed} licence line(s) shown, "
f"{len(all_warns)} warning(s)")
return 0

42
tools/deploy_digalot_procity.sh Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# PROCITY → digalot.fyi/procity — PRIVATE TEST deploy (Fable, R29-era).
# John's ruling: private test only, no payments, NO SEO/indexing. The licence flag
# (docs/V5_REAL_SHOP.md law #3) stays ARMED for any real public release.
#
# Usage: tools/deploy_digalot_procity.sh # rsync + install nginx block + verify
# tools/deploy_digalot_procity.sh --dry # show what would ship
#
# Ship-check status (run 2026-07-17): static files only — no endpoints, no auth
# surface, no money paths. Secrets sweep clean (no .env/.sql; grep hits were stock
# titles). _raw/ Overpass snapshots excluded (pipeline provenance, not runtime).
set -euo pipefail
VPS="root@100.94.195.115"
DEST="/opt/procity/web/"
HERE="$(cd "$(dirname "$0")/.." && pwd)"
DRY=""; [ "${1:-}" = "--dry" ] && DRY="--dry-run"
echo "== 1/4 rsync web/ -> $VPS:$DEST (excluding pipeline provenance + junk)"
rsync -az --delete $DRY \
--exclude '_raw/' --exclude '.DS_Store' --exclude '*.map' \
"$HERE/web/" "$VPS:$DEST"
[ -n "$DRY" ] && { echo "(dry run — stopping)"; exit 0; }
echo "== 2/4 install nginx location (noindex, no autoindex, no listing)"
ssh "$VPS" 'CONF=$(docker exec dealgod-nginx sh -c "grep -rl digalot /etc/nginx/conf.d/" | head -1); \
echo "digalot server block: $CONF"; \
docker exec dealgod-nginx sh -c "grep -q location\\ /procity/ $CONF" 2>/dev/null && { echo "location exists — skipping insert"; exit 0; }; \
docker exec dealgod-nginx sh -c "sed -i \"/server_name.*digalot/a\\ location /procity/ { alias /srv/procity/web/; autoindex off; add_header X-Robots-Tag \\\"noindex, nofollow, noarchive\\\" always; }\" $CONF"'
# NOTE: the alias path above must be the path AS THE CONTAINER SEES IT. If /opt/procity
# is not already bind-mounted into dealgod-nginx, add a mount (or symlink under an
# existing mounted root) BEFORE reload — the installer checks and tells you.
echo "== 3/4 robots: ensure digalot.fyi/robots.txt disallows /procity/"
ssh "$VPS" 'R=/opt/procity/robots-note.txt; echo "add to digalot robots.txt: Disallow: /procity/" > $R; \
docker exec dealgod-nginx sh -c "nginx -t" && docker exec dealgod-nginx nginx -s reload'
echo "== 4/4 verify with own eyes (cache-busted)"
curl -sf -o /dev/null -w "index: %{http_code}\n" "https://digalot.fyi/procity/?cb=$(date +%s)"
curl -sf -o /dev/null -w "module: %{http_code} %{content_type}\n" "https://digalot.fyi/procity/js/citygen/index.js?cb=$(date +%s)"
curl -sI "https://digalot.fyi/procity/" | grep -i x-robots-tag || echo "WARN: noindex header missing"
echo "DONE — boot https://digalot.fyi/procity/ in a browser; classic check: ?classic=1"

File diff suppressed because it is too large Load Diff

View File

@ -24,10 +24,10 @@ Run: tools/.venv/bin/python tools/qa/interior_scale_check.py [--seed N]
Setup: the same Playwright venv as tools/soak.py / tools/flags_check.py.
Needs Lane B's window.DBG hook (?dbg=1) + window.PROCITY.interiorMode (Lane C/F).
"""
import sys, time, socket, subprocess, pathlib
import sys, os, time, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = 8130
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
if '--seed' in sys.argv:

View File

@ -222,13 +222,13 @@ if (existsSync(R('web/js/citygen/plan.js'))) {
`${p1?.streets?.edges?.length ?? '?'} edges, name "${p1?.name ?? '?'}"`);
// [F2/R8] plan-source selector — pin goldens keyed by (seed, plansrc, TOWN).
// Lane A contract: synthetic 0x3fa36874 (selfcheck) · osm/melbourne 0x34cfdec0 · osm/katoomba 0x0f652510.
// Lane A contract: synthetic 0x5f76e76 (selfcheck) · osm/melbourne 0x9c7e76b3 · osm/katoomba 0xf3aafec8.
// Guarded: no-op until A ships generatePlanFor + generatePlanOSM.
try {
const bar = await import(R('web/js/citygen/index.js'));
if (typeof bar.generatePlanFor === 'function' && typeof bar.generatePlanOSM === 'function') {
const { xmur3 } = await import(R('web/js/core/prng.js'));
const TOWN_GOLDENS = { melbourne: '0x34cfdec0', katoomba: '0x0f652510' };
const TOWN_GOLDENS = { melbourne: '0x9c7e76b3', katoomba: '0xf3aafec8' };
for (const [town, golden] of Object.entries(TOWN_GOLDENS)) {
const a = bar.generatePlanFor(20261990, 'osm', { town }), b2 = bar.generatePlanFor(20261990, 'osm', { town });
assert(JSON.stringify(a) === JSON.stringify(b2),

View File

@ -7,7 +7,7 @@ The v4 thesis in five frames, into docs/shots/release_v4/ (+ contact.html). The
fitzroy_mecca the mecca (160 shops, 2× anything absorbed before this epoch)
toowoomba_thintail the thin tail (12 shops): proof the floor still reads as a town
synthetic_control the procedural town, UNCHANGED: the control the whole epoch was measured against
classic_covenant `?classic=1`: the v2 covenant made visible, byte-identical forever (0x3fa36874)
classic_covenant `?classic=1`: the v2 covenant made visible, byte-identical forever (0x5f76e76)
Posing: every real-town frame resolves through Lane B's `DBG` retail-heart anchor / `clusterPose` — the
primitive that came out of F's R21 finding (per-EDGE shop counts are ~1 on a real graph, so "densest edge"

200
tools/qa/tour_v5.py Normal file
View File

@ -0,0 +1,200 @@
#!/usr/bin/env python3
"""PROCITY v5.0 RELEASE TOUR — THE LIVE CRATE (Lane F, R27 ledger #5)
The crate walk, in the order you'd actually do it: Musgrave Road → Monster Robot Party's frontage (B's
stocked-shopfront mark) inside, riffling ITS OWN records a mint shop, captioned honestly the
`?classic=1` control that still boots the exact v2 town.
Posing: the street frames go through F's R21 cluster primitive (reused from tour_shots.py). Per-EDGE shop
counts are ~1 on a real graph, so "the busiest edge" poses you at bare bitumen the Fitzroy trap, twice
learned. The interior frames pose themselves: the room builds you a metre inside its own door.
Honesty rules baked in, not bolted on:
· the mint frame says MINT in its caption. A minted crate is plausible, not real, and a tour is exactly
where that distinction gets quietly lost.
· the real frame's caption carries the shop's actual name and address, because it IS one.
· nothing here boots ?live=1. A tour frame must be reproducible by anyone who clones this repo, and
tier 2 needs a POS none of them have. The live tier is proven by the gate, which owns its own server;
the tour shows tier 1, which is what ships in the box.
"""
import json, subprocess, sys, time, pathlib
ROOT = pathlib.Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / 'tools' / 'qa'))
PORT = 8131
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
OUTDIR = ROOT / 'docs' / 'shots' / 'release_v5'
SHOP = 3962749
# name, query, segment, mode, caption
V5_TOUR = [
('musgrave_road', f'plansrc=osm&town=redhill_godverse&stock=real&seed={SEED}', 3, f'frontage:{SHOP}',
"the frontage — Monster Robot Party on Musgrave Road, Red Hill, Brisbane. It is on this road in the "
"game because it is on this road"),
('the_real_crate', f'plansrc=osm&town=redhill_godverse&stock=real&dig=1&seed={SEED}', 3, f'shop:{SHOP}',
"THE REAL CRATE — Monster Robot Party (147 Musgrave Rd): 120 records off its own till, real covers, "
"real titles, real prices. Sell one in the shop and it leaves this crate"),
('a_mint_crate', f'plansrc=osm&town=redhill_godverse&stock=real&dig=1&seed={SEED}', 3, 'mint',
"a MINT crate — plausible, NOT real: a seeded sample standing in for a shop whose till we don't hold. "
"Same tier, same offline guarantee, different provenance — and the atlas says so in its own index"),
('classic_covenant', f'classic=1&seed={SEED}', 2, 'street',
"?classic=1 — the exact v2 town, byte-identical forever (0x5f76e76). Five epochs, one covenant"),
]
SHOT_JS = r"""
async ({ mode, seg, shop }) => {
const P = window.PROCITY, D = window.DBG, T = P.THREE;
D.setSegment(seg);
const plan = P.plan, shops = plan.shops || [];
if (mode.startsWith('shop:') || mode === 'mint') {
let target;
if (mode === 'mint') {
const man = await (await fetch('assets/stock_godverse/index.json')).json();
const mint = new Set((man.shops||[]).filter(s => s.sourcing === 'mint').map(s => s.godverseShopId));
const byId = new Map((man.shops||[]).map(s => [s.godverseShopId, s]));
target = shops.find(s => mint.has(s.godverseShopId) &&
(byId.get(s.godverseShopId).types||[]).includes(s.type));
} else {
target = shops.find(s => s.godverseShopId === shop);
}
if (!target) return { err: 'no target shop for ' + mode };
D.enterShop('g:' + target.godverseShopId);
await new Promise(r => setTimeout(r, 1500));
const si = D.stockInfo();
// pose: stand back from the spawn looking into the room, so the crates are in frame
const im = P.interiorMode, sp = im.current.spawn;
P.camera.position.set(sp.x, 1.6, sp.z + 0.2);
P.camera.rotation.set(0, sp.ry || 0, 0);
P.camera.updateMatrixWorld();
// Beauty shot: hide EVERY overlay, not just the street HUD. R21 taught this on the street frames
// and F only fixed it there the first v5 interior frame came back with "press Esc to leave"
// across the top of the money shot. Same lesson, second surface.
// Hide EVERY DOM overlay rather than guessing ids the interior banner is created at runtime, so a
// hardcoded selector list is exactly how the HUD sneaks back into the next money shot.
for (const el of document.body.children) if (el.tagName !== 'CANVAS') el.style.display = 'none';
for (let i = 0; i < 3; i++) await new Promise(r => requestAnimationFrame(r));
const info = D.info();
return { name: si.name, gid: si.godverseShopId, items: si.packItems, base: si.base,
shops: shops.length, edges: (plan.streets.edges||[]).length,
draws: info.drawCalls, tris: info.tris };
}
// street
// [R27w5] The frontage frame now goes through B's `poseForShopFront` — B's helper, B's geometry, B's
// shop. F re-derived this three times and produced three photographs of empty road; the fourth attempt
// would have been the same mistake with more arithmetic. The lane that owns the frontage owns the pose.
// (And the empty road was never the pose at all the shops faced backwards. Fixed by A, wave 5.)
const nById = new Map(plan.streets.nodes.map(n => [n.id, n]));
const lots = (plan.lots || []).filter(l => l.use === 'shop' || l.use === 'anchor');
let hub = -1, best = null;
if (mode.startsWith('frontage:')) {
const r = D.poseForShopFront('g:' + mode.split(':')[1], 8);
if (!r || r.error) return { err: 'poseForShopFront: ' + JSON.stringify(r) };
// poseForShopFront RETURNS a pose; it does not apply one. F called it, error-checked it, and threw
// the result away so the camera never moved and the frame was the spawn paddock. A helper that
// hands you a value is not a helper that does the thing.
P.camera.position.set(r.pos[0], r.pos[1], r.pos[2]);
P.camera.lookAt(r.look[0], r.look[1], r.look[2]);
P.camera.updateMatrixWorld();
const sh = shops.find(x => x.godverseShopId === +mode.split(':')[1]);
const lot = sh ? (plan.lots || []).find(l => l.id === sh.lot) : null;
hub = lot ? lots.filter(o => Math.hypot(o.x - lot.x, o.z - lot.z) < 60).length : -1;
} else {
for (const l of lots) { let c = 0; for (const o of lots) if (Math.hypot(o.x - l.x, o.z - l.z) < 60) c++;
if (c > hub) { hub = c; best = l; } }
if (best) {
const e = (plan.streets.edges || []).find(x => x.id === best.frontEdge) || plan.streets.edges[0];
const a = nById.get(e.a), b2 = nById.get(e.b);
const dx = b2.x - a.x, dz = b2.z - a.z, L = Math.hypot(dx, dz) || 1, ux = dx / L, uz = dz / L;
let t = (best.x - a.x) * ux + (best.z - a.z) * uz;
t = Math.max(6, Math.min(L - 6, t));
const back = Math.min(26, t);
D.teleport(a.x + ux * (t - back), a.z + uz * (t - back), Math.atan2(ux, uz));
} else { D.shot('street_noon'); }
}
await new Promise(r => setTimeout(r, 1800));
for (const el of document.body.children) if (el.tagName !== 'CANVAS') el.style.display = 'none';
for (let i = 0; i < 3; i++) await new Promise(r => requestAnimationFrame(r));
const info = D.info();
return { name: plan.name, shops: shops.length, edges: (plan.streets.edges || []).length,
hub, draws: info.drawCalls, tris: info.tris };
}
"""
def ensure_server():
import socket
s = socket.socket()
try:
s.connect(('127.0.0.1', PORT)); s.close(); return None
except Exception:
pass
srv = subprocess.Popen([sys.executable, '-m', 'http.server', str(PORT)],
cwd=str(ROOT / 'web'), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(1.2)
return srv
def main():
try:
from playwright.sync_api import sync_playwright
except ImportError:
sys.exit('playwright not installed')
print(f"\033[1mv5.0 RELEASE TOUR — THE LIVE CRATE\033[0m ({len(V5_TOUR)} frames → docs/shots/release_v5/)")
OUTDIR.mkdir(parents=True, exist_ok=True)
srv = ensure_server()
shots, errs_total = [], 0
try:
with sync_playwright() as p:
b = p.chromium.launch()
for name, query, seg, mode, cap in V5_TOUR:
pg = b.new_page(viewport={'width': 1280, 'height': 720})
errs = []
pg.on('pageerror', lambda e: errs.append(str(e)))
pg.on('console', lambda m: errs.append(m.text) if m.type == 'error' else None)
pg.goto(f'{HOST}/index.html?{query}&dbg=1')
pg.wait_for_function('window.PROCITY && window.PROCITY.plan && window.DBG && window.DBG.ready',
timeout=25000)
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
try:
pg.wait_for_function('() => !window.PROCITY.fleet || window.PROCITY.fleet.ready', timeout=15000)
except Exception:
pass
r = pg.evaluate(SHOT_JS, {'mode': mode, 'seg': seg, 'shop': SHOP})
if r.get('err'):
print(f" \033[31m✗\033[0m {name}: {r['err']}"); errs_total += 1; pg.close(); continue
pg.screenshot(path=str(OUTDIR / f'{name}.png'))
extra = (f" · {r['items']} records from {r['base']}" if r.get('items')
else (f" · retail heart: {r['hub']} shops/60m" if r.get('hub') else ''))
caption = (f"{cap} · {r['name']}{extra} · {r['draws']} draws · {r['tris']:,} tris")
shots.append({'name': name, 'file': f'{name}.png', 'caption': caption})
print(f" \033[32m●\033[0m {name}: {caption}")
errs_total += len(errs)
if errs:
print(f" \033[31m{len(errs)} page error(s)\033[0m: {errs[0][:110]}")
pg.close()
b.close()
finally:
if srv:
srv.terminate()
html = ["<!doctype html><meta charset=utf-8><title>PROCITY v5.0 — THE LIVE CRATE</title>",
"<style>body{background:#111;color:#ddd;font:15px/1.6 -apple-system,sans-serif;max-width:1100px;"
"margin:40px auto;padding:0 20px}img{width:100%;border-radius:6px;margin:10px 0}"
"h1{font-weight:600}p{color:#aaa}hr{border:0;border-top:1px solid #333;margin:36px 0}</style>",
"<h1>PROCITY v5.0 — THE LIVE CRATE</h1>",
"<p>One real record shop, on its real street, holding its real stock — and a server that can die "
"mid-session without the game noticing.</p>"]
for s in shots:
html.append(f"<hr><h3>{s['name']}</h3><img src='{s['file']}'><p>{s['caption']}</p>")
(OUTDIR / 'contact.html').write_text('\n'.join(html))
(OUTDIR / 'shots.json').write_text(json.dumps(shots, indent=1))
print(f"\n{OUTDIR}/contact.html ({len(shots)} frames, {errs_total} page error(s))")
return 0 if (len(shots) == len(V5_TOUR) and errs_total == 0) else 1
if __name__ == '__main__':
sys.exit(main())

View File

@ -16,10 +16,10 @@ Gates per town (all on the DEFAULT boot — gigs/weather/winmap/tram on, post-R1
Run: tools/.venv/bin/python tools/qa/town_matrix.py
Needs the Playwright venv + Lane B's ?dbg=1 hook + Lane F's real-cache shell wiring (R17).
"""
import sys, time, socket, subprocess, pathlib
import sys, os, time, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = 8130
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
HOST = f'http://127.0.0.1:{PORT}'
STREET_DRAWS_MAX, STREET_TRIS_MAX = 300, 200_000
# [R21 ledger #6c] B's R20 tram ruling: on a REAL-ROADS town the tram only runs if its best main chain

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_record_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Downtown Music's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_1082_record.snapshot.json",
"shop": {
"godverse_id": 1082,
"slug": "downtown-music",
"name": "Downtown Music",
"suburb": "Annandale",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 1082,
"sample": "random.Random(1082) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_1082_record.snapshot.json",
"snapshot_sha256": "92f9a9e09c857e2257ec7b35e9ca32899be0ee51d8a49babde0fed93b5eb5283",
"determinism": "pure bake; the sample itself is seeded random.Random(1082) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_1382327",
"title": "Building Beauty",
"artist": "Endgames",
"price": 25,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1497796",
"title": "True (The Faggot Is You)",
"artist": "Morel",
"price": 26,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1499132",
"title": "You & Me",
"artist": "Philippe B.",
"price": 8,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1510082",
"title": "Music Is Freedom Remixes",
"artist": "Richie Santana",
"price": 14,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_1572084",
"title": "Paco Pe\u00f1a - Flamenco Paco Pe\u00f1a (LP, Album)",
"artist": "Paco Pe\u00f1a",
"price": 12,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_1952439",
"title": "The Party Boys",
"artist": "The Party Boys (3)",
"price": 8,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_2634580",
"title": "Shakti With John McLaughlin = \u30a6\u30a3\u30ba\u30fb\u30b8\u30e7\u30f3\u30fb\u30de\u30af\u30e9\u30d5\u30ea\u30f3",
"artist": "Shakti",
"price": 25,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_2636233",
"title": "Donny Hathaway \u2013 Live (Rhino Reserve)",
"artist": "Donny Hathaway",
"price": 58,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_3375700",
"title": "I Love You Jennifer B",
"artist": "Jockstrap (4)",
"price": 60,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_3377454",
"title": "Charcoal Grace",
"artist": "Caligula's Horse",
"price": 90,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_5040486",
"title": "Fun Time",
"artist": "Anri (2)",
"price": 150,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_5077859",
"title": "Everything Is Alive",
"artist": "Slowdive",
"price": 25,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6319846",
"title": "A 3 Song Recording",
"artist": "The Black Heart Procession",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6364791",
"title": "Garford Mute",
"artist": "Scarcity Of Tanks",
"price": 32,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6511306",
"title": "The Wanted Man",
"artist": "Nick Cave & The Bad Seeds",
"price": 75,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_7329708",
"title": "Dirrty",
"artist": "Christina Aguilera",
"price": 8,
"price_band": "bargain",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_record_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Haze Guitars's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_1173_record.snapshot.json",
"shop": {
"godverse_id": 1173,
"slug": "haze-guitars",
"name": "Haze Guitars",
"suburb": "Newtown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 1173,
"sample": "random.Random(1173) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_1173_record.snapshot.json",
"snapshot_sha256": "342d7612904564f43b5e06fae3197caf88a6e864e5404fd1fe9e4501ec38c527",
"determinism": "pure bake; the sample itself is seeded random.Random(1173) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_735827",
"title": "Behind The Barricades",
"artist": "Kasey Chambers",
"price": 15,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1506171",
"title": "Just FX",
"artist": "Jas Van Houten",
"price": 33,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1572335",
"title": "Fats Waller In London (No. 2)",
"artist": "Fats Waller",
"price": 15,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1927813",
"title": "More Blood, More Tracks (The Bootleg Series Vol.14)",
"artist": "Bob Dylan",
"price": 75,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_1955485",
"title": "Stay With Me Heartache / I Feel Fine",
"artist": "Wet Wet Wet",
"price": 14,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_2949568",
"title": "Blondie Singles Collection: 1977-1982",
"artist": "Blondie",
"price": 29,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_3150782",
"title": "Jolene",
"artist": "Dolly Parton",
"price": 58,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_3150992",
"title": "Rose Pink Cadillac",
"artist": "Dope Lemon",
"price": 65,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_3261454",
"title": "Feature Magnetic",
"artist": "Kool Keith",
"price": 60,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_3274472",
"title": "Ayers Rock \u2013 Big Red Rock Vinyl LP (Used)",
"artist": "Ayers Rock",
"price": 45,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_4521621",
"title": "The Pleasure Of Your Company...",
"artist": "Models (2)",
"price": 37,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_6008610",
"title": "Love Me Like A Rocket",
"artist": "Kate Garner",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6308495",
"title": "Little River",
"artist": "Audrey Landers",
"price": 13,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6355830",
"title": "Butthole Surfers + PCPPEP",
"artist": "Butthole Surfers",
"price": 45,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6356386",
"title": "Around The Sun",
"artist": "R.E.M.",
"price": 87,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6841696",
"title": "Alice Cooper's Greatest Hits",
"artist": "Alice Cooper",
"price": 35,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_record_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Egg Records's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_158_record.snapshot.json",
"shop": {
"godverse_id": 158,
"slug": "egg-records",
"name": "Egg Records",
"suburb": "Newtown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 158,
"sample": "random.Random(158) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_158_record.snapshot.json",
"snapshot_sha256": "26c5c16e160d1955dd8424ed95fb22af340cd62aab8a0b15dc78ac07c70b5376",
"determinism": "pure bake; the sample itself is seeded random.Random(158) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_1490673",
"title": "High Time",
"artist": "MC5",
"price": 50,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1504320",
"title": "1 Thing",
"artist": "Amerie",
"price": 3,
"price_band": "bargain",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1505490",
"title": "Nasty Girl",
"artist": "Inaya Day",
"price": 13,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1555544",
"title": "Heartache All Over The World",
"artist": "Elton John",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_1960626",
"title": "The Very Best Of Johnny Rivers",
"artist": "Johnny Rivers",
"price": 12,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_3148069",
"title": "Sunburn",
"artist": "Dominic Fike",
"price": 69,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_3274969",
"title": "God Save The Queen",
"artist": "Sex Pistols",
"price": 50,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_3312435",
"title": "Death of a Party Girl",
"artist": "TV Girl",
"price": 70,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_5225870",
"title": "Systematic Chaos",
"artist": "Dream Theater",
"price": 135,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_6005806",
"title": "Physical Graffiti",
"artist": "Led Zeppelin",
"price": 115,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_6007579",
"title": "The Budos Band EP",
"artist": "The Budos Band",
"price": 52,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_6307515",
"title": "Shockwave Supernova",
"artist": "Joe Satriani",
"price": 80,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6512824",
"title": "Royal Trux",
"artist": "Royal Trux",
"price": 15,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6515893",
"title": "First We Take Manhattan",
"artist": "Jennifer Warnes",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6842418",
"title": "Any Day Now",
"artist": "Joan Baez",
"price": 32,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6974962",
"title": "Di Wo Ho Ni",
"artist": "Vis A Vis",
"price": 50,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_book_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Better Read Than Dead's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_2200_book.snapshot.json",
"shop": {
"godverse_id": 2200,
"slug": "better-read-than-dead",
"name": "Better Read Than Dead",
"suburb": "Newtown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2200,
"sample": "random.Random(2200) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_2200_book.snapshot.json",
"snapshot_sha256": "f2483807c6c8e28761d1801093644ae07a4b462259974483c331adf98bcb32e1",
"determinism": "pure bake; the sample itself is seeded random.Random(2200) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_831781",
"title": "The Fallen - David Baldacci - BPAP - BOO020",
"artist": "Lifeline Queensland",
"price": 8,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1925436",
"title": "Lodestars Anthology: New Zealand",
"artist": "Lodestars",
"price": 35,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1931996",
"title": "Audacity : Why Being Too Much Is Exactly Enough - Katherine Ryan",
"artist": "Katherine Ryan",
"price": 27,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1932007",
"title": "Why We Sleep : The New Science of Sleep and Dreams - Matthew Walker",
"artist": "Matthew Walker",
"price": 27,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_4069295",
"title": "99 TV Crosswords - A24",
"artist": "A24",
"price": 70,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_6728501",
"title": "Liars, cheats and copycatsTrickery and deception in nature",
"artist": "NewSouth Publishing",
"price": 35,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_6728751",
"title": "The Story of Growl",
"artist": "Brio Books",
"price": 19,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_6730340",
"title": "Ozzie Goes to School",
"artist": "HarperCollins Publishers Australia",
"price": 17,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_6962635",
"title": "Leonardo Da Vinci: the Anatomical Dra...",
"artist": "Royal Academy of Arts",
"price": 35,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_6962907",
"title": "Painting by Numbers: The life and art of Ferdinand",
"artist": "NewSouth Publishing",
"price": 14,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_6964245",
"title": "ECO STYLE",
"artist": "New Holland Publishers",
"price": 45,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_6966282",
"title": "Children of the World",
"artist": "teNeues Verlag",
"price": 125,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6967149",
"title": "Australia's Wild Places",
"artist": "National Library of Australia",
"price": 40,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6967659",
"title": "Fashion Central Saint Martins",
"artist": "Thames & Hudson, Limited",
"price": 60,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6969218",
"title": "Simple Natural Crochet: 35 Projects to Make - Gorge",
"artist": "Ryland Peters & Small",
"price": 35,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6999001",
"title": "Grow, Cook, Dye, Wear: From Seed To Style The Susta",
"artist": "Dorling Kindersley Publishing, Incorporated",
"price": 40,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_book_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Elizabeth's Bookshop's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_2201_book.snapshot.json",
"shop": {
"godverse_id": 2201,
"slug": "elizabeth-s-bookshop",
"name": "Elizabeth's Bookshop",
"suburb": "Newtown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2201,
"sample": "random.Random(2201) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_2201_book.snapshot.json",
"snapshot_sha256": "ad170343f039a5fe37b6cac8d804d5001ddaa8533dcb4a1a58a259a629d46770",
"determinism": "pure bake; the sample itself is seeded random.Random(2201) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_826954",
"title": "Lessons in Chemistry - Bonnie Garmus - BPAP - BOO008",
"artist": "Lifeline Queensland",
"price": 4,
"price_band": "bargain",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1931980",
"title": "Beaumaris Modern 2 - Fiona Austin",
"artist": "Fiona Austin",
"price": 65,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_2065183",
"title": "Slow Cooker: The Complete Collection - Australian Women's Weekly - BCOO - BOO053A",
"artist": "Lifeline Queensland",
"price": 10,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_3310554",
"title": "Jones, Lesley-Anne - Songbird: An Intimate Biography Of [Book]",
"artist": "GAR",
"price": 25,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_6109637",
"title": "Breakfast At Tiffany's (Popular Penguins) - Truman Capote",
"artist": "Truman Capote",
"price": 15,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_6109781",
"title": "Lorax - Dr Seuss",
"artist": "Dr Seuss",
"price": 12,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_6729688",
"title": "The Man Who Wasn't There",
"artist": "Ultimo Press",
"price": 37,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_6965715",
"title": "Matisse",
"artist": "Tate Publishing",
"price": 14,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_6966158",
"title": "More Dream Homes: 100 Inspirational Interiors",
"artist": "Merrell Publishers Limited",
"price": 100,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_6966174",
"title": "Wild: The Naturalistic Garden",
"artist": "Phaidon Press Limited",
"price": 80,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_6966273",
"title": "Tales from the Forest Kingdom Coloring Book",
"artist": "Gibbs Smith, Publisher",
"price": 25,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_6968094",
"title": "Ravens and Red Lipstick: Japanese Photography Since",
"artist": "Thames & Hudson, Limited",
"price": 60,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6968262",
"title": "A Fire Born of Exile - A Spellbinding Standalone Sci-Fi Romance and 2024 Hugo Award Finalist Perfect for Fans of Becky Chambers\\n by Aliette de Bodard",
"artist": "Orion Publishing Group, Limited",
"price": 25,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6968962",
"title": "Audrey Hepburn: Icons Of Style",
"artist": "HarperCollins Publishers",
"price": 17,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6994056",
"title": "Marina Abramovic: A Visual Biography",
"artist": "Hachette UK",
"price": 175,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_7035785",
"title": "Scorched Grace: A Sister Holiday Mystery",
"artist": "Margot Douaihy",
"price": 25,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

View File

@ -0,0 +1,273 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_record_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Papa Disquo's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_2212_record.snapshot.json",
"shop": {
"godverse_id": 2212,
"slug": "papa-disquo",
"name": "Papa Disquo",
"suburb": "Newtown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2212,
"sample": "random.Random(2212) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_2212_record.snapshot.json",
"snapshot_sha256": "a50aa47ca1f9d4a5234ba66f44513e208cd9be0e9dca77ae867b1ba881352a82",
"determinism": "pure bake; the sample itself is seeded random.Random(2212) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_1487770",
"title": "Rhythm And Stealth",
"artist": "Leftfield",
"price": 75,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
],
"condition": "Mint (M)\\n\\n \\n \\n \\n \\n Absolutely perfect in every way. Certainly never been played. Should be used sparingly as a grade. / sleeve Mint (M)"
},
{
"id": "mint_1508398",
"title": "Djengastyle#1",
"artist": "Djenga",
"price": 32,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1542765",
"title": "Fever",
"artist": "Peggy Lee",
"price": 6,
"price_band": "bargain",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1954799",
"title": "Not Ready For Prime Time: Live At The Cabaret Metro, 1986",
"artist": "The Replacements",
"price": 82,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_1955503",
"title": "Matt Bianco - Whose Side Are You On? (7\", Single)",
"artist": "Matt Bianco",
"price": 20,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_2635771",
"title": "Clifford Brown And Max Roach \u2013 Study In Brown (E/E)",
"artist": "Clifford Brown and Max Roach",
"price": 70,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_3153138",
"title": "Hours...",
"artist": "David Bowie",
"price": 45,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_3277085",
"title": "Armistice Day",
"artist": "Midnight Oil",
"price": 200,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_3277357",
"title": "How Much Longer",
"artist": "Re-Flex (2)",
"price": 40,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_3293690",
"title": "The Runner (Original Soundtrack)",
"artist": "Boy Harsher",
"price": 60,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_3511436",
"title": "Bye Bye Blues!",
"artist": "Les Paul & Mary Ford",
"price": 30,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
],
"condition": "M"
},
{
"id": "mint_4520905",
"title": "Various \u2013 Now Generation - The Best Of The Indie Stuff (2 x CD, Compilation)",
"artist": "Various",
"price": 15,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_5947485",
"title": "Where Shadows Forever Reign",
"artist": "Dark Funeral",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_5948394",
"title": "Metal Church",
"artist": "Metal Church",
"price": 30,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6358606",
"title": "Autofiction",
"artist": "Suede",
"price": 30,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6359222",
"title": "Ultraviolet Battle Hymns And True Confessions",
"artist": "The Dream Syndicate",
"price": 32,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "book",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_book_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Little Bird Bookshop's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_2287_book.snapshot.json",
"shop": {
"godverse_id": 2287,
"slug": "little-bird-bookshop",
"name": "Little Bird Bookshop",
"suburb": "Paddington",
"state": "QLD"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2287,
"sample": "random.Random(2287) over an id-ordered pool of 19707 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_2287_book.snapshot.json",
"snapshot_sha256": "cc100d0518b78bc0f095f945c186e8e179ca82d16ed973d11f0b56984f6dbd49",
"determinism": "pure bake; the sample itself is seeded random.Random(2287) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_826912",
"title": "An Undeniable Voice - Tania Blanchard - BPAP - BOO009",
"artist": "Lifeline Queensland",
"price": 10,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1926301",
"title": "I'm Very Busy - Oliver Jeffers",
"artist": "Oliver Jeffers",
"price": 25,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1926750",
"title": "Such Great Heights - Chris Deville",
"artist": "Chris Deville",
"price": 55,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_3275778",
"title": "Daniel Falconer - The Art Of District 9 Weta Workshop 2010 Hardcover Book",
"artist": "Movie",
"price": 190,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_3310625",
"title": "Ruland, Jim - Corporate Rock Sucks: The Rise And Fall [Book]",
"artist": "GAR",
"price": 45,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_4296826",
"title": "Trowell, Ian - Throbbing Gristle: An Endless Discontent [Book]",
"artist": "GAR",
"price": 70,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_6962945",
"title": "The Iconic Interior - 1900 to the Present",
"artist": "Thames & Hudson, Limited",
"price": 1900,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_6963194",
"title": "Modern Architecture A-Z",
"artist": "Taschen",
"price": 50,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_6965345",
"title": "Learn Oils Quickly",
"artist": "Pavilion Books",
"price": 20,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_6966159",
"title": "Flower Colour Theory",
"artist": "Phaidon",
"price": 50,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_6966277",
"title": "The Library: A World History",
"artist": "Thames and Hudson Ltd",
"price": 60,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_6966390",
"title": "Small Space Decorating",
"artist": "John Wiley and Sons Ltd",
"price": 28,
"price_band": "collector",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_6966784",
"title": "Little Guides to Style: The Story of Four Iconic Fa",
"artist": "Welbeck",
"price": 100,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_6967561",
"title": "Akris",
"artist": "Assouline",
"price": 175,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6969361",
"title": "RHS Encyclopedia of Plants and Flowers",
"artist": "Penguin Books Ltd",
"price": 100,
"price_band": "grail",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6994248",
"title": "Common Reader",
"artist": "HarperCollins Publishers Limited",
"price": 10,
"price_band": "standard",
"atlas": "stock_book_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "toy",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_toy_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not Kidstuff's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_2377_toy.snapshot.json",
"shop": {
"godverse_id": 2377,
"slug": "kidstuff",
"name": "Kidstuff",
"suburb": "Camperdown",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 2377,
"sample": "random.Random(2377) over an id-ordered pool of 103530 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_2377_toy.snapshot.json",
"snapshot_sha256": "904cbaf444ed38cb85d22696904c4f07b3a1b2302b3f1964c0796ff96f45ab70",
"determinism": "pure bake; the sample itself is seeded random.Random(2377) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_28804",
"title": "Mx Vs Atv - Unleashed Xbox (Original)",
"artist": "Microsoft",
"price": 15,
"price_band": "standard",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_28813",
"title": "Rapala Pro Fishing Playstation 2 (PS2)",
"artist": "Sony",
"price": 10,
"price_band": "standard",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_60762",
"title": "Mario Isalnd Party World Tour Nintendo 3DS",
"artist": "Nintendo",
"price": 18,
"price_band": "standard",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_64465",
"title": "FIFA 15 Playstation 3 (PS3)",
"artist": "Ea Sports",
"price": 2,
"price_band": "bargain",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_165601",
"title": "Spiderman Miles Morales Playstation 5 (PS5)",
"artist": "Sony",
"price": 45,
"price_band": "collector",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_370120",
"title": "Nintendo Switch Black Hac-001",
"artist": "Nintendo",
"price": 149,
"price_band": "grail",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_387296",
"title": "Sony Playstation 4 (PS4) Slim 500GB Cuh-Zct2e 500GB Black",
"artist": "Sony",
"price": 129,
"price_band": "grail",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_393994",
"title": "Centenary Of The Overland Telegraph White",
"artist": "Centenary Of The Overland Telegraph",
"price": 2,
"price_band": "bargain",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_525015",
"title": "Nintendo Sports Nintendo Switch",
"artist": "Nintendo",
"price": 12,
"price_band": "standard",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_1949276",
"title": "Call Of Duty Black Ops Iii Playstation 4 (PS4)",
"artist": "Sony",
"price": 5,
"price_band": "bargain",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_2144718",
"title": "Jim Shore Disney Traditions - The Lion King - Remember Who You Are Storybook",
"artist": "JIM SHORE DISNEY TRADITIONS",
"price": 201,
"price_band": "grail",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_2145450",
"title": "Games Workshop Warhammer 40K World Eaters Codex",
"artist": "Games Workshop",
"price": 93,
"price_band": "grail",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_4720215",
"title": "Pokemon Sheild Nintendo Switch",
"artist": "Nintendo",
"price": 49,
"price_band": "collector",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_5522185",
"title": "Pop! Minnie & Mickey",
"artist": "Pop!",
"price": 39,
"price_band": "collector",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6812211",
"title": "AK Interactive Wargame: Medium Earth Enamel Liquid Pigment 35ml",
"artist": "AK INTERACTIVE",
"price": 11,
"price_band": "standard",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6858153",
"title": "Nemesis Retaliation: Core Box",
"artist": "Awaken Realms",
"price": 250,
"price_band": "grail",
"atlas": "stock_toy_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

View File

@ -0,0 +1,271 @@
{
"version": 1,
"kind": "record",
"tier": 1,
"sourcing": "mint",
"atlas_px": 1024,
"cell_w": 256,
"cell_h": 256,
"count": 16,
"atlases": [
"stock_record_atlas_00.webp"
],
"license": "in-house-green \u2014 real dealgod listing photos (the sellers' own product shots), cached locally; FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"attribution": "MINT (seeded sample, not The Vintage Record's stock): real dealgod listings \u2014 the sellers' own product shots \u00b7 cover cache: GODVERSE/dealgod",
"generator": "pipeline/godverse_stock.py",
"snapshot": "mint_247_record.snapshot.json",
"shop": {
"godverse_id": 247,
"slug": "the-vintage-record",
"name": "The Vintage Record",
"suburb": "Annandale",
"state": "NSW"
},
"provenance": {
"generator": "pipeline/godverse_stock.py",
"source": {
"feed_db": "dealgod",
"feed_dump": "dealgod-20260715-033001.sql.gz",
"image_cache": "/Volumes/johnking/dealgod_backup/images",
"captured": "2026-07-15",
"seed": 247,
"sample": "random.Random(247) over an id-ordered pool of 111320 listings"
},
"license": {
"covers": "real dealgod listing photos (the sellers' own product shots), cached locally",
"zone": "in-house-green",
"flag": "FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE",
"metadata": "real titles/artists \u2014 John's R23 amendment to the no-real-trademarks law",
"sourcing": "MINT \u2014 a seeded sample standing in for a shop with no POS. Plausible, and NEVER this shop's actual stock. Nothing here is a claim about this business."
},
"snapshot": "mint_247_record.snapshot.json",
"snapshot_sha256": "1f7a99208d876b3c013335cce8103b561b5743a6cff894e4c44d253cda94d253",
"determinism": "pure bake; the sample itself is seeded random.Random(247) over an id-ordered pool => same shop id + same pool = same crate, forever"
},
"items": [
{
"id": "mint_1383351",
"title": "The Real Macaw",
"artist": "Graham Parker",
"price": 15,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.0,
0.25,
0.25
]
},
{
"id": "mint_1449829",
"title": "Survive",
"artist": "Lewis Capaldi",
"price": 60,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.0,
0.5,
0.25
]
},
{
"id": "mint_1501649",
"title": "Eclissi",
"artist": "Stefano Di Carlo",
"price": 25,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.0,
0.75,
0.25
]
},
{
"id": "mint_1572697",
"title": "The First Time Ever (I Saw Your Face)",
"artist": "Peter Nero",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.0,
1.0,
0.25
]
},
{
"id": "mint_1923720",
"title": "Distasteful Character Assissination",
"artist": "Couch Kids Antidote",
"price": 35,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.25,
0.25,
0.5
]
},
{
"id": "mint_1954310",
"title": "Crying In The Chapel",
"artist": "Peter Blakeley",
"price": 8,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.25,
0.5,
0.5
]
},
{
"id": "mint_2638666",
"title": "Frank Tovey \u2013 Tyranny And The Hired Hand (VG+/VG+)",
"artist": "Frank Tovey",
"price": 20,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.25,
0.75,
0.5
]
},
{
"id": "mint_3149798",
"title": "Sung Tongs",
"artist": "Animal Collective",
"price": 67,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.25,
1.0,
0.5
]
},
{
"id": "mint_3151476",
"title": "All Shook Down",
"artist": "The Replacements",
"price": 45,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.5,
0.25,
0.75
]
},
{
"id": "mint_3153341",
"title": "Have You Ever Seen The Rain",
"artist": "Smokie",
"price": 55,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.5,
0.5,
0.75
]
},
{
"id": "mint_3277109",
"title": "Strictly 4 My N.I.G.G.A.Z...",
"artist": "2Pac",
"price": 140,
"price_band": "grail",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.5,
0.75,
0.75
]
},
{
"id": "mint_3308281",
"title": "Introduce Yourself",
"artist": "Faith No More",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.5,
1.0,
0.75
]
},
{
"id": "mint_3415870",
"title": "Coyote Butterfly",
"artist": "Simon Joyner",
"price": 46,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.0,
0.75,
0.25,
1.0
]
},
{
"id": "mint_5943968",
"title": "Armistice Day: Live At The Domain, Sydney",
"artist": "Midnight Oil",
"price": 28,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.25,
0.75,
0.5,
1.0
]
},
{
"id": "mint_6010664",
"title": "The Freewheelin' Bob Dylan",
"artist": "Bob Dylan",
"price": 48,
"price_band": "collector",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.5,
0.75,
0.75,
1.0
]
},
{
"id": "mint_6537917",
"title": "The Videos 1989 - 2004",
"artist": "Metallica",
"price": 10,
"price_band": "standard",
"atlas": "stock_record_atlas_00.webp",
"uv": [
0.75,
0.75,
1.0,
1.0
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Some files were not shown because too many files have changed in this diff Show More