fix: sync-status reports success for runs that did not succeed - #304
Open
alecsammon wants to merge 1 commit into
Open
fix: sync-status reports success for runs that did not succeed#304alecsammon wants to merge 1 commit into
alecsammon wants to merge 1 commit into
Conversation
sync-status only fails on a `failure` or `cancelled` conclusion, so every other non-success outcome sets the step to success: - `timed_out` — the triggered run hit a job timeout - `startup_failure` — the run never started (e.g. an invalid workflow file) - `stale` — the run was discarded before it could report - a `null` conclusion, which is what the API returns whenever the run has not completed, including after `wait-for-completion` gives up at its own timeout The last case is the easiest to hit: the wait loop exits on its timeout, warns, and then sync-status reads a `null` conclusion and passes the step — so a job that waited the full timeout reports success while the run it triggered is still going. Fail on the conclusions that unambiguously mean the run did not succeed, and on any run that has not reached `completed`. `neutral`, `skipped` and `action_required` keep passing as before, so runs that deliberately end without doing work are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alecsammon
marked this pull request as ready for review
September 10, 2026 10:56
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.
The problem
With
wait-for-completion: trueandsync-status: true, the step only fails on afailureorcancelledconclusion:Every other non-success outcome therefore sets the step to success:
timed_outstartup_failurestalenullThe
nullcase is the easiest one to hit in practice, and it does not need anything unusual to gowrong. When the polling loop gives up at
wait-timeout-secondsit sets its own status totimed_out,emits
core.warning, and falls through — andsync-statusthen reads anullconclusion from a runthat is still in progress and passes the step. So a job that waited the entire timeout and never
observed an outcome reports success.
That matters most where this action is used to gate something: a required status check, or a deploy
that waits on a rollout. In both cases "we never saw it finish" and "it finished successfully" are
reported identically.
The change
Fail when the run has not reached
completed, and fail on the conclusions that unambiguously mean itdid not succeed:
neutral,skippedandaction_requiredkeep passing exactly as before, so runs that deliberatelyend without doing work are unaffected. The existing
failureandcancelledmessages are unchanged.Is this breaking?
It is a behaviour change, but only for outcomes that are already not successes — a caller relying on a
timed_outorstartup_failurerun reporting success is relying on the bug. I have kept the ambiguousconclusions (
neutral,skipped,action_required) passing precisely to avoid changing behaviour where"success" is arguable.
If you would rather not change the default at all, I am happy to rework this behind an opt-in input
(something like
fail-on-any-non-success) — just say which you prefer and I will push that instead.Verification
The repository has no test suite, so I verified in three ways:
npm run lint(eslint + prettier) passes.npm run buildreproduces the committed bundle exactly — thedist/index.jsdiff is only thischange, with no unrelated bundler churn.
Note
src/main.tsdoes not currently typecheck undertsc --noEmitbecause@types/nodeis absent(
fsandprocessare unresolved). That is pre-existing and untouched by this change.