Fable (integrator): ROUND 34 CLOSED — GODBAY hammers, all green; v7.0 needs only the first five minutes

qa.sh --strict --matrix: 7 passed · 0 failed · 0 warn · 0 skipped, MATRIX 10 towns green. New
STRICT smoke_godbay: consigned through the real crate DOM · ONE-HAMMER determinism (a frozen
pre-sleep export slept twice lands byte-identical — the seeded gavel, measured not trusted) ·
honest bounded resolution (net $19 ≤ 2×$24×0.88) · the wake toast carried the morning paper ·
the ride to melbourne hammered a listing on arrival · ?game=0 has no crate to list from · 0
console errors. Money shots in docs/shots/v7_beta/ (the dawn paper + the ⚖ crate). The full
v7 loop now closes end to end: dig the gem → keeper cash now or the gavel tomorrow → the
wantlist points at another town → the ride costs a day and hammers your listings on arrival.
Remaining for the v7.0 tag: THE FIRST FIVE MINUTES (onboarding), the tour, docs freeze — and
John's hands on the whole thing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-20 19:32:06 +10:00
parent e528c92fed
commit c9e233a7e5
7 changed files with 203 additions and 0 deletions

View File

@ -4,6 +4,21 @@
--- ---
## Round 34 (v7.0 close, wave 1) — GODBAY: the offline auction (Fable, solo, JING5)
⚖ LIST from the crate (mail-in consignment) → `listings` on procity-save/1 → resolves the day
after, wherever the day moves (sleep, travel, imports — adopt() and sleep() share the
resolver). THE ONE-HAMMER LAW: identity-seeded (never day-seeded) — a passed-in item re-lists
to the same result forever, no retry farm. 15% pass · uniform-across-band hammer (grail 2×low)
· 12% war ×1.52 · 12% fees, net floor $1; EV ≈ 0.8×mid so random flips bleed and only finds
profit; keeper = floor, auction = bounded ceiling. The morning paper rides the wake/arrival
toast ("⚖ SOLD 'Cane Toad Blues' — hammer $22, $19 after fees"). Band table imported from C's
bible.js (one authority), PRNG from core. STRICT `smoke_godbay` green (real-DOM consign ·
byte-identical double-sleep · bounded honest resolution · travel hammers · game=0 control).
Full record: LANE_F_NOTES §34.
---
## Round 33 (v7.0-beta wave 2) — THE HUNT: wantlist · rumor · travel (Fable, solo, JING5) ## 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 ☆ WANT on the pull card → `wants` on procity-save/1 (optional, capped, deduped); buying a

View File

@ -26,6 +26,7 @@ Click **START** to lock the mouse. Then:
| **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 | | **E** (at the counter) · **C** (anywhere) · **SLEEP** (bottom bar) | sell a find to the keeper (he pays under the **guide band**, never over) · open THE CRATE — your collection, in shops too now · sleep = save, the day advances, stock rotates |
| read the **guide** stamp on every price card | the 90s price-guide band ("guide $824"). An item **asking under its band is a gem** — buy it, sell it to a keeper, pocket the margin. Bought slots stay gone for the day (the bin remembers) | | read the **guide** stamp on every price card | the 90s price-guide band ("guide $824"). An item **asking under its band is a gem** — buy it, sell it to a keeper, pocket the margin. Bought slots stay gone for the day (the bin remembers) |
| **☆ WANT IT** on a pull card | start hunting it — keepers gossip ("saw one in Castlemaine"), and buying one clears the hunt. **Switching towns costs a day** — the 23 towns are a map now, not a dropdown | | **☆ 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) | | 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) | | walk out the door / **Esc** | leave the shop (Esc also frees the mouse anywhere) |
| **M** | 2D town map (street only) | | **M** | 2D town map (street only) |

View File

