Skip to content

release: prepare v2.0.0 - #106

Merged
MCamner merged 1 commit into
mainfrom
release/v2.0.0
Jul 28, 2026
Merged

release: prepare v2.0.0#106
MCamner merged 1 commit into
mainfrom
release/v2.0.0

Conversation

@MCamner

@MCamner MCamner commented Jul 28, 2026

Copy link
Copy Markdown
Owner

The version bump release.sh produced. Opened as a PR because the repository rule on main requires one.

-VERSION                        1.0.1
+VERSION                        2.0.0
-.mq/repo-contract.json         1.0.1
+.mq/repo-contract.json         2.0.0
-README badge                   1.0.1
+README badge                   2.0.0

Three files, three lines. Generated by ./release.sh 2.0.0, not by hand.

What happened

release.sh step 13 pushes main and the tag together. That push was rejected:

remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: - Changes must be made through a pull request.
 ! [remote rejected] main -> main (push declined due to repository rule violations)

Nothing reached origin — no commit, no tag. The local tag was deleted rather than kept, because a squash merge gives this content a new SHA and the tag would have pointed at a commit that never lands on main.

Release state

Everything else the release needs is already in place and was verified on main before the attempt:

test-all            EXIT=0, 0 failures
shell lint          PASS, 170 files, warning severity
selftest            PASS
registry            OK, 73 commands, 48 subcommands, dispatch agree
release-check json  schema OK
release-check human "Repo looks publish-ready" ✔
CHANGELOG           ## [2.0.0] - 2026-07-28 present

release.sh also re-gates after the bump that .mq/repo-contract.json matches VERSION, which it does — that check passed before the push was attempted.

After merge

Tag the merged commit and push only the tag:

git switch main && git pull --ff-only
git tag -a v2.0.0 -m "v2.0.0"
git push origin v2.0.0

The GitHub Release stays a separate deliberate step after the tag is verified.

Follow-up worth filing

release.sh cannot complete on this repository as written — steps 11–13 assume a direct push to main that the branch rule forbids. It should either push the tag only and leave the commit to a PR, or grow a mode that does. Not fixed here; a release PR is not the place to change the release script.

🤖 Generated with Claude Code

@MCamner
MCamner merged commit 95ea58f into main Jul 28, 2026
3 checks passed
@MCamner
MCamner deleted the release/v2.0.0 branch July 28, 2026 21:53
MCamner added a commit that referenced this pull request Jul 29, 2026
* fix(release): obey release_mode instead of pushing main

.mq/repo-contract.json declares release_mode: pull_request, but release.sh
committed the version bump on main, tagged it, and ran `git push origin main`.
main has no branch protection on GitHub, so nothing outside the contract would
have refused that push. v2.0.0 was released through PR #106 plus a hand-made
tag on the merge commit — the script was the only part of the flow that
disagreed with how releases are actually cut here.

The mode is read from the contract, not from a flag. A flag would have left
`./release.sh <version>` working as a direct-push path in a repo whose contract
forbids one, which is the bug rather than a fix for it.

pull_request: bump on release/v<version>, push the branch, open the PR via gh
when present (warn-only — the branch is already safe on origin by then), return
the checkout to main, create no tag. Tagging is printed as a post-merge step
because the tag belongs on the merge commit, and the wiki Command-Reference and
GitHub Release move with it: until the merge lands, main does not carry the
version they would document.

direct: unchanged, and still reachable only when the contract asks for it.

The error trap now also restores the base branch. `git branch -d` refuses
unmerged work, so a branch already holding the release commit survives a
failure rather than taking the commit with it. `git checkout main` in the sync
step loses its `|| true`: a swallowed failure used to mean bumping whatever
branch was out, and would now mean branching the release off it.

tests/release-pull-request-mode-smoke.sh asserts origin's ref state against a
real bare repo, not a stub's command log — a push nobody logged can fool a
stub, but not a ref.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): run the rollback the gates always skipped

Driving the real flow found it: `./release.sh --dry-run 2.0.1` against this
repo's own contract failed the CHANGELOG gate and left VERSION, README.md and
.mq/repo-contract.json bumped on disk, with no "Rolled back" line and no
"Release command failed" line — the trap had not run at all.

Bash does not run an ERR trap for an explicit `exit`, and every gate here exits
rather than failing a command. So the rollback the usage text promises has
never covered the gates: not the CHANGELOG check, not the contract re-gate, not
the tag checks. Only a command that failed on its own reached it.

That is a pre-existing hole, but it stops being only pre-existing once the
release runs on a branch: a gate tripping after the branch is cut would strand
the checkout on it. So the EXIT trap lands with the mode change rather than
after it.

Cleanup is guarded on a mutation flag rather than running unconditionally.
Before the first bump the tree still holds whatever the operator had, and
`git checkout --` there would discard their work instead of restoring ours —
an unknown flag must not clean the tree.

Test [5/5] cuts a release for a version with no CHANGELOG section and asserts
the tree is clean, the release branch is gone, and the checkout is back on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(changelog): record the release rollback fix

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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