diff --git a/F-progress.md b/F-progress.md index 462171d..cd79106 100644 --- a/F-progress.md +++ b/F-progress.md @@ -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.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 diff --git a/README.md b/README.md index 152d987..8ed3edc 100644 --- a/README.md +++ b/README.md @@ -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 | | 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) | diff --git a/docs/LANES/LANE_F_NOTES.md b/docs/LANES/LANE_F_NOTES.md index 726773f..c12f46a 100644 --- a/docs/LANES/LANE_F_NOTES.md +++ b/docs/LANES/LANE_F_NOTES.md @@ -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. 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. + +## §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::::`), 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.5–2.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). diff --git a/docs/LANES/ROUND34_INSTRUCTIONS.md b/docs/LANES/ROUND34_INSTRUCTIONS.md new file mode 100644 index 0000000..ffc46cd --- /dev/null +++ b/docs/LANES/ROUND34_INSTRUCTIONS.md @@ -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::::` — 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. diff --git a/docs/shots/v7_beta/godbay_at_auction.png b/docs/shots/v7_beta/godbay_at_auction.png new file mode 100644 index 0000000..b5d71ec Binary files /dev/null and b/docs/shots/v7_beta/godbay_at_auction.png differ diff --git a/docs/shots/v7_beta/godbay_morning_paper.png b/docs/shots/v7_beta/godbay_morning_paper.png new file mode 100644 index 0000000..fe0b817 Binary files /dev/null and b/docs/shots/v7_beta/godbay_morning_paper.png differ diff --git a/tools/flags_check.py b/tools/flags_check.py index 4f414f9..3b51f94 100644 --- a/tools/flags_check.py +++ b/tools/flags_check.py @@ -2253,6 +2253,110 @@ def smoke_wantlist_travel(p): 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): """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 @@ -2408,6 +2512,7 @@ def main(): 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_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_real_crate(p) # R24 — THE REAL CRATE (per-shop atlas identity · soft fall · no-op off the path) finally: