checkride ships a Claude Code plugin from the package root: /checkride:check (triage a red gate to one root cause) and /checkride:qa (read the quality artifacts — mutation, dead, dupes, health). Bundled, not published separately: same npm package, same version, one install — which is what keeps a reader and the format it reads from drifting apart.
The plugin runs nothing new. No extra command, no flag, no config, no hook. Both skills read what a checkride run already wrote; the check skill additionally runs the repo's existing check script — or, in a repo with no package.json, mise exec -- checkride where its mise.toml pins checkride — so there is something current to read.
Reading the summary by hand is the procedure the AGENTS.md stanza teaches, and it works — but it is wrong in two directions at once. It is unbounded: in checkride's own repo, mutation.json is over 12 MB — opening it spends the context you need for the fix. It is credulous: every run overwrites summary.json, so an ok: true on disk may describe three of twenty-one slots from fourteen minutes ago. So each skill pairs a deterministic reader — measures artifacts instead of opening them, pins the schema, dates every file against the run that claims it — with model judgment: ranking simultaneous failures into one root cause, or deciding which surviving mutant deserves a test.
checkride's repository is its own marketplace, and its one entry installs the published npm package, so the install depends on nothing else. There is no build step — the readers ship prebuilt in the package's dist/ and import node: builtins only. And nothing about the plugin is required: the CLI, exit codes, and .check/ contract are unchanged whether it is installed or not.
Both readers are first-class CLI commands, so any agent — or you — can run them directly. What you lose is the skill: the procedure and the judgment that turn a report into a diagnosis.
The skills run checkride the way the repo's AGENTS.md stanza does: through the package manager where there's a package.json, as mise exec -- checkride where a mise.toml pins it and there's none, and as the checkride on PATH otherwise. Where checkride isn't in the repo at all, they run the copy the plugin ships, node "${CLAUDE_PLUGIN_ROOT}/dist/triage/cli.js", which renders the identical report.
In a repo with targets, both readers follow each target's artifacts into .check/<target>/: triage names each check <target>:<slot>, and qa gives each target its own row. Both take --out for a run whose artifacts live outside .check/.
What the skills deliberately do not do: fix anything (proposing the fix is the deliverable), normalize tool output (the raw file is the truth), gate (the readers report; the gate gates), or touch the baseline — when the run's own warnings say a merge clobbered checkride.baseline.json, the skill names checkride recover as the remedy, and running it stays the human's call.
checkride wires both the same way — a stop gate, an edit marker, a baseline guard, and the two reader skills; checkride agent-setup writes all of it (Cursor is included when .cursor/ exists, or with --harness cursor). The wiring is symmetric. The harnesses are not.
The Cursor gate's config entry (timeout: 900, loop_limit: null, failClosed: true) is checkride-owned — edits are restored on the next agent-setup. The supported off switch is --remove-hook gate, not waiting out a loop cap.
With "Include third-party Plugins, Skills, and other configs" enabled — and it ships enabled by default — Cursor loads hooks from .claude/settings.json too and runs all matching hooks from every source. A repo wired for both harnesses would fire two full pipelines for one turn, into one .check/. checkride resolves it inside checkride gate: when the Claude-protocol run detects a registered native Cursor gate, it stands down and lets the one gate that can speak Cursor's protocol answer. The check is deliberately narrow — it defers only when a Cursor gate is actually registered, so a stale environment variable costs a duplicate run, never a missing gate.
Cursor has a plugin system; checkride deliberately doesn't use it — a Cursor plugin can't invoke a script it shipped itself, installs per user rather than per repo, and distributes from git rather than npm. So agent-setup writes the same two skills into the repo at .cursor/skills/, from the same source files the plugin ships. Three differences follow: the names are prefixed (/checkride-check, /checkride-qa — a repo-local skill named check would shadow the bare command everywhere); they are committed and reviewed with the repo, refreshed by the next agent-setup rather than a plugin update; and they invoke checkride triage / checkride qa directly — which is exactly why those commands exist.
The third verdict is the environment's, not the code's: nothing executed, so nothing in .check/ describes the turn. The usual cause is the hook's Node; in a repo without Node, it's an untrusted mise.toml.
Agent harnesses run hooks in a non-login shell. A version manager puts its shims on PATH from a shell rc file, so the hook never sees them — it gets the machine's default node, not your terminal's. With a repo pinning engines.node and the wrong Node arriving, the package manager refuses to run anything at all — while doctor, running in your terminal on the right Node, says everything is fine.
checkride handles it in two halves. It aligns when it can: if .nvmrc or .node-version names an exact version the running Node fails, the gate prepends a matching already-installed interpreter's bin to the child's PATH (searched under nvm, fnm, nodenv, asdf, volta, n — nothing downloaded, never silently: every aligned run prints the line). It names the cause when it cannot: no matching install means a "could not run" verdict that says which Node arrived, what the pin wants, and what to do — never a misleading red.
Three practical levers: add a .nvmrc so there is an exact version to align to (engines.node is a range — a compatibility declaration, not an instruction to switch interpreters); set CHECKRIDE_NODE_BIN to a bin directory to pick the interpreter yourself (off disables alignment entirely); and run checkride doctor — its node pin row answers the one question your shell can't: if a hook arrives on a different Node, can checkride put the pinned one back?
In a repo with no package.json, the gate runs mise exec -- checkride through the mise.toml that pins checkride. mise won't read a config file it hasn't trusted, and checkride's pin sets a tool option (allow_low_downloads), so every Go or Rust scaffold and every fresh clone of one starts untrusted. The hook script tells that apart from a missing install and answers "could not run", naming the cause:
It doesn't block: the fix is a person choosing to trust a file, not an edit the agent can make, so blocking would only repeat the message — and it says plainly that nothing was verified this turn. A checkride that isn't installed at all stands down the same way. In CI, MISE_TRUSTED_CONFIG_PATHS set to the workspace trusts the repo's own file without a mise trust step. The route itself, from the pin to the first run, is on the Languages page.