553db2bce3
Replaces PreMatch/Live placeholder pages with a shared EventListShell
(filter chips, date range, sortable virtualized-friendly table, debounced
search, live auto-refresh with odds-movement indicators) and adds a new
/events/{eventCode} detail page (asymmetric header lockup, dynamic
Match/Period tabs, Plotly.Blazor odds-over-time chart with accessible
data-table fallback, snapshot history, Excel export modal).
New primitives matching Phase 5's editorial-quant system:
- SportIcon: inline SVGs per sport (basketball=6, football=11,
tennis=22723, hockey=43658, generic fallback)
- OddsCell: tabular mono with ▲/▼/— delta + flash on change
(prefers-reduced-motion honored)
- OddsTimeline: Plotly.Blazor wrapper with theme-aware colors and
<details>/<summary> data-table screen-reader fallback
- ExportDialog: From/To pickers + ExportKind radio + Esc/Enter
keyboard, surfaces use-case errors inline
- EventListShell: shared section shell for PreMatch/Live cadence
State + service split keeps the RCL host-agnostic:
- IEventBrowsingService / EventBrowsingService — wraps repos, returns
view-model records (EventListItem, EventDetail, EventScopeBoard,
BetRow, OddsTimelinePoint, SnapshotHistoryEntry); pages never see
EF or domain entities directly.
- EventBrowsingState — singleton (per-circuit in BlazorWebView) holding
immutable PageFilter records for PreMatch and Live.
Plotly.Blazor 5.4.1 added (latest .NET 8 line; 7.x has breaking changes).
+59 RU/EN localization keys following the Phase 5 dot-segmented convention.
Tests: +26 bUnit tests (PreMatch/Live/Detail pages, OddsCell/SportIcon/
ExportDialog components, EventBrowsingState). Total 228/228 passing
(Domain 96 + Application 15 + Infrastructure 80 + UI 37; baseline 202).
Build clean (0/0).
PLAN.md: P2/P3/P5 top-level checkboxes ticked; P6 row marked Done.
172 lines
9.6 KiB
Markdown
172 lines
9.6 KiB
Markdown
# 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 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)
|
||
- [ ] 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 | ⬜ | ✅ Build OK + 228/228 tests | ⬜ |
|
||
| Phase 7: Anomaly detection | fullstack | ⬜ Not Started | ⬜ | ⏭️ Big Bang | ⬜ |
|
||
| 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 1–7 do not depend on Phase 8's tactical implementation.
|