checkride's slots — types, lint, dead, test and the rest — are roles, not tools. A pack fills them for one language: its adapters, where each tool is pinned, and the environment its checks run with. Seven ship: five languages beside TypeScript, and repo, which checks the repo as a whole.
In each card, a slot's first tool is its blessed default: the one detection prefers and init scaffolds. The others are alternates, which run when their own config file is the one the repo holds. A slot marked opt-in stays out of the default run until the config names it or --include does.
Grey tools are opt-in. publish is the ts pack's six-slot publish-ready bundle. terraform validate runs only where a .terraform.lock.hcl marks a root module, with terraform or tofu as the version file names. A bash target has no marker file: its checks run over its shell files, every .sh and .bash file and each extensionless file with a shell shebang, narrowed with include and exclude globs. Every adapter's argv is in Packs and pins.
A repo whose root marks one language is checked as one project of that pack, with no checkride.config.json at all. The first match wins:
A Go, Rust, Python or Terraform root runs its pack's checks under their bare slot names (types, lint, test), from the root, into .check/, with that pack's tools, pin chain and environment, and a slot the pack doesn't fill sits out. A repo with code in more than one language, or one language in more than one place, declares targets.
checkride never reshapes what a tool prints. golangci-lint's JSON, go test's JSON lines and shellcheck's json1 land in .check/ as the tools wrote them, and the agent reads them that way. A reader that wants findings by file and line imports checkride/findings, whose parsers cover 23 adapters across the packs. The baseline keys with the same parsers, so eslint, biome, golangci-lint, staticcheck, clippy, shellcheck, ruff and tflint findings can be grandfathered like oxlint's.
A repo with more than one language, or one language in more than one place, declares targets: named parts of the repo, each checked from its own root by the pack that knows its language.
Each target runs the slots its pack fills, once, from its root, named <target>:<slot>. The repo pack's checks (struct, docs, links, spell, prose and unclaimed) still run once for the whole repo, under their bare names. Each line ends with why its check runs:
checkride target add declares one, adopting the tools its root already configures. In a repo with no targets yet, the project at the root becomes a target too, named app, when the root holds one. init proposes the targets a repo's files suggest and writes them, and --discover, on a run or on doctor, shows the proposal without writing anything.
With targets, the unclaimed check fails when a toolchain marker (a package.json, tsconfig.json, go.mod, go.work, Cargo.toml, pyproject.toml, setup.cfg, setup.py, .terraform.lock.hcl or a .tf file) sits outside every target of its language. .check/unclaimed.json names the target that would claim it, so a new service can't land unchecked: the fix is one target add, or a glob in unclaimed.ignore for a marker that holds no code to check.
A check never downloads a tool, and never picks one up by accident. Each pack resolves a tool through its pin chain: the repo's own pin file first, then a mise.toml, then PATH. The first source that declares the tool runs it, and when that copy can't run, the check fails naming the source rather than reach for another.
Every check records the source that ran its tool as pin_source, the version when the source names one as tool_version, and the exact argv it spawned, so two runs that disagree can say why. checkride doctor shows the same per check, and lists each tool nothing pins with the mise use command that would.
Set "requirePins": true in checkride.config.json and a tool only PATH provides fails its check instead of running whatever the machine holds:
A toolchain is the exception: go, cargo and the Terraform or OpenTofu CLI still run from PATH, since the repo's own files set the version each accepts and the pack's environment stops it fetching another.
Each pack's checks spawn with the variables that switch its downloads off, so a missing dependency fails the check rather than fetch, and the line under the red check names the step to run first:
checkride runs on Node, but a Go, Rust, Python or Terraform repo doesn't have to carry one. mise installs checkride and a Node for it beside the repo's other tools, and runs it as mise exec -- checkride.
mise trust comes first. checkride's entry sets a tool option, allow_low_downloads, which mise only loads from a file it trusts, and which it needs before it installs an npm package with fewer than 1000 weekly downloads. init then adopts the repo through the pack its root marks, and writes the AGENTS.md stanza and the hooks, both spelled mise exec -- checkride. With no package.json, there's no check script to write.
init --lang go or --lang rust scaffolds one with no package.json: a mise.toml that pins checkride and every tool its checks run, a config with one target that names every slot and sets requirePins, the pack's dialect rules under rules/go/ or rules/rust/, and code that's green once its setup steps have run. The shapes and the steps are on the Getting started page.
A repo whose languages all live in subdirectories has no go.mod, Cargo.toml, pyproject.toml or .tf file of its own at the root, and a mise.toml isn't a marker. init still reads it as an existing repo, from the projects discovery finds below the root, and declares a target for each, so the same five commands adopt it. mise exec -- checkride doctor --discover shows what it will propose before anything is written.
Where a root mise.toml pins npm:checkride and there's no package.json, the stop gate, the stanza and the plugin skills run mise exec -- checkride; with no pin they run the checkride on PATH, which doctor reports as the harness unpinned. A fresh clone's mise.toml starts untrusted, and the gate stands down naming mise trust rather than blocking; the agents page shows the verdict. In CI, jdx/mise-action installs every pin, and MISE_TRUSTED_CONFIG_PATHS set to the workspace trusts the repo's own file.