---
title: "CI Score: grade a repo's GitHub Actions setup"
description: "Check your GitHub Actions against best practices. CI Score is a pass/fail rubric computed from a repository's own workflow configuration."
url: https://starsling.dev/ci-score
canonicalUrl: https://starsling.dev/ci-score
---

# Improve your GitHub Actions setup with /ci-score

Install the agent skill to check your repo against best practices and enforce them.

CI Score gauges best-practice adherence on CI speed and gate hygiene: a straightforward pass/fail rubric of best-practice checks computed from a repo's workflow configuration.

Spec version: ci-score-v0.1.3.

## How it works

### 01. Read the workflow files

Every check is a configuration fact: something present or absent in the repository's own GitHub Actions YAML, and in the composite actions and build-tool config it references.

### 02. Mark each check pass, fail, or not applicable

A check whose subject does not exist in the repo (no build tool, no OIDC, no third-party actions) is not applicable and leaves the denominator entirely, and a mechanism the scan cannot see is never marked as a failure.

### 03. Divide and round

The score is checks passed divided by checks applicable, times 100, rounded half up. Nothing is computed at render time: a page prints the number the scorer stamped, or it prints nothing.

```
round_half_up(100 * checks_passed / checks_applicable) - not_applicable leaves the denominator
```

ROUND HALF UP - floor(x + 0.5). Python's round() is banker's rounding; any reimplementation must agree.

Published pages render the numeric score only. The bands stay defined in the spec and stamped beside every value, but no page prints a letter: with at most eleven applicable checks the instrument cannot separate adjacent values, and the number says that honestly where a report-card letter would not.

Calibrated against 31 public open-source repositories. Private and monorepo calibration is pending.

## The 11 checks

The score reads CONFIGURATION FACTS a maintainer can verify in their own YAML in seconds. The measured wall-clock report renders beside it and enriches check evidence; measured magnitudes never gate a score in v1. Security stays out of v1 except SHA pinning (owner-named); runner-minute efficiency is reported alongside, not scored.

- **Dependency caching** (`ci.cache.dependency-cache`): at least one workflow or local composite action configures dependency caching (a cache action - actions/cache incl. restore/save, rust-cache, buildjet - or a setup-* action's cache input). Caching housed in cross-repo reusable workflows is not visible; run the score in that repo. ([practice guide](https://starsling.dev/best-practices/github-actions/cache-dependencies))
- **Build caching** (`ci.cache.build-cache`): a build-tool cache is configured (turbo/nx/gradle/sccache/bazel remote or actions-cache-backed)
- **Shallow checkout** (`ci.checkout.shallow-clone`): no checkout step on a PR-GATING workflow sets fetch-depth: 0 (structure-walked; comments never count; post-merge automation - a pull_request trigger whose types are only [closed], e.g. backport/changelog jobs - is exempt: it needs history and gates nobody) ([practice guide](https://starsling.dev/best-practices/github-actions/shallow-checkout))
- **Test sharding / matrix** (`ci.parallel.test-sharding`): a test-like job (test/spec/e2e/integration/unit in its id or name) runs a matrix, or any job's matrix axis is shard-like (shard/chunk/split/partition) ([practice guide](https://starsling.dev/best-practices/github-actions/shard-tests))
- **Change-scoped builds** (`ci.build.change-scoped`): CI scopes work to what changed: --filter=...[base], nx affected, --changed/--onlyChanged/--affected run modes, turbo-ignore, or a changed-files step (dorny/paths-filter, tj-actions/changed-files) ([practice guide](https://starsling.dev/best-practices/github-actions/build-only-affected))
- **Concurrency groups** (`ci.trigger.concurrency-groups`): at least one PR-triggered workflow declares a concurrency group (the config lever against self-stampede queue time) ([practice guide](https://starsling.dev/best-practices/github-actions/cut-queue-time))
- **Superseded runs cancelled** (`ci.trigger.cancel-superseded`): at least one PR-triggered workflow sets concurrency with cancel-in-progress (true or a templated expression - conditional cancellation is the practice too; an explicit false is not) ([practice guide](https://starsling.dev/best-practices/github-actions/cancel-superseded-runs))
- **Path filters** (`ci.trigger.path-filter`): at least one PR-triggered workflow scopes itself with paths / paths-ignore ([practice guide](https://starsling.dev/best-practices/github-actions/path-filter-workflows))
- **Job timeouts** (`ci.hygiene.job-timeouts`): workflow jobs set timeout-minutes (any usage; GitHub's default is 360 minutes)
- **Scoped OIDC id-token** (`ci.security.scoped-id-token`): id-token: write is granted at job level, never workflow-wide (an explicit grant or a workflow-level permissions: write-all both count as workflow-wide) ([practice guide](https://starsling.dev/best-practices/github-actions/scope-id-token-per-job))
- **Pinned action SHAs** (`ci.security.pinned-action-shas`): at least 95% of remote action references are pinned to a full commit SHA, across workflow files AND local composite actions (first-party actions/* included - the pinning practice does not exempt them; the evidence always shows pinned-of-total). The threshold asks 'is pinning an adopted, automated practice?': >=95% admits Renovate-style stragglers (at most 1 in 20) while rejecting partial adoption, and is gaming-resistant - reaching it from a low base means actually pinning the fleet (OD-CS17) ([practice guide](https://starsling.dev/best-practices/github-actions/pin-action-shas))

## Scored repositories

21 repositories scored with ci-score-v0.1.3. Latest collection July 29, 2026. 10 are listed below, one per distinct score.

Scores are passed checks over applicable checks, so the scale has fixed steps: a repository with 9 applicable checks can only land on 0, 11, 22, 33, 44, 56, 67, 78, 89, 100. The board shows one repository per step.

This grade measures configuration adherence to CI best practices. It is not a speed measurement and does not predict how fast this repo's CI runs.

01. [vitejs/vite](https://github.com/vitejs/vite): 100/100, 9 of 9 applicable checks
02. [immich-app/immich](https://github.com/immich-app/immich): 89/100, 8 of 9 applicable checks
03. [vercel/next.js](https://github.com/vercel/next.js): 82/100, 9 of 11 applicable checks
04. [react/react](https://github.com/react/react): 78/100, 7 of 9 applicable checks
05. [calcom/cal.diy](https://github.com/calcom/cal.diy): 73/100, 8 of 11 applicable checks
06. [fastapi/fastapi](https://github.com/fastapi/fastapi): 67/100, 6 of 9 applicable checks
07. [usememos/memos](https://github.com/usememos/memos): 63/100, 5 of 8 applicable checks
08. [psf/requests](https://github.com/psf/requests): 56/100, 5 of 9 applicable checks
09. [discordjs/discord.js](https://github.com/discordjs/discord.js): 45/100, 5 of 11 applicable checks
10. [coollabsio/coolify](https://github.com/coollabsio/coolify): 22/100, 2 of 9 applicable checks

## Score your own repo

Run ci-score against a local checkout. It writes a report with your score, every check and the evidence behind it, and a ranked fix for each gap with a prompt for your coding agent.

```sh
npx skills add starslingdev/skills
```

Then run `/ci-score` in your checkout. It writes ci-score-report.md to the repository. No network, no telemetry.

Wall-clock is a different question, and deliberately not part of this grade. See [ci-speedup](https://starsling.dev/ci-speedup.md).

## More

- [Methodology, the published formula](https://starsling.dev/ci-score/methodology.md)
- [The scoring spec as JSON](https://starsling.dev/ci-score/spec.json)
- See an error? Email founders@starsling.dev
