M2-06: Collect location while a recording runs #47

Merged
wk merged 1 commit from m2-06-location-collection into main 2026-08-09 19:21:16 -04:00
Member

Closes #33.

Starts a CLLocationManager at the instant a recording is running and stops it at the top of
stop(). Points are held in memory and shown in Settings → Diagnostics; nothing is written to
disk, which is #34.

What it does

Nothing is collected unless a recording is running. M2-04 chose Always authorization, so iOS is
no longer what enforces that — it is a promise only this code keeps. Collection begins in
markRunning, before the first await, because a stop arriving inside that suspension would
otherwise be followed by collection starting for a recording that had already ended.

Every way consent can end arrives on one path. The toggle, a revocation in iOS Settings, a
restriction and a denial at the prompt all stop the manager and drop what that recording collected.
Handling the CLError alone would miss the toggle; handling the toggle alone would miss a
revocation.

A fix taken before the recording began is refused. startUpdatingLocation() can deliver a
cached one, and a point from ten minutes earlier is exactly what the rule forbids.

Core Location's sentinels are decoded, not carried. A negative speed, course or accuracy
means "could not measure", and a negative horizontalAccuracy means the coordinates are invalid —
that fix never becomes a point. Nothing else is dropped; accuracy filtering stays downstream.

The rules live in TrackSession, a value, so the privacy behaviour is unit tests rather than a walk
with a phone. LocationConsent and LocationUpdating are seams for the same reason.

The route-repair half of AudioRecorder moved into an extension in the same file — the class went
three lines over the type-body limit, and that half is where the growth was.

Decisions from Wes, 2026-08-09

  • The background location indicator is on. M2-04 bought the choice; this spends it on showing
    the indicator. What Always buys is that it is absent when no recording is running.
  • Turning the toggle off mid-recording stops collection and drops that recording's route,
    superseding M2-04's disable() comment about affecting only recordings that have not started.

Codex

Four passes, artifacts in .agent/m2-06-location-collection/. No critical findings in any.

The ones that changed the code: reduced accuracy plus the stale-fix rule can leave a short memo with
no route (the Settings sentence now says so); speedAccuracy and courseAccuracy were not being
consulted; span read the ends of the array rather than the extremes, which is wrong when Core
Location delivers a fix late; and the app-switcher snapshot would have written the last coordinate
to disk, outliving the app it was promised to die with.

The test round found two real bugs, both fixed and pinned: a recording made with the opt-in off
showed the previous recording's route, and a CLError.denied that outran the authorization change
left updates running.

SUMMARY.md carries every finding with its disposition, including the rejections.

Validated

435 tests, 41 suites, passing. make lint clean. On the device: recording with the toggle on
collects points, the blue location indicator appears, and it goes away at stop.

Not validated

  • CoreLocationUpdates has no unit test — every collector test replaces it, so a no-op
    startCollecting would pass the whole suite and collect nothing on a phone. The device pass is
    the only evidence it works.
  • The AudioRecorder call sites are not covered. The ordering argument for begin sitting before
    the await is reasoning and a comment, not a test; AudioRecorder needs an audio session and a
    microphone, which AGENTS.md keeps out of StashTests.
  • Reduced accuracy has not been exercised on a phone, so the claim that a short memo may end with no
    route is derived from the stale-fix rule rather than observed.
  • The toggle-off and revoke-mid-recording paths were not walked through on the device — they are
    covered by unit tests through the real collector, not by hand.
