Versioning
Releases are tagged vX.Y.Z, and the vX major tag moves to the latest compatible release, following the r-lib/actions convention. @v1 was frozen at the pre-2.0.0 snapshot when the breaking quarto-publish change cut @v2, so it has picked up no fixes since - not even non-breaking ones. Pin preview.yml, preview-deploy.yml, quarto-publish.yml, check-bibliography-dois.yml, check-phi.yml, check-links.yml, check-non-standard-chars.yml, claude.yml, claude-code-review.yml, and check-news.yml to @v2; test-coverage.yml, check-equation-renders.yml, lint-yaml.yml, lint-markdown.yml, lint-qmd.yml, lint-changed-lines.yml, lint-changed-files.yml, check-new-line-breaks.yml, check-secrets.yml, check-junk-files.yml, request-dependabot-review.yml, sync-upstream.yml, altdoc-multiversion-docs.yml, report-failure.yml, gemini.yml, gemini-code-review.yml, antigravity-code-review.yml, cursor-code-review.yml, opencode-code-review.yml, ai-code-review.yml, bump-dev-version.yml, version-check.yml, small-model-agent.yml, check-ai-tells.yml, lint-workflows.yml, spellcheck.yml, check-typos.yml, check-extra.yml, check-formatting.yml, claude-manage-project.yml, r-cmd-check.yml, check-code-similarity.yml, and check-one-function-per-file.yml only ever shipped at @v2 (too new to exist at the frozen @v1 tag). check-dependency-updates.yml, check-duplicate-roxygen.yml, check-informal-definitions.yml, check-literal-math-letters.yml, check-wikipedia-further-reading.yml, check-redundant-citations.yml, check-self-credits.yml, check-one-div-per-subfile.yml, check-heading-levels.yml, check-heading-titles.yml, check-self-reference.yml, check-div-ids.yml, check-page-length.yml, check-heading-siblings.yml, check-figure-placement.yml, check-code-messages.yml, check-multipart-exercises.yml, check-rubric-checklists.yml, check-single-ask-parts.yml, check-split-solutions.yml, check-restated-results.yml, check-extracted-lemmas.yml, check-definition-equations.yml, check-hybrid-sentences.yml, check-format-output-files.yml, check-caller-permissions.yml, check-exm-figures.yml, check-contour-3d.yml, check-visualization-figures.yml, check-axis-titles.yml, check-tree-lists.yml, check-search-shortcut.yml, check-self-contained-captions.yml, check-def-commentary.yml, check-unexplained-contrasts.yml, check-unexplained-choices.yml, check-solution-citations.yml, check-exercise-before-result.yml, check-pr-concerns.yml, cancel-superseded.yml, check-pr-size.yml, check-def-title-synonyms.yml, check-bare-yes-no.yml, check-serial-comma.yml, check-exercise-background.yml, check-exercise-question-paragraph.yml, check-key-equations-displayed.yml, check-note-sheet-rows.yml, check-definite-antecedents.yml, check-term-links.yml, check-paired-file-change.yml, annotation-gate.yml, annotation-gate-run.yml, check-local-macros.yml, check-fform-paren.yml, check-macro-expansions.yml, check-write-idiom.yml, check-avoided-words.yml, check-proof-statements.yml, check-dependency-graph.yml, check-math-definitions.yml, check-merge-drops.yml, check-orphaned-images.yml, check-python-package.yml, check-quarto-book.yml, check-quarto-links.yml, check-source-overlap.yml, check-quarto-manuscript.yml, check-quarto-prose.yml, check-quarto-website.yml, check-r-package.yml, check-repo-hygiene.yml, check-typed-output.yml, cleanup-pr-previews.yml, opposition-research.yml, student-qmd.yml, summary.yml, update-quarto-extensions.yml, and update-snapshots.yml only ever shipped at @v3 (or moved to @v3 for cleanup-pr-previews.yml, summary.yml, and update-snapshots.yml). See Workflows for the full breakdown, including which capabilities still pin @v1 and why. Breaking changes bump the major tag (v2, and so on) with migration steps.
Widening permissions is a breaking change
A reusable workflow’s job-level permissions: block is part of its caller contract: a nested job cannot request more than the caller grants, so adding a permission - even a read - fails every caller written against the old contract at parse time (Invalid workflow file), with no API-visible diagnostic (a startup_failure run exposes no jobs, logs, or annotations; the error text appears only on the run page in the UI). gha#685 measured this when gha#638 added issues: read to ai-code-review.yml and the v2 slide delivered it: every consumer run concluded startup_failure until the caller-side grant landed. So treat a permissions: widening like any other breaking change: prefer a major-version bump; where a bump is disproportionate, find the callers and PR the caller-side grant before sliding the tag, and name the required caller edit in the change’s changelog fragment. Find them with the per-workflow code searches that REVDEPS.md documents, not by reading its consumer table: that table is hand-maintained, and so is the owner list beside it. Measured 2026-09-07, the owner list this change replaces returned 10 of the 18 repositories pinning claude-code-review.yml@v2, missing all 8 under owners nobody had added. Its names last changed 2026-06-18, so it had been wrong for as long as nobody looked. Run the unscoped search too and union the results, and treat any count as a floor: a push to a caller’s file can drop it out of the code-search index until it is reindexed, so even a long-standing caller can be missing from either form with nothing saying so.
Advancing a major tag
Unlike r-lib/actions, a major tag here does not slide automatically on every push to main - merging a change does not, by itself, change what a consumer pinned to @v1/@v2 picks up next. Advancing the tag is a deliberate, manual step, so a change can be validated on one or a few consumer repos before everyone else has to deal with it:
- Merge the change to
main. - Optionally, point a consumer’s
uses:line at@main, at a specific commit SHA, or temporarily at a feature branch of this repo, and let that consumer’s own CI run against the unreleased change. - Once confident, advance the shared major tag to
main’s current tip by runningslide-major-tag.ymlviaworkflow_dispatch(frommain), or withgit tag -f/git push --forcedirectly. Every consumer pinned to that tag picks up the change the next time its CI runs.
A brand-new capability’s own PR merging to main does not make it usable at @v2 yet - if a consumer repo’s PR needs to reference a just-merged workflow at @v2 right away, check whether @v2 has actually been advanced past that merge first (step 3 above), or the consumer’s reference resolves to a workflow-not-found error rather than a stale-but-working one.
To prevent manual tag advancement from being forgotten when main moves ahead of the major tag, a tag drift check (check-tag-drift) runs as part of CI to surface when main has unreleased commits ahead of the active major tag (e.g. v2). It emits a GitHub Actions notice and job summary pointing to slide-major-tag.yml whenever tag advancement is pending.
See CHANGELOG.md for what changes as a major tag moves.
Changelog entries are added as fragment files under changelog.d/ (one per PR, so parallel PRs never conflict on the shared changelog) and collated into CHANGELOG.md at release time; see changelog.d/README.md for the fragment format and release flow.
Pinning third-party actions
Every third-party action is pinned to a full commit SHA, with the human-readable version in a trailing comment:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0This follows GitHub’s recommended hardening posture: a SHA is immutable, so a re-pointed tag or a compromised upstream cannot silently change what runs. That matters here because jobs like the preview deploy run with contents: write and pull-requests: write. .github/dependabot.yml bumps these pins as upstreams publish releases, so they stay current instead of freezing. When adding a new third-party action, pin it the same way.
First-party Morrison-Lab/gha/* self-references and most examples/ templates intentionally track the moving major tag (currently @v1, except preview.yml, preview-deploy.yml, quarto-publish.yml, test-coverage.yml, check-equation-renders.yml, check-bibliography-dois.yml, check-phi.yml, check-links.yml, check-non-standard-chars.yml, claude.yml, claude-code-review.yml, lint-yaml.yml, lint-markdown.yml, lint-qmd.yml, lint-changed-lines.yml, lint-changed-files.yml, check-new-line-breaks.yml, check-secrets.yml, check-junk-files.yml, request-dependabot-review.yml, sync-upstream.yml, check-news.yml, altdoc-multiversion-docs.yml, report-failure.yml, gemini.yml, gemini-code-review.yml, antigravity-code-review.yml, cursor-code-review.yml, opencode-code-review.yml, ai-code-review.yml, bump-dev-version.yml, version-check.yml, small-model-agent.yml, check-ai-tells.yml, lint-workflows.yml, spellcheck.yml, check-typos.yml, check-extra.yml, check-formatting.yml, claude-manage-project.yml, r-cmd-check.yml, check-code-similarity.yml, and check-one-function-per-file.yml at @v2, and check-dependency-updates.yml, check-duplicate-roxygen.yml, check-informal-definitions.yml, check-literal-math-letters.yml, check-wikipedia-further-reading.yml, check-redundant-citations.yml, check-self-credits.yml, check-one-div-per-subfile.yml, check-heading-levels.yml, check-heading-titles.yml, check-self-reference.yml, check-div-ids.yml, check-page-length.yml, check-heading-siblings.yml, check-figure-placement.yml, check-code-messages.yml, check-multipart-exercises.yml, check-rubric-checklists.yml, check-single-ask-parts.yml, check-split-solutions.yml, check-restated-results.yml, check-extracted-lemmas.yml, check-definition-equations.yml, check-hybrid-sentences.yml, check-format-output-files.yml, check-caller-permissions.yml, check-exm-figures.yml, check-contour-3d.yml, check-visualization-figures.yml, check-axis-titles.yml, check-tree-lists.yml, check-search-shortcut.yml, check-self-contained-captions.yml, check-def-commentary.yml, check-unexplained-contrasts.yml, check-unexplained-choices.yml, check-solution-citations.yml, check-exercise-before-result.yml, check-pr-concerns.yml, cancel-superseded.yml, check-pr-size.yml, check-def-title-synonyms.yml, check-bare-yes-no.yml, check-serial-comma.yml, check-exercise-background.yml, check-exercise-question-paragraph.yml, check-key-equations-displayed.yml, check-note-sheet-rows.yml, check-definite-antecedents.yml, check-term-links.yml, check-paired-file-change.yml, annotation-gate.yml, annotation-gate-run.yml, check-local-macros.yml, check-fform-paren.yml, check-macro-expansions.yml, check-write-idiom.yml, check-avoided-words.yml, check-proof-statements.yml, check-dependency-graph.yml, check-math-definitions.yml, check-merge-drops.yml, check-repo-hygiene.yml, check-quarto-website.yml, check-quarto-book.yml, check-quarto-manuscript.yml, check-quarto-links.yml, check-quarto-prose.yml, check-orphaned-images.yml, check-source-overlap.yml, check-r-package.yml, check-python-package.yml, check-typed-output.yml, cleanup-pr-previews.yml, opposition-research.yml, student-qmd.yml, summary.yml, update-quarto-extensions.yml, and update-snapshots.yml at @v3 - see the versioning note above), so they are not SHA-pinned.
Reverse dependencies
REVDEPS.md tracks repos that call these workflows, so consumers can be notified before a breaking change. If your repo uses gha, please add it there.