Permissions

A called reusable workflow cannot hold more GITHUB_TOKEN permissions than the caller grants, and most repos default to a read-only token. So workflows that need to write must have the caller grant it on the calling job. The stubs in examples/ already include the right permissions: blocks; copy them as-is.

Per-workflow grants

  • check-ai-tells, check-bibliography-dois, check-code-similarity, check-equation-renders, check-extra, check-formatting, check-junk-files, check-new-line-breaks, check-news, check-non-standard-chars, check-one-function-per-file, check-phi, check-secrets, check-typos, cursor-code-review, lint-changed-files, lint-changed-lines, lint-markdown, lint-qmd, lint-workflows, lint-yaml, preview, r-cmd-check, spellcheck, test-coverage, version-check need only contents: read (the default), so no permissions: block is needed. This list is checked against the workflows’ own permissions: blocks by .github/workflows/scripts/tests/run-permissions-docs-tests.py; keep the markers around it.
  • check-links opens an issue on main failures, so grant issues: write, pull-requests: read, contents: read.
  • summary comments on issues and calls the models API, so grant issues: write, models: read, contents: read.
  • update-snapshots pushes the snapshot-update commit back to the branch, so grant contents: write.
  • quarto-publish deploys to the gh-pages branch, so grant contents: write, and set Settings -> Pages -> Source to “Deploy from a branch”, branch gh-pages / (root) once. Grant contents: write even with deploy: false: the deploy job is part of the workflow, so the caller must grant its permissions even when it is skipped.
  • preview-deploy (deploy half) pushes gh-pages and comments, so grant contents: write, pull-requests: write, actions: read. The preview build half is read-only, above.
  • check-equation-renders downloads the build artifact. It is read-only, above, but a caller that narrows below the read-only default token still needs actions: read alongside contents: read.
  • check-news reads the PR’s labels live for its skip-label exemption. It is read-only, above, but a caller that narrows below the read-only default token still needs issues: read and pull-requests: read alongside contents: read.
  • version-check reads the PR’s labels live for its no version increment exemption. It is read-only, above, but a caller that narrows below the read-only default token still needs pull-requests: read alongside contents: read. Not issues: read: that scope alone returned 403 on the same call (gha#724), since GitHub authorizes a label read on an issue object that is a pull request against the pull-requests permission.
  • cleanup-pr-previews commits deletions to gh-pages, so grant contents: write, pull-requests: read.
  • bump-submodule, sync-shared-fragments, sync-upstream open a PR, so grant contents: write, pull-requests: write, and enable Settings -> Actions -> General -> “Allow GitHub Actions to create and approve pull requests”.
  • report-failure files or updates the issue tracking a failing workflow, so grant issues: write on the reporting job – and nothing else. It performs no checkout, so it needs no contents grant; the job it watches keeps its own permissions.
  • claude pushes branches, opens PRs, and dispatches the review workflow, so grant contents: write, pull-requests: write, issues: write, id-token: write, actions: write, and add either the CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret.
  • claude-code-review (read-only review) grants contents: read, pull-requests: write, issues: write, actions: read, checks: read (grant it; required by the currently-tagged @v2), and either the CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret. The model job’s GITHUB_TOKEN has no write scopes (contents / pull-requests / issues / actions: read); write is confined to jobs that never run the model (gather-context and post-review) (gha#580). actions: read is required on the caller: a permissions: block sets unspecified scopes to none, and post-review needs it to download the packed artifact (the model job also uses it for the github_ci MCP server). Grant checks: read, but note what it does and does not buy. actions: read covers workflow runs but not GET .../commits/{ref}/check-runs, so without that scope the reviewer’s check-status reads fail with HTTP 403 and a clean diff can be reported as blocked (ucdavis/bcs#964). As of 2026-09-06 the @v2 tag still points at a commit that DOES request the scope, which is why a caller lacking it fails at startup. Once v2 is slid onto this change the model job stops requesting it, and the 403 returns for everyone until the v3 reinstates it. Keep the grant through all three phases: it is what makes a caller work today, it is harmless while the slid @v2 ignores it, and it is what makes the v3 cost nothing later. The startup failure is the reason the callee gave the scope up: a called workflow cannot request a permission its caller lacks, so the run ends before any job starts. That is how the v2 slide for that grant broke 17 of the 18 repositories pinning this workflow at @v2 (gha#831 carries the derivation; how many still lack the grant falls as consumers add it, and is tracked in gha#833 as the v3 precondition rather than restated here).
  • cursor-code-review queues a Cursor Bugbot review. It is read-only, above, but does need the CURSOR_API_KEY secret (Enterprise, admin:* scope).
  • opencode-code-review is a read-only review that posts its own comment, so grant contents: read, pull-requests: write, issues: write, and set the OPENCODE_API_KEY secret (OpenCode Zen).
  • ai-code-review selects the first available AI reviewer and dispatches its review workflow, so grant contents: read, pull-requests: read, issues: read, actions: write, and pass the secrets of whichever agent review workflows are installed (see examples/ai-code-review.yml). issues: read is required since the delivery classifier’s comment scan landed (gha#638); a caller missing it fails at parse time with no API-visible diagnostic (gha#685).
  • gemini pushes branches, opens PRs, and dispatches the review workflow, so grant contents: write, pull-requests: write, issues: write, id-token: write, actions: write, and add the GEMINI_API_KEY secret.
  • gemini-code-review posts the Gemini review, so grant contents: read, pull-requests: write, issues: write, id-token: write, and the GEMINI_API_KEY secret.
  • antigravity-code-review posts the Antigravity review, so grant contents: read, pull-requests: write, issues: write, id-token: write, and the GEMINI_API_KEY secret.
  • small-model-agent posts the small-model agent’s PR comment, so grant contents: read, pull-requests: write.
  • claude-manage-project files issues and updates the project board, so grant contents: read, issues: write, repository-projects: write, and add the CLAUDE_CODE_OAUTH_TOKEN secret (required).
  • altdoc-multiversion-docs deploys to gh-pages, comments PR previews, and rewrites rendered links, so grant contents: write, pull-requests: write, and issues: write, and set Settings -> Pages -> Source to “Deploy from a branch”, branch gh-pages / (root) once.
  • request-dependabot-review requests a reviewer on the PR, so grant pull-requests: write.
  • bump-dev-version opens (or auto-merges) a PR, so grant contents: write, pull-requests: write, enable the same “Allow GitHub Actions to create and approve pull requests” setting as above, and (for its default auto-merge: true) “Allow auto-merge” too. Its version-check counterpart is read-only, above.

Optional secrets

  • WORKFLOW_TOKEN (a PAT or GitHub App token with contents:write + workflows:write). The integrated GITHUB_TOKEN cannot push changes to files under .github/workflows/; GitHub rejects them without the workflows scope. Add this secret if claude will edit workflow files, to push to a protected branch, or – for bump-dev-version with its default auto-merge in a repo with required status checks – to let the bump PR’s checks run so it can merge (a GITHUB_TOKEN-authored PR’s pull_request checks never report, so the merge stays blocked; see its reference page). Unlike GITHUB_TOKEN, a PAT or App-token push does trigger other push-based workflows, so enabling it can set off extra CI runs. When the secret is absent and claude does edit a workflow file, the rejected push is reported as an error naming this secret, and the commits are posted to the thread as a git format-patch so they survive the run.
  • SUBMODULES_TOKEN. Public submodules clone anonymously; private ones need this secret. Relevant to claude, claude-code-review, r-cmd-check, and bump-submodule when checkout-submodules (or the submodule update) touches a private submodule.

Notes for private consumers

Reusable workflows in this public repo are callable from public repos automatically. A private consumer must allow access to this repo under Settings -> Actions -> General -> Access before it can call these workflows.