diff --git a/CLAUDE.md b/CLAUDE.md index d005de1..da62e21 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -121,6 +121,26 @@ Marathon__to_.xlsx - **`Event.ScheduledAt` requires offset `+03:00`.** Test fixtures and any code that constructs Moscow datetimes must use `new DateTimeOffset(date, TimeSpan.FromHours(3))`, never pass a `DateTime.UtcNow` value to that constructor. +- **EF migrations — generate with `dotnet ef`, do NOT `migrations remove`.** + `MarathonDbContextFactory` is a self-contained design-time factory, so + `dotnet ef migrations add X --project src/Marathon.Infrastructure --startup-project src/Marathon.Infrastructure` + works. AVOID `dotnet ef migrations remove`: older migrations were hand-written without a + Designer snapshot, so remove blanks `MarathonDbContextModelSnapshot.cs` and the next `add` + regenerates the WHOLE schema. Validate a migration on a throwaway DB with + `dotnet ef database update --connection "Data Source=/data/_migtest.db"` (absolute + path — EF resolves relatives from the build-output dir, not the shell cwd; create `./data/` first). +- **`Sports.Code` is `.ValueGeneratedNever()`** — it's the bookmaker's natural sport id + (6/11/22723…), not an autoincrement surrogate. Without it EF's int-PK convention emits a + spurious AUTOINCREMENT `AlterColumn` on every migration. +- **Validated get-only record properties block `with`** (CS0200): `BacktestStrategy.MinScore`, + `PaperBet.Rate`/`Stake` are re-declared with validation, so build a new instance instead of + `with { ThatProp = … }` (you can still `with` the un-redeclared props, e.g. `SavedStrategy with { Strategy = … }`). +- **`JsonSettingsWriter.SaveSectionAsync` replaces the whole section** (`root[section]=json`), + so a Settings-UI save drops any key not on the form. Never surface a secret-bearing section + (e.g. `Notifications` → Telegram token) in the UI — it would wipe the secret from + `appsettings.Local.json`. To surface a non-secret section, add a mutable mirror options class + in `Marathon.UI.Services` bound to the same section name (UI can't reference Infrastructure's + options types — same pattern as the two `WorkerOptions`). ## Feature: Initial Implementation > Phase 4: Application + Workers — Learnings @@ -203,3 +223,25 @@ For full detail see `spike/SCRAPE_FINDINGS.md` and `spike/SCHEMA_DRAFT.md`.) - **Signal-red is the load-bearing alert tone** for Phase 7. Use `var(--m-c-anomaly)` exclusively (never raw `#dc2626`). Pulsing animation (`m-pulse`) MUST respect `prefers-reduced-motion`. + +## Feature: Analysis Hardening (H-series) — Learnings + +- **Anomaly detectors are a fan-out array in `DetectAnomaliesUseCase`** — 4 now + (`SuspensionFlip`, `SteamMove`, `SuspensionFreeze`, `OverroundCompression`). A new detector + implements `IAnomalyDetector`, reuses `MatchWinEvidence` for the canonical evidence JSON + (so the parser + outcome evaluator work unchanged), and is added to that array. Continuous + sliding-window detectors (steam, overround) emit at every `end` and skip suspension-sized + gaps so they never overlap the across-suspension flip/freeze detectors. +- **`AnomalyKind.IsDirectional()` gates staking/grading** — flip + steam are directional + (predict a side); freeze + overround are informational and are excluded from the backtest + (`RunBacktestUseCase`) and the outcome evaluator so they don't skew hit-rate/score calibration. +- **`SavedStrategy` (backtest presets) use NOCASE name collation** — the unique index AND + `GetByNameAsync` both fold case (column-level `UseCollation("NOCASE")`), so save-by-name + upserts rather than creating "Kelly"/"kelly" duplicates. Domain stores stake fractions; the + form/VM speak percent — convert ×100/÷100 at the UI boundary. +- **Paper-trading (forward-test) is a config-gated worker** (`PaperTrading:Enabled`, default + false; tunable on the Settings page). `PaperTradingWorker` opens flat-stake `PaperBet`s on + new directional anomalies (unique on `AnomalyId`; baseline since-marker advances only after a + successful open pass; settle runs in its own catch so a settle failure can't strand the + marker) and settles them against results (Won iff pick == winner). `/paper-trading` shows + settled-only P&L. The ledger is FK-free (survives snapshot-retention pruning), like `PlacedBets`.