Chapter 1 ยท the game

Game Rules โ€” the daily model

Since 2 Sep 2026 the product spec is Ben's master Source of Truth (36pp, v2026-09-02) and the rules live in Engine V4 โ€” packages/game-core (spec, 110 tests) ported to the app as apps/sot-engine.js, driven by apps/sot/engine.js. One line: a group plays on agreed battle days; everyone races to their adjusted target โ€” first eligible player there takes the Daily Win; everyone who finishes after still banks the day; Daily Wins stack into a weekly season; stakes settle when the season ends. Numbers below are read straight from the code.

The 300-rep match era this supersedes is preserved at the bottom of this page and on The four versions.

The daily battle โ€” 200 adjusted reps

live (v4)

On an active day every eligible player gets a personally adjusted target โ€” 200 by default โ€” and races. The first eligible player to reach their target earns the Daily Win. The battle does not end there: it rolls on until the day's clock closes, and everyone who reaches their own target afterwards still banks the day.

V4 battle home โ€” hero numeral, progress ring, leaderboard
v4-battle-home.png ยท Battle Home: hero numeral, adjusted target, live board

How it works. Each logged set is converted to effort-adjusted reps before it hits the engine:

entry value = physical reps ร— exercise conversion ร— tierMultiplier ร— (lightning? 3)
progress = ฮฃ entries + credits (rescue rope) + bonuses (steal ยท bomb defusal)
Daily Win = first player with progress โ‰ฅ their adjusted target

Bank-day continuation is the core SOT correction over the old model: a finished battle is not a dead battle. Later completers preserve their streak, earn participation credit, feed team totals and keep the feed alive โ€” the SOT's words: "the battle does not become meaningless after somebody wins."

Worked example (from the v4 e2e): target 200. Marco (casual ร—1.25) logs 48 push-ups โ†’ 60 reps. I steal 10% of Priya's completed 100 โ†’ +10 to me, Priya keeps 100 (pure gain). My plank hold of 11s โ†’ 1 rep. Then 195 push-ups โ†’ 206 โ‰ฅ 200: Daily Win, battle stays open, Priya finishes her 200 and banks the day (streak 1), Jack misses at the deadline โ€” failed day, streak resets.

Failed days: at the clock's close, any eligible player under target fails the day โ€” streak resets to zero, failed-day counter increments, the recap says so plainly. An armed Group Shield converts a failed day into a streak-preserved one (consumed at the close it saves).

engine: apps/sot-engine.js createDay / logSet / targetProgressOf / effectiveTargetOf / closeDay ยท game-core ENGINE V4
proven by: game-core 110 tests ยท apps/sot e2e 120 checks โ€” win/bank/fail all asserted in state

Handicap tiers โ€” what a rep is worth

live

Fitness tiers multiply every rep toward your target: the couch player's push-up counts more than the athlete's. The objective is similar competitive effort, not equal physical output. The exact formula beyond the tier multiplier is an open SOT question (Q217โ€“221).

Tier picker with four cards
app-onboard-tier.png ยท the tier pick โ€” 4 options, set per player
TierMultiplier100 physical push-ups scoreTarget 200 needs
Couchร—1.5150134 push-ups
Casualร—1.25125160 push-ups
Fitร—1.0100200 push-ups
Athleteร—0.8585236 push-ups

Exercises convert too (a working proposal โ€” the official table is an open SOT gap): burpee ร—2.0, push-up/squat/lunge/dip ร—1.0, mountain climbers/jump rope ร—0.5, and timed holds at 10 seconds = 1 rep (plank, wall sit).

The measured-heart-rate blend from the earlier engine (70% %HRR / 30% declared tier) remains in game-core, waiting on straps โ€” see Verification.

engine: apps/sot-engine.js TIER_MULTIPLIERS / tierMultiplier ยท conversion table in apps/sot/engine.js EXERCISES
proven by: tier cases in the game-core suite ยท the v4 e2e asserts handicap-adjusted gains (48 โ†’ 60)

Streaks, rest days & the shield save

live (v4)

Streaks count consecutive banked battle days. Rest days never break them. A failed day resets the streak โ€” unless the Group Shield is armed.

