Showing 13 changed files with 60 additions and 60 deletions
+3 -3
HealthProbe/Doc/04-project/IMPLEMENTATION_STATUS.md
@@ -28,7 +28,7 @@ There are no real deployments, only test installations. Existing prototype datab
28 28
 | SQLite archive | Archive v2 schema, snapshot-level observation grouping, differential write path, v2 verification/delete bookkeeping, daily aggregate rebuilds, integrity report, v2 record reads, SQL diff/count/aggregate/provenance/consolidation-evidence APIs, large synthetic diff pagination coverage, formal timing/memory metrics, and XCTest coverage are in place; the legacy `archive_samples` mirror has been removed | Move Snapshots/Data Types from SwiftData previews to archive/cache DTOs |
29 29
 | Core Data cache | Initial programmatic Core Data model, full-cache rebuild service, read DTOs for observation/type/health rows, and Dashboard archive-cache status wiring are in place | Move Snapshots/Data Types to cache DTOs and add targeted partial invalidation |
30 30
 | SwiftData cache | Exists; test builds now reset legacy prototype UI/archive/cache stores once for archive v2 so old SwiftData-only snapshots are not treated as backed-up observations | Treat as disposable prototype data; reset/ignore during v2 transition |
31
-| UI | Prototype exists; Snapshots/Data Types now default to the local device timeline instead of a multi-device picker. Dashboard status prefers archive/cache observation rows and shows cache health; Snapshots timeline, snapshot detail summaries/type rows, and Data Types list prefer Core Data cache rows when archive observation ids exist; data type detail/drill-down uses SQLite `diffSummary`/`diffRecords`, with SwiftData detail cache as transition fallback | Finish remaining detail charts/export previews on paged SQLite DTOs |
31
+| UI | Prototype exists; Snapshots/Data Types now default to the local device timeline instead of a multi-device picker. Dashboard status prefers archive/cache observation rows and shows cache health; Snapshots timeline, snapshot detail summaries/type rows, and Data Types list prefer Core Data cache rows when archive observation ids exist; data type detail/drill-down uses SQLite `diffSummary`/`diffRecords`; visible change labels now use neutral new/missing/change-review language, with SwiftData detail cache as transition fallback | Finish remaining detail charts/export previews on paged SQLite DTOs |
32 32
 | Diff/change explanation | Prototype/legacy anomaly logic exists | Move heavy diffing into SQLite and use neutral change classifications |
33 33
 | Export | Prototype scoped JSON export exists | Add recovery-compatible manifests and streaming/paged export |
34 34
 | Legacy device support | Not implemented | Remove SwiftData dependency and simplify heavy views for low-memory devices |
