Workflows

Every capability is a reusable workflow you call with a small stub. The table below is the full catalog; each workflow links to its reference page, with full input tables and a copy-paste example.

Workflow Purpose Key inputs
check-ai-tells.yml Scan narrative prose in Markdown and Quarto files for AI-generated tell density and rhetorical markers paths, paths-ignore, base-ref, threshold, ignore-tells, fail
check-bibliography-dois.yml Validate book/article BibTeX entries have resolvable DOIs matching CrossRef metadata exclude-keys, install-quarto, no-metadata-check
check-formatting.yml Fail when any .R/.r file would be rewritten by Air, Posit’s R formatter (Rust; no R session). Check-only version, path
check-code-similarity.yml Flag code highly similar to a caller-supplied corpus of prior submissions, using JPlag. Computed entirely on the runner — nothing is uploaded. Warns rather than fails by default, since shared skeleton code and common idioms raise similarity legitimately corpus-path, language, threshold, fail, base-code-path
check-junk-files.yml Fail when the repository tracks operating-system or editor detritus (.DS_Store, AppleDouble ._*, .Rhistory, .RData, Thumbs.db), naming the git rm --cached fix and the global-gitignore / usethis::git_vaccinate() fix that stops it recurring patterns, paths-ignore, fail
check-non-standard-chars.yml Detect curly quotes, en/em dashes, and the multiplication sign in .qmd, .R, and .md files python-version, extensions
check-phi.yml Scan PRs (added lines only) for content that looks like PHI detectors, paths-ignore, allowlist-file, fail
check-secrets.yml Scan the repository’s git history for committed credentials with gitleaks version, checksums-sha256, config, paths-ignore, allowlist-file, log-opts, fail
check-links.yml lychee link check with bundled config, PR skip-label, and auto-issue on main lychee-config, lychee-args, fail-if-empty, create-issue-on-main, skip-label
lint-yaml.yml yamllint over tracked YAML with a bundled config, plus a check that flags long run: script blocks as decomposition candidates python-version, config-file, paths-ignore, fail, max-script-lines, fail-on-long-scripts
lint-markdown.yml markdownlint-cli2 over tracked Markdown with a bundled config, plus checks for long fenced code blocks, list-item merge splices, and blank lines that split a table config-file, globs, paths-ignore, fail, max-code-block-lines, fail-on-long-code-blocks, base-ref, fail-on-item-splices, fail-on-table-splits
check-new-line-breaks.yml Diff-scoped check that flags newly-added Markdown lines packing more than one sentence/clause onto one source line python-version, globs, paths-ignore, fail, clause-breaks, clause-min-length
lint-qmd.yml markdownlint over the prose sections of tracked .qmd Quarto files (code chunks stripped, YAML front matter skipped natively) with a bundled default config; default 80-char line-length ceiling encourages semantic line breaks config-file, globs, paths-ignore, fail, max-line-length
lint-changed-lines.yml lintr over only the lines a PR adds or modifies (not whole changed files), so lint rules can be adopted or tightened incrementally path, install-quarto, use-renv, renv-cache-version, apt-packages, extra-packages, install-package, fail
lint-changed-files.yml lintr over a PR’s changed files, a whole package, or a whole project, selected by scope scope, path, linter-file, install-quarto, use-renv, renv-cache-version, apt-packages, extra-packages, install-package, fail
lint-workflows.yml actionlint (syntax/semantics) and zizmor (security) over the caller’s GitHub Actions workflows and composite actions path, actionlint-version, actionlint-checksum, zizmor-version, python-version, pedantic, fail
spellcheck.yml Spellcheck an R package’s prose – DESCRIPTION’s Title/Description, man/*.Rd, vignette sources, and root README/NEWS/CHANGES/index Markdown – with {spelling}, accepting the package’s own inst/WORDLIST path, exclude, fail, additional-options, install-quarto
check-typos.yml Diff-scoped spellcheck of the files spellcheck.yml cannot see – Quarto site pages, CONTRIBUTING.md-class Markdown, YAML, code comments, and non-R-package repos – with crate-ci/typos version, checksums-sha256, path, config, globs, paths-ignore, base-ref, fail
check-news.yml Enforce a NEWS.md changelog entry on PRs changelog, no-changelog-label
test-coverage.yml Measure R-package test coverage with covr and upload the Cobertura report to Codecov path, install-quarto, extra-packages, fail-ci-if-error, upload-test-results, examples-coverage, min-coverage
check-extra.yml Extra R-package checks that R CMD check passes over: warnings as errors on examples/tests/vignettes, random test order, and a README.Rmd render that can also fail when README.md is stale path, extra-packages, install-quarto, check-warnings, check-random-order, check-readme, check-readme-freshness
r-cmd-check.yml Run R CMD check across an OS x R-version matrix, with an optional hard-dependencies-only job gated to pull_request hard, error-on, force-suggests, setup-julia, install-quarto, linux-container, extra-packages, timeout-minutes
update-snapshots.yml Regenerate testthat snapshots, accept the new output, commit, and push; snapshot correctness is judged at PR review of the pushed commit ref, pr-mode, julia, extra-packages, apt-packages, commit-message
summary.yml AI summary comment on newly opened issues (GitHub Models brownout notice: configure endpoint/model or use claude.yml) endpoint, model
quarto-publish.yml Render a Quarto site and deploy it to GitHub Pages path, setup-r, r-packages, use-renv, install-package, setup-chrome, tinytex, apt-packages, output-dir, render-profile, formats, freeze-cache, deno-v8-options, checkout-submodules, pre-render-artifact, pre-render-artifact-path, fail-on-render-warning, forbid-log-patterns, deploy
report-failure.yml File an issue when a watched job fails, or comment on the issue already open for that failure title, body, labels
preview.yml Build half of the PR-preview family: render in the PR context and upload an artifact (read-only) path, r-version, r-packages, apt-packages, use-renv, install-package, setup-chrome, tinytex, submodules, render-profile, output-dir, formats, extra-preview-labels, fail-on-render-warning, forbid-log-patterns, detect-changed-chapters, changed-chapters-banner, changed-chapters-glob, deployed-branch, deployed-subdir, changed-chapters-normalize-patterns, banner-index
preview-deploy.yml Deploy half: publish the artifact to gh-pages and comment the preview link pages-base-url, pages-base-path
check-equation-renders.yml Crawl the build artifact with a headless browser and fail on equations MathJax can’t render fail
cleanup-pr-previews.yml Housekeeping: delete preview directories for closed PRs preview-dir, compact-history
claude.yml Agent-mode Claude Code bot: responds to @claude, edits files, opens/updates PRs. A quoted or code-span mention starts only a cheap filter job, not the agent. setup-r, install-quarto, use-renv, apt-packages, pip-packages, checkout-submodules, use-ai-config, plugin-marketplaces, plugins, reviewer, dispatch-review-on-agent-push, report-cost, trusted-bot-logins, dispatch-on-assignee, extra-secret-names
claude-code-review.yml Read-only Claude PR review (default stub is mention-triggered via workflow_dispatch; add pull_request for automatic reviews) pr-number, prompt-addendum, checkout-submodules, allowed-bots, track-progress, apt-packages, pip-packages, lab-manual, check-latex-macros, use-ai-config, plugin-marketplaces, plugins, report-cost, model, extra-secret-names
claude-manage-project.yml Triage a newly-opened issue: apply a priority label and add it to the project board. Gated to OWNER/MEMBER/COLLABORATOR authors, since the issue body reaches a write-capable agent prompt-addendum, trusted-bot-logins
gemini.yml Agent-mode Gemini CLI bot: responds to @gemini and @gemini-cli, edits files, opens/updates PRs setup-r, install-quarto, use-renv, renv-cache-version, r-extra-packages, apt-packages, pip-packages, checkout-submodules, eager-pr, reviewer, mark-ready-for-review, prompt-addendum, gemini-model, review-workflow-file, extra-secret-names
gemini-code-review.yml Read-only Gemini PR review (default stub is mention-triggered via workflow_dispatch; add pull_request for automatic reviews) pr-number, prompt-addendum, checkout-submodules, gemini-model, extra-secret-names
antigravity-code-review.yml Automated agentic code review, security audit, or test-suite generation via Google Antigravity SDK (google-antigravity) mode, pr-number, prompt-addendum, trigger-policy, checkout-submodules, model, workload-identity-provider, service-account, gcp-project, gcp-location, max-diff-lines, max-diff-files, fail-on-error
cursor-code-review.yml Queue a Cursor Bugbot PR review via the Enterprise Bugbot API; success means the review was queued pr-number, dry-run
opencode-code-review.yml Read-only OpenCode PR review running the opencode CLI headless (default stub is dispatch-triggered; add pull_request for automatic reviews) pr-number, prompt-addendum, checkout-submodules, opencode-model, opencode-version, opencode-attempts
ai-code-review.yml Multi-agent PR review: picks one configured agent at random and dispatches its review workflow, falling through when one can’t be dispatched or fails during execution agents, pr-number, claude-review-workflow-file, gemini-review-workflow-file, antigravity-review-workflow-file, cursor-review-workflow-file, opencode-review-workflow-file, watch-timeout
small-model-agent.yml Small or self-hosted model PR agent: runs a small-model agent against a PR diff with bounded verification gates (wai#39, ai-config#1292) pr-number, endpoint-url, model, max-iterations, setup-r, install-quarto, apt-packages, pip-packages, checkout-submodules, run-gates, dry-run, extra-secret-names
bump-submodule.yml Update a submodule to upstream HEAD and open a PR when the pointer moves submodule-path, remote-branch, base-branch, pr-branch
sync-shared-fragments.yml Vendor files from an upstream repo (pinned to a commit) and open a PR when they change source-repo, source-ref, source-paths, dest-dir, manifest-path
sync-upstream.yml Merge an upstream repo’s branch into a fork and open a PR when the merge brings changes upstream-repo, upstream-branch, base-branch, pr-branch, fail-on-conflict
request-dependabot-review.yml Request review from configured reviewers when a PR’s author matches a bot actor (Dependabot by default) reviewers, bot-actor
altdoc-multiversion-docs.yml Render an altdoc-based R package’s docs and deploy multiple versions side by side on gh-pages (/dev/, /latest-tag/, /vX.Y.Z/, plus PR previews and a root redirect) r-packages, needs, apt-packages, setup-julia, checkout-submodules, default-branch, quarto-config-path, docs-base-url, preview-branch, timeout-minutes, rewrite-pr-preview-links, rewrite-issue-links, dispatch-version, dispatch-release-tag, legacy-paths, version-dropdown-title-template, version-in-navbar-title
bump-dev-version.yml Bump an R package’s DESCRIPTION dev-version counter after every merge to main, and open/auto-merge a PR to carry it in description-path, base-branch, pr-branch, auto-merge, dry-run
version-check.yml Fail a PR whose DESCRIPTION Version: differs from the base branch’s description-path, no-version-increment-label, bump-branch

Versioning

@v1 was frozen at the pre-2.0.0 snapshot when the breaking quarto-publish change cut @v2, so any capability pinned there has picked up no fixes since - including non-breaking ones, like cleanup-pr-previews’s compact-history input, which does not exist at @v1 at all. Pin preview.yml, preview-deploy.yml, cleanup-pr-previews.yml, and quarto-publish.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, lint-workflows.yml, spellcheck.yml, check-typos.yml, check-extra.yml, check-formatting.yml, claude-manage-project.yml, r-cmd-check.yml, and check-code-similarity.yml only ever shipped at @v2 (too new to exist at the frozen @v1 tag). quarto-publish.yml additionally has a genuine behavioral fork: @v1 stays frozen on the older GitHub Actions Pages-artifact deploy, while @v2 deploys to the gh-pages branch instead - required if the same repo also uses the PR-preview family, since Pages can only have one Source. check-bibliography-dois.yml, check-phi.yml, check-links.yml, check-non-standard-chars.yml, claude.yml, claude-code-review.yml, and update-snapshots.yml also pin @v2: each picked up a real fix since the freeze (a dependency-pin bump, a new input, or a security fix) that a consumer still on @v1 would miss (audited in gha#182). request-dependabot-review.yml only ever shipped at @v2 too (it postdates the freeze - see gha#252), as does sync-upstream.yml (added after the freeze - see gha#254), altdoc-multiversion-docs.yml (added after the freeze), and report-failure.yml (added after the freeze - see gha#325). gemini.yml, gemini-code-review.yml, antigravity-code-review.yml, cursor-code-review.yml, opencode-code-review.yml, and ai-code-review.yml likewise only ever shipped at @v2, having been added well after the freeze - see gha#357, gha#510, and gha#586. bump-dev-version.yml and version-check.yml postdate the freeze too (added in gha#388); pin both to @v2. small-model-agent.yml postdates the freeze too (added in gha#436); pin to @v2. check-ai-tells.yml postdates the freeze too (added in gha#382); pin to @v2. summary.yml, bump-submodule.yml, and sync-shared-fragments.yml were audited in the same pass and found unchanged since the freeze, so @v1 remains current for them. check-news.yml was initially grouped with them, but later gained the configurable no-changelog-label input at gha#143 - pin it to @v2 too. The stubs in examples/ carry the tag and permissions: block documented on each workflow’s own reference page; copy the specific stub you need as-is.

Quality checks

These run on pull requests (and often on main) to keep a repo’s content clean.

  • check-ai-tells.yml scans narrative prose in Markdown and Quarto files for AI tell patterns and density.

  • check-bibliography-dois.yml confirms BibTeX entries have DOIs that resolve and match CrossRef metadata.

  • check-junk-files.yml fails when the repository tracks operating-system or editor detritus – a .DS_Store, an AppleDouble ._* sidecar, an .Rhistory or .RData. It reports each file, the git rm --cached line that clears it, and the per-machine fix that stops it recurring in every other repository too.

  • check-non-standard-chars.yml flags curly quotes, en/em dashes, and the multiplication sign in .qmd, .R, and .md files, which can break Quarto and R rendering. The multiplication sign’s ASCII replacement depends on context: x or * in

    code, and $\times$ or × in prose, since Pandoc renders a \uXXXX escape literally rather than decoding it.

  • check-links.yml runs a lychee link check with a bundled config, a PR skip-label, and an auto-filed issue when main breaks.

  • check-news.yml enforces a NEWS.md changelog entry on each PR.

  • lint-yaml.yml runs yamllint over tracked YAML with a bundled config, plus a check that flags long run: script blocks as decomposition candidates.

  • lint-markdown.yml runs markdownlint-cli2 over tracked Markdown with a bundled config, plus a check that flags long fenced code blocks as decomposition candidates.

  • lint-qmd.yml runs markdownlint over the prose sections of tracked .qmd Quarto files; code chunks are stripped before linting and YAML front matter is skipped natively. A default 80-char line-length ceiling encourages semantic line breaks.

  • lint-changed-lines.yml runs lintr over only the lines a pull request adds or modifies (not whole changed files), so lint rules can be adopted or tightened incrementally: new and edited code must comply while untouched legacy code is left alone.

  • lint-changed-files.yml runs lintr at one of three scopes selected by scope: the files a pull request changed (default), a whole package (lintr::lint_package()), or a whole project (lintr::lint_dir()). lintr is installed from CRAN, not GitHub HEAD.

  • check-formatting.yml runs air format --check -- <path> via posit-dev/setup-air. Air is Posit’s R formatter, a Rust binary, so there is no R session to start. It complements lint-changed-lines.yml: Air settles layout, lintr decides the rest of R style. Check-only – it never rewrites the branch. Adopting it in a repo that is not already Air-formatted is a large first-pass diff; commit an air.toml first (empty is enough), run air format and commit that rewrite on its own, then enable the check.

  • check-phi.yml scans for content that looks like protected health information; see below.

  • check-secrets.yml scans the repository’s git history for committed credentials; see below.

  • spellcheck.yml spellchecks an R package’s prose with the {spelling} package, treating the package’s own inst/WORDLIST as the accepted vocabulary. It reads DESCRIPTION’s Title and Description fields, man/*.Rd, vignette sources (.Rmd, .qmd, .Rnw), and root README/NEWS/CHANGES/index Markdown – so a Quarto site’s other pages, and any repo that is not an R package, are out of its reach. It replaces the bespoke check-spelling.yaml several lab repos carry, and needs no wordlist migration, since that file is {spelling}’s own format.

  • check-typos.yml spellchecks those remaining files with crate-ci/typos: a Quarto site’s non-vignette pages, CONTRIBUTING.md-class Markdown, YAML, code comments, and repositories that are not R packages. Diff-scoped by default (only lines a PR adds, plus filename typos on paths the PR added or renamed), so a first run over an existing repo does not reflag years of drift; pass base-ref: all to scan the whole tree.

  • test-coverage.yml measures R-package test coverage with covr and uploads the Cobertura report to Codecov; pass the CODECOV_TOKEN secret through the caller’s secrets: block (optional for public repos, but recommended). Set examples-coverage: true for a second job that runs examples and vignettes. \dontrun{} does not run under R CMD check by default (only with --run-dontrun, which --as-cran does not imply); \donttest{} is skipped unless --as-cran or --run-donttest. That job does not upload to Codecov, produce a JUnit report, or apply min-coverage. On failure it uploads examples-coverage-test-failures. The percent appears in the log and step summary. Enabling it adds a check named <caller job> / Examples and vignettes coverage. Set min-coverage to fail the tests job when coverage falls below a chosen percent (empty/off by default). It does not apply to the examples job.

  • check-extra.yml runs three R-package checks that R CMD check passes over: warnings as errors on examples, tests, and vignettes; tests in random order with a logged seed; and a README.Rmd render that also fails when the knitted README.md is stale. Each check is its own job so every failure surfaces at once.

  • r-cmd-check.yml runs R CMD check across an OS x R-version matrix (r-lib’s 5-way default). A second caller job with hard: true runs a Depends/Imports/LinkingTo-only check on pull requests, with cache: false so a restored pak cache cannot silently contain Suggests.

PHI scanning

check-phi is a heuristic tripwire, not a HIPAA compliance tool. It flags patterns that should almost never be committed: US Social Security numbers, medical record numbers, dates of birth, study/participant identifier literals, and PHI-suggestive column headers in delimited data files (.csv/.tsv/.psv), so a human reviews before the data merges. It is tuned for high precision, so it misses free-text PHI such as patient names.

  • Diff-scoped on PRs. Only lines added by the PR are scanned; push scans the whole tracked tree.
  • Values are never printed. Findings report only file:line:col and the detector name, never the matched text, because a leaked identifier in a CI log is still a leak.
  • Suppressing false positives. Add a phi-allow comment on the line, or list a regex in an allowlist file (defaults to .github/phi-allowlist.txt). Use fail: false to downgrade findings to warnings.

The phone and email detectors exist but are off by default because they are noisy in source; enable them through the detectors input.

Secret scanning

check-secrets is check-phi’s counterpart for credentials: check-phi detects identifiers and has no notion of a password or a token, so a committed credential passes it cleanly.

  • It scans history, not the diff. A secret committed and then removed in a later commit is still exposed, because the orphaned commit stays fetchable through the GitHub API until the repository is garbage-collected. So the caller checks out with fetch-depth: 0, and a shallow clone is refused rather than reported clean on a partial scan.
  • Values are never printed, in the log or the run summary. Findings report only the rule, file:line, and the commit.
  • It blocks by default, where non-blocking prose checks only annotate.
  • Its paths-ignore patterns are Go regexes matched unanchored, not globs, so docs suppresses every path containing that substring; anchor with ^ when that matters.

It complements GitHub’s native secret scanning rather than replacing it – that is a repository setting evaluating pushes, not a pull-request check – and neither substitutes for rotating an exposed credential. See check-secrets.yml for the full reference.

Quarto sites

This family builds and ships Quarto sites, and it builds this very site.

  • quarto-publish.yml renders a Quarto project and deploys it to the gh-pages branch on main. One-time setup: set Settings -> Pages -> Source to “Deploy from a branch”, branch gh-pages / (root).
  • The preview family publishes a rendered site for each open PR to a pr-preview/pr-<n>/ directory on gh-pages. It is four cooperating workflows:
    1. preview.yml (build) renders the site and uploads it plus PR metadata as an artifact. It runs read-only in the possibly-forked PR context, so it cannot write to the base repo.
    2. preview-deploy.yml (deploy) triggers on the build’s workflow_run completion, downloads the artifact, and publishes it to gh-pages in the base-repo context, then comments the preview link.
    3. check-equation-renders.yml also triggers on the build’s workflow_run completion, downloads the same artifact, and crawls it with a headless browser to catch equations MathJax can’t typeset – invisible in the Quarto/pandoc build log, since MathJax only runs client-side.
    4. cleanup-pr-previews.yml (housekeeping) runs on a schedule and removes preview directories for PRs that have closed.

The build/deploy split is a trust boundary: untrusted fork code only ever runs in the read-only build half, while the privileged gh-pages push happens in the deploy half against base-repo code. Do not collapse them into one job. Repos with custom build sequences can keep a bespoke build workflow and still use preview-deploy.yml and cleanup-pr-previews.yml by uploading the three-file pr-preview-site artifact (site/, meta/pr-number.txt, meta/action.txt). See Partial adoption and artifact contract.

Two wiring requirements:

  • Both the deploy stub’s and the equation-check stub’s on: workflow_run: workflows: value must match the build stub’s name:. That string is how workflow_run finds the build.
  • workflow_run and schedule triggers only fire for the copy of the file on the default branch, so previews and cleanup do not take effect until the stubs merge to main.

This repo dogfoods all five. The caller stubs live in .github/workflows/: website-publish.yml, website-preview.yml, website-preview-deploy.yml, website-check-equation-renders.yml, and website-preview-cleanup.yml. They render this pure-markdown site with the R machinery turned off (no renv, no R CMD INSTALL, no Chrome).

Claude bots

The two Claude workflows are a pair.

  • claude.yml runs the agent-mode Claude Code bot: it responds to @claude mentions, edits files, and opens or updates PRs. A mention inside a code span, fence, or blockquote is not an invocation: a cheap mention-filter job runs detect-bot-mention and withholds the agent job (gha#554).
  • claude-code-review.yml runs a read-only Claude review: a consolidated summary on dispatched runs by default; add a pull_request trigger in the caller if you also want automatic reviews on PR updates. The model job’s GITHUB_TOKEN has no write scopes (contents / pull-requests / issues / actions: read); jobs that hold pull-requests: write / issues: write never invoke the model (gha#580). The caller still grants actions: read: post-review downloads the packed artifact, and a permissions: block sets unspecified scopes to none.

An @claude review mention (or any commit Claude pushes) routes through claude.yml, which dispatches claude-code-review.yml via workflow_dispatch. Install both, and keep the review stub named claude-code-review.yml (or set claude.yml’s review-workflow-file input to match) so the dispatch resolves. If you also want the direct /review path, enable issue_comment in the review stub too. Enabling issue_comment is required for either comment path, since dispatch-on-comment only ever runs on that event. If you have disabled claude.yml, enable it and also set the repository variable CLAUDE_AGENT_DISABLED to true, so an @claude review mention dispatches a review from the stub rather than reaching a workflow that no longer answers it.

gemini.yml and gemini-code-review.yml are the same pair for Gemini CLI, with the same division of labour: the agent answers @gemini and @gemini-cli mentions, and an @gemini review dispatches the reviewer rather than being answered in place. Keep the review stub named gemini-code-review.yml (or set gemini.yml’s review-workflow-file to match). Gemini needs a GEMINI_API_KEY secret; the two families are independent, so a repo can install either or both.

cursor-code-review.yml queues a Cursor Bugbot review through the Enterprise Bugbot API. It is not a mention bot: there is no cursor.yml counterpart. Team and individual Cursor plans should enable Bugbot in the dashboard (automatic reviews, or a PR comment cursor review / bugbot run) instead of this workflow. A Team-plan CURSOR_API_KEY fails the queue step with HTTP 401 Invalid Team API Key (gha#601); do not debug caller secret wiring for that signature. Add cursor to ai-code-review.yml’s agents input when CURSOR_API_KEY is set and the caller stub is installed.

opencode-code-review.yml runs the opencode CLI headless as a read-only reviewer: the diff arrives as an attachment, the agent reads the checkout for context, and its review is posted by the workflow itself. There is no opencode.yml mention bot yet (tracked in gha#586’s deferred follow-ups), so reviews arrive via pull_request, ai-code-review.yml dispatch, or manual workflow_dispatch. Add the OPENCODE_API_KEY secret (OpenCode Zen) and add opencode to ai-code-review.yml’s agents input.

All agent workflows align with upstream action standards (such as anthropics/claude-code-action v1.0.191 and run-gemini-cli v0.1.22, alongside the google-antigravity Python SDK) for trigger policies, permission isolation, and secret handling. In particular, central workflows (claude.yml, claude-code-review.yml) and the review attempt composite action (run-claude-review-attempt) were audited against upstream reference templates in anthropics/claude-code-action:

  • Action input forwarding. Upstream inputs such as claude_code_oauth_token, anthropic_api_key, show_full_output, and allowed_bots are forwarded cleanly, with use_sticky_comment: 'false' hardcoded in run-claude-review-attempt so each push surfaces fresh PR activity.
  • Permission isolation. In agent mode, review runs enforce read-only execution by disallowing git-write tools (git add, git commit, git rm, git push, and gh pr comment) while allowlisting inline comments and read-only tools (gh pr view, gh pr diff, WebFetch, WebSearch).
  • Execution guards. Single-turn verdicts are requested in system prompt addenda, while pre-flight credential checks and post-run execution checks gracefully handle unconfigured secrets or exhausted quota (#395).

ai-code-review.yml sits above whichever review workflows a repo has installed: it picks one of the configured agents at random and dispatches that agent’s own review workflow, falling through to the next candidate when one can’t be dispatched (no API key or token secret configured for it, or its review workflow file missing or disabled in the caller repo) as well as an agent run that fails or is cancelled during execution (gha#362, gha#444).

Content sync

Three workflows keep a repo current with content that lives elsewhere, without hand-bumping. The first two are the two directions of sharing single-source-of-truth content between a pair of repos; the third tracks an upstream a fork was cut from.

  • bump-submodule.yml is for the side that vendors the other repo as a git submodule. A scheduled run advances the submodule to its upstream HEAD and opens a PR when it moved.
  • sync-shared-fragments.yml is for the side that cannot add a submodule because the other repo already submodules it (a mutual submodule would recurse). It vendors a pinned copy of the named files into a dest-dir, records the source repo and commit in a JSON manifest, and opens a PR when the copy changes.
  • sync-upstream.yml is for a fork that tracks the project it was cut from. A scheduled run merges the upstream branch into a fork-owned automation branch and opens a PR when the merge brings changes, so the fork’s own changes are preserved and upstream’s updates are reviewed before they land. On a clean merge the PR is mergeable; on a conflict it carries the conflict markers for manual resolution.

All three reuse the open-sync-pr composite, which commits staged changes to a reused automation branch and opens or updates one PR (a no-op when nothing changed). For bump-submodule.yml/sync-shared-fragments.yml, scope each side to the other repo’s shared content so the two auto-PRs do not ping-pong.