IF Dev mode ON → every order becomes a [DEV] log row. Nothing sent. ELSE IF Trading OFF → every order becomes a [SIM] log row. ELSE → real Kalshi limit order at the leg's cap.
The "game lines" toggle only stops the run-crosser ladders and RFI YES auto-fire. Sliders, batter props, K, SB, pitcher change, and moneyline all fire with it off. Order CANCEL buttons bypass every switch — a cancel is always sent live.
Every button gets ONE pool of BET CAP contracts per activation; every leg it fires drains
that same pool in the button's listed order. Pool empty → later legs skip "budget drained".
EXCEPTION: moneyline sizes from its own ML STAKE setting and ignores the pool.
Caps (the limit price IS the cap; ask above cap → leg skipped, never chased):
PRICE CAP (profile setting) → EVERYTHING except moneyline: props, K, SB, pitcher chg, RFI,
all settlement sliders, all crossers, all hidden binaries.
NO hardcoded caps anywhere (ruling 2026-07-17 — the old hard-99¢
limits on Top-9 / Bot-9 / F3/F7 / team totals were REMOVED).
ML cap / model buy_limit → moneyline legs only (shadow / v8-live)
↻ SYNC (green ✓ = bases already match the feed) single tap → bases copied from the MLB feed, immediately
// 2026-09-12: the hidden double-tap-to-clear on this button is GONE (undiscoverable, delayed every
sync 300ms, and a slow double-tap wiped the bases) — double-tapping it now just syncs twice
✕ CLEAR (directly BELOW sync) only shown while someone is on base · SINGLE tap clears the bases
(toast + buzz confirm; logged "Cleared bases") · the button disappears with the runners
// 2026-09-12 pm: was hidden under the +RUN swipe on day one (Steve) — moved below SYNC;
arm-then-confirm double tap dropped for a single tap (Shawn)
operator gestures (WALK · HIT · runner modal · +RUN · ✕ CLEAR · inning double-tap) → their own entries MLB feed 3rd out (raw outs 2→3; skipped while the operator is a half AHEAD) → "Bases cleared by MLB feed" reopening a game on this device restores its saved diamond (≤ 8h) → "Restored N runners"
Tap confirmation: WALK, base presses and runner moves now toast the runner + base (and vibrate on Android) the instant the tap lands — no toast = the tap did not register, press again. Durable log: activity entries queue on the phone and retry until the server acknowledges them (server dedups), so a tap logged during a bad-signal moment still reaches the admin log late instead of never.
IF displayed batter has ≥ 1 live batter-prop market → visible
BUY YES Hits — ONLY the rung this hit newly decided (hits + 1) @ price cap BUY YES Total Bases — ONLY the newly-decided rung (TB + 1) @ price cap // buys the rungs floor < line ≤ target only (floor = pre-play tally); a // batter's 2nd hit buys 2+ ALONE, never re-buys the already-won 1+. // Multi-rung spans fire HIGHEST line first, down to lowest (RBI 3+ → 2+ → 1+).
IF moneyline armed → moneyline pair (one-shot, see Auto-riders) team totals fire (SCORING team — right after moneyline, first claim ahead of props/RFI/game totals) @ price cap IF inning == 1 AND RFI unfired → BUY YES every Run-in-1st market @ price cap BUY YES Hits+Runs+RBIs — ONE line: (H+R+RBI) + N @ price cap BUY YES RBIs — ONE line: (RBI) + N @ price cap game-total crossers fire (F5 + Total Runs + Inning Total — see Run crossers; team totals already fired above) hidden binaries fire (legs this run decided — see Auto-riders)
BUY YES Total Bases single lines up to (TB + 2) @ price cap
Done / backdrop buys nothing — it finalizes the diamond, advances the lineup, re-arms the ML pre-sample.
+ 3-WAY (2026-10-19, KXMLBINNINGWIN, innings 1–8, BOTTOM half only): rides each runner tapped home in the hit-flow modal one at a time off the inning A–H counter — H reaching A → BUY AWAY NO; H passing A → BUY HOME YES · TIE NO (+ AWAY NO if unowned). Top-half runs buy nothing. Every leg @ price cap, bonded, dedup via hbBoughtTickers. See the "Per-inning three-way" card.
BOND (2026-09-12, admin-locked bond cap > 0 only): same as +RUN: bond YES the highest just-crossed full-game Total Runs line (one bond per swipe).
same gate as HIT — batter has ≥ 1 live prop market
1 IF moneyline armed → moneyline pair, runs = scored + 1 (one-shot) 2 game-total crossers fire (F5 Total + Total Runs + Inning Total; scored + 1 runs, HIGHEST line first) @ price cap 3 IF inning == 1 AND RFI unfired → BUY YES every Run-in-1st market @ price cap 4 team totals fire (SCORING team, HIGHEST rung first) @ price cap 5 BUY YES Home Runs — newly-decided rungs (HR floor < line ≤ HR+1) @ price cap 6 BUY YES Hits — newly-decided rungs (hits floor < line ≤ hits+1) @ price cap 7 hidden binaries fire (legs these runs decided — see Auto-riders) 8 BUY YES Hits+Runs+RBIs — newly-decided rungs (HRR floor < line ≤ HRR+3+scored) @ price cap 9 BUY YES Total Bases — newly-decided rungs (TB floor < line ≤ TB+4) @ price cap 10 BUY YES RBIs — newly-decided rungs (RBI floor < line ≤ RBI+1+scored) @ price cap // Order set 2026-07-20 (operator ruling) and UNIQUE to the HR swipe. The batter-prop // ladder is deliberately split: Home Runs + Hits (5,6) fire BEFORE the hidden binaries (7), // then HRR + Total Bases + RBIs (8,9,10) after. 1B/2B/3B · WALK · +RUN keep the default order // (team totals right after the moneyline, then props, then RFI/game totals/hidden binaries). // "newly-decided" = floor < line ≤ target (pre-play tally): a multi-run play buys the whole // crossed span at once (HIGHEST line first), but a rung an earlier play won is never re-bought.
+ 3-WAY (2026-10-19, KXMLBINNINGWIN, innings 1–8, BOTTOM half only): rides each run of the homer one at a time (a 3-run shot from A 2 takes AWAY NO on run 2, HOME YES + TIE NO on run 3) off the inning A–H counter — H reaching A → BUY AWAY NO; H passing A → BUY HOME YES · TIE NO (+ AWAY NO if unowned). Top-half runs buy nothing. Every leg @ price cap, bonded, dedup via hbBoughtTickers. See the "Per-inning three-way" card.
IF bases loaded (1B+2B+3B occupied) → gold WALK · RUN IN swipe (drag ←, walk button's slot)
// a loaded-bases walk always forces a run → fires the FULL run basket below,
// so the tap is upgraded to a slider — an accidental tap can't buy anything
ELSE → plain WALK tap button (unchanged)
forced = runners pushed home by the walk chain
IF forced == 0 → NO BUYS — diamond + lineup only
ELSE:
IF moneyline armed → moneyline pair, runs = forced (one-shot)
team totals fire (SCORING team — right after moneyline, first claim ahead of props/RFI/game totals) @ price cap
IF inning == 1 AND RFI unfired → BUY YES every Run-in-1st market @ price cap
BUY YES Hits+Runs+RBIs — newly-decided rungs (HRR floor < line ≤ HRR+forced) @ price cap
// HRR is the ONLY batter prop a walk buys — no Hits, TB, or RBIs series
// floor = pre-play HRR: never re-buys a rung an earlier play already won;
// multi-rung spans fire HIGHEST line first, down to lowest
game-total crossers fire (forced runs, F5 + Total Runs + Inning Total; team totals already fired above)
hidden binaries fire (legs the forced runs decided — see Auto-riders)
+ 3-WAY (2026-10-19, KXMLBINNINGWIN, innings 1–8, BOTTOM half only): rides a forced-in run one at a time off the inning A–H counter — H reaching A → BUY AWAY NO; H passing A → BUY HOME YES · TIE NO (+ AWAY NO if unowned). Top-half runs buy nothing. Every leg @ price cap, bonded, dedup via hbBoughtTickers. See the "Per-inning three-way" card.
IF pitcher has a live Strikeouts market AND pitcher Ks ≥ (lowest live line − 1) AND (card NOT pinned to the remaining starter OR that starter is the live pitcher on the mound) → visible
BUY YES ONE Strikeouts market — wheel-selected line, else lowest live line @ price cap ALWAYS advance the lineup, even when the buy skips
Pitcher card pins to the remaining starter once the other is pulled. Normally the card rotates each half between the two starters. When the MLB feed shows a team's starter replaced by a reliever (live pitcher ≠ roster index-0 starter), that starter's Strikeouts contract is closed, so the card stops rotating and shows ONLY the other team's starter — the last open contract — in both halves. The K swipe is then withheld on the pinned starter's off-mound half (reliever pitching) so it can't fire a strikeout buy while that starter is sitting. And as of 2026-08-13: during the pinned starter's OWN team's at-bats (he's in the dugout, the opposing reliever on the mound has no contracts) the pitcher card AND its Strikeouts ladder hide entirely — both return the next half when he retakes the mound. Follows the DISPLAYED batting team — the batter TEAMS toggle is now the ONLY team switch (the pitcher-card TEAMS button was REMOVED 2026-08-13); flipping to the opposing lineup brings the pinned pitcher back up. Feed-derived (not the CHG swipe); resets per game (the pitcher-card TEAMS toggle was removed 2026-08-13 — the batter TEAMS button drives the whole view; FIXED 2026-08-15 after Steve's 8/14 report: with both starters in, the batter TEAMS flip now actually flips the pitcher card to the team that faces the displayed lineup — it used to stay stuck on the live mound pitcher, so CHG for the other side was unreachable).
double-tap an OCCUPIED base → runner modal
IF that runner has a live Stolen Bases market → STOLEN BASE tap opens the
catcher-throw question (2026-08-25): "Did the catcher throw to {base}?"
ELSE advance-only button (no buy, no question)
YES · THROW (slide ← to fire) — throw = official stolen base:
BUY YES runner's LOWEST live Stolen Bases line @ price cap
then advance the runner (3B → scores; stealing home does NOT fire run crossers)
NO THROW (single tap) — likely defensive indifference, no SB credited:
advance the runner only — NO BUY (logged `SB no throw`)
Cancel / backdrop → nothing (runner stays put)
TEAM-TOTAL RESOLVER FIX (2026-09-16): 09/15 ATH@TB — every A's team-total line skipped "team unresolved" on Tom's +RUN because Kalshi
labels the club "A's" and no alias carried it. Aliases now include "A's" / "A’s" and every NICKNAME-ONLY form ("Diamondbacks wins by…",
"Tigers …"; "Red Sox" / "White Sox" / "Blue Jays" whole), and the team-total sweep falls back to the TICKER's team code (…-ATH8) when a
label still doesn't match. The same matcher serves the F5 / Winner / spread / Top-9 / Bot-9 / walk-off resolvers.
BOND (2026-09-12, admin-locked bond cap > 0 only): run crossing full-game total line(s) → ONE bond per swipe: YES the HIGHEST just-crossed Total Runs line (a 3-run play 2→5 bonds Over 4.5 only). A tie-break run whose line was already bonded as the tie-total → no new bond. F5 + team totals never bonded. 1st-inning run → also bonds YES RFI (Over 0.5).
LEAD runner (highest occupied base: 3B → 2B → 1B) removed from the diamond // 2026-08-25:
the swipe asserts they crossed home — no second gesture; bases empty → nothing cleared
IF moneyline armed → moneyline pair, runs = 1 (one-shot)
team totals fire (SCORING team — right after moneyline, first claim ahead of RFI/game totals) @ price cap
IF inning == 1 AND RFI unfired → BUY YES every Run-in-1st market @ price cap
game-total crossers fire (+1 run, F5 + Total Runs + Inning Total; team totals already fired above) // no direct orders of its own
+ 3-WAY (2026-10-19, KXMLBINNINGWIN, innings 1–8, BOTTOM half only): rides each run one at a time off the inning A–H counter — H reaching A → BUY AWAY NO; H passing A → BUY HOME YES · TIE NO (+ AWAY NO if unowned). Top-half runs buy nothing. Every leg @ price cap, bonded, dedup via hbBoughtTickers. See the "Per-inning three-way" card.
IF the displayed pitcher is the STARTER (boxscore index 0) → visible
FOR EACH Strikeouts line > pitcher's current K count (lowest first):
BUY NO @ price cap
// bets the pulled starter never reaches those lines. Nothing bought for the reliever.
BOND (2026-09-12, admin-locked bond cap > 0 only): END-1ST swipe → after the NO 0.5 taker, REST bond_cap NO on the RFI market at the price cap (never the 1.5). Button: CANCEL BOND · Run in 1st Inning NO, in this slider's slot.
upd 2026-09-09 (Steve): the slider track now shows the LIVE per-inning
run count — the exact runsThisInning() value the integrated inning-total NO sweep
settles against at swipe time — tracks shortened 2026-09-09 to fit (e.g. "BOT 5 · 2 RUNS", "RFI NO · 0 RUNS", "END 5TH · 2 RUNS · NO ×3"). Counts the
whole inning, top+bottom. If the count on the pill looks wrong (timeout, poll gap, app opened
mid-inning — the tally baselines at inning change), do NOT swipe: the sweep would buy NO on
lines the missed runs already crossed.
IF inning == 1 AND half == Bottom AND score 0–0 AND neither RFI YES nor NO fired AND an RFI market exists
BUY NO on EVERY active Run-in-1st-Inning market @ price cap + INTEGRATED END-OF-INNING (2026-08-25): NO both inning-1 Inning Total lines (Over 0.5 + Over 1.5) — marks inning 1 swept, generic END swiper never re-offers
+ 3-WAY END (2026-10-19): the swipe asserts the 1st ended 0–0 → also buys inning-1 TIE YES · HOME NO · AWAY NO (owned legs skipped), bonded. Rail colour is now HOT PURPLE (full settlement basket attached).
IF inning == 5 AND half == Top AND home > away (strictly) AND unfired AND F5 home Winner exists
BUY YES HOME F5 Winner @ price cap BUY NO AWAY F5 Winner @ price cap BUY NO TIE F5 Winner @ price cap BUY NO AWAY −1.5 F5 Spread @ price cap BUY NO AWAY −2.5 F5 Spread @ price cap IF home leads by ≥2 → BUY YES HOME −1.5 F5 Spread @ price cap IF home leads by ≥3 → BUY YES HOME −2.5 F5 Spread @ price cap // the swipe asserts the top half is done — home only ADDS runs, so the // margin is the segment floor (operator ruling 2026-08-22). Speculative by // design — away still bats this half. Tickers shared with Bot-5 dedup.
+ 3-WAY TOP (2026-10-19): records A for inning 5; A = 0 → also buys inning-5 AWAY NO (bonded). HOT PURPLE.
BOND (2026-09-12, admin-locked bond cap > 0 only): BOT-5 sweep → REST NO on the LOWEST uncrossed F5 Total Over (one bond). Button takes this slider's slot.
upd 2026-09-09 (Steve): the slider track now shows the LIVE per-inning
run count — the exact runsThisInning() value the integrated inning-total NO sweep
settles against at swipe time — tracks shortened 2026-09-09 to fit (e.g. "BOT 5 · 2 RUNS", "RFI NO · 0 RUNS", "END 5TH · 2 RUNS · NO ×3"). Counts the
whole inning, top+bottom. If the count on the pill looks wrong (timeout, poll gap, app opened
mid-inning — the tally baselines at inning change), do NOT swipe: the sweep would buy NO on
lines the missed runs already crossed.
IF inning == 5 AND half == Bottom AND not fired AND an F5 market exists → visible // no outs gate, no stages — the swipe itself ASSERTS the first five are final (F3/F7 law)
IF home > away → BUY YES HOME Winner · NO AWAY · NO TIE
ELSE IF away > home → BUY YES AWAY Winner · NO HOME · NO TIE
ELSE (tied) → BUY YES TIE Winner · NO HOME · NO AWAY
FOR L in {1.5, 2.5}, each team: BUY YES if final margin > L, ELSE BUY NO (all 4 rungs resolve)
FOR EACH F5 Total Over: IF total < count → BUY NO (cleared Overs skipped, never Over YES)
+ INTEGRATED END-OF-INNING (2026-08-25): NO every inning-5 Inning Total line the
inning's runs didn't cross — marks inning 5 swept (generic END swiper never re-offers)
// per-ticker dedup vs Top-5 (shared f5BoughtTickers) — nothing bought twice
Every leg buys at the operator's set price cap — deliberately NOT the hard-99¢ limit the 9th/F3/F7 sweeps use (operator ruling 2026-07-16). The swipe is the operator's assertion that the segment ended: swiping mid-inning buys a full settlement basket on an unfinished segment. Feed outs are logged for forensics only.
+ 3-WAY END (2026-10-19): also buys the inning-5 three-way basket for the final A–H (A > H → AWAY YES · HOME NO · TIE NO; tied → TIE YES · HOME NO · AWAY NO; H > A → nothing new), bonded. HOT PURPLE.
BOND (2026-09-12, admin-locked bond cap > 0 only): 9TH slider → three bonds, in this capital-priority order: YES winner ML · NO loser ML · NO lowest uncrossed Total Runs Over. One button (CANCEL BONDS · 3 legs), in this slider's slot. Tie ML never bonded.
IF inning ≥ 9 AND half == Top AND home > away AND unfired AND Winner market exists // NO outs gate — "the game is over" is the operator's judgment
BUY YES HOME Winner · BUY NO AWAY Winner · BUY NO Tie (if listed) FOR EACH Total Runs Over: IF total < count → BUY NO (achieved Overs skipped) FOR EACH Team Total Over (both teams): IF team runs < count → BUY NO FOR EACH Spread: plus-lines skipped · home YES if margin covers ELSE NO · away always NO
Colour only (2026-10-19): HOT PURPLE like every full-settlement rail. The 9th inning is OUT of the three-way patch by ruling — no three-way legs here.
BOND (2026-09-12, admin-locked bond cap > 0 only): same three bonds and order as Top of 9th; one button in this slider's slot.
upd 2026-09-09 (Steve): the slider track now shows the LIVE per-inning
run count — the exact runsThisInning() value the integrated inning-total NO sweep
settles against at swipe time — tracks shortened 2026-09-09 to fit (e.g. "BOT 5 · 2 RUNS", "RFI NO · 0 RUNS", "END 5TH · 2 RUNS · NO ×3"). Counts the
whole inning, top+bottom. If the count on the pill looks wrong (timeout, poll gap, app opened
mid-inning — the tally baselines at inning change), do NOT swipe: the sweep would buy NO on
lines the missed runs already crossed.
IF inning ≥ 9 AND half == Bottom AND away > home AND unfired AND Winner market exists // hides itself if home ties or walks it off; no outs gate
BUY YES AWAY Winner · BUY NO HOME Winner · BUY NO Tie (if listed) Totals + Team Totals identical to Top-9 · Spreads: away covers → YES ELSE NO · home always NO + INTEGRATED END-OF-INNING (2026-08-25): NO the leftover inning-9 Inning Total lines
Colour only (2026-10-19): HOT PURPLE. No three-way legs in the 9th by ruling.
upd 2026-09-09 (Steve): the slider track now shows the LIVE per-inning
run count — the exact runsThisInning() value the integrated inning-total NO sweep
settles against at swipe time — tracks shortened 2026-09-09 to fit (e.g. "BOT 5 · 2 RUNS", "RFI NO · 0 RUNS", "END 5TH · 2 RUNS · NO ×3"). Counts the
whole inning, top+bottom. If the count on the pill looks wrong (timeout, poll gap, app opened
mid-inning — the tally baselines at inning change), do NOT swipe: the sweep would buy NO on
lines the missed runs already crossed.
TOP: IF inning == N AND half == Top AND home > away AND unfired BOTTOM: IF inning == N AND half == Bottom AND home ≤ away AND unfired (tie counts)
TOP: BUY YES HOME · NO AWAY · NO TIE BOTTOM, away leading: BUY YES AWAY · NO HOME · NO TIE BOTTOM, tied: BUY YES TIE · NO HOME · NO AWAY B3/B7 only, + INTEGRATED END-OF-INNING (2026-08-25): NO the leftover inning-3 / inning-7 Inning Total lines (tops never — the inning isn't over at the top) // the swipe asserts the segment's FINAL OUT was just recorded — that is the operator's call
+ 3-WAY (2026-10-19): T3/T7 also record A and buy inning-3/7 AWAY NO when A = 0; B3/B7 also buy the inning-3/7 three-way basket for the final A–H (owned legs skipped). All bonded. All four rails are now HOT PURPLE (full basket attached) — blue/pink/cyan/crimson retired.
IF inning == N AND half == Top AND home == away (TIED) AND away F(N) Winner market exists AND unfired
BUY NO F(N) AWAY Winner // tied game, away bats last in its half of the segment inning: if it doesn't score in // the top, it can no longer WIN the segment (home bats last → can only tie or take the lead). // Home YES + Tie stay LIVE (home still bats). NEVER a home/tie leg, never a ladder.
Mutually exclusive with the home-led T3/T7 and the Top-of-5th lock-in (those gate on a lead; this on a tie), so they never co-occur. The swipe asserts the top-half final out was recorded still tied. One-shot per segment; ticker dedup via the shared hbBoughtTickers set.
+ 3-WAY TOP (2026-10-19): also records A for inning N and buys inning-N AWAY NO when A = 0 (bonded). Stays GREY (slate) — this rail commits one leg, not a basket.
upd 2026-09-09 (Steve): the slider track now shows the LIVE per-inning
run count — the exact runsThisInning() value the integrated inning-total NO sweep
settles against at swipe time — tracks shortened 2026-09-09 to fit (e.g. "BOT 5 · 2 RUNS", "RFI NO · 0 RUNS", "END 5TH · 2 RUNS · NO ×3"). Counts the
whole inning, top+bottom. If the count on the pill looks wrong (timeout, poll gap, app opened
mid-inning — the tally baselines at inning change), do NOT swipe: the sweep would buy NO on
lines the missed runs already crossed.
IF half == Bottom AND inning N ≤ 9 AND live leftover Inning Total lines exist
AND inning N not already swept
AND NO settlement slider for this inning is on screen:
N=1 RFI NO · N=3 B3 · N=5 Bottom-5 sweep · N=7 B7 · N=9 Bot-9
// when one of those IS visible it carries these legs itself and this swiper hides —
// every bottom half shows exactly ONE control for the inning lines.
// Bottoms of 2/4/6/8 (no settlement slider exists) always use this swiper.
FOR EACH inning-N Inning Total line: IF runs-this-inning < count → BUY NO @ price cap 0 runs → NO Over 0.5 + NO Over 1.5 · 1 run → NO Over 1.5 only · 2+ runs → nothing left (hidden) // crossed lines are never touched (their YES was bought live by the run crossers)
+ 3-WAY (2026-10-19): the rail now ALSO carries the inning three-way basket for the final A–H and stays visible while EITHER family has legs left (2+ runs no longer hides it). Track reads END 4TH · 2 RUNS · ×5 (total legs). Plus a GENERIC TOP-OF-INNING rail (grey) at the top of every inning 1–8 when no T3/T5/T7 rail is up: buys AWAY NO at A = 0; it disappears the moment the away team scores (nothing to buy until the bottom half).
Steve's "Inning 3-Way Patch 01" (2026-10-19). Three contracts per inning — AWAY wins · HOME wins · TIE. Away bats first, so one rule drives everything: once the top half is complete, the away team cannot win the moment H ≥ A. AWAY YES and TIE YES have NO early lock (home can score until the 3rd out) — they only come off the end-of-inning rail. The 9th and extras are OUT by ruling (the backend never fetches them). No new gestures.
A = away runs THIS inning · H = home runs THIS inning · resets every inning (baselines at the inning change) Tap ✎ to correct a miscount — edits never touch the scoreboard; they arm/disarm the legs below. READ IT BEFORE THE GESTURE. App opened mid-inning → starts at 0 → edit it first. Inning advance matters more now: double-tap the inning chip (yellow circle) or A/H are the wrong team.
End of top, A = 0 top rail (generic grey, or T3/T5/T7) BUY AWAY NO End of top, A ≥ 1 (generic top rail HIDES on the away run — Steve 2026-10-19) nothing to buy; T3/T5/T7 rails just record A Run in bottom, H = A +RUN / HR / WALK / hit-flow (hbOnRun) BUY AWAY NO Run in bottom, H > A run gesture BUY HOME YES · TIE NO · AWAY NO if unowned End of bottom, A > H end rail (generic / RFI-NO / B3 / B5 / B7) BUY AWAY YES · HOME NO · TIE NO End of bottom, A = H end rail BUY TIE YES · HOME NO · AWAY NO (if unowned) End of bottom, H > A end rail nothing new — all three owned already // the top rail may simply not be swiped — the end rail picks AWAY NO up through the dedup. No warning: ordinary.
Edit makes a bought leg undecided (e.g. AWAY NO taken at H = A = 2, then A corrected to 3)
→ RED BANNER above the rails: "INNING n · COUNTER NOW A 3 – H 2 · 1 BOUGHT LEG NO LONGER DECIDED — ESCALATE NOW"
→ also an error row in the activity log. NO automatic unwind — unwinding is manual, full stop.
HOT PURPLE a full settlement basket is attached (RFI NO · T3/B3 · T5/B5 · T7/B7 · Top 9 · Bot 9) GREY one leg or a record only (tied-top T3/T5/T7 · generic TOP-OF-INNING · generic END-OF-INNING) // innings 2/4/6/8 have no legacy rail → always grey; a moot legacy rail (wrong score state) simply doesn't render → grey
Bonding sizing to be aware of: bond_cap is PER LEG — a go-ahead run commits 2–3× the cap, an end-of-inning sweep up to 3×; across eight innings that is a materially larger resting book. Cancel buttons as usual. Third-out certainty applies: the rails are operator assertions, no outs gate.
BOND (2026-09-12, admin-locked bond cap > 0 only): #6 tie-total → REST YES full-game Over (new_total + 0.5), the one bond on that swipe. #9 walk-off → the same three bonds as the 9TH slider (winner ML YES · loser ML NO · NO lowest uncrossed total); button where the 9th slider would have been.
The play's runs are walked ONE AT A TIME — each run is its own margin event.
Segment members are BOTTOM-half only (a top-half run decides nothing; home still answers).
#6 TIE-TOTAL — IF any run makes the game TIED (any inning, EITHER half):
BUY YES full-game Total Runs Over (new_total + 0.5) // ONE line, GAME total only
// a game cannot end tied ⇒ the next run is guaranteed before it scores
// never on a go-ahead · never a ladder · never team totals
IF half == Bottom:
#1/#7 — IF inning == 3 or 7 AND the run TIES it:
BUY NO F3/F7 AWAY Winner // away is done batting the segment. TIE stays LIVE
#2/#8 — IF inning == 3 or 7 AND the run puts HOME AHEAD:
BUY YES F3/F7 HOME Winner · BUY NO F3/F7 AWAY Winner · BUY NO F3/F7 TIE
#3 — IF inning == 5, per run, whenever away's lead can no longer reach a spread line:
IF (away − home) ≤ L → BUY NO AWAY −L F5 Spread // away 3–0, 2-run shot ⇒ both rungs die
#4 — IF inning == 5 AND the run TIES it:
BUY NO F5 AWAY Winner (+ any remaining away-spread rung via #3) // F5 TIE stays LIVE
#5 — IF inning == 5 AND the run puts HOME AHEAD:
BUY YES F5 HOME Winner · BUY NO F5 AWAY Winner · BUY NO F5 TIE
// F5 TOTALS stay LIVE — home can keep scoring
#9 WALKOFF — IF inning ≥ 9 AND the play flips home from not-leading to leading:
the game ends on the play — the WHOLE BOARD settles at the play's FINAL score:
Winner: YES HOME · NO AWAY (· NO Tie if listed)
Game totals: reached Overs YES · unreached NO
Team totals: BOTH teams — reached rungs YES · unreached NO
Spreads: home rung YES if final margin clears it ELSE NO · away rungs NO · plus-lines skipped
// PLAYER PROPS EXCLUDED by ruling · multi-run walkoffs judged at the FINAL margin
EXCLUSION LAW: no spread-shrink in the 9th/extras — a tie there reopens unlimited innings,
so nothing is capped until the game actually ends. A B9 equalizer fires ONLY the tie-total.
All legs at the set price cap, sharing the run swipe's click pool. Per-ticker dedup both ways: F5 legs share f5BoughtTickers with the Top-5/Bot-5 sliders; everything else shares hbBoughtTickers with the F3/F7 and 9th settlement sweeps — a binary fire followed by a slider swipe (or vice versa) skips instead of doubling. An equalizer is also a tying run, so #1/#4/#7 naturally fire together with the tie-total. AUTO-FIRE means a mistaken run swipe places these orders instantly — the run swipe is no longer a pure signal.
10th family — per-inning three-way (2026-10-19): inside hbOnRun, bottom-half home runs only, innings 1–8: H = A → AWAY NO; H > A → HOME YES · TIE NO · AWAY NO. Labels 3WAY n · CODE SIDE · run, strategy inning_win, bonded. AWAY YES / TIE YES NEVER here — rails only.
rides every scoring action · IF game lines ON AND inning == 1 AND unfired → BUY YES every Run-in-1st-Inning market @ price cap // firing it permanently hides the RFI NO swipe
ride every run (+RUN, HR, forced WALK, hit-flow taps, diamond scores) · IF game lines ON:
TEAM TOTALS FIRST — fired right after the moneyline, BEFORE batter props / RFI /
game totals / hidden binaries (ruling 2026-07-20 — first claim on the shared pool):
SCORING team only, defending team never touched:
FOR EACH Over rung ≤ team's new run count, unfired → BUY YES @ set price cap
HR sorts HIGHEST rung first; back-fills all reached rungs (decided binaries)
THEN game-total crossers (inside onRunsScored) · FOR EACH run (post-play total):
IF inning ≤ 5 AND F5 Total line crossed, unfired → BUY YES F5 Total @ price cap
IF Total Runs line crossed, unfired → BUY YES Total Runs @ price cap
INNING TOTAL (2026-08-25, KXMLBINNINGTOTAL) — current inning N ≤ 9:
the run crosses inning line runs-this-inning+1 (1st run → Over 0.5, 2nd → Over 1.5)
IF line unfired for inning N → BUY YES "Inn N Over x.5" @ price cap
3rd+ run of an inning: no market exists (silent) · dedup per inning per line
runs-this-inning = (feed total + optimistic) − baseline snapshotted at each
inning change; BOTH halves count — the market settles on the whole inning
armed via Profile toggle · fires on the next scoring action · stake = ML STAKE (not bet cap)
the arm is consumed IMMEDIATELY — even if both legs skip, re-arm from Profile
BUY YES scoring team's full-game Winner · BUY NO opponent's Winner (independent legs)
price: v8 model ON → model buy_limit (backend re-prices; no-trade → skip)
shadow → skip if ask > ML cap, else limit = ML cap
exit: backend sell ladder arms on the real fill
LOCK CHIP V2 — two states, ONE gesture (no long-press, no pin, no app_reentry class):
GREEN (machine): chip shows LIVE batting-team mid + standing crystal (✓/⚠ + age) + card caps
TAP → YELLOW (operator): the DISPLAYED number becomes THE pre for EVERY fire while held —
including multiple scoring plays. Releases on: batter change (EXCEPT through a swiped
hit's popup — dismissal releases), HR-timer baseline, or tap again.
FIRE CARD — every anchor (auto crystal or tap) immediately prices BOTH outcomes
(next-run + HR at runners+1) into ready payloads; regenerates on every state change.
The swipe indexes and sends — ZERO pricing on the hot path (chip shows "46¢ · R53 H61").
NO FIRE-TIME REFEREE — anchor health is guaranteed at LOCK time only (coherence gate +
mid-anchoring + operator lock); a diverged anchor still fires. The per-fire skew line
(anchor vs live, logging only) is the postmortem record.
Coherence gate: ask-sum 98–105 (105 since 2026-07-27, histogram-calibrated), spread ≤ 5¢.
Positions tab now mimics Kalshi: OPEN CONTRACTS only (no order history, no day picker, no pager).
Each open-position card: price input (CENTS, 1–99 — type "45" for 45¢, no dot; ".45" also accepted) + SELL ALL button.
IF no price typed / out of 1–99¢ → SELL button stays DISABLED (red border on bad entry)
ELSE IF Dev mode ON → [DEV] log row, nothing sent
ELSE IF Trading OFF → [SIM] log row, nothing sent
ELSE → backend re-fetches the EXACT open count from Kalshi (never trusts the card),
FLOORS fractional positions (34.7ct → 34 — overselling would OPEN the other side),
then places ONE limit order: sell <whole count> <held side> @ typed price,
GOOD-TILL-CANCELED. No crossability check — resting is the point. Crosses the
book if it can; otherwise RESTS until filled or canceled.
YES position → sells YES at the typed YES price; NO position → sells NO at the typed NO price.
Cards with resting orders show an "N resting" chip + ✕ CANCEL RESTING (cancels every active order on that market — bypasses dev/SIM like all cancels). Strategy tag position_sell in order_log + loghub. The 10s UNDO toast still appears when an order rests.
ML lock chip (tap = yellow operator lock / release — an ANCHOR control, it never places an order itself) · screen-lock swipe · market rows & prop wheel (selection only — no tap-to-buy exists) · dev controls (display state only) · BET CAP input · Positions-page REFRESH and price inputs (typing never sends — only the SELL ALL button does). The 1B/2B/3B diamond buttons are dead code — never rendered. Inning+outs stack (scoreboard): DOUBLE-TAP = advance one half-inning — ASSUMES the displayed batter made the last out (their team's next at-bat opens on the FOLLOWING batter), flips batting/pitching teams (releases TEAMS pins), resets the batter selection, clears outs to 0 AND clears the base runners (a back-step can't restore chips — re-place them after a mis-tap); clamped to ONE half ahead of the MLB feed, auto-clears when the feed catches up. SINGLE TAP = step back a half, ONLY when ahead of the MLB feed (undoes the double-tap, rolls the batter pointer back; at the live feed it does nothing; fires on a 350ms delay so a double-tap's first tap never triggers it). The game-state modal was retired with this (ruling 2026-08-13, third revision same day). Changes the v8 model's inputs but never places an order itself. Positions-page ✕ CANCEL RESTING and UNDO cancels are always live (bypass dev/SIM).
POST /api/order holds every money wall and signs with the operator's own key; the operator's DEMO/REAL setting decides real money. Binaries only, taker buys, the app never sells. Pool rule = MLB/NFL (one bet_cap pool per gesture, a skipped leg leaves the pool to the next leg).Rev A.7 pending (2026-10-19): this section is seeded verbatim from Steve's CourtSlyder Button Commands once Rev A.7 lands (A.6 was reviewed; A.7 folds in the tied-segment rule on every settle slider, win margin after the game spread, END OT buys nothing, and the MLB/NFL pool rule). Until then the handover copy in ~/kalshi-basketball/handover/CourtSlyder_Button_Commands.html is the reference.
upd 2026-10-29 — nba.slyderlabs.com → basketball.slyderlabs.com. The short name is a 301 redirect (path kept) to the real app host, on the shared certificate (expires 2027-01-27, auto-renews). One origin, so saved boards, settings and the home-screen tile stay in one place — add the tile from basketball.slyderlabs.com, not from nba.
upd 2026-10-29 — LIVE LINK: phone ⇄ server WebSocket (push + heartbeat). Needs one nginx change (Shawn) before it carries traffic. The app now opens wss://basketball.slyderlabs.com/api/live (session token, resume=<last seq>) and the server PUSHES: row — every log row the server writes for the operator (💸 / 🧪 / ⛔ / REST / CANCEL) lands in the LOG at once and triggers a bond re-read (the 5 s bond poll drops to 30 s while the link is up); book — top of book for every ticker of the loaded game from the server's Kalshi socket, coalesced 250 ms, so the MARKETS list shows a live ask; build — a new index.html raises the NEW BUILD chip within 10 s; status — the server's Kalshi socket up/down → K pill; pong — the 5 s heartbeat reply drives the ms pill (the /healthz poll steps aside while the link is up). Orders never ride the socket — still POST /api/order, idempotent by client id. Reconnect: backoff 1 → 30 s with jitter, also on app foreground / network back; the server keeps a ring of the last 300 replayable messages per user and replays everything after resume. Two missed heartbeats → LINK LOST on the pill, socket recycled, logged. Settings → LIVE LINK row shows UP / rtt / seq / pushed / book ticks and a 5S · 15S · OFF heartbeat choice (OFF = the polls of before). Every order row carries meta.link = { ws, rtt_ms, last_post_ms, hb_s } — the warm-vs-cold evidence for whether the heartbeat is worth its battery; review after a week and drop to 15S if it isn't. Until nginx forwards the Upgrade on /api/live the socket cannot connect and the app silently stays on polls (nothing on the pill). Smoke (bh).
upd 2026-10-29 — order-book WEBSOCKET on the basketball backend (MLB / NFL parity). The server now keeps one persistent connection to Kalshi's ws/v2 feed subscribed to orderbook_delta for every ticker of every game a phone has loaded (loading a board adds the game's tickers; one subscription per ticker for the life of the connection, resnapshot on reconnect, sequence gaps force a resync). When a leg is sized, the phone's /kalshi/markets/{ticker}/orderbook call is answered from that live in-memory book (_src: "ws" + _age_ms) instead of a REST round-trip to Kalshi per leg; no book yet or WS down → REST exactly as before. Order rows now carry book_src / book_age_ms. /api/status reports kalshi.ws and ws_books. Market data only — orders are unchanged REST POSTs signed with the operator's own key; phones never talk to Kalshi. Smoke (bg).
upd 2026-10-29 — RELOAD APP button + NEW BUILD watch (for the home-screen app). The home-screen app has no browser reload, so: Settings → App build row shows when the loaded copy was built and a RELOAD APP button (saves the board, fetches a fresh copy, reloads; the game resumes from the game card — nothing bought). Every 60 s the app also checks the server's build; when a newer one is up an orange NEW BUILD · RELOAD chip appears in the status bar (tap = reload), the Settings row flips to RELOAD · NEW BUILD, a toast fires once and it is logged. The page is served no-store, so any reload is already the latest copy — no hard refresh needed anywhere. Smoke (bf).
upd 2026-10-29 — run CourtSlyder from the HOME SCREEN (no browser bars). A web page cannot hide a browser's own URL bar or back/forward row — Safari and Firefox on iPhone both slide them in on scroll. The fix is to add the app to the Home Screen: in Safari, Share → Add to Home Screen (Firefox on iOS 16.4+ also offers it under Share); the tile opens the app standalone, full-screen, with no browser chrome, and iOS then also honours the no-zoom viewport flags. The site now ships a manifest (standalone, portrait) and a basketball icon so the tile is named CourtSlyder. The home-screen app keeps its own cookies, so sign in once through the SSO prompt on first launch. Same recipe applies to the NFL and MLB apps (they already carry the standalone meta).
upd 2026-10-29 — END-OF-QUARTER sliders are bright green. END Q1 / END Q2 · HALF / END Q3 / END REG / END OT: green knob (dark text on it), 2 px green border, green tint, green label text, the sub-line in full-contrast text instead of faint grey, and a slow pulsing green glow so the operator cannot miss the slider when it appears (inside the 2:00 window or when summoned). Was slate grey. The glow stops once the slider has fired. Behaviour unchanged (bugaboo #9: the swipe fires). Smoke (as).
upd 2026-10-29 — rebound modal is full-screen after the free-throw sheet. When a trip ends on a missed last attempt, the MISS · REBOUND modal that follows now fills the screen like the free-throw sheet, with ✕ DISMISS · POSSESSION UNCHANGED · BACK TO THE BOARD at the top standing in for the tap-outside. Same content and rules (opens on the defensive side, DEFENSIVE / OFFENSIVE tabs, possession flips on the pick, uncontested gate, punch list #8 rungs). From the MISS button the modal is still a bottom sheet. Smoke (g) / (bd).
upd 2026-10-29 — FREE THROWS is a full-screen sheet. Tapping the board's ▸ FREE THROWS · TAP TO OPEN header (or a missed-last-attempt flow) now opens the trip as a sheet that fills the whole screen — shooter picks, 1 / 2 / 3 attempts, the MAKE rails + MISS buttons, RETAIN POSSESSION and DONE — instead of the inline fold-able panel of 10-24. The ✕ CANCEL TRIP · MAKES STAND · BACK TO THE BOARD button at the top is bugaboo #15's fold: it cancels the trip (makes and their orders stand, unrecorded attempts dropped, nothing ticks, possession unchanged, logged). No tap-outside on this sheet (it fills the viewport). The idle header leaves the board while a trip is open and returns on DONE or ✕; a SUB sheet can still open on top of the trip. Everything the trip does (each make buys on its own, DONE gate, attempt count editable until DONE) is unchanged. Smoke (g) / (aa) / (ad) / (ae).
upd 2026-10-29 — no iPhone zoom, app-wide. Safari in a browser tab ignores the viewport's user-scalable=no / maximum-scale=1 (only the home-screen PWA honours them), which left two zoom paths open: double-tap zoom — rapid taps on the − / + steppers of SET SCORE (and the stat editor) read as a double tap and the page zoomed in, hard to undo — and pinch. Fix: touch-action: manipulation on the page, every button / input / sheet / suite / card (removes double-tap zoom, taps stay instant, scrolling normal; the sliders keep their own pan-y), inputs never under 16px (an input focused at a smaller size also makes iOS zoom), and pinch blocked in script (gesturestart / gesturechange / two-finger touchmove prevented; one-finger moves untouched). If a phone is still zoomed from before the reload, pinch it back out once; after that it cannot zoom again. Smoke (bc).
upd 2026-10-28 — bugaboo #15 (Steve, P0): FREE-THROW trip sheet — attempts editable until DONE; FOLD cancels the trip. The 1 / 2 / 3 attempt count can be changed at any time until DONE: raising it adds a row (attempts already recorded keep their MADE / MISSED state), lowering it below the number already recorded is refused with a toast. Folding the sheet away cancels the trip: unrecorded attempts are dropped, attempts already recorded as MAKE stand in state and their orders stand (the app never sells), nothing ticks, possession unchanged, one log row (FT · FOLDED · trip cancelled · 2/3 recorded (1 made, makes stand) …). The old collapse-and-resume fold and the DISMISS button are gone (the fold is the dismiss). DONE gate unchanged: disabled until every attempt in the current count has a result. Smoke (g) / (aa) / (ad).
upd 2026-10-28 — bugaboo #14 (Steve, P0): rosters.json jerseys trued up against ESPN. Steve's "refreshed 10-23 file" never reached the handover folder, so the live rosters.json was diffed against ESPN's current NBA rosters (every team) and the five mismatches fixed in place: Thybulle LAL 26 → 4, Ayton WAS 6 → 8, Gilbert ATL 51 → 00, Braden Smith IND 18 → 3, Tony Bradley UTA (blank) → 13. Ticker = jersey, so a stale number means none of a player's props attach. Kalshi confirms AYTON8; Thybulle had no open markets at the time of the check (ESPN says 4). File carries a patched note. Smoke (bb).
upd 2026-10-28 — bugaboo #13 (Steve): MISS opens the rebound modal on the DEFENSIVE side. The modal opens already listing the other team's five + UNKNOWN (sub-line DEFENSIVE · WHO GRABBED · OKC). Two team tabs at the top (logo + name); tapping OFFENSIVE switches the list to the shooting team's five for an offensive board, DEFENSIVE switches back. Possession flips to the defence only when a defensive pick (player or UNKNOWN) is made; tapping outside dismisses with possession unchanged. Each player card now also shows REB n · SEEN w. Everything after the pick (uncontested gate, punch list #8 rungs) unchanged. Smoke (ag) / (ah) / (h) / (ax).
upd 2026-10-28 — bugaboo #12 (Steve): STATS page per-player editor — SNAP to API (including DOWNWARD) + manual ± edit. The + on each STATS row now opens EDIT · player: quick CREDIT buttons on top (move an UNKNOWN to this player, as before), then PTS / 3PM / REB rows with − / + (the API figure shown beside each), SNAP TO API · PTS a · 3PM b · REB c (disabled when ESPN has no figure) and APPLY EDIT. All scorekeeping: buys nothing, cancels nothing, sells nothing — rungs already bought stand; seen (punch list #8) is unchanged by any of it. One log row per change: STAT SNAP / EDIT / CREDIT · #1 WEMBANYAMA · pts 14→10 · reb 5→2 · … · seen 3 unchanged with before/after in the meta. Note: AUTO-ADOPT still raises a tally to the (lagged) API figure on the next release, so an edit BELOW the API figure lasts only until the feed says otherwise — an edit after points come off officially sticks, because the API drops too. Smoke (ba).
upd 2026-10-28 — bugaboo #11 (Steve): SET SCORE. Tap either score on the scoreboard strip → SET SCORE sheet: − / + or type for both teams, up or down, button reads SET OKC 31–26 SAS. Scorekeeping only: buys nothing, cancels nothing, no settle. Quarter baselines move the way they do on SNAP TO API — within the current period they do not move, so a total line the edit crosses is MISSED (never bought, never marked). Logged as one gesture with before/after (SCORE SET · OKC 30–28 SAS → OKC 31–26 SAS · …). Final game: no sheet. Smoke (az).
upd 2026-10-28 — bugaboo #10 (Steve): five on the court follow ESPN with NO lag; every other ESPN value is released 60 s late, BY DESIGN. Two paths now. Lineup (zero lag): ESPN's active flag marks exactly the five on the floor per team; when ESPN's lineup CHANGES the board's five follow at once (incoming players take the outgoing slots), one board note FIVE · ESPN lineup · home · in: … · out: … · no lag · buys nothing. An operator SUB made ahead of the feed stands until ESPN reports its next change. Everything else (60 s lag): score / clock / period strip for SNAP, every player stat, the STATS page, AUTO-ADOPT — each poll is queued and released 60 s after it was taken (own 1 s clock, independent of the ESPN FEED cadence). Reason, stated in the code where the lag is set: stats are deliberately delayed so that an operator swipe can never land after the feed has already credited the same event — a three goes in and a whistle goes up; the refs gather; we hold the swipe; ESPN credits the three during the discussion because being wrong costs them nothing; the refs rule it good plus a foul; we swipe; with no lag the tally is one three AHEAD of the official count and every rung bought off it is wrong. The STATS as-of line reads … · DELAYED 60s BY DESIGN (lineup only is live), the SNAP sheet says FEED · 65s old · DELAYED 60s BY DESIGN (its staleness limit is 60 + 90 s), the Settings ESPN FEED row explains it, and the release note reads STATS AUTO · … 60s lag. Lobby starter pick still reads the freshest poll. Smoke (ay) holds a poll at 59 s and releases it at 61 s.
upd 2026-10-28 — bugaboo #9 (Steve, P0): settle sliders FIRE on the swipe — no confirm sheet. END Q1 / END Q2 · HALF / END Q3 / END REG (both faces) place their taker legs the moment the slider is swiped. Knowing the score going into the horn is the operator's responsibility; the taker risk is the one we want out fast. A confirm gate belongs in front of BONDING only, and the settle sliders carry no bond step, so there is no gate at all. This replaces the CONFIRM sheet added 2026-10-24 (Shawn's request) — flagged at ship time. The slider still only appears inside the 2:00 window or when summoned via the Q chip. Smoke (as) + the settle helper.
upd 2026-10-28 — punch list #8 (Steve): REBOUNDS · witnessed-count rule. The official scorer can take a rebound away after the fact, and by a player's fourth rebound nobody can audit the earlier three live. Each player now carries seen (W) beside the tally: rebounds the operator credited to him himself this game — a named tap on WHO GRABBED, gate or no gate. A stats refresh (STATS AUTO), SNAP, the STATS-page CREDIT sheet, UNKNOWN and a gate CANCEL never raise it (Steve's prose says "swiped and confirmed through the gate", but his worked table only balances if every named tap counts — the table is what shipped; flag if that reading is wrong). On a credited rebound taking the tally to N: 1 TRAILING rung, always — the highest line N−1 clears, YES at cap, unless already held. 2 CURRENT rung, only when fully witnessed — the highest line N clears, YES at cap, only if seen = N. 3 DD / TD unchanged. The gate still opens exactly when either rung would buy; a tap that buys nothing records the rebound and closes without a gate. Once a refresh has raised a player's tally, seen can never catch the tally again that game → only trailing rungs for him (intended). STATS page REB cell reads 5 · seen 3; the gate's sub-line reads REB 4 → 5 · SEEN 3 · TRAILING OVER 3.5; every rebound-credit log row carries N, seen and both verdicts (trailing: over 3.5 already held · current: W 4 < N 6). Persisted with the board snapshot. Smoke 322 (section (ax) walks Steve's table: P with two refreshed rebounds buys over 3.5 on his 5th and over 5.5 on his 7th, nothing on the 4th/6th; Q fully witnessed buys over 3.5 as the current rung on his 4th; CANCEL and CREDIT leave seen alone).
upd 2026-10-28 — punch list #7 (Steve): settle sliders buy NO YES on cleared spread lines. By the horn every cleared spread line sits at 99 — a YES there is a wasted order attempt ahead of the legs that fill. On every settle (END Q1, END Q2 · HALF, END Q3, END REG both faces, the Q4 / 2H baskets inside END REG, and the game spread) the spread family is now: short lines (the segment winner's lines the margin fell short of) → NO, lowest first; losing-side lines → NO. Cleared lines are left alone. Tied segment: every line NO, as before. Winner trio first, totals NO-only on unreached Overs, game ML, win margin, team-total NOs and OT unchanged. 10-23 effect: END Q1 CHI–MIL 29 → 20 legs, END Q3 DAL–IND 29 → 25. Smoke section (aw) + the END Q1 / HALF / END REG / pool-drain expectations rewritten (the Q1 basket is 5 legs now, not 7).
upd 2026-10-27 — 3PTER points leg double-counted the make (fixed). POR@LAL, Q2 0:20, operator steve: MAKE +2 on Mamukelashvili (tally 10→12), then 3PTER on the confirm sheet (12→13, matches ESPN at the half) — but the 3PTER's player points leg was priced "pts 14 → 15 crosses 14.5" and bought 1000ct of the 15+ at the 99¢ cap (swept 57–68¢, ~$646 real) while he sat at 13. Cause: the three-pointer branch of scoringLegs computed the points leg as t.pts + 2 → t.pts + 3, a formula written for a pre-make snapshot, while the confirm-sheet 3PTER tap snapshots after the make's +2 is already in the tally — the make counted twice. Latent on every 3PTER since launch; only buys when the inflated figure lands on a rung (two earlier Sandro threes priced 4→5 and 11→12 and missed the rungs). Fix: the three's points leg is the LAST point of the delta (t.pts + delta − 1 → t.pts + delta): the sheet tap (delta 1) prices 12→13, the HOT-rail preview (delta 3 from a pre-make tally) still shows the right next rung. 3PM leg, team/quarter/half/game totals and double-double checks were already +1 and are unchanged. Smoke 295 (new section (av): a 7→8 three buys nothing, a 9→10 three buys exactly the 10+ with label 9 → 10; the pre-fix page fails all five).
upd 2026-10-24 — scrolling over sliders (NFL parity). Every slider's touch listeners are now passive and never call preventDefault, so the rails' touch-action: pan-y lets the browser own vertical pans: a finger moving mostly up/down scrolls the page even when it lands on a rail. A direction lock seals it — once the first ~8 px of a touch are more vertical than horizontal the gesture is released for that touch and the knob never moves; a horizontal swipe behaves exactly as before. Applies to player rails, +2 UNKNOWN, the settle slider, FT and bond/rebound sliders (one shared wiring). Smoke 286 (section (au)).
upd 2026-10-24 — ESPN FEED cadence setting (NFL app pattern). Settings page: ESPN FEED · 5S · 15S · 30S · 45S · 60S · OFF (default 30 s; stored as espn_feed_secs in the operator's synced settings, admin-lockable like the caps). It drives the ESPN summary poll everywhere — board and STATS alike — which feeds the auto-adopted player stats and the score/quarter/clock strip SNAP uses. OFF = no polling at all: swipes only, the ESPN pill reads OFF in grey, SNAP reports no live feed, STATS says the feed is off. Change takes effect at once (a poll fires on switching to a live value). Logged on the device as ESPN FEED · 15 s. Smoke 280 (section (at)).
upd 2026-10-24 — END-OF-QUARTER slider: MLB end-of-inning look + CONFIRM before buying. The settle slider now matches the MLB app's end-of-inning slider: slate tone, thumb on the RIGHT, swipe LEFT, centred mono label with a ← arrow (the tied / winner wording stays in the text). Swiping no longer fires: it opens a CONFIRM sheet showing both teams' logo · score · code (+ the segment score, e.g. SAS · Q1 17) and a green CONFIRM · BUY N LEGS · 20–17 button; only that button fires the basket and advances the period. CANCEL · NOT OVER YET or a tap outside leaves the quarter open, buys nothing, re-arms the slider and logs CANCELLED at confirm. Applies to END Q1–Q3, END Q2 · HALF, END REG (settle + tied) and END OT. Smoke 273 (section (as)).
upd 2026-10-24 — NETWORK TEST on the settings page (MLB Profile pattern). RUN sends 4 GETs to this server's /healthz 350 ms apart and prints ping-style rows (64 bytes from … icmp_seq=n status=200 time=… ms, or request n timeout) with a summary line: packets transmitted / received / % loss · min/avg/max. Green = no loss, amber = some, red ≥ 50 %. COPY RESULTS puts a plain-text report (target, time, user, result, latency, per-sample) on the clipboard for a chat. One run per minute (button shows WAIT Ns). Result also logged on the device. Buys nothing. Smoke 265 (section (ar)).
upd 2026-10-24 — SETTINGS is its own page. The settings sheet is gone: SETTINGS in the bottom nav opens a full page (works with or without a board, tab highlighted like the others). Same rows — bet cap, price cap, DEMO, real-money trading (confirm prompt kept), bond cap (admin), effective caps, colour scheme, signed-in user — and the red LEAVE THE BOARD button while a board is loaded. No DONE button: leave via any other tab or the ‹ chevron. Smoke 257.
upd 2026-10-24 — HIDE ESPN FEED SYNC box on the LOG. A checkbox at the top of the Activity card (checked by default, remembered per device) hides the 🛰 SYNC ESPN rows — over half the feed. Filtering is server-side (exclude_kind=basketball-sync on /api/basketball_activity) so hidden rows never cross the wire; toggling refetches from the top. Smoke 256 (section (aq)).
upd 2026-10-24 — LOG is one combined list. The device and server cards are merged into a single Activity list, newest first. Rows from this phone carry a 📱 mark; a server row that is this phone's own gesture (same label within 5 s) is shown once, as the 📱 row, so nothing appears twice. The 🏀 prefix the ingest adds is stripped from every row (💸 ⏭ ⛔ ⚙ kept). Server rows still page in 40 at a time as you scroll; device rows older than the loaded server window surface once their page arrives. Smoke 251.
upd 2026-10-24 — LOG loads as you scroll. The LOG page no longer pulls 200 server rows at once: it fetches 40, then the next 40 each time the bottom sentinel scrolls into view (/api/basketball_activity now takes a before_ts/before_id cursor and answers has_more + the next cursor, so pages never overlap or skip). The sentinel reads N rows · scroll for more / end of log · N rows. The on-device log (no bandwidth) shows 60 rows with a SHOW ALL button. Smoke 250 (section (ap)).
upd 2026-10-24 — brand header removed; SETTINGS in the bottom nav. The CourtSlyder wordmark / game caption / ⚙ header row is gone (the status bar and the board's own scoreboard carry that information). Bottom nav is BOARD · POSITIONS · STATS · LOG · SETTINGS; SETTINGS opens the same settings sheet over whatever screen is showing. The wordmark long-press that tore the board down is now a red LEAVE THE BOARD · BACK TO THE LOBBY button inside Settings (shown only while a board is loaded; the game card's RESUME still restores it). Smoke 239 (section (an)).
upd 2026-10-24 — LOBBY tab removed; back chevron top-left. Bottom nav is now BOARD · POSITIONS · STATS · LOG. A ‹ button at the far left of the status bar, before the user pill (MLB app pattern), returns to the lobby with the board kept; it is hidden whenever the lobby screen is showing (including first load). The bottom nav, every page and every sheet now span the full screen width like the MLB app (the 420 px column is gone). Long-press on the wordmark unchanged. Smoke 233 (sections (al)–(am)).
upd 2026-10-24 — per-player ⟳ removed; tallies auto-adopt the ESPN figure. Every ESPN poll (board 30 s / STATS 10 s today; cadence setting to follow) raises each player's PTS / 3PM / REB tally to the API figure. Never down (ESPN lags the operator 10–30 s, so a lower figure is ignored) and buys nothing — a rung the feed passes stays missed; the swipe remains the only buy trigger. Bench players the operator never touched pick up their figures too (so UNKNOWN credits self-correct). One board note per poll that changed something: STATS AUTO · adopted API figures … · JONES pts 2→4 · …. The + credit button stays. Smoke 224 (section (ak)).
upd 2026-10-24 — STATS page look. Each team table now has a header band in the team colour with the ESPN logo, full name, code · side · current score; every player row carries the headshot before the name (jersey number when ESPN has no photo). Figures, ⟳ and + unchanged. Smoke 218 (section (aj)).
upd 2026-10-24 — ROSTERS page removed, POSITIONS page added (MLB pattern). The ROSTERS tab is gone (rosters still feed the board/SUB sheet). POSITIONS (always enabled, no game needed) lists every open position on the operator's Kalshi key — the key is shared, so other sports show too — newest-touched first: YES/NO badge, market title (+ strike sub-title), Nct · avg · cost · now · mark · ±P&L · realized · resting; mark = YES bid for a YES position, 100 − YES ask for a NO. Fetched on tab open and ↻ REFRESH via new GET /api/positions (operator's key; batched /markets lookup for titles; mirrored into the local positions table). Read-only — sells nothing. Smoke 213 (section (ai)).
upd 2026-10-24 — MISS · REBOUND step 1 = team cards. OFFENSIVE / DEFENSIVE are replaced by two team cards (ESPN logo + full name in the team colour): the shooting team first, tagged OFFENSIVE REBOUND · KEEPS THE BALL, the other team second, tagged DEFENSIVE REBOUND · BALL FLIPS. Same semantics; team code stands in when ESPN has no logo. WHO GRABBED (step 2) is now player photo cards — face filling the left edge like the sliders, name, jersey + current REB; jersey number when ESPN has no photo; UNKNOWN stays a plain button. Smoke 205 (sections (ag)–(ah)).
upd 2026-10-24 — MARKETS list follows the ball. The list shows only the players of the team in possession (the side whose five are on the rails) and the title says which: MARKETS · CHA · 5. Flipping possession (arrow, TURNOVER, DEF rebound, FT DONE) swaps it to the other team's players at once. Same next-rung-per-player rows. Smoke 196 (section (af)).
upd 2026-10-24 — FT button removed; the suite header is always on the board. The button row is now MISS · TURNOVER. A folded ▸ FREE THROWS · TAP TO OPEN header sits permanently under it (hidden on a final board); tapping it opens a trip in place (logged FT · trip opened). DONE or DISMISS return the idle header. +FOUL from a make still opens the trip directly. Shooter picks are photo cards: the headshot fills the pill with the jersey + name underneath (number alone when ESPN has no photo; UNKNOWN keeps a ?). Smoke 193 (sections (ad)–(ae)).
upd 2026-10-24 — FT is an inline suite (NFL FOLD TD SUITE pattern). FT (and +FOUL from a make) no longer opens a sheet: the trip appears on the board under MISS · FT · TURNOVER with a ▾ FOLD FREE THROWS header, so the five rails above stay live (SUB, other swipes) while attempts are recorded. Same contents and rules: shooter · attempts · one MAKE slider + MISS per attempt · RETAIN · DONE gated until every attempt is recorded. DISMISS · NOTHING RECORDED clears an unused trip (logged); it disappears once anything is recorded. Fold collapses the body and the header shows ▸ FREE THROWS · n/N RECORDED. Pressing FT again while a trip is open just unfolds it (never a second trip). Smoke 182.
upd 2026-10-24 — MARKETS list shows each player's NEXT rung only. In PLAYER POINTS / 3PT / REBOUNDS one row per player: the lowest unbought line above the player's current figure (app tally or ESPN box score, whichever is higher), labelled e.g. WIGGINS 2+ · AT 1; once that rung is passed or bought the next one (3+) takes its place, and a player with nothing left drops out. DOUBLE-DOUBLE / TRIPLE-DOUBLE rows stay until completed or bought. The HOT rail and the swipe still go by the app tally alone. Smoke 175.
upd 2026-10-24 — MARKETS list = player families only. The board's MARKETS list now shows just PLAYER POINTS · PLAYER 3PT · PLAYER REBOUNDS · DOUBLE-DOUBLE · TRIPLE-DOUBLE (game, segment and team ladders are driven by the sliders and no longer listed). All five open by default; a collapse sticks per family. Same row format (label · yes ask · resting at the ask), same 60 s refresh, still buys nothing. Smoke 171.
upd 2026-10-24 — lobby vs Kalshi 429. The lobby's Kalshi events call was anonymous (shared the VPS's per-IP limit with every other process → HTTP 429 Too Many Requests in red). It now goes through the app's signed Kalshi client like the markets path. When Kalshi still refuses a refresh, the server serves the last good list flagged stale and the caption turns amber: KALSHI REFUSED THE REFRESH (RATE LIMIT) · SHOWING THE LIST FROM Ns AGO; with no list at all the lobby says so in plain words and retries once after 20 s. Logged server-side as lobby nba: …. Smoke 175 (section (ac)).
upd 2026-10-24 — players ordered by ESPN minutes. The ESPN box-score poll now also reads each player's minutes. The SUB sheet's bench list is ordered most minutes first (pill tag shows 14 MIN instead of BENCH; players ESPN hasn't listed keep roster order after them; sub line says ORDERED BY MINUTES). The STATS tab gets a MIN column and its rows sort the same way. The five swipers on the board are NOT reordered (the operator's thumb positions stay put). Refreshes with the ESPN poll (30 s board / 10 s STATS). Buys nothing. Smoke 171 (section (ab)).
upd 2026-10-24 — tap outside a sheet. Tapping the dimmed area outside any bottom sheet now closes it. Sheets that hold a decision take the safe choice instead of vanishing: make confirm → CONFIRM (the +2/+3 is already recorded and bought; NOT A BASKET must be tapped on purpose) · bond screen → NO BOND · rebound step 1 → dismissed, possession unchanged (logged) · WHO GRABBED → UNKNOWN · rebound gate → CANCEL · RECORD AS UNKNOWN · free throws → DONE when every attempt is recorded, dismissed (logged) when none is, stays open with a toast when the trip is half recorded. Settings, SUB, clock, SNAP, credit and game-setup sheets simply close. Smoke 166 (section (aa)).
upd 2026-10-24 — SUB sheet look. Double-tap a rail → the SUB sheet now opens with that team's colour gradient and a large ESPN team logo as a backdrop (the logo sits behind the slightly translucent bench pills); bench faces are full-height on the pill's edge like the swipers. Tap behaviour unchanged (sub in, buys nothing). Also: the swipe trail resets to 0 at rest (a tap no longer leaves a gradient sliver). Smoke 159 (sections (y) + (z)).
upd 2026-10-24 — HOT rail (next make crosses a rung). A player rail glows amber when that player's next make would newly cross one of their own Kalshi rungs — computed with the same leg builder the swipe fires, own props only (PTS / 3PM / REB / DD / TD; team and game totals never light a rail), rungs already bought this game excluded. The subtitle names it: NEXT +2 → PTS 10+, and/or 3 → 3PM 2+ when only a three would cross. Recomputed on every render (each gesture, snap, stats refresh). Buys nothing by itself. Smoke 149 (section (x)).
upd 2026-10-24 — possession button = team logo. The button between the two scores no longer shows ◀ / ▶: it shows the ESPN logo of the team with the ball (the five rails below are that team's). Tap still flips possession (POSSESSION ARROW · flipped, buys nothing); the logo stands alone (no pill behind it, 64 px); the bordered pill only returns for the ◀ / ▶ fallback when ESPN supplied no logo. Smoke 142 (section (w)).
upd 2026-10-24 — status pills copied from the MLB app. Top-right corner now carries the MLB trio: ● Nms (round trip of a /healthz GET every 5 s: green <100, amber <300, red beyond, "down" when unreachable), ESPN Ns (seconds since the last ESPN poll, number coloured green within one poll cycle + 4 s, amber to 3×, red beyond; hidden until a board's first poll; tap on the board = SNAP TO API, unchanged) and ● K (Kalshi market list: green = refreshed within 90 s, red = last refresh failed, grey = nothing yet). The old API strip and the DEMO chip are gone; the mode pill reads just 🧪 DEMO / 💸 REAL / ⏸ TRADING OFF (username only in its tooltip, since 2026-10-24 PM). Nothing here buys. Smoke 138 checks (new section (v)).
upd 2026-10-24 — MARKETS list on the board (read-only). Below MISS · FT · TURNOVER the board now mirrors the MLB app's Game Lines card: every live Kalshi line for the game, grouped by family (WINNER, SPREAD, TOTAL POINTS, TEAM TOTALS, WIN MARGIN, OVERTIME, RACE TO 10, then the current quarter's and half's winner/spread/total, then PLAYER POINTS · 3PT · REBOUNDS · DOUBLE-DOUBLE · TRIPLE-DOUBLE). One row per line: label · yes ask · contracts resting at that ask (Kalshi yes_ask_size, top of book only — not a sweep). Past segments, a decided race and the final board drop out; player families start collapsed (tap the family name; the choice survives re-renders). Refreshes with the 60 s market poll and on every gesture. Buys nothing, touches no bonds. Backend ladder rows now carry yes_ask_size, yes_bid_size, volume. Smoke harness 128 checks (new section (u)).
upd 2026-10-23 — Phase 0 punch list (Steve, OKC–SAS dry run 2026-10-20) shipped, all six items:
(1) FT trip sheet — one row per attempt (MAKE slider + MISS button) now actually renders (the rail was collapsing to zero width inside its flex row); each MAKE buys +1 immediately in the §5 order (quarter total → half total → team total → game total → race to 10 → shooter's PTS rung → DD/TD; UNKNOWN shooter skips the PTS rung); MISS records, buys nothing; DONE is disabled (grey, "RECORD EVERY ATTEMPT · n/N") until every attempt has a MAKE or a MISS; 2/3-attempt trip = one possession + 20 s tick, and-1 trip no tick, RETAIN keeps possession, missed last attempt → rebound modal.
(2) ENTER BOARD — each roster column shows a live count (OKC · 4/5, or 4 FLAGGED · TAP 1 ON when the ESPN/roster pre-fill did not yield five); the button is grey and reads PICK 5 PER SIDE until both columns read 5/5, then green ENTER BOARD. Root cause confirmed from the log: the live pre-fill could select <5 with no indication. Also fixed: jersey #00 and #0 on one team collided into one rail/tally key (PHI / NYK in the log).
(3) Settle leg order on every settle slider: winner trio first — YES winner → NO loser → NO tie (tied: YES tie → NO away → NO home) → spread family → total NOs on every unreached Over. END REG not-tied: game ML (YES → NO) → game spread → win margin → game total NOs → team total NOs → Q4 basket → 2H basket → NO KXNBAOT last. Buys nothing new; only the order changed.
(4) Spread sub-order inside every spread family: cleared lines (winner's, margin past the line) YES highest-first → short lines (winner's, margin short) NO lowest-first → every losing-side line NO. Tied segment: every line NO.
(5) SNAP TO API — tap the API pill on the status bar → confirm sheet (board vs feed) → adopts the feed's score, period and clock in one action. Bookkeeping only: buys nothing, cancels nothing (bonds included), advances no settle slider; a total line the snap crosses is MISSED (same as a stats refresh); quarter baselines move with the snap (from ESPN linescores, else the board's pre-snap score); logged as one gesture row with before/after state. Operator-initiated only — the feed never writes to the board on its own; stale feed (>90 s) adopts nothing.
(6) −30s chip beside the clock chip: each tap drains 30 s (floored at 0:00), logged, buys nothing. Clock chip double-tap true-up and the 20 s possession tick unchanged.
Log note: admin rows arrive in ingest order, not leg order — every order AND skip row now carries leg_index / leg_total (skips are labelled [leg n/N]), so read the leg order from that, not from row order. STATS tab (same day, Shawn): live — the ESPN box score is polled every 10 s while STATS is showing (30 s in the background on the board) and the page re-renders itself on every poll with an "API FIGURES AS OF Ns AGO" line; opening the tab polls at once. Display only: nothing is adopted into the tallies and nothing is bought until the operator taps ⟳ or +. Smoke harness 115 checks. Deferred to Rev A.7 (Shawn 2026-10-23): top-of-book logging / signal_window.
POST /api/order on nfl-dashboard, where every wall lives server-side. Money walls: operator DEMO mode → validated then SIMULATED at the relay, nothing sent to Kalshi; operator REAL mode → real order signed with THAT operator's key. SLYDER_LIVE_ARM=DISARMED is an estate-wide emergency brake only. The relay allow-list widened from KXNFLGAME-only to the eight winner+totals series prefixes; the winner-book BUY path is unchanged (cap 1, per-operator keys); engine ARMED since 2026-08-22 — each operator's own DEMO/REAL profile setting decides real money. SLICE 2 (2026-08-21): QUARTER-SPREAD SETTLE. Final minute of Q1–Q4 → "END OF {N} QUARTER · SETTLE" swiper in the kickoff slot. Swipe = the quarter ends at this score: engine buys NO on every KXNFL{n}QSPREAD line above each team's QUARTER margin (leading team: lines above margin; trailing team: all; tie: everything), one-shot per quarter, via the first NO-side path (wire ask, triple-fenced: engine settle-ticker allow-list · relay permits ask on the 8 quarter settle series only · side-aware price-cap clamp on the NO payment). SAME-DAY EXTENSION: the swipe also sweeps the quarter POINT-TOTAL ladder — NO on every KXNFL{n}QTOTAL line above the quarter's combined points (6–0 quarter → NO on 6.5+); LINE-LEVEL fence guarantees an ask never touches a YES-held totals line. AND the quarter MONEYLINE basket: winner YES · loser NO · tie NO (tied quarter → Tie YES · both NO — the MLB T3/T7 doctrine), winner decided by the swipe's margin. AND (same day) the Q2 swipe settles the 1ST-HALF books and the Q4 swipe the 2ND-HALF books — spread + point total + moneyline each, identical rules on the half margins/points; Q1/Q3 never touch half books. ALSO 2026-08-21 (second ruling): the OPERATOR'S OWN BET CAP / PRICE CAP settings are the EXECUTION size/price for EVERY order — the relay CLAMPS count and price down to them and signs the clamped body (price cap 1 → all orders go out at 1¢). Server-synced, ahead of the demo wall (demo simulates the clamped order), CAP CLAMP logged. SIZING (2026-08-21, third ruling — MLB pool+depth doctrine ported; PER-SWIPE since 2026-09-08): every GESTURE gets ONE pool of bet-cap contracts shared across EVERY intent that swipe fires (Shawn's ruling — was per-intent: a first-TD swipe carved 7–8 pools and could deploy a multiple of the cap; now the whole swipe is bounded by bet_cap, drained in firing order, later intents log "budget drained"). Within a gesture, each intent gets (a settle swipe's pool spans ALL its books — quarter + half); each leg is depth-sized by a strict cheapest-first walk of the live orderbook at/below the price cap (never buys more than the book actually shows), UNCAPPED per order since 2026-08-22 (MLB-exact ruling — the bet-cap pool and book depth are the only bounds; empty-book legs skip 'no depth', never rest); a book with NO visible depth under the cap gets a 1-contract resting leg AT the cap; a drained pool logs budget drained and skips remaining legs — so one swipe can never exceed the operator's bet cap, with the relay clamp as backstop. ACTIVITY META (2026-08-21, MLB parity): every 💸/🧪/⛔ order row now carries the SAME forensics as the MLB app's rows — est/max dollars in the label (7ct ≤ 97¢ · ~$3.22 @ 46¢ (max $6.79)) and meta with ask_cents · book_top · book_age_ms · book_stale · bet/price_cap_setting · leg_label · game (the swipe-asserted scope/margin/points) · request_id; est is capped at the limit price (a clamped order can only fill at its limit). This section is the CONTRACT the prop-execution phase must implement; each card flips to "live" as it ships. Same standing rule as the MLB section: update the affected card AND its "upd" chip in the same pass as any behavior change.
The OPERATOR'S LAST GESTURE is the newest truth. A feed spot frame (ESPN's current-drive last play:
possession · down · distance · spot, stamped with the play's wallclock + sequence number) is adopted
ONLY when it is (1) NEWER than the last gesture (any swipe/entry, or a clock sync) —
(2) a NEWER play than the one already adopted (monotonic sequence) — and (3) not a down walking
BACK on the same spot (4th→3rd with the same to-go/yardline = an old play, not a replayed down).
Refused frames are LOGGED once each: "STALE feed spot REJECTED · before ⇠ frame · why · frame play
id + wallclock (age) · last gesture time". The ⟳ API snap ignores the idle grace but obeys the same
staleness rule. Clock: within a quarter it only runs DOWN — a frame putting >45s BACK is refused
("STALE feed clock REJECTED"); clock jumps ≥10s are logged with the frame's fetch age.
EVERY feed/API adoption of possession · down · distance · spot · quarter · clock · score is logged
before→after with the frame stamp — quiet feed-follows included (they used to be unlogged).
STATS NEVER WALK DOWN: a feed value below the board is logged "feed shows LOWER stats · NOT adopted"
and the board keeps its count (until 09-14 a 60s "drift correction" or the final box overwrote it).
TAP CONFIRMATION RUSH / CATCH / rung / TD / try / clock −30s / ⟳ API show a green pill (+ vibrate on Android)
the instant the gesture lands — no pill = the tap did not register, press again
BOARD PERSISTENCE score · clock · quarter · spot · kickoff/PAT latches · stat book · quarter baselines · TD one-shots
mirror to this device per game; a RELOAD / relaunch on the game URL restores them (≤ 8h) and logs
SYS BOARD_RESTORE. A lobby tap is a deliberate FRESH board (no restore). Sandbox never persists.
FEED CHANGES every feed overwrite of operator-visible state is logged as 🛰 FEED (kind nfl-feed, NOT an intent):
score/quarter adopted · kickoff row cleared · spot backfilled over a swiped spot · stats walked DOWN
DURABLE LOG intents already queue + retry (bridge); the server now dedups a re-flushed intent, and the feed notes
ride their own retry queue — a note logged during a bad-signal moment lands late instead of never
upd 2026-10-26 — TD YARDAGE PATCH 01 (Steve; CIN@BAL 10/25: a stale app spot credited an 8-yard TD from the 1).
The law: a figure the app works out and then buys from is on the face of the gesture that buys it. 1 · SET SPOT ONLY — DOWN AT has a second exit, SET SPOT ONLY · NO YARDS · PLACES THE BALL AT CHA 8: places the ball, credits nobody, buys nothing (SYS SPOT_SET; a staged play is discarded, the button says so). The SITUATION editor still places the ball too and now logs it. 2 · the TD distance on the gesture — the affordance over the rungs sheet reads PERINE 8 YD TD › and the window's button reads PERINE 8 YD TD (the number the APP picked, checkable against the field — never the running total). The window's − / + edit that DISTANCE 1–99 in either direction; a changed distance re-places the ball when you commit (logged with both figures). Normal plays unchanged (CONFIRM · N YDS = running figure). 3 · ceiling — an app-derived TD yardage can never exceed the distance from the spot to the goal line; an upstream figure above it is clamped, logged TD_YDS_CLAMPED + hub row, toasted. 4 · freshness-aware spot dispute — when the feed holds a play NEWER than the board's last spot write (any source: commits, kicks, SET SPOT ONLY, editor, feed adoptions) AND it moved the ball >1 yd, the window warns in amber and 5 · the TD yardage leg SITS OUT (SYS YDS_WITHHELD + hub row ⏸ YARDAGE SAT OUT; the tally is not credited either); points, totals, ANY_TD, attempts and bonds proceed. A merely-late feed (frame older than the last spot write) never gates — the disagreement is still logged either way (SPOT_DISPUTE, gated / feed behind / undated). This is a drift detector for a feed that is working normally, not a safety net for 10/25 (that frame was processed 33 s after the swipe). 6 · logs — every spot write is a board note ball spot ← source · OWN 25 → CHA 8 (yl 25→92); PLAY_COMMIT carries TD 8 yd (app derived from CHA 8) or (app derived 8 …; operator set 1). 7 · to-go — nothing that buys reads it (static gate check). 8 · audit of other derived figures: FG distance (yardsToGoal + 17) display only · FG no-good / blocked / punt / kickoff / touchback spots board state only · legacy wheel ceiling unreachable · DOWN AT gain and correction clamp already on the window's stepper + CONFIRM · wp({yardline100, dist: toGo}) feeds market-making quotes only (flagged, no order path). Gate: verify-app.sh 430 ✓ (new step TD YARDAGE PATCH 01, (c)(a)(b)(f)(e)(e2)(e3)(d)(g)).
upd 2026-10-24 — flag · abort is a SLIDER at the top of the play sheet. On the staged-play confirm sheet (rush / pass / incompletion / correction / generic TD) the flag — abort tap button at the bottom — one thumb-slip below CONFIRM — is gone. Aborting is now a red left→right slider FLAG — ABORT · SLIDE TO THROW THE PLAY AWAY at the TOP of the sheet, above the player header; a tap on it does nothing. Same semantics as before: PLAY_ABORTED logged, no stat, no board change, nothing bought. CONFIRM stays where it was. Gate: verify-app.sh green (406 ✓); the yardage-buffer step's harness now answers a leaked instance's 60 s re-probe with parseable rows (the pre-existing "no open market" flake).
Soccer app (same day): activity uploads queue + retry (server dedups) · match stays mounted if a feed poll omits it · VAR unwind handle survives a reload (25 min) · corner/undo/sync/settings/2H-baseline all toast + log · a silent zero-market corner swipe now says "counted · no market".
a separate GTC bid of bond_cap contracts at the operator's price cap, left in the book on a contract a gesture just DECIDED, to absorb holders exiting early at 99¢ · never draws from / competes with the bet-cap pool bond_cap is ADMIN-ONLY: Managed settings → MLB → "Bond cap", set + LOCKED (0 / absent / unlocked = bonding OFF) fires immediately AFTER the taker leg for that ticker (server waits for the taker's placement) — never before it fires even when the taker was skipped for depth/price (the book above the cap is exactly when a bid should rest) plain GTC limit (not post-only): if asks appear under the cap it fills — fine on a decided contract no depth clamp · no global budget · no exchange-limit check — buying power is the only ceiling (Kalshi rejects the last)
run in the 1st (RFI YES auto-fire) → YES RFI Over 0.5 run crossing game total line(s) → YES the HIGHEST just-crossed Total Runs line (one per swipe; F5 + team totals never) run that TIES the game (#6) → YES Total Over (new_total + 0.5) END-1ST slider → NO RFI Over 0.5 (not the 1.5) BOT-5 sweep → NO the LOWEST uncrossed F5 Total Over 9TH slider (Top-9 / Bot-9) · walk-off → YES winner ML · NO loser ML · NO lowest uncrossed Total Runs (that order = capital priority)
one button per bonding gesture at the top of the game screen: CANCEL BOND · <leg> · N resting / CANCEL BONDS · 3 legs · N resting takes the fired slider's slot; a bond from a run swipe while the BOT-5 slider is still up renders directly BELOW it tap = cancel the UNFILLED remainder (filled contracts stay) · gone when fully filled / cancelled / market closed survives a reload on this device (8h) and is rebuilt from the Kalshi orders list (client_order_id "bond-…") on any device
REST 5,000 @ 99¢ · bond · NO KXMLBRFI-… (kind rest, not BUY) → BOND FILL n/N → BOND FILLED / BOND CANCELED / Bond REJECTED server: one bond per ticker per game (order_log guard) · "bonding off" / "already bonded" replies log as skips
Why the whitelist is what it is: since June the account's resting 97–99¢ fills on exactly these contract types (RFI, full-game totals, moneylines at game end, F5 totals NO) have zero losses; every resting loss came from types the spec excludes (F5 YES on crossed runs, HRR, hits). A bond still rests up to bond_cap with no depth clamp, so a mis-swipe risks ≈ bond_cap × 99¢ to earn bond_cap × 1¢ — the cancel button is the safety.
The partner's Kalshi account stream (their READ-ONLY key): the FIRST sight of a partner ORDER (resting or executed) on a series switched ON under partner.slyderlabs.com → Markets.
Our account: BET CAP contracts of the same ticker + side, limit = PRICE CAP (per-contract cost of the side we buy), good-till-canceled → the unfilled part RESTS at the cap. Then, if BOND CAP > 0, an extra resting bid of BOND CAP at the cap (one per ticker+side, placed after the taker). Per-series overrides (bet/price/bond) beat the Settings defaults. Our size never follows the partner's.
Never copies: sells (inferred from the partner's position — shown in the Live feed, flagged), a second copy of the same partner order, a second copy into a market+side we already hold (Once per market), orders older than "ignore orders older than" (backfill after a restart), closed markets, anything past "max copies per hour", and everything while the master switch is OFF. If the partner cancels an order we copied, our resting remainder is canceled too (Follow partner cancels).
DEMO wall: Settings → Demo mode (default ON) simulates every copy — zero Kalshi calls on the order path, rows tagged demo. Turning it OFF asks for confirmation; copies then place REAL orders with the "Our account" registry key. Every copy / bond / failure / setting change is journaled here under app partner, user partner (dash.slyderlabs.com attributes those fills to a "partner" operator).
IF sandbox-lobby game → intent tagged sandbox:true → order-free engine. Nothing sent.
ELSE IF ticker ≠ the actively-selected game → REFUSED // game-lock: a prop buy can never hit the wrong game
ELSE IF SLYDER_LIVE_ARM ≠ ARMED → "WOULD PLACE" log row only. Nothing sent.
ELSE IF count > per-swipe cap (1) → REFUSED — never clamped
ELSE IF operator's DEMO MODE is ON (settings; default ON when unset) →
SIMULATED at the signing relay: validated like a real order, answered with a
Kalshi-shaped fill (order_id demo-…), logged to activity as 🧪 DEMO BUY. No
signing, no money — enforced SERVER-side per operator, independent of arm state.
ELSE → real Kalshi limit order, signed with the OPERATOR'S OWN key
Session cap: 20 order attempts/session (reserved synchronously — burst-proof).
One gesture = one pool. A single spot confirm may cross SEVERAL rungs (a 40-yard catch clears 2+ receiving lines at once) — all fire from that activation's cap pool, HIGHEST rung first. The limit price IS the cap: ask above cap → that rung skips, never chased. Untagged intents route to the SANDBOX engine (default-deny); navigation (LOBBY/LIVE/SQUAD/STATS) fires no intents at all.
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): the two stages no longer buy on their own. Entry, rung, DOWN AT and the TD slider STAGE; the CONFIRM button in the review modal commits every leg in ONE gesture pool (one bet cap per play), highest line first. The newly-decided rule is unchanged but evaluated once, at CONFIRM, against the BUFFERED total (yardage −3, QB rushing −6, counts/TDs 0). The modal's manifest lists exactly the lines that will be bought; lines inside the buffer show as held. The basket lives in app state only — a reload mid-play = basket gone, nothing bought.
floor = the player's PRE-PLAY cumulative total
target = the player's POST-PLAY cumulative total
BUY YES only the rungs where floor < line ≤ target, HIGHEST line first.
A rung an earlier play already won is NEVER re-bought.
A single play fires props in TWO STAGES — different gestures, different jobs:
STAGE 1 — FLOOR ("at least N") → buy what a +N minimum already GUARANTEES,
the instant it's certain. NO playOver.
STAGE 2 — DOWN AT (precise spot) → buy ONLY the remainder beyond the floor,
then close the play (playOver:true → book may requote).
// per-player, per-rung dedup runs ACROSS both stages. Each player tracks an
// INDEPENDENT total: passer's passing line ≠ receiver's receiving line.
// Identical discipline to baseball's batter-prop ladder; the difference is
// football yardage is CONTINUOUS, so the instigator is the spot confirm.
tap the down-&-distance chip in the header (or the yardline strip) once the kickoff is done
BALL KC | DEN // possession — flipping keeps the ball where it is on the field
DOWN 1ST | 2ND | 3RD | 4TH
TO GO tens ▲▼ · ones ▲▼ // odometer: ones past 9 carries, past 0 borrows · 1–99 · "10 yds to go"
BALL SPOT [KC SIDE | DEN SIDE] ◀ DEN · tens ▲▼ · ones ▲▼ · KC ▶ // same widget as DOWN AT / kick returns
◀ ▶ = one yard toward that team's goal, crossing the 50 flips the side automatically · "ball at KC 25"
every tick writes straight to the live board (the chip + yardline strip follow as you step) · SET SITUATION swipe just closes the sheet places NO orders and logs no play — it is the sandbox editor for the structural undersell / a corrected feed
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): the rung no longer fires the floor (or the ML lock — removed). ‹ AT / NEAR THE LINE, AT LEAST 5 ›, ‹ AT LEAST 10 and the new 20+ › (bottom slot) only seed the DOWN AT spot. The TD slider moved OUT of the sheet to a fixed affordance in the top third of the screen (TD › / RUSH TD ›, left-to-right); it stages the play as ending in the end zone and goes straight to the confirm modal. A mis-swipe at the old bottom slot now buys a conservative 20-yard floor instead of asserting six points.
LONGEST RECEPTION (2026-09-15, KXNFLLONGREC): on a CATCH the floor also decides the longest single reception — if N > the receiver's current longest, BUY YES every listed strike N crosses (bought-set fenced, per player per strike). Never on a rush.
YARDAGE BUFFER (Steve 2026-09-14 — 09/13 Mitchell credited 10, finished 9): a RUSHING rung
(KXNFLRSHYDS) fires only when the player's tally clears the line by 3+ (tally ≥ line + 3). A rung
crossed by 1–2 is HELD, not discarded — it fires on the LATER operator gesture whose tally reaches
line+3 (10 → 13 fires 9.5; 10 → 30 fires 9.5 and every rung the new tally clears by 3+, highest
first). A property of the TALLY, so the floor and the precise spot obey it alike. Only operator
gestures fire: the ⟳ API snap fires no intents (it may move the tally; the buy waits for the next
gesture). A bought rung is never unwound. Every hold logs
⏸ HELD · RSHYDS #22 — 10 vs line 9.5 · margin 0.5 < 3 (once per rung per tally). Receiving /
passing yardage rungs keep the plain newly-crossed rule.
on the board, to commit a minimum gain BEFORE logging the precise spot
new_floor = player_pre_total + N BUY YES every rung where player_pre_total < line ≤ new_floor, HIGHEST first: 1 PROP/PASS_YARDS_OVER → passer's guaranteed passing rungs (note: "floor +N") 2 PROP/RUSH_YARDS → ball-carrier's guaranteed rushing rungs FIX 2026-09-08 (Steve: rush props never fired, any runner): the rushing ladder is Kalshi series KXNFLRSHYDS (no U) — the app had probed KXNFLRUSHYDS, which is not a series at all, so the rush ladder was permanently empty and every rush swipe (RB, QB scramble) died in the loud PROP_SKIP added 09-06. Probe + relay allowlist corrected; regression pins the probe to the real series name. 3 PROP/REC_YARDS_OVER → receiver's guaranteed receiving rungs (pass play) // NO playOver — the play isn't terminal; the precise spot still follows. // The guaranteed rungs are certain the instant the minimum is — they fire NOW, // ahead of the market, not at the spot confirm.
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): the swipe-to-confirm is gone. REVIEW · +N YDS › (tap) opens the confirm modal: player + tracked N (base), the MANIFEST (buffered lines, highest first, e.g. RUSH YDS ≥ 44 / RUSH YDS ≥ 40 / RUSH ATT ≥ 12 / PASS YDS ≥ 225 for the QB mirror; held lines faint), a TOUCHDOWN toggle line (independent of the number — editing yards never clears it), tap steppers [−] N [+] for the ASSERTED gain (no wheel, no drag), a large fixed CONFIRM · 44 YDS · TD button showing the BUFFERED total, and a flag — abort tap (logged PLAY_ABORTED, nothing changes). Gain 0 or a loss reads NO BUY: no orders, the stat still updates on a loss (forward-progress gate). Haptic on commit. No timeout, no swipe, no tap-outside exit. After CONFIRM the board moves, then the clock prompt if ≤ 2:00.
LONGEST RECEPTION (2026-09-15, KXNFLLONGREC): on a CATCH the spot's final yards (a TD catch = its full receiving yards) fire the newly-crossed longest-reception rungs the floor did not cover, HIGHEST first; then longest = these yards if greater. A catch no longer than the current longest changes nothing. The reception entry alone never fires this series (count, no yards yet). Only correction path = ⟳ API snap (adopts the feed's LONG even when lower, outside the 90 s live-lead grace; bought rungs stand). Strike operators come from market metadata (≥ vs >). Q/H longest-reception markets are not in the market map — game only.
YARDAGE BUFFER (2026-09-14): the precise rushing spot obeys the same 3-yard buffer as the floor — see the FLOOR card. A rung the floor HELD fires here once the precise tally reaches line + 3.
after any offensive play, to log where the ball actually ended (side + tens/ones odometer + team arrows)
precise net yards = pre-snap spot → play-final spot target = player_pre_total + precise_yards (each involved player, own total) BUY YES ONLY rungs the floor did NOT already cover: prev_bought < line ≤ target, HIGHEST first 1 PROP/PASS_YARDS_OVER 2 PROP/RUSH_YARDS 3 PROP/REC_YARDS_OVER // carries playOver:true — the play is terminal (also the maker's requote cue). // No floor committed first? DOWN AT buys the WHOLE ladder pre_total → target. // Floor committed? Only the top-up — never re-buys a rung the floor won. // "QB goes over his passing yards" resolves across BOTH stages.
GENERIC POSITION SLOTS — "SEA Dummy QB" / "SEA Dummy RB" style stand-ins (renamed 2026-09-06) so gestures survive an
injury: always offered in the rusher/QB swap pickers (the Pass/Run rooms' COMPLETED
PASS/RUN and the TD picker's no-listed-scorer rows are the same idea). A generic slot
fires every TEAM-level leg (winner book, totals sweep incl. team total, NTH_TD, spot
entry) and SUPPRESSES every player-specific prop leg (yardage floors/finals,
receptions/completions mirrors) — each suppression leaves a SYS PROP_SKIP row
("generic player — prop skipped") so the audit trail shows it was considered.
LOUD FAILURES (2026-09-06, Steve's Stevenson session) — a player leg is NEVER
dropped silently: a generic carrier's yardage floor/precise/TD-final leaves a SYS
PROP_SKIP row naming the market; a NAMED player whose gesture finds no mapped Kalshi
tickers leaves a ⏭ SKIP ("no mapped prop tickers"); a family-level intent drop (zero
open markets) leaves a SYS row too. ML lock and every team leg still fire.
CONTRACTS-AWARE RUSHER DEFAULT (2026-09-06) — the designated-rusher row used to
HIDE when the depth-chart RB had no listed books (taking its double-tap swap with it,
SEA: Charbonnet dark while Price #8 held live contracts): the default now picks the
deepest RB WITH books; none at all → the generic RB slot. The row always renders; an
explicit operator pick is honored regardless of contracts.
DOUBLE-TAP SWAP, ALL POSITIONS — dbl-tap the RB rush block (existing), the QB rush
slider, or ANY receiver pill → swap picker (bench room for that position + the "Dummy" row —
2026-09-06: picking it swaps IN a rendered "SEA Dummy WR/TE" slider that tracks the ball
like a real player, fires team legs, and never fires player contracts; double-tap the
dummy to swap a real player back in). Radio semantics: incoming IN, outgoing benches; outgoing player's fired
props STAND and his tallies freeze; incoming tracks his own cumulative (zero if
untouched). Every swap logs a SYS SWAP row. Downstream consumers (rush slider,
pass mirrors, TD pills) re-aim automatically.
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): the catch no longer buys at the swipe. Reception, QB completion and pass attempt are COUNTS staged with the play and bought at CONFIRM (manifest rows REC ≥ N / CMP ≥ N / PASS ATT ≥ N, never buffered). INCOMPLETION now also opens the confirm modal with the PASS ATT line only (CONFIRM · INCOMPLETE) — a pass attempt is bought only on a confirmed play (Shawn 2026-10-14). The INT turnover sheet's own confirm swipe remains the attempt's commit.
PASSING COMPLETIONS (2026-09-15): the reception entry is the ONLY thing that moves the IN QB's completion count — exactly +1 per entry (the receiver's RECEPTIONS count gets its own +1 from the same gesture). The floor, the DOWN AT spot and a TD finish never touch it; INCOMPLETE / INTERCEPTION / SACK / RUSH never do. KXNFLPASSCOMP rungs the new count crosses BUY YES immediately, on the reception, before any spot — per-QB-per-strike, bought once, never unwound. Strike operators are read from market metadata: a "20+" listed as floor 20 with strike_type greater_or_equal fires at 20 (≥); a plain 19.5 / "greater" strike keeps its > semantics. Only correction path = ⟳ API snap: it adopts the feed's official completion count even when LOWER (logged "corrected completions DOWN … no unwind") unless the operator credited that QB within the last 90 s (live lead — kept, logged). Bought rungs always stand. Q/H (segment) completion markets are not in the market map — game markets only.PASS ATTEMPTS (KXNFLPASSATT, 2026-09-08 operator spec): any pass-RESULT gesture on the active QB — this catch entry, an INCOMPLETION tap, an INTERCEPTION resolution (No-TD or pick six) — adds exactly +1 to the IN QB's attempt count and immediately buys any newly-crossed attempt strike (YES, each rung once), BEFORE the spot. A SACK, a rush, the floor/precise stages and a TD finish NEVER touch the count (a catch that finishes as a TD is still one attempt — counts fire at catch entry only). Generic QB → PROP_SKIP. Correction = ESPN reconciler adopting the official C/ATT denominator; bought rungs stand. Game-level series only. Relay allowlist 32. Stats board shows the live PASS ATT ladder for QBs (real rows only). PASS COMPLETIONS (KXNFLPASSCOMP, same pass): the reception entry's long-standing COMPLETIONS_OVER mirror (+1 to the IN QB, exactly once per catch — the 08/30 catch→TD double-count fix stands) now BUYS its newly-crossed strikes: the series exists on Kalshi; "record-only, no series" was stale. Same rules: fires pre-spot, each rung once, incomplete/INT/sack/rush never touch it, reconciler adopts the official C/ATT numerator. Stats board shows the QB's live PASS CMP ladder.
PROP/RECEPTIONS_OVER → the newly-decided receptions rung for that receiver PROP/COMPLETIONS_OVER → the newly-decided completions rung for the passer IF the catch's yards also cross a receiving-yards rung → PROP/REC_YARDS_OVER // count entries do NOT carry playOver — not play-terminal
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): QB rushing yards use the 6-yard kneel reserve instead of the 3-yard buffer (it replaces, never stacks): a KXNFLRSHYDS line fires only when tally − 6 clears it (QB at 8 vs over-5 = held, margin 2.5 < 6; at 20 it fires). Kneel-downs are negative rushing attempts and the whole downside on a QB rushing line. The QB's passing-yards mirror keeps 3. Otherwise identical to RUSH: stages → REVIEW → CONFIRM.
RUSH ATTEMPTS (KXNFLRSHATT, 2026-09-08 operator spec): the RUSH swipe IS the attempt — this swipe (and the RB RUSH swipe) adds exactly +1 to that player's attempt count and immediately buys any newly-crossed attempt strike (YES, floor < count, each rung once), BEFORE the spot is entered. Floor, precise, TD finish and receptions NEVER touch the count; a generic rusher logs PROP_SKIP. The only correction is the ESPN reconciler adopting the feed's official carry count (CAR) per doctrine — a bought rung is never unwound. Kalshi lists game-level attempts only (no Q/H series). Relay allowlist 30. Stats board shows the live RUSH ATT ladder for RB/QB (real rows only).PROP/RUSH_YARDS → fires for the ACTIVE QB at the play-final spot, newly-crossed rushing rungs // the active QB is the squad-page IN QB (a swappable member) — swap the QB and // RUSH_YARDS re-aims to the new number
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): APPLY CORRECTION re-enters the SAME confirm modal with the pre-play base restored and the corrected yards seeded. An upward edit buys via the manifest (buffered, already-bought lines fenced); a downward edit fixes the stat and the spot only (NO BUY, nothing unwound).
IF UPGRADE (more yards than logged) → fire the corrected PROP finals for the newly-crossed rungs
IF DOWNGRADE (fewer yards) → fix the player's stat/total ONLY;
prop buys already fired STAND — never unwound
// mirrors baseball: corrections are authoritative replacement of the total,
// but a won prop is never clawed back
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): still ZERO orders and no bond gate. The chain is now one prompt at a time: try → clock modal (if ≤ 2:00) → kickoff row; ⤺ TD DID NOT STAND stays available on the try and aborts the whole chain (no try, no clock, no kickoff).
2026-09-15: no CONFIRM TD? row and no picker on these — nothing to bond.
kickoff-return TD (Kickoff sheet → RETURN TD → Confirm TD?) · punt-return TD (Punt sheet) · blocked-FG return TD (Field Goal → BLOCKED) defensive TD — interception or fumble return (TURNOVER sheet → DEFENSIVE TD ›) // offensive rushing / receiving TDs are UNCHANGED — full basket. (No blocked-punt sheet exists in the app.)
the scores most often negated by a return penalty (block in the back, illegal block, holding) and unattributable to a scorer
score +6 · quarter / half / game / team points · clock · possession · PAT overlay · stats · NTH_TD ordinal · team TD count BUT NO ORDERS: WINNER reprice, game / 1H / 2H / quarter / team / 1H-team totals, ANY_TD, FIRST_TD, TEAM_FIRST_TD — zero takers, zero bonds lines this score crossed are NOT marked bought (they ride a later scoring gesture normally — the app's standing catch-up rule) log, once per gesture (activity feed + hub row): "RET/DEF TD — board updated, orders suppressed by doctrine (penalty risk) · <why> · <team> +6"
FIRST_TD / TEAM_FIRST_TD (scoring side): DECIDED AND CLOSED with no order — logged "decided & CLOSED by <why>" the next offensive TD is TD #2: it fires ANY_TD, totals and its own legs normally and NOTHING from FIRST_TD / TEAM_FIRST_TD
PAT / 2-pt after a suppressed TD fires its normal legs on the lines the TRY itself crosses, and bonds them it never buys a line the TD crossed (those were never bought — the try skips them explicitly)
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): this is now the BOND GATE — its own overlay AFTER the play's CONFIRM committed the takers and BEFORE the try; nothing else is on screen with it. Run: ‹ BOND · SCORED CLEAN (• standing in the end zone • no dive for the line). Pass: ‹ BOND · CATCH SURVIVED (• two feet comfortably in bounds • upright, not going to the ground). It is the ONLY leftward swipe in the chain (the play's abort is a tap). The swipe rests the bonds (scorer's ANY_TD first, then the crossed total lines, each behind its taker) and the try appears. ✕, a backdrop tap, backgrounding or locking the phone = NO bonds, TD stands, LOGGED (SYS TD_CONFIRM/DISMISS + hub row td_bond_declined). ⤺ TD DID NOT STAND (here or on the try) reverses the score and aborts the whole chain.
after any OFFENSIVE TD (per-player pill, QB rush, How-far TD, generic TOUCHDOWN ›) — a row at the top of the try overlay, above the WHO SCORED? picker (generic slider only) and the PAT / 2-pt / No Good pills. Optional. Never gates the try. Persists until swiped, ✕-dismissed, ⤺ voided, or the try is swiped (which closes it — no bonds). Return / defensive TDs: never.
swipe the TD immediately as always; swipe CONFIRM TD? when the try unit takes the field (kicking unit on a PAT, offense staying out on a 2-pt)
1 YES the scorer's ANY_TD contract // ATTRIBUTED gestures only — the generic slider never bonds a scorer, even once one is picked 2 YES every total line the 6 crossed // game → live half → quarter → team → 1H team (parked at the TD swipe) same bond_cap / price cap / one-bond-per-ticker-per-game; each rests behind the taker that bought the ticker (after_request_id) or alone no takers, no board change, FIRST_TD / TEAM_FIRST_TD never bonded · a second swipe is impossible (the row is gone) · cancel button appears inline log: "TD CONFIRMED · TD · ARI #22 NAME · N bond legs"
closes the row, nothing else: takers stand, board untouched, no bonds, nothing flagged in the log · final for that TD
REVERSES THE GAME STATE ONLY: −6 (score / segment / team totals follow), the scorer's TD credit + any stats the gesture bumped, possession · down · distance · spot back to pre-play; PAT overlay closes. KEEPS THE LEDGER: bought-set untouched (a later score re-crossing one of those lines does NOT fire), placed takers stand, TD ordinals + FIRST_TD / TEAM_FIRST_TD stay consumed, no bonds fire or cancel. Open positions are unwound BY HAND — the log says which: "TD DID NOT STAND · #22 NAME (ARI) · score reversed −6 · ledger retained · N taker legs remain open: <tickers> · lines held bought: <lines>" (activity feed + hub row, reason td_did_not_stand) · final for that TD
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): nothing fires at the swipe. The pill stages a TD-ONLY basket → confirm modal with the TOUCHDOWN line only (no steppers) → CONFIRM · TD fires ANY_TD + the first-TD baskets + the totals takers → bond gate → try → clock → kickoff.
TD SUITE = TD LEGS ONLY + CONFIRM TD? (2026-09-15): a suite TD books the touchdown and nothing else — NO rushing / receiving / passing yards, attempts, receptions, completions or passing-TD legs; no tally moves (goal-line yards are the API snap's job). ANY_TD · FIRST_TD · TEAM_FIRST_TD · totals · WINNER fire as before. Never a picker. After the swipe the CONFIRM TD? row waits in the try overlay — see the TD BOND CONFIRM card.ANYTIME TD BUYS (2026-09-08, Steve's "First TD is missing some buys" item i): every ATTRIBUTED TD (per-player TD pills, the How-far house call, the scorer picker) now also buys the scorer's KXNFLTD anytime-TD rung — floor < his TD count, newly-decided only (1st TD buys the 1+ line, 2nd buys the 2+ line; already-bought rungs are fenced). Generic/unattributed scorer → PROP_SKIP audit row, no buy. Relay allowlist 28 series. TEAM-FIRST-TD BASKET (2026-09-08, operator spec, same pass): each team's FIRST TD also decides that team's KXNFLTEAMFIRSTTD event (one Kalshi event per team) — scorer YES (first claim on the pool) + every other listed TEAMMATE NO, once per team per game, fired at scorer attribution on every TD path with a per-team ordinal mirroring the game FIRST-TD machinery. Unattributed team-first TD (defense/returns, closed picker) → loud "basket held" and that team's event stays untouched (the D/ST contract exists on Kalshi but the app can never attribute a D/ST scorer). Ask fence admits the series — the YES and NO legs are disjoint tickers. Same pass, root cause of the report itself: the "missing buys" a–h all fired as intents in Steve's session — every order leg skipped "no depth" because his OWN saved settings were bet cap 1 · price cap $0.01 (nothing trades under a 1¢ limit). Raise the caps in Settings; the executor was working as designed.
per-player TD pills in the TD suite (rush-TD for RB/QB, receiving-TD per catcher), formation-first
1 THE FIELD-DEFINING MARKET FIRST (whichever is live):
IF game's FIRST TD → PROP/FIRSTTD — the scorer's first-TD YES
ELSE IF next-TD series listed → PROP/NEXTTD — the scorer's next-TD YES
2 PROP/ANYTD → the scorer's anytime-TD YES // deepest, most-traded NFL book
3 TOTALS SWEEP → the Over lines the TD's CONCRETE +6 crosses (see Auto-riders —
buys through +6 ONLY, never the 6.5/7 line: the PAT hasn't happened)
4 FIELD NO-SWEEP → BUY NO the SAME field market for EVERY OTHER listed player
// order: the field market moves fastest on a specific scorer (1), anytime-TD is
// deep/liquid (2), point totals a beat slower (3), field NOs are near-certain
// settled contracts and not running away (4).
// RUSH TD / REC TD route to the SAME markets — "kind" tags the stat, not the market.
// Return/defensive TDs are winner-book events, NOT offensive-player prop gestures
// (the scorer's ANYTD still counts, via its own gesture).
// Real ticker shape: KXNFLANYTD-<date><away><home>-<TEAM><SCORER>
// e.g. KXNFLANYTD-26JAN04WASPHI-PHITMCKEE16
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): WHO SCORED? moved INTO the confirm modal as optional tap-pick chips. NOTHING fires before CONFIRM · TD (Shawn 2026-10-14 — reverses the 08-28 money-first order): winner-book intent, NTH_TD, the totals takers and (if picked) the scorer's ANY_TD / first-TD takers all fire at CONFIRM. The bond gate follows and still bonds total lines only — a picked scorer is never bonded.
ORDER (2026-09-15): TOUCHDOWN › (money fires) → CONFIRM TD? (total-line bonds only — the generic slider NEVER bonds a scorer) → WHO SCORED? (every player; on pick the scorer TAKERS fire: ANY_TD · FIRST_TD · TEAM_FIRST_TD — never a bond, and TD legs only, no rush-vs-receive props) → the try. All three sit in the try overlay; the try closes both. Dismissing the picker holds the scorer legs as today; a dismissed confirm still lets a later pick fire its takers.
NO BOND (2026-09-13): the TD's own +6 is never bonded in v1 (reviews overturn TDs). The PAT / 2-pt that follows bonds ONLY the lines the try itself crosses — never a line the TD crossed.
The game's FIRST TD decides every KXNFLFIRSTTD contract. At scorer attribution (never before): BUY YES the scorer's contract (first claim on the pool) + BUY NO every other listed player's contract. ONE-SHOT per game — later TDs never touch the series. An UNATTRIBUTED first TD (picker closed, defensive/return TD) fires NOTHING and logs "first TD unattributed — basket held": the mystery scorer might be one of the listed players, so the field's NOs are not certain. Relay: allowlist 26 series; the ask fence admits KXNFLFIRSTTD (the app never holds YES on a market it NOs).
ON THE SWIPE — before the picker even opens (the +6 is certain, the scorer isn't):
winner-book TD intent ("scorer pending") · NTH_TD stub · full totals basket
(game / H1 / quarter / 2H / team crossers — same sweep as every score) · +6 on the
board · PAT overlay armed
THEN the "Who scored?" picker — attribution ONLY (kind inferred: RB/QB → "rush",
catcher → "rec"): the catch + QB completion mirror, receiving/rushing-yards finals,
passing-yards mirror, TD stat bumps. Closing the picker WITHOUT a pick skips
attribution only — the money already went. Nothing double-fires on the pick.
TD BONDS (2026-09-15): a touchdown's crossed lines (and, for an ATTRIBUTED gesture, the scorer's ANY_TD) bond only on the CONFIRM TD? swipe — never at the TD swipe. CANCEL BOND button: now INLINE in the board's scroll flow, first thing under the header block at scroll top — it scrolls away with the board, no sticky / fixed pin.
A bond = a SEPARATE resting GTC bid of bond_cap contracts at the operator's PRICE CAP on a
contract a gesture just DECIDED, first in line for holders exiting at 99¢. ADDITIVE: the taker
leg is sized by bet_cap exactly as before; the bond never draws from the gesture pool, has NO
depth clamp (it is meant to rest) and fires even when its taker was skipped for depth / price /
drained pool — but never when the market is missing or closed, and never off a feed change.
Sent immediately AFTER its own taker's round-trip (never before — a cap bid placed first would
sweep the book itself). Logged as REST n @ cap¢ · bond · YES|NO ticker · STATUS (kind rest,
orange REST badge), then BOND FILL / FILLED / CANCELED rows as the exchange reports them.
bond_cap is the NFL managed setting "Bond cap" on the console (🔒 locked → hidden from the operator, enforced by the server). An ADMIN may also set their OWN on the NFL SETTINGS page (BOND CAP, admin only). Absent / unlocked-for-an-operator / 0 = bonding OFF: no REST rows, no buttons, nothing in SETTINGS. New operators are seeded bond_cap 0, locked.
FG (+3) / PAT (+1) / 2-PT (+2) YES on every total line the kick or try ITSELF just crossed, one
bond per line, priority: game total → live half total (1H or 2H)
→ quarter total → team total → 1H team total. A line the TD
crossed is NEVER bonded by the PAT (just-crossed window).
Segment ladders obey the same "baseline estimated" gate as
the taker.
TD swipe · safety NO bonds (v1 — reviews overturn TDs; revisit with live data).
END Q1 · Q2 · Q3 · Q4 the quarter's 3-WAY MONEYLINE TRIO: YES the finalized outcome
(winner or TIE), then NO the other two (YES first = capital
priority). END Q2 adds the 1ST-HALF trio (cumulative half
margin — its own determination). END Q4 adds the 2ND-HALF
trio (Shawn's ruling 2026-09-13).
END Q4 game-winner LIVE since 2026-09-14 (END-Q4 finalization basket): YES the
winner's moneyline then NO the loser's, after the Q4 trio and
before the H2 trio. Tied at END-Q4 → nothing; END-OT bonds them
when a winner emerges (never on a tie game).
One red button per bonding gesture, DIRECTLY BELOW the Kickoff button while it shows and in its region once it is gone (top of the board):CANCEL BOND · YES GAME O44.5 · 840 restingorCANCEL BONDS · 3 legs · 2,400 resting. Tap = cancel the UNFILLED remainder of that gesture's bond(s); fills stay. Disappears when fully filled / cancelled / closed. Rebuilt from the exchange's order list on reload (bond orders carry abond-client-order-id prefix) — a refresh cannot orphan a resting bond. DEMO mode: the bond is simulated as resting (🧪 DEMO REST) so the button can be exercised without money; a demo bond never blocks the live one.
RET/DEF TD DOCTRINE (2026-09-15): a kickoff / punt / blocked-kick return TD or a defensive TD updates the board and every counter exactly like any TD but fires ZERO order legs from this sweep (nothing marked bought; the lines it crossed are skipped by the TRY that follows, then ride the next scoring gesture normally). See the RETURN & DEFENSIVE TD card.
BOND (2026-09-13): when the score is a KICK (FG / PAT / 2-pt) every line the kick itself just crossed also gets a RESTING bond of bond_cap at the price cap behind its taker (see the BONDING card above). TD and safety crossings bond nothing.
TEAM-TOTAL FIX (2026-09-05, Steve's confirmed wrong-side leg): the team filter matched
the team code anywhere in the ticker — the EVENT suffix ("…SEANE…") contains BOTH codes, so
the opening SEA score built the NE ladder keyed to SEA's points (bet_cap 1 was the only
thing that kept it off the book). The filter now requires the code in the ticker's MARKET
segment ("-NE4") — scoring team only, its own cumulative, each line once.
points = the CERTAIN value of the score ONLY: TD = 6 (NOT 7 — the PAT has not happened) FG = 3 SAFETY = 2 PAT good = +1 · 2PT good = +2 (their OWN gestures, later) new_total(segment) = segment_points_before + points for EACH live totals line in the segment: IF line ≤ new_total AND unbought → BUY YES Over (HIGHEST first) Segments swept, all by the same points: the CURRENT quarter total (Q1–Q4) · the H1 total (ONLY in Q1/Q2) · the full-game total the SCORING TEAM'S team total // only that team advances — never the other side
NEVER buy a total the score only reaches WITH a not-yet-happened PAT/2PT. A TD makes the segment +6, CERTAIN → buy every Over ≤ (before + 6). DO NOT buy (before + 6.5) or (before + 7) — that 7th point needs the unkicked PAT. When the PAT is actually good, the PAT gesture fires +1 and buys the 6.5/7 line THEN. // the scoring analog of the yardage doctrine: buy what already happened, // never a milestone that depends on a future event
RET/DEF TD DOCTRINE (2026-09-15): a return / defensive TD as the game's (or the team's) first TD DECIDES AND CLOSES FIRST_TD / TEAM_FIRST_TD without an order — the next offensive TD is TD #2 and fires NOTHING from these series.
Only ONE player can win the field — the moment the scorer takes it, every OTHER leg is CERTAIN: NO.
for EVERY listed player EXCEPT the scorer:
BUY NO that player's field-market YES (first-TD, or the current next-TD leg)
FIRST-TD: one-shot — fires once, on the game's first TD only.
NEXT-TD: fires per TD, then the series advances to the following leg.
// fires FOURTH, after the scorer's YES legs are secured — the NOs are near-certain
// and not running away. The scorer himself NEVER gets a NO — he took the YES.
// The football analog of baseball's hidden-binary field settlement.
RET/DEF TD DOCTRINE (2026-09-15): a suppressed return / defensive TD still advances the game and team TD ordinals (it is TD #1; the next TD is #2) — only its order legs are withheld.
Next-TD legs fire ONLY IF the series actually lists on Kalshi. KXNFLNEXTTD was NOT a 2025-season market and still does not exist as of 2026-08-09 — the backend probes it on every market resolve and reports it via unavailable (the weekly re-check, automated). The ENGINE owns this gate: never fire next-TD legs against a market that doesn't exist.
upd 2026-10-14 (Steve patch 01 · ONE COMMIT POINT): CLOCK: one 2:00 window for everything (modal, red chip, wheel ceiling — was 2:30 / 2:00 / 2:00). The wheel runs 0:01–2:00; END OF {Q} · 0:00 is its own button on the clock modal — SYS bookkeeping (QUARTER_END typed SYS, no playOver), buys nothing. The clock modal auto-summons the moment the operator clock crosses 2:00 (the crossing, not the condition; re-arms when reclaimed above 2:00; resets each quarter). After a TD the clock prompt queues AFTER the try. The ESPN clock never writes the board: an amber ⟳ M:SS chip beside the clock shows the discrepancy, one tap adopts it (logged CLOCK_ADOPT). The END-Q4 tied-game bonding rule is UNCHANGED (deferred).
Shows in the final minute of Q4 (time alone), and is PROMPTED by any score entered inside it.
Definitions: OUTCOME = home / away / TIE by margin · MoV = winner's margin · REACHED Over = crossed
during play (bought YES live, NEVER touched by the slider) · UNREACHED = above the final points.
SPREAD SETTLEMENT (MoV m): winner's line −L → YES if m > L else NO · loser's every line NO — one leg
per listed line per team, a MIX of YES and NO. NEAREST-FIRST: legs sorted by |L − MoV|, YES before NO
on ties. The bet-cap pool drains down this order; legs past it are logged as skips.
NOT TIED — the GAME basket leads (deepest book):
1 GAME MONEYLINE YES winner · NO loser (KXNFLGAME, 2 legs)
2 GAME SPREAD spread settlement on game MoV, nearest-first (KXNFLSPREAD, every line)
3 GAME TOTAL NO on each UNREACHED Over, lowest first (KXNFLTOTAL)
4 TEAM TOTALS per team: NO on each UNREACHED Over above that team's final (KXNFLTEAMTOTAL)
TIED — the whole game basket is HELD ("END-Q4 tied — game basket held for OT" logged, OT_HOLD)
and the END-OT slider appears.
THEN, tied or not:
5 Q4 3-WAY YES outcome · NO other two 6 Q4 SPREAD nearest-first (tied Q4 → every line NO)
7 Q4 TOTAL NO on UNREACHED lines 8 H2 3-WAY from the H2 margin — its OWN determination
9 H2 SPREAD nearest-first 10 H2 TOTAL NO on UNREACHED lines
Per-ticker dedup across the swipe; every leg carries settled:true. Q1–Q3 / END-Q2 keep the legacy
order (spread → totals → 3-way, ladder order).
END-OT SLIDER — only after an END-Q4 swiped TIED; not clock-gated; one-shot. The swipe asserts
overtime ended; the FINAL includes OT points. Winner → steps 1–4 on the OT final. STILL TIED →
steps 2–4 only: spread at MoV 0 (every line, both teams, NO), game-total NOs, team-total NOs —
THE MONEYLINE IS NOT SETTLED ON A TIE GAME (no leg, no lookup). Q4 / H2 are never re-fired.
BOND (2026-09-13): the quarter's 3-way moneyline trio is bonded behind its takers — YES the finalized outcome (winner / TIE) first, then NO the other two. END Q2 also bonds the 1H trio, END Q4 the 2H trio. Since 2026-09-14 END Q4 also bonds the game moneyline (YES winner, NO loser) after the Q4 trio and before the H2 trio — held on a tie, fired by END-OT when a winner emerges. One CANCEL BONDS button per swipe under the Kickoff slot.
Every quarter / 2H number is measured from the score at the START of that segment,
NOT the running game score. The baseline is registered by whichever comes first:
clock hits 0:00 · ESPN advances the period · the operator picks a quarter in the
clock editor · THIS swipe
Idempotent — two signals for the same quarter can't double-advance or re-snapshot.
The swipe now also registers the boundary and moves the board to the next quarter.
END-OF-QUARTER PROMPT (2026-08-28): any score entered inside the FINAL MINUTE pops
"End of quarter?" — accepting runs THIS settle (score included: it applied to state
first; same estimated-baseline confirm). FG / safety → prompt immediately; a TD holds
the prompt until the PAT/2-pt entry resolves (untimed down — the conversion belongs
inside the settled segment). Declining does nothing; the swipe stays available.
CATCH→TD COUNT FIX (2026-08-28): the rungs "TD ›" finish no longer re-fires the
reception/completion counted at catch entry — one catch that ends in the end zone is
exactly 1 reception + 1 completion.
IF the baseline was only ESTIMATED (board jumped quarters with no real boundary)
→ the swipe first asks the operator to CONFIRM the points scored in that quarter;
nothing is bought until they do. Live quarter/2H total sweeps hold off too
(⏭ SKIP · reason "baseline estimated"). Game + team totals are cumulative and
keep firing normally.
// 08/26: the clock editor's quarter picker advanced the board WITHOUT a baseline,
// so every segment read current−0 = the cumulative score — Q3 settled on "margin -2"
// (true segment 9-0) and bought the wrong side of LAR -7.5 with real money.
SPREADS (2026-08-24 — YES side added): quarter (and H1/H2 on Q2/Q4) spread ladders
winner's lines the margin CLEARED (floor < margin, e.g. −2.5 at +3) → BUY YES
lines ABOVE either team's margin → BUY NO (tie margin 0 → NO everything)
MONEYLINE: winner YES · others NO (tied segment → TIE YES · both NO)
segment_final = points scored IN that segment (now locked)
END Q1: Q1 total → BUY NO every Over line ABOVE segment_final
END Q2: Q2 total → same NO-sweep · H1 books now CLOSED → same spread/ML/total settle on H1
END Q3: Q3 total → NO-sweep
END Q4: Q4 total → NO-sweep · H2 books CLOSED → same spread/ML/total settle on H2
IF regulation ends here (no OT) → full-game total CLOSED → NO every unreached FG Over
BOTH team totals CLOSED → NO every unreached team-total Over
ELSE (OT) → game + team totals STAY LIVE into overtime
// mirrors baseball's F3/F5/F7 settlement: reached Overs were already taken live
// by the TOTALS SWEEP as points scored; the slider cleans up the NO side the
// boundary just made concrete.
Clock editor — quarter picker (2026-08-27): picking a LATER quarter now asks for the score AT THE START of that quarter (prefilled with the live score, i.e. "this quarter just started"), because every quarter/2H total, spread and moneyline is measured from it. Confirming fires a CLOCK_SYNC bookkeeping intent only — no orders. Picking an EARLIER quarter (a mis-tap correction) reuses the baseline already recorded and never rewrites it.
Navigation fires nothing: the bottom nav (LOBBY / LIVE / SETTINGS / ACTIVITY — SQUAD + STATS moved to header chips on the board, 2026-08-09) is proven intent-free in the ship gate — returning to the lobby mid-game keeps the session live (the running game shows ▶ IN PROGRESS). SETTINGS (per-operator BET CAP + PRICE CAP, server-synced, blur-commit) and ACTIVITY (this central feed, app='nfl' — since 2026-08-22 scoped to the SIGNED-IN operator's own rows; the admin #activity page keeps the all-operators view; SKIPPED legs now log too — ⏭ amber SKIP rows (kind nfl-order-skipped) for every leg the executor evaluated but did not place, with reason budget drained / session cap, so a swipe's FULL intended basket is visible) are pages, not gestures — no intents, no orders. Bookkeeping intents that NEVER order: SYS/STAT_SYNC (⟳ API adopt — 2026-08-21: now also adopts the ESPN score + quarter (max-only / never walks the board backwards) AND the game clock (exact, live-only, skipped when the feed's period trails the board quarter) AND the ball spot — down/distance/yardline/possession from the current drive's last play-end. AUTO-SYNC (same day): all of the above now applies AUTOMATICALLY on every feed poll (5s cadence, was 12s) for the whole game — same doctrines; ball spot/down/distance/possession hold a 15s gesture-grace so the trailing feed never reverts a just-swiped play; ⟳ API remains the manual grace-ignoring full adopt, and rebuilds the settle-swiper quarter baselines from ESPN linescores when the quarter jumps), CLOCK_SYNC, GAME_SELECT (repoints the engine's allow-list — refused engine-side while it holds exposure or resting quotes, and requires live-game provenance). Out of this section's scope (documented when their phase ships): the winner-book reprice legs of TD/FG/safety/turnover, the maker layer (@ · HIKE · REQUOTE · box quoting · LIFT) — maker UI BUILT and deliberately HIDDEN 2026-08-10 (MM toggle + @ button exist in the app behind the single flag MM_UI_ENABLED; flip it when the market-making update ships — do not rebuild these), NTH_TD, and the winner-book reprice legs. TEST BUY panel REMOVED 2026-08-10: the app now has ZERO order-capable emitters (structurally asserted in the ship gate — no type:BUY exists in the bundle); the engine's order path is unreachable from the UI until the prop-execution phase ships.