Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/snapshot-shards.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"WhereUISnapshotTests/DayRelabelViewSnapshotTests",
"WhereUISnapshotTests/OnboardingViewSnapshotTests",
"WhereUISnapshotTests/PersonalizationFeaturesViewSnapshotTests",
"WhereUISnapshotTests/RecentActivitySummaryViewSnapshotTests",
"WhereUISnapshotTests/RegionCustomizeViewSnapshotTests",
"WhereUISnapshotTests/RegionDaysViewSnapshotTests",
"WhereUISnapshotTests/ResolutionViewSnapshotTests",
Expand Down
5 changes: 0 additions & 5 deletions Where/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,6 @@ Rules the code enforces and agents must preserve:
- **Manual entries carry a `ManualEntryAudit`**; `DayJournal`'s write methods
take an explicit `audit:` (no default). An additive backfill can't downgrade
an authoritative row's regions, but the newer audit always wins.
- **`WhereServices.recentActivity`** (the on-demand Foundation Models
summarizer, behind `ActivitySummaryGenerating`) is distinct from
`WhereServices.summary` (the daily notification recap); model unavailability
surfaces as a typed reason, never a silent empty summary.

## Spans

Anything plausibly expensive is measured — `logger.measure(.name, budget:)` on
Expand Down
6 changes: 3 additions & 3 deletions Where/TODOs.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ The item format and the placement rule live in the root
- test(WhereShareExtension, WhereWidgets) [quick-win]: Close the two extension-shaped test gaps that don't need a new bundle pattern decision — `ShareEvidenceModel.buildPendingEvidence()` (`ShareEvidenceModel.swift:126-132`, documented as exposed for testing at `:124-125`, nothing tests it) and `WhereWidgetProvider`'s midnight reload policy (`WhereWidgetProvider.swift:34-41`, `.after(nextMidnight)` — the extension's core scheduling logic, untested in any target). (audit 2026-07-26)
- test(RegionKit) [quick-win]: Add `GeoJSONTests.swift`. The unsupported-geometry throw (`GeoJSON.swift:62-67`) and malformed-coordinate drop (`:124-128`) are untested, and `RegionCatalog.loadFromBundle()`'s degrade-to-empty-catalog behavior is asserted only at the log-event level (`RegionLogTests.swift:27-30`) rather than at runtime. `RegionKit/README.md:161-162` meanwhile claims GeoJSON decoding *is* covered — filed in the docs item below, and closing this item is what would make the README true. (audit 2026-07-26; re-verified 2026-08-09)
- test(WhereUI) [quick-win]: Add the missing namesake tests for `LocationNamer` (cache / coalescing) and `CalendarContentView`'s `scrolledForYear` scroll-reveal gate, which has hosting smoke only. (audit 2026-07-26)
- test(WhereIntents) [quick-win]: Test `RegionSpotlightIndexer` and `WhereIntentReader.recentActivity`. The indexer **shipped in PR #210** (`RegionEntity+Spotlight.swift:16-33`, wired in `Where/Where/Sources/RegularApplicationRuntime.swift` with a demo-mode skip) and arrived with no test of its own: nothing references `RegionSpotlightIndexer` or `indexRegions`, so neither the success log, the degraded failure log, nor the demo skip is verified. `RegionEntityTests.swift:27-45` covers `RegionEntity.tracked(from:)` — the indexer's *input* — which is easy to mistake for coverage of the indexer. `WhereIntentReaderTests` still has no `recentActivity` case. (audit 2026-07-26; re-verified 2026-08-09)
- test(WhereCore) [needs-design]: Close the namesake-test debt — **59** of the 118 implementation files have no same-named `*Tests.swift` (re-derived by basename 2026-08-09; was 28 of 87 at the July 26 audit, so the debt grew with the module rather than being worked down). Still uncovered among the originally named files: `FoundationModelSummaryGenerator`, `WidgetTimelineRefresher`, `BackupArchive` (`WherePreferences` closed 2026-08-05). `WhereCoreTests.swift` is an omnibus holding five suites, not just `YearReportTests` — also `SwiftDataStoreFactoryTests`, `SDLocationSampleTests`, `EvidenceKindTests`, `SampleSourceTests`. The basename count is a proxy: `Sources/Logging/*` event types and `SD*` record shells reasonably have no namesake file, so treat it as a trend line, not a work list. Split by concern as those files change rather than in one pass. (audit 2026-07-26)
- test(WhereIntents) [quick-win]: Test `RegionSpotlightIndexer`. The indexer **shipped in PR #210** (`RegionEntity+Spotlight.swift:16-33`, wired in `Where/Where/Sources/RegularApplicationRuntime.swift` with a demo-mode skip) and arrived with no test of its own: nothing references `RegionSpotlightIndexer` or `indexRegions`, so neither the success log, the degraded failure log, nor the demo skip is verified. `RegionEntityTests.swift:27-45` covers `RegionEntity.tracked(from:)` — the indexer's *input* — which is easy to mistake for coverage of the indexer. (audit 2026-07-26; re-verified 2026-08-09)
- test(WhereCore) [needs-design]: Close the namesake-test debt — **57** of the 114 implementation files have no same-named `*Tests.swift` (re-derived by basename 2026-08-09; was 28 of 87 at the July 26 audit, so the debt grew with the module rather than being worked down). Still uncovered among the originally named files: `WidgetTimelineRefresher`, `BackupArchive` (`WherePreferences` closed 2026-08-05). `WhereCoreTests.swift` is an omnibus holding five suites, not just `YearReportTests` — also `SwiftDataStoreFactoryTests`, `SDLocationSampleTests`, `EvidenceKindTests`, `SampleSourceTests`. The basename count is a proxy: `Sources/Logging/*` event types and `SD*` record shells reasonably have no namesake file, so treat it as a trend line, not a work list. Split by concern as those files change rather than in one pass. (audit 2026-07-26)
- docs(WhereCore) [quick-win]: Refresh stale doc claims — `WhereCore/README.md:66-67` claims every write reconciles (it omits summary and `setPrimaryRegions`, both filed above) and `:271-272` claims errors are never swallowed into an empty default (the badge path above contradicts it); `LocationIngestor.swift:473-474` says it logs through `os.Logger` when the code immediately below emits typed `WhereLog` events (`:477-481`); `RegionViewer/README.md:17-18` describes its **Source** mode as decoding "all US-state features in `us-states.geojson`", but `RegionGeometryCatalog.buildSourceOutlines()` (`:146`) walks `RegionCatalog.shared.entries` and decodes each region's **bundled per-region** GeoJSON — 54 files under `RegionKit/Sources/Resources/regions/`. The monolith still exists at `RegionKit/Tools/source/us-states.geojson`, but it is a build-time input to the extraction tooling, never bundled and never read at runtime, so the README describes the pipeline's input as if it were the app's data. The same file's hand-listed region set is stale for the same reason. `RegionKit/README.md:161-162` also claims GeoJSON decoding is covered, which the test item above would make true instead. (Two halves already closed: the `RootView.swift` "four screens" doc now reads three tabs, and the `WhereShareExtension/AGENTS.md:21` compose-model credit was fixed 2026-07-27.) (audit 2026-07-26; citations refreshed 2026-08-09)
- refactor(WhereCore) [quick-win]: Drop the remaining Core-API parameter defaults — `DayJournal.addEvidence(_:blob:)` (`DayJournal.swift:271`) and `WidgetDataReader`'s aggregator/attributor (`WidgetDataReader.swift:77-78`). The composition root already knows each value. (Two of the four filed are now done: `WherePreferences.init(store:)` and `SwiftDataStore.make(storage:)` both require the argument.) (audit 2026-07-26; re-verified 2026-08-09)
- convention(WhereIntents) [quick-win]: Small polish, all four parts still open — register `LogTripIntent` in `WhereShortcuts` (`Where/Where/Sources/WhereShortcuts.swift:11-56` registers five, no trip backfill) or document Shortcuts-only discovery; use `Calendar.whereIntents` for `LogDayIntent`'s default day instead of `date ?? Date()` (`LogDayIntent.swift:39`, no data impact today since `DayJournal` buckets Gregorian); log the App Group open failure behind `WhereIntentReader.todaySnapshot`'s `try?` (`WhereIntentReader.swift:17-18`); and wrap `RegionViewer`'s `RegionMapView` in `.whereBroadwayRoot()` (`RegionViewerApp.swift:15-18`) so the dev tool renders with app styling. (audit 2026-07-26; re-verified 2026-08-09)
Expand All @@ -84,7 +84,7 @@ The item format and the placement rule live in the root
- feat(WhereUI): Raw data browser (similar to the SwiftData browser). (human)
- docs(WhereUI): Add comments to strings in the xcstrings files. (human)
- refactor(WhereUI) [quick-win]: The widget fixtures' pinned instant is still `1_770_000_000` (02:40 UTC / Feb 1 evening Pacific, near a day boundary) at `PreviewSupport.swift:611` and `:652`. Moving it safely off midnight was skipped to avoid re-recording the widget references; PR #196's re-record makes that cheap now. (From the July 2026 snapshot-testing PR review; re-verified 2026-08-09)
- test(WhereUI) [needs-design]: Snapshot matrix gaps — `RecentActivitySummaryView.loading` (the sole user of `AppIconActivityIndicator`, so its `@MotionIsStatic` pinning is the one motion adoption without direct capture coverage; the suite covers only Loaded/Empty/Unavailable/Failed at `:148-171`), an explicit `LocationsView` empty state, and `ManualDayView`'s range-mode add have no snapshot case. (`YearView` gained its `Empty` case at `YearView.swift:149-151`.) (From the July 2026 snapshot-testing PR review; re-verified 2026-08-09)
- test(WhereUI) [needs-design]: Snapshot matrix gaps — an explicit `LocationsView` empty state and `ManualDayView`'s range-mode add have no snapshot case. (`YearView` gained its `Empty` case at `YearView.swift:149-151`.) (From the July 2026 snapshot-testing PR review; re-verified 2026-08-09)
- test(WhereUI) [quick-win]: Three screens have a `#Preview` but no `SnapshotProviding` conformance, so no image pins them — against the module convention that an image bundle, not a hosting smoke test, owns "does this screen render". They are the only Settings drill-ins without coverage: `AlertsSettingsView` (reminders, daily summary, issue alerts, the drift threshold, and a manual "find issues now"), `VisibleYearSettingsView`, and `RemovedDeviceView` (`Devices/RemovedDeviceView.swift`), the blocking CloudKit-removal recovery gate with the rejoin call to action — a screen a user only reaches when something has already gone wrong, which is the worst place for an unpinned regression. **Two of the three are not new debt:** Alerts and VisibleYear have been uncovered since PR #111 landed the drill-in restyle, so three prior audits missed them; only `RemovedDeviceView` arrived with PR #160. Every other screen added in this window — About, Devices, License, LifecycleFailure, ShareEvidence, Siri, Widget, CardDesignerStudio, Flyover root — did get coverage, so the convention holds and these are the exceptions. Add cases following `Settings/DevicesSettingsView.swift` (for Alerts, an authorized and a denied-notifications variant, toggles on and off). (audit 2026-08-09)

## Deferred snapshot-test flakiness
Expand Down
9 changes: 0 additions & 9 deletions Where/Where/Sources/WhereShortcuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,6 @@ struct WhereShortcuts: AppShortcutsProvider {
shortTitle: "Region on a Date",
systemImageName: "calendar.badge.clock",
)
AppShortcut(
intent: RecentActivitySummaryIntent(),
phrases: [
"Summarize my recent activity in \(.applicationName)",
"Where have I been in \(.applicationName)",
],
shortTitle: "Recent Activity",
systemImageName: "sparkles",
)
AppShortcut(
intent: LogDayIntent(),
phrases: [
Expand Down
12 changes: 3 additions & 9 deletions Where/WhereCore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
The domain layer of the **Where** app: it ingests location, persists it, rolls
it up into per-day and per-year region presence, finds the data-quality problems
worth resolving, and drives the side effects that follow a change (reminders,
widget snapshots, backups, on-device activity summaries). It is pure Swift +
Foundation + SwiftData + CoreLocation + FoundationModels — **no SwiftUI or
UIKit** — so all of it is unit-testable off-screen. It builds on
widget snapshots, backups). It is pure Swift + Foundation + SwiftData +
CoreLocation — **no SwiftUI or UIKit** — so all of it is unit-testable off-screen. It builds on
[`RegionKit`](../RegionKit) for coordinate→region lookup and logs through
[`Periscope`](../../Shared/Periscope) via the `WhereLog` facade.

Expand Down Expand Up @@ -120,7 +119,7 @@ one it belongs to rather than to a god-object:
target-owned advisory check-ins, and global removal tombstones sync independently. Another
installation can rename or remove a device identity, but cannot change its recording consent.
- **`LocationHistoryReader`** — the shared removal-aware read boundary used by reports, widgets,
recent activity, and foreground capture checks. It hides a removed identity's GPS samples at
and foreground capture checks. It hides a removed identity's GPS samples at
and after its earliest tombstone while keeping earlier raw storage, backups, legacy samples
without provenance, and user-asserted samples lossless.

Expand Down Expand Up @@ -154,8 +153,6 @@ one it belongs to rather than to a god-object:
sidecar tombstone before clearing recovery, so a cold launch can repair a preference write
that did not reach disk without offering the same archive again.
Check-ins are deliberately neither exported nor restored because they are live advisory status.
- **`RecentActivitySummarizer`** — an on-device Foundation Models narrative over
a selectable look-back `RecentActivityWindow`.
- **`InstallationRecordingContext`** — the device-local installation identity,
explicitly confirmed local recording choice, and stable timestamp for recreating
its immutable device profile idempotently.
Expand Down Expand Up @@ -270,9 +267,6 @@ rotates to a Reset child generation, and discards the retry queue only after com
incomplete causal generation DAG fails closed instead of mixing old and new state.
- **Failures surface.** Store methods are `async throws`; errors are logged via
`WhereLog` and left observable — never swallowed into an empty default.
- **Foundation Models may be unavailable.** `RecentActivitySummarizer` reports a
typed reason rather than a silently empty summary.

## Testing

Swift Testing in [`Tests/`](Tests) (`WhereCoreTests`), hosted in `StuffTestHost`.
Expand Down
32 changes: 0 additions & 32 deletions Where/WhereCore/Sources/Logging/RecentActivitySummarizerLog.swift

This file was deleted.

5 changes: 1 addition & 4 deletions Where/WhereCore/Sources/Logging/WhereLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct WhereRoot: LogEvent {
/// Every logger derives from one `"Where"` root `Log` and emits into the
/// process-wide Periscope system (``Periscope/shared``). Collaborators that
/// belong together sit under a shared group scope (``location``, ``reminders``,
/// ``backup``, ``widgets``, ``session``, ``evidence``, ``recentActivity``);
/// ``backup``, ``widgets``, ``session``, ``evidence``);
/// everything else hangs directly off ``root``. A collaborator derives its own
/// typed leaf — `WhereLog.location(LocationIngestorLog.self)` — so its events
/// carry a structured payload the log viewer can decode, and the loggers form a
Expand Down Expand Up @@ -45,8 +45,6 @@ public enum WhereLog {
public static let session = group(.session)
/// Evidence capture/list/detail view models.
public static let evidence = group(.evidence)
/// On-device recent-activity summarization.
public static let recentActivity = group(.recentActivity)
/// The read/derive path everything else consumes: year reports, calendar
/// layout, and the data-issue scan. Mostly a span subtree — these
/// collaborators throw their failures rather than logging them, so what's
Expand All @@ -66,7 +64,6 @@ public enum WhereLog {
case widgets
case session
case evidence
case recentActivity
case reporting
}
}
Loading
Loading