Lane F R30 (v7.0-alpha ledger #1, THE CONTRACT FIRST): LANE_F_NOTES §30 — window.PROCITY.game published ({day,cash,collection,townKey,save,load,sleep,export,import,wallet,recordFind,removeFind}), procity-save/1 schema (the delta law by shape), the day-salt rotation law (day 1 = pre-v7, real never rotates, gig night = day%7), C's §9 asks honoured (type/title/artist + removeFind)
This commit is contained in:
parent
c8503a5a2e
commit
53fcd3ca1b
@ -1447,3 +1447,124 @@ subject-presence check now reads the disk. *The vacuous-gate law has a sibling:
|
||||
own subject being absent.*
|
||||
|
||||
**No tag** — wave 0 rides Spike 2's close, which is gated on John's footage. Nothing else is owed on m3.
|
||||
|
||||
---
|
||||
|
||||
## 30. Round 30 — v7.0-alpha THE SAVE CORE (ledger #1) + SLEEP=SAVE=TOMORROW (ledger #3)
|
||||
|
||||
### §30.1 THE CONTRACT — `window.PROCITY.game` (PUBLISHED FIRST, per the brief; B builds on this, C reads it at the counter)
|
||||
|
||||
`window.PROCITY.game` is **null** under `?classic=1` and under `?game=0`; on every other boot it is:
|
||||
|
||||
```js
|
||||
window.PROCITY.game = {
|
||||
day, // getter, int ≥ 1. DAY 1 IS THE PRE-v7 TOWN: stock streams carry no day salt on day 1,
|
||||
// so a fresh game's shops are byte-identical to a ?game=0 boot. Rotation begins at the
|
||||
// first sleep. (Falsifiable: day-1 stock == no-game stock; day-2 stock != day-1 stock.)
|
||||
cash, // getter, int dollars — always equals PROCITY.wallet.cash()
|
||||
collection, // getter → the LIVE array. READ-ONLY for every consumer (C's §9.3 law); the only
|
||||
// mutations are game.recordFind (buy seam) and game.removeFind (sell seam).
|
||||
townKey, // string for THIS boot: `${plansrc}/${town || 'default'}@${seed}`
|
||||
// e.g. 'synthetic/default@20261990' · 'osm/redhill_godverse@20261990'
|
||||
save(), // → bool. Serializes schema procity-save/1 → localStorage['procity-save'].
|
||||
load(), // → bool (ran once at construction). Corrupted/foreign blob → LOUD reject
|
||||
// (console.error), raw stashed at localStorage['procity-save.rejected'], fresh start.
|
||||
// THE TOWN NEVER BREAKS: the save holds only player deltas (the delta law), so a bad
|
||||
// save can cost you your stuff, never the world.
|
||||
sleep(), // → the new day (int). day+1 → gig weekNight re-keys to day % 7 → wake at DAWN
|
||||
// (lighting segment 0) → save(). Stock pick streams re-seed via the day salt (§30.3).
|
||||
export(), // → JSON string — the exact procity-save/1 payload (John moves machines).
|
||||
import(json), // → bool. Validated exactly like load(). true ⇒ state replaced + saved + day-derived
|
||||
// state applied (weekNight etc). false ⇒ loud reject, CURRENT STATE UNTOUCHED.
|
||||
wallet, // the wallet-compatible facade — PROCITY.wallet IS this object when the game is on.
|
||||
// Full Lane C v0 interface: cash() start() canBuy(p) buy(o) sell(o, offer)
|
||||
// inventory() count() onChange(fn). buy/sell/inventory keep C's exact semantics
|
||||
// (sell credits the offer + drops the matching v0-inventory entry, per §9.3);
|
||||
// cash is game-backed so a loaded save's cash is authoritative.
|
||||
recordFind(shop, info), // F's buy-seam internal (interior_mode calls it) — appends a collection
|
||||
// entry stamped with townKey + day. Exposed so the gates can drive it.
|
||||
removeFind(entry), // → bool. Identity (===) removal — THE only way the collection shrinks.
|
||||
// C's sell counter: onSell ⇒ F calls wallet.sell(item, offer) +
|
||||
// game.removeFind(entry). Returns false (nothing moves) if entry ∉ collection.
|
||||
};
|
||||
```
|
||||
|
||||
**Collection entry shape** (C's §9.2 asks honoured: `type` stamped at buy time, `title`/`artist` carried):
|
||||
|
||||
```js
|
||||
{ townKey, // where in the world — the boot key above
|
||||
shopId, // plan shop id (always present)
|
||||
godverseShopId, // the real POS id, ONLY when the shop has one (the two id spaces stay separate — R24 law)
|
||||
type, // the ITEM's stock type: 'record' (any dig, incl. an opshop bin) · 'book' · 'toy' (shelf
|
||||
// buys). Sellability is type = type, fail-closed (C §9.2) — this field is load-bearing.
|
||||
sku, // pack item id (`sku_*` / `mint_*` / `rec_*` — the sku IS the identity, R25/26 fences)…
|
||||
slotId, // …OR, for parody items with no pack id: `<binKey>#<offerIndex>` (dig). Exactly one
|
||||
// of sku|slotId is set.
|
||||
title, artist, // display (B's collection card; C's sell card falls back to sku/slotId when absent)
|
||||
pricePaid, // int dollars actually debited
|
||||
dayFound } // game.day at purchase
|
||||
```
|
||||
|
||||
**Save schema `procity-save/1`** (localStorage key `procity-save`):
|
||||
|
||||
```js
|
||||
{ schema: 'procity-save/1', day, cash, town: townKey, collection: [entries], savedAt }
|
||||
```
|
||||
|
||||
THE DELTA LAW, enforced by shape: no plan, no shop, no stock, no clock, no world field of any kind ever
|
||||
enters this object — the world regenerates from seed. Validation on load/import is exact-schema-string +
|
||||
field checks (day int ≥ 1 · cash finite ≥ 0 · collection entries carry townKey/pricePaid/dayFound and
|
||||
one of sku|slotId); any miss rejects the WHOLE save loudly. Versioning: a future `procity-save/2`
|
||||
migrates-or-rejects, never silently coerces.
|
||||
|
||||
**Save points:** `sleep()` and `beforeunload`. (Note, measured against the brief's "save, day+1" order:
|
||||
sleep increments FIRST and saves the post-sleep state, so disk always carries the morning you woke into —
|
||||
the literal order would leave disk one day stale between sleep and unload. Same falsifiable outcome, one
|
||||
fewer window.)
|
||||
|
||||
### §30.2 The laws, wired from birth
|
||||
|
||||
- **`?classic=1`** → `createGame` is NEVER CALLED: no game object, no wallet facade, no listener, ZERO
|
||||
localStorage touches (save.js has no module-scope storage access — the classic-purity gate in ledger #5
|
||||
measures this rather than trusting it). `?game=0` → same absence, C's plain `createWallet` runs the
|
||||
session exactly as pre-v7. Default boot → game on (the v3.1 flip precedent).
|
||||
- **Game money is game money:** the save carries dollars that exist nowhere else; import validates shape,
|
||||
not provenance — there is no bridge to anything real, in either direction.
|
||||
|
||||
### §30.3 SLEEP = TOMORROW — the rotation law (ledger #3, runtime only)
|
||||
|
||||
- **The day salt.** The parody/mint stock pick streams re-seed per (shop, day) by salting the EXISTING
|
||||
runtime streams — plan generation NEVER sees `day` (A verifies the boundary, ledger #6):
|
||||
- interior stock: `buildInterior(shop, THREE, { …, stockDay: day })` → `ctx.stockSalt = ':d'+day` →
|
||||
layout.js appends it to the `stk-*` sub-stream salts (one marked seam line each in interiors.js +
|
||||
layout.js — `[Lane F R30 seam]`). Fittings/layout/audio streams untouched: the ROOM is the same
|
||||
room every day; what rotates is which items the streams pick into it.
|
||||
- the dig: `binSeed(shop.seed, binKey + '@d' + day)` (interior_mode, F-owned).
|
||||
- `day === 1` or no game layer ⇒ NO salt (byte-identical to pre-v7 — the day-1 convention above).
|
||||
- **REAL-sourced stock never rotates.** A shop whose per-shop pack the godverse manifest declares
|
||||
`sourcing:'real'` (Monster Robot Party) gets NO day salt, ever — its crate is a real crate. `mint`
|
||||
crates and parody stock rotate. (The town-wide v2 packs are real *images* used as seeded set-dressing,
|
||||
not a real shop's inventory — they rotate with the parody streams. Ruled here so nobody re-derives it.)
|
||||
- **Gig night = day % 7.** `gig_state.js` (F-owned) gains `setWeekNight(n)` + `get weekNight` — every
|
||||
venue's latch re-keys from its night-0 gig to its night-(day % 7) gig (plan.gigs is already the whole
|
||||
week; the three hard-won latch laws are untouched). Cover stamps reset with the re-key (new night ⇒
|
||||
cover due), which is correct by John's R12 ruling.
|
||||
- **Wake at dawn:** `sleep()` → `lighting.setSegment(0)` (fires `procity:segment`, so every latch +
|
||||
facade observes the morning — the listens-not-polls law does the work for free).
|
||||
|
||||
### §30.4 Cross-lane notes on this contract
|
||||
|
||||
- **→ B (collection UI + SLEEP surface, ledger #4):** everything you need is `PROCITY.game`
|
||||
(`day`/`cash`/`collection`/`sleep()`) + `wallet.onChange` for the cash chip. Cover thumbs: resolve via
|
||||
the entry's `sku` against the shop's pack where present; parody entries have no thumb (title/artist
|
||||
card is correct-and-readable, per the brief).
|
||||
- **→ B (found while wiring weekNight):** `venue.js:38` keys "tonight" to **night 0** — correct pre-game,
|
||||
but after the first sleep the marquee/frontage would advertise night-0's band while the latch plays
|
||||
night-(day%7)'s. `PROCITY.gigs.weekNight` (and `game.day`) are published; one line in venue.js when
|
||||
you touch the HUD. Filed, not silently fixed (your file).
|
||||
- **→ C (sell counter, §9.3):** your asks are honoured — `type` stamped at buy time, `title`/`artist`
|
||||
carried, removal via `game.removeFind(entry)`, credit via the facade's `sell(item, offer)` (mirrors
|
||||
your wallet.js exactly). The E-key routing hook (aimed bin → dig · near counter + sellable → sell card
|
||||
· else shelf-buy) is F's and lands with the sell wiring pass, not this one.
|
||||
- **→ A (ledger #6):** the day salt rides `buildInterior` opts / `binSeed` only — grep-provable that
|
||||
`generatePlan*`/plan_osm never receive `day`.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user