Closes #33. Starts a `CLLocationManager` at the instant a recording is running and stops it at the top of `stop()`. Points are held in memory and shown in Settings → Diagnostics; nothing is written to disk, which is #34. ## What it does **Nothing is collected unless a recording is running.** M2-04 chose Always authorization, so iOS is no longer what enforces that — it is a promise only this code keeps. Collection begins in `markRunning`, before the first await, because a stop arriving inside that suspension would otherwise be followed by collection starting for a recording that had already ended. **Every way consent can end arrives on one path.** The toggle, a revocation in iOS Settings, a restriction and a denial at the prompt all stop the manager and drop what that recording collected. Handling the `CLError` alone would miss the toggle; handling the toggle alone would miss a revocation. **A fix taken before the recording began is refused.** `startUpdatingLocation()` can deliver a cached one, and a point from ten minutes earlier is exactly what the rule forbids. **Core Location's sentinels are decoded, not carried.** A negative `speed`, `course` or accuracy means "could not measure", and a negative `horizontalAccuracy` means the coordinates are invalid — that fix never becomes a point. Nothing else is dropped; accuracy filtering stays downstream. The rules live in `TrackSession`, a value, so the privacy behaviour is unit tests rather than a walk with a phone. `LocationConsent` and `LocationUpdating` are seams for the same reason. The route-repair half of `AudioRecorder` moved into an extension in the same file — the class went three lines over the type-body limit, and that half is where the growth was. ## Decisions from Wes, 2026-08-09 - **The background location indicator is on.** M2-04 bought the choice; this spends it on showing the indicator. What Always buys is that it is absent when no recording is running. - **Turning the toggle off mid-recording stops collection and drops that recording's route**, superseding M2-04's `disable()` comment about affecting only recordings that have not started. ## Codex Four passes, artifacts in `.agent/m2-06-location-collection/`. No critical findings in any. The ones that changed the code: reduced accuracy plus the stale-fix rule can leave a short memo with no route (the Settings sentence now says so); `speedAccuracy` and `courseAccuracy` were not being consulted; `span` read the ends of the array rather than the extremes, which is wrong when Core Location delivers a fix late; and the app-switcher snapshot would have written the last coordinate to disk, outliving the app it was promised to die with. The test round found two real bugs, both fixed and pinned: a recording made with the opt-in off showed the *previous* recording's route, and a `CLError.denied` that outran the authorization change left updates running. `SUMMARY.md` carries every finding with its disposition, including the rejections. ## Validated 435 tests, 41 suites, passing. `make lint` clean. On the device: recording with the toggle on collects points, the blue location indicator appears, and it goes away at stop. ## Not validated - `CoreLocationUpdates` has no unit test — every collector test replaces it, so a no-op `startCollecting` would pass the whole suite and collect nothing on a phone. The device pass is the only evidence it works. - The `AudioRecorder` call sites are not covered. The ordering argument for `begin` sitting before the await is reasoning and a comment, not a test; `AudioRecorder` needs an audio session and a microphone, which `AGENTS.md` keeps out of `StashTests`. - Reduced accuracy has not been exercised on a phone, so the claim that a short memo may end with no route is derived from the stale-fix rule rather than observed. - The toggle-off and revoke-mid-recording paths were not walked through on the device — they are covered by unit tests through the real collector, not by hand.
Starts a CLLocationManager at the instant a recording is running and stops
it at the top of stop(). Points are held in memory and shown in Settings →
Diagnostics; nothing is written to disk, which is M2-07.

Nothing is collected unless a recording is running, and since M2-04 chose
Always authorization that is a promise only this code keeps. Every way
consent can end — the toggle, a revocation in iOS Settings, a restriction,
a denial — stops the manager and drops what that recording collected. A fix
taken before the recording began is refused, because startUpdatingLocation
can hand over a cached one.

The rules live in TrackSession, a value, so the privacy behaviour is unit
tests rather than a walk with a phone. LocationConsent and LocationUpdating
are seams for the same reason.

Decided with Wes: the background location indicator is on, and turning the
toggle off mid-recording stops collection immediately.
wk merged commit 3132add1b7 into main 2026-08-09 19:21:16 -04:00
wk deleted branch m2-06-location-collection 2026-08-09 19:21:16 -04:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Stash/stash-ios!47
No description provided.