Bots
Notes
Review 14
-
Re-Review: Per-sport analytics: add sport breakdown to scoreboard service
review-1851-2026-07-10-v2Verdict: APPROVED
Re-review of board item #1851. Previous review (
review-1851-2026-07-10) returned NEEDS_REFINEMENT for two reasons: (1) missing arch note, (2) issue body included already-completed dashboard work. Both issues are now resolved.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone, references #19 dependency
- [x] Repo -- ldraney/botfire
- [x] User Story -- "As a trader, I want the scoreboard service to log Live Edge win rate, P&L, and trade count broken down by sport"
- [x] Context -- Explains Live Edge expansion, notes dashboard already has sport analytics
- [x] File Targets -- Modify scoreboard.rb, create scoreboard_test.rb, explicit NOT-touch list
- [x] Feature Flag -- none (internal analytics)
- [x] Acceptance Criteria -- 3 scoreboard-specific criteria
- [x] Test Expectations -- unit test with run command
- [x] Constraints -- SQLite, BotFire->Market->Game path
- [x] Checklist -- standard PR/test/scope checklist
- [x] Related -- project-botfire, issue #19
Traceability
- [x] story:bot-strategy label -- "As a bot operator, I want to identify and exploit profitable trading conditions..."
- [x] story note verified -- found in project-botfire user-stories section
- [x] arch:bots label -- bots architecture component
- [x] arch note verified -- arch-bots note exists in pal-e-docs (ID 2527, "Architecture: BotFire Bot Engine"); arch-domain-bots also exists (ID 2541, "Domain Model: BotFire"). Previous review's [SCOPE] finding resolved.
- [x] Forgejo issue -- ldraney/botfire#20, open
File Targets
- [x]
app/services/scoreboard.rb-- verified: file exists (102 lines), groups by bot only, no sport_type references. Valid modification target. - [x]
test/services/scoreboard_test.rb-- verified: does not exist yet.test/services/directory exists with sibling test files (bot_engine_active_hours_test.rb, position_monitor_test.rb, volatility_bot_test.rb). Valid creation target. - [x] NOT-touch list correct: dashboard view already has sport analytics, bot_engine is sport-agnostic, scanner is separate ticket (#19).
Repo Placement
OK -- all work is in ldraney/botfire, matching the Forgejo issue repo. Single repo, no cross-repo concerns.
Dependencies
- Issue #19 (scanner bug, board item #1850) is documented in Lineage. Currently in backlog. Non-blocking -- scoreboard changes work with whatever BotFire records exist. Basketball trades will flow once #19 is fixed, and per-sport breakdown will pick them up automatically.
- No other blocking dependencies on the board.
Acceptance Criteria
All 3 AC are scoreboard-specific, verifiable by an agent:
- [x] AC1: "Scoreboard service outputs per-sport stats" -- agent can verify by checking Scoreboard.report output contains sport grouping
- [x] AC2: "When a sport has 0 trades, it doesn't appear" -- agent can verify with test data (empty sports excluded)
- [x] AC3: "All-sport totals still displayed alongside per-sport breakdown" -- agent can verify totals_line still present in report output
Previous review's flagged AC about the dashboard has been removed. All remaining AC are valid.
Blast Radius
Scoreboard is consumed by two callers:
bot_engine.rbline 269 -- printsScoreboard.reportto terminal log. Adding sport grouping to report output changes log format (expected, that's the goal).dashboard_controller.rbline 36 -- usesScoreboard.new.bot_summary(bot)for bot card data. As long as bot_summary return hash is not modified, dashboard is unaffected.
portfolio_grouper.rbline 67 already usesf.market.game.sport_typefor grouping, validating the data path pattern. Not affected by this change.No downstream consumers beyond these two callers.
Decomposition Assessment
1 file to modify (scoreboard.rb), 1 file to create (scoreboard_test.rb), 3 AC, single repo. Well within single-agent pass (<5 min). No decomposition needed. Points of 2 are appropriate.
Recommendation
No action needed. All previous NEEDS_REFINEMENT findings have been addressed:
- arch-bots note now exists (resolved [SCOPE])
- Issue body scoped to scoreboard only, dashboard references removed (resolved [BODY])
- AC rewritten to scoreboard-specific criteria (resolved [BODY])
- Points reduced from 3 to 2 (resolved [BODY])
- scoreboard_test.rb correctly listed as file to CREATE in File Targets (resolved [BODY])
-
Re-Review: Bug: Scanner never attaches markets to basketball games
review-1850-2026-07-10-v2Verdict: 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 missingarch-botsarchitecture 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. Additionallyarch-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_gamesmethod — 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 fromnew_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 inapp/services/scanner.rbin 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 inscanner.rbline 53 — no sibling services share this pattern. - Downstream:
bot_engine.rbqueries markets filtered bysport_typeviabot.watched_sports(lines 77-81). Once basketball markets appear, bots configured to watchbasketball_gamewill start evaluating those markets. This is expected/desired behavior. volatility_bot.rbincludesbasketball_gameinHEDGEABLE_SPORTS(line 13) — but the volatility bot is currently disabled per board item #1840.game_phase.rbalready handlesbasketball_gamesport type (lines 10, 32) — no changes needed there.- Dashboard (
index.html.erbline 190) already mapsbasketball_gameto "Basketball" — no UI changes needed. Bot::SPORT_TYPES(bot.rb line 2) already includesbasketball_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.
-
Review: Bug: Scanner never attaches markets to basketball games
review-1850-2026-07-10Verdict: NEEDS_REFINEMENT
Board item #1850 —
ldraney/botfire#19. Bug type issue. The scope is well-written and the bug is confirmed in code, but the traceability triangle is incomplete: thearch-botsarchitecture note referenced by thearch:botslabel does not exist in pal-e-docs.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
- [x] Repro Steps — 5 concrete steps with Rails console commands
- [x] Expected Behavior — clear description of retry behavior
- [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..."
- [x] arch:bots label — present on board item
- [ ] arch note MISSING — [SCOPE] Create architecture note
arch-botsfor component bots. Search for "arch-bots" returned 0 results. - [x] Forgejo issue — ldraney/botfire#19, state: open
File Targets
- [x]
app/services/scanner.rb— verified: file exists (258 lines) - [x]
discover_gamesmethod — 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 fromnew_milestones, so their market discovery is never retried. - [x]
real_game_market?filter — verified: line 116-122, checks for/GAME/in ticker.KXWNBAGAME-*tickers would pass this filter correctly.
Repo Placement
OK. Issue filed on
ldraney/botfire, fix is inapp/services/scanner.rbin 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: track Live Edge performance by sport type) 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
All criteria are concrete and agent-verifiable. No missing criteria detected.
Blast Radius
- The
reject { |m| Game.exists? }pattern exists only inscanner.rbline 53 — no sibling services share this pattern. - Downstream:
bot_engine.rbqueriesMarket.where(status: "active").joins(:game).where(games: { sport_type: sport_types })(line 78-81). Once basketball markets appear, bots configured to watchbasketball_gamewill start evaluating those markets. This is expected/desired behavior. volatility_bot.rbincludesbasketball_gameinHEDGEABLE_SPORTS(line 13) — but the volatility bot is currently disabled per board item #1840.- The
game_phase.rbservice already handlesbasketball_gamesport type (line 10, 32) — no changes needed there.
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
[SCOPE]Create architecture notearch-botsfor the bots component. All 12 board items on board-bots carry thearch:botslabel but no backing architecture note exists. This should document the scanner, bot_engine, and bot service architecture.
-
Review: Per-sport analytics: track Live Edge performance by sport type
review-1851-2026-07-10Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Feature Flag
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label -- "As a bot operator, I want to identify and exploit profitable trading conditions..."
- [x] story note verified -- found in project-botfire user-stories section
- [x] arch:bots label -- bots component
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-bots for component bots
- [x] Forgejo issue -- ldraney/botfire#20, open
File Targets
- [x]
app/services/scoreboard.rb-- verified: file exists, currently has no sport_type grouping, valid target for modification - [ ]
app/views/dashboard/index.html.erb-- ISSUE: Dashboard ALREADY has full per-sport analytics. Existing features include: Sport filter buttons (line 119-122), "P&L by Sport" panel (line 136-139), SPORT_LABELS mapping (line 188-194), sport column in Open Positions and Recent Exits tables, calcBreakdown for sport grouping (line 544). The dashboard controller already passessport: g.sport_typein the data response (dashboard_controller.rb line 21). This file target is invalid -- the described dashboard work is already complete. - [ ]
test/services/scoreboard_test.rb-- ISSUE: Referenced in Test Expectations but does not exist yet. Should be listed as a file to CREATE in File Targets, not just referenced in tests.
Repo Placement
OK -- all work is in ldraney/botfire, matching the Forgejo issue repo.
Dependencies
- Issue #19 (scanner bug, board item #1850) is correctly identified as a dependency in the Lineage section. It is in backlog. This ticket can proceed independently -- the scoreboard changes work with whatever BotFire records exist, and basketball records will flow once #19 is fixed.
- No other blocking dependencies found on the board.
Acceptance Criteria
- [ ] AC1: "Dashboard shows trade count, win rate, and P&L grouped by sport_type" -- ALREADY SATISFIED. The dashboard's "P&L by Sport" panel already shows this breakdown with wins/losses, win rate, and P&L per sport. An agent implementing this AC would make no changes.
- [x] AC2: "Scoreboard service outputs per-sport stats in its log/display" -- Valid, not yet implemented. Scoreboard currently only groups by bot, not by sport.
- [x] AC3: "When a sport has 0 trades, it doesn't appear in the breakdown" -- Valid for scoreboard. Dashboard already handles this (calcBreakdown only includes groups with data).
- [x] AC4: "All-sport totals still displayed alongside per-sport breakdown" -- Valid for scoreboard. Dashboard already shows this via summary cards.
Net: 3 of 4 AC are already satisfied on the dashboard side. Only scoreboard work remains.
Blast Radius
Scoreboard is consumed by two callers:
bot_engine.rbline 269 -- printsScoreboard.reportto terminal log periodicallydashboard_controller.rbline 36 -- usesScoreboard.new.bot_summary(bot)for bot card data
Changes to scoreboard report format will affect terminal log output. Adding a new method (e.g.,
sport_summary) is safe. Modifyingbot_summaryreturn hash could break the dashboard controller if keys change. No other downstream consumers found.Decomposition Assessment
After correcting scope: 1 file to modify (scoreboard.rb), 1 file to create (scoreboard_test.rb), 3 effective AC, single repo. Well within single-agent pass. No decomposition needed.
Recommendation
[BODY]Removeapp/views/dashboard/index.html.erbfrom File Targets -- the dashboard already has per-sport analytics (sport filter, "P&L by Sport" panel, sport columns in tables). No dashboard changes needed.[BODY]Rewrite AC1 or remove it -- "Dashboard shows trade count, win rate, and P&L grouped by sport_type" is already satisfied. Replace with scoreboard-specific AC or remove entirely.[BODY]Addtest/services/scoreboard_test.rbto File Targets as a file to CREATE (currently only referenced in Test Expectations).[BODY]Reduce points from 3 to 2 -- with dashboard work removed, this is a single-service change plus test.[SCOPE]Create architecture note arch-bots for component bots.
-
Review: Spike: Multi-sport expansion -- does the late-game edge work beyond baseball?
review-1849-2026-07-10Verdict: READY
Template Completeness
- [x] Type -- Spike
- [x] Lineage -- Follows Spike #11, establishes volume motivation (3.3 trades/day vs 14+ target)
- [x] Repo -- ldraney/botfire
- [x] Question -- 7 sub-questions covering price bands, timing, risk params, time windows, volume, seasonal availability, sample size
- [x] Deliverables -- 3 deliverables: analysis doc, follow-up tickets, or no-action rationale
- [x] Time-box -- 3 hours
- [x] Related -- 4 related items (project-botfire, #11, #12, #14)
Traceability
- [x] story:bot-strategy label -- present on board item
- [x] story note verified -- found in project-botfire user-stories section. Entry: "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 note exists ("Architecture: BotFire Bot Engine", status=active)
- [x] Forgejo issue -- ldraney/botfire#18, state: open
File Targets
- [x] docs/multi-sport-analysis.md -- spike deliverable (to be created). Docs directory exists at /home/ldraney/botfire/docs/ with existing bot docs. Correct placement.
Note: Spike issues produce docs, not code. No existing file targets to verify.
Repo Placement
OK. Issue specifies ldraney/botfire, Forgejo issue filed on ldraney/botfire. Single-repo spike -- no cross-repo concerns.
Dependencies
- Spike #11 (Live Edge deep dive, item #1845) -- COMPLETED (in qa column). This spike follows it. No blocker.
- Issue #12 (Evening filter, item #1846) -- PR #16 merged, in qa. Related (sport-specific hours may differ) but not a blocker.
- Issue #14 (Ceiling 89c, item #1848) -- PR #15 merged, in qa. Related (sport-specific bands may differ) but not a blocker.
- No items block this spike. No undocumented dependencies found.
Acceptance Criteria
Deliverables are clear and agent-verifiable:
- docs/multi-sport-analysis.md exists with per-sport sections -- verifiable via file check
- Follow-up tickets created on Forgejo for viable sports -- verifiable via API
- If no viable sports, docs file contains rationale -- verifiable via content check
Blast Radius
Low blast radius. The codebase already has multi-sport infrastructure baked in:
game_phase.rb-- already handles baseball, basketball, football, hockey, soccer with sport-specific "nearly over" and "about to settle" logicscanner.rb-- DEFAULT_SPORTS already includes all 5 sport typesbot.rb-- SPORT_TYPES constant already lists all 5 sports; watched_sports defaults to allbot_engine.rb-- already filters by watched_sports, active hours are per-bot configurableportfolio_grouper.rb-- already groups positions by sport_typelive-edge.md-- already documents game phase filters for all 5 sports
This spike is research-only (no code changes). Follow-up feature tickets would likely involve trigger configuration changes (sport-specific thresholds, time windows) rather than structural code changes.
Decomposition Assessment
No decomposition needed. Single cohesive investigation producing one analysis doc. File target count: 1 (docs/multi-sport-analysis.md). Deliverables: 3 (all part of the same analysis flow). Time-box: 3 hours. Fits comfortably in a single agent pass.
Recommendation
No action needed. Scope is solid, all traceability legs verified, template is complete, and the spike is well-bounded.
-
Re-Review: Pre-settlement exit to avoid catastrophic losses
review-1847-2026-07-10-v2Verdict: APPROVED
Re-review of board item #1847. Previous review
review-1847-2026-07-10returned NEEDS_REFINEMENT with 5 issues. All 5 have been resolved.Previous Issue Resolution
- [x] Wrong file path -- Fixed: issue now says
app/services/game_phase.rbwith explicit note "(note: this is in app/services/, NOT app/concerns/)" - [x] Missing blast-radius files -- Fixed:
scoreboard.rbanddashboard/index.html.erbnow in File Targets with clear change descriptions - [x] Missing include -- Fixed: first file target notes "must also
include GamePhase(currently not included, butgame_nearly_over?is needed from that module)" - [x] Non-testable AC -- Fixed: old AC4 (outcome metric "settlement loss rate drops to near zero") replaced with two verifiable ACs: scoreboard counting and dashboard styling
- [x] Traceability notes -- Fixed:
project-botfireuser-stories section exists withstory:bot-strategyentry;arch-botsnote exists (Architecture: BotFire Bot Engine, status=active)
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Discovered during Spike #11
- [x] Repo -- ldraney/botfire
- [x] User Story -- As a bot operator / I want / So that
- [x] Context -- Detailed settlement loss data ($2.97 catastrophic loss, TP $0.59 vs settlement win $0.33)
- [x] File Targets -- 4 modify + 1 do-not-touch, all verified against codebase
- [x] Feature Flag -- none
- [x] Acceptance Criteria -- 5 verifiable criteria
- [x] Test Expectations -- 3 unit tests + run command
- [x] Constraints -- 5 constraints including include requirement and downstream consumer note
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:bot-strategy label -- present on board item
- [x] story note verified -- project-botfire user-stories section contains bot-strategy entry
- [x] arch:bots label -- present on board item
- [x] arch note verified -- arch-bots note exists in pal-e-docs (Architecture: BotFire Bot Engine, covers 7 services, key files, order pattern, data model, infrastructure)
- [x] Forgejo issue -- ldraney/botfire#13, open
File Targets
- [x]
app/services/position_monitor.rb-- verified: exists, has evaluate_exit (line 48) and resolve_settlement (line 69). Does NOT currently include GamePhase. Correct target for pre-settlement exit check. - [x]
app/services/game_phase.rb-- verified: exists atapp/services/(noapp/concerns/directory in project). Hasgame_nearly_over?method. Correct path. - [x]
app/services/scoreboard.rb-- verified: lines 35-36 only countstop_lossandtake_profit. New exit reason would be invisible in stats. Correct target. - [x]
app/views/dashboard/index.html.erb-- verified: line 592 hasisWin = f.exit_reason?.includes('win') || f.exit_reason?.includes('take'). A profitablepre_settlement_exitwould show as loss pill. Correct target. - [x]
app/services/bot_engine.rb-- verified: exists. Correctly listed as do-not-touch (entry logic separate from exit logic).
Repo Placement
OK -- All affected files in ldraney/botfire. Issue filed on ldraney/botfire. Single-repo change.
Dependencies
- Spawned from Spike #11 (board item #1845, ldraney/botfire#11) -- in QA column, upstream work complete.
- Sibling items from same spike: #1846 (botfire#12, backlog), #1848 (botfire#14, backlog) -- no blocking dependency.
- Item #1843 (botfire#8, "Add game phase tracking to BotFire records") -- could overlap on game_phase.rb but this ticket only consumes existing
game_nearly_over?, no conflict. - PositionMonitor must
include GamePhase-- documented in issue File Targets.
Acceptance Criteria
All 5 ACs are agent-verifiable:
- AC1: "Position monitor detects games approaching final settlement" -- testable via unit test with game_nearly_over? returning true
- AC2: "Open positions in near-settlement games are exited at market price" -- testable via unit test
- AC3: "Exit logged with exit_reason: 'pre_settlement_exit'" -- testable via assertion on BotFire record
- AC4: "Scoreboard correctly counts pre_settlement_exit in win/loss stats based on P&L sign" -- testable
- AC5: "Dashboard renders pre_settlement_exit with correct win/loss styling based on P&L" -- testable via DOM assertion
Blast Radius
- dashboard/index.html.erb line 326 -- Exit breakdown table has same
isWin = reason.includes('win') || reason.includes('take')pattern. Not explicitly called out in issue, but the constraint "All downstream consumers of exit_reason must handle the new value" covers it, and an agent grepping for exit_reason will find both locations. - dashboard_controller.rb lines 87-90 -- Computes avg_sl/avg_tp only for stop_loss/take_profit. Pre-settlement exits are a distinct category and correctly excluded from SL/TP averages. No change needed.
- volatility_bot.rb -- Has own exit logic but currently disabled. Not affected.
- volatility_bot_test.rb -- Tests check take_profit, stop_loss, time_stop exit reasons. New exit reason doesn't affect existing tests.
Decomposition Assessment
4 file targets in 1 repo, 5 ACs. All changes tightly coupled (add exit path + update downstream consumers of exit_reason). Estimated agent work: under 5 minutes. No decomposition needed.
Recommendation
No action needed. Scope is solid, all previous issues resolved, traceability triangle complete.
- [x] Wrong file path -- Fixed: issue now says
-
Re-Review: Add evening-only time filter to Live Edge
review-1846-2026-07-10-v2Verdict: READY
Re-review of board item #1846. Both NEEDS_REFINEMENT issues from
review-1846-2026-07-10have been resolved. Scope is solid, file targets verified, traceability complete.Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Feature Flag (none -- acceptable, no feature flag doc in repo)
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label -- present on board item
- [x] story note verified -- found in project-botfire user-stories section. Entry: "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 note exists in pal-e-docs (note_type: architecture, status: active, project: bots). Covers 7 services, key files, order patterns, data model, infrastructure.
- [x] Forgejo issue -- ldraney/botfire#12, open
Previous Review Issues -- Resolved
- [x] user-stories section -- Was missing on project-botfire. Now present with
story:bot-strategyentry in a table with Story Label and User Story columns. - [x] arch-bots note -- Was not found by search. Now exists at slug
arch-botswith note_type: architecture, status: active, tagged architecture+active.
File Targets
- [x]
app/services/bot_engine.rb-- verified: exists (268 lines, 7.9k).evaluate_botat line 44,execute_actioncalled at line 65, defined at line 158. Natural gate insertion point is top ofevaluate_bot(line 44) or before theexecute_actioncall at line 65. - [x]
app/models/bot.rb-- verified: exists (12 lines, 319 bytes). Simple model withsports_filterserialization. Noactive_hourscolumns yet. Ticket Constraints section says aBOT_ACTIVE_HOURShash in bot_engine.rb is acceptable (no migration needed). - [x]
app/views/dashboard/index.html.erb-- correctly excluded from scope.
Repo Placement
OK. Issue says
ldraney/botfire, filed onldraney/botfire. Single-repo change, no cross-repo concerns.Dependencies
- Parent spike #11 (board item #1845) is complete, in QA column. This ticket was spawned from it.
- Sibling tickets #13 (item #1847) and #14 (item #1848) were also spawned from #11 but are independent -- no ordering dependency.
- No blocking items in in_progress column.
Acceptance Criteria
4 AC, all agent-verifiable:
- [x] "Live Edge does not fire on games outside 12pm-midnight MDT" -- testable with time stubbing
- [x] "Time window is configurable per bot" -- testable by checking hash/config structure
- [x] "Skipped trades are logged with reason 'outside active hours'" -- testable by capturing log output
- [x] "Existing trigger logic unchanged" -- testable by verifying trigger evaluation still called normally inside window
Test command
bin/rails testis valid. Existing test pattern intest/services/volatility_bot_test.rbprovides a fixture template. Nobot_engine_test.rbexists yet -- agent will create one.Note: Title says "evening-only" but AC defines 12pm-midnight MDT (wider than evening). The AC is the authoritative spec; the title is colloquial. Not a conflict.
Blast Radius
Low. The time gate is a pre-filter in
evaluate_botbefore any trigger evaluation or order placement. No other callers ofevaluate_botorexecute_actionoutside bot_engine.rb. Dashboard correctly excluded. App timezone is UTC (config.time_zonecommented out inconfig/application.rb); agent must use explicitTime.use_zone("America/Denver")orin_time_zonecalls rather than relying onTime.zone.Decomposition Assessment
No decomposition needed. 2 file targets in 1 repo, 4 AC, estimated agent work well under 5 minutes. Straightforward pre-filter gate pattern.
Recommendations
No action needed.
-
Re-Review: Tighten Live Edge ceiling from 92c to 89c
review-1848-2026-07-10-v2Verdict: APPROVED
Re-review of board item #1848. Previous review (
review-1848-2026-07-10) returned NEEDS_REFINEMENT for two traceability gaps. Both are now resolved.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- references Spike #11, supersedes #7
- [x] Repo -- ldraney/botfire
- [x] User Story
- [x] Context -- includes P&L data by price band
- [x] File Targets
- [x] Feature Flag -- none (appropriate for DB-only change)
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label present
- [x] story note verified -- found in project-botfire user-stories section. Entry: "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
- [x] arch note verified --
arch-botsnote exists in pal-e-docs (note_type=architecture, status=active, project=bots, title="Architecture: BotFire Bot Engine") - [x] Forgejo issue -- ldraney/botfire#14, state: open
File Targets
- [x]
app/services/bot_engine.rb-- verified: line 135 containsreturn false if trigger.condition_max.present? && value > trigger.condition_max.to_f. Issue correctly says NOT to touch this file. - [x]
db/seeds.rb-- verified: file exists but does NOT seed triggers (only users and bots). Issue correctly says "optionally if trigger defaults are seeded there" -- no change needed. - [x] DB change
Trigger.find_by(name: "Live Edge").update!(condition_max: 89)-- thecondition_maxcolumn exists (migration20260708130000_add_condition_max_to_triggers.rb, schema.rb line 107). The Trigger model references are consistent.
Repo Placement
OK. Issue specifies
ldraney/botfireand is filed onldraney/botfire. Single-repo change.Dependencies
- Supersedes Issue #7 (board item #1842, backlog, marked closed/superseded)
- Spawned from Spike #11 (board item #1845, currently in qa column)
- No blockers identified. No items in in_progress that this depends on.
- Sibling issues from same spike: #12 (board item #1846, 3pt) and #13 (board item #1847, 5pt) -- no blocking relationship.
Acceptance Criteria
3 criteria, all agent-verifiable:
condition_maxset to 89 -- verifiable via rails runner command provided in Test Expectations- No entries above 89c after deploy -- verifiable by monitoring BotFire records post-deploy
- Dashboard confirms new trades stay in 85-89c band -- verifiable via analytics dashboard
Test command is real and correct:
bin/rails runner "puts Trigger.find_by(name: 'Live Edge').condition_max"Blast Radius
Low.
condition_maxis referenced in exactly 2 places:bot_engine.rb:135-- the check_late_game guard (already correct)db/schema.rb:107-- column definition
Only the Live Edge trigger uses the late_game trigger type. No other bots, services, or downstream consumers are affected.
Decomposition Assessment
No decomposition needed. 1 effective file target (DB record update), 3 acceptance criteria, estimated agent time well under 5 minutes. This is a single rails runner command or a data migration.
Recommendation
No action needed.
-
Review: Pre-settlement exit to avoid catastrophic losses
review-1847-2026-07-10Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Discovered during Spike #11
- [x] Repo -- ldraney/botfire
- [x] User Story -- As a bot operator / I want / So that
- [x] Context -- Detailed with settlement loss data
- [x] File Targets -- 2 modify + 1 do-not-touch
- [x] Feature Flag -- none
- [x] Acceptance Criteria -- 5 criteria
- [x] Test Expectations -- Unit tests + run command
- [x] Constraints -- 3 constraints listed
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:bot-strategy label -- present on board item
- [ ] story note MISSING -- project-botfire has no user-stories section. [SCOPE] Create user-stories section on project-botfire with bot-strategy story entry.
- [x] arch:bots label -- present on board item
- [ ] arch note MISSING -- search for arch-bots returned no results. [SCOPE] Create architecture note arch-bots for the bots component.
- [x] Forgejo issue -- ldraney/botfire#13, open
File Targets
- [x]
app/services/position_monitor.rb-- verified: exists, contains evaluate_exit and resolve_settlement logic (lines 48-94). Correct target for pre-settlement exit check. - [ ]
app/concerns/game_phase.rb-- ISSUE: wrong path. File is atapp/services/game_phase.rb. Noapp/concerns/directory exists in this project. The module already hasgame_nearly_over?which is the exact method needed. - [x]
app/services/bot_engine.rb-- verified: correctly listed as do-not-touch. - [ ]
app/services/scoreboard.rb-- MISSING from targets. Scoreboard counts exits only as stop_loss and take_profit (lines 35-36). New pre_settlement_exit reason will be invisible in stats. - [ ]
app/views/dashboard/index.html.erb-- MISSING from targets. Line 592:isWin = f.exit_reason?.includes('win') || f.exit_reason?.includes('take'). A profitable pre_settlement_exit would be styled as a loss pill because "pre_settlement_exit" contains neither "win" nor "take".
Repo Placement
OK -- all affected files are in ldraney/botfire. Issue correctly filed on that repo. Single-repo change.
Dependencies
- Spawned from Spike #11 (board item #1845, ldraney/botfire#11) -- in QA column, upstream work complete.
- Sibling items from same spike: #12 (botfire#12, backlog), #14 (botfire#14, backlog) -- no blocking dependency.
- Item #1843 (botfire#8, "Add game phase tracking to BotFire records") -- could overlap on game_phase.rb but this ticket only consumes existing
game_nearly_over?, no conflict. - PositionMonitor does NOT currently
include GamePhase-- BotEngine and VolatilityBot do, but PositionMonitor does not. The ticket must add this include.
Acceptance Criteria
- AC1-3, AC5 are directly verifiable via unit tests.
- AC4 ("Settlement loss rate drops to near zero for new trades") is an outcome metric, not directly testable in a unit test. An agent could verify it by checking that settlement paths are unreachable when pre-settlement exit fires, but the criterion as written reads as a production metric. Consider rephrasing to: "Games where pre-settlement exit fires produce no settlement_loss exit reasons."
- Test command
bin/rails testis valid for Rails projects.
Blast Radius
- scoreboard.rb -- Only counts stop_loss and take_profit. New exit reason will not appear in any scoreboard tallies. Must either add a pre_settlement_exit counter or generalize the counting logic.
- dashboard/index.html.erb -- isWin classification at line 592 will misclassify profitable pre-settlement exits as losses (red pill). The pill text will render correctly ("pre settlement exit") but the color will always be loss-colored.
- volatility_bot.rb -- Has its own settlement logic (lines 116-127) but is currently disabled/self-managing. Not directly affected but same pattern exists.
- dashboard_controller.rb -- Computes avg_sl and avg_tp from exit reasons (lines 87-90). Pre-settlement exits won't be included in either bucket, which is correct but means the analytics won't reflect them. Consider whether a new stat is needed.
Decomposition Assessment
With blast-radius additions: 4 file targets across 1 repo, 5 AC. All changes are tightly coupled (add exit path + update consumers of exit_reason). Estimated agent work: under 5 minutes. No decomposition needed.
Recommendations
- [BODY] Fix file path:
app/concerns/game_phase.rb→app/services/game_phase.rb - [BODY] Add
app/services/scoreboard.rbto file targets -- must handlepre_settlement_exitin exit reason stats - [BODY] Add
app/views/dashboard/index.html.erbto file targets -- isWin logic at line 592 must classifypre_settlement_exitby pnl sign, not by string match - [BODY] Note in file targets that PositionMonitor must
include GamePhaseto accessgame_nearly_over? - [BODY] Rephrase AC4 to be agent-verifiable: "Games where pre-settlement exit fires produce no settlement_loss exit reasons" instead of outcome metric
- [SCOPE] Create user-stories section on project-botfire with bot-strategy story entry
- [SCOPE] Create architecture note arch-bots for the bots component
-
Review: Add evening-only time filter to Live Edge
review-1846-2026-07-10Verdict: NEEDS_REFINEMENT
The ticket is well-scoped and all code claims verify against the codebase. Two traceability backing notes are missing, preventing a READY verdict.
Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Feature Flag (none -- acceptable, no feature flag doc in repo)
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label -- present on board item
- [ ] story note MISSING -- [SCOPE] project-botfire has no user-stories section. Create user story entry on project-botfire user-stories section.
- [x] arch:bots label -- present on board item
- [ ] arch note MISSING -- [SCOPE] arch-bots note does not exist in pal-e-docs. project-botfire references it but the note was never created. Create architecture note arch-bots.
- [x] Forgejo issue -- ldraney/botfire#12, open
File Targets
- [x]
app/services/bot_engine.rb-- verified: exists (7.9k),execute_actiondefined at line 158, called at line 65 insideevaluate_bot. Natural gate insertion point is line 64 (before call) or top ofevaluate_botat line 44. - [x]
app/models/bot.rb-- verified: exists (319 bytes), simple model withsports_filterserialization. Noactive_hourscolumns in schema yet. Ticket Constraints section says aBOT_ACTIVE_HOURShash in bot_engine.rb is acceptable (no migration needed).
Repo Placement
OK. Issue says
ldraney/botfire, filed onldraney/botfire. Single-repo change, no cross-repo concerns.Dependencies
- Parent spike #11 (board item #1845) is complete, in QA column. This ticket was spawned from it.
- Sibling tickets #13 (item #1847) and #14 (item #1848) were also spawned from #11 but are independent -- no ordering dependency.
- No blocking items in in_progress column.
Acceptance Criteria
4 AC, all agent-verifiable:
- [x] "Live Edge does not fire on games outside 12pm-midnight MDT" -- testable with time stubbing
- [x] "Time window is configurable per bot" -- testable by checking hash/config structure
- [x] "Skipped trades are logged with reason 'outside active hours'" -- testable by capturing log output
- [x] "Existing trigger logic unchanged" -- testable by verifying trigger evaluation still called normally inside window
Test command
bin/rails testis valid. Existing test pattern intest/services/volatility_bot_test.rbprovides a fixture template. Nobot_engine_test.rbexists yet -- agent will create one.Note: Title says "evening-only" but AC defines 12pm-midnight MDT (wider than evening). The AC is the authoritative spec; the title is colloquial. Not a conflict.
Blast Radius
Low. The time gate is a pre-filter in
evaluate_botbefore any trigger evaluation or order placement. No downstream consumers affected. Dashboard (index.html.erb) correctly excluded from scope. App timezone is currently UTC (commented out inconfig/application.rb); the agent must use explicitTime.use_zone("America/Denver")orin_time_zonecalls rather than relying onTime.zone.Decomposition Assessment
No decomposition needed. 2 file targets in 1 repo, 4 AC, estimated agent work well under 5 minutes. Straightforward pre-filter gate pattern.
Recommendations
[SCOPE]Create user-stories section on project-botfire with the bot-strategy story entry.[SCOPE]Create architecture note arch-bots for the bots component (project-botfire already references it but the note does not exist).
-
Review: Tighten Live Edge ceiling from 92c to 89c
review-1848-2026-07-10Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- references Spike #11, supersedes #7
- [x] Repo -- ldraney/botfire
- [x] User Story
- [x] Context -- includes P&L data by price band
- [x] File Targets
- [x] Feature Flag -- none (appropriate for DB-only change)
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label present
- [ ] story note MISSING -- project-botfire page has no
user-storiessection. [SCOPE] Create user story entry on project-botfire user-stories section. - [x] arch:bots label present
- [ ] arch note MISSING -- no
arch-botsnote found in pal-e-docs. [SCOPE] Create architecture note arch-bots for component bots. - [x] Forgejo issue -- ldraney/botfire#14, state: open
File Targets
- [x]
app/services/bot_engine.rb-- verified: line 135 containsreturn false if trigger.condition_max.present? && value > trigger.condition_max.to_f. Issue correctly says NOT to touch this file. - [x]
db/seeds.rb-- verified: file exists but does NOT seed triggers (only users and bots). Issue correctly says "optionally if trigger defaults are seeded there" -- no change needed. - [x] DB change
Trigger.find_by(name: "Live Edge").update!(condition_max: 89)-- thecondition_maxcolumn exists (migration20260708130000_add_condition_max_to_triggers.rb, schema.rb line 107). The Trigger model references are consistent.
Repo Placement
OK. Issue specifies
ldraney/botfireand is filed onldraney/botfire. Single-repo change.Dependencies
- Supersedes Issue #7 (board item #1842, marked closed/superseded)
- Spawned from Spike #11 (board item #1845, currently in qa column, title: "Spike: Live Edge deep dive -- COMPLETED, spawned #12 #13 #14")
- No blockers identified. No items currently in_progress that this depends on.
- Sibling issues from same spike: #12 (board item #1846, 3pt) and #13 (board item #1847, 5pt) -- no blocking relationship.
Acceptance Criteria
3 criteria, all agent-verifiable:
condition_maxset to 89 -- verifiable via rails runner command provided in Test Expectations- No entries above 89c after deploy -- verifiable by monitoring BotFire records post-deploy
- Dashboard confirms new trades stay in 85-89c band -- verifiable via the analytics dashboard
Test command is real and correct:
bin/rails runner "puts Trigger.find_by(name: 'Live Edge').condition_max"Blast Radius
Low.
condition_maxis referenced in exactly 2 places:bot_engine.rb:135-- the check_late_game guard (already correct)db/schema.rb:107-- column definition
Only the Live Edge trigger uses the late_game trigger type. No other bots, services, or downstream consumers are affected. No sibling services share this pattern.
Decomposition Assessment
No decomposition needed. 1 effective file target (DB record update), 3 acceptance criteria, estimated agent time well under 5 minutes. This is a single rails runner command or a data migration.
Recommendation
[SCOPE]Createuser-storiessection onproject-botfirepage and add the "bot-strategy" user story entry.[SCOPE]Create architecture notearch-botsfor the bots component in pal-e-docs.
Core scope is solid. File targets verified, acceptance criteria testable, blast radius minimal. The two [SCOPE] items are traceability hygiene -- they do not affect the implementability of this ticket.
-
Re-Review: Analytics dashboard: find winning conditions
review-1841-2026-07-09-v2Verdict: APPROVED
Re-review of board item #1841 after refinement. Previous review (review-1841-2026-07-09) returned NEEDS_REFINEMENT with 6 recommendations. 5 of 6 addressed in this revision.
Previous Findings Resolution
- [x] [DECOMPOSE] ACs reduced from 8 to 5 — now within threshold (>5 triggers decomposition, 5 does not)
- [x] [SCOPE] arch-bots note — confirmed exists at slug
arch-bots(project: bots, sections: services, key files, order pattern, data model, infrastructure) - [ ] [SCOPE] story-bots-bot-strategy note — still listed as "(pending)" on project-botfire user-stories section. Not addressed, but story is defined inline on the project page. Project-level hygiene item, not a ticket blocker.
- [x] [BODY] Game phase mapping — deferred to follow-up ticket. No mention of game_phase in issue body or codebase. Clean removal.
- [x] [BODY] sport_type migration — clarified: "No migrations needed. sport_type is already on the games table and accessed via join (market.game.sport_type)." Verified in controller line 21:
sport: g.sport_typeand schema:t.string "sport_type"on games table. - [x] [BODY] Hypothesis persistence — specified: "Persisted to localStorage (key: botfire-hypothesis)." Explicit key name and rationale ("Ephemeral per browser, not shared — this is a scratchpad for the operator, not a database").
Template Completeness
- [x] Type — Feature
- [x] Lineage — project-botfire, board-bots, arch-bots
- [x] Repo — ldraney/botfire
- [x] User Story — "As a bot operator, I need to slice trade data by sport, entry price, and bot..."
- [x] Context — P&L data (-$25.41 total), 63% stop-losses, breakeven win rate calculation
- [x] File Targets — 3 files listed with clear change descriptions
- [x] Feature Flag — "None" (replaces existing dashboard, acceptable)
- [x] Acceptance Criteria — 5 criteria, well-structured
- [x] Test Expectations — 4 items, verifiable
- [x] Constraints — 5 constraints (single-page, no chart libs, responsive, auto-refresh, collapsible legacy sections)
- [x] Checklist — 8 items mapping to ACs
- [x] Related — 4 references (issues #1, #2, project page, arch note)
Traceability
- [x] story:bot-strategy label — "Bot operator identifies winning conditions"
- [ ] story note — story-bots-bot-strategy listed as "(pending)" on project-botfire. Story is defined inline (role: Bot operator, metric: Per-bot positive P&L trending upward over 50+ trades). [SCOPE] Create story note story-bots-bot-strategy — project-level hygiene, not blocking.
- [x] arch:bots label — bots architecture component
- [x] arch note verified — arch-bots note exists in pal-e-docs (project: bots, covers 7 services, key files, order pattern, data model, infrastructure)
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/botfire/issues/5, open
File Targets
- [x]
app/controllers/dashboard_controller.rb(99 lines) — verified: exists, hasdataendpoint with BotFire joins to bot, trigger, market, game. Already returnssport: g.sport_typevia join (line 21). Breakeven calculation already present (lines 86-95):breakeven_wr,avg_stop_loss,avg_take_profitfields returned in JSON response. - [x]
app/services/scoreboard.rb(99 lines) — verified: exists, hasbot_summarymethod with P&L, win/loss, stop-loss/take-profit counts. Issue correctly states "no changes needed." - [x]
app/views/dashboard/index.html.erb(642 lines) — verified: exists. Note: file has grown significantly from 261 lines (at previous review) to 642 lines. Current state already contains analytics tables, filter bar, canvas P&L chart, hypothesis textarea with localStorage, and cross-filter interactivity — suggesting implementation may have already begun or completed.
Repo Placement
OK — issue filed on ldraney/botfire, all file targets in ldraney/botfire. Single-repo scope.
Dependencies
- Board item #1839 (Rework Volatility Bot) — in QA column, deployed. Generates the trade data this ticket analyzes. Not a blocker.
- Board item #1840 (Rework Consensus Bot) — in backlog, ON HOLD. This ticket (#1841) is a prerequisite for #1840. Dependency direction correct and documented in Related section.
- No blockers. Dependencies documented.
Acceptance Criteria
5 criteria, all testable and specific:
- AC 1 (Analytics breakdown tables) — clear: P&L by sport, by price band (5c buckets defined), exit reason breakdown. Each table specifies columns: trades, W/L, win%, P&L.
- AC 2 (Breakeven threshold) — clear: calculated from actual avg stop-loss / avg take-profit ratio, purple threshold line, bar chart overlay. Not hardcoded.
- AC 3 (Cumulative P&L trend) — clear: canvas line chart, per-bot and total, color-coded.
- AC 4 (Cross-filter) — clear: sport, bot, price band buttons. Click to filter all views. Click table row to toggle filter.
- AC 5 (Hypothesis log) — clear: textarea, localStorage key
botfire-hypothesis, ephemeral per browser.
All criteria are agent-verifiable.
Blast Radius
- Dashboard changes are visually isolated (single page, no new routes).
- Scoreboard service used by bot services for console logging — no changes specified, no risk.
- Controller data endpoint is additive (breakeven fields added to existing JSON response).
- No migration, no model changes, no new dependencies.
Decomposition Assessment
No decomposition needed:
- File targets: 2 active files in 1 repo (controller + view; scoreboard unchanged) — within threshold.
- Acceptance criteria: 5 — at threshold, not exceeding (>5 triggers decomposition).
- Estimated agent work: Single-page frontend rework with backend data endpoint enhancement. No migrations, no new routes, no external dependencies. Fits in one agent pass.
Observation
The dashboard view has grown from 261 lines to 642 lines since the previous review. The current codebase already contains implementations matching all 5 ACs: analytics breakdown tables with breakeven bars, canvas P&L trend chart, cross-filter interactivity, and hypothesis log with localStorage persistence. The controller already returns
breakeven_wr,avg_stop_loss, andavg_take_profit. If implementation is complete, this ticket may be ready for QA validation rather than agent implementation.Recommendations
[SCOPE]Create story notestory-bots-bot-strategy— currently "(pending)" on project-botfire. Project-level hygiene; not blocking this ticket.
No other action needed. Scope is solid, all previous [BODY] and [DECOMPOSE] findings addressed.
-
Review: Analytics dashboard: find winning conditions
review-1841-2026-07-09Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Feature Flag -- "None" (replaces existing dashboard, acceptable)
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:bot-strategy label -- "Bot operator identifies winning conditions"
- [ ] story note MISSING -- story-bots-bot-strategy listed as "(pending)" on project-botfire user-stories section. [SCOPE] Create story note story-bots-bot-strategy.
- [x] arch:bots label -- bots architecture component
- [ ] arch note MISSING -- search for arch-bots returned no results in pal-e-docs. [SCOPE] Create architecture note arch-bots for the bots component.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/botfire/issues/5, open
File Targets
- [x]
app/controllers/dashboard_controller.rb-- verified: exists (2.5k), hasdataendpoint querying BotFire with joins to bot, trigger, market, game. Already accessesg.sport_typevia join. - [x]
app/services/scoreboard.rb-- verified: exists (3.0k), hasbot_summarymethod with P&L, win/loss, stop-loss/take-profit counts. Good foundation to extend. - [x]
db/migrate/-- verified: directory exists with 13 migrations. - [ ]
db/migrate/-- ISSUE: Issue mentions addingsport_typetobot_fires, butsport_typealready exists ongamestable and the controller already accesses it via join (g.sport_type). Clarify whether denormalization is intended or join-based computation suffices. [BODY] - [ ]
db/migrate/-- ISSUE:game_phase_at_entryis proposed but "game phase" is not defined in terms of existing data. Thegamestable hasperiod(string) andclock(string) fields. How to map these to early/mid/late across 5 different sports (each with different period structures -- innings, quarters, halves, periods) needs explicit definition. [BODY] - [x]
app/views/dashboard/index.html.erb-- verified: exists (261 lines), live position monitoring dashboard with dark theme, summary cards, bot cards, open-positions table.
Repo Placement
OK -- issue filed on ldraney/botfire, all file targets in ldraney/botfire. Single-repo scope.
Dependencies
- Board item #1839 (Rework Volatility Bot) -- in QA column, deployed. Generating the trade data this ticket will analyze. Not a blocker.
- Board item #1840 (Rework Consensus Bot) -- in backlog, ON HOLD pending analytics. This ticket (#1841) is a prerequisite for #1840. Dependency direction is correct and documented in Related section ("Issue #2 -- Consensus Bot rework (ON HOLD until analytics prove what to amplify)").
- No blockers for this ticket. Dependencies are documented.
Acceptance Criteria
8 acceptance criteria. Most are testable, with these concerns:
- AC 3 (P&L by Game Phase) -- "early/mid/late" mapping from existing
period/clockfields is undefined. Different sports have different period structures (innings, quarters, halves, periods). An agent cannot implement this without a definition. - AC 7 (Cross-filter) -- complex interactive behavior: "click a sport, price band, or game phase to filter all other views." Significant frontend engineering effort, especially with no chart libraries allowed.
- AC 8 (Hypothesis Log) -- persistence mechanism undefined. Is this localStorage, a new DB table, or ephemeral? "Simple text area or section" is too vague for agent implementation.
Blast Radius
- Dashboard changes are visually isolated (single page, no new routes).
- Scoreboard service is used by bot services for console logging. Extending with analytics methods is additive and non-breaking.
- BotFire model is used by 6 services (volatility_bot, consensus_bot, bot_engine, position_monitor, portfolio_grouper, scoreboard). Adding columns via migration is non-breaking.
- No chart libraries constraint means all visualization (6+ chart types including line charts, bar charts, tables with breakeven lines) must be custom HTML/canvas. This is the highest-effort constraint.
Decomposition Assessment
NEEDS DECOMPOSITION -- fails the 5-minute rule on multiple criteria:
- File targets: 3-4 files in 1 repo -- borderline acceptable.
- Acceptance criteria: 8 criteria -- exceeds the >5 threshold.
- Estimated agent work: Well over 15 minutes. Backend analytics endpoint + 6 chart views (all custom HTML/canvas, no libraries) + cross-filter interactivity + responsive design + hypothesis log + possible migration + tests. At minimum 3 separate agent passes.
Suggested decomposition into 3 sub-tickets:
- Analytics backend + static tables (3pt) -- analytics endpoint with P&L breakdowns by sport, price band, game phase, exit reason. Static HTML tables with breakeven threshold. AC 1-5 data + display.
- Cumulative P&L trend chart (2pt) -- custom canvas line chart per bot showing P&L over time. AC 6. Responsive.
- Cross-filter + hypothesis log (3pt) -- AC 7-8: interactive filtering across all views and hypothesis persistence. Depends on tickets 1 and 2.
Recommendations
[DECOMPOSE]8 AC across backend analytics + 6 custom chart types + cross-filter interactivity + hypothesis log. Route toskill-decompose-ticket.[SCOPE]Create architecture notearch-botsfor the bots component.[SCOPE]Create story notestory-bots-bot-strategy(currently listed as pending on project-botfire).[BODY]Define "game phase" mapping: how doperiodandclockfields on thegamestable map to early/mid/late for each sport type (baseball innings, basketball quarters, football quarters, hockey periods, soccer halves)?[BODY]Clarifysport_typemigration: the field already exists ongamestable and is accessible via join. State explicitly whether denormalization tobot_firesis intended or join-based access suffices.[BODY]Define hypothesis log persistence: localStorage, new DB table, or ephemeral?
-
Re-Review: Rework Volatility Bot: small-bump strategy
review-1839-2026-07-09-v2Verdict: READY
Re-review after refinement. All 3 issues from
review-1839-2026-07-09are resolved: issue body restructured to matchtemplate-issue-feature,project-botsproject page created with user-stories section, andarch-botsarchitecture note created.Template Completeness
Checked against
template-issue-feature(Type = Feature):- [x]
### Type— "Feature" - [x]
### Lineage— "Standalone — identified during initial bot deployment when Volatility Bot produced 0 fires due to flawed entry logic." - [x]
### Repo—ldraney/bots - [x]
### User Story— "As a bot operator / I want the Volatility Bot to independently find and trade... / So that I can profit from normal game momentum swings..." - [x]
### Context— Detailed explanation of 4 current problems plus reworked strategy with entry/exit conditions - [x]
### File Targets— Clear "modify" (1 file) and "do NOT touch" (3 files) sections - [x]
### Feature Flag— "None — this is a rework of an existing bot service." - [x]
### Acceptance Criteria— 7 checkbox items - [x]
### Test Expectations— 5 items including paper mode and live mode stages, with run command - [x]
### Constraints— Patterns to follow, IOC order requirements, constants to update - [x]
### Checklist— 3 items (PR, paper mode, no unrelated changes) - [x]
### Related— References project and ldraney/bots#2
Traceability
- [x] story:bot-strategy label — present on board item
- [x] story note verified — found in project-bots user-stories section: key=bot-strategy, role="Bot operator", success metric="Per-bot win rate > 55%, positive P&L after fees"
- [x] arch:bots label — present on board item
- [x] arch note verified —
arch-botsnote exists in pal-e-docs (tagged architecture+active, project=bots). Covers 7 services, key files, order pattern, data model, infrastructure. - [x] Forgejo issue — ldraney/bots#1, state=open
File Targets
- [x]
app/services/volatility_bot.rb— verified: exists (7.4k), containsfind_hedgeable_fires(L179),TAKE_PROFIT_MULTIPLIER = 3(L6),HEDGE_CONTRACTS = 3(L5),scan_for_hedges(L166). All patterns match issue description. - [x]
app/services/position_monitor.rb— verified: exists (4.3k), excludes Volatility Bot at L34. Correctly marked no-change. - [x]
app/services/bot_engine.rb— verified: exists (8.7k),game_nearly_over?at L137. Correctly marked no-change. Issue plans to reuse this logic (inverted) inside volatility_bot.rb. - [x]
app/controllers/dashboard_controller.rb— verified: exists (2.5k). Correctly marked no-change.
Repo Placement
OK. Forgejo issue is on
ldraney/bots. All changes targetapp/services/volatility_bot.rbin the same repo. No cross-repo concerns.Dependencies
Board has 2 items: #1839 (this) and #1840 (Rework Consensus Bot). Both in backlog. No dependency between them — volatility bot operates independently of consensus bot. Issue's Related section correctly documents ldraney/bots#2 as independent. No blockers identified.
Acceptance Criteria
7 criteria total. All are code-level verifiable:
- [x] "Scans markets independently" — verify code doesn't call
find_hedgeable_fires - [x] "Entries only early/mid-game with yes_bid >= 0.90 and no_ask <= 0.15" — verify entry conditions
- [x] "Spread check (no_ask - no_bid <= 0.07)" — verify MAX_SPREAD logic
- [x] "Take-profit exits on 5c bump" — verify TAKE_PROFIT_BUMP constant and exit logic
- [x] "Stop-loss at 50% of entry" — verify STOP_LOSS_FLOOR constant and exit logic
- [x] "Time-stop at final 20%" — verify time-stop exit path
- [x] "Settlement logic unchanged" — verify NO-position math preserved
Previous review flagged a non-verifiable operational criterion ("5 paper fires"). This has been correctly moved to Test Expectations, and all AC are now code-level verifiable.
Blast Radius
Clean. No references to
find_hedgeable_fires,TAKE_PROFIT_MULTIPLIER, orscan_for_hedgesin any file outsidevolatility_bot.rb.consensus_bot.rbhas no volatility references.position_monitor.rbalready excludes Volatility Bot fires (L34).bot_engine.rbownsgame_nearly_over?but issue correctly plans a new method inside volatility_bot.rb rather than modifying shared code.Decomposition Assessment
1 file target in 1 repo. 7 acceptance criteria (exceeds the 5-AC guideline). Estimated agent work: ~5 minutes — single file rewrite with well-defined entry/exit logic. All 7 AC modify the same file and are tightly coupled (entry conditions, exit conditions, constants). Decomposition is not warranted because the work is cohesive within a single service file. No decomposition needed.
Recommendation
No action needed.
- [x]
Architecture 2
-
Domain Model: BotFire
arch-domain-botsDomain Model: BotFire
Diagram
erDiagram USER ||--o{ BOT : "owns" BOT ||--o{ BOT_FIRE : "executes" BOT ||--o{ TRIGGER : "evaluates" GAME ||--o{ MARKET : "has" MARKET ||--o{ MARKET_SNAPSHOT : "captures" MARKET ||--o{ BOT_FIRE : "traded on" USER { string kalshi_api_key_id string kalshi_private_key_path boolean paper_mode } BOT { string name boolean paper_mode boolean active } TRIGGER { string condition_field string condition_operator float condition_value float condition_max string action string trigger_type } GAME { string milestone_id PK string event_ticker string sport_type string title string live_status integer period string clock integer home_score integer away_score } MARKET { string ticker PK float yes_ask float yes_bid float last_price string status datetime close_time } MARKET_SNAPSHOT { float yes_ask float yes_bid float last_price datetime captured_at } BOT_FIRE { float entry_price float exit_price float pnl string exit_reason string position_status integer count }Components
Component Purpose Notes User Kalshi account holder Credentials for API auth signing Bot Trading strategy instance Only "Live Edge" active; Volatility/Consensus disabled Trigger Entry condition condition_field + operator + value + max defines price band Game Sporting event sport_type: baseball_game, basketball_game, football_game, hockey_match, soccer_tournament_multi_leg Market Tradeable contract 2 per game (one per team). Ticker prefix = league (KXMLB, KXWNBA, KXLMB, KXNPB, KXKBO) MarketSnapshot Point-in-time price Captured every scanner tick for analytics BotFire Trade record Full lifecycle: entry, open, exit (stop_loss, take_profit, pre_settlement_exit, settlement_win, settlement_loss) Key Decisions
- One BotFire per trade. Entry creates BotFire with position_status "open". Exit updates same record with exit_price, pnl, exit_reason.
- Game-winner markets only. real_game_market? filters to tickers containing "GAME", ignoring spreads/totals/props.
- sport_type from Kalshi milestones. Scanner maps milestone types directly. GamePhase module uses sport_type for period/clock interpretation.
- condition_max on Trigger. Creates a ceiling (89c) on the entry band, preventing entries above the profitable range.
Related
project-botfire— project pagearch-dataflow-bots— data flow (TODO)arch-deployment-bots— deployment (TODO)
-
Architecture: BotFire Bot Engine
arch-botsArchitecture: Bots
Kalshi live sports betting platform — Rails 8.1 + SQLite on Docker Compose.
Services (7 Docker Compose containers)
Service Role Interval web Rails on port 3003, live dashboard — scanner Polls milestones, fetches markets, refreshes prices 1s bot_engine Evaluates triggers, places orders 1s position_monitor Stop-loss / take-profit / settlement exits 5s consensus_bot Detects 2+ bot agreement, reinforces 10s portfolio_grouper Groups positions by sport/time/confidence 60s volatility_bot Buys underdog (NO side) as hedge 10s Key Files
app/services/bot_engine.rb— Core trigger evaluation loopapp/services/position_monitor.rb— Exit monitoring (excludes Volatility Bot)app/services/volatility_bot.rb— Independent underdog strategyapp/services/consensus_bot.rb— Multi-bot agreement detectorapp/services/scanner.rb— Kalshi API pollerapp/services/kalshi_client.rb— API client (auth, orders, balance)app/controllers/dashboard_controller.rb— JSON API for live dashboard
Order Pattern
All bots use IOC (immediate-or-cancel) orders. Critical invariants:
- Check
fill_count > 0before creating BotFire records - Use
average_fill_pricefrom API response, not the order price - Per-bot contract counts via
BOT_CONTRACTShash in bot_engine.rb
Data Model
Game → Market → MarketSnapshot (price history). Bot → Trigger → BotFire (position tracking with entry/exit/pnl).
Infrastructure
- Host: archbox (10.0.0.149)
- Dashboard: port 3003, tunneled via Tailscale
- Secrets:
~/secretsmounted read-only into containers - DB: SQLite (single-writer, no external DB service)
Project Page 1
-
Project: BotFire
project-botfireProject: BotFire
Vision
Automated Kalshi sports betting platform focused on finding and proving profitable bot strategies through rigorous trend analytics. Success = bots that trend upward over time with reliable upside and mitigated risk. Every strategy must prove itself with real P&L data before scaling.
Core principles:
- Data before amplification — don't scale a strategy until analytics prove it has a real dollar-positive edge, not just a win rate
- No bullshit numbers — report actual P&L vs cost basis, never oversell performance
- Test before deploy — verify code works end-to-end before claiming deployed
- Track everything — every bet must have full visibility: entry, exit, P&L, reason, bot, game
- Cut losers fast — if analytics show a bot is underwater, disable it
Current Focus: Live Edge Only
All bots disabled except Live Edge (2026-07-10, PR #10). Analytics proved none of the other bots were dollar-positive. Live Edge contracts doubled from 12 to 24.
Why Live Edge: 85-89c entry band shows 61.5% WR (above 46.6% breakeven), P&L trending upward in second half of trades (+$1.41). Only bot with a positive trend.
Disabled Bots
Bot WR P&L Why Disabled Lucas Bot 17.9% -$13.95 Proven loser, no late-game filter Lucas Late Game 38.5% -$20.16 Catastrophic settlement losses at 90c+ entries Consensus Bot 44.4% -$5.14 One -$5.40 settlement loss wiped all gains Portfolio Grouper 40% -$2.70 One settlement loss, too few trades Volatility Bot — $0 0 fires after rework Marcus Bot 0% -$0.42 Paper mode only Architecture
See arch-bots for service inventory, key files, order patterns, and infrastructure.
Rails 8.1 + SQLite, 7 Docker Compose services. IOC orders only,
fill_count > 0invariant,average_fill_pricefor all entry/exit tracking.Board
board-bots(BotFire Sprint)User Stories
Board items on
board-botstrace to these stories viastory:<name>labels.Story Label User Story story:bot-strategyAs a bot operator, I want to identify and exploit profitable trading conditions so that my automated bots generate consistent positive P&L. This covers all Live Edge optimization work — entry price tuning, time-of-day filtering, exit strategy improvements, and new strategy development based on analytics. Status
Balance: ~$322. All-time P&L: ~-$44 (mostly from disabled bots). Only Live Edge active at 24 contracts.
Milestones
- M1: Analytics dashboard — DONE (PR #6)
- M1.5: Focus on Live Edge, disable losers — DONE (PR #10)
- M2: Confirm Live Edge edge over 100+ trades
- M3: Build alternative strategies based on Live Edge's success patterns
Repos
Repo Platform Role Status ldraney/botfire Forgejo Application monorepo Active
Board 1
-
BotFire Sprint
board-botsNo content