Skip to content

fix(#550): tag the squash commit on main + ancestry guard in /release#565

Merged
atlas-apex merged 1 commit into
devfrom
fix/GH-550-release-tag-ancestry
Jun 7, 2026
Merged

fix(#550): tag the squash commit on main + ancestry guard in /release#565
atlas-apex merged 1 commit into
devfrom
fix/GH-550-release-tag-ancestry

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • Step 6 now targets the squash commit, not the release-branch HEAD — after a squash-merge, the release branch's HEAD is a different SHA that is no longer in main's ancestry. The updated bash block fetches upstream first, then tags upstream/main (the exact squash commit), making the mis-tag mechanically impossible when the prescribed commands are followed.
  • Mandatory ancestry guard added before the tag is pushedgit merge-base --is-ancestor vA.B.C upstream/main runs immediately after tagging and aborts with a descriptive error if the tag is off-ancestry. This catches any remaining mis-tag (e.g. a manual override) before it reaches the public remote.
  • Post-tag release checklist added — three assertions (merge-base, git describe, and exact-tip equality) give the operator a repeatable verification step before announcing the release, preventing silent ancestry drift.

Out of scope: Re-pointing the existing v2.3.0 tag is an operational, published-tag rewrite that must be done by the maintainer (force-updating a public tag). This PR does not touch it.

Testing

  1. npx markdownlint-cli2 .claude/skills/release/SKILL.md — confirmed 0 errors.
  2. No shell unit tests exist for the /release skill (it is a markdown/prose spec, unlike /release-sync which has test_release_sync.sh). Verification is markdownlint-clean output + correctness of the guard logic.
  3. Guard logic correctness: git merge-base --is-ancestor <tag> upstream/main exits 0 only when the tag commit is reachable from upstream/main. Tagging upstream/main directly (as prescribed) always satisfies this, while tagging the release-branch HEAD (the previous accidental pattern) does not — confirmed by the repro steps in [Bug] release tag can land off main's ancestry (v2.3.0 mis-tagged) #550.

Refs #550


Glossary

Term Definition
Squash commit The single commit GitHub creates on main when a PR is squash-merged; its SHA differs from every commit on the source branch and is the only valid tagging target after a squash-merge.
Ancestry / git describe A tag is "on ancestry" when it is a reachable ancestor of the branch tip; git describe --tags upstream/main and git merge-base --is-ancestor both depend on this property to work correctly.
Release-branch HEAD The tip commit of the release/vX.Y.Z branch before it is merged; after a squash-merge this commit is not in main's history and must not be used as a tagging target.
Ancestry guard The git merge-base --is-ancestor assertion added in step 6 that aborts the tag-push workflow if the newly created tag is not reachable from upstream/main, preventing a mis-placed tag from reaching the public remote.

Step 6 (Tag + push) now explicitly:
- Requires tagging upstream/main (the squash commit) not the release-branch HEAD
- Includes a mandatory ancestry guard before pushing the tag:
  git merge-base --is-ancestor vA.B.C upstream/main
  exits non-zero → error + abort before the mis-placed tag is pushed
- Adds a post-tag release checklist with three assertions (ancestry,
  git-describe discoverability, and exact-tip equality)
- Notes that v2.3.0 re-pointing is operational / maintainer-handled (out of scope)

Rule 6 updated to name the squash-merge mechanics and reference step 6.

Refs #550

@atlas-apex atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #565

Commit: `1c7b261f3a94a7c2af93308f6a8c4744e0118f7c`

Summary

Rewrites step 6 (Tag + push) of the /release skill so the vA.B.C tag is placed on the squash commit that lands on main (git tag vA.B.C upstream/main) rather than the release-branch HEAD, adds a pre-push ancestry guard (git merge-base --is-ancestor, exit 1 on failure), and a post-tag verification checklist. Rule #6 is updated to match. Fixes the mis-tag class from #550. The one-time v2.3.0 re-point is correctly scoped out.

Checklist Results

  • Architecture & Design: Pass (docs/skill-only)
  • Code Quality: Pass
  • Testing: Pass (markdownlint clean; guard logic verified empirically)
  • Security: Pass
  • Performance: N/A
  • PR Description & Glossary: Pass (4-term glossary, narrative summary bullets)
  • Technical Decisions (AgDR):N/A (no new library/pattern/architecture)

Verification against the review brief

Tagging target + guard — correct & unambiguous. Tags upstream/main (explicitly named as "the squash commit"), with prose stating "never the release-branch HEAD." The guard asserts git merge-base --is-ancestor vA.B.C upstream/main.

Bash sound — fetch → tag → guard → push. Ordering is correct: the guard runs and exit 1s before git push upstream vA.B.C, so a mis-tag can never reach the public remote. Dropping the main arg from git fetch upstream maingit fetch upstream is an improvement — fetching all refs reliably updates the upstream/main remote-tracking ref the next two commands depend on.

Squash mechanics prose — correct. Accurately states that a squash-merge creates a single new commit on main whose SHA differs from every source-branch commit, and that the release-branch HEAD is therefore no longer in main's ancestry (breaking git describe / git merge-base).

Checklist assertions — verified empirically. git merge-base --is-ancestor is reflexive (exit 0 when tag == tip), so tagging upstream/main directly always satisfies the guard. git rev-parse vA.B.C^{} peels a lightweight tag to its commit and equals git rev-parse upstream/main at a clean release; the third bullet correctly caveats the post-merge-commits case.

No contradiction with the rest of the skill. Rule #6 now reads "never tag before merge, and never tag the release-branch HEAD" and cross-references step 6 — consistent. Step 3.5 (pre-merge site-version bump in the release commit) and step 9 (/release-sync vA.B.C merging upstream/main into dev) both align with the new step 6's upstream/main target; no overlap or conflict.

markdownlint: 0 errors (markdownlint-cli2 v0.22.1).

No leaks: only me2resh/apexyard + generic version placeholders.

Issues Found

None.

Suggestions

  • nit (non-blocking): the --tag vA.B.C invocation in the step-6 lead-in still implies /release --tag runs these commands; the manual block is what actually changed. Fine as-is — the parenthetical "(or runs the suggested commands manually)" covers it.

Verdict

APPROVED


🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: `1c7b261f3a94a7c2af93308f6a8c4744e0118f7c`

@atlas-apex atlas-apex merged commit 63b72bf into dev Jun 7, 2026
8 checks passed
@atlas-apex atlas-apex deleted the fix/GH-550-release-tag-ancestry branch June 7, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants