Phase 7 reviewer (Sonnet, combined backend + frontend) flagged 3 🟡 warnings; two real fixes here, one tracking: W1 — DetectAnomaliesUseCase had an undocumented N+1: _anomalyRepo.ListAsync was called inside ProcessEventAsync, once per event. Hoisted to ExecuteAsync before the loop and threaded into ProcessEventAsync as a parameter. The per-event slice happens in-memory now. O(N_events) DB round-trips → 1. W2 — AnomalyDetector.ExtractMatchWinProbabilities had a dead expression '(decimal?)null ?? 0m' that always evaluated to 0m. Simplified to 'drawBet is not null ? rawDraw / total : 0m'. The 0m is never surfaced anyway (PDraw in the return uses the same null guard), so behaviour is identical. W3 — PLAN.md row updated with both Phase 7 commit hashes (a6ff368backend +12208a4frontend) and review verdict. Build 0/0, 276 tests still passing.
9.7 KiB
Feature: Initial Implementation (maraphon-app)
Branch: feature/initial-implementation
Base branch: main
Created: 2026-05-05
Status: 🟡 In Progress
Strategy: Big Bang
Mode: Automated
Execution: Orchestrator
Implementer models: Sonnet 4.6 (backend) · Opus (frontend, with frontend-design skill)
Reviewer model: Sonnet 4.6
Summary
Build the maraphon-app end-to-end: a Blazor Hybrid (.NET 8 + WPF) sports-betting odds analyzer that scrapes marathonbet.by, persists snapshots in SQLite, exports to Excel matching the customer spec, and detects coefficient-flip anomalies. Architecture is Clean Architecture with all UI in a Razor Class Library so the host can later swap to ASP.NET Core Blazor Server with no UI rewrite. RU + EN localization, every variable parameter configurable.
Build & Test Commands
- Build:
dotnet build Marathon.sln - Test:
dotnet test Marathon.sln - Lint:
dotnet format Marathon.sln --verify-no-changes - Run:
dotnet run --project src/Marathon.Hosts.WpfBlazor
Big Bang strategy: Build/tests are NOT run for intermediate phases (Phases 0–8). The full build + test suite must pass at Phase 9 before final review. An exception: a
dotnet buildcompile-only smoke check is allowed after each phase to catch syntax/type errors early — this is faster than running tests and consistent with Big Bang ("we don't run tests until the end").
Phases
- Phase 0: Scraping spike (research, throwaway) [domain: backend] → subplan
- Phase 1: Solution skeleton + Domain model [domain: backend] → subplan
- Phase 2: Infrastructure — Storage [domain: backend] → subplan
- Phase 3: Infrastructure — Scraping [domain: backend] → subplan
- Phase 4: Application layer + Background workers [domain: backend] → subplan
- Phase 5: Blazor Hybrid host + Theme + i18n [domain: frontend] → subplan
- Phase 6: Event browsing UI [domain: frontend] → subplan
- Phase 7: Anomaly detection [domain: fullstack] → subplan
- Phase 8: Results loader [domain: fullstack] → subplan
- Phase 9: Packaging + polish (final phase — full build + tests required) [domain: fullstack] → subplan
Parallelization Plan (Orchestrator mode)
| Round | Phases | Notes |
|---|---|---|
| 1 | Phase 0 | Spike — gating research, no parallelism |
| 2 | Phase 1 | Domain — must finish before Phases 2/3/5 |
| 3 | Phases 2, 3, 5 in parallel | Storage, Scraping, UI Shell — disjoint files |
| 4 | Phase 4 | Application + Workers — depends on 2 + 3 |
| 5 | Phase 6 | Event UI — depends on 4 + 5 |
| 6 | Phase 7 | Anomaly detection — depends on 6 |
| 7 | Phase 8 | Results loader — depends on 6 |
| 8 | Phase 9 | Packaging — final, runs full build + tests |
Phase Progress Log
| Phase | Domain | Status | Review | Build | Committed |
|---|---|---|---|---|---|
| Phase 0: Scraping spike | backend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ⏭️ N/A (research) | ✅ 070e34b |
| Phase 1: Solution + Domain | backend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 96/96 Domain tests | ✅ 61114ea |
| Phase 2: Storage | backend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 77/77 Infra tests | ✅ batch (e4d8476…686550d…+) |
| Phase 3: Scraping | backend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 77/77 Infra tests | ✅ batch (e4d8476…686550d…+) |
| Phase 4: Application + Workers | backend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 202/202 tests | ✅ 2acbaa5 |
| Phase 5: Host + Theme + i18n | frontend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 11/11 UI tests | ✅ batch (e4d8476…686550d…+) |
| Phase 6: Event browsing UI | frontend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 228/228 tests | ✅ 553db2b |
| Phase 7: Anomaly detection | fullstack | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 276/276 tests | ✅ a6ff368 + 12208a4 |
| Phase 8: Results loader | fullstack | ⬜ Not Started | ⬜ | ⏭️ Big Bang | ⬜ |
| Phase 9: Packaging + polish | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
Final Review
- Comprehensive code review (final-reviewer agent)
- Security review (auth N/A, but covers scraping HttpClient, file I/O, user input)
- Full build passes
- Full test suite passes
- User merge approval
- Merged to
main
Resume Notes (2026-05-05 — paused at end of parallel batch P2/P3/P5)
Where we left off:
The parallel batch (Phases 2, 3, 5) completed code-wise. Phase 5 was killed near the
end of its "verify build" step. All files are committed as a single WIP snapshot
on feature/initial-implementation so nothing is lost. No reviewer ran on this batch
yet, and the solution does NOT build cleanly — there are known cross-phase compile
issues to resolve before review.
Tomorrow's action list (in order):
git pull(or just verify branch) — confirm we're onfeature/initial-implementationat the WIP commit.- Run
dotnet build Marathon.slnto capture the current error set as a baseline. - Resolve known cross-phase compile issues:
- Phase 2 ↔ Phase 3: Phase 2's repository classes are
internal; Phase 3'sMarathon.Infrastructure.Testsreferences them directly. Fix: add<InternalsVisibleTo Include="Marathon.Infrastructure.Tests" />tosrc/Marathon.Infrastructure/Marathon.Infrastructure.csproj. (Or make the repos public — choose by reading the actual csproj first.) - Phase 5:
LocalizationOptionsnamespace ambiguity (Microsoft.AspNetCore vs Microsoft.Extensions). Fix in WPF host or UI project — qualify or alias. - Phase 5: Serilog API mismatch in WPF host (likely
UseSerilogextension not found because Serilog.Extensions.Hosting wasn't pulled in transitively via the right namespace, OR the API call site uses an older Serilog API).
- Phase 2 ↔ Phase 3: Phase 2's repository classes are
- Once
dotnet build Marathon.slnis green:- Run
dotnet test Marathon.slnto see how many tests pass. - Spawn the phase-reviewer agent (Sonnet) to review the parallel batch as a
single combined review (Phase 2 + 3 + 5 diff). Pass
git diff 144c936...HEAD. - Address blocker findings; re-review until pass.
- Run
- After review passes, finalize with one or more clean commits (the WIP commit
can be
git reset --softto base and re-committed cleanly per phase, OR left as-is and the review passes apply). Update PLAN.md tracking rows for P2/P3/P5 to ✅ Done with commit hashes. - Move to Phase 4 (Application + Workers — backend, Sonnet 4.6). Phase 4
composes the per-module DI extensions (
PersistenceModule.AddMarathonPersistenceandScrapingModule.AddMarathonScraping) into a top-levelMarathon.Infrastructure/DependencyInjection.csand addsBackgroundServicepollers (UpcomingEventsPoller,LiveOddsPoller, plus a futureResultsWatchListPollerper the Phase 8 amendment).
Useful pointers:
- Phase 2 implementer report: see
tasks/a56ecc5e24bd7ea43.output(don't read — context-heavy; the summary is in the conversation transcript). - Phase 3 implementer report: agent ID
a8a537ba5721fba3d. Same caveat. - Phase 5 implementer was killed; final state is the WIP commit. The agent had finished implementation and was about to verify build — assume code is ~95% complete but unreviewed.
- All 3 phase subplans have their
## Handoff to Next Phasesections filled. - Cross-phase issues already documented in the conversation by the parallel agents — see Phase 2 and Phase 3 reports for the specifics.
Do NOT:
- Reset/discard the WIP commit without first reading what's in it.
- Skip the cross-phase fix step — Phase 4 cannot proceed against a broken build.
- Move to Phase 4 before reviewing the P2/P3/P5 batch.
Amendment Log
Amendment 1 — 2026-05-05 — Phase 8 strategy change (deferred — formal approval will be requested when Phase 8 begins)
Type: Modify upcoming phase (Phase 8 — Results loader)
What changed: Phase 8's original subplan assumed marathonbet.by exposes a public
results / archive page that we can scrape to back-fill EventResults. Phase 0 spike
proved this endpoint does NOT exist (/su/results returns 404).
Why: Spike findings — see spike/SCRAPE_FINDINGS.md and the deviation note in
plans/initial-implementation/phase-0-scraping-spike.md (Handoff section).
New approach (to be formalised when Phase 8 begins): Maintain a "watch list" of
events whose ScheduledAt + EstimatedDuration is in the past but whose status is not
Completed. Poll those event-detail URLs every 5 min until either
eventJsonInfo.matchIsComplete=true (capture resultDescription, mark complete) or
the URL 404s (mark ResultUnknown). Optional fallback to flashscore/sofascore is a
Phase 8 design decision.
Impact on existing phases: Phase 4 (Application + Workers) may need a new
ResultsWatchListPoller : BackgroundService in addition to the previously planned
UpcomingEventsPoller and LiveOddsPoller. Phase 2 schema may need a WatchStatus
field on Event (Pending | InWatchList | Completed | ResultUnknown). Both will be
re-evaluated when Phase 8 starts.
Status: Logged — formal subplan revision and user approval will be requested at the start of Phase 8 (per skill rule: "All amendments require explicit user approval before taking effect"). Phases 1–7 do not depend on Phase 8's tactical implementation.