ci: detect unreleased work and silently skipped publishes - #70
Merged
Conversation
abrichr
force-pushed
the
ci/release-health
branch
3 times, most recently
from
July 27, 2026 15:48
8023d83 to
0bc6deb
Compare
Four native releases here never published and nothing said so.
desktop-v0.12.0, desktop-v0.8.0, desktop-v0.7.0 and desktop-v0.1.0 are
immutable public tags with no release object behind them:
desktop-v0.12.0 failed macOS x86_64 build run 30169968004
desktop-v0.8.0 ungranted native-release approval run 29799291231
desktop-v0.7.0 wholly cancelled run run 29756479649
desktop-v0.1.0 ModuleNotFoundError: tomllib on a
pre-3.11 runner run 29514474536
Different causes, one shape: a human-gated publish job that is skipped,
cancelled, or fails produces no signal. native-release.yml has no failure
notifier at all, and an `if: failure()` step could not have seen `cancelled`
or `skipped` anyway.
Nothing here publishes anything. The engine lane stays workflow_dispatch-only
and the native lane keeps its native-release reviewer gate on publish-draft,
the single point where installer bytes are written. That design exists
because Flow 1.13.0/1.14.0 were yanked for shipping AGPL benchmark files.
This adds visibility and prints the command a human would run.
scripts/check_release_health.py evaluates three state-based detectors per
release lane declared in .github/release-health.json (engine: v*, PyPI;
native: desktop-v*, installers):
unreleased-work main carries commits past the newest release tag that
WOULD bump the version under this repository's own
[tool.semantic_release.commit_parser_options], main is
green, no run is in flight, and the oldest is past the
grace window.
tag-without-release a release tag has no release object, no run for it is
queued/waiting/running, and it is past the grace
window -- reporting failure/cancelled/skipped, or that
the workflow never started at all.
unpublished-release the newest tag's version is absent from PyPI past the
CDN grace window.
State-based rather than event-based on purpose: "a run was cancelled" is not
the alarm, "something is still unpublished" is. The launcher's release
workflow cancels superseded runs constantly, and a guard that fired on those
would go the way of the platform-manifest drift check -- correct, always red,
and therefore unread.
The native lane's grace is 6h because a full macOS/Windows/Linux Tauri build,
smoke test and attest legitimately takes far longer than a wheel, and a run
still `waiting` on the native-release approval gate suppresses the detector
regardless of age: an unanswered human gate is a pending decision, not a
failure. --self-test covers both, along with PyPI CDN lag, an unreachable
index, in-flight runs, a red or pending main, and the ~90s post-tag
release-commit window.
Python is pinned to 3.12 for the obvious reason, and the script imports
tomllib defensively so an old interpreter degrades to documented defaults
with a warning instead of repeating run 29514474536.
One open issue per repository, rewritten in place, closed automatically.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
abrichr
force-pushed
the
ci/release-health
branch
from
July 27, 2026 15:50
0bc6deb to
db94449
Compare
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.
Why
Four native releases here never published, and nothing said so.
desktop-v0.12.030169968004desktop-v0.8.0native-releaseenvironment approval29799291231desktop-v0.7.029756479649desktop-v0.1.0ModuleNotFoundError: No module named 'tomllib'on a pre-3.11 runner29514474536Four different causes, one shape: nothing alerts when a human-gated publish job is skipped, cancelled, or fails. Each left an immutable public tag promising installers that were never published.
native-release.ymlhas no failure notifier at all, and anif: failure()step could not have seencancelledorskippedanyway.The sibling failures elsewhere are the same shape:
openadapt-capturePR #51 removed a path that uploaded raw audio waveforms to a third party, merged tomain, and sat unreleased while PyPI's only installable version still contained it; and the launcher'sRelease and PyPI Publishruns30275153205/30226357239were cancelled seconds in.What this is not
Nothing here publishes anything. The engine lane stays
workflow_dispatch-only and the native lane keeps itsnative-releasereviewer gate onpublish-draft, the single point where installer bytes are written. That design exists because Flow 1.13.0 and 1.14.0 were yanked for shipping AGPL benchmark files. This adds visibility and prints the command a human would run.What it is
scripts/check_release_health.py(stdlib only — no dependency install, no lockfile, no cache) evaluates three state-based detectors per release lane declared in.github/release-health.json(engine:v*+ PyPI;native:desktop-v*+ installers):unreleased-workmaincarries commits past the newest release tag that would bump the version under this repo's own[tool.semantic_release.commit_parser_options],mainis green, no release run is in flight, and the oldest such commit is past the grace windowtag-without-releasefailure/cancelled/skipped, or that the workflow never started at allunpublished-releaseState-based rather than event-based on purpose. "A release run was cancelled" is not the alarm; "something is still unpublished" is. The launcher's release workflow cancels superseded runs constantly under its concurrency group, and a detector that fired on those would go the way of the launcher's platform-manifest drift check — correct, permanently red after every release, and therefore indistinguishable from noise.
Proof it fires: run live against this repository right now
The issue body it would file, verbatim:
It independently reproduced all four evidence items with their exact run IDs and conclusions, from live state — no fixture involved.
False positives are the failure mode
--self-testenumerates every transient these repositories actually produce and proves the detector stays quiet on each. It runs offline in CI whenever the detector itself changes.Two windows matter most here:
native-releaseapproval gate. A run stillwaitingon it suppressestag-without-releaseregardless of the tag's age. An unanswered human gate is a pending decision, not a failure. Only once the run reaches a terminal non-success conclusion — which is what happened todesktop-v0.8.0— does it alert.Triggers and cost
scheduleevery 3h — the actual detector; with the 4h grace it bounds "how long can a releasable commit sit unnoticed" to ~7h.workflow_runonRelease and PyPI PublishandNative Installer Releasecompletedwith a non-successconclusion — this is the trigger that seescancelledandskipped.pull_requeston the detector's own files — runs--self-test.One stdlib step, no dependency install, no cache. Python is pinned to
3.12for the obvious reason, and the script importstomllibdefensively so an old interpreter degrades to documented defaults with a warning instead of repeating run29514474536.Issue behaviour
Exactly one open issue per repository, rewritten in place on each run (editing a body does not notify, so a persistent gap is not a daily ping) and closed automatically with a comment once every gap is closed. Never duplicated.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM