# Plan Maker 2000 — Spec v0.1 Living spec. Source of truth is the Claude Doc (see README); this file is exported from it. Last export 2026-09-19. ## Summary Plan Maker 2000 is a mobile-web decision game you boot from a stuck group chat: screenshot the argument, an agent turns it into options, everyone places yes and veto chips, Jev pulls the lever, the decision posts, and the table either runs with it or goes another round. Balatro structure (table, run, hand, round), no messaging, no real money. The pitch line still holds: normal chat vibe on top, hectic probability machine underneath. The probability machine is Jev. ## Use cases Design for dinner tonight first; the holiday run must fit the same primitives without a second model. Friday dinner falls out of the ledger for free. | Priority | Use case | People | Hands | Duration | What it stresses | | --- | --- | --- | --- | --- | --- | | 1 | Dinner tonight | 2–6 | 1 | minutes | intake speed, reveal, re-share | | 2 | Holiday planning | 2–6 | 3–10 over days | days | run structure, veto scarcity, expiry | | 3 | Recurring Friday dinner | 2–4 | 1 per week | weeks | ledger, streaks, rubber-banding | The couple case (2 people) is the hard-feelings test; the group case (4–6) is the re-share test. Both must work, but only the group produces the success metric. ## Core model: table, run, hand Three objects, borrowed directly from Balatro's run/hand structure. Everything else in the product hangs off them. | Object | What it is | Lifetime | Holds | | --- | --- | --- | --- | | Table | A group of people (phone numbers) | persistent | members, ledger, veto allowance per run | | Run | An optional sequence of hands toward one goal ("Japan trip") | until last hand accepted, or 14 days idle | ordered hands, remaining vetoes | | Hand | One decision, looped through rounds until accepted | until accepted or expired (default 24 h) | question, rounds, final decision | | Round | One pass of options → chips → lever → posted decision | minutes | options, chip placements, Jev's odds, outcome | Rules: - The loop is: generate options → place yes and veto chips → Jev pulls the lever → decision posts → accept or go again. A hand is that loop until the table accepts. - A hand ends on accept or expiry and never reopens. Refinement happens in rounds before accept, not by reopening. - Every round carries a standing "More options" card. If it wins the lever, the options agent regenerates and a new round starts. - A single-hand decision is a run of length 1. No separate code path. - A table is created implicitly by the first hand shared to a new set of people. Nobody "makes a table". - Two-person tables are just tables. The couple case gets no special mode; rubber-banding covers it. ## Identity A phone number is the whole identity. It exists for one reason: so the ledger follows a person across hands and tables. - Join flow: open hand link → enter phone → 6-digit SMS OTP → type a display name → you're at the table. Under 30 seconds, once per device. - Returning device: cookie/session, no OTP. Re-verify only when the session is gone. - No profile, no avatar upload, no username, no password, no email. Display name is per-table and editable. - Invite = the hand link, pasted back into the WhatsApp group. Whoever opens it and verifies is a member of that table. - Table membership is inferred from who joined the first hand. Later hands at the same table reuse the member list; new people can still join via link. - Phone number is never shown to other members. Only the display name is. Why phone and not name-only: the group already exists as phone numbers in WhatsApp, so it costs nothing to ask, and without it "you're paying next time" can't survive a hand. ## Intake and the options agent One LLM agent generates options: from a screenshot at intake, and again mid-hand whenever "More options" wins a round. It is the only text-generating model in the product; Jev takes over the moment options exist. ```mermaid flowchart LR A[Screenshot or paste] --> B[Options agent
question + 2-4 options] B --> C{Ambiguous?} C -- yes --> D[One clarifying
question] D --> B C -- no --> E[Confirm / edit card] E --> F[Round opens] M[Manual entry] --> E G[More options
wins a round] --> B ``` At intake the agent sees the screenshot and the table's ledger and returns a question, 2–4 options, and any constraint it spotted ("Sam said not Tuesday"). The initiator sees a single editable card and taps share. On a regen it sees the previous round instead: which options had chips, which were vetoed, what Jev's odds were. That is its brief ("more like Thai, cheaper, not Tuesday"); vetoed options never come back. Rules: - Options cap at 4 per round, plus the standing "More options" card. More than that is two hands. - One clarifying question max before the card. Intake never becomes a conversation. - Regen rounds cap at 3 per hand. After that "More options" is greyed and the table picks from what's there. - Screenshots are processed and discarded. Only the extracted card persists. - Multiple screenshots (a long argument) are accepted in one intake. - Target: screenshot to open round in under 20 seconds; regen under 10. ## Chips Everyone gets a fresh stack each round. Yes chips say what you'd be happy with and how much; veto chips kill an option outright. Chips don't set the odds directly — Jev reads the whole table, chips included, and sets them. **Yes chips (per round)** - Each member receives 10 yes chips at the start of a round (plus any rubber-band bonus). - Chips go on options in any split, including the "More options" card. Stacking is intensity: 8 on Thai says more than 3. - Placing is live and visible to the table. You can move your chips until you lock. - Jev's odds are shown live under the options and shift as chips move. That is the feedback loop: you watch your vote move the needle. - A member who places nothing by expiry is treated as abstaining. Jev sees that too. - Chips do not carry over. Unspent chips vanish at the pull. There is no balance. **Veto chips (per run)** - Each member gets 2 vetoes per run (a single-hand decision is a run, so 2 per hand in the dinner case). - A veto deletes an option from the hand. Chips already on it return to their owners for re-placement. - A veto cannot remove the last two options. Minimum viable hand is 2 options. - Vetoes are visible and attributed. "Sam vetoed Thai" is part of the fun and the accountability. **The lever** When everyone has locked or the round's time is up, Jev pulls the lever. Its calibrated probability over the options (the one it has been showing live) is the draw weights; the server samples from it, seeded and logged so the reveal can replay it. Sampling, not top-pick: the underdog can still win, so nobody's choice "got validated by the AI", and a 30 % option landing reads as luck. **Rubber-banding** - Members whose chips were mostly on losing options receive bonus chips next round or hand at the same table: +3 if less than 25 % of your chips were on the winner, +1 if 25–50 %. - Bonus is capped at +5 so a streak of losses doesn't make you a dictator. - This is what keeps the two-person table from souring. The loser has a structural edge next time without anyone conceding, and Jev is told about it in the state so it doesn't double-count. ## Jev Jev is the probability machine. It is TypeSafe AI's System One model: it takes a state plus a set of typed questions and returns calibrated probabilities for all of them in parallel, in 70–500 ms, at roughly $0.04 per million input tokens with output free ([TypeSafe announcement](https://typesafe.ai/blog/introducing-system-one-models-and-jev), [LangChain guide](https://www.langchain.com/blog/building-a-harness-with-jev)). It does not generate text and cannot hallucinate; outputs are schema-guaranteed. Three question types: **choice** (probability per option + confidence), **score** (position on an ordered scale), **noul** (probability a statement is true). Cardinality up to 255 per choice. Jev is the decider, but a semi-decision-makery one: it weighs the chips heavily, along with constraints and the ledger, produces odds, and the lever samples them. It is not a majority counter and not an oracle. Every chip placement is a state change; Jev re-reads the round and re-answers a fixed question set fast enough that the odds move as you drag. **The hand state Jev sees** Question, options (including "More options"), current yes-chip placements per member, vetoes spent, constraints from intake, rubber-band bonuses in play, previous rounds of this hand (options, chips, what was posted), time to expiry, and the table's ledger (past outcomes, streaks, house rule). No phone numbers. **Question set, asked on every state change** | Question | Type | Used for | | --- | --- | --- | | Which option should this table go with? Yes chips are votes and weigh heavily; respect constraints; be fair over the ledger. | choice over options + More options | the odds shown live, and the lever's draw weights | | Is this round contentious? | noul | triggers the negotiator card when > 0.7 | | What is the disagreement about? | choice: cost / timing / distance / people / taste / other | picks which canned clarifying prompt to show | | Is any option effectively dead? | noul per option | greys the option, suggests a veto | | Is anyone sandbagging (chips spread to avoid commitment)? | noul per member | light nudge to commit | | How spicy is this round? | score: mild / warm / hot / volcanic | drives the UI escalation ramp | | Which dealer line fits? | choice over a bank of \~200 pre-written lines | the posted-decision line and ledger line | **Why sample instead of pick** If Jev picked its top option every time, it would be a weighted vote with extra steps and "the AI sided with Sam" becomes the hard feeling. Sampling from calibrated odds keeps the ritual: the lever is a real pull, the odds were on the screen before it, and a loss is bad luck. Jev's influence is in the odds, which everyone can see and argue with by moving chips. **Rules** - Jev decides by odds, not by top-pick. The lever always samples. - Jev never writes text. Every string a player sees from Jev is from a human-authored bank, selected by a choice question. Australian-English, irreverent, warm, never smug. - One Jev call per state change, all questions in parallel. Debounce chip drags to 200 ms. Budget: a busy hand is a few hundred calls; at Jev's pricing that is well under a cent. - Option generation stays on the LLM agent. Jev has no image input and no string output. - Jev's confidence is surfaced, not hidden. Low confidence on the odds shows as a wobbling bar, not a false number. ## Ledger The ledger is the only thing that persists and the only thing that makes losing fun. It records outcomes and social debt, never chip balances. Per table, per resolved hand: | Field | Example | | --- | --- | | Question | Friday dinner | | Rounds | 2 (More options won round 1) | | Winner (option) | Thai | | Who backed it | Sam 8/10, Tom 2/10 | | Vetoes spent | Tom vetoed pizza | | Jev's odds at the pull | 62 % Thai | | Debt line | Tom pays; Sam picks next | | Dealer line | "Thai. Again. Tom, get your wallet." | Derived views the table sees: - Streaks: "Sam has won 4 Friday dinners in a row." - Pays-next / picks-next: set by a house rule chosen when the table is created (loser pays, winner picks, or none). - Most-vetoed option: the running joke. - Run summary: for a holiday run, the resolved hands in order as the itinerary. Not in the ledger: chip counts across hands, any score, any rank. Nothing accrues that could look like a currency. ## UI escalation Calm to hectic is the emotional arc. The Balatro treatment is a ramp driven by Jev's "how spicy is this hand" score, not a theme applied from screen one. A unanimous hand never leaves the poll look; a contested one climbs to full CRT. | Stage | Look | Motion | Jev avatar | | --- | --- | --- | --- | | Intake / confirm | plain iMessage-style poll card, system font, flat | none | text bubble, no face | | Chips placing | felt table, chip stacks, Jev's odds bar live | chips slide, odds bar eases | low-res sprite, 16 px | | Lock / countdown | felt darkens, options become cards, lever arm appears | cards tilt, CRT scanlines fade in | sprite animates, 32 px | | Pull (reveal) | full CRT, screen shake, score-tick sound | lever cranks, reels spin on Jev's odds, winner card flips, confetti | full dealer sprite, speaks the line | | Posted | decision card, accept / go again / more options | none | dealer sprite idles | | Ledger | back to flat, receipt-style | none | text bubble | Reveal requirements: - Capture-safe: the final frame fits a 9:16 phone screenshot with question, winner, Jev's odds, dealer line and table name all visible. No overlays, no UI chrome in that frame. - Replayable: any member can re-watch the pull from the ledger. Same seed, same reels. - Under 6 seconds from pull to final frame. Skippable after 2. - Sound is on by default with a visible mute. Haptics on the lever crank and the winner flip. - Jev's odds are shown before the pull, so a 70 % favourite losing reads as bad luck, not a rigged app. ## Engagement design: dopamine, serotonin, cortisol The lever is a slot machine with no money in it, so design it like one: variable reward, near-misses, named winners, a countdown, scarce vetoes, and a house (Jev) that everyone can blame. The one rule that keeps it a game rather than a fight is that every spike resolves inside the hand. **Dopamine: anticipation and variable reward** | Mechanic | Why it works | Build note | | --- | --- | --- | | The pull is a variable-ratio reward | Outcome uncertain, odds visible, action is yours (you crank the lever) | The crank is a drag gesture, not a tap. Effort before reward. | | Stretch the moment | Anticipation beats outcome for dopamine; slots spend 3–4 s on reels for a 200 ms decision | Reels spin on Jev's odds. 2 s minimum before the first reel stops, even if the draw is already known. | | Near-miss | Losing by a hair drives replay more than losing outright | Reels always pass through the runner-up (by chips) last before landing. If your option was runner-up, your card visibly almost lands. | | Upsets | A 20 % option landing is a story; a 70 % landing is Tuesday | Jev samples, so upsets happen naturally. When the winner was under 30 %, escalate: "UPSET" banner, double confetti, a ledger badge. | | Called it | Backing the winner heavily against the odds feels like skill | Ledger line: "Jem called it at 25 %". Titles for repeat callers. | | Live odds move as you drag | Action → visible consequence at 100 ms is the core slot loop | Jev call per drag, debounced 200 ms. The needle must move on every placement or the loop breaks. | | Ding ding ding | Sound anchors the reward | Escalating tick as reels stop, a bell on the winner, a groan on a veto. Mute visible, default on. | **Serotonin: status and being seen** | Mechanic | Why it works | Build note | | --- | --- | --- | | Winner is a person, not an option | "We're going with Jem's option" attributes the win | Whoever had the most chips on the winner gets their name on the decision card. Ties → earliest placement. | | Titles | Public, earned, in-group status | Pattern-derived by Jev over the ledger: Table Oracle (calls it), Chaos Agent (backs long shots), Serial Vetoer, The House Always Wins (Jev's favourite). Shown next to the name at the table. | | Streaks | Visible dominance is status; visible losing streaks trigger the rubber-band so nobody bottoms out | "Sam: 4 Friday dinners in a row" on the ledger. | | Being seen by Jev | Jev naming what you care about is validation | The negotiator card: "Jev reckons Tom's no is about money, not Thai." Human-authored line, Jev-selected. | | In-group language | Ritual lines become the table's slang | One fixed ritual line on every pull ("the lever has spoken") plus a rotating dealer line. The fixed line is what gets quoted back in WhatsApp. | | The screenshot | Social proof and bragging rights outside the app | Capture-safe final frame with the winner's name large. This is the acquisition loop. | **Cortisol: stakes and urgency** | Mechanic | Why it works | Build note | | --- | --- | --- | | Round countdown | Time pressure raises arousal; last-to-lock is social pressure | "Everyone's waiting on Tom" card, in-room only. No push. | | Vetoes are scarce, public, attributed | Spending one is a costly signal; watching someone reach for one is tension | Veto has a hold-to-confirm and a distinct sound. The table sees the hover. | | Chips vanish at the pull | Loss aversion: unspent is wasted, so place them | Unplaced chips fly off the table at the pull. | | Social debt | A consequence outside the app is real stakes | Loser pays / winner picks, written on the ledger and the screenshot. | | Comeback | Hope keeps the loser in | Rubber-band bonus is announced at the start of the next round: "Tom's got +3 this round." | | Spice ramp | The app visibly gets more intense as the table does; feedback amplifies | Jev's spice score drives the CRT ramp. Volcanic rounds shake. | **Every spike resolves in-hand.** Countdown → pull → named winner → dealer line → debt line → accept. Nothing is left hanging when the hand closes. A couple app that manufactures cortisol and doesn't discharge it is a churn machine and a breakup tool; the discharge is the product, not the spike. **Interpersonal dynamics** - Coalitions: chips are visible, so piling onto someone's option is an alliance and splitting is a hedge. Jev's sandbagging noul exposes fence-sitters with a nudge, not a callout. - Jev is the lightning rod. The lever decided, not Sam, so the loser's resentment goes to the machine. Give Jev enough personality to be ribbed collectively ("Jev's a Thai shill"). A shared antagonist bonds the table. - Reciprocity is mechanical: rubber-band, winner-picks-next, and Jev instructed to be fair over the ledger. Nobody has to concede; the system does it for them. - Vetoes are statements. "Tom vetoed pizza" is on the record and becomes table lore. Expect vetoes to be spent on people as much as options; that's fine, it's costly. - Two-person tables run hot because every hand is zero-sum. Three levers cool it: winner attributed to Jev's odds not the partner, rubber-band, and the negotiator card turning "no" into a reason. - Absence has a cost. A hand you skipped still resolves and the ledger says you abstained. No penalty beyond that; no streak-loss anxiety for being away. **Jev's natural form, used on purpose** - Calibrated odds are the drama. A 50/50 pull is a coin flip; a 90/10 upset is a legend. Show the number, always. - Confidence is tension. Low confidence wobbles the odds bar; high confidence locks it. "Jev's sure" and "Jev's torn" are two of the dealer states. - Parallel questions at 100 ms make Jev feel like it's watching. It reacts to every chip. That reactivity is the personality; there is no text. - Nouls are narrative triggers. Contentious, sandbagging, dead option, and per-member "who is this decision for" each fire a human-written callout. Cheap enough to run per member, per drag. - Score is the mood ring. Spice drives the UI; "how close is this" drives the reel duration. - **Jev picks the format.** One more choice question per round: "which game fits this state?" over a small bank of mechanics. Two options and low spice → a Tinder-style swipe, yes or no. Four options and split chips → the full craps table. Near-unanimous → a one-tap confirm with a tiny pull. Contentious and volcanic → reels with the long near-miss. The table never knows which game they're getting until the round opens; that unpredictability is itself the Reels feed. Each format ends in the same lever pull so the ritual is constant while the game varies. - No hallucination means Jev can be handed sharp lines safely. It picks the sting from a bank we wrote; it can never invent one. Cardinality 255 means the bank can be big and tagged. - Cost is a non-issue. A volcanic hand with 500 state changes is under a cent. Spend Jev calls freely on feel. **Where it stops being fun** - No mechanic may persist a negative state past the hand: no losing-streak badges, no "you owe 3 dinners" running tally beyond the last hand's debt line. - No urgency that reaches outside the room. No push, no SMS beyond OTP. - Nothing purchasable, ever. The moment chips can be bought the chemistry above becomes the thing regulators mean by gambling. ## Hand state machine A hand loops through rounds and has two terminal states. Nothing reopens after accept or expiry. ```mermaid stateDiagram-v2 [*] --> Draft: intake Draft --> Open: initiator shares Open --> Open: chips placed / vetoed Open --> Pulled: all lock or round timer Pulled --> Posted: Jev's odds sampled Posted --> Open: refine, or More options
won and agent regenerated Posted --> Accepted: table accepts
or quiet window passes Open --> Expired: hand expiry, no chips Accepted --> [*] Expired --> [*] ``` | State | Who can act | Timer | | --- | --- | --- | | Draft | initiator only | 1 h then discarded | | Open | all members: place, move, veto, lock | round timer, default 2 h; hand expiry default 24 h, settable 1 h–7 d | | Pulled | nobody; draw runs within seconds | n/a | | Posted | all members: accept, or go again | quiet window 1 h → auto-accept | | Accepted | nobody; ledger written, reveal replayable | n/a | | Expired | nobody; default applies | n/a | Posted → Open is the GOTO. Any member can tap "go again" to start a new round with the same options (chips reset, vetoed options gone) or "more options" to send the agent back in. Accept needs a majority of members, or 1 h of silence. Expiry: if any chips were placed this round, expiry pulls the lever and the posted decision auto-accepts. If no chips were placed at all, the hand expires with the initiator's first-listed option and the ledger notes "decided by default". Reminders are in-room only (a countdown), no push. ## Guardrails These are the lines that don't move. Each one exists because crossing it turns the product into something else (a chat app, a casino, a native app). - No purchasable currency, no cash-out, no chip balance that persists. Chips reset every hand. This is what keeps it clear of AU interactive gambling rules; the line is value changing hands, not the presence of odds. - No messaging infrastructure. No DMs, no threads, no message history, no notification backend. Reactions on options are the only in-hand expression. - No accounts beyond a phone number. No profiles, no friend graph, no discovery. - Mobile web only. No native app, no app store, no push. Share sheet and browser are the distribution. - A hand ends on accept or expiry and never reopens. Rounds are how you refine; reopening is not. Ephemerality is the product. - Jev decides by sampling its own calibrated odds, never by taking the top option outright, and never writes text. Every line it "says" is human-authored. ## v1 scope **In** - Phone + OTP join, per-table display name - Options agent: screenshot intake, manual entry fallback, regen on "More options" (cap 3 per hand) - Rounds: 2–4 options + More options card, 10 yes chips, 2 veto chips, Jev's odds live - The lever: server-side sample of Jev's odds, seeded and replayable - Posted → accept / go again loop, majority accept or 1 h quiet window - Full pull animation, capture-safe final frame - Ledger: outcomes, rounds, debt line, streaks, Jev's odds at the pull, house rules (loser pays / winner picks) - Rubber-banding - Hand expiry with default - Runs (ordered hands under one goal) — thin: just a name and a list - Jev negotiator card: contention-triggered, canned clarifying prompt chosen by Jev, no free text - Spice-driven UI ramp and Jev-selected dealer lines from a \~200-line bank **Out** - Any free text shown as Jev; the options agent's output is only ever option cards - Any payment, premium tier, or purchase - Native app, push notifications - Reactions or emoji on options - Table admin (removing members, renaming) - Anything that reads like a chat **Stretch (only after an unsolicited share is observed)** - Jev question-set expansion: per-member "what does Tom actually want" reads, run-level predictions - Sound design beyond the reveal - Run templates ("plan a trip" pre-seeds where / when / stay hands) - Table-level house rules beyond the three ## Validation Success is one unsolicited share: someone forwards a hand link or a reveal screenshot to a chat the initiator isn't in. If that doesn't happen in 4 weeks, the wedge is wrong, not the UI. - Seed two tables: one couple (hard-feelings test) and one friend group of 4–6 (re-share test). Run them in parallel for 4 weeks. - Watch: hands per table per week, time from intake to lock, reveal replays, screenshots of the reveal (detect via share-sheet events), new phone numbers joining a table that weren't invited by the initiator. - The couple table validates rubber-banding and tone. The group table is the only one that can produce the success metric. - Don't ask for feedback in week 1. Read the ledger and the join graph instead. - Kill criteria: fewer than 2 hands per table per week by week 3, or zero reveal screenshots by week 4. ## Open questions - [ ] Name. "Plan Maker 2000" is a working title; the hand link domain is the brand people see in WhatsApp. - [ ] Chip numbers. 10 yes / 2 veto / +3 rubber-band are first guesses. Tune on the seed tables. - [ ] Lever sampling: straight sample from Jev's odds, or sharpened (temperature < 1) so a strong majority usually wins? Straight is more honest; sharpened is less annoying. - [ ] How heavily should the choice question's instructions tell Jev to weigh chips vs ledger fairness? This is the whole "semi-decision-makery" dial and needs playtesting. - [ ] Format bank for v1: swipe, craps table, one-tap, reels. Do all four ship, or two with the others behind the Jev choice question later? - [ ] Accept mechanics: majority tap, any single tap, or quiet window only? - [ ] Jev early access: confirm we can get an API key and what the rate limits are before building the live loop on it. - [ ] Dealer line bank: who writes 200 lines, and how do we tag them so Jev's choice question has enough signal? - [ ] Does a run need its own screen, or is it just the ledger filtered by goal? - [ ] Veto scarcity in a long run: 2 vetoes across 8 hands may be too tight. Consider +1 veto every 3 hands. - [ ] Seeded draw: publish the seed post-pull so anyone can verify it wasn't rigged? - [ ] Screenshot intake and privacy: the image contains other people's messages. State the discard policy in-app. ## Jev findings (live API, 19 Sep) Tested against `POST https://api.typesafe.ai/v1/systemone` with `jev-latest`. Six hand states through the full question set, then targeted probes. All of this shapes the build. | Finding | Evidence | What it means for the build | | --- | --- | --- | | Latency is fine for a drag loop, not for 100 ms | 6 questions in one call: 320–420 ms. Single question: \~200 ms. | Debounce chip drags at 200 ms; expect the odds bar to move \~400 ms after the finger. Show it easing, not snapping. | | Chips dominate the odds by default | Tom 9 Thai / Sam 10 pizza → 45 / 43. Sam 8 concentrated vs Tom spread 3/3/2/2 → pizza 93 %. | Stacking is intensity, as specced. Spread chips read as weak. | | The fairness dial is the instruction text alone | Same chips (Sam 10 pizza, Tom 6 Thai / 4 ramen, Sam won last 4): no fairness clause → pizza 99 %. "Be fair across the ledger" → thai 54 %, pizza 18 %. "Lean toward the others" → thai 75 %, pizza 5 %. | `PICK_INSTRUCTIONS` is a config var, hot-reloadable. This is the whole "semi-decision-makery" tuning surface; no code changes needed to playtest it. | | "More options" works as a votable card | Both members put most chips on it → 94 %. | Regen trigger is reliable. | | Classification questions want derived features, not raw chips | Format question over raw chip state picked `craps` for everything, never `one_tap` or `reels`. With `chip_agreement`, `live_option_count` and `spice` pre-computed into the state and named in the criteria → one\_tap 97 %, swipe 99 %, craps 100 %, reels 72 %. | The DO computes features before every Jev call. Criteria reference state fields by name. | | Spice reads low from chips alone | Contentious 0.76 but spice 0.27 on the same round. | Spice needs signals beyond chip position: chip moves per minute, vetoes, re-placements. Or drive the ramp from contentious + format instead. | | Sandbagging noul is noisy | 0.6 for a member who put 7 chips on More options. | Nudge only, never a callout. Threshold ≥ 0.7. | | Large choice banks work | 40 dealer tags: 301 ms, correct pick. Duplicate tags split probability. | Bank can be big; tags must be unique and descriptive. | | Cost is nothing | \~750 in / \~215 out tokens per full call. | Hundreds of calls per hand is well under a cent. | | Straight sampling punishes unanimity | Local run: both members 10 chips on Thai → Jev 78 % Thai (history in state pulled it down from 99 %), sample landed Ramen at 6 %. | Design change: when format is `one_tap` the lever does not sample; it confirms. Sampling is for craps and reels. Sharpening (temperature < 1) still open for craps. | ## Dev setup What exists after 19 Sep: a running loop prototype, driven end to end locally. this repo. Stack as built: - Hono on Cloudflare Workers, generated from `@better-auth-cloudflare/cli` (Hono template, D1, no KV/R2). - Better Auth 1.7 with the `phoneNumber` plugin (6-digit OTP, 5 min expiry, sign-up on verify) and `anonymous` as the zero-friction path. OTP prints to the wrangler console in dev; `OTP_MODE=twilio` sends SMS. - One Durable Object per hand (`src/hand.ts`): state, WebSocket fan-out via the hibernation API, chips/veto/lock, Jev odds recomputed on every state change with a 200 ms debounce, the lever as a seeded weighted sample with the runner-up placed last in the reel order, rounds (again / more / accept with majority). - Typed Jev client (`src/jev.ts`) with 429/529 backoff. Dealer bank (`src/dealer.ts`), 22 lines, Jev picks by tag. - Mobile web client in one HTML file: felt table, chip stacks, live odds bars that wobble under 50 % confidence, lever, CRT reels reveal with the near-miss hang and an UPSET banner under 30 %, posted → accept / go again / more options. - `scripts/drive.sh` runs a full two-member hand against a dev server for smoke testing. Dev loop: 1. `npm install && cp .dev.vars.example .dev.vars`, paste keys. 2. `npm run db:migrate:dev && npm run dev` — local D1 + DO in miniflare, hot reload, OTP codes in the console. 3. Two phones on the LAN: `wrangler dev --ip 0.0.0.0`, open `/`, jump in, create a hand, share `/h/`. 4. Tune the pick instruction in `wrangler.toml` `[vars].PICK_INSTRUCTIONS`; it hot-reloads. 5. Preview deploys: `npx wrangler versions upload` gives a URL without touching prod. First real deploy: `wrangler d1 create pm2000-db`, paste the id, `wrangler secret put TYPESAFE_API_KEY`, `wrangler secret put BETTER_AUTH_SECRET`, `npm run deploy`. Still to support the dev loop: - Options agent: one Worker route with a vision model for screenshot intake and "More options" regen. The DO already accepts new labels via the `more` op. - Expiry: DO alarms for round timer and hand expiry (currently manual pull only). - A Jev eval harness: `jev_test.py` / `jev_probe2.py` are the seed. Turn them into a fixture set of hand states with expected odds ranges so instruction changes can be regression-tested. - Sound and haptics beyond the one vibrate pattern. - Move `TYPESAFE_API_KEY` to `wrangler secret` before the first deploy and roll it. Temp Jev key lives in `.dev.vars` (gitignored) and in the Claude Doc, not in this repo. Roll it before first deploy. ## Next steps Order matters: the whole product rests on the drag → odds → pull loop feeling good, and that loop rests on Jev. Prove those two before touching auth, tables or ledger. 1. ~~Get Jev.~~ Done: key in hand, API probed, client written. 2. ~~Loop prototype.~~ Built and driven locally. Next: put it on two phones and answer the only question — does dragging a chip and watching the odds move feel like anything? 3. Write the choice question and 30 dealer lines. 22 lines exist; the pick instruction is a config var. Iterate both on the prototype. 4. Reveal bake-off. Reels with near-miss exists. Add dice tumble and card flip variants in the same client; pick by what gets screenshotted. 5. Format bank. The format question works with derived features; the client currently renders one layout. Add swipe and one-tap views; one-tap confirms without sampling. 6. Options agent. Screenshot intake, then More options regen. One LLM route. 7. Tables, ledger, rubber-banding, expiry alarms, runs. 8. Seed two tables (couple + friend group of 4–6), run four weeks, watch the join graph and reveal screenshots. Kill criteria as in Validation. 9. Name and domain, before step 8 goes out, because the link is the brand. Stack, given the shape: Cloudflare Workers + Durable Objects (one DO per hand holds the state and fans out odds over WebSocket), D1 or KV for tables and ledger, Pages for the mobile web client. The Jev call sits inside the DO so every state change is one round-trip.