Skip to content

Import photo location history during onboarding - #201

Open
kyleve wants to merge 7 commits into
mainfrom
codex/onboarding-photo-import
Open

Import photo location history during onboarding#201
kyleve wants to merge 7 commits into
mainfrom
codex/onboarding-photo-import

Conversation

@kyleve

@kyleve kyleve commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Posted by an AI agent on kve's behalf.

Summary

  • User-facing: add an optional onboarding step that scans current-year Photos location metadata, previews an editable timeline, and imports only the history the user approves
  • User-facing: make photo access metadata-only and explicit: Where never loads or uploads image data, thumbnails, captions, or filenames
  • Internal: tag accepted samples with .photo provenance, use deterministic identifiers for repeat-import deduplication, and audit user corrections
  • Internal: integrate the approved draft with main's installation-local recording flow, current data generation, and deterministic region-attribution reconciliation
  • Internal: bump the backup archive to v5 and extend the external upgrader for the expanded sample-source vocabulary

Why

A first install otherwise starts the year empty even when the user's own photo library contains enough location metadata to reconstruct useful travel history. The import remains optional and provisional so Photos permission, origin filtering, and inferred stays are visible choices rather than background behavior.

Product behavior

After choosing and styling regions, onboarding offers to scan photo locations. The user can skip without writing anything, or grant Photos access and review the inferred stays. Date ranges can be corrected, excluded, and restored before approval.

Approving the draft does not create services early. It carries the draft to main's final per-device recording-choice page; final confirmation opens the real scope, commits and reconciles the selected regions, persists the photo transaction in the current data generation, configures recording, and completes onboarding. If region or photo persistence fails, the flow returns to the editable preview with an honest error state.

Privacy and origin filtering

The PhotoKit adapter reads only capture date, library-added date, coordinate, accuracy, source bucket, and hidden state.

PhotoKit does not expose the originating device identity. The implementation therefore applies a conservative “likely taken on this device” policy: main user library, image assets only, not hidden, valid location metadata, and capture time within five minutes of the library-added time. Shared-album and synced-library sources are excluded.

Architecture

  • Planning: PhotoHistoryPlanner creates an in-memory draft from the metadata-only PhotoLocationLibrary seam.
  • Onboarding: OnboardingFlowModel owns scan/review state and retains an approved draft until final confirmation; OnboardingView only renders and routes.
  • Persistence: DayJournal.importPhotoHistory(_:) commits deterministic .photo samples and audited authoritative corrections in one current-generation transaction, then runs the ordinary day-data fan-out.
  • Attribution: WhereServices.setPrimaryRegions(_:) waits for the serialized live attributor reconciliation before a dependent import can publish widgets or reminders.
  • Backups: v5 declares the new persisted SampleSource.photo vocabulary; the out-of-band upgrader carries v1–v4 archives forward without inventing photo records.

Design decisions

The five-minute capture/added-date heuristic favors false negatives over importing shared or synchronized photos. It cannot prove device origin because PhotoKit exposes no stable originating-device identifier.

The import waits for final recording confirmation rather than opening the service scope when the user approves the preview. This preserves main's onboarding invariant that device discovery may prepare the store, but services, App Intents, and GPS remain dormant until the user finishes choosing a world.

⚠️ Breaking changes

Backup format v5 is intentionally rejected by older readers. Archives containing .photo cannot be safely decoded by a build whose sample-source vocabulary predates this feature.

Compatibility

Existing v1–v4 archives can be reshaped to v5 with Where/Tools/upgrade-backup.rb; their existing source values require no photo-specific transformation. Photo samples remain device-agnostic user-approved data, so device-removal filtering preserves them.

Review focus

  • the conservative Photos origin heuristic and the limited-library behavior
  • the deferred draft handoff through OnboardingFlowModel.finish(using:)
  • region commit → live attribution reconcile → photo transaction ordering
  • backup v5 rejection and upgrader coverage

Backlog reconciliation

The completed photo-import item moved to Where/TODOs.md's completed section. Main's new onboarding follow-ups remain open.

Testing

  • ./swiftformat --lint — 0 files require formatting
  • ./xcstrings --lint — 7 catalogs match Xcode serialization
  • mise exec -- ruby Where/Tools/Tests/upgrade_backup_test.rb — 7 runs, 17 assertions passed
  • ./test --no-generate WhereCoreTests — 557 tests passed
  • ./test --no-generate WhereUITests — 425 tests passed
  • ./test --no-generate --all — 1,843 tests passed
  • ./test --no-generate --snapshots — every non-photo snapshot suite passed; the photo accessibility references changed under main's intrinsic-height capture behavior
  • ./test --no-generate --snapshots --only 'WhereUISnapshotTests/OnboardingPhotoImportViewSnapshotTests' — passed after visual review and reference refresh
  • swift run bumper config .
  • swift run bumper test .
  • swift run bumper lint . --timings

let capturedAt = asset.capturedAt,
let addedAt = asset.addedAt,
interval.contains(capturedAt),
abs(addedAt.timeIntervalSince(capturedAt)) <= Self.captureAddedTolerance,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on kve's behalf.

Review focus: PhotoKit does not expose capture-device identity. This five-minute capture-to-library-added tolerance is the best-effort proxy selected for this flow; it deliberately excludes obvious later saves but cannot rule out immediately shared or synchronized photos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant