---
title: "More GitHub Actions optimizations: 16 recipes your agent can apply"
description: "Sixteen GitHub Actions optimization recipes with runnable YAML, tradeoffs, and verify steps, plus one JSON file your coding agent fetches to find the right one."
date: 2026-08-24
url: https://starsling.dev/blog/more-github-actions-optimizations-16-recipes-your-agent-can-apply
canonicalUrl: https://starsling.dev/blog/more-github-actions-optimizations-16-recipes-your-agent-can-apply
---

# More GitHub Actions optimizations: 16 recipes your agent can apply

<figure>
  [Image: 16 recipes for agents: GitHub Actions optimizations you can give to your coding agent]
</figure>

Last week we published [18 best practices guides](/blog/github-actions-optimizations-18-best-practices-guides-you-can-give-to-your-agent-to-read): one page per practice, on what good CI looks like. They are written to be handed to a coding agent, and every one is served as plain markdown at the same URL plus `.md`, so an agent reads the page without executing a line of JavaScript.

Today there are 16 more pages, built the same way, and they answer a different question. A practice page says what good looks like. A recipe page says **what to change in your workflow when it does not**: the configuration that is wrong, the configuration that is right, and how to tell which one you have. Each one carries a copy-paste prompt that points your agent at that specific fix.

New today, [`/github-actions/catalog.json`](/github-actions/catalog.json) indexes every rule across both sets. An agent told to make your CI faster fetches that one file, picks the rules that apply to your repo, and goes straight to the page for each, instead of crawling the site and guessing at which advice is relevant.

## One file for your agent

One fetch gets the whole index:

```bash
curl https://starsling.dev/github-actions/catalog.json
```

It opens with the skills that run these rules, then lists the rules themselves, one row each:

```json
{
  "version": 1,
  "install": "npx skills add starslingdev/skills",
  "skills": [
    {
      "name": "ci-speedup",
      "url": "https://starsling.dev/ci-speedup",
      "description": "Reads your GitHub Actions run history and finds what is costing you wall-clock, then opens the fix as a reviewable pull request."
    }
    ...
  ],
  "rules": [
    {
      "id": "ci.cache.turborepo",
      "title": "Turborepo cache health",
      "category": "performance",
      "family": "optimization",
      "url": "/github-actions/optimizations/optimize-turborepo",
      "markdown": "/github-actions/optimizations/optimize-turborepo.md",
      "patternIds": [
        {
          "id": "OPT3",
          "url": "https://raw.githubusercontent.com/starslingdev/skills/main/skills/ci-speedup/references/optimization-patterns.md"
        }
        ...
      ],
      "detectionMode": "static",
      "retired": false
    }
  ]
}
```

Every `patternIds` entry carries the document that defines that detector, so an agent holding `OPT3` can fetch what OPT3 actually checks.

Install the skills and they run these rules against your repo directly. To work from the pages instead: fetch the catalog, pick the rules that apply, fetch those pages as markdown, act.

## Static, runtime, or both

Each rule carries a `detectionMode`, and it is worth reading before you point an agent at a repo.

A **static** rule is decidable from your repository alone: a missing cache key, a fixed sleep, a `needs:` edge with nothing behind it. An agent with your files can find and fix these on its own.

A **runtime** rule needs your actual run history: which job is the long pole, which step's duration swings run to run, how much of a job is cleanup rather than work. Those pages send the agent to the Actions API, because the finding does not exist in the file.

A **hybrid** rule needs both, and the catalog carries all three values. An agent filtering on this field can select the rules its context actually supports instead of guessing at conclusions it has no data for.

## The ids are stable

`ci.cache.turborepo` is a permanent name, not a slug that gets renamed when the page is rewritten. The same id identifies the same rule in the catalog and on the page it resolves to, today and later, so an id you record against your own workflows keeps pointing at the same thing.

## The catalog

### Installs and dependencies

The work most pipelines repeat the most.

- [Optimize npm installs in GitHub Actions](/github-actions/optimizations/optimize-npm-install) covers turning on the setup action's own cache, and dropping tool installs the runner already provides.
- [Optimize pnpm installs in GitHub Actions](/github-actions/optimizations/optimize-pnpm-install) covers the store cache, the setup-order trap that makes it silently cache nothing, and version drift between workflows.
- [Stop reinstalling the same dependencies](/github-actions/optimizations/avoid-duplicate-dependency-installs) is about every job installing the same tree because nothing is handed between jobs.
- [Share setup steps across jobs](/github-actions/optimizations/avoid-duplicated-setup) covers composite actions, and the conditional step whose expensive setup runs unconditionally.

### Builds

- [Optimize Turborepo caching](/github-actions/optimizations/optimize-turborepo) covers `TURBO_FORCE`, a read-only remote cache with no writer, and tasks missing `outputs` or `inputs`.
- [Keep build caches between runs](/github-actions/optimizations/use-incremental-builds) is about the clean step that deletes the incremental state you just restored.
- [Build once and reuse it across jobs](/github-actions/optimizations/avoid-duplicate-compilation) covers the same commit compiled twice, in one workflow or across two.

### Docker

- [Use Docker layer caching](/github-actions/optimizations/use-docker-layer-caching) covers `cache-from` and `cache-to` with the GitHub Actions cache backend.
- [Speed up Docker builds](/github-actions/optimizations/optimize-docker-builds) covers starting only the service containers a test needs, and pinning image tags.

### Tests

- [Speed up Playwright tests](/github-actions/optimizations/optimize-playwright) is about traces, videos and screenshots uploaded on green runs as well as red ones.
- [Make vitest run mode explicit](/github-actions/optimizations/optimize-vitest) covers pinning run mode to the command rather than depending on an implicit environment variable, and bounding the job.

### The job graph

- [Run independent jobs in parallel](/github-actions/optimizations/parallelize-independent-jobs) covers workflows chained by `workflow_run`, and matrices throttled to one.
- [Remove needs: dependencies that do not exist](/github-actions/optimizations/remove-unnecessary-job-dependencies) gives the test for whether an edge is real: an artifact, an output, or a side effect. Anything else is ordering for taste, paid for in wall-clock.

### Measured from your run history

These three cannot be answered by reading your repository, so their pages send your agent to the GitHub Actions API instead.

- [Find and shorten the critical path](/github-actions/optimizations/reduce-workflow-critical-path) reconstructs the job graph from real run timings to find the long pole, then classifies which of three shapes it is.
- [Split long jobs into parallel work](/github-actions/optimizations/split-long-running-jobs) is about the slow or high-variance step, and the post-step cleanup most people do not know is counted in their job duration.
- [Speed up fork pull request builds](/github-actions/optimizations/speed-up-fork-pr-builds) covers the cold build a fork PR pays because it cannot write your cache, and the trusted-producer split that warms it without weakening fork isolation.

## What is on each page

Every recipe has the same shape, so you can check one against your own repo in a couple of minutes:

- **How to detect it.** Concrete steps, usually a `grep`, a `jq` over a config file, or a specific thing to look for in a run. Not "review your workflows".
- **A bad and a good example.** Both runnable workflow YAML, not fragments, differing in exactly the thing the recipe teaches.
- **The tradeoffs.** When the fix is wrong, what it can break, and when the anti-pattern is actually deliberate. `max-parallel: 1` is sometimes a real constraint, not an oversight.
- **How to verify it worked.** The command or the run view that shows the change landed, so the fix is not just applied but confirmed.
- **A copy-paste agent prompt.** Hand it to your coding agent and it runs that audit against your repo and opens a reviewable PR.
- **A stable `ci.*` rule id**, and whether the rule is decidable from your files or needs your run history.
- **A markdown mirror**, at the same path plus `.md`, so an agent reads the page without executing JavaScript.

## How the skills, the recipes, and the guides fit together

These pages are not a separate body of advice. They are the same knowledge our skills already run on, published so you can read it.

The skills act on your repo. [ci-speedup](/ci-speedup) reads your own runs and finds what is actually costing you wall-clock. [ci-secure](/ci-secure) checks the ten critical attack vectors an outsider can reach. [ci-score](/ci-score) grades your configuration against pass-or-fail checks. All three are open source and install in one command from [the skills page](/skills).

The join is in the catalog, not just in the prose. Every row carries `patternIds`, the internal detector ids the skills match on, so a rule here and the detector that finds it are the same thing named twice rather than two descriptions of one problem. A couple of detectors back two rules each, because the same check matters in two different contexts. Of the 34 rules, 28 rest on detectors from ci-speedup and 6 on detectors from ci-secure. The Turborepo row above lists eight of them, which is why that page covers `TURBO_FORCE`, a read-only remote cache and missing `outputs` in one place: one rule, eight things the skill looks for.

The ids match too. Ten of the ids in this catalog are ones [ci-score](/ci-score) already publishes, adopted exactly as they were rather than minting a second scheme beside them, so `ci.security.pinned-action-shas` names the same rule whether you meet it in a CI Score result or in the catalog.

And the two page families split by what you need at the time. The [best practices guides](/best-practices/github-actions) are the reference layer: what good CI looks like, one page per practice, which is what you want when you are deciding whether to accept a fix an agent proposed or explaining to a teammate why the concurrency group is scoped that way. The [optimization recipes](/github-actions/optimizations) are the implementation layer: what to change when yours does not match, with the YAML to change it to. Recipes link out to the related pages that apply, and the catalog covers both families, giving every rule a stable id and one canonical URL, so an agent resolves any rule straight to its page instead of crawling for it.

## Start here

Point an agent at the catalog:

```text
Read https://starsling.dev/github-actions/catalog.json and audit this repository's
GitHub Actions workflows. Pick the rules whose detectionMode your context supports,
read each rule's markdown page, run its detection steps, and open the fixes as a
reviewable pull request.
```

That works with any agent that can fetch a URL. If you would rather it ran continuously, [StarSling](https://github.com/apps/starslingdev) runs the same rules against your pipeline and opens the pull requests itself.