@@ -40,7 +40,7 @@ Detailed checkable milestones live in [`Refactoring-Plan.md`](Refactoring-Plan.m
40 40
 
41 41
 1. Move Snapshots/Data Types from SwiftData model reads to Core Data/cache DTOs.
42 42
 2. Add targeted cache invalidation for affected observation/type ranges.
43
-3. Update UI language from anomaly/status to observation/diff/export.
43
+3. Finish remaining UI language cleanup from anomaly/status to observation/diff/export where legacy model names still leak into active flows.
44 44
 4. Add streaming exports with manifests.
45 45
 5. Validate on low-memory/legacy-class devices.
46 46
 
@@ -65,7 +65,7 @@ Detailed checkable milestones live in [`Refactoring-Plan.md`](Refactoring-Plan.m
65 65
 - [x] Snapshots timeline rows use Core Data cached observation counts/change summaries when cache rows are available.
66 66
 - [x] Snapshot detail summary/type rows use Core Data cached summaries plus SQLite diff summaries when archive observation ids are available.
67 67
 - [x] Data Types list rows use Core Data cached counts plus SQLite diff summaries when archive observation ids are available.
68
-- [x] Data type added/disappeared drill-down pages records from SQLite diff queries when archive observation ids are available.
68
+- [x] Data type new/missing drill-down pages records from SQLite diff queries when archive observation ids are available.
69 69
 - [x] Expensive counts used by reports/UI are cached and rebuildable.
70 70
 - [x] Deleting Core Data cache and rebuilding from SQLite restores UI/report summaries.
71 71
 - [x] Dashboard surfaces SQLite/Core Data cache health, cache schema, cache errors, and latest archive observation counts.
+2 -2
HealthProbe/Doc/04-project/Refactoring-Plan.md
@@ -226,9 +226,9 @@ Checklist:
226 226
 - [x] Observation detail uses cached summary/type rows plus SQLite diff summaries when archive observation ids exist.
227 227
 - [x] Data Types list rows prefer Core Data cached counts plus SQLite `diffSummary` when archive observation ids exist.
228 228
 - [x] Data type detail uses SQLite `diffSummary` when archive observation ids exist.
229
-- [x] Data type added/disappeared drill-down pages through SQLite `diffRecords` when archive observation ids exist.
229
+- [x] Data type new/missing drill-down pages through SQLite `diffRecords` when archive observation ids exist.
230 230
 - [ ] Diff detail fully uses cached summary plus paged SQLite DTOs.
231
-- [ ] Data type screens use target change labels.
231
+- [x] Data type screens use target change labels.
232 232
 - [ ] Export preview uses export query/manifest APIs.
233 233
 - [x] Archive status reflects SQLite/Core Data cache health.
234 234
 - [ ] Legacy/small-device UI mode simplifies heavy visualizations.
+2 -2
HealthProbe/ViewModels/DataTypeRecordListViewModel.swift
@@ -195,7 +195,7 @@ final class DataTypeRecordListViewModel {
195 195
         case .disappeared(let typeID), .disappearedDiff(let typeID, _, _):
196 196
             return HealthArchiveReportRequest(
197 197
                 reportID: UUID(),
198
-                title: "Disappeared Records - \(displayName)",
198
+                title: "Missing Records - \(displayName)",
199 199
                 typeIdentifierFilter: typeID,
200 200
                 disappearedOnly: true
201 201
             )
@@ -203,7 +203,7 @@ final class DataTypeRecordListViewModel {
203 203
              .addedDiff(let typeID, let afterDate, let beforeDate, _, _):
204 204
             return HealthArchiveReportRequest(
205 205
                 reportID: UUID(),
206
-                title: "Added Records - \(displayName)",
206
+                title: "New Records - \(displayName)",
207 207
                 typeIdentifierFilter: typeID,
208 208
                 firstSeenAfter: afterDate,
209 209
                 firstSeenBefore: beforeDate
+17 -17
HealthProbe/Views/Dashboard/DashboardView.swift
@@ -186,7 +186,7 @@ struct DashboardView: View {
186 186
 
187 187
     private func failureImpact(_ reason: String) -> String {
188 188
         switch reason {
189
-        case "Not authorized": return "Excluded from checksum and anomaly detection"
189
+        case "Not authorized": return "Excluded from checksum and change analysis"
190 190
         case "Timeout":        return "Data unavailable — type skipped this run"
191 191
         case "Unsupported":    return "Not supported on this device or OS version"
192 192
         default:               return "Data unavailable"
@@ -206,28 +206,28 @@ struct DashboardView: View {
206 206
         }
207 207
         if !unauthorized.isEmpty {
208 208
             items.append("Open iOS Settings, choose HealthProbe, and re-enable Health read access for the listed metrics.")
209
-            items.append("After restoring access, create a new snapshot before relying on checksum or anomaly results.")
209
+            items.append("After restoring access, create a new snapshot before relying on checksum or change analysis.")
210 210
         }
211 211
         if !timedOut.isEmpty {
212 212
             items.append("Timeout reflects HealthProbe's configured limit, not missing Health data or permission.")
213 213
         }
214 214
         if viewModel.snapshotProgress == .incomplete {
215
-            items.append("Partial snapshots are excluded from anomaly detection. A complete snapshot is needed to resume integrity checks.")
215
+            items.append("Partial snapshots are excluded from change analysis. A complete snapshot is needed to resume integrity checks.")
216 216
         }
217 217
         return items
218 218
     }
219 219
 
220 220
     private func ambiguousDisappearanceNotes() -> [String] {
221 221
         [
222
-            "All values disappeared for the listed metric. Apple API does not allow HealthProbe to verify Health read authorization status.",
223
-            "Possible causes: the metric data was deleted, or Health read authorization was withdrawn."
222
+            "All values are missing for the listed metric in the current Health view. Apple API does not allow HealthProbe to verify Health read authorization status.",
223
+            "Possible causes: Health read authorization was withdrawn, or the metric is genuinely absent from the current Health store."
224 224
         ]
225 225
     }
226 226
 
227 227
     private func ambiguousDisappearanceActions() -> [String] {
228 228
         [
229 229
             "Treat the metric as unauthorized.",
230
-            "Treat the metric as totally deleted.",
230
+            "Treat the metric as absent from the current Health store.",
231 231
             "Cancel saving this snapshot."
232 232
         ]
233 233
     }
@@ -366,9 +366,9 @@ struct DashboardView: View {
366 366
         lines.append("INTERPRETATION_HINTS")
367 367
         lines.append("Partial snapshot: \(isPartialSnapshot ? "true" : "false")")
368 368
         lines.append("Requires user resolution: \(requiresResolution ? "true" : "false")")
369
-        lines.append("Failed metrics are excluded from checksum/anomaly detection")
369
+        lines.append("Failed metrics are excluded from checksum/change analysis")
370 370
         lines.append("Do not infer deletion from partial snapshots")
371
-        lines.append("All-values-disappeared metrics require classification before saving")
371
+        lines.append("All-values-missing metrics require review before saving")
372 372
         lines.append("Timeout means HealthProbe cancelled after configured timeout, not necessarily HealthKit denial")
373 373
         lines.append("")
374 374
         lines.append("CONFIGURATION")
@@ -595,7 +595,7 @@ struct DashboardView: View {
595 595
                 VStack(alignment: .leading, spacing: 2) {
596 596
                     Text(isComplete ? "Snapshot created successfully" : (requiresResolution ? "Snapshot needs review" : "Partial snapshot"))
597 597
                         .font(.subheadline.weight(.semibold))
598
-                    Text(isComplete ? "All monitored metrics loaded" : (requiresResolution ? "\(viewModel.ambiguousDisappearedMetrics.count) metric\(viewModel.ambiguousDisappearedMetrics.count == 1 ? "" : "s") need classification" : "\(progress.failedCount) metric\(progress.failedCount == 1 ? "" : "s") failed"))
598
+                    Text(isComplete ? "All monitored metrics loaded" : (requiresResolution ? "\(viewModel.ambiguousDisappearedMetrics.count) metric\(viewModel.ambiguousDisappearedMetrics.count == 1 ? "" : "s") need review" : "\(progress.failedCount) metric\(progress.failedCount == 1 ? "" : "s") failed"))
599 599
                         .font(.caption)
600 600
                         .foregroundStyle(.secondary)
601 601
                 }
@@ -658,7 +658,7 @@ struct DashboardView: View {
658 658
                         Text(metric.displayName)
659 659
                             .font(.subheadline.weight(.semibold))
660 660
                         Spacer()
661
-                        Text("all values disappeared")
661
+                        Text("all values missing")
662 662
                             .font(.caption)
663 663
                             .foregroundStyle(.secondary)
664 664
                     }
@@ -1109,17 +1109,17 @@ struct DashboardView: View {
1109 1109
             }
1110 1110
             .buttonStyle(.borderedProminent)
1111 1111
             .disabled(viewModel.isCreatingSnapshot)
1112
-            .accessibilityLabel("Treat disappeared metrics as unauthorized")
1112
+            .accessibilityLabel("Treat missing metrics as unauthorized")
1113 1113
 
1114 1114
             Button {
1115 1115
                 Task { await viewModel.treatAmbiguousMetricsAsDeleted(context: modelContext) }
1116 1116
             } label: {
1117
-                Label("Treat as Deleted", systemImage: "trash")
1117
+                Label("Treat as Missing", systemImage: "minus.circle")
1118 1118
                     .frame(maxWidth: .infinity)
1119 1119
             }
1120 1120
             .buttonStyle(.bordered)
1121 1121
             .disabled(viewModel.isCreatingSnapshot)
1122
-            .accessibilityLabel("Treat disappeared metrics as deleted")
1122
+            .accessibilityLabel("Treat missing metrics as absent from the current Health store")
1123 1123
 
1124 1124
             Button {
1125 1125
                 Task { await viewModel.discardSnapshot(context: modelContext) }
@@ -1436,9 +1436,9 @@ private struct AnomalySummarySection: View {
1436 1436
 
1437 1437
     var body: some View {
1438 1438
         if !unresolved.isEmpty {
1439
-            Section("Anomalies") {
1439
+            Section("Change Review") {
1440 1440
                 if criticalCount > 0 {
1441
-                    Label("\(criticalCount) critical \(criticalCount == 1 ? "anomaly" : "anomalies")",
1441
+                    Label("\(criticalCount) critical \(criticalCount == 1 ? "change" : "changes")",
1442 1442
                           systemImage: "exclamationmark.circle.fill")
1443 1443
                         .foregroundStyle(Color.criticalRed)
1444 1444
                 }
@@ -1449,10 +1449,10 @@ private struct AnomalySummarySection: View {
1449 1449
                 }
1450 1450
                 if !disappearedRecords.isEmpty {
1451 1451
                     VStack(alignment: .leading, spacing: 8) {
1452
-                        Label("Records disappeared", systemImage: "eye.slash")
1452
+                        Label("Records missing from current view", systemImage: "eye.slash")
1453 1453
                             .font(.subheadline.weight(.semibold))
1454 1454
                             .foregroundStyle(Color.criticalRed)
1455
-                        Text("Check Health read access for the affected metrics first. If you intentionally revoked access, confirm that here; otherwise investigate as possible data deletion.")
1455
+                        Text("Check Health read access for the affected metrics first. If you intentionally revoked access, confirm that here; otherwise treat this as a change that needs external review.")
1456 1456
                             .font(.caption)
1457 1457
                             .foregroundStyle(.secondary)
1458 1458
                         ForEach(disappearedRecords.prefix(3)) { anomaly in
+2 -2
HealthProbe/Views/DataTypes/DataTypeTemporalDistributionView.swift
@@ -249,8 +249,8 @@ struct DataTypeTemporalDistributionView: View {
249 249
                 .foregroundStyle(.primary)
250 250
 
251 251
             HStack(spacing: 12) {
252
-                statCard("Added", totalAdded, Color.healthyGreen)
253
-                statCard("Disappeared", totalDisappeared, Color.criticalRed)
252
+                statCard("New", totalAdded, Color.healthyGreen)
253
+                statCard("Missing", totalDisappeared, Color.criticalRed)
254 254
                 statCard("Unchanged", totalUnchanged, Color.neutralGray)
255 255
             }
256 256
 
+2 -2
HealthProbe/Views/DataTypes/RecordChangeComparisonCard.swift
@@ -89,7 +89,7 @@ struct RecordChangeComparisonCard: View {
89 89
                                 Image(systemName: "plus.circle.fill")
90 90
                                     .foregroundStyle(Color.healthyGreen)
91 91
                                 VStack(alignment: .leading, spacing: 2) {
92
-                                    Text("Added")
92
+                                    Text("New")
93 93
                                         .font(.caption)
94 94
                                         .foregroundStyle(.secondary)
95 95
                                     Text("\(addedCount)")
@@ -114,7 +114,7 @@ struct RecordChangeComparisonCard: View {
114 114
                                 Image(systemName: "minus.circle.fill")
115 115
                                     .foregroundStyle(Color.criticalRed)
116 116
                                 VStack(alignment: .leading, spacing: 2) {
117
-                                    Text("Disappeared")
117
+                                    Text("Missing")
118 118
                                         .font(.caption)
119 119
                                         .foregroundStyle(.secondary)
120 120
                                     Text("\(disappearedCount)")
+2 -2
HealthProbe/Views/DataTypes/RecordChangeEvolutionChart.swift
@@ -286,8 +286,8 @@ struct RecordChangeEvolutionChart: View {
286 286
 
287 287
     private var changeLegend: some View {
288 288
         HStack(spacing: 10) {
289
-            legendItem(color: Color.healthyGreen, label: "Added")
290
-            legendItem(color: Color.criticalRed, label: "Disappeared")
289
+            legendItem(color: Color.healthyGreen, label: "New")
290
+            legendItem(color: Color.criticalRed, label: "Missing")
291 291
             Spacer(minLength: 0)
292 292
         }
293 293
         .font(.caption2)
+9 -9
HealthProbe/Views/DataTypes/RecordChangeIndicator.swift
@@ -1,6 +1,6 @@
1 1
 import SwiftUI
2 2
 
3
-/// Visual indicator of record changes (added/disappeared) with navigation
3
+/// Visual indicator of record changes with navigation.
4 4
 struct RecordChangeIndicator: View {
5 5
     let addedCount: Int
6 6
     let disappearedCount: Int
@@ -27,7 +27,7 @@ struct RecordChangeIndicator: View {
27 27
 
28 28
             HStack(spacing: 12) {
29 29
                 changeButton(
30
-                    title: "Added",
30
+                    title: "New",
31 31
                     count: addedCount,
32 32
                     percentage: addedPercentage,
33 33
                     icon: "plus.circle.fill",
@@ -36,7 +36,7 @@ struct RecordChangeIndicator: View {
36 36
                 )
37 37
 
38 38
                 changeButton(
39
-                    title: "Gone",
39
+                    title: "Missing",
40 40
                     count: disappearedCount,
41 41
                     percentage: disappearedPercentage,
42 42
                     icon: "xmark.circle.fill",
@@ -124,8 +124,8 @@ struct RecordChangeIndicator: View {
124 124
             disappearedCount: 12,
125 125
             totalCount: 500,
126 126
             displayName: "Steps",
127
-            onAddedTap: { print("Added tapped") },
128
-            onDisappearedTap: { print("Disappeared tapped") }
127
+            onAddedTap: { print("New tapped") },
128
+            onDisappearedTap: { print("Missing tapped") }
129 129
         )
130 130
 
131 131
         RecordChangeIndicator(
@@ -133,8 +133,8 @@ struct RecordChangeIndicator: View {
133 133
             disappearedCount: 8,
134 134
             totalCount: 200,
135 135
             displayName: "Heart Rate",
136
-            onAddedTap: { print("Added tapped") },
137
-            onDisappearedTap: { print("Disappeared tapped") }
136
+            onAddedTap: { print("New tapped") },
137
+            onDisappearedTap: { print("Missing tapped") }
138 138
         )
139 139
 
140 140
         RecordChangeIndicator(
@@ -142,8 +142,8 @@ struct RecordChangeIndicator: View {
142 142
             disappearedCount: 0,
143 143
             totalCount: 1000,
144 144
             displayName: "Sleep",
145
-            onAddedTap: { print("Added tapped") },
146
-            onDisappearedTap: { print("Disappeared tapped") }
145
+            onAddedTap: { print("New tapped") },
146
+            onDisappearedTap: { print("Missing tapped") }
147 147
         )
148 148
     }
149 149
     .padding()
+4 -4
HealthProbe/Views/DataTypes/TemporalDistributionChartView.swift
@@ -59,7 +59,7 @@ struct TemporalDistributionChartView: View {
59 59
                     Circle()
60 60
                         .fill(Color.healthyGreen.opacity(0.7))
61 61
                         .frame(width: 8, height: 8)
62
-                    Text("Added")
62
+                    Text("New")
63 63
                         .font(.caption)
64 64
                         .foregroundStyle(.secondary)
65 65
                 }
@@ -68,7 +68,7 @@ struct TemporalDistributionChartView: View {
68 68
                     Circle()
69 69
                         .fill(Color.criticalRed.opacity(0.7))
70 70
                         .frame(width: 8, height: 8)
71
-                    Text("Disappeared")
71
+                    Text("Missing")
72 72
                         .font(.caption)
73 73
                         .foregroundStyle(.secondary)
74 74
                 }
@@ -233,11 +233,11 @@ struct TemporalDistributionChartView: View {
233 233
             Divider()
234 234
 
235 235
             HStack {
236
-                detailMetric("Added", bin.added, Color.healthyGreen)
236
+                detailMetric("New", bin.added, Color.healthyGreen)
237 237
                 Spacer()
238 238
                 detailMetric("Unchanged", bin.unchanged, Color.neutralGray)
239 239
                 Spacer()
240
-                detailMetric("Disappeared", bin.disappeared, Color.criticalRed)
240
+                detailMetric("Missing", bin.disappeared, Color.criticalRed)
241 241
             }
242 242
         }
243 243
         .padding(12)
+1 -1
HealthProbe/Views/Snapshots/DataTypeRecordListView.swift
@@ -204,7 +204,7 @@ private struct DataTypeDetailRow<Content: View>: View {
204 204
 #Preview {
205 205
     NavigationStack {
206 206
         DataTypeRecordListView(
207
-            title: "Disappeared Records",
207
+            title: "Missing Records",
208 208
             displayName: "Step Count",
209 209
             totalCount: 1500,
210 210
             mode: .disappeared(typeIdentifier: "HKQuantityTypeIdentifierStepCount"),
+7 -7
HealthProbe/Views/Snapshots/DataTypeSnapshotDetailView.swift
@@ -267,7 +267,7 @@ struct DataTypeSnapshotDetailView: View {
267 267
                 .navigationDestination(isPresented: $showAddedRecords) {
268 268
                     if let previous = previousSnapshot {
269 269
                         DataTypeRecordListView(
270
-                            title: "Added Records",
270
+                            title: "New Records",
271 271
                             displayName: displayName,
272 272
                             totalCount: diff.addedCount,
273 273
                             mode: addedRecordListMode(previous: previous),
@@ -278,7 +278,7 @@ struct DataTypeSnapshotDetailView: View {
278 278
                 }
279 279
                 .navigationDestination(isPresented: $showDisappearedRecords) {
280 280
                     DataTypeRecordListView(
281
-                        title: "Disappeared Records",
281
+                        title: "Missing Records",
282 282
                         displayName: displayName,
283 283
                         totalCount: diff.disappearedCount,
284 284
                         mode: disappearedRecordListMode(),
@@ -302,11 +302,11 @@ struct DataTypeSnapshotDetailView: View {
302 302
 
303 303
                         HStack {
304 304
                             quickStat(label: "New", value: "\(quick.added)", color: .healthyGreen)
305
-                            quickStat(label: "Disappeared", value: "\(quick.disappeared)", color: .criticalRed)
305
+                            quickStat(label: "Missing", value: "\(quick.disappeared)", color: .criticalRed)
306 306
                         }
307 307
 
308 308
                         if !quick.exact {
309
-                            Text("New/Disappeared are net values from snapshot delta. Exact split needs deep record analysis.")
309
+                            Text("New/Missing are net values from observation delta. Exact split needs deep record analysis.")
310 310
                                 .font(.caption2)
311 311
                                 .foregroundStyle(.secondary)
312 312
                         }
@@ -403,7 +403,7 @@ struct DataTypeSnapshotDetailView: View {
403 403
                         Text("Temporal Distribution")
404 404
                             .font(.headline.weight(.semibold))
405 405
                             .foregroundStyle(.primary)
406
-                        Text("Added / disappeared by time bucket")
406
+                        Text("New / missing by time bucket")
407 407
                             .font(.caption)
408 408
                             .foregroundStyle(.secondary)
409 409
                     }
@@ -450,9 +450,9 @@ struct DataTypeSnapshotDetailView: View {
450 450
             let prefix = typeDelta.countDelta > 0 ? "+" : ""
451 451
             return "Count delta: \(prefix)\(typeDelta.countDelta)."
452 452
         case .appeared:
453
-            return "Metric appeared in this snapshot."
453
+            return "Metric is new in this observation."
454 454
         case .disappeared:
455
-            return "Metric disappeared in this snapshot."
455
+            return "Metric is missing from this observation."
456 456
         }
457 457
     }
458 458
 
+4 -4
HealthProbe/Views/Snapshots/SnapshotDetailView.swift
@@ -522,10 +522,10 @@ private struct SnapshotArchiveTypeRowView: View {
522 522
     private var changeLabel: String {
523 523
         guard hasBaseline else { return "Stored" }
524 524
         if row.disappearedCount > 0 {
525
-            return "\(row.disappearedCount) disappeared"
525
+            return "\(row.disappearedCount) missing"
526 526
         }
527 527
         if row.appearedCount > 0 {
528
-            return "\(row.appearedCount) added"
528
+            return "\(row.appearedCount) new"
529 529
         }
530 530
         if row.representationChangedCount > 0 {
531 531
             return "\(row.representationChangedCount) changed"
@@ -583,9 +583,9 @@ private struct SnapshotTypeDeltaRow: View {
583 583
             let prefix = typeDelta.countDelta > 0 ? "+" : ""
584 584
             return "\(prefix)\(typeDelta.countDelta) records"
585 585
         case .appeared:
586
-            return "Appeared"
586
+            return "New"
587 587
         case .disappeared:
588
-            return "Disappeared"
588
+            return "Missing"
589 589
         case .unchanged:
590 590
             return "No changes"
591 591
         }
+5 -5
HealthProbe/Views/Snapshots/SnapshotsView.swift
@@ -263,8 +263,8 @@ private struct SnapshotRow: View {
263 263
             guard total > 0 else { return "No record changes" }
264 264
 
265 265
             var parts: [String] = []
266
-            if appeared > 0 { parts.append("\(appeared) added") }
267
-            if disappeared > 0 { parts.append("\(disappeared) disappeared") }
266
+            if appeared > 0 { parts.append("\(appeared) new") }
267
+            if disappeared > 0 { parts.append("\(disappeared) missing") }
268 268
             if changed > 0 { parts.append("\(changed) changed") }
269 269
             return parts.joined(separator: " • ")
270 270
         }
@@ -282,11 +282,11 @@ private struct SnapshotRow: View {
282 282
         }
283 283
 
284 284
         if deltaSummary.appearedMetricCount > 0 {
285
-            parts.append("\(deltaSummary.appearedMetricCount) metric appeared")
285
+            parts.append("\(deltaSummary.appearedMetricCount) metric new")
286 286
         }
287 287
 
288 288
         if deltaSummary.disappearedMetricCount > 0 {
289
-            parts.append("\(deltaSummary.disappearedMetricCount) metric disappeared")
289
+            parts.append("\(deltaSummary.disappearedMetricCount) metric missing")
290 290
         }
291 291
 
292 292
         if parts.isEmpty {
@@ -345,7 +345,7 @@ private struct SnapshotRow: View {
345 345
                     Image(systemName: "exclamationmark.triangle.fill")
346 346
                         .foregroundStyle(Color.warningAmber)
347 347
                         .font(.caption)
348
-                        .accessibilityLabel("Has anomalies")
348
+                        .accessibilityLabel("Has changes to review")
349 349
                 }
350 350
             }
351 351