Re-Review: Bug: Scanner never attaches markets to basketball games

review-1850-2026-07-10-v2 Review

review ready

Verdict: APPROVED

Re-review of board item #1850 — ldraney/botfire#19. Bug type issue. Previous review (review-1850-2026-07-10) returned NEEDS_REFINEMENT due to missing arch-bots architecture note. That note now exists (arch-bots, id 2527) along with the newly created domain model note (arch-domain-bots, id 2541). All previous findings re-verified. Scope is solid and ready for implementation.

Template Completeness

  • [x] Type — Bug
  • [x] Lineage — references spike #18
  • [x] Repo — ldraney/botfire
  • [x] What Broke — detailed explanation of the skip-existing-games bug in discover_games
  • [x] Repro Steps — 5 concrete steps with Rails console commands and Kalshi API queries
  • [x] Expected Behavior — clear description of retry behavior for 0-market games
  • [x] Environment — Docker Compose on archbox, scanner service path
  • [x] Acceptance Criteria — 4 testable criteria
  • [x] Related — links to project-botfire and spike #18

Traceability

  • [x] story:bot-strategy label — present on board item
  • [x] story note verified — found in project-botfire user-stories section: "As a bot operator, I want to identify and exploit profitable trading conditions so that my automated bots generate consistent positive P&L."
  • [x] arch:bots label — present on board item
  • [x] arch note verified — arch-bots (Architecture: BotFire Bot Engine) exists in pal-e-docs, active. Additionally arch-domain-bots (Domain Model: BotFire) created since last review.
  • [x] Forgejo issue — ldraney/botfire#19, state: open

File Targets

  • [x] app/services/scanner.rb — verified: file exists (258 lines)
  • [x] discover_games method — verified: defined at line 45
  • [x] Game.exists?(milestone_id: m["id"]) — verified: line 53, this is the root cause. Games that already exist are rejected from new_milestones, so their market discovery is never retried.
  • [x] real_game_market? filter — verified: lines 116-122, checks for /GAME/ in ticker. KXWNBAGAME-* tickers would pass this filter correctly.

Repo Placement

OK. Issue filed on ldraney/botfire, fix is in app/services/scanner.rb in the same repo. Single-repo fix.

Dependencies

  • No blocking dependencies. Item is in backlog with no items it depends on.
  • Downstream: item #1851 (Per-sport analytics) would benefit from this fix, as it needs basketball market data to track per-sport performance. Not a blocker.
  • Item #1848 (Tighten Live Edge ceiling) is in QA but is independent — it adjusts price thresholds, not sport type discovery.

Acceptance Criteria

4 criteria, all machine-verifiable:
  • [x] AC1: Basketball game-winner markets attached — testable via Market.joins(:game).where(games: { sport_type: "basketball_game" }).count
  • [x] AC2: Scanner retries for games with 0 markets — testable by observing scanner logs after restart
  • [x] AC3: No regression in baseball discovery — testable by checking baseball market counts before/after
  • [x] AC4: Market count > 0 after scanner runs — directly testable Rails console query

Blast Radius

  • The reject { |m| Game.exists? } pattern exists only in scanner.rb line 53 — no sibling services share this pattern.
  • Downstream: bot_engine.rb queries markets filtered by sport_type via bot.watched_sports (lines 77-81). Once basketball markets appear, bots configured to watch basketball_game will start evaluating those markets. This is expected/desired behavior.
  • volatility_bot.rb includes basketball_game in HEDGEABLE_SPORTS (line 13) — but the volatility bot is currently disabled per board item #1840.
  • game_phase.rb already handles basketball_game sport type (lines 10, 32) — no changes needed there.
  • Dashboard (index.html.erb line 190) already maps basketball_game to "Basketball" — no UI changes needed.
  • Bot::SPORT_TYPES (bot.rb line 2) already includes basketball_game.

Decomposition Assessment

No decomposition needed. 1 primary file target (scanner.rb), 4 acceptance criteria, estimated under 5 minutes of agent work. Well within the single-pass threshold.

Recommendation

No action needed. All traceability requirements satisfied. Scope is complete and verified.