CHECKRIDE
AGENTS
Home Docs Languages API GITHUB
{{ grp.title }}
{{ it.label }}
How the hooks get written in the first place is on the docs page →
THE PLUGIN

The two skills

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.

Why not just read summary.json?

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.

Install

Claude Code
> /plugin marketplace add robmclarty/checkride
> /plugin install checkride@checkride

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.

Without the plugin

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.

shell
$ pnpm exec checkride triage # what /checkride:check runs
$ pnpm exec checkride qa # what /checkride:qa runs
$ mise exec -- checkride triage # the same, in a repo without Node

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.

HARNESSES

Claude Code vs Cursor

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.

 CLAUDE CODECURSOR
{{ r.dim }} {{ r.claude }} {{ r.cursor }}

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.

Cursor also runs your Claude Code hooks

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.

The skills, without a Cursor plugin

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.

HARNESSES

When the gate says "could not run"

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.

what an aligned run prints
checkride: running the check on Node 22.22.3 from ~/.nvm/versions/node/v22.22.3/bin
(.nvmrc pins 22.22.3; this hook started on 24.9.0).

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?

An untrusted mise.toml

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:

the verdict
checkride: the gate could not run — mise has not trusted the mise.toml that pins checkride here, so it will not run it. Nothing ran: no check executed and no artifact was written, so `.check/` holds nothing from this turn. Review mise.toml, run `mise trust`, then run `mise exec -- checkride`.

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.