Skip to content

Do not fail a release that is already published - #683

Merged
MongLong0214 merged 1 commit into
mainfrom
fix-681-idempotent-publish
Aug 15, 2026
Merged

Do not fail a release that is already published#683
MongLong0214 merged 1 commit into
mainfrom
fix-681-idempotent-publish

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Closes #681.

Publishing v1.0.0 left main red. A maintainer cut the release by hand seconds
before the tag push woke this workflow; the workflow tried to create a second
one:

gh release create → HTTP 422 Validation Failed
                    Release.tag_name already exists
                    tag_name was used by an immutable release

The release was fine and the run was red — the worst combination.
docs/RELEASE-GATE.md §6b now asks someone to read main after every merge, and
an expected red head with no explanation is how that habit gets unlearned.

The branch, and the part that is easy to get wrong

Existence is checked before creating:

  • the tag resolves to this run's verified commit → skip and succeed
  • it resolves elsewhere → fail, printing both shas

Existence alone must not mean skip. A release for this tag pointing at a
different commit is a tag that moved, or one cut from the wrong branch, and
swallowing that is worse than the 422 — the 422 at least made noise. So the
branch is on what the tag resolves to, not on whether a release is present.

Verified

release-tag-binding, release-publish-prerequisites and action-lint — 91
tests, all passing. release.yml is not one of the workflows pinned by
EXPECTED_CI_WORKFLOW_SHA256; that pin covers ci.yml, checked rather than
assumed.

Publishing v1.0.0 left main red. A maintainer cut the release by hand seconds
before the tag push woke this workflow; the workflow tried to create a second
one and GitHub refused with 422 immutable release already exists. The release
was fine and the run was red, which is the worst combination -- RELEASE-GATE
6b asks someone to read main after every merge, and an expected red head with no
explanation trains people to stop reading it.

Existence is now checked before creating. Same commit, skip and succeed;
different commit, fail and print both shas.

Existence alone must not mean skip. A release for this tag pointing somewhere
else is a tag that moved or a release cut from the wrong branch, and swallowing
that is worse than the 422 -- the 422 at least made noise. So the branch is on
what the tag resolves to, not on whether a release is there.

Limit: a release already published is never overwritten, and never assumed correct
Blast: system
Undo: easy
Certainty: firm
Provenance: authored
Record-Id: r-681idem
@github-actions

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/main..635210113005b967b7d526e689dccc7856fbbaf4
Active constraints: not read — commitlore: the index is incomplete: the build stopped after its time budget with 716 commit(s) or note(s) unread — records in them are missing from this answer. fix: commitlore init (or commitlore index) to finish the index (1 changed path)

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

@MongLong0214
MongLong0214 merged commit 4d3f22f into main Aug 15, 2026
12 checks passed
MongLong0214 added a commit that referenced this pull request Aug 15, 2026
Five fixes have been sitting on main since v1.0.0 and none of them has reached
anyone. They are all defects at the install and distribution boundary, and that
boundary has exactly one delivery mechanism: a release.

    #683  the release workflow failing on a release that already exists
    #684  hermes install refusing the config it wrote
    #687  the skills root taken from the running bundle instead of --data-root
    #688  an installed plugin treated as a reason to stop rather than upgrade
    #690  claude-code wired by nothing, in neither hosts nor notDetected

Measured rather than assumed: re-installing with main's install.sh still left the
plugin cache at 0.8.0 and claude-code out of both lists, because the enumeration
that #690 fixed is compiled into the binary being installed -- and that binary is
v1.0.0.

dist is unchanged and the canonical digest is identical to before the bump. The
version is read from package.json at runtime rather than compiled in. Rebuilt
through the canonical builder and verified rather than assumed, the same as
v1.0.0.

Thirty-eight pins across nine files. The installers and READMEs carry the tag in
URLs that resolve only once the tag exists, so this is one commit and the tag
goes on it.

This is the first release cut under release-gate 6c, which asks for an upgrade
over the previous version rather than a fresh install. The five fixes above are
why that section exists.

Limit: a distribution-boundary fix reaches nobody until it is released
Blast: system
Undo: costly
Certainty: firm
Provenance: authored
Record-Id: r-rel101
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.

The release workflow is not idempotent against an existing release

1 participant