fix(release): unblock tagging — drop the self-defeating README slug rewrite, compare versions not strings - #12
Merged
Merged
Conversation
…ewrite, compare versions not strings
Two defects, either of which fails a tag push on its own. Runbook F.1 ("validate the release under the
new OIDC binding") could not be performed until both were fixed.
1. THE README SLUG REWRITE FAILED UNCONDITIONALLY. publish.ps1 used to rewrite the private source slug
to the public mirror's before `build` embedded README.md as the PyPI long_description. That rewrite
had been applied to this workflow itself, so at the cutover both sides of the substitution collapsed
to the same string: the sed replaced the public slug with itself (a no-op) and the guard after it
failed if that slug appeared in the README -- which it does, 19 times. Every tag push died there.
That is not hypothetical: the v0.3.0 tag (2026-07-13) failed at this exact step, which is why the
repo has ZERO published releases. Removed outright -- there is one repo now and README.md already
names it, so there is nothing to rewrite. Same root cause as the release job's inverted `if:` guard
fixed in #7: mirror-era logic the slug rewrite mangled and the cutover left behind.
2. TAG-VS-BUILT COMPARED AS STRINGS, IN BOTH JOBS. The trigger only fires on `vX.Y.Z` / `vX.Y.Z-*`, so
a pre-release tag must be spelled with a hyphen -- while hatchling and PyPI normalise `0.3.0-rc1` to
`0.3.0rc1`. A raw compare therefore forced __version__ to carry the non-canonical "0.3.0-rc1" purely
to satisfy the gate, and the tag, the module attribute and the wheel filename could not all be
canonical at once. In the HARNESS job it was worse than awkward: `built` is parsed out of the
already-normalised wheel FILENAME, so the compare could NEVER match a pre-release tag -- and with
PUBLISH_HARNESS=true that job runs AFTER the engine has uploaded, so it would fail half-published.
Both now compare packaging.Version objects; a genuine mismatch still fails loudly.
Verified by running the real comparison: 0.3.0rc1 vs tag v0.3.0-rc1 matches; 0.3.0-rc1 matches; 0.3.0
vs v0.3.0-rc1 does not; 0.3.1 vs v0.3.0 does not.
Tests pin both, mutation-verified: restoring the slug step fails, and restoring the string compare
fails. The existing "version==tag comparison" canary tracked the raw shell test by its exact spelling,
so it is retargeted at the CHECK rather than its wording.
STILL OWNER-ONLY for F.1, unchanged by this commit:
* a `messagefoundry-harness` PyPI Trusted Publisher (PUBLISH_HARNESS is already true, so the harness
job WILL attempt an upload on the next tag);
* setting __version__ to the rc being cut -- canonical `0.3.0rc1` now works, tagged `v0.3.0-rc1`;
* the tag itself, which publishes to PRODUCTION PyPI (there is no TestPyPI route).
wshallwshall
enabled auto-merge (squash)
July 27, 2026 16:50
Merged
wshallwshall
added a commit
that referenced
this pull request
Jul 27, 2026
Bumps the single-sourced version and cuts the CHANGELOG's Unreleased section into a dated 0.3.1 release. `messagefoundry/__init__.py` is the only version to edit -- packaging/messagefoundry-harness reads it via `[tool.hatch.version] path = "../../messagefoundry/__init__.py"`, so both wheels ship matched, which the release workflow then asserts against the tag. Also repairs the CHANGELOG's compare links, which the 0.3.0 release never updated: `[Unreleased]` still pointed at v0.2.10 and there was no `[0.3.0]` definition at all, so five versions of "what changed in this release" rendered as dead links. Added 0.3.1 down to 0.2.11. Other files matching "0.3.0" were checked and deliberately left alone -- they are synthetic fixtures and comment examples (tests/test_sbom_finalize.py's inline __version__ string, test_update_check.py's comparator cases, a docstring in pipeline/update_check.py), not the shipped version. NOTE for the tag: v0.3.1 must be cut from a commit that already contains the release.yml fix (#12). Before it, every tag push dies at the README slug-rewrite step -- which is exactly what happened to v0.3.0 on 2026-07-13.
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.
Two defects, either of which fails a tag push on its own. Runbook F.1 could not be performed until both were fixed.
1. The README slug rewrite failed unconditionally
publish.ps1used to rewrite the private source slug to the public mirror's beforebuildembeddedREADME.mdas the PyPI long_description. That rewrite had been applied to this workflow itself, so at the cutover both sides of the substitution collapsed to the same string — asedreplacing the public slug with itself, followed by a guard that fails if that slug appears in the README. It appears 19 times.So every tag push died there. Not hypothetical: the
v0.3.0tag (2026-07-13) failed at this exact step, which is why the repo has zero published releases.Removed outright — there's one repo now and the README already names it, so there is nothing to rewrite. Same root cause as the release job's inverted
if:guard fixed in #7.2. Tag-vs-built compared as strings, in both jobs
The trigger only fires on
vX.Y.Z/vX.Y.Z-*, so a pre-release tag must carry a hyphen — while hatchling and PyPI normalise0.3.0-rc1→0.3.0rc1. A raw compare forced__version__to hold the non-canonical"0.3.0-rc1"purely to satisfy the gate, meaning the tag, the module attribute and the wheel filename could never all be canonical at once.In the harness job it was worse than awkward:
builtis parsed from the already-normalised wheel filename, so the compare could never match a pre-release tag. And withPUBLISH_HARNESS=truethat job runs after the engine has uploaded — so it would fail half-published.Both now compare
packaging.Versionobjects. Verified against the real logic:__version__0.3.0rc1v0.3.0-rc10.3.0-rc1v0.3.0-rc10.3.0v0.3.0-rc10.3.1v0.3.0Tests
Both pinned and mutation-verified: restoring the slug step fails, restoring the string compare fails. The existing
"version==tag comparison"canary tracked the raw shell test by its exact spelling, so it's retargeted at the check rather than its wording — otherwise it would have blocked the fix while claiming to protect it.Still owner-only for F.1
messagefoundry-harnessPyPI Trusted Publisher —PUBLISH_HARNESSis alreadytrue, so that job will attempt an upload on the next tag.__version__to the rc being cut — canonical0.3.0rc1now works, taggedv0.3.0-rc1.🤖 Generated with Claude Code