Follow-ups from reviewing #17 and #24: CI, a queue perf fix, and issue #19 - #26
Merged
Merged
Conversation
A CancellationError that neither `cancel` nor a requeue claimed is an interruption, not a decision — a staging read dropped as iOS suspended the app, say. It landed in a terminal `.cancelled` row labelled only "Cancelled": no reason, no way back, and still sitting there when a later scan backed the same photo up and added an "Already backed up" row beside it. That pair is what #19 reported. Such a row now retries like any other interruption and, if it keeps happening, fails in words the user can act on. `.cancelled` is left meaning one thing only — the user stopped it — so the label can say so. Also count a row dropped because its photo left the library as settled. A continued backup reports its total as settled plus unfinished, so dropping one without counting it walked the Live Activity's total backwards mid-run. The test that pinned the old count is updated with the reason. Fixes #19 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The follow-up hook runs on the main actor as every row finishes, and it fetched the asset twice: once to see whether the item is a Live Photo, once to see whether it carries a Google Photos edit. On a full-library backup that is two library reads per item on the thread drawing the activity list, which is the list #8 reports as slow. One fetch now answers both. The cheap checks go first, so the resource read behind the edit-base test still only runs for an adjusted asset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both of the last two pull requests merged with no checks reported: there was no workflow that built the app or ran its 191 tests. One now runs on every pull request and every push to main, reading its simulator from what the runner actually has so an image bump cannot silently break it. The release build's Xcode pin also failed open — a missing Xcode_16.4.app fell through to whatever the image shipped, which would quietly have broken the one thing building in CI is for: a binary tied to the commit and the toolchain it claims. It now fails the run and says what is installed instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first thing CI caught. `pauseForRateLimit` sets `rateLimitPauseReason` synchronously but only reaches the sleeper once its task body runs, so waiting on the reason alone could observe the pause before any delay had been requested. That ordering held on a fast machine and lost on a loaded GitHub runner, where the assertion saw no delay at all. The wait now covers both. The queue itself is not affected: the reason is what gates `pump`, and it is in place before either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Three follow-ups to the two pull requests just merged, plus the bug they surfaced.
Run the tests on every pull request
Both #17 and #24 merged with no checks reported — there was no workflow that built the app or ran its tests.
#24was 1,941 lines across 25 files, verified only by its author running the suite locally.tests.ymlnow runsxcodebuild teston every PR and every push tomain. It resolves its simulator from what the runner actually has installed rather than naming a device that disappears on the next image bump, and it reads the verdict back out of the log, because piping xcodebuild throughgrepfor a readable summary otherwise swallows the exit status.The release build's Xcode pin also failed open:
If GitHub retires that image, this silently builds on whatever Xcode ships instead — which defeats the point of building in CI at all, since the release note claims a specific toolchain. It now fails the run and prints what is installed.
Ask the photo library once per finished row, not twice
#24's follow-up hook runs on the main actor as every row finishes, and fetched the asset twice — once for the Live Photo check, once for the Google Photos edit check. On a full-library backup that is twoPHAsset.fetchAssetscalls per item on the thread drawing the activity list, which is the list #8 reports as slow.One fetch now answers both, with the cheap checks first so the resource read behind the edit-base test still only runs for an adjusted asset.
Issue #19: "Items getting cancelled without knowing why"
Root cause found. A
CancellationErrorthat neithercancelnor a requeue claimed reached this:That is an interruption, not a decision — a staging read dropped as iOS suspended the app, say. It became a terminal row labelled only
"Cancelled": no reason, no way back, and still sitting in the list when a later scan backed the same photo up and added an "Already backed up" row beside it. That pair is exactly the screenshot in #19.Such a row now retries like any other interruption, and if it keeps happening it fails in words the user can act on.
.cancelledis left meaning one thing only — the user stopped it — so the label now says "Stopped by you".Three regression tests cover the retry, the recovery, and the label.
Also: a dropped row counts as settled
A continued backup reports its Live Activity total as settled plus unfinished.
#24drops a row whose photo left the library without counting it as either, so the total walked backwards mid-run. The test that pinned the old count is updated with the reasoning rather than quietly flipped.Test plan
xcodebuild teston Xcode 16.4: 191 tests, 2 skipped, 0 failures (188 before, 3 added here).make-ipa.shdoes.simctloutput.Not verified: the workflows themselves have never run — this PR is the first thing that will exercise them.
🤖 Generated with Claude Code