|
Bogdan Timofte
authored
a month ago
|
1
|
# HealthProbe – Claude Code Instructions
|
|
|
2
|
|
|
|
3
|
## Project Context
|
|
|
4
|
|
|
|
5
|
**HealthProbe** is an iOS app that audits Apple HealthKit data integrity.
|
|
|
6
|
It detects anomalies: data loss, historical insertions, duplicates, divergence trends.
|
|
|
7
|
Full specification: `HealthProbe/Doc/HealthProbe – Complete Specification & Motivations.md`
|
|
|
8
|
|
|
Bogdan Timofte
authored
2 weeks ago
|
9
|
**Current state:** SwiftUI + SwiftData app is active. Product direction changed on 2026-05-18: HealthProbe is a local audit/capture agent. Do not add HealthProbe CloudKit/iCloud sync.
|
|
Bogdan Timofte
authored
a month ago
|
10
|
|
|
|
11
|
---
|
|
|
12
|
|
|
|
13
|
## Claude Code Scope: UI Layer
|
|
|
14
|
|
|
|
15
|
Claude Code is responsible for:
|
|
|
16
|
- All **SwiftUI Views** (`Views/` directory)
|
|
|
17
|
- All **ViewModels** (`ViewModels/` directory)
|
|
|
18
|
- **Navigation structure** and tab/split layout
|
|
|
19
|
- **Design system** (colors, typography, spacing)
|
|
|
20
|
- **Preview providers** for all views
|
|
|
21
|
- **Accessibility** (VoiceOver, Dynamic Type)
|
|
|
22
|
|
|
|
23
|
Claude Code does NOT own:
|
|
Bogdan Timofte
authored
2 weeks ago
|
24
|
- `Services/` — HealthKit queries, anomaly detection, archive store, context monitoring (see AGENTS.md)
|
|
Bogdan Timofte
authored
a month ago
|
25
|
- `Models/` — SwiftData models (see AGENTS.md)
|
|
|
26
|
- Entitlements, Info.plist, project configuration
|
|
|
27
|
|
|
|
28
|
When services are not yet implemented, **consume their protocols and use mock implementations** for UI development.
|
|
|
29
|
|
|
|
30
|
---
|
|
|
31
|
|
|
|
32
|
## Target Screen Structure
|
|
|
33
|
|
|
|
34
|
```
|
|
|
35
|
App (TabView)
|
|
|
36
|
├── Tab 1: Dashboard → DashboardView
|
|
|
37
|
├── Tab 2: Anomalies → AnomalyListView → AnomalyDetailView
|
|
|
38
|
├── Tab 3: Audit Trail → AuditTrailView
|
|
Bogdan Timofte
authored
2 weeks ago
|
39
|
├── Tab 4: Archive Status → ArchiveStatusView
|
|
Bogdan Timofte
authored
a month ago
|
40
|
└── Tab 5: Settings → SettingsView
|
|
|
41
|
```
|
|
|
42
|
|
|
|
43
|
### DashboardView
|
|
|
44
|
- Large status indicator: ✅ Healthy / ⚠️ Check / 🚨 Critical
|
|
|
45
|
- Last check timestamp
|
|
|
46
|
- Summary cards: samples tracked, anomalies found (all-time)
|
|
|
47
|
- Up to 3 recent active alerts (tappable → AnomalyDetailView)
|
|
|
48
|
- "Check Now" button (calls monitoring service)
|
|
|
49
|
|
|
|
50
|
### AnomalyListView
|
|
|
51
|
- List of `DetectedAnomaly` sorted by date (most recent first)
|
|
|
52
|
- Filter: All / Critical / Warning / Info
|
|
|
53
|
- Filter: by type (deletion, insertion, duplicate, divergence)
|
|
|
54
|
- Each row: severity badge, type, sample type, date
|
|
|
55
|
- Swipe to mark resolved
|
|
|
56
|
|
|
|
57
|
### AnomalyDetailView
|
|
|
58
|
- Full anomaly details
|
|
|
59
|
- Evidence dictionary displayed as key-value rows
|
|
|
60
|
- Severity badge
|
|
|
61
|
- Share button → exports as Markdown (for bug reports)
|
|
|
62
|
- "Mark Resolved" action
|
|
|
63
|
|
|
|
64
|
### AuditTrailView
|
|
|
65
|
- Chronological list of `AuditTrailEntry`
|
|
|
66
|
- Each row: timestamp, event type chip, message
|
|
|
67
|
- Search/filter by event type
|
|
|
68
|
- Export button → JSON
|
|
|
69
|
|
|
Bogdan Timofte
authored
2 weeks ago
|
70
|
### ArchiveStatusView
|
|
|
71
|
- Current local archive health
|
|
|
72
|
- Last archive verification timestamp
|
|
|
73
|
- Selected data types covered by forensic capture
|
|
|
74
|
- Recent Health/iCloud context events (for correlation only; no HealthProbe sync)
|
|
Bogdan Timofte
authored
a month ago
|
75
|
|
|
|
76
|
### SettingsView
|
|
|
77
|
- Check frequency: 2h / 6h / 12h / 24h (Picker)
|
|
|
78
|
- Sample types to monitor (MultiSelect toggle list)
|
|
|
79
|
- Alert thresholds (severity level for push notifications)
|
|
Bogdan Timofte
authored
2 weeks ago
|
80
|
- Point export/report actions for selected findings
|
|
Bogdan Timofte
authored
a month ago
|
81
|
- Delete all audit data (destructive, confirm alert)
|
|
|
82
|
|
|
|
83
|
---
|
|
|
84
|
|
|
|
85
|
## Design Guidelines
|
|
|
86
|
|
|
|
87
|
**Tone:** Professional, calm, medical-adjacent. Not alarming unless critical.
|
|
|
88
|
|
|
|
89
|
**Color System:**
|
|
|
90
|
```swift
|
|
|
91
|
// Status colors
|
|
|
92
|
.healthyGreen // SF: green — all clear
|
|
|
93
|
.warningAmber // SF: yellow — attention needed
|
|
|
94
|
.criticalRed // SF: red — action required
|
|
|
95
|
.neutralGray // SF: gray — informational / resolved
|
|
|
96
|
```
|
|
|
97
|
|
|
|
98
|
**Typography:** SF Pro (system font). No custom fonts.
|
|
|
99
|
|
|
|
100
|
**Spacing:** 8pt grid. Use `VStack(spacing: 12)` as baseline.
|
|
|
101
|
|
|
|
102
|
**Icons:** SF Symbols only. No third-party icon sets.
|
|
|
103
|
|
|
|
104
|
**Key SF Symbols:**
|
|
|
105
|
- `checkmark.shield.fill` — healthy status
|
|
|
106
|
- `exclamationmark.triangle.fill` — warning
|
|
|
107
|
- `xmark.shield.fill` — critical
|
|
|
108
|
- `clock.arrow.circlepath` — audit trail
|
|
Bogdan Timofte
authored
2 weeks ago
|
109
|
- `externaldrive.fill.badge.checkmark` — archive status
|
|
Bogdan Timofte
authored
a month ago
|
110
|
- `waveform.path.ecg` — health data
|
|
|
111
|
- `doc.text.magnifyingglass` — anomaly detail
|
|
|
112
|
|
|
|
113
|
**Dark mode:** Required. Test in both modes.
|
|
|
114
|
|
|
|
115
|
**Privacy-first UI:**
|
|
|
116
|
- Health metric values are **never shown in plain text** in list rows
|
|
|
117
|
- Values visible only in `AnomalyDetailView` after tap
|
|
|
118
|
- Evidence dictionary values shown as monospace text, not highlighted
|
|
|
119
|
|
|
|
120
|
---
|
|
|
121
|
|
|
|
122
|
## SwiftData Integration
|
|
|
123
|
|
|
|
124
|
Models are defined in `Models/`. Reference them read-only from views:
|
|
|
125
|
|
|
|
126
|
```swift
|
|
|
127
|
// In views, use @Query — never write directly from a View
|
|
|
128
|
@Query(sort: \DetectedAnomaly.detectedAt, order: .reverse)
|
|
|
129
|
private var anomalies: [DetectedAnomaly]
|
|
|
130
|
|
|
|
131
|
// Mutations go through ViewModels or services only
|
|
|
132
|
```
|
|
|
133
|
|
|
|
134
|
Until `Models/` are implemented, use mock data via `PreviewProvider`.
|
|
|
135
|
|
|
|
136
|
---
|
|
|
137
|
|
|
|
138
|
## ViewModel Pattern
|
|
|
139
|
|
|
|
140
|
```swift
|
|
|
141
|
// Pattern for all ViewModels
|
|
|
142
|
@MainActor
|
|
|
143
|
@Observable
|
|
|
144
|
final class DashboardViewModel {
|
|
|
145
|
private let monitor: HealthMonitorProtocol // protocol, not concrete type
|
|
|
146
|
|
|
|
147
|
var status: HealthStatus = .unknown
|
|
|
148
|
var recentAnomalies: [DetectedAnomaly] = []
|
|
|
149
|
var lastChecked: Date?
|
|
|
150
|
|
|
|
151
|
init(monitor: HealthMonitorProtocol = HealthMonitorService.shared) {
|
|
|
152
|
self.monitor = monitor
|
|
|
153
|
}
|
|
|
154
|
|
|
|
155
|
func refresh() async {
|
|
|
156
|
await monitor.runCheck()
|
|
|
157
|
}
|
|
|
158
|
}
|
|
|
159
|
```
|
|
|
160
|
|
|
|
161
|
Always inject dependencies via protocols — makes previews and tests possible without real HealthKit.
|
|
|
162
|
|
|
|
163
|
---
|
|
|
164
|
|
|
|
165
|
## Mock Data Protocol
|
|
|
166
|
|
|
|
167
|
Until services are ready, define preview mocks in `Utilities/Mocks.swift`:
|
|
|
168
|
|
|
|
169
|
```swift
|
|
|
170
|
struct MockHealthMonitor: HealthMonitorProtocol {
|
|
|
171
|
func runCheck() async { }
|
|
|
172
|
var status: HealthStatus { .warning }
|
|
|
173
|
}
|
|
|
174
|
|
|
|
175
|
extension DetectedAnomaly {
|
|
|
176
|
static var preview: DetectedAnomaly {
|
|
|
177
|
DetectedAnomaly(
|
|
|
178
|
detectedAt: .now,
|
|
|
179
|
type: "silent_deletion",
|
|
|
180
|
severity: "warning",
|
|
|
181
|
sampleType: "Steps",
|
|
|
182
|
summary: "72 samples missing without deletion event",
|
|
|
183
|
evidence: ["loss_count": "72", "loss_percent": "23.4"]
|
|
|
184
|
)
|
|
|
185
|
}
|
|
|
186
|
}
|
|
|
187
|
```
|
|
|
188
|
|
|
|
189
|
---
|
|
|
190
|
|
|
|
191
|
## File Organization
|
|
|
192
|
|
|
|
193
|
```
|
|
|
194
|
HealthProbe/
|
|
|
195
|
├── Views/
|
|
|
196
|
│ ├── Dashboard/
|
|
|
197
|
│ │ ├── DashboardView.swift
|
|
|
198
|
│ │ └── StatusCardView.swift
|
|
|
199
|
│ ├── Anomalies/
|
|
|
200
|
│ │ ├── AnomalyListView.swift
|
|
|
201
|
│ │ └── AnomalyDetailView.swift
|
|
|
202
|
│ ├── AuditTrail/
|
|
|
203
|
│ │ └── AuditTrailView.swift
|
|
Bogdan Timofte
authored
2 weeks ago
|
204
|
│ ├── Archive/
|
|
|
205
|
│ │ └── ArchiveStatusView.swift
|
|
Bogdan Timofte
authored
a month ago
|
206
|
│ └── Settings/
|
|
|
207
|
│ └── SettingsView.swift
|
|
|
208
|
├── ViewModels/
|
|
|
209
|
│ ├── DashboardViewModel.swift
|
|
|
210
|
│ ├── AnomalyListViewModel.swift
|
|
Bogdan Timofte
authored
2 weeks ago
|
211
|
│ └── ArchiveStatusViewModel.swift
|
|
Bogdan Timofte
authored
a month ago
|
212
|
├── Models/ ← NOT owned by Claude Code
|
|
|
213
|
├── Services/ ← NOT owned by Claude Code
|
|
|
214
|
└── Utilities/
|
|
|
215
|
├── Mocks.swift
|
|
|
216
|
├── DateFormatters.swift
|
|
|
217
|
└── DesignSystem.swift
|
|
|
218
|
```
|
|
|
219
|
|
|
|
220
|
---
|
|
|
221
|
|
|
|
222
|
## Privacy Directives
|
|
|
223
|
|
|
|
224
|
**Mandatory — no exceptions:**
|
|
|
225
|
- No credentials, tokens, or API keys in any file
|
|
|
226
|
- No personal data, device identifiers, or account identifiers
|
|
|
227
|
- No real health values in code, comments, previews, or tests
|
|
|
228
|
- Synthetic preview data only (see Mocks.swift above)
|
|
|
229
|
|
|
|
230
|
---
|
|
|
231
|
|
|
|
232
|
## Before Marking a Task Complete
|
|
|
233
|
|
|
|
234
|
- [ ] View renders in both Light and Dark mode (use Preview)
|
|
|
235
|
- [ ] VoiceOver labels set on interactive elements
|
|
|
236
|
- [ ] Dynamic Type tested (at least xSmall and AX3)
|
|
|
237
|
- [ ] Works with mock data (no real HealthKit dependency in View layer)
|
|
|
238
|
- [ ] No health values displayed without explicit user tap
|
|
|
239
|
- [ ] Compiles without warnings
|