fix(cancel): report a cancelled job as cancelled, not as a failure - #66
Merged
Merged
Conversation
Third attempt at the same report, and the first two missed the actual cause. The worker sends `complete(false)` on its way out of a cancellation as well as on a real failure, and `_handleStdoutLine` builds that into a CompletionResult with no `cancelled` flag -- so it defaults to false. `_pendingCompletion` is therefore ALWAYS set when the worker exits, which made the previous fix inert: it put `cancelled: _cancelRequested` in a `??` fallback behind `_pendingCompletion`, where it could never be reached. Cancelling still looked exactly like a failure. That distinction drives the queue. `_handleQueueItemCompletion` stops on `cancelled`, but on a plain failure it marks the item failed and calls `_processNextItem()`. So cancelling silently started the next job and left `_isQueueProcessing` true -- and the next conversion the user asked for sat on "processing" with a spinner and no progress, because the queue believed it was already busy. The decision is now a pure function, WorkerManager.completionFor, with the cancellation check first so it overrides the worker's own report rather than filling in for it. Why extract it: the previous two attempts were verified by tests that read the source. Those passed, and the bug shipped twice. This is testable directly, and the decisive case -- cancelled WITH a pending complete(false), which is the only case that actually occurs -- fails against the shipped logic and passes against this one. The stale source-scanning assertion in worker_manager_generation_test is reduced to checking the handler routes through completionFor and passes the flag; what it was trying to assert is now covered by behaviour.
StuartCameronCode
added a commit
that referenced
this pull request
Aug 8, 2026
Artifacts on the draft predate the cancellation-reporting fix in #66. Version string stays at 0.9.12; build number 30 -> 31.
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.
Third attempt at the same report. The first two missed the actual cause, so this
one starts from the evidence.
Cause
The worker sends
complete(false)on its way out of a cancellation as well ason a real failure, and
_handleStdoutLinebuilds that into aCompletionResultwith no
cancelledflag — so it defaults tofalse._pendingCompletionis therefore always set when the worker exits, which madethe previous fix inert: it put
cancelled: _cancelRequestedin a??fallbackbehind
_pendingCompletion, where it could never be reached. Cancelling stilllooked exactly like a failure.
That distinction drives the queue.
_handleQueueItemCompletionstops oncancelled, but on a plain failure it marks the item failed and calls_processNextItem(). So cancelling silently started the next job and left_isQueueProcessingtrue — and the next conversion the user asked for sat on"processing" with a spinner and no progress, because the queue believed it was
already busy.
Fix
The decision is now a pure function,
WorkerManager.completionFor, with thecancellation check first, so it overrides the worker's own report rather than
filling in for it.
Why it is extracted
Both previous attempts were verified by tests that read the source. Those passed,
and the bug shipped twice. This is testable directly, and the decisive case —
cancelled with a pending
complete(false), which is the only case thatactually occurs — fails against the shipped logic and passes against this one.
The stale source-scanning assertion is reduced to checking the handler routes
through
completionForand hands over the flag; what it was trying to assert isnow covered by behaviour instead of by text.