@ -1845,3 +1845,28 @@ m3ultra. If a third machine joins the rotation, sync that dir (49 M) or re-run E
Green gates missed it because every gate's boot idles on the street long enough to build. Green gates missed it because every gate's boot idles on the street long enough to build.
Fixed: tickGame() now builds AND refreshes (any mode); found by taking the money shot, which Fixed: tickGame() now builds AND refreshes (any mode); found by taking the money shot, which
entered the shop faster than any gate does. Shots are a detector, not decoration. entered the shop faster than any gate does. Shots are a detector, not decoration.
## §34 — R34: GODBAY, the offline auction (v7.0 close opens) (Fable, solo, JING5)
- **save.js:** `godbayHammer(entry, townKey)` — THE ONE-HAMMER LAW: seeded by ITEM IDENTITY
(`godbay:<townKey>:<sku|slotId>:<dayFound>:<pricePaid>`), never by resolution day — a
passed-in item re-lists to the same result forever, no retry farm. Fixed 4-draw stream. Rolls:
15% passed in (comes home free) · uniform hammer across the band (grail top = 2×low; unbanded
alpha finds auction around pricePaid — honest, never invented) · 12% bidding war ×1.52.0 ·
12% commission, net floor $1. EV ≈ 0.8×mid — under in-band asking EV, so random flipping
bleeds and only FINDS profit; the keeper's half-of-low stays the instant floor, the auction
is the gambler's ceiling (bounded 2×high×0.88). PRNG imported from core/prng.js; the band
table imported from C's bible.js (one authority — no mirror after all).
- `listings` optional on procity-save/1 (entry shape + listDay, same back-compat law).
`listFind` (identity, entry leaves the crate — can't also sell it) · `resolveAuctions()` runs
in adopt() AND sleep(): sleep, travel, and day-moving imports all hammer what they pass.
`takeAuctionNews()` drains the transient morning paper (NOT saved — the ledger is cash/
collection; the paper is a toast).
- **hud.js:** ⚖ LIST on crate find rows (mail-in consignment) + AT AUCTION section; rebuild
watches listings. **index.html:** the wake toast and the arrival toast fold the paper in.
- **Gate `smoke_godbay` (STRICT):** consign through the real crate DOM → save carries it ·
ONE-HAMMER determinism (a frozen pre-sleep export slept twice lands byte-identical) · bounded
honest 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.
- G's later LIVE tier must match these laws (the ladder rule: the offline tier is the contract).

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

View File

@ -2253,6 +2253,110 @@ def smoke_wantlist_travel(p):
b.close() b.close()
def smoke_godbay(p):
"""R34 gate — GODBAY, the offline seeded auction. Legs: (a) ⚖ LIST through the real crate DOM →
the entry leaves the collection into save-carried listings; (b) sleep resolved: listings empty,
cash+net (bounded [1, 2×high×0.88 + slack]) XOR the item came home, the wake toast carries the
morning paper; (c) THE ONE-HAMMER DETERMINISM: a pre-sleep export imported and slept TWICE lands
byte-identical cash/collection both times; (d) travel resolves too (list ride hammered on
arrival); (e) ?game=0 builds no crate at all (nothing to list from)."""
head('GATE R34: GODBAY — the offline auction (one hammer · bounded · morning paper · controls)')
BANDHI = {'record': {'bargain': 7, 'standard': 24, 'collector': 59, 'grail': 120},
'book': {'bargain': 4, 'standard': 14, 'collector': 39, 'grail': 80},
'toy': {'bargain': 5, 'standard': 19, 'collector': 49, 'grail': 100}}
b, pg, errs = new_page(p)
try:
boot(pg, '')
r0 = _enter_record_shop(pg)
if r0.get('err'): FAIL(f"godbay: {r0['err']}"); return
rich = pg.evaluate("""() => { const g = window.PROCITY.game;
const ex = JSON.parse(g.export()); ex.cash = 5000; ex.savedAt = Date.now();
return g.import(JSON.stringify(ex)); }""")
if not rich: FAIL('godbay: bankroll import failed'); return
pg.evaluate("() => window.DBG.setSegment(2)")
for i in range(2): # two finds: one to list now, one for the travel leg
_at_bin(pg)
rb = _dig_buy(pg)
if rb.get('err') or rb.get('disabled'): FAIL(f"godbay: buy {i + 1} failed — {rb}"); return
# (a) ⚖ LIST through the real crate DOM
rl = pg.evaluate("""() => {
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyC' }));
const g = window.PROCITY.game, n0 = g.collection.length;
const lb = document.querySelector('#pc-crate .pc-crate-row div[title*="GodBay"]');
if (!lb) return { err: 'no ⚖ LIST control in the crate' };
lb.click();
const ex = JSON.parse(g.export());
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyC' }));
return { n0, n1: g.collection.length, listings: (ex.listings || []).length,
entry: (ex.listings || [])[0] || null }; }""")
if rl.get('err'): FAIL(f"godbay: {rl['err']}"); return
if rl['n1'] == rl['n0'] - 1 and rl['listings'] == 1 and rl['entry'] and rl['entry'].get('listDay'):
OK(f"godbay: consigned through the crate — collection {rl['n0']}{rl['n1']}, "
f"the save carries the listing (listDay {rl['entry']['listDay']})")
else:
FAIL(f"godbay: listing did not take — {rl}"); return
# (c) THE ONE-HAMMER DETERMINISM: freeze pre-sleep, sleep twice from the same frozen state
pre = pg.evaluate("() => window.PROCITY.game.export()")
outcomes = []
for _ in range(2):
pg.evaluate("(ex) => window.PROCITY.game.import(ex)", pre)
pg.evaluate("() => { document.getElementById('pc-sleep-btn').click(); }")
pg.wait_for_timeout(250)
toast = pg.evaluate("() => (document.getElementById('pc-toast') || {}).textContent || ''")
st = pg.evaluate("""() => { const g = window.PROCITY.game;
const ex = JSON.parse(g.export()); delete ex.savedAt;
return { cash: g.cash, n: g.collection.length, nl: g.listings.length, ex: JSON.stringify(ex) }; }""")
st['toast'] = toast
outcomes.append(st)
a, b2 = outcomes
if a['ex'] == b2['ex'] and a['cash'] == b2['cash']:
OK('godbay: ONE HAMMER — the same frozen state slept twice lands byte-identical both times')
else:
FAIL(f"godbay: hammer not deterministic — {a['cash']} vs {b2['cash']}")
# (b) resolution honesty + bounds + the morning paper
pre_cash = json.loads(pre)['cash']; pre_n = len(json.loads(pre)['collection'])
ent = rl['entry']; hi = BANDHI.get(ent.get('type'), BANDHI['record']).get(ent.get('band'), 120)
sold = a['cash'] > pre_cash; came_home = a['n'] == pre_n + 1
net = a['cash'] - pre_cash
if a['nl'] == 0 and (sold != came_home) and (not sold or 1 <= net <= int(2.0 * hi * 0.88) + 2):
OK(f"godbay: resolved — {'SOLD net $' + str(net) + f' (≤ 2×${hi}×0.88 bound)' if sold else 'passed in, came home'}"
f" · listings drained")
else:
FAIL(f"godbay: resolution dishonest — nl={a['nl']} sold={sold} home={came_home} net={net} hi={hi}")
if '' in a['toast']:
OK(f"godbay: the wake toast carried the morning paper — {a['toast'][:90]}")
else:
FAIL(f"godbay: no ⚖ in the wake toast — {a['toast'][:90]!r}")
# (d) travel resolves too: list the second find, ride to melbourne
r2 = pg.evaluate("""() => {
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyC' }));
const g = window.PROCITY.game;
const lb = document.querySelector('#pc-crate .pc-crate-row div[title*="GodBay"]');
if (!lb) return { err: 'no ⚖ for the travel leg (collection empty?)' };
lb.click();
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyC' }));
return { cash: g.cash, nl: g.listings.length, n: g.collection.length }; }""")
if r2.get('err'): FAIL(f"godbay/travel: {r2['err']}")
else:
boot(pg, 'plansrc=osm&town=melbourne')
t = pg.evaluate("""() => { const g = window.PROCITY.game;
return { nl: g.listings.length, cash: g.cash, n: g.collection.length, trav: g.traveled }; }""")
if t['trav'] and t['nl'] == 0 and (t['cash'] > r2['cash'] or t['n'] == r2['n'] + 1):
OK(f"godbay/travel: the ride to melbourne hammered the listing (cash ${r2['cash']}→${t['cash']}, "
f"collection {r2['n']}{t['n']})")
else:
FAIL(f"godbay/travel: {r2}{t}")
# (e) ?game=0 — no crate, nothing to list from
boot(pg, 'game=0')
g0 = pg.evaluate("() => ({ crate: !!document.getElementById('pc-crate'), game: !!window.PROCITY.game })")
if not g0['crate'] and not g0['game']: OK('godbay/control: ?game=0 — no game, no crate, no auction house')
else: FAIL(f"godbay/control: game=0 leaked — {g0}")
if errs: FAIL(f"godbay: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK('godbay: 0 console errors')
finally:
b.close()
def smoke_classic_purity(p): def smoke_classic_purity(p):
"""R30 gate (c) — ?classic=1 and ?game=0: ZERO Storage-prototype calls (instrumented before any """R30 gate (c) — ?classic=1 and ?game=0: ZERO Storage-prototype calls (instrumented before any
page script), zero game DOM (B's ids), game null. Default boot is the POSITIVE control (the page script), zero game DOM (B's ids), game null. Default boot is the POSITIVE control (the
@ -2408,6 +2512,7 @@ def main():
smoke_rotation(p) # R30 gate d — vertex-data rotation determinism (the wave-3 binding) smoke_rotation(p) # R30 gate d — vertex-data rotation determinism (the wave-3 binding)
smoke_bible(p) # R32 — THE BIBLE (bands · gems · ledger) + per-day scarcity + ?game=0 control smoke_bible(p) # R32 — THE BIBLE (bands · gems · ledger) + per-day scarcity + ?game=0 control
smoke_wantlist_travel(p) # R33 — wantlist · keeper rumor · travel-costs-a-day (+ controls) smoke_wantlist_travel(p) # R33 — wantlist · keeper rumor · travel-costs-a-day (+ controls)
smoke_godbay(p) # R34 — GODBAY offline auction (one hammer · bounded · morning paper)
smoke_tier2(p) # R27 — the kill-the-server gate (charter law #1) smoke_tier2(p) # R27 — the kill-the-server gate (charter law #1)
smoke_real_crate(p) # R24 — THE REAL CRATE (per-shop atlas identity · soft fall · no-op off the path) smoke_real_crate(p) # R24 — THE REAL CRATE (per-shop atlas identity · soft fall · no-op off the path)
finally: finally: