Authors fast, cheap, maintainable GitHub Actions workflows applying 2026 best practices: caching with `hashFiles` + `restore-keys`, parallelization via matrix + artifacts, reusability (composite actions for steps, reusable workflows for jobs), security (SHA-pinned actions, least-privilege `GITHUB_TOKEN`, concurrency), trackable errors (named steps, step summaries, annotations, and stdout/stderr that always reaches the run log so agents can act on failures), and feedback for comment-triggered runs (👀 acknowledgement reaction on start, 🚀/👎 outcome reaction plus a run-linked comment at the end). Two modes: `scaffold` (default) generates workflow YAML; `review` audits an existing workflow against the same rules. Use when creating CI/CD pipelines, optimizing slow workflows, deduping copy-pasted YAML across repos, or auditing workflow security. Triggers on "github action", "github workflow", "ci pipeline", "create workflow", "speed up ci", "review my workflow", "/github-actions-author".
Non-negotiable — every step's output reaches the run log
This applies to both modes and outranks every other preference in this skill.
Every command in every scaffolded or reviewed workflow must write its stdout and stderr to the job log.
Output that lands only in a file, only in an artifact, only in $GITHUB_STEP_SUMMARY, or in /dev/null is invisible to gh run view <run-id> --log-failed — the only surface /ci-auto-fix, /test-auto-fix, /implement-suggestion, and an on-call human read a failure from.
A failing step that printed nothing cannot be diagnosed or fixed by an agent; it can only be escalated.
Minimum bar for every run: block:
- name: Run unit tests
shell: bash
run: |
set -euo pipefail
npm test 2>&1 | tee test-output.log # tee, never `> file`
Forbidden outright: > /dev/null, 2>/dev/null, cmd > out.txt 2>&1, --silent, --quiet, -q, a machine-only reporter with no human output, and || true without echoing the captured output and exit code.
tee requires set -o pipefail, otherwise the step goes green on a failed command.
Full rule, decision table, examples, and the review-mode grep:
rules/log-output-visibility.md — read it in Phase 4 of scaffold and in every review.
Mode Detection
Parse $ARGUMENTS (first token):
Mode
Default
Trigger
scaffold
yes
Default. "create", "scaffold", "new workflow", or no token.
review
"review", "audit", path to an existing .github/workflows/*.
State the detected mode and target in one line before continuing:
Mode: scaffold
Target: .github/workflows/ci.yml
Scaffold Workflow
Five phases. Each has a gate; do not proceed until it passes.
Third-party actions SHA-pinned, permissions: minimal, every step named, failures surface a stack-trace path, every command's stdout + stderr reaches the run log, and any comment-triggered workflow — including a workflow_dispatch a comment or bot fired, but not one fired from the Actions UI — acknowledges (👀) and reports its outcome (🚀/👎 + run link).
Phase 0 — Intent and shape
Ask in one batched message:
Workflow purpose — one sentence. CI, deploy, release, scheduled,
manual, or composite/reusable shared piece?
Trigger surface — push, pull_request, schedule, workflow_dispatch,
workflow_call, or a comment / slash command (issue_comment,
pull_request_review_comment, pull_request_review)? Which branches?
Which path globs (to skip irrelevant runs)? Comment-triggered runs
additionally require acknowledgement + outcome feedback — see
rules/feedback.md.
Shape — single job, matrix (axes?), build-then-test (artifact
hand-off), or split jobs (lint / typecheck / test / build) running
in parallel?
Reuse — is this YAML duplicated across repos or workflows? If so,
refactor target is a composite action (steps) or reusable workflow
(jobs) — see rules/reusability.md.
Secrets — none, repo secrets, environment secrets, or OIDC to a
cloud provider (AWS/GCP/Azure)?
Repeat the answers back before generating.
Phase 1–4
Walk each phase using the linked rule file. Each rule is self-contained
and includes a decision table plus a good/bad example.
Cache hit rate over the last 10 completed runs — count cache-restore
outcomes in the logs (hit rate = Cache restored ÷ total restore
attempts; logs older than the retention window return nothing, so
report n/a rather than guessing):
gh run list --workflow <file>.yml --status completed --limit 10 \
--json databaseId --jq '.[].databaseId' \
| while read -r id; do
gh run view "$id" --log 2>/dev/null \
| grep -hoE 'Cache restored from key|Cache not found'
done | sort | uniq -c
For each rule file in rules/, mark PASS / WARN /
FAIL with one line of evidence (line N: <quote>).
Log visibility is mandatory in every review — run the grep in
rules/log-output-visibility.md
and report every unjustified hit as a FAIL.
End with a prioritised "Top 3 fixes" list — biggest speed / cost /
security wins first.
Offer to apply the fixes if the user wants — switch to scaffold
mode for that section.
Format:
Workflow: .github/workflows/ci.yml
Lines: 142
Jobs: 4
Average run (last 10): 7m12s # or: n/a (no completed runs)
Cache hit rate (last 10): 30% # or: n/a (logs expired / no cache steps)
Anatomy: PASS
Triggers + concurrency: WARN — no `cancel-in-progress` on PR (line 8)
Caching: FAIL — primary key uses `github.sha`, no `restore-keys` (line 34)
Parallelization: PASS
Reusability: WARN — install-deps duplicated across 3 jobs (lines 28, 71, 94)
Security: FAIL — `actions/checkout@v4` tag-pinned, no SHA (line 22)
Observability: WARN — 4 unnamed steps (lines 31, 45, 68, 102)
Log visibility: FAIL — `npm test > test.log 2>&1` hides all output (line 57); `npm ci --silent` (line 29)
Top 3 fixes:
1. Replace `github.sha` cache key with `${{ hashFiles('package-lock.json') }}` + restore-keys (line 34) — expected 60-80% faster on cache hits.
2. SHA-pin every third-party action, comment with the version (line 22, 38, 51).
3. Extract install-deps into `.github/actions/setup-node-deps/action.yml` (composite) — removes 2x 40 LOC duplication.
Cache the package manager's global directory, not node_modules.
Use actions/setup-node@<sha> { cache: 'npm' } or actions/cache@<sha> keyed by hashFiles('lockfile') with restore-keys fallback.
One responsibility per workflow file.ci.yml, deploy.yml,
release.yml, scheduled.yml. Resist the mega-workflow.
Parallelize first, then cache. Splitting lint / typecheck / test
into separate jobs gives near-linear wins; cache reduces the cold
tail.
Composite actions for steps, reusable workflows for jobs. Never
put job orchestration into a composite action; never use a reusable
workflow to wrap two shell lines.
SHA-pin every third-party action. Tags are mutable; SHAs are
immutable. actions/checkout@<40-hex> # v4.2.0.
Least-privilege GITHUB_TOKEN. Start with permissions: {} at
the workflow level; grant per-job. Read-only by default in
2023+ repos — keep it that way.
concurrency is mandatory. PRs use cancel-in-progress: true;
deploys use cancel-in-progress: false. No exceptions.
Name every step. Anonymous run: blocks are unsearchable in logs
and unsourceable in failure annotations.
Never swallow output. Every command's stdout and stderr must reach
the run log — tee, never >; no --silent / --quiet / /dev/null.
The log is the only thing gh run view --log-failed returns, and it is
what agents act on. See
rules/log-output-visibility.md.
Comment-triggered runs must give feedback. A workflow with no PR
status check (issue_comment, pull_request_review_comment,
pull_request_review, or a workflow_dispatch a comment or bot fired)
is invisible. Acknowledge as the first step, then report the outcome
on both paths — a 🚀/👍 reaction on success, a 👎 reaction plus a
comment linking the run on failure. pull_request_review has no
reactable comment, so it uses a single sticky PR comment for both beats.
See rules/feedback.md.
Anti-patterns (one-liners — full list in each rule file)
@main / @latest / unpinned third-party action.
Primary cache key includes ${{ github.sha }}.
permissions: write-all (or the default, unset, on a pre-2023 repo).
Lint, typecheck, and test glued sequentially in one job.
Composite action that defines jobs: (it can't — that's a workflow).
Reusable workflow used to wrap two shell steps.
cancel-in-progress: true on a deploy workflow.
Unscoped on: push: triggering on every branch and every path.
20 anonymous run: blocks with no name:.
Secrets passed as workflow inputs instead of secrets: map.
Output redirected to a file or /dev/null instead of tee-d to the log.
--silent / --quiet / -q on a step whose job is to report.
Machine-only reporter (JUnit/SARIF/JSON) with no human output on stdout.
Diagnostics uploaded as an artifact or written only to $GITHUB_STEP_SUMMARY.
|| true or continue-on-error: true with nothing echoed.
tee without set -o pipefail (green job, failed command).
Comment/slash-command workflow that never reacts to the triggering comment (user can't tell it ran).
Feedback only on success — a failed comment-triggered run left with no reaction or comment.
Failure reaction (👎) with no comment linking the run (user knows it broke, not where).
Reacting to a comment before gating the command by author / prefix (any user drives the bot).
Definition of Done
A scaffold run is done when:
Workflow purpose, triggers, stack, and shape were confirmed
before any YAML was written.
on: block is scoped to the relevant branches and paths.
concurrency is set with the correct cancel-in-progress value
for the workflow type.
permissions: is set at the workflow level (or every job) and
lists only what each job actually needs.
Every third-party action is pinned to a full-length commit SHA
with a # vX.Y.Z comment.
Cache key uses hashFiles(<lockfile>) and includes runner.os
(plus matrix axes); restore-keys is present.
Independent jobs run in parallel; sequential dependencies are
explicit via needs:.
Repeated step blocks are extracted (composite action) or
repeated job blocks are extracted (reusable workflow).
Every step has a name: that reads as a sentence ("Install
dependencies", not npm-ci).
Failure paths surface to the PR via annotations or
$GITHUB_STEP_SUMMARY.
Every command's stdout and stderr reaches the run log — no
/dev/null, no file-only redirection, no --silent / --quiet,
no machine-only reporter.
Every run: block that pipes to tee (or any pipe) sets
set -o pipefail.
Every || true / continue-on-error: true step echoes the captured
output and its exit code.
If comment-triggered and producing no PR status check — issue_comment,
pull_request_review_comment, pull_request_review, or a
workflow_dispatch a comment or bot fired, per the trigger table in
rules/feedback.md — the
workflow acknowledges as its first step and reports the outcome on both
paths, with issues: write / pull-requests: write granted and the
command gated before it acknowledges. Where the trigger carries a
reactable comment that means a 👀 reaction first, then a 🚀/👍 reaction
on success and a 👎 reaction plus a run-linked comment on failure.
A pull_request_review trigger carries none — GitHub exposes no
reactions endpoint for a review — so it uses a single sticky PR comment
for both beats. A workflow_dispatch fired from the Actions UI has no
triggering comment and is out of scope.
If using OIDC, id-token: write is set at the job level only.
User received a one-paragraph summary of what was created and
where to commit it.
A review run is done when:
Every rule produced a PASS / WARN / FAIL with line evidence.
Log visibility was checked with the grep and reported explicitly.
Top 3 fixes are ranked by impact (speed, cost, or security).
User received an offer to apply the fixes interactively.
1---2name: github-actions-author3description: Authors fast, cheap, maintainable GitHub Actions workflows applying 2026 best practices: caching with `hashFiles` + `restore-keys`, parallelization via matrix + artifacts, reusability (composite actions for steps, reusable workflows for jobs), security (SHA-pinned actions, least-privilege `GITHUB_TOKEN`, concurrency), trackable errors (named steps, step summaries, annotations, and stdout/stderr that always reaches the run log so agents can act on failures), and feedback for comment-triggered runs (👀 acknowledgement reaction on start, 🚀/👎 outcome reaction plus a run-linked comment at the end). Two modes: `scaffold` (default) generates workflow YAML; `review` audits an existing workflow against the same rules. Use when creating CI/CD pipelines, optimizing slow workflows, deduping copy-pasted YAML across repos, or auditing workflow security. Triggers on "github action", "github workflow", "ci pipeline", "create workflow", "speed up ci", "review my workflow", "/github-actions-author".4license: MIT5---67# GitHub Actions Author89Generate or audit GitHub Actions workflow YAML against 2026 best10practices for speed, cost, reusability, and security.1112> **This `SKILL.md` is a thin index.** Detailed rules live in13> [`rules/*.md`](./rules/) and load on demand. Drop-in starters live in14> [`templates/*.md`](./templates/). The decision tree for picking a15> shape lives in [`references/decision-tree.md`](./references/decision-tree.md).1617---1819## Non-negotiable — every step's output reaches the run log2021This applies to **both modes** and outranks every other preference in this skill.2223Every command in every scaffolded or reviewed workflow must write its stdout **and** stderr to the job log.24Output that lands only in a file, only in an artifact, only in `$GITHUB_STEP_SUMMARY`, or in `/dev/null` is invisible to `gh run view <run-id> --log-failed` — the only surface `/ci-auto-fix`, `/test-auto-fix`, `/implement-suggestion`, and an on-call human read a failure from.25A failing step that printed nothing cannot be diagnosed or fixed by an agent; it can only be escalated.2627Minimum bar for every `run:` block:2829```yaml30- name: Run unit tests31 shell: bash32 run: |33 set -euo pipefail34 npm test 2>&1 | tee test-output.log # tee, never `> file`35```3637Forbidden outright: `> /dev/null`, `2>/dev/null`, `cmd > out.txt 2>&1`, `--silent`, `--quiet`, `-q`, a machine-only reporter with no human output, and `|| true` without echoing the captured output and exit code.38`tee` requires `set -o pipefail`, otherwise the step goes green on a failed command.3940Full rule, decision table, examples, and the review-mode grep:41[`rules/log-output-visibility.md`](./rules/log-output-visibility.md) — read it in Phase 4 of scaffold and in every review.4243---4445## Mode Detection4647Parse `$ARGUMENTS` (first token):4849| Mode | Default | Trigger |50| ---------- | ------- | ------------------------------------------------------------- |51| `scaffold` | **yes** | Default. "create", "scaffold", "new workflow", or no token. |52| `review` | | "review", "audit", path to an existing `.github/workflows/*`. |5354State the detected mode and target in one line before continuing:5556```57Mode: scaffold58Target: .github/workflows/ci.yml59```6061---6263## Scaffold Workflow6465Five phases. Each has a gate; do not proceed until it passes.6667| Phase | Name | Rule file | Gate |68| ----- | --------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------- |69| 0 | Intent + shape | [`references/decision-tree.md`](./references/decision-tree.md) | Trigger, stack, and shape (single / matrix / reusable) confirmed. |70| 1 | Anatomy + triggers | [`rules/workflow-anatomy.md`](./rules/workflow-anatomy.md), [`rules/triggers-and-concurrency.md`](./rules/triggers-and-concurrency.md) | `on:` block scoped (branches + paths), concurrency set. |71| 2 | Speed (cache + parallel) | [`rules/caching.md`](./rules/caching.md), [`rules/parallelization.md`](./rules/parallelization.md) | Cache key is `hashFiles`-based with `restore-keys`; independent jobs run in parallel. |72| 3 | Reusability | [`rules/reusability.md`](./rules/reusability.md) | Any block used > 1 place is extracted to a composite action or reusable workflow. |73| 4 | Security + errors | [`rules/security.md`](./rules/security.md), [`rules/observability.md`](./rules/observability.md), [`rules/log-output-visibility.md`](./rules/log-output-visibility.md), [`rules/feedback.md`](./rules/feedback.md) | Third-party actions SHA-pinned, `permissions:` minimal, every step named, failures surface a stack-trace path, **every command's stdout + stderr reaches the run log**, and any comment-triggered workflow — including a `workflow_dispatch` a comment or bot fired, but not one fired from the Actions UI — acknowledges (👀) and reports its outcome (🚀/👎 + run link). |7475### Phase 0 — Intent and shape7677Ask in **one** batched message:78791. **Workflow purpose** — one sentence. CI, deploy, release, scheduled,80 manual, or composite/reusable shared piece?812. **Trigger surface** — push, pull_request, schedule, workflow_dispatch,82 workflow_call, or a comment / slash command (`issue_comment`,83 `pull_request_review_comment`, `pull_request_review`)? Which branches?84 Which path globs (to skip irrelevant runs)? Comment-triggered runs85 additionally require acknowledgement + outcome feedback — see86 [`rules/feedback.md`](./rules/feedback.md).873. **Stack** — Node (npm/yarn/pnpm/bun), Python (pip/uv/poetry), Go,88 Rust, Java/Gradle, Docker, mixed?894. **Shape** — single job, matrix (axes?), build-then-test (artifact90 hand-off), or split jobs (lint / typecheck / test / build) running91 in parallel?925. **Reuse** — is this YAML duplicated across repos or workflows? If so,93 refactor target is a composite action (steps) or reusable workflow94 (jobs) — see [`rules/reusability.md`](./rules/reusability.md).956. **Secrets** — none, repo secrets, environment secrets, or OIDC to a96 cloud provider (AWS/GCP/Azure)?9798Repeat the answers back before generating.99100### Phase 1–4101102Walk each phase using the linked rule file. Each rule is self-contained103and includes a decision table plus a good/bad example.104105### Phase 5 — Self-check106107Run the [Definition of Done](#definition-of-done) checklist below.108109---110111## Review Workflow112113Read the target `.yml` and produce a structured report — do not mutate114unless asked.1151161. Parse the workflow: triggers, jobs, steps, permissions, concurrency.1172. Measure the run metrics — **report each metric when computable; print118 `n/a (<reason>)` otherwise** (no runs yet, logs expired, no cache steps).119120 Average run duration over the last 10 completed runs:121122 ```bash123 gh run list --workflow <file>.yml --status completed --limit 10 \124 --json startedAt,updatedAt \125 --jq 'map((.updatedAt | fromdate) - (.startedAt | fromdate))126 | add / length | round127 | "\(. / 60 | floor)m\(. % 60)s"'128 ```129130 Cache hit rate over the last 10 completed runs — count cache-restore131 outcomes in the logs (hit rate = `Cache restored` ÷ total restore132 attempts; logs older than the retention window return nothing, so133 report `n/a` rather than guessing):134135 ```bash136 gh run list --workflow <file>.yml --status completed --limit 10 \137 --json databaseId --jq '.[].databaseId' \138 | while read -r id; do139 gh run view "$id" --log 2>/dev/null \140 | grep -hoE 'Cache restored from key|Cache not found'141 done | sort | uniq -c142 ```1431443. For each rule file in [`rules/`](./rules/), mark **PASS / WARN /145 FAIL** with one line of evidence (`line N: <quote>`).146 Log visibility is mandatory in every review — run the grep in147 [`rules/log-output-visibility.md`](./rules/log-output-visibility.md#verification)148 and report every unjustified hit as a **FAIL**.1494. End with a prioritised "Top 3 fixes" list — biggest speed / cost /150 security wins first.1515. Offer to apply the fixes if the user wants — switch to `scaffold`152 mode for that section.153154Format:155156```157Workflow: .github/workflows/ci.yml158Lines: 142159Jobs: 4160Average run (last 10): 7m12s # or: n/a (no completed runs)161Cache hit rate (last 10): 30% # or: n/a (logs expired / no cache steps)162163Anatomy: PASS164Triggers + concurrency: WARN — no `cancel-in-progress` on PR (line 8)165Caching: FAIL — primary key uses `github.sha`, no `restore-keys` (line 34)166Parallelization: PASS167Reusability: WARN — install-deps duplicated across 3 jobs (lines 28, 71, 94)168Security: FAIL — `actions/checkout@v4` tag-pinned, no SHA (line 22)169Observability: WARN — 4 unnamed steps (lines 31, 45, 68, 102)170Log visibility: FAIL — `npm test > test.log 2>&1` hides all output (line 57); `npm ci --silent` (line 29)171172Top 3 fixes:1731. Replace `github.sha` cache key with `${{ hashFiles('package-lock.json') }}` + restore-keys (line 34) — expected 60-80% faster on cache hits.1742. SHA-pin every third-party action, comment with the version (line 22, 38, 51).1753. Extract install-deps into `.github/actions/setup-node-deps/action.yml` (composite) — removes 2x 40 LOC duplication.176```177178---179180## Required Reading by Phase181182Load on demand — do not preload.183184| Phase | Files |185| ----- | --------------------------------------------------------------------------------------------------------------------------- |186| 0 | [`references/decision-tree.md`](./references/decision-tree.md) |187| 1 | [`rules/workflow-anatomy.md`](./rules/workflow-anatomy.md), [`rules/triggers-and-concurrency.md`](./rules/triggers-and-concurrency.md) |188| 2 | [`rules/caching.md`](./rules/caching.md), [`rules/parallelization.md`](./rules/parallelization.md) |189| 3 | [`rules/reusability.md`](./rules/reusability.md) |190| 4 | [`rules/security.md`](./rules/security.md), [`rules/observability.md`](./rules/observability.md), [`rules/log-output-visibility.md`](./rules/log-output-visibility.md), [`rules/feedback.md`](./rules/feedback.md) |191192Drop-in starters in [`templates/`](./templates/):193194- [`node-ci.yml.md`](./templates/node-ci.yml.md) — Node.js CI with cache, matrix, parallel jobs.195- [`python-ci.yml.md`](./templates/python-ci.yml.md) — Python CI with pip cache.196- [`reusable-workflow.yml.md`](./templates/reusable-workflow.yml.md) — `workflow_call` callee + caller.197- [`composite-action.yml.md`](./templates/composite-action.yml.md) — `.github/actions/<name>/action.yml`.198- [`deploy-oidc.yml.md`](./templates/deploy-oidc.yml.md) — deploy with OIDC, no long-lived secrets.199200---201202## Core Principles2032041. **Cache the package manager's global directory, not `node_modules`.**205 Use `actions/setup-node@<sha> { cache: 'npm' }` or `actions/cache@<sha>` keyed by `hashFiles('lockfile')` with `restore-keys` fallback.2062. **One responsibility per workflow file.** `ci.yml`, `deploy.yml`,207 `release.yml`, `scheduled.yml`. Resist the mega-workflow.2083. **Parallelize first, then cache.** Splitting lint / typecheck / test209 into separate jobs gives near-linear wins; cache reduces the cold210 tail.2114. **Composite actions for steps, reusable workflows for jobs.** Never212 put job orchestration into a composite action; never use a reusable213 workflow to wrap two shell lines.2145. **SHA-pin every third-party action.** Tags are mutable; SHAs are215 immutable. `actions/checkout@<40-hex> # v4.2.0`.2166. **Least-privilege `GITHUB_TOKEN`.** Start with `permissions: {}` at217 the workflow level; grant per-job. Read-only by default in218 2023+ repos — keep it that way.2197. **`concurrency` is mandatory.** PRs use `cancel-in-progress: true`;220 deploys use `cancel-in-progress: false`. No exceptions.2218. **Name every step.** Anonymous `run:` blocks are unsearchable in logs222 and unsourceable in failure annotations.2239. **Never swallow output.** Every command's stdout and stderr must reach224 the run log — `tee`, never `>`; no `--silent` / `--quiet` / `/dev/null`.225 The log is the only thing `gh run view --log-failed` returns, and it is226 what agents act on. See227 [`rules/log-output-visibility.md`](./rules/log-output-visibility.md).22810. **Comment-triggered runs must give feedback.** A workflow with no PR229 status check (`issue_comment`, `pull_request_review_comment`,230 `pull_request_review`, or a `workflow_dispatch` a comment or bot fired)231 is invisible. Acknowledge as the **first** step, then report the outcome232 on **both** paths — a 🚀/👍 reaction on success, a 👎 reaction plus a233 comment linking the run on failure. `pull_request_review` has no234 reactable comment, so it uses a single sticky PR comment for both beats.235 See [`rules/feedback.md`](./rules/feedback.md).236237---238239## Anti-patterns (one-liners — full list in each rule file)240241- `@main` / `@latest` / unpinned third-party action.242- Primary cache key includes `${{ github.sha }}`.243- `permissions: write-all` (or the default, unset, on a pre-2023 repo).244- Lint, typecheck, and test glued sequentially in one job.245- Composite action that defines `jobs:` (it can't — that's a workflow).246- Reusable workflow used to wrap two shell steps.247- `cancel-in-progress: true` on a deploy workflow.248- Unscoped `on: push:` triggering on every branch and every path.249- 20 anonymous `run:` blocks with no `name:`.250- Secrets passed as workflow inputs instead of `secrets:` map.251- Output redirected to a file or `/dev/null` instead of `tee`-d to the log.252- `--silent` / `--quiet` / `-q` on a step whose job is to report.253- Machine-only reporter (JUnit/SARIF/JSON) with no human output on stdout.254- Diagnostics uploaded as an artifact or written only to `$GITHUB_STEP_SUMMARY`.255- `|| true` or `continue-on-error: true` with nothing echoed.256- `tee` without `set -o pipefail` (green job, failed command).257- Comment/slash-command workflow that never reacts to the triggering comment (user can't tell it ran).258- Feedback only on success — a failed comment-triggered run left with no reaction or comment.259- Failure reaction (👎) with no comment linking the run (user knows it broke, not where).260- Reacting to a comment before gating the command by author / prefix (any user drives the bot).261262---263264## Definition of Done265266A **scaffold** run is done when:267268- [ ] Workflow purpose, triggers, stack, and shape were confirmed269 before any YAML was written.270- [ ] `on:` block is scoped to the relevant branches **and** paths.271- [ ] `concurrency` is set with the correct `cancel-in-progress` value272 for the workflow type.273- [ ] `permissions:` is set at the workflow level (or every job) and274 lists only what each job actually needs.275- [ ] Every third-party action is pinned to a full-length commit SHA276 with a `# vX.Y.Z` comment.277- [ ] Cache key uses `hashFiles(<lockfile>)` and includes `runner.os`278 (plus matrix axes); `restore-keys` is present.279- [ ] Independent jobs run in parallel; sequential dependencies are280 explicit via `needs:`.281- [ ] Repeated step blocks are extracted (composite action) or282 repeated job blocks are extracted (reusable workflow).283- [ ] Every step has a `name:` that reads as a sentence ("Install284 dependencies", not `npm-ci`).285- [ ] Failure paths surface to the PR via annotations or286 `$GITHUB_STEP_SUMMARY`.287- [ ] Every command's stdout **and** stderr reaches the run log — no288 `/dev/null`, no file-only redirection, no `--silent` / `--quiet`,289 no machine-only reporter.290- [ ] Every `run:` block that pipes to `tee` (or any pipe) sets291 `set -o pipefail`.292- [ ] Every `|| true` / `continue-on-error: true` step echoes the captured293 output and its exit code.294- [ ] The log-visibility grep from295 [`rules/log-output-visibility.md`](./rules/log-output-visibility.md#verification)296 returns no unjustified hits.297- [ ] If comment-triggered and producing no PR status check — `issue_comment`,298 `pull_request_review_comment`, `pull_request_review`, or a299 `workflow_dispatch` a comment or bot fired, per the trigger table in300 [`rules/feedback.md`](./rules/feedback.md#when-this-rule-applies) — the301 workflow acknowledges as its first step and reports the outcome on both302 paths, with `issues: write` / `pull-requests: write` granted and the303 command gated before it acknowledges. Where the trigger carries a304 reactable comment that means a 👀 reaction first, then a 🚀/👍 reaction305 on success and a 👎 reaction plus a run-linked comment on failure.306 A `pull_request_review` trigger carries none — GitHub exposes no307 reactions endpoint for a review — so it uses a single sticky PR comment308 for both beats. A `workflow_dispatch` fired from the Actions UI has no309 triggering comment and is out of scope.310- [ ] If using OIDC, `id-token: write` is set at the job level only.311- [ ] User received a one-paragraph summary of what was created and312 where to commit it.313314A **review** run is done when:315316- [ ] Every rule produced a PASS / WARN / FAIL with line evidence.317- [ ] Log visibility was checked with the grep and reported explicitly.318- [ ] Top 3 fixes are ranked by impact (speed, cost, or security).319- [ ] User received an offer to apply the fixes interactively.
Run npx skillmds@latest add mthines/github-actions-author in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Authors fast, cheap, maintainable GitHub Actions workflows applying 2026 best practices: caching with `hashFiles` + `restore-keys`, parallelization via matrix + artifacts, reusability (composite actions for steps, reusable workflows for jobs), security (SHA-pinned actions, least-privilege `GITHUB_TOKEN`, concurrency), trackable errors (named steps, step summaries, annotations, and stdout/stderr that always reaches the run log so agents can act on failures), and feedback for comment-triggered runs (👀 acknowledgement reaction on start, 🚀/👎 outcome reaction plus a run-linked comment at the end). Two modes: `scaffold` (default) generates workflow YAML; `review` audits an existing workflow against the same rules. Use when creating CI/CD pipelines, optimizing slow workflows, deduping copy-pasted YAML across repos, or auditing workflow security. Triggers on "github action", "github workflow", "ci pipeline", "create workflow", "speed up ci", "review my workflow", "/github-actions-author". It is listed under DevOps & Infra on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
mthines (@mthines) published this skill. Their other Agent Skills are listed on their SkillMD profile.