Files
maraphon-app/plans/initial-implementation/PLAN.md
T
alexei.dolgolyov 828dcf5a08 fix(phase-7): close review notes — hoist anomaly dedup query, drop dead expr
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 (a6ff368 backend
  + 12208a4 frontend) and review verdict.

Build 0/0, 276 tests still passing.
2026-05-05 13:46:34 +03:00

172 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 08).
> The full build + test suite must pass at Phase 9 before final review.
> An exception: a `dotnet build` *compile-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
- [x] Phase 0: Scraping spike (research, throwaway) [domain: backend] → [subplan](./phase-0-scraping-spike.md)
- [x] Phase 1: Solution skeleton + Domain model [domain: backend] → [subplan](./phase-1-solution-and-domain.md)
- [x] Phase 2: Infrastructure — Storage [domain: backend] → [subplan](./phase-2-storage.md)
- [x] Phase 3: Infrastructure — Scraping [domain: backend] → [subplan](./phase-3-scraping.md)
- [x] Phase 4: Application layer + Background workers [domain: backend] → [subplan](./phase-4-application-and-workers.md)
- [x] Phase 5: Blazor Hybrid host + Theme + i18n [domain: frontend] → [subplan](./phase-5-host-theme-i18n.md)
- [x] Phase 6: Event browsing UI [domain: frontend] → [subplan](./phase-6-event-browsing-ui.md)
- [x] Phase 7: Anomaly detection [domain: fullstack] → [subplan](./phase-7-anomaly-detection.md)
- [ ] Phase 8: Results loader [domain: fullstack] → [subplan](./phase-8-results-loader.md)
- [ ] Phase 9: Packaging + polish (final phase — full build + tests required) [domain: fullstack] → [subplan](./phase-9-packaging-polish.md)
## 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):**
1. `git pull` (or just verify branch) — confirm we're on `feature/initial-implementation`
at the WIP commit.
2. Run `dotnet build Marathon.sln` to capture the current error set as a baseline.
3. **Resolve known cross-phase compile issues:**
- **Phase 2 ↔ Phase 3:** Phase 2's repository classes are `internal`; Phase 3's
`Marathon.Infrastructure.Tests` references them directly. Fix: add
`<InternalsVisibleTo Include="Marathon.Infrastructure.Tests" />` to
`src/Marathon.Infrastructure/Marathon.Infrastructure.csproj`. (Or make the
repos public — choose by reading the actual csproj first.)
- **Phase 5:** `LocalizationOptions` namespace 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 `UseSerilog` extension
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).
4. Once `dotnet build Marathon.sln` is green:
- Run `dotnet test Marathon.sln` to 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.
5. After review passes, finalize with one or more clean commits (the WIP commit
can be `git reset --soft` to 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.
6. Move to **Phase 4** (Application + Workers — backend, Sonnet 4.6). Phase 4
composes the per-module DI extensions (`PersistenceModule.AddMarathonPersistence`
and `ScrapingModule.AddMarathonScraping`) into a top-level
`Marathon.Infrastructure/DependencyInjection.cs` and adds `BackgroundService`
pollers (`UpcomingEventsPoller`, `LiveOddsPoller`, plus a future
`ResultsWatchListPoller` per 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 Phase` sections 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 `EventResult`s. 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 17 do not depend on Phase 8's tactical implementation.