From a4abbb0d4de2f8efa4bc9eacf787aca3252b664d Mon Sep 17 00:00:00 2001 From: kutesir Date: Tue, 7 Jul 2026 01:42:45 +0300 Subject: [PATCH] =?UTF-8?q?docs(issues):=20KC-51=20=E2=80=94=20analytics?= =?UTF-8?q?=20system=20plan,=20persistence=20eval,=20blockers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- KisaniCal/ISSUES.md | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/KisaniCal/ISSUES.md b/KisaniCal/ISSUES.md index 868ba85..9b8203c 100644 --- a/KisaniCal/ISSUES.md +++ b/KisaniCal/ISSUES.md @@ -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.