Explicit Skips
When a capability is intentionally unavailable, a command says so with a skip: line and succeeds, while a check that actually fails still exits non-zero.
- Kind
- Contract
- Domain
- Environment & Tooling
- Applies at
scripts/- Status
- Stable
- Source · managed
scripts/README.md
01 Defines
The output and exit-code contract for commands that meet an absent capability: print a single skip: <reason> line, exit zero, and reserve non-zero exits for checks that ran and failed.
02 Applies when
- A command covers a capability that may legitimately be absent — an uninstalled linter, a component the repository does not have, a step that needs the network.
- The same command has to work in a bare checkout, in the dev container, and in CI.
- A reader of the log needs to tell “did not run” apart from “ran and passed”.
03 Boundaries
- A skip is never a way to hide a failing check; once a check runs, its failure is reported as a failure.
- Diagnosing a missing tool is the job of
make doctor, which reportsmissing:and exits non-zero. - Skips cover intentional unavailability, not misconfiguration of something the repository declares it needs.
Expected behaviour
- 01Every skipped step prints one line that starts with
skip:and names what was not done and why. - 02A skip exits zero and lets the surrounding command carry on.
- 03The reason is specific enough to act on, such as
prettier is not installed (run npm ci). - 04A check that runs and fails makes the overall command exit non-zero, even if other steps were skipped.
- 05Skipped and failed steps are never reported with the same word.
Behaviour#
Repository commands are written to run wherever the repository is checked out. Some of what they cover is optional: a linter that is only installed in the dev container, a link checker the repository may not have, Terraform roots that may not exist yet, a plugin download that needs the network. Rather than fail on the first absent piece, or silently do nothing, each step announces that it did not run.
The prefix is the contract. Anything that reads the log — a person, a CI
summary, a grep — can find every step that was not exercised by looking for
skip: at the start of a line, and can trust that the exit status still
reflects every check that did run.
Three outcomes, three words#
| Outcome | Output | Exit | Where it appears |
|---|---|---|---|
| Capability intentionally absent | skip: shellcheck is not installed | 0 | scripts/lint.sh, make modules |
| Required tool or file absent | missing: tflint | non-zero | make doctor, make format |
| Check ran and failed | The tool’s own output, then Lint FAILED | non-zero | Any gate or validation target |
missing: belongs to diagnostics and to commands that cannot do their job at
all. make format exists to rewrite files, so without Prettier it prints
missing: root npm dependencies; rebuild the devcontainer or run npm ci and
exits 1. The lint gate, by contrast, treats an absent Prettier as a skip,
because a gate in a bare checkout should still run every other linter.
Where skips come from#
- Tools not installed. The shared
runhelper inscripts/lint.shandscripts/security-scan.shcheckscommand -vfirst. - Components not present. Hugo and Terraform targets skip when none of the
declared directories exist:
skip: no Hugo sites (directories not found). - Optional configuration not set. An empty
HUGO_PREPARE_COMMAND, a missingscripts/check-links.mjs, or noLINT_COMMANDeach print their own line, such asskip: no link checker configured. - No input to work on. The docs stager prints
skip: no docs/ directory; the Node restore hook printsskip: no root package.json. - Network deliberately off. Under Offline Validation , plugin and provider downloads skip and validation continues with what is cached.
Examples#
The lint gate’s runner is the whole pattern in a few lines:
run() {
local name="$1"
shift
if ! command -v "$1" > /dev/null 2>&1; then
printf 'skip: %s is not installed\n' "$1"
return 0
fi
printf '\n--- %s ---\n' "$name"
"$@" || failed=1
}
The same shape appears outside shell scripts. The documentation stager exits cleanly when there is nothing to stage:
try {
readdirSync(DOCS_DIR);
} catch {
console.log("skip: no docs/ directory");
process.exit(0);
}
Connections
How Explicit Skips relates to the library
Relied on by
- Lint Gate One aggregate lint command runs every linter that is installed, skips the rest with a clear …
- Offline Validation Networked setup hydrates the workspace once; validation then runs with OFFLINE=1 and never …
- Scripts Narrow, deterministic command helpers for make modules, CI, and local tooling — glue that takes …
- Staged Content Mounts Repository documentation is staged from docs/ into .cache/docs-site/ and mounted into a Hugo …