Review: Late-Game Lock: entry logic + OrderService integration

review-1749-2026-07-04-r2 Review

review ready

Verdict: READY

Template Completeness

  • [x] Type -- Feature
  • [x] Lineage -- Sub-ticket of #4, second of 3 from decomposition
  • [x] Repo -- ldraney/prediction-assistant
  • [x] User Story -- present, well-formed
  • [x] Context -- thorough explanation of bot behavior and pattern references
  • [x] File Targets -- 3 files to modify/create, 3 files NOT to touch
  • [x] Feature Flag -- "none" with explanation (no flag infra, config controls activation)
  • [x] Acceptance Criteria -- 5 criteria, all testable
  • [x] Test Expectations -- unit tests, edge cases, dry-run, run command
  • [x] Constraints -- 4 constraints documented
  • [x] Checklist -- present
  • [x] Related -- project, parent issue, dependencies listed

Traceability

  • [x] story:watchdog-trading label -- Watchdog Trading
  • [x] story note verified -- story-prediction-assistant-watchdog-trading exists (active, user-story type); entry found in project-prediction-assistant user-stories section
  • [x] arch:rails label -- Rails Architecture
  • [x] arch note verified -- arch-rails note exists in pal-e-docs (active, architecture type)
  • [x] Forgejo issue -- ldraney/prediction-assistant#68, open

File Targets

  • [x] app/services/late_game_lock_bot.rb -- new file (correct, to be created)
  • [x] app/services/late_game_lock_evaluator.rb -- file to modify; does not exist yet but depends on #66 (event dispatch sub-ticket, board item #1747 in todo column). Dependency correctly documented in Lineage. When #66 completes, this target will be valid.
  • [x] test/services/late_game_lock_bot_test.rb -- new file (correct, to be created)
  • [x] app/services/order_service.rb -- exists (14k, verified API: place_order(bot:, ticker:, side:, quantity:, price:))
  • [x] app/services/position_tracker.rb -- exists (11k, verified API: open_positions(user:, bot:), conflict?(user:, ticker:, side:, exclude_strategy:))
  • [x] app/models/late_game_lock_config.rb -- exists (1.1k, has bet_size and max_concurrent_positions in REQUIRED_CONFIG_KEYS)

Repo Placement

OK. Issue #68 is filed on ldraney/prediction-assistant and all file targets are in that same repo. Single repo, no cross-repo concerns.

Dependencies

  • Blocking: Board item #1747 (issue #66, "Late-Game Lock: event dispatch wiring + dual-trigger evaluator") -- in todo column. Creates the LateGameLockEvaluator that this ticket modifies. Correctly documented in Lineage section.
  • Merged: Board item #1710 (#23, OrderService) -- in validation column. File exists and API verified.
  • Merged: Board item #1711 (#24, PositionTracker) -- in validation column. File exists and API verified.

Acceptance Criteria

5 ACs, all verified against actual codebase APIs:
  • [x] AC1: OrderService.place_order signature matches -- takes bot:, ticker:, side:, quantity:, price:
  • [x] AC2: PositionTracker.open_positions(bot: config) works -- user: defaults to nil
  • [x] AC3: config.trades.where(ticker:, status: %w[pending filled]).exists? matches PregameStackerBot#already_trading? pattern (line 132-134)
  • [x] AC4: PositionTracker.conflict?(ticker:, side:, exclude_strategy:) API matches -- user: defaults to nil
  • [x] AC5: Logging pattern Rails.logger.info("[LateGameLock] ...") consistent with PregameStackerBot convention
All criteria are agent-verifiable via unit tests.

Blast Radius

Limited. New bot service following the established PregameStackerBot pattern. No changes to shared services (OrderService, PositionTracker). Error handling pattern (rescue DuplicateOrderError, OrderError) is consistent with existing consumers (PregameStackerBot in app/services/pregame_stacker_bot.rb lines 201-205, BulkSweepJob in app/jobs/bulk_sweep_job.rb lines 169-176). No downstream consumers affected.

Decomposition Assessment

3 file targets in 1 repo, 5 ACs, straightforward pattern-following implementation. Well under all thresholds (<3 files across >2 repos, <=5 ACs, estimated <5 min agent work). No decomposition needed.

Recommendation

No action needed.