Day to day with hushrun
Updated
This page is for you if you work in a repository that already uses hushrun. You run checks as before, and hushrun skips what this exact code already passed.
Before you begin
Sign in once on your machine, so that your passes count for teammates and CI, and theirs for you:
npx hushrun loginUntil you do, checks run as usual, and nothing is shared.
Run checks as usual
Run your checks the way the repository always has, such as npm test or pnpm lint. Each script starts hushrun run, which skips the check when this exact code already passed it on your machine, in CI or in an agent's sandbox, and runs it otherwise.
- A skip exits 0, like a pass.
- A failure keeps the check's own exit code and output.
- hushrun's own lines go to stderr and start with
hushrun:. Everything else is the check's. - Change a file and the checks run again. Go back to files that passed and they skip.
Note: With Bun, run
bun run test, notbun test.bun testis Bun's own runner, which never readspackage.json, so hushrun never sees it.
A partial run, such as npm test -- src/cart.test.ts, is a separate check, and never stands in for the full run.
Read the lines
The lines you'll see most:
| Line | What it means |
|---|---|
test running: no pass yet for this exact code | Nothing passed on these files yet, so the check runs |
test passed in 41s; it won't run again on this code | It passed, and the pass is recorded |
test skipped: this exact code passed in CI 3 min ago (saves 41s) | A pass was found. The command didn't run, and hushrun exited 0 |
test failed in 2s (exit 1) | It failed. The failure is recorded, and the exit code kept |
Every other line, and what to do about it, is in Lines hushrun prints.
Name a check
A check's name labels its history and is part of what hushrun compares. You rarely set one:
- Run through a
package.jsonscript, a check takes the script's name.test:unitbecomestest-unit. - Anywhere else, it takes the program's name.
hushrun run -- vitest runisvitest.
Give a variant its own name when a check depends on something outside the files, such as an environment variable or the Node.js version. hushrun doesn't compare those, so a pass under Node.js 22 would skip a run under 24. Put the name before --:
npx hushrun run unit-node22 -- vitest runName the Node.js 24 run unit-node24, and each skips only on its own pass. Where a check counts is another guard, and Commands has the naming rules.
Run a check again
--force runs a check even after a pass, and records the new result. Use it to see a skipped check's output, such as a coverage report:
npx hushrun run --force test -- vitest runGive the check's name and exact command, from the same directory, so the result counts for the same check.
Before you push
Commit, then ask what CI would still run on this exact code:
$ npx hushrun ready a0b1c2d isn't ready: CI would run 2 checks again. missing test no pass yet for this exact code missing lint it failed on this exact code last time passed types Run these here, then push: npm run test npm run lint
Run the commands it prints. Once it says a0b1c2d is ready to push., push, and CI skips what passed. A check marked ci is one nothing on this machine can settle, such as one needing a secret, so CI runs it.
The pre-push hook asks the same question when you push. For your push, it warns and goes on:
hushrun: CI will run test on a0b1c2d. To skip that, run these before you push: npm run test
It refuses a coding agent's push instead, until those checks pass (For coding agents). It tells an agent from a person by what the agent sets in its environment: Who is an agent.
Flaky checks
A failure is recorded too, and the newest result decides the next run. So a check that last failed always runs again.
A check that passed and failed on the same code is flaky, and the dashboard marks it flaky. If you doubt a skip, run the check again.
Keep generated files ignored
hushrun looks at the files again when the command ends. If they changed, the result belongs to neither version of the code, so it isn't recorded:
hushrun: test passed in 12s, but files changed while it ran, so it wasn't recorded. If test writes files, list them in .gitignoreList what your checks write, such as coverage/ or build output, in .gitignore.
When a check keeps running
Most often a file changes on every run, or the command differs: npm test -- --coverage is not npm test. Troubleshooting has every cause and its fix.
Next
- How it works: what hushrun compares, and when a skip can be wrong.
- For coding agents: the rules your agents follow.
- Several projects: checks per project in one repository.
- Lines hushrun prints: every line, and what to do.