Bankroll40 docs

Acceptance contract, phase 1

Version: 2 · 2026-10-02 · the criteria the phase 1 build is judged against; each names its evidence

Phase 1 is done when every row below is verified by the evidence in its last column, on the live product, not on a branch. A row that cannot be verified as written is a defect in this contract, fixed here before it is fixed in code.

Money and currencies

# Criterion Evidence
M1 Every money value is stored as an integer in minor units with an ISO 4217 code on the same row; no float money anywhere in the schema packages/schema test "every money column is an integer in minor units"
M2 A pot has exactly one currency; a session or tournament may be in a different currency and stores the rate to the pot and to the home currency at its date Schema columns fx_to_home; API test logging a HKD session against a USD pot
M3 Changing the home currency re-renders totals without rewriting any row API test: change home currency, assert no session row changed, totals differ
M4 A transfer between pots writes two linked rows with the FX applied stored Schema counter_pot_id, fx_applied; API test for a USD to HKD transfer
M5 Daily ECB rates for USD, HKD, GBP, PHP are present in fx_rate; a missing pair falls back to a remembered manual rate Cron run log; API test with an unsupported currency

The discipline engine

# Criterion Evidence
D1 Default rule set per pot: floor 30, standard 40, move up at 50 of the next stake plus 100 hours, stop-loss 3, cool-off 12 h, session cap 8 h, re-entry cap 2, tournament budget 5 percent packages/rules DEFAULT_RULES; ADR-0003
D2 Starting a session offers only the stakes the pot supports; a stake above the floor is shown greyed with the shortfall App screen test; stakeVerdict tests
D3 When the pot falls under the floor the default stake moves down and Home shows the pot size that brings it back stakeVerdict "drop" test; screenshot of Home in that state
D4 At the stop-loss the add-buy-in control is locked; continuing requires a typed reason, which is stored as a rule_break stopLossState tests; API test that a buy-in past the limit without a reason is refused
D5 After a stop-loss or an override the next session cannot start for the cool-off period; Home shows the countdown coolOffUntil test; API test on session_start inside the window
D6 The third re-entry into one event asks for a reason; every bullet past the cap is recorded as a break reentryGate tests; API test on tournament_entry
D7 Monthly tournament spend is checked against the cap the moment a bullet is fired tournamentBudget tests
D8 Rule thresholds can be edited only when no session is live; an edit below the standard is logged as a rule change API test editing rules with a live session (refused) and without (logged)
D9 The weekly report lists breaks and overrides beside the results of those sessions Report fixture test with two clean and two broken sessions
D10 Home shows the promotion bar: roll and hours against the next stake, with the amount and hours still needed promotionProgress tests; screenshot of Home
D11 The Tournaments screen shows the max buy-in the roll supports today and which limit set it maxTournamentBuyIn tests; screenshot
D12 Sign-up asks the eight questions and the suggested setup matches suggestSetup for the same answers, including a roll held in another currency suggestSetup tests including the 20,000 USD at 2/5 case and a roll held in another currency; a sign-up walk-through recorded with screenshots
D13 Home shows the current stake with a red down arrow when the roll is under the floor at it and a green up arrow when the next stake is open; one tap moves the stake or keeps it, and staying under a red arrow writes a rule_break stakeVerdict tests; a UI test tapping each button; the note in the record after staying

Sessions, tournaments, online

# Criterion Evidence
S1 A live session's timer, buy-ins and rule checks work with no network; writes replay in order when the network returns Device test in airplane mode; outbox replay test
S2 An online session stores site, hands and tables instead of a venue, and reports bb/100 Schema columns; stats test
S3 A tournament is an event with entries per bullet (flight, kind, cost, outcome, payout) and days played; it stays open until closed Schema; API test reproducing the Metro Poker Manila case: 15 bullets, one bag paid, one bag to Day 2, closed on Day 2
S4 ROI is computed per event over every bullet; open events are excluded from closed ROI and shown as in play Stats test on the same fixture

Media, profile, journal

# Criterion Evidence
P1 Receipts, photos, 60 s clips and 5 min voice memos attach to buy-ins, cash-outs, tournament days, transactions or the profile, and are uploaded straight to R2 with a signed URL API test: signed upload URL issued, object present, media row written
P2 Photos are resized and stripped of EXIF on the phone before upload; no location is stored Device test asserting no GPS tags on the uploaded object
P3 Deleting a media row deletes the R2 object API test
P4 The journal lists sessions, media, breaks and milestones in time order Journal test on a mixed fixture
P5 Everything is private by default; sharing is per item API test: an unauthenticated read of any media URL fails after expiry; a shared item is readable

Access

# Criterion Evidence
A1 Sign in with Apple, Google and passkeys works on web and in the native builds Manual check on each, recorded with screenshots
A2 Every write records an actor: app, personal token or OAuth client id Schema audit columns; API test
A3 CSV export returns every row the user owns Export test comparing row counts

Deployment and verification

# Criterion Evidence
V1 The Worker API and the web build deploy from main through the pipeline only; no manual deploys after bootstrap Pipeline history
V2 The owner logs a real session at a Manila venue from the phone with the tunnel off, and it syncs on leaving The owner's own session in the journal
V3 The phase ends with a signed tag on app and an outcome note on the tracking issue Tag and note present