Source of truth: docs/release.rst. If this skill and that file disagree, follow docs/release.rst and update this skill.
Require both the exact version and the merged release-preparation PR URL or number. Define the tag as v followed by that exact version (for example, v3.2.0rc1, never v3.2.0 for that RC). Do not infer either input from the current checkout. On a retry, also require any previously recorded release checkpoint, especially a nondefault release commit.
Pause and get explicit confirmation before every push, label mutation, and the GitHub release. Show the exact repository, refs, commit SHA, and release notes involved. Everything else can proceed autonomously.
upstream identify the official pybind/pybind11 repository, run git fetch upstream --prune --tags, and check gh auth status and the account's repository release permissions.master or vX.Y), record its merge commit, and require gh pr checks <PR> --repo pybind/pybind11 to show the complete expected release matrix finished successfully. Investigate skipped or cancelled coverage rather than checking only the required subset. Record the tested SHA; require it to be the release SHA or prove that their source trees are identical.python -c 'from pybind11._version import __version__; print(__version__)' exactly equals the requested version and is not a development version.include/pybind11/detail/common.h has internally consistent version macros, including release level and serial.docs/changelog.md at the release commit has the matching version and the intended tag/release date. Before a remote tag exists, a slipped date must either be accepted explicitly or corrected through a follow-up PR to the same release base, with the resulting merge selected and tested as the new release commit. Once the remote tag exists, its changelog date is frozen; accept it explicitly or abort publication, but never move the tag.pybind11 and pybind11-global are absent from PyPI unless this is an intentional resume of a partially completed publication. Treat publication of only one distribution as partial state, not success.vX.Y, verify that upstream/vX.Y contains the release commit. The merge already updated the branch; do not merge master or push it again.master, create vX.Y at the exact release commit if the remote ref is absent. If upstream/vX.Y equals the release commit, record this step as complete. If it is an ancestor, fast-forward it by pushing the recorded SHA directly to refs/heads/vX.Y after confirmation. Inspect the remote ref, not a local tracking branch.vX.Y contains later commits, do not rewind it. Stop and ask whether those commits are intentional before proceeding; stop on divergence.upstream namespaces. In every existing state, require an annotated tag object that peels to the recorded release commit; a lightweight tag is not equivalent and must not be silently replaced.git tag -a <tag> <release-sha> -m '<tag> release'.upstream after confirmation.upstream/stable. Never update it for a prerelease, and never move it backward to an older maintenance line. A final release on the current or a newer line updates it only when the user confirms that the release should become the project's designated stable.upstream/stable, merge the annotated tag with -X theirs, and enforce tree equality with git diff --exit-code <tag> HEAD --. If upstream/stable already contains the release commit and has that tree, record this step as complete. Stop and ask if the trees differ; abort any in-progress merge and discard the temporary branch rather than reconciling it autonomously.git push upstream HEAD:stable after confirmation. Never force-push.#1234. Show the complete file to the user and verify once more that the remote annotated tag object and peeled commit match the recorded values.gh release create <tag> --repo pybind/pybind11 --verify-tag --title "Version <version>" --notes-file <file>. Add --prerelease for an alpha, beta, or RC. Add --latest=false whenever this release should not become GitHub's latest release, including an older-line maintenance release or a final release that was not designated current stable.vX.Y, leave an already-ahead master unchanged, and leave the maintenance branch at the final version unless a separate next-development version is explicitly approved. If master lacks this release, open a separate PR against master that copies only the released changelog section.master, the project may choose a next patch alpha, a next-minor alpha, or no immediate bump. Once confirmed, create a fresh branch from the explicit remote ref, update all version macros and the IN DEVELOPMENT changelog section consistently, and run nox -s tests_packaging.pybind11 and pybind11-global. Report failures and stop. Only after both succeed, revalidate the consumed-PR list against the released changelog and remove needs changelog from exactly those PRs, after confirmation.twine upload is a separate, high-impact recovery action and requires new explicit confirmation; docs/release.rst describes the artifact-based procedure.