docs(issues): KC-51 — analytics system plan, persistence eval, blockers
Records the /goal analytics-system implementation plan: reusable-functionality survey, SwiftData/CoreData/current persistence evaluation, proposed architecture (stores, immutable reports, pure analysis engine, reconciliation, notifications, UI, tests), and the two blockers (build-verification unavailable; persistence + min-iOS decision). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1451,3 +1451,85 @@ toggle, a Snooze submenu (15 min / 1 hr / tomorrow), then the shared
|
||||
Events (no `taskID`) get no menu.
|
||||
|
||||
Files: `CalendarView.swift`.
|
||||
|
||||
---
|
||||
|
||||
## KC-51 — Health & Workout Analytics system (12-month, immutable reports) — PLAN
|
||||
|
||||
**Status:** Planning (blocked on 2 decisions + build-verification environment)
|
||||
**Reported by:** User (/goal)
|
||||
**Area:** Analytics / Persistence / HealthKit / Notifications
|
||||
|
||||
### Reusable existing functionality (survey)
|
||||
- `WorkoutViewModel`: `history: [WorkoutDayLog]`, `workoutDates`, `schedule`,
|
||||
`missedDates`, `compensations`; `StatPeriod`/`WorkoutStat`/`StatBucket`
|
||||
aggregation already exists (`stat(period:offset:)`, `statBuckets`).
|
||||
- `WorkoutDayLog` → `LoggedExercise` → `LoggedSet(weight, reps, done)` with
|
||||
computed `volume` (Σ weight×reps for done sets), `totalSets`, `doneSets`.
|
||||
- `HealthKitManager`: steps, active calories, resting HR, workoutsThisWeek,
|
||||
`fetchWorkoutDates(from:to:)`, `saveWorkout`, `sumQuery`/`latestQuery`.
|
||||
- `NotificationManager`: categories/actions, foreground `reschedule(...)`, and
|
||||
**`schedulePeriodMilestones()`** (week/month/year-close nudges) — extend this.
|
||||
- Lifecycle reconcile pattern already used: `checkPendingMissed` /
|
||||
`checkPendingHealthConfirm` on `onAppear` + `scenePhase == .active`.
|
||||
- Persistence: `UserDefaults` mirrored to iCloud KVS (`CloudSyncManager`).
|
||||
|
||||
### Persistence evaluation (SwiftData / Core Data / current)
|
||||
- **Current (JSON blobs in UserDefaults + iCloud KVS):** iCloud KVS has a hard
|
||||
**1 MB total** ceiling (see KC-39 context). 12 months of daily snapshots +
|
||||
HealthKit reference cache **will exceed it** → silent sync failure. Not viable
|
||||
as the analytics store.
|
||||
- **SwiftData:** cleanest modelling/migration, but requires **iOS 17+**. App
|
||||
deploymentTarget is **iOS 16.0** → SwiftData is UNAVAILABLE unless the min
|
||||
target is raised (drops iOS 16 devices — a product call).
|
||||
- **Core Data:** works at iOS 16, unbounded local storage, supports lightweight
|
||||
migration; more boilerplate. Local-only (analytics need not sync via KVS;
|
||||
reports are device-derived and reconstructable).
|
||||
|
||||
### Proposed architecture (pending the decision below)
|
||||
- **Stores:** `WorkoutRecordStore` (app-generated, mirrors `WorkoutDayLog`) +
|
||||
`HealthReferenceStore` (HealthKit cache; HealthKit = source of truth) +
|
||||
`ReportStore` (immutable daily snapshots, weekly, monthly). Keep app records
|
||||
and HealthKit reference separate; analysis reads across both.
|
||||
- **Immutability:** reports store a frozen snapshot at generation; later edits to
|
||||
source data never mutate a generated report. Retention: prune raw beyond 12
|
||||
months; keep report summaries.
|
||||
- **Analysis engine (`AnalyticsEngine`)**: pure, no SwiftUI/persistence deps,
|
||||
deterministic. Formulas documented: volume = Σ(weight×reps) of completed sets;
|
||||
%Δ = (cur−base)/|base| guarded for base≈0/new/rest; consistency = sessions ÷
|
||||
scheduled over window; trend = classify(slope, threshold) → improve / decline /
|
||||
plateau / insufficient-data. Handles missing/partial HK, deloads, outliers
|
||||
(winsorize/IQR), unit compatibility, divide-by-zero. Non-medical language +
|
||||
disclaimer; never diagnoses.
|
||||
- **Comparisons:** daily vs previous comparable day + same weekday −1 week;
|
||||
weekly vs previous completed week; monthly vs previous month.
|
||||
- **Reconciliation:** idempotent generators keyed by period id; on launch/active,
|
||||
backfill any missing completed week/month; never duplicate reports or
|
||||
notifications. Do not depend on exact BG execution.
|
||||
- **Notifications:** extend `schedulePeriodMilestones()`; one permission-aware,
|
||||
user-scheduled local notification per weekly/monthly report; deep-link via
|
||||
`userInfo` → route to the specific report (new deep-link cases).
|
||||
- **UI (`Analytics` area):** Overview · Daily comparison · Weekly reports ·
|
||||
Monthly reports · 12-month trends · Per-exercise history. Full loading / empty /
|
||||
denied-permission / partial-data / error states; Dynamic Type, VoiceOver,
|
||||
light/dark, compact/regular; Wenza design tokens; charts only where they aid
|
||||
understanding; non-color status indicators.
|
||||
- **Tests:** date boundaries (daily/weekly/monthly), calendar/locale/firstWeekday/
|
||||
timezone/DST, volume/%/consistency/trend math, missing/partial HK, migration/
|
||||
dedup/reconcile/retention, exercise identity + unit compat, report+notification
|
||||
idempotency, deep-link routing.
|
||||
|
||||
### BLOCKERS (must resolve before build-verified completion)
|
||||
1. **Build/test verification is impossible in this environment.** The iOS 26.5
|
||||
simulator runtime was removed this session and CLI builds are broken, so
|
||||
completion conditions #10 (tests pass) and #11 (Xcode build succeeds) CANNOT
|
||||
be verified here. Per "never claim verification not performed", any code
|
||||
produced now is inspection-only until a runtime is restored / built on device.
|
||||
2. **Persistence + min-iOS decision (irreversible):** SwiftData needs iOS 17
|
||||
(app is iOS 16). Migrating live workout history to a new store is a one-way
|
||||
data migration. Needs the founder's call before implementation.
|
||||
|
||||
### Next action
|
||||
Awaiting decision on persistence backend / deployment target, then implement the
|
||||
pure `AnalyticsEngine` + models + tests first (most inspection-verifiable), UI
|
||||
last. Device/build QA tracked here.
|
||||
|
||||
Reference in New Issue
Block a user