Skip to content

Separate a failed run from a wait that could not finish - #6

Merged
cfsmp3 merged 1 commit into
masterfrom
fix/wait-error-vs-outcome
Aug 15, 2026
Merged

Separate a failed run from a wait that could not finish#6
cfsmp3 merged 1 commit into
masterfrom
fix/wait-error-vs-outcome

Conversation

@cfsmp3

@cfsmp3 cfsmp3 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #5, whose fix was right and whose reasoning applies once more one layer over.

The same collision, one layer up

#5 established that run wait must not share an exit code with something that means a different thing. It still does. An ApiError with no code of its own exits 1, and 1 is what this command uses for a run you waited for failed:

server error (500)     -> exit 1
a run genuinely failed -> exit 1
not found (404)        -> exit 4     # mapped, so already distinct

Measured on master at fe8384a.

These want opposite responses. The gating script from #5's own description:

sp run wait $ID
case $? in 0) merge ;; 1) block ;; 9) retry ;; esac

A 500 from the platform takes the block branch — the branch is reported bad although nothing was learned about it — and retry, the correct response to a 500, is the one path the script will not take. Same shape as the original bug: a condition that is not an outcome wearing an outcome's code.

The fix

Codeless API failures exit 10. Errors that map to a code of their own keep it, so nothing that already worked changes:

condition before after
every run passed 0 0
a run failed or was canceled 1 1
timeout 9 9
API error mapping to a code (404, 401, 429, 409 …) 4, 6, 7, 8 unchanged
any other API failure (500, malformed response) 1 10

After this, 1 under run wait means exactly one thing: a run reported a bad verdict. Everything else that can go wrong has its own code, which is the property #5 was after.

case $? in
  0)     merge ;;
  1)     block ;;      # the branch is bad
  9|10)  retry ;;      # the platform is; the branch is unjudged
esac

Testing

Two tests, both of which fail without the change:

  • test_a_broken_wait_is_distinguishable_from_a_failed_run — a 500 and a failed run must not share a code
  • test_api_errors_that_map_to_a_code_keep_it — 404→4, 401→6, 429→7, 409→8 are untouched, so the remap is narrow

220 pass under both pytest and unittest discover; isort, pycodestyle, pydocstyle, mypy clean. README and AGENTS.md tables updated, including the worked case above.

Also

Drops a stray blank line that landed above constants.py's module docstring in #5. Harmless — __doc__ still resolves — but it is not meant to be there and no linter flags it.

#5 stopped `run wait` sharing exit code 2 with Click. The same collision was
still present one layer over: an API error with no code of its own exits 1, and
1 is what this command uses to say "a run you waited for failed".

Those want opposite responses. The gating script in #5's own description --

    case $? in 0) merge ;; 1) block ;; 9) retry ;; esac

-- blocks the branch when the platform returns a 500, and retrying, which is the
right answer to a 500, is the one branch it will not take. Nothing about the
code was learned, but the verdict says otherwise.

Codeless API failures now exit 10. Errors that map to a code of their own (3
through 8) keep it, and 1 is left meaning only what a run itself reported.

Also drops a stray blank line that landed above constants.py's module
docstring.
@cfsmp3
cfsmp3 merged commit 5519913 into master Aug 15, 2026
5 checks passed
@cfsmp3
cfsmp3 deleted the fix/wait-error-vs-outcome branch August 15, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant