Count the records that never decrypted, instead of losing them between two lines - #152
Merged
Merged
Conversation
…n two lines Pin moves 4f940158 -> ddc7f234, which carries three fixes from jamorenom's review of #144 plus the merge of ubrt's `current_keys()` work. **`fetch` built its "not exportable" list by walking what came back.** Anything `decrypt_records` dropped was therefore absent from that list *and* from the candidates at once, and the user got a shorter table with nothing to explain it. The comment two lines below has been saying why that is bad the whole time: Named rather than dropped quietly: "fewer tags than expected" and "some of those were never tags" look identical from outside. Which is exactly right, and the layer above it was dropping quietly. This is the residue of #89's fix - that bug was one unreadable item ending the whole export, fixed by skipping - and it was reported by the same person, who had to count raw records by hand to notice. FindMy.py had always computed the tally and logged it; it now returns it, so `Fetched` carries `undecryptable` and `first_miss`. **Counted separately from `skipped`, and not phrased as a failure.** Those are different things: `skipped` is per accessory and names one, because something was read and set aside for a stated reason. These were never read - there is no beacon id to name, the id being inside what would not open - so folding them in would invent rows. And a zone legitimately holds records belonging to other parties, so a non-zero count is usually nothing. Both surfaces say so, and say the one thing that would make it worth a second look: it being close to the number of tags somebody expected. `first_miss` rides along because the count cannot tell "somebody else's records" from "we are comparing keys in the wrong encoding", and those lead in opposite directions. Five tests, verified by breaking three things: dropping the count, counting only the first reason (the old undercount), and folding them into the not-exportable rows - three, one and one red. Driven with `asyncio.run` like the rest of the suite; there is no pytest-asyncio here. Also in the pin, unused by the exporter but now available: a 401 on a session with no password raises `UnauthorizedError` rather than a bare `ValueError` three frames down, and a secondary-key match no longer ratchets alignment forward past the accessory. The app does not reach that second one - it drives `_fetch_key_reports` with its own `index_by_key`, built from `keys_between`, which dedupes ascending and so already holds the lowest index. 560 exporter tests and 227 bridge tests pass. All four pins moved together, per rule 14. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parawanderer
temporarily deployed
to
Android Build
August 23, 2026 18:34 — with
GitHub Actions
Inactive
parawanderer
temporarily deployed
to
Android Build
August 23, 2026 18:34 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pin moves
4f940158→ddc7f234, carrying the three fixes from @jamorenom's review of #144, plus the merge of @ubrt'scurrent_keys()work.The exporter was undercounting its own inventory
fetch()builds its "not exportable" list by walkinggroup_records(decrypted)— so anythingdecrypt_recordsdropped was absent from that list and the candidates at once. A shorter table, nothing to explain it.The comment two lines below has been saying why that's bad the whole time:
Which is right, and the layer above it was dropping quietly. It's the residue of #89's fix — that bug was one unreadable item ends the whole export, fixed by skipping — and it was reported by the same person, who had to count raw records by hand to notice.
FindMy.py always computed the tally and logged it. It now returns it, so
Fetchedcarriesundecryptableandfirst_miss.Counted separately, and not called a failure
skippedis per accessory and names one: something was read, understood, set aside for a stated reason. These were never read — there is no beacon id to name, because the id is inside what would not open. Folding them in would invent rows.And a non-zero count is usually nothing: a zone legitimately holds records belonging to other parties. Both surfaces say so, and say the one thing that would make it worth a second look — it being close to the number of tags you expected.
first_missrides along because the count cannot tell "somebody else's records" from "we are comparing keys in the wrong encoding", and those lead in opposite directions.Also in the pin
Unused by the exporter, but worth knowing:
UnauthorizedErrorrather than a bareValueError("No username or password specified")three frames downThe app does not reach that second one. It never calls FindMy.py's accessory fetch; it drives
_fetch_key_reportswith its ownindex_by_key, built fromkeys_between, which dedupes ascending and therefore already holds the lowest index — the conservative value the upstream fix now produces.Testing
Five tests, verified by breaking three things: dropping the count, counting only the first reason (the old undercount), and folding them into the not-exportable rows — three, one and one red respectively.
560 exporter tests and 227 bridge tests pass. All four pins moved together, per rule 14.
PR description summarised by Claude Code.