Skip to main content
Status: partly live. .github/workflows/ci.yml runs the Quality Gate job (toolchain from mise, actions pinned by SHA, contents: read), release.yml runs the release job and linear-sync.yml reconciles Linear. Not built: a workflow linter, a pin test, changeset status in the gate, and any deploy (the Deploy Staging job is a no-op until the deploy questions DP1–DP7 are decided). The code host is GitHub (ADR-0012).

The model

  • One required check, named Quality Gate, runs pnpm verify (0119). Branch protection on main requires it and nothing else.
  • Logic lives in pnpm scripts, not in workflow YAML. The workflow installs the toolchain and calls a script. A step that exists only in YAML cannot be reproduced locally, and moving to another CI host stays cheap.
  • Local and remote run the same command. If pnpm verify is green on a clean clone, the check is green. The exceptions are listed under What green does not prove.
  • Verify needs zero secrets. A pull request from a fork has no vault access, and a verify stage that needs one cannot check a fork’s work at all.

What pnpm verify runs

pnpm verify:affected runs the turbo part for members changed since main only. Use it while iterating; the required check runs the full pnpm verify. Checks CI adds beyond pnpm verify:

Run it locally

To reproduce one failing step, run exactly that script: pnpm turbo run test --filter=<package-name>, pnpm validate, pnpm knip. The workflow’s job log prints the command it ran; copy that line. Nothing in pnpm verify reads a vault, a .env or a token. If a step seems to need a credential, that is a bug in the step (see Secrets).

Rules for the workflow file

1

Toolchain from mise.toml

One setup step installs what mise.toml pins, asserts that the pnpm pin equals packageManager in package.json, caches the pnpm store and runs pnpm install --frozen-lockfile. It installs tools and decides nothing.
2

Pin every action by SHA

Each uses: carries a full commit SHA and a semver comment. Renovate keeps them current. Add a workflow lint (zizmor or equivalent) to catch unpinned actions and injection patterns.
3

Least privilege

Top-level permissions are empty or contents: read. A job asks for more only when it must, and id-token: write only on a job that logs in to the vault.
4

No secrets in verify

The verify job has no vault identity and no stored secret. Only deploy and release jobs may log in, through OIDC (Secrets).
5

Scope with affected, never skip

A pull request may use --affected for turbo steps. main and release tags run everything. pnpm validate, pnpm boundaries and pnpm knip never scope: they check repository-wide facts.

A check that cannot run is a failure

A step that is skipped because its tool is missing, its secret is absent or its input is empty must fail in CI, never pass. A local run may report a skip, with the reason; the CI run may not. Do not resolve a red gate by weakening it. Fix the thing the failure names, or change the gate through its own record (an ADR or the standard that created it), never in the same commit as the code that was failing it.

What green does not prove

  • No service is started. pnpm verify builds and tests; it does not run a deployed system.
  • The runner image is only proven for the jobs that use it. A tool present on your machine and absent in CI fails in CI only. Keep tools in mise.toml.
  • The vault and deploy paths are unexercised by verify. The first real deploy is the first time that code runs.
Say what you ran when you report. A green verify shows the repository conforms to its checks; it does not show the change is correct.

When CI is down

Use this only when the host cannot run workflows at all (billing lock, outage, startup failure). It is a break-glass path for gating, not a way to skip the gate.
1

Gate the exact commit locally

From a clean worktree of the commit, never a checkout with local edits: git fetch origin && git worktree add ../gate-<sha7> <sha>, then cd into it and run pnpm install --frozen-lockfile && pnpm verify && pnpm secrets:scan. Record the full SHA and the result. Do not ship a commit whose gate you did not see pass.
2

Ship it by hand, as the owner

Follow Deploys: the owner runs the deploy command for that commit, and a production deploy needs the owner’s explicit approval. Never use --no-verify or skip a step to save time.
3

Smoke test and record

Run the deploy’s smoke test. Append a row to the table below in the same pull request or a docs follow-up: date, environment, full SHA, who deployed, gate result, smoke result, reason.
4

Reconcile

When CI is back, push one normal commit to main so the pipeline’s view matches what is running.

Enforcement status

See also Definition of done, Release and Testing.