# Session handoff — 2026-06-12 Working plan agreed with the user: step through P0→P4 below, in order. Read this file instead of re-exploring the repo; README.md + ROADMAP.md cover architecture and conventions. Delete this file when the list is done. ## State (verified 2026-06-12, P0 session) - All 111 tests pass (`uv run pytest`). - **P0 DONE** (commit 6df25bcd, NOT yet pushed — push denied to agent, run `git push origin main`): .gitignore fixed and Takeout dump (23,673 files, personal health data) untracked; note it is still in remote history from earlier pushes — rewrite history if that ever matters. CI added at `.gitea/workflows/test.yml` (remote g.o00.io is Gitea — Actions must be enabled repo-side with a registered runner). `gitlab` remote URL typo (ttps://) fixed. `scripts/backup_db.sh` snapshots the DB, keeps 14. - DB: `garmin/data/garmin.db` is canonical — 378 activities (→ 2026-05-10), 349 FITs linked, wellness through 2026-05-17. - The stray `../data/garmin.db` (vault root) was a Takeout-ZIP ingest run from the wrong cwd on 2026-06-08 (7 extra activities May 17–Jun 2, epoch-ms timestamps, no FITs). Copied to `data/backups/vault-root-takeout-ingest-2026-06-08.db`; the original at `../data/` is redundant once live sync runs — user should delete it. - **SYNC DONE (2026-06-12)** via new garminconnect backend. garth's SSO login is Cloudflare-429-blocked AND garth is deprecated (matin/garth#222), so `src/openrun/ingest/garminconnect_backend.py` now authenticates with python-garminconnect 0.3.5 DI tokens (`.secrets/garmin_tokens.json`) and shims garth's client surface; `openrun-sync --backend=auto` prefers it. This is P3-lite, pulled forward. Login (once/year-ish): `uv run python -m openrun.ingest.garminconnect_backend`. Gotchas learned: usersummary endpoint needs UUID displayName, not userName (403 otherwise); a parseable-but-dead tokenstore blocks the password fallback in gc.login (main() parks it as .bak first). - DB after sync: 385 activities (→ 2026-06-02), all ISO timestamps on new rows, 356 FITs linked, 364 TIZ rows, wellness through 2026-06-12. The 18 legacy epoch-ms rows remain (P2). Web Sync page still uses the garth login flow — port it to the gc backend (or hide login) later. - `race_plan` table is EMPTY and `manual_activities` empty, but openrun.toml has races: 30K 2026-06-13, 50K 2026-07-25, 50 MILE 2026-09-12. - Known bugs: 18 activities have epoch-ms floats in `start_time_local` (should be ISO strings) — normalize on ingest + one-time migration; `datetime.utcnow()` deprecation warnings in `src/openrun/ingest/garmin_api.py`. ## Plan - **P0 — protect the work**: .gitignore, private remote, CI running pytest, pick canonical DB + backup story. (commit done) - **P1 — use it for the ultra build**: live sync (`openrun-auth` + `openrun-sync`, fall back to `../garmin-pgc/` python-garminconnect backend if garth hits Cloudflare 429), then populate `race_plan` through 2026-09-12 using `banister_forecast` + `calibrate_tl_per_km`; log off-watch work. - **P2 — data quality**: fix the 18 mixed-format timestamps, utcnow warnings, add `schema_version` + tiny migration runner before any public release. - **P3 — merge sync fork**: fold `../garmin-pgc/` into openrun as `openrun-sync --backend=garminconnect` instead of a sibling project. - **P4 — roadmap items reordered for the ultra**: TrainingReadinessDTO + RunRacePredictions ingest (ROADMAP 1.2), route map (3.2). Defer DBSCAN, multi-athlete, distribution until after September. ## Gaps identified (not yet scheduled) - Subjective data: RPE/soreness/injury notes (user hand-writes these in ../RunningLogs.md; March right-leg injury) — tie into DB, plot vs ACWR/TSB. - Ultra metrics: weekly vert, time-on-feet, back-to-back long-run detection, grade-adjusted pace (`elevation_gain` already in schema, unused). - Sync automation (launchd/cron weekly `openrun-sync`).