HealthProbe / AGENTS.md
Newer Older
271 lines | 11.204kb
Bogdan Timofte authored a month ago
1
# HealthProbe – Multi-Model Development Guide
2

            
3
## Overview
4

            
5
HealthProbe is built by multiple AI models, each owning a distinct domain.
6
This document defines boundaries, interfaces, and handoff contracts.
7

            
Bogdan Timofte authored 3 weeks ago
8
**Agentic reality:** The repo is developed largely via agents (Codex CLI, Claude, and dedicated model sessions). When updating product scope, update docs first, then implement behind flags, and add tests for the new behavior.
9

            
Bogdan Timofte authored a month ago
10
---
11

            
12
## Model Allocation
13

            
14
| Domain | Owner | Tools |
15
|--------|-------|-------|
16
| **UI / SwiftUI Views** | Claude Code | Xcode, SwiftUI, CLAUDE.md |
Bogdan Timofte authored 3 weeks ago
17
| **Archive Store** | Dedicated model session | SQLite/local archive format, HealthKit metadata mapping |
18
| **Data Models (SwiftData)** | Dedicated model session | Xcode, Swift; derived UI/cache/settings/log models only |
Bogdan Timofte authored a month ago
19
| **HealthKit Integration** | Dedicated model session | Xcode, HealthKit docs |
20
| **Anomaly Detection Algorithms** | Dedicated model session | Swift, statistical references |
Bogdan Timofte authored 3 weeks ago
21
| **Context Monitoring** | Dedicated model session | Xcode; logs Health/iCloud state as context only |
Bogdan Timofte authored a month ago
22
| **Documentation** | Claude Code + dedicated session | Markdown |
23
| **Tests** | Dedicated model session | XCTest, Swift Testing |
24

            
25
---
26

            
27
## Directory Ownership
28

            
29
```
30
HealthProbe/
31
├── Views/           ← Claude Code (UI)
32
├── ViewModels/      ← Claude Code (UI)
33
├── Utilities/       ← Claude Code (shared helpers, mocks)
Bogdan Timofte authored 3 weeks ago
34
├── Models/          ← Models agent (SwiftData UI/cache schemas)
35
├── Services/        ← Services agent (HealthKit, archive store, anomaly, context)
Bogdan Timofte authored a month ago
36
└── Tests/           ← Tests agent
37
```
38

            
39
**Rule:** Each agent writes only within its owned directories.
40
Cross-boundary changes require an explicit interface contract (protocol) first.
41

            
Bogdan Timofte authored 3 weeks ago
42
**Documentation scope:** `HealthProbe/Doc/` is shared. Keep it consistent with shipped behavior, and add a dated entry when objectives change.
43

            
Bogdan Timofte authored a month ago
44
---
45

            
46
## Interface Contracts
47

            
48
All service boundaries are defined as Swift protocols.
49
Claude Code (UI) consumes protocols — never concrete implementations.
50

            
51
### HealthMonitorProtocol
52

            
53
```swift
54
/// Owned by: Services agent
55
/// Consumed by: UI (DashboardViewModel)
56
protocol HealthMonitorProtocol {
57
    var currentStatus: HealthStatus { get }
58
    var lastChecked: Date? { get }
59
    func runCheck() async throws
60
}
61
```
62

            
63
### AnomalyStoreProtocol
64

            
65
```swift
66
/// Owned by: Services agent
67
/// Consumed by: UI (AnomalyListViewModel)
68
protocol AnomalyStoreProtocol {
69
    var anomalies: [DetectedAnomaly] { get }
70
    func markResolved(_ anomaly: DetectedAnomaly) async throws
71
}
72
```
73

            
74
### AuditTrailProtocol
75

            
76
```swift
77
/// Owned by: Services agent
78
/// Consumed by: UI (AuditTrailView)
79
protocol AuditTrailProtocol {
80
    var entries: [AuditTrailEntry] { get }
81
    func export() async throws -> Data  // JSON
82
}
83
```
84

            
Bogdan Timofte authored 3 weeks ago
85
### ContextMonitorProtocol
Bogdan Timofte authored a month ago
86

            
87
```swift
88
/// Owned by: Services agent
Bogdan Timofte authored 3 weeks ago
89
/// Consumed by: UI (ContextViewModel)
90
protocol ContextMonitorProtocol {
Bogdan Timofte authored a month ago
91
    var iCloudEnabled: Bool { get }
Bogdan Timofte authored 3 weeks ago
92
    var lastObservedAt: Date? { get }
93
    var stateChanges: [ContextStateChange] { get }
Bogdan Timofte authored a month ago
94
}
95
```
96

            
97
---
98

            
99
## Shared Types (Models Agent)
100

            
101
These types are defined once in `Models/` and shared across all agents:
102

            
103
```swift
104
// Models/TypeDistributionBin.swift
105
@Model
106
final class TypeDistributionBin {
107
    var bucketStart: Date
108
    var bucketEnd: Date
109
    var count: Int
110
}
111

            
112
// Models/TypeCount.swift
Bogdan Timofte authored 3 weeks ago
113
// TypeCount owns zero or more TypeDistributionBin records.
114
// These bins store sample counts and import anchors, not raw health values.
115

            
116
// Interface updated 2026-05-12 — see AGENTS.md
117
// Models/HealthRecord.swift
118
// HealthRecord stores one anonymized HealthKit record fingerprint plus its start/end dates.
119
// It intentionally does not store raw health values, device identifiers, or source metadata.
120
// UI may compare HealthRecord fingerprints between adjacent snapshots to expose losses
121
// that are masked by newly-added records with the same total count.
122
// High-volume snapshots store these records in TypeCount.recordArchiveData instead of
123
// creating one SwiftData model per record, avoiding main-thread stalls after import.
124

            
125
// Interface updated 2026-05-13 — see AGENTS.md
126
// TypeDistributionBin also stores content hashes and HealthKit query anchors.
127
// Import uses a global anchored query per data type so follow-up snapshots fetch only
128
// HealthKit deltas instead of scanning calendar blocks with fixed per-query latency.
Bogdan Timofte authored a month ago
129

            
Bogdan Timofte authored 3 weeks ago
130
// Interface updated 2026-05-18 — see AGENTS.md
131
// SwiftData is not the forensic source of truth. TypeCount and related rows store
132
// precomputed UI/index data only. Complete HealthKit samples and metadata belong
133
// in the local archive store, in one schema that can preserve relationships across
134
// data types, sources, devices, workouts, and metadata.
Bogdan Timofte authored 3 weeks ago
135

            
Bogdan Timofte authored 3 weeks ago
136
// Interface updated 2026-05-18 — see AGENTS.md
137
// Services/Protocols/HealthArchiveStore.swift defines the local archive boundary.
138
// SQLiteHealthArchiveStore is the current implementation. HealthKit anchored-query
139
// pages must be written to this archive before SwiftData UI/cache rows are saved.
140
// Deletions are recorded by sampleUUIDHash because HKDeletedObject exposes UUIDs,
141
// not complete sample payloads.
142

            
Bogdan Timofte authored 3 weeks ago
143
// Interface updated 2026-05-17 — see AGENTS.md
144
// Models/TypeCount.detailCacheData stores precomputed detail data for the current
145
// TypeCount compared with the immediately previous snapshot on the same device.
146
// The cache contains aggregate added/disappeared counts, capped preview records for
147
// UI drill-down, and daily change bins for temporal charts. It must be computed when
148
// snapshots are saved and refreshed for neighboring snapshots when snapshot deletion
149
// changes chain links. Existing stores are backfilled incrementally with a strict
150
// per-launch TypeCount cap to avoid decoding many large archives in one run.
151

            
Bogdan Timofte authored 3 weeks ago
152
// Interface updated 2026-05-17 — see AGENTS.md
153
// Models/HealthSnapshot.contentEquivalentSnapshotID marks snapshots whose TypeCount
154
// content is identical to a previous snapshot on the same device. These snapshots are
155
// retained as temporal labels but behave as aliases to the representative content
156
// snapshot for expensive detail cache/diff work.
157

            
158
// Interface updated 2026-05-17 — see AGENTS.md
159
// Models/TypeCount.contentEquivalentTypeCountID marks individual data types whose
160
// content is identical to the previous snapshot's same TypeCount. This allows a
161
// snapshot to contain real changes for some metrics while long-stable metrics behave
162
// as temporal aliases and skip per-type detail cache/diff work.
163

            
Bogdan Timofte authored 3 weeks ago
164
// Interface updated 2026-05-17 — see AGENTS.md
165
// Models/HealthSnapshot stores cached overview scalars for UI consumption:
166
// tracked type count, aggregate record count, and overall oldest/newest record dates.
167
// These values must be computed during snapshot save while TypeCount data is already
168
// in memory, so snapshot list/detail screens never recompute them by traversing
169
// snapshot.typeCounts on the UI thread.
170

            
171
// Interface updated 2026-05-17 — see AGENTS.md
172
// Models/SnapshotDelta stores cached list/detail summary scalars derived from TypeDelta.
173
// Overview screens consume these scalars and type-delta summaries directly instead of
174
// recalculating per-snapshot changes from HealthSnapshot.typeCounts.
175

            
Bogdan Timofte authored a month ago
176
// Models/DetectedAnomaly.swift
177
enum AnomalyType: String, Codable {
178
    case historicalInsertion = "historical_insertion"
179
    case silentDeletion      = "silent_deletion"
180
    case duplicate           = "duplicate"
181
    case divergence          = "divergence"
182
}
183

            
184
enum Severity: String, Codable, Comparable {
185
    case info, warning, critical
186
}
187

            
188
enum HealthStatus: String {
189
    case healthy, warning, critical, unknown
190
}
191
```
192

            
193
Any model changes must be announced in this file before other agents consume them.
194

            
195
---
196

            
197
## Handoff Process
198

            
199
When a module is ready to be consumed by another agent:
200

            
201
1. **Define the protocol** in `Services/Protocols/` (services agent)
202
2. **Implement a mock** in `Utilities/Mocks.swift` (Claude Code)
203
3. **Build UI against the mock** (Claude Code)
204
4. **Replace mock with real implementation** (services agent)
205
5. **Integration test** (tests agent)
206

            
207
This allows UI development and service development to proceed in parallel.
208

            
209
---
210

            
211
## Algorithms & Detection Logic
212

            
213
The following modules involve non-trivial logic and should be reviewed carefully:
214

            
215
| Module | File | Description |
216
|--------|------|-------------|
217
| **Anomaly Detector** | `Services/AnomalyDetector.swift` | Statistical detection: insertions, deletions, duplicates, divergence |
218
| **Divergence Engine** | `Services/DivergenceEngine.swift` | Time-series trend analysis, σ comparison |
219
| **Fingerprinter** | `Services/SampleFingerprinter.swift` | Duplicate detection via sample hashing |
220
| **Snapshot Comparator** | `Services/SnapshotComparator.swift` | Diff between two HealthKit snapshots |
221
| **Distribution Comparator** | `Services/SnapshotDiffService.swift` | Daily per-type distribution diff to reveal old-data disappearance masked by new data |
222

            
223
**Guidelines for algorithm modules:**
224
- Document assumptions explicitly (e.g., "assumes continuous monitoring since install")
225
- All thresholds (e.g., `age > 7 days`) must be configurable constants, not magic numbers
226
- Include unit tests for edge cases (empty snapshots, partial data, clock skew)
227
- No UI code; return plain Swift types only
228

            
229
---
230

            
231
## Privacy Directives — All Agents
232

            
233
**Mandatory across all modules:**
234
- No credentials, API keys, tokens, or certificates in any file
235
- No personal data: names, emails, phone numbers, dates of birth
236
- No device identifiers: UDID, serial number, advertising ID, device name
237
- No account identifiers: Apple ID, iCloud account info, CloudKit record IDs
238
- No raw health values in code, tests, previews, logs, or comments
239
- No location data or patterns enabling re-identification
240
- Synthetic data only in tests and previews
241

            
Bogdan Timofte authored 3 weeks ago
242
**Clarification:** “No raw health values” applies to this repository’s contents. The app may optionally store a user's raw HealthKit samples *locally on-device* for forensic backup purposes, but such samples must never appear in source control, logs, or docs.
243

            
Bogdan Timofte authored a month ago
244
---
245

            
246
## Communication Between Agents
247

            
248
When one agent needs to communicate a decision or change to another:
249

            
250
1. **Update this file** (`AGENTS.md`) with the protocol/interface change
251
2. **Update the relevant protocol** in `Services/Protocols/`
252
3. **Add a comment** in the affected file: `// Interface updated YYYY-MM-DD — see AGENTS.md`
253

            
254
---
255

            
256
## Current Status
257

            
258
| Module | Status | Owner |
259
|--------|--------|-------|
260
| SwiftData Models | ✅ Done | Models agent |
261
| HealthKit Integration | ✅ Done | Services agent |
262
| Snapshot Diff Service | ✅ Done | Services agent |
263
| Service Protocols | ⏳ Not started | Services agent |
264
| Anomaly Detection | ⏳ Not started | Services agent |
265
| Sync Monitor | ⏳ Not started | Services agent |
266
| UI – App entry + TabView | ✅ Done | Claude Code |
267
| UI – Dashboard | ✅ Done (functional, minimal) | Claude Code |
268
| UI – Snapshots + Detail | ✅ Done | Claude Code |
269
| UI – Data Types | ✅ Done | Claude Code |
270
| UI – Settings | ✅ Done | Claude Code |
271
| Unit Tests | ⏳ Not started | Tests agent |