M2-10 (1/2): read every route and bin it #54

Merged
wk merged 1 commit from m2-10-heat-map into main 2026-08-10 13:45:44 -04:00
Member

The first half of M2-10 (#39). Wes decided on 2026-08-10 that the issue ships as two PRs: this is
the whole-library read and the binning, with their tests. There is nothing to look at on a phone
yet, which is the point of splitting here — this is the half where the rules live and the half a
review can check.

What this is

HeatGrid — how often each 25-metre square of ground has been walked, across every recording.

  • A cell's weight is the number of distinct recordings that crossed it, never the number of
    fixes that landed in it. Counting fixes ranks five minutes at a bus stop above a street walked a
    hundred times, which inverts the question the map is asked.
  • The colour curve is logarithmic against the busiest cell, so a daily walk does not render every
    occasional street as indistinguishable from ground nobody has covered. The dark parts are the
    point.
  • A fix worse than 50 metres is not evidence about one square, and an unusable fix ends the run
    rather than merely being dropped. An unknown radius is not a bad one — a GPX from another tool
    carries none. Both rules are TrackFigures', at its numbers.
  • The line between two fixes is filled in only where they establish it: past 200 metres or 30
    seconds it is a hole in the route, and drawing across it draws a path through buildings.
  • A leg takes the shorter way round the world, and cells(in:) honours the same wrap.
  • The grid is sparse and its ground is capped at half a million cells, visibly — a library that
    reaches the cap goes on getting hotter where it already knows about and says it stopped taking
    new ground.

TrackLibrary — one cancellable pass off the main actor that reads every track the date filter
admits, folds each route into the grid as it is read, and drops its points before opening the next.
What it could not draw comes back as separate facts: files that will not read, and routes still
sitting unassembled in a parts folder. A directory that will not list throws rather than answering
"no routes".

The measurement

Reading every file on every open is the honest implementation and it was measured rather than
asserted to be fine. Debug simulator build: 100 tracks of 2,400 fixes — 5.5 s; 500 tracks of
300 fixes — 6.0 s
. Almost all of it is XML parsing. A real library is tens of tracked memos, so
nothing here is cached; reading concurrently is the obvious next lever and is deliberately not
pulled until the device number is in hand.

What was validated

  • make test — 668 tests, green. make lint — clean.
  • The tests were written by a subagent that had not written the implementation, reviewed
    adversarially by Codex, then strengthened against all eight of its findings. Ten mutations were
    run against the finished suite; each is caught by its intended test and nothing else.
  • The two security findings were verified by reverting each fix and confirming the test dies —
    the polar-latitude one crashes the test runner with exactly the trap it names.

What was NOT validated

  • Nothing ran on a device, and nothing ran on a screen. There is no UI in this PR. The device
    pass belongs to the second one.
  • The measurement is a debug simulator build over synthetic captures, not a release build on a
    phone against a real library. That number is taken at the second PR's hand-off.
  • "The filter is applied before the file is opened" has no test. It cannot be distinguished
    from "opened and then discarded" through the public API, and adding a seam for it would be a
    production change made to satisfy a test. What is tested is the accounting: removing the filter
    fails. The cost claim is unverified.

Still to come, in the second PR

The screen, the MKMapView renderer, the map icon on the memo list, the date filter's menu, the
empty states and their copy, the centring fix — and with it the documented exception to M2-06's
rule and the correction to both location purpose strings, which describe a use narrower than what
the app will do once a map can ask where the phone is.

Full reasoning and every Codex disposition: .agent/m2-10-heat-map/PLAN.md and SUMMARY.md.

⚠️ No screenshots of this feature anywhere — this repository is public and a heat map of daily walks
is a home address with extra steps. Every fixture is synthetic coordinates.

The first half of M2-10 (#39). Wes decided on 2026-08-10 that the issue ships as two PRs: this is the whole-library read and the binning, with their tests. There is nothing to look at on a phone yet, which is the point of splitting here — this is the half where the rules live and the half a review can check. ## What this is **`HeatGrid`** — how often each 25-metre square of ground has been walked, across every recording. - A cell's weight is the number of **distinct recordings** that crossed it, never the number of fixes that landed in it. Counting fixes ranks five minutes at a bus stop above a street walked a hundred times, which inverts the question the map is asked. - The colour curve is logarithmic against the busiest cell, so a daily walk does not render every occasional street as indistinguishable from ground nobody has covered. The dark parts are the point. - A fix worse than 50 metres is not evidence about one square, and an unusable fix ends the run rather than merely being dropped. An unknown radius is not a bad one — a GPX from another tool carries none. Both rules are `TrackFigures`', at its numbers. - The line between two fixes is filled in only where they establish it: past 200 metres or 30 seconds it is a hole in the route, and drawing across it draws a path through buildings. - A leg takes the shorter way round the world, and `cells(in:)` honours the same wrap. - The grid is sparse and its ground is capped at half a million cells, **visibly** — a library that reaches the cap goes on getting hotter where it already knows about and says it stopped taking new ground. **`TrackLibrary`** — one cancellable pass off the main actor that reads every track the date filter admits, folds each route into the grid as it is read, and drops its points before opening the next. What it could not draw comes back as separate facts: files that will not read, and routes still sitting unassembled in a parts folder. A directory that will not list throws rather than answering "no routes". ## The measurement Reading every file on every open is the honest implementation and it was measured rather than asserted to be fine. Debug simulator build: **100 tracks of 2,400 fixes — 5.5 s**; **500 tracks of 300 fixes — 6.0 s**. Almost all of it is XML parsing. A real library is tens of tracked memos, so nothing here is cached; reading concurrently is the obvious next lever and is deliberately not pulled until the device number is in hand. ## What was validated - `make test` — 668 tests, green. `make lint` — clean. - The tests were written by a subagent that had not written the implementation, reviewed adversarially by Codex, then strengthened against all eight of its findings. Ten mutations were run against the finished suite; each is caught by its intended test and nothing else. - The two security findings were verified by reverting each fix and confirming the test dies — the polar-latitude one crashes the test runner with exactly the trap it names. ## What was NOT validated - **Nothing ran on a device, and nothing ran on a screen.** There is no UI in this PR. The device pass belongs to the second one. - **The measurement is a debug simulator build over synthetic captures**, not a release build on a phone against a real library. That number is taken at the second PR's hand-off. - **"The filter is applied before the file is opened" has no test.** It cannot be distinguished from "opened and then discarded" through the public API, and adding a seam for it would be a production change made to satisfy a test. What is tested is the accounting: removing the filter fails. The cost claim is unverified. ## Still to come, in the second PR The screen, the `MKMapView` renderer, the map icon on the memo list, the date filter's menu, the empty states and their copy, the centring fix — and with it the documented exception to M2-06's rule and the correction to both location purpose strings, which describe a use narrower than what the app will do once a map can ask where the phone is. Full reasoning and every Codex disposition: `.agent/m2-10-heat-map/PLAN.md` and `SUMMARY.md`. ⚠️ No screenshots of this feature anywhere — this repository is public and a heat map of daily walks is a home address with extra steps. Every fixture is synthetic coordinates.
The first half of the heat map: the whole-library read and the binning,
with no screen yet. Split on Wes's call — this is the half where the
rules live and the half a review can check.

A cell's weight is how many distinct recordings crossed it, never how
many fixes landed in it, and the colour curve is logarithmic, because
the map is read for the ground that has no colour on it. Fixes worse
than fifty metres do not colour a twenty-five metre square, and the
line between two fixes is drawn only where they establish it.

Reading, binning and framing are one cancellable pass off the main
actor, and each route is dropped as soon as it is counted. Reading
every file on every open was measured rather than assumed: 5.5 s for
100 tracks of 2,400 fixes in a debug simulator build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
wk merged commit 22f0b2d18c into main 2026-08-10 13:45:44 -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!54
No description provided.