Rest day home state in the v4 app
v4-states-rest-day.png ยท rest-day home: streak safe, next battle named
  • Banked day โ€” reached your target after the winner: streak +1, participation credit, history dot โœ“.
  • Failed day โ€” under target at the close: streak โ†’ 0, โœ— in the history, comeback framing never shame (founder directive).
  • Rest day โ€” no battle scheduled today: the home screen says the streak is safe and names the next battle day (SOT #29/#101).
  • Shield save โ€” an armed Group Shield converts every failure at that day's close into streakPreserved, then the shield is consumed.

Streak truth lives in the engine's season record (recordBattleDay โ†’ battleStandings) โ€” the live UI's optimistic counts reconcile there at every close, so a mid-day crash can't corrupt a streak.

engine: apps/sot-engine.js closeDay outcomes / recordBattleDay / battleStandings
proven by: v4 e2e โ€” Priya banks day 2 (streak 2), Jack/Marco fail day 1 (streak 0), shield-save unit tests in game-core

Weekly seasons ยท 1:1 points ยท stakes at season level

live (v4)

Seasons default to weekly (one pass of the group's battle days; monthly optional, Major seasons future). 1 Daily Win = 1 season point. Nothing financial or material settles on an individual day โ€” the season ends, then the agreed stake resolves.

Season hub with weekly standings and calendar
v4-season-hub.png ยท Season Hub โ€” standings, calendar, stake
Charity donation confirmed with receipt
v4-stake-donated.png ยท charity resolved โ€” receipt + disclosed fee
StakeResolves asIn the build
๐Ÿฝ DinnerLoser buys the agreed meal (spend cap)agreement-gated at join, obligation + mark-fulfilled at season end
๐Ÿ˜ˆ DareForfeit locked before the seasondeclaration frozen at creation โ€” can't be edited mid-season
๐Ÿงน DeliverablePractical favour owed by the losersame obligation lifecycle
โค๏ธ Charity potEveryone contributes; winner directs the pot; disclosed platform feefull lifecycle: contribute โ†’ pot maths โ†’ winner chooses charity โ†’ donation + receipt

Charity mechanics exactly: each player's contribution agrees up-front (points are the trial currency โ€” the demo displays 1 point as $1); at season end the pot totals, the platform fee (5% default, always disclosed) is deducted on screen, the winner picks from the eligible list, and the donation lands with a receipt code. The winner never receives cash โ€” stakes without gambling. Cash wagering is explicitly deferred by the SOT (licensing/KYC/geo โ€” not V1).

engine: apps/sot-engine.js createBattleSeason / recordBattleDay / endBattleSeason / proposeStake / agreeToStake / contributeToCharityStake / resolveSeasonStake / designateCharity / processCharityDonation
proven by: season + stake suites in game-core ยท the v4 e2e walks a full 2-battle week to charity resolution ($38.00 of $40.00 after the $2.00 fee)

Power-ups โ€” the SOT canon (corrected steal & shield)

live (v4)

Four launch cards per the SOT โ€” Lightning, Rep Steal, Group Shield, Time Freeze โ€” plus the post-launch set. Two semantics were corrected from our earlier build: Rep Steal is a pure gain (the target keeps every rep), and Group Shield protects streaks (it does not block steals).

Power-up stack as hidden loot cards
v4-powerups.png ยท the stack โ€” hidden loot cards, tap to flip
CardEffect (exact, from the engine)Guard rails
โšก Lightning RoundYour reps count ร—3 for 10 minutesonce per player per day; window expires, the once-flag never resets
๐Ÿฅท Rep StealYou gain 10% of a rival's completed score โ€” they keep every reponce per day; counts toward your target only under the day's stealCanTriggerWin flag (our answer to SOT Q237 โ€” the demo sets it)
๐Ÿ›ก Group ShieldProtects the group's streaks from one day's failuresarmed any time, consumed at the close it saves; Shield Bash (competitive mode) shatters it
โ„๏ธ Time FreezeBattle clock +30 minutes, whole groupstack limit 1 per day
๐Ÿ’ฃ Surprise Bomb post-launch+20 reps lands on a rival โ€” deliver inside 10 minutes or it fizzlesa HIT banks the victim a +20 defusal bonus; a MISS sticks nothing (target never grows)
๐Ÿชข Rescue Rope post-launch50-rep credit to a mate with zero reps todayonce per day, inactive players only, counts toward target
๐Ÿ”ฅ Combo Boost ยท ๐ŸŽฒ Double Down ยท ๐Ÿค Assist Boost ยท ๐Ÿ”จ Shield Bashprescribed-sequence bonus ยท personal 2ร— target quest ยท +25 both-ways on a teammate's finish ยท shatter an armed shieldin the engine, off in the default group config

The economy question, answered in code: SOT Q240โ€“242 are open โ€” how are cards acquired, how is power kept fair, do they expire? Our live answer: a season-1 founder pack, then a daily drop each battle day, a hold cap of 4 with leftovers carrying across battles (no expiry โ€” cards are kept, not burned by time), and the draft-from-3 + catch-up curve from the v2 engine remains the proposal for competitive fairness. The v4 app plays all of it; this wiki keeps the receipts.

engine: apps/sot-engine.js activatePowerUp / grantPowerUp / inventoryOf / resolveExpiredBombs + constants LIGHTNING_MS ยท STEAL_SHARE=0.1 ยท FREEZE_MS ยท SURPRISE_BOMB_RUF/BONUS/WINDOW ยท RESCUE_ROPE_RUF=50 ยท ASSIST_BONUS_RUF=25
proven by: power-up suite in game-core ยท v4 e2e asserts steal pure-gain in state (gain 10, target kept 100), bomb HIT + MISS, lightning ร—3 on a real log

The battle clock & the danger-zone ramp

live (v4)

Two clock modes: a window (battle opens at 06:00, closes 22:00, group timezone) or a sprint (N minutes from open โ€” demo compression). The final stretch escalates: final 3 hours warn, the final hour banners, the final 30 โ†’ 10 minutes are the Danger Zone.

Danger zone level 1 chip
game-dz1.png ยท final 3h โ€” urgency label
Danger zone level 2 banner
game-dz2.png ยท final hour banner
Danger zone level 3 red wash
game-dz3.png ยท DZ โ€” red wash, DO OR DIE

How it works. A 1-second ticker recomputes the remaining time and the urgency level (Battle live โ†’ Final 3 hours โ†’ Final hour โ†’ Final 30 โ†’ DO OR DIE); at the level-3 threshold the whole screen washes red and the copy pushes the log. A Time Freeze overlays everything with ice while it runs. At the deadline the day auto-closes exactly once: logging locks, completions/failures/shield-saves are stamped, the day folds into the season record, and the next scheduled battle opens.

Close-call detection (SOT #98) fires when the lead gap is under 5% of target with the leader past 70% โ€” the "anything can happen" banner.

engine: apps/sot/engine.js urgency() / snapshot() clock ยท tick() ยท deadline authority in sot-engine.js effectiveDeadline
proven by: v4 e2e asserts the DZ state and the deadline auto-close

The winners family & what the day says

live (v4)

Every way a day can land has its own moment: You Won The Day (first to target โ€” the dopamine hit, confetti, canvas share card), Other Player Won (bank-your-day push), Target Completed / Day Banked (streak intact), Failed Day (comeback framing, shield note if saved), and the Battle recap (order of finish, key moments).

You won the day celebration in v4
v4-you-won.png ยท YOU WON THE DAY โ€” confetti + share card
Winner-known transition card with tomorrow framing
v4-states-win-known.png ยท winner known, battle rolls on, tomorrow framed
Battle complete but season live card
v4-states-battle-complete.png ยท battle complete โ€” season live

How it works. Win detection happens inside logSet โ€” the engine decides, the app renders. The win moment generates a 780ร—936 canvas share card (PNG download / clipboard copy). Tone awareness (cheeky / neutral / corporate-safe) swaps every line of winner and failure copy โ€” the same state speaks three dialects. See App Screens โ†’ v4 for the full screen-by-screen walk including the critical states (offline, sync conflict, rest day).

engine: apps/sot/engine.js recordCompletion + app.js overlay family (#171โ€“183)
proven by: v4 e2e โ€” both Daily Wins, the bank, the fail, the recap, zero console errors

The "reps" ruling (RUF internal)

live

SOT Q216: the internal unit is RUF; the UI says reps, always. Handicap math happens under the hood; the player only ever sees "reps".

How it's enforced. The v4 e2e carries two gates: a page-text regex and a source-string scan that fail the run on banned vocabulary (match / kitty / poker / RUF / 300 in UI copy) and require Daily Win / banked / reps to be present. Engine identifiers (the ruf fields) are code, not copy โ€” they never render.

lives: apps/sot/e2e.mjs language gates ยท the founder's standing directive (reps language, never poker)
proven by: every e2e run since the v2 wave

Legacy โ€” the 300 format (superseded 2 Sep 2026)

preserved

The model that built v1โ€“v3: any reps, any order, any mix; first to the raw target closed the match; winner = highest effort-adjusted score at closure (+15 closure bonus); 4-week seasons with 3/2/1+MVP points; comeback ร—1.2 for players >30% behind; Shield blocked steals; steal transferred reps away.

What the SOT kept: effort-adjusted scoring, tier multipliers (unchanged numbers), power-up cards, the charity pot, catch-up as a design principle. What it replaced: first-to-close-ends-match โ†’ first-to-target Daily Win with bank-day continuation; 300 โ†’ 200 adjusted; 4-week/3-2-1 seasons โ†’ weekly 1:1; stakes per match โ†’ stakes per season; steal/shield semantics corrected. The old rules still run the preserved v1 app (/figma-app) and the 300-era bots โ€” the living history is on The four versions. The full legacy page is archived at apps/wiki/archive/game_300era_20260904.html.

engine (frozen): apps/figma-app/engine.js ยท packages/game-core v3-era suites
why kept: design progression is a deliverable โ€” old versions stay runnable, forever
โ† backIndex