20 KiB
FABLE'S NOTE (2026-07-26). John's pitch; designed by a 3-framing panel (time-attack / market-day / daily-challenge), each adversarially judged, then synthesised. I verified the load-bearing finding myself before circulating this:
kind:'bin'is minted at exactlyfittings.js:60and:75(recordBin,crate), and those appear in exactly two recipes —record(theme.js:38-39) and marketstall(theme.js:196). Confirmed: only record shops and market stalls have a diggable crate. The op shop's recipe (theme.js) is clothes racks, metal shelves, a bookshelf, a trestle table andcrateStack— which its own comment labels "back-room dressing", i.e. scenery, not a bin.That is a finding about the MAIN GAME, not just this mode, and it is filed to the v8 debt ledger: PROCITY is built on thriftgod's op-shop DNA and you cannot dig in an op shop. Whether that is a gap to close (give the op shop a bric-a-brac bin) or a fact to design around (records are the deep dig; op shops are pull-and-buy) is a John-level taste call, and RED TICKET DAY does not depend on the answer.
AMENDMENT (R39, Lane C, measured): this design's roster counts "crate-shops" from the RECIPES — but 44% of market-stall rooms contain no crate at all. The stall's
crateiszone:'centre'and its 3–6 trestle tables take the aisle first, so the roster can name a stall you cannot dig in. Also: a stall dig stamps the pulltype:'record'while the stall itself istype:'stall', andsellableInis type-equals-type — so a stall find has nowhere to be sold. Both are pre-existing, neither is this design's fault, and both must be resolved before RED TICKET DAY ships a roster. Lane C holds a one-linefallbackZonesfix that would give stalls the crates their own recipe already promises.Superseded in part by John's later reframe — "the city being procedural and nebulous… you have to even know where to find the store" — which is running as its own design pass. Read that charter alongside this one: this document owns the ROUND and the LAWS (the Band Floor Law is the best idea in it); the reframe owns the HUNT.
RED TICKET DAY
synthesis of three designs and three adversarial passes — decision-grade, for John
1. WHATAYARECKON
Yes. Build it. Not because the game needs another system — it needs fewer — but because this is the first thing anyone has proposed that spends the clock.
Read our own frontier doc back: "hours exist! nothing makes you race them" and "the clock is scenery." That is not a missing feature, it is a missing cost. Every verb in PROCITY is currently free: riffle a crate, walk a block, dig all day, nothing is consumed except cash. The shops shut and it costs you nothing because there is nothing you were going to do anyway. The sale fixes that from the other end — it makes today the only day the $110 grail is $66, and once today has a thing in it that tomorrow doesn't, the hours that already exist start biting without a single new clock.
And it answers "what is a session?" — which has been open since v4. A session is one town, one trading day, one docket: wake, read the paper, walk, choose which crates you can afford to open, get shut out of the last shop, sleep. Ten to twenty minutes with a beginning, a middle and an end.
Two things it is not. It is not a stopwatch — all three judges landed on that from different directions and the code agrees (details in §8). And it is not free money: the round is a net cash loss by construction, and that is the point. You are not trading, you are buying the day you finally got the thing.
2. WHAT IT IS
RED TICKET DAY. One day in seven, seeded per town, the shops that have crates in them mark stock down. The paper tells you which ones. The markdown never crosses the guide band's floor, so a red ticket is never a gem and a gem is never red — the skill you already have gets harder, not easier. You cannot buy them all. The shops shut. What you carried out is the score, and it lives in the crate forever with "day 14, Bendigo" under it.
Structure and scoring come from TIMEATTACK (bounded round, forced-flag gates, DOM-only, the Band Floor Law). Diegesis and rhythm come from MARKETDAY (a day, not a stopwatch; the morning paper; the market's own FIRST IN BEST DRESSED fiction, already painted on the wall at theme.js:201). The time model comes from DAILY's one great idea — time passes on the street, not in the shop — corrected: seconds don't cost you indoors, but crates do.
3. THE ROUND
Wake. SLEEP rolls the day (save.js, index.html:281 → lighting.setSegment(0)). If today is the town's red-ticket day, the wake toast carries the paper:
🌅 day 14 · RED TICKET DAY — Vinyl Solution ⅓ off · The Turnstyle ¼ off · Sadie's half price
The sale module then sets the segment to the first roster shop's opening, so a sale day always starts with a door about to open.
The roster: what the town actually has. Only two shop types carry crates (measured, §4). The roster is the biggest cluster of ≤3 crate-shops all within 400 m of each other, seeded. Across the 26 shipped town keys at seed 20261990:
| roster | towns | tour | sprint @ 8.8 m/s |
|---|---|---|---|
| 3 shops | 5 (fitzroy, adelaide, brunswick, katoomba_real, melbourne) | 74–369 m | 8–42 s |
| 2 shops | 7 | 6–390 m | 1–44 s |
| 1 shop | 11 | — | — |
| 0 — no sale day | 3 (bowral, darwin, daylesford) | — | — |
Synthetic towns (plansrc default, ~480 shops, 103–151 crate-shops): roster of 3, tour 8–27 m. So the mode is a route problem in the default town and in five real ones, and a depth problem in the other seventeen. Both are rounds. Neither is a lie.
Inside. A record shop holds 3–8 crates × 16 sleeves = 48–128 records, of which (modelled off the shipped band table at bible.js:26-30) 5–13 carry a red ticket. A ticket is 25 / 35 / 50% off asking, seeded per shop-day, floored at the band's low edge. Mean marked price ≈ $21; mean saving ≈ $10; a grail asking $90 comes down to $60 and no further.
The purse. Day-1 wallet is 60 + (r()*140|0) ≈ $130 (wallet.js:10). Six red tickets and you're out. There are 12–30 on a three-shop roster. You take a fifth to a half of what's marked. That is the whole game of the round: not "grab everything", but "which of these do I want enough."
The clock — the one real change. Time does not pass while you riffle. It passes when you walk (the street clock, exactly as shipped) and when you open a crate: one crate ≈ ⅓ of a segment. A trading day gives you roughly nine crate-openings. Fitzroy's 369 m tour eats three of them. So three shops = two crates each, out of the 3–8 each shop has. You will never see the bottom of a town, and the shutter comes down mid-crate.
The end. The last roster shop shuts (isOpen, already enforced at index.html:493). The docket totals what you carried out:
🔴 the day's take — 5 red tickets, $84 saved, $103 spent · you left 9 marked in the crates you opened · you never made it to Sadie's
Then SLEEP. Six ordinary days until the next one, or travel (which costs a day anyway).
4. THE THREE FACTS THAT DECIDED THE DESIGN
Measured this session against the tree, not asserted:
(a) Only two shop types have crates. userData.kind:'bin' is minted at exactly fittings.js:60 (recordBin) and :75 (crate), and those appear in exactly two recipes: record (theme.js:38-39) and market stall (theme.js:196). Op shops, pawnbrokers, book barns, toy shops have no diggable bin at all. Two of the three submitted designs set their flagship scene in an op shop. That scene cannot happen.
(b) The real towns are big and thin. Generated all 26 town keys: crate-shops per town run 0 to 12, and they can be 2.3 km apart (hobart, newtown). Three towns have none. The "three shops, three depths, pick a route" premise is a Fitzroy feature. Hence the 400 m clique rule and the honest "the round is however many shops this town has."
(c) The day is six values wide. HOUR_NUM = [6.5, 9, 12.5, 15.5, 18.5, 22] (lighting.js:22); record hours are 10–18 (registry.js:40), stalls 8–14. So records are open at 12:30 and 15:30 only; stalls at 9:00 and 12:30 only — two segments each, 80 seconds of street clock. MIDDAY is the only segment where the whole diggable town trades. That is not a bug to design around, it is the trading day, already shipped: stalls in the morning, records after lunch, everything at midday. Use it.
5. MECHANISM, LANE BY LANE
Lane C — dig.js, ~8 lines, one new optional opt.
dig.js:421— afteroffers = …, beforebuildStack(offers)at:424:if (opts.sale) offers = offers.map(opts.sale);A post-draw transform.proceduralSleeves(:36-56) andpickRealOffers(:379-402) are complete; zero stream drift, zero re-order, no golden moves. Never in-place onpack.items—stockpack.js:30-31caches by (type, base) and an in-place write leaks town-wide and permanent.{...o}copy, always.dig.js:110-115— redfillStyleon the price wheno.wasis present. Rides the existing per-openCanvasTexture. +0 draws.dig.js:266-275— a🔴 SALE · was $50chip in the stamps row (present-fields-only, so absent ⇒ byte-identical card). Theguide $25–59line stays exactly where it is: that is the discount theatre.~~$22~~ $17insideguide $8–24and~~$50~~ $31insideguide $25–59are the same red ticket and opposite verdicts.dig.js:480-487— widencurrentOffers()withwasandsale. Without this the gates cannot see their subject (vacuous-gate law).bible.js,sell.js: no change. Not one line.
Lane F — one new module + three small edits.
web/js/world/sale.js(~180 lines): the seeded calendar, the roster, the ticket transform, the crate accumulator, the docket DOM. Constructed only underflagOn('sale') && game; never constructed under?classic=1,?sale=0or?game=0, so absence is by non-existence, not by branch (thewashing.js/magpie.jsprecedent).index.html: one construction line, onePROCITY.flagsentry, one?sale=allforce. No per-frame update — the module is event-driven offprocity:segmentandprocity:digClose. Zero frame cost.save.js:recordFindcarrieswasandsale:1on the entry (exactly thebandprecedent at:294);validEntry(:52-60) validates both when present;listFind(:366) refuses asale:1entry.hud.js: the docket strip gets its own body-mounted root, not a child of#pc-game—hud.js:283-285sets#pc-gametodisplay:nonefor the entire dig, and with no countdown to watch that is now fine, but the strip needs its own construction gate and its own DOM assertions.
Lane B — one optional four-line API: lighting.advance(frac) so the crate cost can move the day by a third of a segment without setSegment() stomping sub-segment position (lighting.js:113 sets clock = i + 0.001). Fallback if B is busy: sale.js keeps its own accumulator and calls setSegment only on a boundary crossing. Nothing else from B, A, D or E. Zero assets, zero fetches, zero draws.
6. THE LAWS
1 — THE BAND FLOOR LAW. price = max(bandLow, floor(ask × (1−d))), and no band ⇒ no ticket (fail-closed). One Math.max buys both fences at once: the keeper's counter offer is min(lo−1, ⌊lo/2⌋) off bandLow (sell.js:28-32 + bible.js:52-58), so every marked buy costs ≥ lo and sells for < lo — the no-pump gate's all(d <= -1) holds with zero changes to sell.js. And nothing under a band floor is ever a sale, so under-band still means "nobody noticed" — the gem survives intact.
2 — THE PROVENANCE LAW. A sale:1 entry can never be consigned. "Can't put that through GodBay, mate — half the district was at the same sale." One guard at save.js:366, one hidden ⚖ on the crate row.
This replaces the "drop the band" fence that two of the three designs proposed, and it is not a preference — that fence is broken. With no band,
godbayHammersetslo = hi = pricePaid(save.js:36-37) — but the 12% bidding war multiplies afterwards by 1.5–2.0 (:43), so ~12% of sold listings profit and the design's own gate arm would go red. It also prints your own purchase price back at you in the morning paper, and blanks the guide line on the sell card (sell.js:104). A refusal is one line, hard-assertable, and diegetic.
3 — THE GEM COVENANT. The gem is never ticketed. On a sale day the eye is trained on red for ten minutes and the money is still in the plain yellow sticker at $6 in a guide $25–59 band. Free, and it makes the core skill harder on the day the game is loudest.
4 — DETERMINISM & THE DELTA LAW. Four new kind labels — 'saleday', 'saleroster', 'saleticket', 'salethin' — independent streams by construction, so nothing existing shifts. Tickets are keyed to item identity, never array position (${shopId}:${day}:${binKey}:${sku ?? i}): dig.js:422 filters procedural offers by gone, so a position-keyed ticket would move to a different record the moment you bought one. Save side: zero new top-level fields. The score is a pure function of the collection (Σ was − pricePaid over today's sale:1 entries); only was and sale are added, both optional, both on the entry, both validated exact-and-loud. Old saves load untouched.
7. THE GATES (Lane F)
- Non-vacuity.
?sale=allmarks every eligible shop at rate 1.0, sosmoke_nopump's shop picker (flags_check.py:2537-2559) is guaranteed on the roster. Re-run the 5 real UI round trips; assertall(d <= -1); FAIL — not skip — if fewer than 3 of 5 bought offers carriedwas. - Band floor. Over ≥5 bins: every offer with
washasprice ≥ bandRange(band)[0]andprice < was. - Provenance. Buy marked →
listFind === false. Positive control: buy unmarked in the same shop →listFind === true. - Negative control.
?sale=0/?classic=1/?game=0→ zero[id^=pc-sale]DOM, zerowasanywhere, and the offer tuples byte-identical to a pre-sale boot. Mint that offer-price golden this round — it does not exist today (flags_check.pypins plan fingerprints only). - Identity-keyed tickets. Buy one marked slot, re-open the bin, assert the surviving marked set is unchanged (the R27 zero-collateral law, applied to the ticket).
- Calendar determinism. Same (seed, town, day) ⇒ identical roster and identical marked set across two boots.
- (full version)
PROCITY.sale.debug.crates(n)— every existing tool pins midday (dbg.js:325+ 20 call sites), so the crate clock needs an explicit hook or its gate is vacuous by construction.
8. CUT LIST
| cut | why | verified at |
|---|---|---|
| The 120-second stopwatch | dt is clamped to 0.05 s — a 10 fps machine gets a 2× longer round and best is framerate-denominated; procity:segment can't tell a real transition from a bracket press; DBG pauses the clock in every gate we own; and the strip would be invisible for the whole dig |
index.html:575, lighting.js:109-113, dbg.js:325, hud.js:283-285 |
| Op shops, pawn, book, toy from the pool | no bins, at all | fittings.js:60,75 vs theme.js:56-63,134-141 |
| "Drop the band" as the anti-farm fence | 12% of sold listings profit via the war multiplier; prints a legible inversion in the paper; blanks the sell card's guide line | save.js:36-43, sell.js:104 |
The Wordle bell, share string, ?run=today |
needs createGame({startDay}), which doesn't exist (let day = 1, single writer adopt()); the board is a query string, so the leaderboard is decorative |
save.js:110-113,167 |
🎟 N marked bin hover |
forces exporting proceduralSleeves or mirroring it (~1,100 PRNG draws per room enter), and points the player away from the gems |
dig.js:36 |
| The sandwich-board InstancedMesh | +1 draw is affordable; the frontEdge/ry orientation math, texture, chunk refresh and gate arm are not, when the docket already names the shops |
reserve for v9 |
| The 23-town calendar strip | would advertise sale days in the 3 towns with nothing to dig | measured §4(b) |
Dollar-threshold ranks ("$200 = LEGEND OF THE TIP"), best |
measure wealth, not play — start cash is 60 + (r()*140|0) and grows unbounded |
wallet.js:10 |
goesAt ∈ {2,3,4,5} thinning |
50–75% of that distribution falls in segments the shop is never open in — it resolves to a constant. Replaced by one bit keyed to the shop's own hours: arrive in a shop's last open segment and half the red tickets are gone | registry.js:40,128 × lighting.js:22 |
9. EFFORT, AND THE MINIMUM SHIPPABLE THING
MINIMUM — one round. This is playable this week.
The sale exists and nothing is scored: ?sale flag, seeded calendar, roster, band-floor tickets, red sticker + SALE chip, was/sale:1 on the entry, the listFind refusal, the paper line on the wake toast. Gate arms 1–6.
Cost: one Lane C afternoon (dig.js ~8 lines), one Lane F round (sale.js ~120 lines, three save.js edits, two index.html lines), and the offer-price golden. No lane conflicts — nobody is currently in dig.js:421 or save.js:366, and v8 wave 1 is entirely elsewhere.
FULL — two more rounds.
- the crate clock and the Lane B
advance()API, + thinning at room-enter, + the docket strip and the end-of-day total, + the keeper's rumour instead of a calendar UI, + gate arm 7 and the DBG hook. The second of those two rounds is mostly QA, because the crate clock touches what time means and every existing tool pins midday.
Ship the minimum first as a field test: it tells you whether a red ticket is fun before you rebuild the day around it.
10. WHERE IT SITS AGAINST v8
The minimum version is a genuine v8 sidebar. One flag, zero draws, zero assets, zero fetches, DOM-only, construction-gated, no lane collisions with ambient audio, per-town character, the magpie or backyard props. It is content wearing a very thin system, which is exactly what v8 is.
The crate clock is not a v8 sidebar — it is the head of v9. Making a crate cost time is the first time in this game's history that dwelling costs anything, and it is the direct, literal answer to "the clock is scenery." That deserves an epoch, its own gates and its own retro, not a wave slot.
One flagged follow-up, not this round: give the op shop a record crate. It is one line in theme.js:56-63 and it would take the diggable pool from 0–12 per real town to something like 3–25 — which fixes the roster histogram, fixes the 3 dead towns, and delivers the actual op-shop fantasy the pitch is written in. It re-rolls every op shop interior, so it is a Lane C spike with a golden re-mint behind it. Worth costing properly in v9.
11. WHAT NEEDS JOHN
- Stopwatch: in or out? The recommendation is out — the pressure is the shutter, the purse, and the crate cost. You said "limited times". Is a trading day limited enough for you, or do you want a number counting down?
- Does opening a crate cost time? This is the one real design change in the document, and the one that makes hours bite. It rations breadth, never the riffle itself — you can take as long as you like inside a crate; you just can't open the next one for free. Yes or no.
- Are red-ticket finds locked out of GodBay forever? It is the clean fence and it is diegetic, but it is a real restriction on your auction toy.
- Is the band floor sacred? i.e. a red ticket can never be a gem. This buys the no-pump law and the gem signal with one clamp. Breaking it means red tickets and grails start looking alike.
- Does the sale run in one-shop towns? Eleven of twenty-six get a roster of one. Recommendation: yes — a one-shop round is a depth round. Three towns get no sale day at all, honestly.
- Is the crate the only trophy, or do you want a score that persists? Recommendation: crate only, no personal best, no leaderboard. The number is for the day, the record is forever.
- The name. RED TICKET DAY is the working title. Given three designs independently reached for "red ticket", it is probably right — but it is your word to pick.