Compare commits
No commits in common. "main" and "lane-G-godverse" have entirely different histories.
main
...
lane-G-god
@ -4,25 +4,6 @@
|
||||
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.
|
||||
|
||||
406
C-progress.md
@ -3,411 +3,7 @@
|
||||
*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-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.81–15.63 m, and scaling to
|
||||
the room keeps door→stage proximity in a tight **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 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.
|
||||
Last updated: 2026-07-16 (round 22) · owner: PROCITY-C · reviewer: Fable
|
||||
|
||||
---
|
||||
|
||||
|
||||
278
F-progress.md
@ -4,284 +4,6 @@
|
||||
|
||||
---
|
||||
|
||||
## 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.5–2 · 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
|
||||
|
||||
256
G-progress.md
@ -3,262 +3,6 @@
|
||||
*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** (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 (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 33–64 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):**
|
||||
|
||||
59
README.md
@ -14,36 +14,9 @@ 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 $8–24"). 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)
|
||||
@ -100,7 +73,6 @@ 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 |
|
||||
|
||||
@ -125,37 +97,6 @@ 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
|
||||
|
||||
@ -1,154 +1,10 @@
|
||||
# LANE A — CITYGEN · progress (PROCITY-A)
|
||||
|
||||
*Status: **all deliverables landed; self-check ALL GREEN (156,352/156,352); rounds 2–30 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 2–27 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.**
|
||||
*Status: **all deliverables landed; self-check all-green (161,372/161,372); rounds 2–23 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)*
|
||||
|
||||
## Round 23 (2026-07-16) — v5.0-alpha: the spacing warn (ledger #5)
|
||||
|
||||
|
||||
@ -28,21 +28,7 @@ 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.
|
||||
- **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).
|
||||
points at its street edge. GLB convention: origin at base, facing **−Z**, real-world scale.
|
||||
- **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"`).
|
||||
@ -82,11 +68,8 @@ 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 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.)
|
||||
// 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.
|
||||
// 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.
|
||||
@ -273,30 +256,9 @@ 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 frozen regression target):** the `?classic=1` boot is byte-identical to the v2
|
||||
> baseline — synthetic fingerprint **`0x5f76e76`**, zero gig layer, **zero fetch delta** (no
|
||||
> - **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
|
||||
> `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
|
||||
@ -464,45 +426,6 @@ 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.
|
||||
@ -554,79 +477,6 @@ 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
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
# G3 — THE ECONOMY BACKEND (design doc)
|
||||
# G3 — THE ECONOMY BACKEND (design doc, DRAFT for ratification)
|
||||
|
||||
*Lane G, R23 draft. Drafted per the v5 charter ([V5_REAL_SHOP.md](V5_REAL_SHOP.md)) ledger #2.*
|
||||
*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).*
|
||||
|
||||
**§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.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@ -93,71 +89,30 @@ boot path, gate, or tag that may touch the network.
|
||||
|
||||
---
|
||||
|
||||
## 4. API surface [AS-BUILT — R27, `pipeline/godverse_server.py`]
|
||||
## 4. API surface [DESIGN]
|
||||
|
||||
Read-only, per §7's ruling. **The write verbs are not "gated" — they do not exist.**
|
||||
Read-only in v5.0; the write verbs are drafted here but **gated on §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
|
||||
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]
|
||||
```
|
||||
|
||||
**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 an `id` in `gone[]` → `collapseBuyItem()` (already
|
||||
says only what changed. A sold record is a `sku` 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** (400–800/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. **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.
|
||||
- Shop id is the **godverse id** (§1a), never a POS internal id.
|
||||
|
||||
---
|
||||
|
||||
@ -198,47 +153,13 @@ whichever dataset actually *has* the shop, and mismatches must fail soft, never
|
||||
|
||||
---
|
||||
|
||||
## 7. THE WRITE-BACK QUESTION — **RULED: A THEN B** (John, 2026-07-17, R24 kickoff)
|
||||
## 7. THE WRITE-BACK QUESTION [JOHN — charter law #4]
|
||||
|
||||
> ### 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**.
|
||||
**Does an in-game purchase ever touch real thriftgod/recordgod inventory?**
|
||||
|
||||
**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.
|
||||
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**.
|
||||
|
||||
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
|
||||
@ -269,8 +190,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 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.**)*
|
||||
**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).
|
||||
|
||||
---
|
||||
|
||||
@ -321,8 +242,6 @@ 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.~~ **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.
|
||||
3. **[John, R24]** §7, the write-back question.
|
||||
4. **[Both Fables]** §9's beta scoping — depth (one real shop, 357 crates) vs breadth (more
|
||||
shops, which needs other people's data).
|
||||
|
||||
@ -2,243 +2,6 @@
|
||||
|
||||
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)
|
||||
|
||||
@ -1,343 +1,5 @@
|
||||
# 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/(1−du)` 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.40–2.90**; the mark is **y 2.45–2.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.45–2.52; I had ray-scanned y 3.0–3.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
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
# LANE C — interior audio contract (v3.1) → for Lane F + Lane B
|
||||
# LANE C — interior audio contract (v3.0-FROZEN) → for Lane F + Lane B
|
||||
|
||||
> **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.**
|
||||
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane F + Lane B. Describes the shipped state; changes
|
||||
> require a version bump.
|
||||
|
||||
*`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.*
|
||||
*`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.*
|
||||
|
||||
> **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>`
|
||||
@ -18,7 +17,6 @@ interior_mode just 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
|
||||
@ -41,58 +39,6 @@ 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.81–15.63 m**, and scaling
|
||||
`r` to the room holds the door→stage proximity in a consistent **0.42–0.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.65–4.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):
|
||||
@ -104,92 +50,9 @@ 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.
|
||||
|
||||
---
|
||||
|
||||
# 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.
|
||||
## 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.
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
# 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.
|
||||
# LANE C — interiors contract (venues + real-town class mapping) → Lanes A/D/E/F
|
||||
|
||||
> **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).
|
||||
@ -14,24 +7,6 @@
|
||||
> (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(p−1, 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
|
||||
@ -248,302 +223,55 @@ mapping for the caches is this table.)
|
||||
|
||||
---
|
||||
|
||||
## 7. Real-stock packs — THE RUNTIME IS THE AUTHORITY (v5.0-alpha, R24 ledger #1) → Lanes E/F/G
|
||||
## 7. G2a — per-shop stock atlases: C's interior ceilings (v4.0, ledger #3) → Lane G + Lane E
|
||||
|
||||
> **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).**
|
||||
*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.*
|
||||
|
||||
### 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).
|
||||
**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.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`**.
|
||||
**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.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.**
|
||||
**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`**.
|
||||
|
||||
> **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.)*
|
||||
**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.
|
||||
|
||||
**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.)*
|
||||
**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.
|
||||
|
||||
**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.
|
||||
**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.
|
||||
|
||||
### 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
|
||||
```
|
||||
**7. Parody law — E's (d), unchanged and not C's to relax.** Metadata deterministically parody-transformed;
|
||||
cover art real; prices real-or-seeded.
|
||||
|
||||
@ -6,118 +6,6 @@ _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 3–4k `_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 (15–25 m) **never fires**; an aggressive 8 m one
|
||||
would fire only in half a room, at a distance where a 3–4 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 ~12–15 k tris, ~85 k with the band — comfortably inside the
|
||||
200 k stress ceiling E's R22 tri diet restored, at 35–38 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,
|
||||
|
||||
@ -1,400 +1,5 @@
|
||||
# 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
|
||||
3–4k.)*
|
||||
|
||||
**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: 22–77 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
|
||||
@ -807,52 +412,12 @@ 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) |
|
||||
| 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** |
|
||||
| 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) |
|
||||
|
||||
**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.
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
# 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.
|
||||
@ -1,98 +0,0 @@
|
||||
# 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 A–F 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.
|
||||
@ -1,147 +0,0 @@
|
||||
# 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 1–2 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.
|
||||
@ -1,217 +0,0 @@
|
||||
# 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.
|
||||
@ -1,63 +0,0 @@
|
||||
# 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 (~3–4k) 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 2–3
|
||||
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.
|
||||
@ -1,82 +0,0 @@
|
||||
# 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.
|
||||
@ -1,120 +0,0 @@
|
||||
# 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.
|
||||
@ -1,53 +0,0 @@
|
||||
# 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.
|
||||
@ -1,61 +0,0 @@
|
||||
# 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 $25–59"), 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 1–3:** 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 $8–24" 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.
|
||||
@ -1,52 +0,0 @@
|
||||
# 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.
|
||||
@ -1,57 +0,0 @@
|
||||
# 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.5–2.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.
|
||||
@ -1,36 +0,0 @@
|
||||
# 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
@ -1,122 +0,0 @@
|
||||
# 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.*
|
||||
@ -1,116 +0,0 @@
|
||||
# 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 **15–20 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.
|
||||
@ -81,52 +81,3 @@ 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.
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
# 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) + 1–3 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 2–3 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.
|
||||
@ -1,106 +0,0 @@
|
||||
# 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.
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 784 KiB |
@ -1,8 +0,0 @@
|
||||
<!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>
|
||||
|
Before Width: | Height: | Size: 778 KiB |
@ -1,22 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 894 KiB |
|
Before Width: | Height: | Size: 789 KiB |
|
Before Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 982 KiB |
|
Before Width: | Height: | Size: 705 KiB |
|
Before Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 863 KiB |
|
Before Width: | Height: | Size: 741 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 715 KiB |
@ -1,15 +0,0 @@
|
||||
# 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.
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
[
|
||||
"look.glb",
|
||||
"procity_fit_arcade_cabinet_01.glb",
|
||||
"procity_fit_bass_guitar_01.glb",
|
||||
"procity_fit_bass_guitar_01_hi.glb",
|
||||
|
||||
@ -50,30 +50,10 @@ 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},
|
||||
# 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},
|
||||
"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
|
||||
|
||||
@ -1,302 +0,0 @@
|
||||
#!/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)
|
||||
@ -52,8 +52,8 @@ SHOP = {
|
||||
}
|
||||
OWN_PHOTO_HOST = "monsterrobot.party" # the licence test: the shop's own domain, nothing else
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
def price_band(price):
|
||||
@ -99,127 +99,6 @@ 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:
|
||||
@ -262,10 +141,9 @@ def snapshot(args):
|
||||
items = items[:args.max_items]
|
||||
|
||||
snap = {
|
||||
"version": 1, "kind": "record", "tier": 1, "sourcing": "real",
|
||||
"version": 1, "kind": "record", "tier": 1,
|
||||
"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,
|
||||
@ -273,9 +151,7 @@ def snapshot(args):
|
||||
"captured": args.captured,
|
||||
"extraction": "see load_image_map() docstring — the products projection command",
|
||||
},
|
||||
# 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": {
|
||||
"licence": {
|
||||
"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",
|
||||
@ -297,141 +173,59 @@ 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 = px
|
||||
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
|
||||
sheet = Image.new("RGB", (W, H), (28, 28, 32))
|
||||
aname = f"stock_{kind}_atlas_{ai:02d}.webp"
|
||||
aname = f"stock_record_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['cover']} — snapshot sha mismatch, re-snapshot")
|
||||
sys.exit(f"cover drift: {it['sku']} {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
|
||||
# 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"],
|
||||
idx_items.append({
|
||||
"id": f"rec_{len(idx_items):04d}", "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": 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,
|
||||
"version": 1, "kind": "record", "tier": 1,
|
||||
"atlas_px": ATLAS_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")},
|
||||
# NESTED — the rich block C calls colour; the four gate fields are top level above.
|
||||
"crate": snap["crate"],
|
||||
"provenance": {
|
||||
"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"),
|
||||
"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",
|
||||
},
|
||||
"items": idx_items,
|
||||
}
|
||||
# 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")
|
||||
dst = os.path.join(out_dir, "stock_record_index.json")
|
||||
with open(dst, "w") as fh:
|
||||
json.dump(index, fh, indent=1)
|
||||
fh.write("\n")
|
||||
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)}")
|
||||
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']}'")
|
||||
|
||||
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
@ -447,19 +241,5 @@ 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)
|
||||
|
||||
@ -6,22 +6,12 @@ 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
|
||||
|
||||
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.
|
||||
Runs on ultra (needs the local `thriftgod` PG + psycopg2 — the thriftgod server's own deps).
|
||||
"""
|
||||
import json, math, os, subprocess, sys
|
||||
import json, os, sys
|
||||
|
||||
import psycopg2, psycopg2.extras
|
||||
|
||||
DONOR, OUT = sys.argv[1], sys.argv[2]
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
@ -32,98 +22,17 @@ 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']
|
||||
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
|
||||
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()]
|
||||
|
||||
out = dict(donor)
|
||||
out.update({
|
||||
@ -133,20 +42,10 @@ out.update({
|
||||
'generator': 'pipeline/godverse_town.py',
|
||||
'attribution': donor['attribution'] + ' · shop census: GODVERSE/thriftgod',
|
||||
'shops': 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},
|
||||
'counts': {**donor.get('counts', {}), 'shops': len(shops)},
|
||||
})
|
||||
dst = os.path.join(TOWNS, f'{OUT}.json')
|
||||
with open(dst, 'w') as fh:
|
||||
json.dump(out, fh, indent=1)
|
||||
json.dump(out, open(dst, 'w'), indent=1)
|
||||
types = {}
|
||||
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}")
|
||||
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}")
|
||||
|
||||
@ -1,132 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate per-shop real-stock atlases — the G2a format gate (R23 #4 · R24 #4 · R25 #2 · R26 #3/#6).
|
||||
"""Validate per-shop real-stock atlases — the G2a format gate (v5.0-alpha, ROUND23 ledger #4).
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
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.
|
||||
python3 pipeline/validate_atlas.py # exit 0 = every atlas valid (or none yet), 1 = a bad one
|
||||
|
||||
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:
|
||||
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).
|
||||
|
||||
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
|
||||
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.
|
||||
"""
|
||||
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)
|
||||
GLOB = os.path.join(STOCK_G, "*", "stock_*_index.json") # the RUNTIME path — what the loader fetches
|
||||
STOCK_G = os.path.join(ROOT, "web", "assets", "stock_godverse") # Lane G's namespace (charter law #5)
|
||||
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")
|
||||
# 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.
|
||||
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.
|
||||
FORBIDDEN_ITEM_KEYS = {"seller", "seller_name", "owner", "owner_name", "customer", "user", "username",
|
||||
"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
|
||||
"email", "phone", "mobile", "address", "member", "account", "consignor"}
|
||||
|
||||
|
||||
def image_size(path):
|
||||
@ -134,116 +39,75 @@ def image_size(path):
|
||||
with open(path, "rb") as f:
|
||||
head = f.read(32)
|
||||
if head[:8] == b"\x89PNG\r\n\x1a\n":
|
||||
return struct.unpack(">II", head[16:24])
|
||||
w, h = struct.unpack(">II", head[16:24])
|
||||
return w, h
|
||||
if head[:4] == b"RIFF" and head[8:12] == b"WEBP":
|
||||
fmt = head[12:16]
|
||||
if fmt == b"VP8X":
|
||||
if fmt == b"VP8X": # extended: 24-bit minus-one
|
||||
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 ":
|
||||
if fmt == b"VP8 ": # lossy: 14-bit each
|
||||
w, h = struct.unpack("<HH", head[26:30])
|
||||
return w & 0x3FFF, h & 0x3FFF
|
||||
if fmt == b"VP8L":
|
||||
if fmt == b"VP8L": # lossless: packed 14-bit
|
||||
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.relpath(path, STOCK_G)
|
||||
name = os.path.basename(path)
|
||||
try:
|
||||
idx = json.load(open(path))
|
||||
except Exception as e:
|
||||
return [f"{name}: does not parse: {e}"], [], None # present-but-unparseable ⇒ FAIL
|
||||
return [f"{name}: does not parse: {e}"], []
|
||||
d = os.path.dirname(path)
|
||||
|
||||
# ── 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")
|
||||
# ── 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)")
|
||||
|
||||
# ── 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")
|
||||
# ── C's draw ceiling (LANE_C_PUB §7.1) ──
|
||||
atlases = idx.get("atlases", [])
|
||||
if not isinstance(atlases, list) or not atlases:
|
||||
errs.append(f"{name}: no atlases[] listed")
|
||||
atlases = []
|
||||
items = idx.get("items")
|
||||
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", [])
|
||||
if not isinstance(items, list) or not items:
|
||||
errs.append(f"{name}: no items")
|
||||
items = []
|
||||
if len(atlases) > MAX_ATLASES:
|
||||
errs.append(f"{name}: {len(atlases)} atlases — C §7.2 caps a shop at {MAX_ATLASES}")
|
||||
|
||||
# ── C's texture/VRAM ceiling (§7.2) + index↔atlas consistency (the declared size must be REAL) ──
|
||||
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}")
|
||||
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²)")
|
||||
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}²")
|
||||
|
||||
# ── the index itself (schema per the loader: it reads items[].uv/atlas; `cell`/`cell_w` are informational) ──
|
||||
# ── the index itself (schema confirmed verbatim in LANE_C_PUB §7.3) ──
|
||||
ids = set()
|
||||
for i, it in enumerate(items):
|
||||
if not isinstance(it, dict):
|
||||
@ -269,182 +133,30 @@ 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): # origin top-left; stockpack flips V (R7 law)
|
||||
if not (0 <= u0 < u1 <= 1 and 0 <= v0 < v1 <= 1): # UV origin top-left; C flips V (R7 law)
|
||||
errs.append(f"{name}[{it['id']}]: uv out of range / inverted: {uv}")
|
||||
|
||||
# ── 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
|
||||
return errs, warns
|
||||
|
||||
|
||||
def main():
|
||||
idxs = sorted(glob.glob(GLOB))
|
||||
man, man_errs = load_manifest()
|
||||
idxs = sorted(glob.glob(os.path.join(STOCK_G, "**", "stock_shop_*_index.json"), recursive=True))
|
||||
if not idxs:
|
||||
# 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"))
|
||||
print("atlas-QA: no per-shop atlases yet (Lane G's G2a) — nothing to validate")
|
||||
return 0
|
||||
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
|
||||
all_errs, all_warns = [], []
|
||||
for p in idxs:
|
||||
e, w, evidence = validate_index(p)
|
||||
e, w = validate_index(p)
|
||||
all_errs += e; all_warns += w
|
||||
idx = {}
|
||||
try:
|
||||
idx = json.load(open(p))
|
||||
except Exception:
|
||||
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} "
|
||||
pass
|
||||
print(f" {os.path.basename(p):40} items={len(idx.get('items', [])):>3} "
|
||||
f"atlases={len(idx.get('atlases', []))} px={idx.get('atlas_px')} "
|
||||
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
|
||||
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']}")
|
||||
for w in all_warns:
|
||||
print(f" WARN {w}")
|
||||
for e in all_errs:
|
||||
@ -452,18 +164,7 @@ def main():
|
||||
if all_errs:
|
||||
print(f"atlas-QA FAIL — {len(all_errs)} error(s), {len(all_warns)} warning(s)")
|
||||
return 1
|
||||
# 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)")
|
||||
print(f"atlas-QA OK — {len(idxs)} per-shop atlas(es) valid, {len(all_warns)} warning(s)")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
#!/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"
|
||||
1525
tools/flags_check.py
@ -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, os, time, socket, subprocess, pathlib
|
||||
import sys, time, socket, subprocess, pathlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
|
||||
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
|
||||
PORT = 8130
|
||||
HOST = f'http://127.0.0.1:{PORT}'
|
||||
SEED = 20261990
|
||||
if '--seed' in sys.argv:
|
||||
|
||||
@ -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 0x5f76e76 (selfcheck) · osm/melbourne 0x9c7e76b3 · osm/katoomba 0xf3aafec8.
|
||||
// Lane A contract: synthetic 0x3fa36874 (selfcheck) · osm/melbourne 0x34cfdec0 · osm/katoomba 0x0f652510.
|
||||
// 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: '0x9c7e76b3', katoomba: '0xf3aafec8' };
|
||||
const TOWN_GOLDENS = { melbourne: '0x34cfdec0', katoomba: '0x0f652510' };
|
||||
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),
|
||||
|
||||
@ -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 (0x5f76e76)
|
||||
• classic_covenant — `?classic=1`: the v2 covenant made visible, byte-identical forever (0x3fa36874)
|
||||
|
||||
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"
|
||||
|
||||
@ -1,200 +0,0 @@
|
||||
#!/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())
|
||||
@ -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, os, time, socket, subprocess, pathlib
|
||||
import sys, time, socket, subprocess, pathlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
|
||||
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
|
||||
PORT = 8130
|
||||
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
|
||||
|
||||
|
Before Width: | Height: | Size: 148 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 196 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 212 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 209 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 200 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 231 KiB |
@ -1,273 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 212 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 124 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 254 KiB |
@ -1,271 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 154 KiB |