ci: detect unreleased work and silently skipped publishes - #57
Merged
Conversation
abrichr
force-pushed
the
ci/release-health
branch
from
July 27, 2026 15:39
cf58d33 to
dc5f5e9
Compare
Member
Author
|
Holding this for an 80/20 revision before merge. The signal is worth adding, but 1,399 new lines plus eight scheduled runs per day is disproportionate for release visibility and creates a new subsystem to maintain. Please replace it with one small release-state check attached to the cheapest justified existing event or cadence, keeping only the actionable gap contract. Two correctness details must also change:
Please exercise the simplified path end to end against a temporary or recorded issue state, then re-run exact-head CI. Nothing here needs to publish automatically. |
abrichr
force-pushed
the
ci/release-health
branch
3 times, most recently
from
July 27, 2026 15:48
bb99b10 to
3c577f9
Compare
PR #51 removed a hosted-recognizer path that uploaded raw audio waveforms to a third party. It merged to main and sat unreleased while PyPI's only installable version, 1.2.0, still contained the upload path -- and two downstream packages resolve openadapt-capture>=1.1.0 unpinned. Nothing noticed. It shipped as 1.2.1 only because a human happened to look. Nothing publishes here. Releases stay workflow_dispatch-only by deliberate design; this adds visibility only. scripts/check_release_health.py evaluates three state-based detectors per release lane declared in .github/release-health.json: 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. Reports 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, not 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 guard that fired on those would go the way of the platform-manifest drift check -- correct, permanently red, and therefore unread. Every transient this repository actually produces is enumerated and proven quiet by --self-test: the chore: release and reconcile commits inside the ~90s post-tag window, the launcher's bare-version release commit subject, PyPI CDN lag, an unreachable PyPI, in-flight runs, a run waiting on a human approval gate, a red or pending main, and a tag whose artifact is already on PyPI. The self-test also fires the detectors against the exact historical states of this miss and of the four openadapt-desktop tags that published nothing. Triggers: schedule every 3h (the detector), workflow_run on a non-successful release run (cancelled and skipped are invisible to if: failure()), and pull_request to run --self-test when the detector itself changes. One stdlib-only step, no dependency install. Python is pinned to 3.12 because desktop run 29514474536 died on a missing tomllib on a pre-3.11 runner; the script still degrades to documented defaults rather than crashing. The issue is idempotent: one open issue per repository, rewritten in place, closed automatically when the gap closes. 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
3c577f9 to
1684cde
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
PR #51 removed a hosted-recognizer path that uploaded raw audio waveforms to a third party. It merged to
mainand stayed unreleased while PyPI's only installable version, 1.2.0, still contained the upload path — and two downstream packages resolveopenadapt-capture>=1.1.0unpinned. Nothing alerted. It shipped as 1.2.1 only after a human noticed and dispatched it.The sibling failure is the same shape from the other end:
openadapt-desktoptagsdesktop-v0.12.0,0.8.0,0.7.0,0.1.0exist with no release object (a failed macOS x86_64 build, an ungrantednative-releaseenvironment approval, a wholly cancelled run, and atomllibimport error on a pre-3.11 runner), and the launcher'sRelease and PyPI Publishruns30275153205/30226357239were cancelled seconds in.if: failure()sees none of that:cancelledandskippedare notfailure.What this is not
Nothing here publishes anything. Releases stay
workflow_dispatch-only by deliberate design — the header ofrelease.ymlreads "Publication is a deliberate operation from protected main", and that gate exists because Flow 1.13.0/1.14.0 were yanked for shipping AGPL benchmark files. This is visibility only.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: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: releasegroup, and a detector that fired on those would go the way of the platform-manifest drift check — correct, permanently red after every release, and therefore indistinguishable from noise.False positives are the failure mode
--self-testenumerates every transient this repository actually produces and proves the detector stays quiet on each, alongside the cases it must catch. It runs offline in CI whenever the detector itself changes.The
~90swindow matters most:chore: release 1.2.1and the launcher'schore(release): reconcile platform manifestare inallowed_tagsbut in neitherminor_tagsnorpatch_tags, so they produce no bump and no alert; the launcher's bare1.10.0subject is not a Conventional Commit at all.Proof it fires on the real miss
Replaying the exact commits that sat on
mainbetweenv1.2.0and the human's dispatch, in the counterfactual where nobody looked:Proof it is quiet today
Run against live
origin/main, PyPI and the GitHub API right now:Triggers and cost
scheduleevery 3h — the actual detector; bounds "how long can a releasable fix sit unnoticed" to ~7h with the 4h grace.workflow_runonRelease and PyPI Publishcompletedwith a non-successconclusion — catchescancelledandskipped, including a run cancelled at a human approval gate.pull_requeston the detector's own files — runs--self-test.One stdlib step, no dependency install, no cache. Python pinned to
3.12, becauseopenadapt-desktoprun29514474536died onModuleNotFoundError: No module named 'tomllib'; the script additionally degrades to documented defaults with a warning rather than crashing on an old interpreter.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
Rollout
The detector script is byte-identical across repositories (
sha256:97ecff275830e3be626e4235a74da260ed79085735d9cf8fe2a1917662d77d27); only.github/release-health.jsondiffers. Companion PRs: OpenAdaptAI/OpenAdapt#1063 and OpenAdaptAI/openadapt-desktop#70. A ready-to-apply patch foropenadapt-flowexists but is not opened here, because that repository is owned by another stream this session.