Why Upkeep is a reusable workflow (not a step action)
July 6, 2026 · View on GitHub
Most GitHub Actions are consumed as a step:
steps:
- uses: actions/checkout@v4
Upkeep is consumed as a job, pointing at a workflow file:
jobs:
audit:
uses: wei18/upkeep/.github/workflows/audit.yml@v2
That second form looks unfamiliar next to the usual - uses: owner/action@v1, and people reasonably ask why. It is the standard, documented syntax for a reusable workflow (on: workflow_call) — see GitHub: Reuse workflows. Here is why Upkeep is built that way.
The reason: parallel, fault-isolated reviewers need strategy.matrix
Upkeep dispatches a team of reviewers. We want each reviewer to:
- run in parallel — a full audit should not take six times one reviewer's wall-clock; and
- be fault-isolated — one reviewer failing (timeout, API hiccup) must not abort the others.
The native GitHub primitive for "run the same unit many times, in parallel, independently" is strategy.matrix. Matrix is a job-level feature: only a workflow can declare jobs and a matrix. An action cannot. To fan the reviewers out across parallel, isolated matrix jobs, Upkeep must be a reusable workflow.
Why not just ship an action?
There are two kinds of action, and neither can express that fan-out:
- JavaScript / Docker action — a single entrypoint (e.g.
main: dist/index.js). It cannotuses:another action, so it could not delegate the LLM work toanthropics/claude-code-action; it would have to call Claude itself. And it still could not run jobs in parallel. - Composite action — runs as a sequence of steps inside one job. It can
uses:other actions (so it could callclaude-code-action), but with no matrix the reviewers would run sequentially, in a single job.
So a composite action (- uses: wei18/upkeep@v2) is possible — at the cost of sequential reviewers. Upkeep deliberately chose the reusable-workflow form to keep reviewers parallel and independently isolated. For a scheduled audit, the slower sequential path would be acceptable; we preferred parallelism and clean failure isolation.
As of v2.1, the repo root ships exactly that trade-off as action.yml (- uses: wei18/upkeep@v2) — a sequential alternative that gets Upkeep listed on the GitHub Marketplace. The reusable workflow above stays the recommended integration path whenever reviewer parallelism matters.
What you actually give up
Only the call-site syntax. jobs.<id>.uses: owner/repo/.github/workflows/file.yml@ref instead of - uses: owner/action@ref. Everything else behaves like an action: inputs via with:, secrets via secrets:, version pinning with @v1.