plumbbob 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -13
- package/dist/cli-core.js +19 -2
- package/dist/lib/check.js +46 -13
- package/dist/lib/intent.js +1 -1
- package/dist/lib/latch.js +3 -3
- package/dist/lib/reentry.js +70 -0
- package/dist/verbs/agent.js +2 -2
- package/dist/verbs/doctor.js +4 -4
- package/dist/verbs/finish.js +1 -1
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +2 -2
- package/dist/verbs/spike.js +8 -2
- package/hooks/post-edit.sh +4 -4
- package/hooks/pre-bash-commit.sh +3 -3
- package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
- package/node_modules/checkride/CHANGELOG.md +1003 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +171 -30
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
- package/node_modules/checkride/dist/adapters.d.ts +18 -6
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +42 -23
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
- package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
- package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.js +77 -0
- package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +61 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +189 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.js +80 -0
- package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.js +84 -0
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.js +9 -4
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +58 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +299 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
- package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.js +45 -0
- package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.js +56 -0
- package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
- package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.js +19 -0
- package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.js +47 -0
- package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.js +91 -0
- package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.js +108 -0
- package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +16 -1
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
- package/node_modules/checkride/dist/atomic.js +25 -3
- package/node_modules/checkride/dist/atomic.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
- package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +161 -58
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +7 -7
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +16 -19
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/index.d.ts +4 -4
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +4 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +12 -4
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +16 -9
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
- package/node_modules/checkride/dist/baseline-command.js +7 -7
- package/node_modules/checkride/dist/baseline-command.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +17 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +259 -18
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +84 -8
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +122 -18
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
- package/node_modules/checkride/dist/digest/digest.js +5 -5
- package/node_modules/checkride/dist/digest/index.d.ts +2 -2
- package/node_modules/checkride/dist/digest/index.js +2 -2
- package/node_modules/checkride/dist/doctor.d.ts +25 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +193 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +135 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +490 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +72 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +324 -81
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/json.d.ts +21 -0
- package/node_modules/checkride/dist/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/json.js +21 -0
- package/node_modules/checkride/dist/json.js.map +1 -0
- package/node_modules/checkride/dist/links.d.ts +31 -5
- package/node_modules/checkride/dist/links.d.ts.map +1 -1
- package/node_modules/checkride/dist/links.js +141 -32
- package/node_modules/checkride/dist/links.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +129 -0
- package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
- package/node_modules/checkride/dist/node-pin.js +211 -0
- package/node_modules/checkride/dist/node-pin.js.map +1 -0
- package/node_modules/checkride/dist/orchestrator.d.ts +40 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +196 -102
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pack.d.ts +8 -9
- package/node_modules/checkride/dist/pack.d.ts.map +1 -1
- package/node_modules/checkride/dist/pack.js +53 -17
- package/node_modules/checkride/dist/pack.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/detect.js +6 -1
- package/node_modules/checkride/dist/pm/detect.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +6 -2
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js +6 -2
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +51 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +92 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -0
- package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
- package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/tools.js +124 -0
- package/node_modules/checkride/dist/pm/tools.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +78 -18
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/proc.d.ts +30 -0
- package/node_modules/checkride/dist/proc.d.ts.map +1 -0
- package/node_modules/checkride/dist/proc.js +42 -0
- package/node_modules/checkride/dist/proc.js.map +1 -0
- package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
- package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/cli.js +24 -0
- package/node_modules/checkride/dist/qa/cli.js.map +1 -0
- package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
- package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dead.js +88 -0
- package/node_modules/checkride/dist/qa/dead.js.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.js +62 -0
- package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
- package/node_modules/checkride/dist/qa/health.d.ts +85 -0
- package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/health.js +109 -0
- package/node_modules/checkride/dist/qa/health.js.map +1 -0
- package/node_modules/checkride/dist/qa/index.d.ts +23 -0
- package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/index.js +22 -0
- package/node_modules/checkride/dist/qa/index.js.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.js +195 -0
- package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
- package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
- package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/qa.js +158 -0
- package/node_modules/checkride/dist/qa/qa.js.map +1 -0
- package/node_modules/checkride/dist/qa/read.d.ts +40 -0
- package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/read.js +47 -0
- package/node_modules/checkride/dist/qa/read.js.map +1 -0
- package/node_modules/checkride/dist/qa/render.d.ts +26 -0
- package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/render.js +310 -0
- package/node_modules/checkride/dist/qa/render.js.map +1 -0
- package/node_modules/checkride/dist/security.d.ts +45 -0
- package/node_modules/checkride/dist/security.d.ts.map +1 -0
- package/node_modules/checkride/dist/security.js +86 -0
- package/node_modules/checkride/dist/security.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +5 -5
- package/node_modules/checkride/dist/smoke.js +5 -5
- package/node_modules/checkride/dist/snippets.d.ts +26 -29
- package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
- package/node_modules/checkride/dist/snippets.js +26 -30
- package/node_modules/checkride/dist/snippets.js.map +1 -1
- package/node_modules/checkride/dist/tool-json.d.ts +33 -0
- package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
- package/node_modules/checkride/dist/tool-json.js +58 -0
- package/node_modules/checkride/dist/tool-json.js.map +1 -0
- package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
- package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/cli.js +26 -0
- package/node_modules/checkride/dist/triage/cli.js.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
- package/node_modules/checkride/dist/triage/env.d.ts +54 -0
- package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/env.js +81 -0
- package/node_modules/checkride/dist/triage/env.js.map +1 -0
- package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
- package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/gate.js +121 -0
- package/node_modules/checkride/dist/triage/gate.js.map +1 -0
- package/node_modules/checkride/dist/triage/index.d.ts +22 -0
- package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/index.js +20 -0
- package/node_modules/checkride/dist/triage/index.js.map +1 -0
- package/node_modules/checkride/dist/triage/render.d.ts +19 -0
- package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/render.js +419 -0
- package/node_modules/checkride/dist/triage/render.js.map +1 -0
- package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
- package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/triage.js +131 -0
- package/node_modules/checkride/dist/triage/triage.js.map +1 -0
- package/node_modules/checkride/package.json +17 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +62 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +257 -0
- package/node_modules/checkride/skills/qa/SKILL.md +231 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
- package/package.json +2 -2
- package/skills/build/SKILL.md +23 -19
- package/skills/finish/SKILL.md +3 -1
- package/skills/harvest/SKILL.md +2 -1
- package/skills/plan/SKILL.md +8 -7
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +5 -4
- package/skills/spike/SKILL.md +1 -1
- package/skills/step/SKILL.md +4 -4
- package/skills/verify/SKILL.md +19 -14
- package/templates/intent.md +8 -7
|
@@ -4,6 +4,994 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.10.2] - 2026-07-31
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **A gate profile, for repos where the full pipeline is too slow to run every
|
|
12
|
+
turn.** `checkride.config.json` gains a `gate` key — `only`, `skip`, `changed`
|
|
13
|
+
— that applies to the stop-gate run and nothing else:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{ "gate": { "only": ["types", "lint", "struct"], "changed": true } }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The flags are appended after the check script's own, so the profile wins over
|
|
20
|
+
anything the script already carried. The intended shape is a fast gate per
|
|
21
|
+
turn with the full check still binding at commit or in CI; the gate is not the
|
|
22
|
+
only place "done" can be enforced, and in a large repo it should not be the
|
|
23
|
+
expensive one.
|
|
24
|
+
|
|
25
|
+
**Every verdict a profiled gate emits says it was a profile**, in those words:
|
|
26
|
+
`checkride ✔ green in 4.1s — gate profile: only types, lint — NOT the full
|
|
27
|
+
check`. That is the price of the feature and it is deliberately not
|
|
28
|
+
configurable. A gate that runs three of eighteen slots and reports a bare
|
|
29
|
+
green has told the reader the work is done, which is the one thing it does not
|
|
30
|
+
know — the vacuous pass this tool exists to prevent, reached from the
|
|
31
|
+
comfortable direction rather than the alarming one. A narrowed red points past
|
|
32
|
+
itself for the same reason. A profile that narrows nothing is treated as no
|
|
33
|
+
profile, so the warning never appears on a run that was in fact complete.
|
|
34
|
+
|
|
35
|
+
- **checkride aligns the gate to the repo's Node pin.** Before running the check
|
|
36
|
+
script, if the repo names an exact interpreter (`.nvmrc` or `.node-version`)
|
|
37
|
+
and the running Node does not satisfy it, checkride puts a matching installed
|
|
38
|
+
Node in front of the child's `PATH` — so a hook that arrived on the wrong Node
|
|
39
|
+
runs the pipeline on the right one instead of failing to run it at all.
|
|
40
|
+
|
|
41
|
+
Four rules bound it, because choosing the interpreter a repo's whole pipeline
|
|
42
|
+
runs on is not a small thing to do silently: only on an explicit pin file
|
|
43
|
+
(`engines.node` is a *range* — a compatibility declaration, not an instruction
|
|
44
|
+
to switch interpreters — and is read for diagnosis only); only when the running
|
|
45
|
+
Node does not already satisfy it; only an interpreter already installed under a
|
|
46
|
+
known layout (nvm, fnm, nodenv, asdf, volta, n — nothing is downloaded and no
|
|
47
|
+
version manager is invoked, since under a non-login shell `nvm` is a shell
|
|
48
|
+
function and not a binary); and never without a line on stderr saying what it
|
|
49
|
+
aligned to and why.
|
|
50
|
+
|
|
51
|
+
`CHECKRIDE_NODE_BIN` is the escape hatch in both directions: a directory to use
|
|
52
|
+
verbatim — the documented wrapping point for a layout checkride does not know —
|
|
53
|
+
or `off` to disable alignment entirely. When alignment cannot help, nothing
|
|
54
|
+
changes and the refusal above names the pin, the Node that ran, and the lever.
|
|
55
|
+
|
|
56
|
+
- **`doctor` reports the Node a hook would get, not just this shell's.** Every
|
|
57
|
+
other row describes the terminal `doctor` was typed into, which is how it said
|
|
58
|
+
`environment ok` about a repo whose gate could not start — the bug was
|
|
59
|
+
invisible to every read-only command checkride had. The new `node pin` row asks
|
|
60
|
+
the question that *is* answerable from any shell: given the repo's pin, could
|
|
61
|
+
checkride put the right Node back if a hook arrived with the wrong one? It is
|
|
62
|
+
never required, so it flags a risk without failing a repo that works.
|
|
63
|
+
|
|
64
|
+
- **`checkride hooks add <a,b>` / `checkride hooks remove <a,b>` — hook
|
|
65
|
+
management that never touches AGENTS.md.** `agent-setup` writes four things at
|
|
66
|
+
once (the `check` alias, the stanza, the hooks, the Cursor skills) with the
|
|
67
|
+
stanza guard in front of all of them, so managing a hook inherited the stanza's
|
|
68
|
+
problems: a repo whose AGENTS.md had been edited could not remove a hook *at
|
|
69
|
+
all* — the run refused and, by contract, wrote nothing — and a repo mid-upgrade
|
|
70
|
+
had its stanza rewritten by a command it had asked to touch hooks.
|
|
71
|
+
|
|
72
|
+
The new command writes only the harness config entries and the generated
|
|
73
|
+
scripts. It never reads or writes AGENTS.md, so no state of that file can block
|
|
74
|
+
it and no run of it can change that file. `--harness <a,b>` and `--dry-run`
|
|
75
|
+
both apply, and both directions are idempotent. `add` defaults to every hook;
|
|
76
|
+
`remove` requires an explicit list, because a bare command that tore out the
|
|
77
|
+
gate would be the silent un-gating checkride exists to prevent.
|
|
78
|
+
|
|
79
|
+
`agent-setup` is unchanged, flags and refusal included.
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- **checkride generates one file for Claude Code, not four.** `protect` is now a
|
|
84
|
+
pair of `permissions.deny` rules and `dirty` is an inline command, so
|
|
85
|
+
`.claude/hooks/checkride-protect.cjs` and `.claude/hooks/checkride-dirty.sh`
|
|
86
|
+
are gone. Both are deleted automatically on the next `agent-setup`, `init`, or
|
|
87
|
+
`hooks add`.
|
|
88
|
+
|
|
89
|
+
`protect` gains more than tidiness from the move. Claude Code evaluates a deny
|
|
90
|
+
rule **regardless of what a `PreToolUse` hook returns**, so the same paths are
|
|
91
|
+
now guarded a layer below the hook that used to guard them — and at no cost
|
|
92
|
+
per tool call, where the script spawned Node on every single edit while
|
|
93
|
+
allowing essentially all of them. The rules are also a list checkride
|
|
94
|
+
*appends* to and removes only its own entries from, so a repo can add its own
|
|
95
|
+
suppression files beside them (a `fallow.baseline`, a lint baseline) and keep
|
|
96
|
+
them across every refresh. The generated script could never offer that: it was
|
|
97
|
+
checkride-owned and overwritten wholesale on every run.
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
"permissions": {
|
|
101
|
+
"deny": ["Edit(**/checkride.baseline.json)", "Edit(**/.check/**)"]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`Edit(...)` is the only correct spelling, and getting it wrong fails silently:
|
|
106
|
+
Claude Code checks file paths against `Edit` and `Read` rules only, and a
|
|
107
|
+
`Write(...)` or `NotebookEdit(...)` path rule is accepted, never consulted, and
|
|
108
|
+
warns at startup. An `Edit` rule covers every file-editing tool. `Read` deny
|
|
109
|
+
rules stay deliberately absent — triage reads `.check/` artifacts.
|
|
110
|
+
|
|
111
|
+
Nothing moves in the user-facing surface: `--hook protect`,
|
|
112
|
+
`--remove-hook protect`, and `checkride hooks add|remove protect` all mean what
|
|
113
|
+
they meant. **Cursor is unchanged** and still gets all three scripts; its
|
|
114
|
+
config is hooks and nothing else, with no documented file-path deny list to
|
|
115
|
+
move `protect` into.
|
|
116
|
+
|
|
117
|
+
- **`checkride hooks` counts files changed, not files deleted.** Removing a hook
|
|
118
|
+
a harness expresses as configuration rewrites the config and deletes nothing,
|
|
119
|
+
and the old wording reported that as `removed 0 file(s)` — a run that did
|
|
120
|
+
exactly what was asked, reading as a no-op. The line now names the hooks and
|
|
121
|
+
counts every file it touched.
|
|
122
|
+
|
|
123
|
+
- **The AGENTS.md stanza no longer prescribes an architecture.** It carried
|
|
124
|
+
checkride's own house style as if it were law — "Named exports only; no
|
|
125
|
+
classes; `.js` extensions on relative imports", plus the barrel-`index.ts`
|
|
126
|
+
rules — into every repo that ran `init` or `agent-setup`. For a repo that uses
|
|
127
|
+
classes, default exports, or bundler-style extensionless imports, that is a
|
|
128
|
+
contract file instructing the agent to write the wrong code. The stanza is
|
|
129
|
+
also the one region a repo cannot correct: editing it is what makes the next
|
|
130
|
+
refresh refuse.
|
|
131
|
+
|
|
132
|
+
The `### Module boundaries` section now names where the rules live instead of
|
|
133
|
+
what they say — the `struct` check runs whatever ast-grep rules `sgconfig.yml`
|
|
134
|
+
points at, and those files are the convention. It is emitted only when
|
|
135
|
+
`struct` is an active check; a repo without it gets no section at all. Delete
|
|
136
|
+
`rules/no-class.yml` and the stanza stays true, which is the property it was
|
|
137
|
+
missing.
|
|
138
|
+
|
|
139
|
+
`init` still *scaffolds* those rules as its opinionated default — that part is
|
|
140
|
+
a starter you own and can edit, and is unchanged.
|
|
141
|
+
|
|
142
|
+
- **`--add struct` scaffolds the boundary rule and stops.** It used to write
|
|
143
|
+
four ast-grep rules, three of which are style, not structure: `no-class`,
|
|
144
|
+
`no-default-export`, and `require-js-extension`. A repo that uses classes,
|
|
145
|
+
default exports, or bundler resolution went red the moment it adopted the
|
|
146
|
+
slot — for code it had always written, over a convention it never chose, which
|
|
147
|
+
reads as checkride being broken rather than as a finding. `--add struct` now
|
|
148
|
+
writes `sgconfig.yml` and `rules/no-deep-sibling-import.yml`: the rule that
|
|
149
|
+
makes `struct` the slot it is, and one whose failures mean what they say.
|
|
150
|
+
|
|
151
|
+
New-mode `init` still writes all four. That is a different bargain — it is
|
|
152
|
+
creating the package, so there is no prior decision to override — and the
|
|
153
|
+
other rules are listed in `docs/deep-modules.md` for any repo that wants them.
|
|
154
|
+
|
|
155
|
+
### Fixed
|
|
156
|
+
|
|
157
|
+
- **A package manager that refuses to start the check script is no longer
|
|
158
|
+
reported as a red pipeline.** In any repo pinning `engines.node`, pnpm answers
|
|
159
|
+
a Node it does not accept with `ERR_PNPM_UNSUPPORTED_ENGINE` and **exit 1** —
|
|
160
|
+
the same exit code a failing test uses. The gate read that as a verdict on the
|
|
161
|
+
code and said so: `checkride ✘ red in 265ms`, exit 2, turn blocked, every turn,
|
|
162
|
+
regardless of what was written. It then sent the reader to
|
|
163
|
+
`.check/summary.json`, which no run had written, because no check had run.
|
|
164
|
+
|
|
165
|
+
That is the vacuous gate inverted — not a silent green but a permanent red no
|
|
166
|
+
code change can clear, whose rational response is to switch the gate off. And
|
|
167
|
+
it is not one repo's problem: agent harnesses run hooks in a **non-login
|
|
168
|
+
shell**, which never sources the rc file a version manager puts its shims in,
|
|
169
|
+
so the hook gets the machine's default Node rather than the contributor's. On
|
|
170
|
+
nvm that is the norm, not the exception.
|
|
171
|
+
|
|
172
|
+
`gate` now answers a third verdict — **could not run** — for a launch that
|
|
173
|
+
never happened, naming the cause and saying plainly that nothing ran and no
|
|
174
|
+
artifact describes this turn. It still blocks, in both harnesses: an unrunnable
|
|
175
|
+
gate has never been a pass. `triage` gained the matching `could-not-start`
|
|
176
|
+
verdict, since it runs the same script and had folded the same exit into `red`,
|
|
177
|
+
confirming the gate's wrong answer to whoever came asking why.
|
|
178
|
+
|
|
179
|
+
The classification is guarded on both sides. Only pnpm's and npm's own error
|
|
180
|
+
codes are matched — npm's `EBADENGINE` *warning* deliberately is not, because
|
|
181
|
+
npm runs the script anyway unless `engine-strict` is set — and a summary
|
|
182
|
+
written by the current run vetoes the whole thing, so a check that merely
|
|
183
|
+
*printed* one of these strings can never be reclassified into an environment
|
|
184
|
+
problem.
|
|
185
|
+
|
|
186
|
+
- **Upgrading no longer looks like an edit.** 0.10.1 started stamping the stanza
|
|
187
|
+
so a refresh could tell its own output from a customization — but a stanza
|
|
188
|
+
written *before* the stamp carries no evidence either way, so every upgrading
|
|
189
|
+
repo read as `unstamped`, refused, and by contract wrote **nothing**: not the
|
|
190
|
+
stanza, not the config, not the hooks. That is how 0.10.1's own gate-timeout
|
|
191
|
+
fix failed to reach the repos it was written for. The guard protecting AGENTS.md
|
|
192
|
+
prose was withholding a fix for a gate that had silently stopped gating.
|
|
193
|
+
|
|
194
|
+
checkride now recognizes the wordings it actually shipped — four distinct texts
|
|
195
|
+
across v0.1.1–v0.10.0, matched on everything but the generated
|
|
196
|
+
`Active checks in this repo:` line, so a repo's own slot list is irrelevant. A
|
|
197
|
+
pre-stamp stanza that matches one is checkride's own output, refreshes without
|
|
198
|
+
`--force`, and comes back stamped; one that matches nothing has been edited and
|
|
199
|
+
is still refused, with a message that now says so plainly instead of hedging
|
|
200
|
+
about indistinguishable wordings.
|
|
201
|
+
|
|
202
|
+
The list is closed and needs no maintenance: every release from 0.10.1 stamps
|
|
203
|
+
what it writes, so no future version can add an unstamped wording.
|
|
204
|
+
|
|
205
|
+
- **The AGENTS.md stanza is package-manager-agnostic.** It hardcoded `pnpm
|
|
206
|
+
check` and `pnpm exec checkride triage` regardless of the repo's package
|
|
207
|
+
manager, while checkride detects that manager everywhere else and README
|
|
208
|
+
promises the tool is agnostic. The stanza is a list of commands an agent is
|
|
209
|
+
told to run, so in an npm repo it was an instruction that cannot succeed: the
|
|
210
|
+
agent either invents a substitute or reports the pipeline as broken. It now
|
|
211
|
+
renders `npm run check` / `npx --no-install checkride triage`, `yarn run
|
|
212
|
+
check`, or `bun run check` from the detected manager, and `pnpm check` is
|
|
213
|
+
unchanged for pnpm repos.
|
|
214
|
+
|
|
215
|
+
The Claude Code gate's `statusMessage` had the same bug from the same source
|
|
216
|
+
and is fixed with it — the spinner said `npm check`, which is not a command,
|
|
217
|
+
and `yarn check` names Yarn 1's integrity checker rather than the repo's
|
|
218
|
+
script.
|
|
219
|
+
|
|
220
|
+
- **The `spell` check no longer trips over checkride's own stanza hash.** The
|
|
221
|
+
`hash=v1…` marker is 16 hex digits, so sooner or later it spells something
|
|
222
|
+
cspell does not recognize (`…fedf…`) and fails a slot for a string checkride
|
|
223
|
+
itself generated. The shipped `cspell.json` now ignores the marker.
|
|
224
|
+
|
|
225
|
+
### Contract
|
|
226
|
+
|
|
227
|
+
- **A gate that could not run answers "could not run", not "red"**
|
|
228
|
+
(docs/contract.md §CLI). The exit codes are unchanged and remain the promise —
|
|
229
|
+
2 while blocked under `--harness claude`, always 0 under `--harness cursor` —
|
|
230
|
+
because an unrunnable gate has always blocked. What is new is that the verdict
|
|
231
|
+
distinguishes a red pipeline from a launch that never happened, so anything
|
|
232
|
+
reading the `systemMessage` / `followup_message` body sees a cause instead of a
|
|
233
|
+
pointer to an artifact this run did not write. Additive: a hook script that
|
|
234
|
+
gates on the exit code alone is unaffected.
|
|
235
|
+
|
|
236
|
+
- **`checkride.config.json` promises a `gate` key** (docs/contract.md §CLI), and
|
|
237
|
+
with it the disclosure: while a profile is active, every gate verdict names the
|
|
238
|
+
profile and states that it was not the full check.
|
|
239
|
+
|
|
240
|
+
- **`CHECKRIDE_NODE_BIN`** is promised as the hook author's wrapping point: a
|
|
241
|
+
directory prepended to the check run's `PATH`, or `off` to disable checkride's
|
|
242
|
+
own Node-pin alignment.
|
|
243
|
+
|
|
244
|
+
- **What a hook *is* belongs to the harness** (docs/contract.md §CLI). The three
|
|
245
|
+
hook names stay promised; whether one lands as a config entry, a permission
|
|
246
|
+
rule, or a generated script is checkride's to choose per harness and may change
|
|
247
|
+
again. What is promised about the Claude Code deny rules is the part a consumer
|
|
248
|
+
builds on: checkride appends its own and removes only those. Anyone scripting
|
|
249
|
+
against the *path* `.claude/hooks/checkride-protect.cjs` should read
|
|
250
|
+
`permissions.deny` instead.
|
|
251
|
+
|
|
252
|
+
## [0.10.1] - 2026-07-31
|
|
253
|
+
|
|
254
|
+
### Added
|
|
255
|
+
|
|
256
|
+
- **`init` and `agent-setup` refuse to overwrite an AGENTS.md stanza that has
|
|
257
|
+
been edited.** Repos routinely need a line or two of their own in the contract
|
|
258
|
+
— a directory the `spell` check is allowed to skip, what a custom check means
|
|
259
|
+
here — and the refresh rewrote the marked region blind, taking those additions
|
|
260
|
+
with it. Idempotent, as promised, and lossy: the second run reported
|
|
261
|
+
`changed: false` precisely because the first one had already discarded the
|
|
262
|
+
edit.
|
|
263
|
+
|
|
264
|
+
The begin marker now carries a hash of the body checkride generated
|
|
265
|
+
(`<!-- checkride:begin hash=v1… -->`), so a later run can tell its own output
|
|
266
|
+
from a block someone has since changed. A changed one stops the run: exit 2, a
|
|
267
|
+
message naming the file and the two ways forward, and **nothing written at
|
|
268
|
+
all** — not the config, not the hooks — so a refused run leaves the repo
|
|
269
|
+
exactly as it found it, the rule new-mode `init` already followed for scaffold
|
|
270
|
+
collisions. `--force` accepts the loss and refreshes, and now carries that
|
|
271
|
+
meaning on `agent-setup` too, where the flag previously reached nothing.
|
|
272
|
+
|
|
273
|
+
Two things are deliberately *not* edits. Reformatting: line endings and
|
|
274
|
+
trailing whitespace are normalized away before hashing, so a Prettier run over
|
|
275
|
+
AGENTS.md does not read as a customization. And anything outside the markers,
|
|
276
|
+
which stays the right home for repo-specific additions — checkride has never
|
|
277
|
+
rewritten a line out there and still does not.
|
|
278
|
+
|
|
279
|
+
One-time cost on upgrade: a stanza written by 0.10.0 or earlier carries no
|
|
280
|
+
hash, and an older version's wording is indistinguishable from an edit, so the
|
|
281
|
+
first run after upgrading refuses with its own wording of the message.
|
|
282
|
+
`--force` once stamps it; detection is automatic from then on.
|
|
283
|
+
|
|
284
|
+
- **`--remove-hook <a,b>` tears an installed hook back out** — the config entry
|
|
285
|
+
and the generated script both. `--hook` only ever chose what to *write*, so
|
|
286
|
+
there was no supported way to drop the gate after the fact. It pairs with
|
|
287
|
+
`--no-hook` to remove without refreshing anything else, and removing `dirty`
|
|
288
|
+
rewrites a surviving gate unguarded, so the gate cannot silently disarm itself
|
|
289
|
+
on the way out.
|
|
290
|
+
|
|
291
|
+
- **A `note` field on any check entry, and at the config root**, for commentary
|
|
292
|
+
aimed at whoever reads `checkride.config.json` next. It is validated as a
|
|
293
|
+
string and then dropped — no code path carries it onto an Adapter, so it
|
|
294
|
+
cannot reach the status line or `summary.json`. `description` keeps its
|
|
295
|
+
user-facing role; this repo's own config dogfoods the split.
|
|
296
|
+
|
|
297
|
+
- **The gate states its verdict in words**, not just an exit code: the wall
|
|
298
|
+
clock and the failing slots, read back from `.check/summary.json` —
|
|
299
|
+
`checkride red in 41.7s, 2 of 15 failed: lint, test`. Under Claude Code it
|
|
300
|
+
rides in the hook body as `systemMessage`, which moves the block onto
|
|
301
|
+
`decision: "block"`, since hook JSON is parsed only on exit 0. `checkride
|
|
302
|
+
gate`'s own exit codes are unchanged: the generated script does the
|
|
303
|
+
translation and falls back to exit-2 blocking when no body comes back, so an
|
|
304
|
+
unrefreshed repo or an older checkride keeps gating either way. Cursor gets
|
|
305
|
+
the same line atop its `followup_message` on red; a green Cursor gate stays
|
|
306
|
+
silent, because its only stop-hook output field submits a new turn, and the
|
|
307
|
+
gap is recorded in docs/cursor.md rather than papered over.
|
|
308
|
+
|
|
309
|
+
- **The Claude Code Stop entry carries `statusMessage` and `timeout: 900`.** The
|
|
310
|
+
spinner now names the command rather than leaving a multi-minute pipeline
|
|
311
|
+
indistinguishable from a hung model, and the timeout closes a live hole: the
|
|
312
|
+
platform default is 600s, and a hook cancelled by it exits non-zero, which
|
|
313
|
+
Claude Code reads as a broken hook and lets the turn end. Any repo whose
|
|
314
|
+
pipeline crossed ten minutes had a gate that had quietly stopped gating.
|
|
315
|
+
|
|
316
|
+
### Fixed
|
|
317
|
+
|
|
318
|
+
- **`triage` no longer reports an unparseable summary as a run with no
|
|
319
|
+
failures.** A repo whose gate is a homegrown script writing a checkride-shaped
|
|
320
|
+
`.check/` — same file names, no `schema_version` — made the reader announce
|
|
321
|
+
"checkride never ran, a compound script short-circuited" about a gate that had
|
|
322
|
+
run all eight of its checks and failed three. An empty slot table now reads as
|
|
323
|
+
"nothing was read", not "nothing failed": the short-circuit verdict is
|
|
324
|
+
reserved for a genuinely absent summary, `covered:` renders `unknown` rather
|
|
325
|
+
than a count nobody measured, a missing `schema_version` becomes its own
|
|
326
|
+
`foreign` state, and a new `.check/ contents` section lists the directory when
|
|
327
|
+
the summary is not an index.
|
|
328
|
+
|
|
329
|
+
- **The gate ignores a summary older than its own start time.** A check script
|
|
330
|
+
shaped `tsc --build && checkride` leaves `summary.json` untouched when the
|
|
331
|
+
build fails, and trusting it would report the previous run's failing slots as
|
|
332
|
+
this run's.
|
|
333
|
+
|
|
334
|
+
### Contract
|
|
335
|
+
|
|
336
|
+
- **checkride owns the AGENTS.md stanza and only the stanza**, and the boundary
|
|
337
|
+
is now enforced rather than documented (docs/contract.md §CLI, locked by
|
|
338
|
+
`test/contract/flags.contract.test.ts`). The promise a consumer can build on:
|
|
339
|
+
a stanza carrying local edits is never overwritten without `--force`, and the
|
|
340
|
+
run that refuses writes nothing. Behavioural break for anyone scripting
|
|
341
|
+
`agent-setup` over a repo with a hand-edited or pre-0.10.1 stanza — that
|
|
342
|
+
invocation exited 0 and now exits 2 until it is re-run with `--force`.
|
|
343
|
+
|
|
344
|
+
## [0.10.0] - 2026-07-31
|
|
345
|
+
|
|
346
|
+
### Added
|
|
347
|
+
|
|
348
|
+
- **Cursor support — hooks and skills.** `init` and `agent-setup` now write the
|
|
349
|
+
same three hooks (`gate`, `dirty`, `protect`) into `.cursor/hooks.json` as
|
|
350
|
+
they do into `.claude/settings.json`, and write the bundled `check` and `qa`
|
|
351
|
+
skills into `.cursor/skills/` as `checkride-check` and `checkride-qa` —
|
|
352
|
+
Cursor has no plugin system to install them from. Which harnesses get wired
|
|
353
|
+
is detected (Claude Code always, Cursor when `.cursor/` exists) and
|
|
354
|
+
overridable with `--harness <a,b>`.
|
|
355
|
+
|
|
356
|
+
The gate had to learn a second protocol to get there. Claude Code blocks a
|
|
357
|
+
turn on **exit 2** and reads stderr; Cursor treats any non-zero stop hook as a
|
|
358
|
+
*broken* hook and lets the turn end, so its verdict rides in a
|
|
359
|
+
`{"followup_message": …}` body on stdout instead. Same decision, two wire
|
|
360
|
+
formats.
|
|
361
|
+
|
|
362
|
+
Cursor's defaults would have made that gate advisory, so the entry overrides
|
|
363
|
+
three of them: `timeout` (a pipeline is minutes), `loop_limit: null` (the
|
|
364
|
+
default of 5 stops the gate replying after five turns, where Claude Code
|
|
365
|
+
re-blocks indefinitely) and `failClosed: true` (Cursor is fail-open, so a
|
|
366
|
+
crashed or timed-out gate would end the turn silently). The `dirty` and
|
|
367
|
+
`protect` guards keep the fail-open default, deliberately.
|
|
368
|
+
|
|
369
|
+
- **[Cursor](docs/cursor.md).** A page for the parts that are not symmetric with
|
|
370
|
+
Claude Code — the reply-don't-block stop protocol, the three overridden
|
|
371
|
+
defaults, the third-party-configs setting, and an explicit list of the
|
|
372
|
+
assumptions Cursor's own docs leave open (chiefly: the `tool_input` path key
|
|
373
|
+
`protect` matches on is a guess, and a wrong guess fails open silently).
|
|
374
|
+
|
|
375
|
+
- **`checkride gate`, `checkride triage`, `checkride qa`.** The gate's body —
|
|
376
|
+
the edit-marker check, the `check` run, the digest-or-summary choice — moved
|
|
377
|
+
out of the generated shell script into a real command, so it is testable and
|
|
378
|
+
every harness's hook script is now a thin adapter over it. `triage` and `qa`
|
|
379
|
+
promote the two bundled plugin readers to first-class commands, so a skill no
|
|
380
|
+
longer needs `${CLAUDE_PLUGIN_ROOT}` (or a hardcoded `node_modules/` path) to
|
|
381
|
+
find them.
|
|
382
|
+
|
|
383
|
+
### Fixed
|
|
384
|
+
|
|
385
|
+
- **The gate no longer fails open when checkride cannot run.** An uninstalled
|
|
386
|
+
or unresolvable checkride made the hook exit 1, which Claude Code reads as
|
|
387
|
+
"hook failed, carry on" — leaving a repo whose gate had silently stopped
|
|
388
|
+
gating. Any exit outside the gate's own 0/2 now blocks, naming the cause.
|
|
389
|
+
(`protect` still fails open, deliberately: a broken protect hook must not
|
|
390
|
+
become a repo where nothing can be written.)
|
|
391
|
+
- **A repo wired for both harnesses no longer runs two gates per turn.** Cursor
|
|
392
|
+
loads `.claude/settings.json` hooks when third-party configs are enabled, maps
|
|
393
|
+
`Stop` onto `stop`, and runs *every* matching source — so the default wiring
|
|
394
|
+
(Claude Code is always selected) fired two full pipelines concurrently into one
|
|
395
|
+
`.check/`, racing on the artifacts the orchestrator clears per slot and on the
|
|
396
|
+
edit marker. `checkride gate --harness claude` now stands down when Cursor is
|
|
397
|
+
running it and a native Cursor gate is registered. Narrow on purpose: a stale
|
|
398
|
+
`CURSOR_PROJECT_DIR` costs a duplicate run, never the gate.
|
|
399
|
+
- **A gate that cannot enter the repo now reports it.** The `cd` guard was a bare
|
|
400
|
+
`exit 2`, which said nothing under Claude Code and — because Cursor reads any
|
|
401
|
+
non-zero stop hook as a broken one — ended the turn silently under Cursor. It
|
|
402
|
+
now emits the same "could not run" verdict as every other way the gate can fail
|
|
403
|
+
to start, in each harness's protocol.
|
|
404
|
+
- **`protect` no longer misses paths under a symlinked repo root.** It compared
|
|
405
|
+
the harness's spelling of a path against the environment's, which on macOS
|
|
406
|
+
routinely differ (`/var/…` vs `/private/var/…`), making every in-repo path
|
|
407
|
+
look external — so an edit to `checkride.baseline.json` was allowed through.
|
|
408
|
+
Both sides are now resolved before comparison.
|
|
409
|
+
|
|
410
|
+
### Contract
|
|
411
|
+
|
|
412
|
+
- The command set gains `gate`, `triage` and `qa`; `init`/`agent-setup` gain
|
|
413
|
+
`--harness <a,b>`. All additive. `gate` is documented as the one command
|
|
414
|
+
outside the 0/1/2 exit split, because it answers a harness's hook protocol
|
|
415
|
+
rather than checkride's own — see `docs/contract.md` §CLI. Its one documented
|
|
416
|
+
no-op is promised there too: `--harness claude` stands down for a registered
|
|
417
|
+
native Cursor gate.
|
|
418
|
+
|
|
419
|
+
## [0.9.6] - 2026-07-30
|
|
420
|
+
|
|
421
|
+
### Fixed
|
|
422
|
+
|
|
423
|
+
- **A slot refused for a missing tool was reported as a harness problem.** The
|
|
424
|
+
refusal carried `exit_code: -1`, which is reserved for a spawn failure or
|
|
425
|
+
timeout — so `triage` matched it and printed "it failed to spawn or timed out.
|
|
426
|
+
That is a harness problem, not a finding," telling the reader to discount the
|
|
427
|
+
one failure the pre-flight exists to make them act on, remediation and all. It
|
|
428
|
+
now exits **1**, a finding like any other. `-1` keeps meaning the harness
|
|
429
|
+
broke, and a triage test now pins the two apart.
|
|
430
|
+
- **A tool installed above the repo root satisfied the pre-flight.** The upward
|
|
431
|
+
search for `node_modules/.bin/<tool>` stopped only at the filesystem root, so
|
|
432
|
+
a stray install in a home or projects directory counted — the same
|
|
433
|
+
machine-state dependence the local resolution was added to remove, reached by
|
|
434
|
+
a different route: the slot passed for whoever had that directory above their
|
|
435
|
+
clone and failed on the clean checkout. The search now stops at the repo root,
|
|
436
|
+
marked by a `.git` or a lockfile, and searches that root before stopping.
|
|
437
|
+
- **A timed-out `yarn bin` probe reported the tool as missing.** Under Yarn PnP
|
|
438
|
+
`doctor` folded every probe failure into "not a dependency", so a slow package
|
|
439
|
+
manager produced a confident `missing` and told you to install a tool that was
|
|
440
|
+
already there. A timeout is now `unknown`, with a hint naming it — the
|
|
441
|
+
treatment the `--version` probe alongside it already had.
|
|
442
|
+
- **`doctor` reported a healthy Yarn PnP project as broken.** Both of its
|
|
443
|
+
install questions were path tests against a `node_modules/` tree, which a PnP
|
|
444
|
+
project does not have: `install` asserted the directory and so reported
|
|
445
|
+
`lockfile only`, and each slot's tool was looked for at
|
|
446
|
+
`node_modules/.bin/<tool>` and so reported missing — on a repo where every
|
|
447
|
+
tool was installed and every check ran green. The two commands therefore
|
|
448
|
+
contradicted each other, `doctor` calling the environment broken while the
|
|
449
|
+
gate passed. `install` is now satisfied by `.pnp.cjs` plus the lockfile, and a
|
|
450
|
+
tool is resolved with `yarn bin <tool>`, whose exit code carries the answer.
|
|
451
|
+
A tool that genuinely does not resolve still reports missing — the looser
|
|
452
|
+
question must not become a softer one, or this would trade a false red for a
|
|
453
|
+
false green, which is the worse of the two. Detection is gated on yarn so a
|
|
454
|
+
`.pnp.cjs` left by a migration off Yarn cannot reroute an npm or pnpm repo.
|
|
455
|
+
|
|
456
|
+
## [0.9.5] - 2026-07-30
|
|
457
|
+
|
|
458
|
+
### Contract
|
|
459
|
+
|
|
460
|
+
- **A slot's tool is now resolved in the local tree before the check spawns,
|
|
461
|
+
under `npx` and `bunx`.** 0.9.3 stopped those launchers *fetching* a missing
|
|
462
|
+
tool, but `--no-install` bounds the download and not the per-user cache, so
|
|
463
|
+
both still run a copy left behind by some earlier unrelated invocation. The
|
|
464
|
+
gate's verdict therefore depended on machine state nobody declared: a repo
|
|
465
|
+
whose `docs` slot was detected from `.markdownlint-cli2.jsonc` but never
|
|
466
|
+
installed `markdownlint-cli2` passed for every developer holding a cached copy
|
|
467
|
+
and failed on the clean checkout — which is the CI runner, the most expensive
|
|
468
|
+
place to find out. The tool is now looked up at `node_modules/.bin/<tool>`,
|
|
469
|
+
searched from the check's directory upward so a workspace tool hoisted to the
|
|
470
|
+
repo root still counts, and a slot whose tool is absent fails there with a
|
|
471
|
+
message naming the slot, the path searched, and the PM's own install command
|
|
472
|
+
instead of the launcher's `npx canceled due to missing packages`. **Behavior
|
|
473
|
+
change:** a slot passing only because of a cached tool now fails until that
|
|
474
|
+
tool is a declared dependency — the same trade 0.9.3 made, applied to the
|
|
475
|
+
fallback it left open, and it fails on the developer's machine rather than
|
|
476
|
+
only in CI. Scoped to the two launchers that keep such a cache: `pnpm exec`
|
|
477
|
+
and `yarn` resolve from the project tree already and are not pre-flighted,
|
|
478
|
+
which also leaves Yarn PnP — where no `node_modules/.bin` exists to find —
|
|
479
|
+
working as before. See `docs/contract.md` §Vacuous green and `docs/tools.md`
|
|
480
|
+
§Package managers.
|
|
481
|
+
|
|
482
|
+
### Fixed
|
|
483
|
+
|
|
484
|
+
- **`doctor` told npm, yarn and bun repos to run `pnpm install`.** The
|
|
485
|
+
remediation hint on a missing-tool row was a hardcoded string, so the one
|
|
486
|
+
actionable line on a red row named a package manager the repo does not use —
|
|
487
|
+
while `checkInstall` two functions above it had been PM-aware all along. It
|
|
488
|
+
now names the detected manager and the exact dev-dependency install.
|
|
489
|
+
- **`doctor` reported a hoisted workspace tool as missing.** The probe tested
|
|
490
|
+
`<cwd>/node_modules/.bin/<tool>` and nothing above it, but pnpm and npm both
|
|
491
|
+
hoist a shared tool's bin to the workspace root — so a correctly installed
|
|
492
|
+
monorepo showed a false `missing` for every tool from any package
|
|
493
|
+
subdirectory, on exactly the repos the workspace presets generate. It now
|
|
494
|
+
walks upward like the package managers do, sharing one resolver with the run
|
|
495
|
+
path so the two cannot disagree about where a tool lives.
|
|
496
|
+
|
|
497
|
+
## [0.9.4] - 2026-07-30
|
|
498
|
+
|
|
499
|
+
### Fixed
|
|
500
|
+
|
|
501
|
+
- **Member-scoped fallow findings now key on `<container>.<leaf>` instead of a
|
|
502
|
+
line and column.** fallow reports an unused class/enum/store member as
|
|
503
|
+
`parent_name` + `member_name`, a component binding as `component_name` +
|
|
504
|
+
`prop_name`/`input_name`/`output_name`/`emit_name`/`event_name`, and a catalog
|
|
505
|
+
entry as `catalog_name` + `entry_name` — none of which the extractor
|
|
506
|
+
recognized, so every such finding fell through to the `<path>:<line>:<col>`
|
|
507
|
+
fallback. That key moves whenever code above it does, so editing anything
|
|
508
|
+
earlier in a file re-keyed its grandfathered members and re-surfaced them as
|
|
509
|
+
new findings. They are now line-free
|
|
510
|
+
(`dead-code:unused_class_members:src/a.ts:Svc.unusedOne`) and survive the edit,
|
|
511
|
+
as every other category already did. The same pass gave the report's remaining
|
|
512
|
+
`*_name` categories a stable identity too, so they key on a symbol rather than
|
|
513
|
+
a position as well. **This changes the `dead` slot's fingerprints — re-run
|
|
514
|
+
`checkride baseline` to re-capture, or those findings will report as new
|
|
515
|
+
once.**
|
|
516
|
+
- **Report arrays that fallow does not count are no longer fingerprinted.**
|
|
517
|
+
`workspace_diagnostics` carries a `path`, and the opt-in `thin_wrappers` and
|
|
518
|
+
`duplicate_prop_shapes` rules a `file`, so all three produced keys while
|
|
519
|
+
fallow's issue registry marks them `counts_in_total: false`. The extra keys
|
|
520
|
+
pushed the key count past the issue count, where the un-keyable-findings guard
|
|
521
|
+
clamped the difference to zero — so a spurious key could offset a finding that
|
|
522
|
+
genuinely had no stable identity, and the baseline masked a slot green that it
|
|
523
|
+
had never grandfathered. The guard now counts un-keyable findings directly
|
|
524
|
+
rather than inferring them from the totals, which closes that offset even if a
|
|
525
|
+
future fallow adds an uncounted array this version does not know to skip.
|
|
526
|
+
|
|
527
|
+
## [0.9.3] - 2026-07-29
|
|
528
|
+
|
|
529
|
+
### Contract
|
|
530
|
+
|
|
531
|
+
- **An empty slot selection is now a usage error.** `--only`, `--skip` or
|
|
532
|
+
`--include` given a value that names nothing after trimming (`--only ,`,
|
|
533
|
+
`--only ''`, `--only ' '`) exits **2**, naming the flag. It previously
|
|
534
|
+
parsed to an empty list, which is truthy, so `--only ,` filtered every check
|
|
535
|
+
out and exited **0** having verified nothing — the same vacuous green that
|
|
536
|
+
`--only lints` was fixed for in 0.5.0, reached by a different route. The two
|
|
537
|
+
empty spellings also disagreed: `--only ''` ran the whole pipeline. The rule
|
|
538
|
+
now holds for a programmatic caller too — `runChecks({ only: [] })` throws
|
|
539
|
+
rather than selecting nothing. See `docs/contract.md` §CLI and §Exit codes.
|
|
540
|
+
- **`summary.json`'s published schema described `total_duration_ms` as a sum.**
|
|
541
|
+
It is, and has been since concurrency landed, the **wall-clock** span of the
|
|
542
|
+
run; the two diverge whenever checks overlap. No behavior changed — the
|
|
543
|
+
description in `schema/checkride.summary.schema.json` was simply wrong, and a
|
|
544
|
+
consumer implementing against it would derive the wrong run-start
|
|
545
|
+
(`timestamp - total_duration_ms`) and read stale artifacts as fresh. The
|
|
546
|
+
semantics are now pinned by a test, not just prose. See `docs/contract.md`
|
|
547
|
+
§`.check/summary.json`.
|
|
548
|
+
|
|
549
|
+
### Fixed
|
|
550
|
+
|
|
551
|
+
- **Tools are no longer installed from the registry mid-run under npm or bun.**
|
|
552
|
+
The exec translation emitted a bare `npx <tool>` / `bunx <tool>`, and both
|
|
553
|
+
launchers fetch a missing package and run it rather than failing — checks are
|
|
554
|
+
spawned without a TTY, which is precisely the non-interactive case where
|
|
555
|
+
neither stops to prompt. A repo missing a tool could therefore have its gate
|
|
556
|
+
silently execute an unpinned `latest`. Both now carry `--no-install`, which
|
|
557
|
+
bounds the download but not the launcher's global cache — the guarantee is
|
|
558
|
+
"nothing new is fetched mid-run". **Behavior change:** a tool that would have
|
|
559
|
+
to be downloaded now fails its slot. `pnpm exec` and `yarn` have no such cache
|
|
560
|
+
path and are unchanged.
|
|
561
|
+
- **The triage reader's gate timeout could not reap what it started.** It
|
|
562
|
+
spawned without `detached` and sent a bare `SIGTERM` with no escalation, so
|
|
563
|
+
the signal reached the package-manager wrapper and left the checks themselves
|
|
564
|
+
running — which also meant `close` could wait on their inherited pipes
|
|
565
|
+
forever, hanging the reader the timeout existed to protect. It now leads its
|
|
566
|
+
own process group and escalates to `SIGKILL`, the same discipline the
|
|
567
|
+
orchestrator already applied; both share the new `killGroup` helper.
|
|
568
|
+
- **`.check/` artifacts are now flushed before the rename that publishes them.**
|
|
569
|
+
The atomic write promised each file was "either the previous complete version
|
|
570
|
+
or the new complete version", but without an `fsync` a crash could leave the
|
|
571
|
+
renamed inode holding nothing — the torn read it exists to prevent, relocated.
|
|
572
|
+
- **A committed baseline whose `slots` is an array is rejected instead of being
|
|
573
|
+
keyed by index.** Two of the four copies of the `isRecord` guard accepted
|
|
574
|
+
arrays (`typeof [] === 'object'`); the baseline read was one of them, so such
|
|
575
|
+
a file loaded cleanly and masked nothing. All four now share one definition.
|
|
576
|
+
- **A baseline from a newer schema version is dropped rather than half-read.**
|
|
577
|
+
`schema_version` was recorded and never checked. Dropping it fails closed: the
|
|
578
|
+
run reports the diagnostics that are really there, where guessing risks
|
|
579
|
+
masking findings the author never grandfathered.
|
|
580
|
+
- **AGENTS.md described a tree the repo does not have.** It named four folder
|
|
581
|
+
modules where there are seven (`artifacts/`, `qa/` and `triage/` were
|
|
582
|
+
missing), and stated a test-placement rule — colocate beside the source file —
|
|
583
|
+
that no test in the repo has ever followed. Both are corrected, and both are
|
|
584
|
+
now checked against the tree.
|
|
585
|
+
- **Report columns no longer break on long check names.** Fixed widths were
|
|
586
|
+
sized for catalogue slots, so a config custom check (`typecheck-tests`, or
|
|
587
|
+
`custom:typecheck-tests` in the adapter column) shunted every later column
|
|
588
|
+
right on its own row. `doctor` and the run status lines now size each column
|
|
589
|
+
to the rows it holds.
|
|
590
|
+
|
|
591
|
+
### Internal
|
|
592
|
+
|
|
593
|
+
- **This repo's own gate now runs the `security` slot.** `pnpm audit` takes well
|
|
594
|
+
under a second and reports zero advisories at every level, and a package that
|
|
595
|
+
publishes to npm with provenance should be auditing its own dependency tree as
|
|
596
|
+
part of "done" rather than out of band. Note the trade-off: the gate now needs
|
|
597
|
+
the registry to be reachable, and a failure to *verify* is a failure, not a
|
|
598
|
+
pass — so a network outage turns the run red rather than quietly skipping the
|
|
599
|
+
check.
|
|
600
|
+
- **The stryker break threshold moves 55 → 68.** The measured score is 73.2%
|
|
601
|
+
(up from 71.7%), so the old gate sat 18 points below reality and could not
|
|
602
|
+
have failed on anything short of deleting a suite. It now sits ~5 points
|
|
603
|
+
under — enough headroom that a mutant timing out on a loaded machine does not
|
|
604
|
+
turn the run red, which is the only reason not to set it tighter. The two
|
|
605
|
+
bundled-plugin bins are excluded from the mutate set: they are three lines
|
|
606
|
+
each, do their work at module top level, and are unreachable from the unit
|
|
607
|
+
runner stryker drives, so they reported a permanent 0% no test could move.
|
|
608
|
+
They are covered as processes by `test/e2e/plugin-readers.e2e.test.ts`.
|
|
609
|
+
- Tests for the last uncovered code in the package. The two bundled-plugin
|
|
610
|
+
entry points (`dist/triage/cli.js`, `dist/qa/cli.js`) are exercised as
|
|
611
|
+
processes, which is the only way to reach a bin that works at module top
|
|
612
|
+
level; `checkride fix` gained CLI-dispatch coverage it never had; and the
|
|
613
|
+
three `qa/` extractors gained tests for their truncation caps and
|
|
614
|
+
malformed-input fallbacks — the bounded-reader behavior those modules exist
|
|
615
|
+
for. Branch coverage 84.3% → 85.5%.
|
|
616
|
+
- New guards for the claims that drifted: `test/conventions.test.ts` checks
|
|
617
|
+
AGENTS.md's folder-module list and test placement against the tree, and the
|
|
618
|
+
summary contract suite now pins `total_duration_ms` behaviorally — wall-clock
|
|
619
|
+
under concurrency, equal to the summed durations when sequential. Both fail
|
|
620
|
+
when reverted, which is the point.
|
|
621
|
+
- `test/e2e/defaults.e2e.test.ts` resolves a fixture repo that has the tool
|
|
622
|
+
configs but **no `checkride.config.json`**, so the shipped catalogue and the
|
|
623
|
+
detection path are exercised. This repo's own config names all twenty slots,
|
|
624
|
+
which is deliberate but meant daily runs never walked a consumer's path.
|
|
625
|
+
- First tests for `src/triage/env.ts`, previously the only module in the package
|
|
626
|
+
with no coverage at all.
|
|
627
|
+
- Two new single-file modules: `src/proc.ts` (process-group kill and escalation,
|
|
628
|
+
shared by both spawners) and `src/json.ts` (the one `isRecord`).
|
|
629
|
+
- Long-form rationale moved out of source comments and into the docs that own
|
|
630
|
+
it: the pnpm stdout-narration investigation to `docs/tools.md` §Launcher
|
|
631
|
+
quirks, and the freshness-window derivation to `docs/plugin.md`. The comments
|
|
632
|
+
keep the "why" and point at the rest.
|
|
633
|
+
|
|
634
|
+
## [0.9.2] - 2026-07-28
|
|
635
|
+
|
|
636
|
+
### Changed
|
|
637
|
+
|
|
638
|
+
- **`checkride init` now writes a newer toolchain into generated projects.**
|
|
639
|
+
The pinned versions a new project receives move to oxlint 1.74.0 (with
|
|
640
|
+
oxlint-tsgolint 0.25.0), fallow 3.9.1, ast-grep 0.45.0, vitest 4.1.10,
|
|
641
|
+
markdownlint-cli2 0.23.1, cspell 10.0.1, prettier 3.9.6, biome 2.5.6, jest
|
|
642
|
+
30.4.2, publint 0.3.22, attw 0.18.5, and `@types/node` 22.20.1. A freshly
|
|
643
|
+
generated project still installs and passes green out of the box. TypeScript
|
|
644
|
+
stays at 6.0.3, and the eslint and knip pins stay on their current majors —
|
|
645
|
+
all three are deliberate holds, now marked as such in the adapter registry.
|
|
646
|
+
- **The documented fallow pin is 3.9.1.** The supported floor is unchanged:
|
|
647
|
+
fallow ≥ 3.5, JSON `schema_version` 7. Every release from 3.5.0 through 3.9.1
|
|
648
|
+
emits schema 7 with identical report layouts, so an existing baseline keeps
|
|
649
|
+
masking and no consumer has to move.
|
|
650
|
+
|
|
651
|
+
### Fixed
|
|
652
|
+
|
|
653
|
+
- **The bundled `check` skill quoted a markdownlint summary line that no longer
|
|
654
|
+
exists.** Its guidance on reading a failing `docs` slot cited
|
|
655
|
+
`Summary: 1 error(s)`; markdownlint-cli2 0.23 emits `Summary: 1 issue in 1
|
|
656
|
+
file`. The surrounding advice was still right — that tool puts the count on
|
|
657
|
+
stdout and the `file:line:rule` you actually need on stderr — but the quoted
|
|
658
|
+
string sent agents looking for text no run produces.
|
|
659
|
+
|
|
660
|
+
### Internal
|
|
661
|
+
|
|
662
|
+
- Development dependencies upgraded across the board, in separate verified
|
|
663
|
+
steps: fallow's report schema and layouts confirmed unchanged, ast-grep's
|
|
664
|
+
rules confirmed to still fire (repo *and* shipped template copies), and
|
|
665
|
+
oxlint's diagnostic text confirmed byte-identical for the baseline fixtures
|
|
666
|
+
it fingerprints.
|
|
667
|
+
- Nine new oxlint errors from the 1.61 → 1.74 jump were fixed at the root
|
|
668
|
+
rather than suppressed: two genuinely unnecessary type assertions, and seven
|
|
669
|
+
test helpers hoisted out of the scopes they never captured.
|
|
670
|
+
- `pnpm audit` is clean again. The `brace-expansion` override had gone stale as
|
|
671
|
+
its advisory widened, `fast-uri`, `postcss`, and a js-yaml 5.x entry were
|
|
672
|
+
added, and the `markdown-it` and js-yaml 4.x overrides were retired as
|
|
673
|
+
redundant — each verified by removal and re-audit, not by inspection.
|
|
674
|
+
- Every GitHub Actions ref is now SHA-pinned with a version comment. `ci.yml`
|
|
675
|
+
alone had floated on mutable major tags, which made the workflow gating every
|
|
676
|
+
merge the one an upstream tag move could reach. CI's pnpm moves to 11.17.0,
|
|
677
|
+
verified to accept the committed lockfile unmodified.
|
|
678
|
+
|
|
679
|
+
## [0.9.1] - 2026-07-28
|
|
680
|
+
|
|
681
|
+
### Fixed
|
|
682
|
+
|
|
683
|
+
- **The `links` check no longer flags example links inside code blocks.** A
|
|
684
|
+
`[text](target)` shown inside a fenced block or inline backticks was resolved
|
|
685
|
+
against disk like any real link, so documentation that demonstrates Markdown
|
|
686
|
+
syntax could only pass by enumerating every example target in
|
|
687
|
+
`checks.links.allowlist`. Fence state is now tracked across the file —
|
|
688
|
+
honoring CommonMark on marker length, tilde fences, and info strings — and
|
|
689
|
+
inline code spans are masked before parsing. Two limits are deliberate and
|
|
690
|
+
tested: 4-space indented blocks are still checked, since indentation is
|
|
691
|
+
ambiguous with list continuation, and code spans straddling a newline stay
|
|
692
|
+
unmatched. The check only became less strict, so a repo passing on 0.9.0
|
|
693
|
+
still passes.
|
|
694
|
+
|
|
695
|
+
## [0.9.0] - 2026-07-28
|
|
696
|
+
|
|
697
|
+
### Added
|
|
698
|
+
|
|
699
|
+
- **A PreToolUse deny hook guards checkride's accounting files.** "Never add
|
|
700
|
+
to the baseline to make a check pass" was README advice that an agent
|
|
701
|
+
editing the file has every local incentive to ignore; it is now
|
|
702
|
+
enforcement. Edits (Edit/Write/NotebookEdit) to `checkride.baseline.json`
|
|
703
|
+
and `.check/**` are blocked with a message explaining the ratchet; reads
|
|
704
|
+
are never denied — the stanza's own triage procedure and both plugin
|
|
705
|
+
skills read `.check/` artifacts. Both paths are unambiguous, so the deny
|
|
706
|
+
is exact; module-boundary rules deliberately stay in ast-grep, where they
|
|
707
|
+
are enforced over parsed code rather than approximated by paths.
|
|
708
|
+
- **The gate now skips turns that edited nothing.** A new PostToolUse hook
|
|
709
|
+
(`dirty`) touches `.check/.dirty` on every Edit/Write/NotebookEdit; the gate
|
|
710
|
+
script exits 0 immediately when the marker is absent and clears it after a
|
|
711
|
+
green run. Stop fires at the end of *every* turn, including
|
|
712
|
+
pure-conversation ones — on a real repo that was a multi-second pipeline
|
|
713
|
+
tax (plus formatter writes) for a turn that changed nothing, the single
|
|
714
|
+
best reason to disable the gate. File writes made through Bash don't set
|
|
715
|
+
the marker (a known, accepted gap — matching Bash would fire on every
|
|
716
|
+
command); the next tool-edited turn re-covers it. A `--hook gate`
|
|
717
|
+
selection without `dirty` writes an unconditional script, so the guard can
|
|
718
|
+
never disarm a gate that has no marker source.
|
|
719
|
+
- **The generated gate runs `--strict --digest`.** The Stop hook is a gate,
|
|
720
|
+
and the contract says anything that gates should pass `--strict` — now
|
|
721
|
+
checkride's own generated hook does. `--digest` writes the token-bounded
|
|
722
|
+
`.check/digest.md` on red, and the guidance message points there when it
|
|
723
|
+
exists (summary.json otherwise) and names `/checkride:check` for full
|
|
724
|
+
triage. npm repos get the `--` passthrough form; pnpm/yarn/bun forward
|
|
725
|
+
flags directly.
|
|
726
|
+
|
|
727
|
+
### Changed
|
|
728
|
+
|
|
729
|
+
- **The Stop-hook gate moved into a checkride-owned script,
|
|
730
|
+
`.claude/hooks/checkride-gate.sh`.** The settings.json entry is now a stable
|
|
731
|
+
one-liner invoking it (`sh "${CLAUDE_PROJECT_DIR:-.}/…"`), so refreshing is
|
|
732
|
+
genuinely lossless: checkride overwrites its script freely, and consumer
|
|
733
|
+
customization has a natural home (a sibling script, or the environment)
|
|
734
|
+
instead of an inline command the next `agent-setup` would clobber. Repos
|
|
735
|
+
carrying the old inline form are migrated in place on the next
|
|
736
|
+
`agent-setup`/`init` — detected by the existing sentinel and replaced, never
|
|
737
|
+
duplicated. `init`/`agent-setup` grow `--hook <a,b>` to select which hooks
|
|
738
|
+
to write (default: all); `--no-hook` remains the write-none escape.
|
|
739
|
+
|
|
740
|
+
### Fixed
|
|
741
|
+
|
|
742
|
+
- **The `security` slot now gates at `--audit-level`, not at any advisory.**
|
|
743
|
+
pnpm's JSON mode exits 1 on *any* advisory regardless of `--audit-level`
|
|
744
|
+
(only table mode lets the level gate the exit code — reproduced with a
|
|
745
|
+
single-moderate lockfile: `--json` exits 1, table exits 0), so the slot
|
|
746
|
+
judged by exit code effectively gated at zero advisories of any severity
|
|
747
|
+
rather than the declared `high`. `security` is now a built-in evaluator: it
|
|
748
|
+
runs the audit with `--json`, parses `metadata.vulnerabilities`, and fails
|
|
749
|
+
only on advisories at or above the threshold parsed from the adapter's own
|
|
750
|
+
`--audit-level` arg — so a consumer override keeps meaning what it says,
|
|
751
|
+
and "audit could not run" (registry unreachable, malformed output) stays a
|
|
752
|
+
failure rather than a silent pass. Consumers who dropped `--json` to work
|
|
753
|
+
around this can return to the default args and get the
|
|
754
|
+
`.check/security.json` artifact back (`--json` is appended if an override
|
|
755
|
+
omits it).
|
|
756
|
+
- **`pack` no longer fails hard under pnpm older than 10.26.** pnpm only
|
|
757
|
+
learned `pack --dry-run` in 10.26.0; every earlier pnpm — including 10.18.x
|
|
758
|
+
lines consumers actually pin — rejects the flag with
|
|
759
|
+
`Unknown option: 'dry-run'`, and the slot failed on a manager checkride
|
|
760
|
+
has promised since 0.5.0 (reported by a real consumer on 10.18.1). On that
|
|
761
|
+
rejection checkride now falls back to a real pack into a temp destination
|
|
762
|
+
outside the repo (`pnpm pack --json --pack-destination <tmp>` — same
|
|
763
|
+
npm-shaped `files[].path` JSON, verified back to 10.18.1), with lifecycle
|
|
764
|
+
scripts suppressed the same way, and deletes the tarball afterward whether
|
|
765
|
+
the check passes or not. Capability fallback, not version sniffing, so it
|
|
766
|
+
keeps working wherever the flag is missing.
|
|
767
|
+
- **The default concurrency no longer collapses to 1 on a 2-core CI runner.**
|
|
768
|
+
`defaultConcurrency` reserved a core to keep the machine responsive — sound
|
|
769
|
+
on a laptop, inapplicable on a hosted runner with no human at it. A
|
|
770
|
+
standard GitHub-hosted runner reports 2 CPUs, so the pool was 1 and a
|
|
771
|
+
wave-scheduled config executed fully sequentially (measured on a real
|
|
772
|
+
consumer: 27.4s wall against 27.4s of summed check time — 1.00x), on
|
|
773
|
+
exactly the machine class the docs say to gate on. With `CI` set (every
|
|
774
|
+
provider sets it) no core is reserved, so that runner now gets a pool of 2.
|
|
775
|
+
The cap of 4, the `--concurrency` override, and `--bail`'s sequential
|
|
776
|
+
behavior are unchanged. How much a 2-wide pool on 2 cores buys depends on
|
|
777
|
+
the mix: checks dominated by process startup and file discovery overlap
|
|
778
|
+
well; two CPU-bound tools may be closer to a wash. It is never slower than
|
|
779
|
+
the serialized pool it replaces, and `order` waves now schedule as
|
|
780
|
+
documented. docs/ci.md explains the runner-size effect and how to read
|
|
781
|
+
`total_duration_ms` against the per-check sum.
|
|
782
|
+
- **The AGENTS.md stanza reports the configured gate, not the detected one.**
|
|
783
|
+
`init` and `agent-setup` derived the stanza's "Active checks in this repo"
|
|
784
|
+
line from the adoption inventory, which has detection semantics and never
|
|
785
|
+
reads `checkride.config.json` — so opt-in slots a config entry opts in
|
|
786
|
+
(`format`, `build`, …) and non-catalogue custom checks were silently missing
|
|
787
|
+
from the agent-facing contract while the gate ran them. The stanza now
|
|
788
|
+
derives from the same selection a default run makes (the pattern `doctor`
|
|
789
|
+
already used), so what it names and what `pnpm check` runs agree.
|
|
790
|
+
|
|
791
|
+
### Internal
|
|
792
|
+
|
|
793
|
+
- The design record for this release is
|
|
794
|
+
[docs/spec-agent-setup-upgrade.md](./docs/spec-agent-setup-upgrade.md): two
|
|
795
|
+
consumers' reports validated against source, with the measured pnpm version
|
|
796
|
+
boundaries. docs/getting-started.md's hard-gate section was rewritten for
|
|
797
|
+
the three-hook suite.
|
|
798
|
+
|
|
799
|
+
## [0.8.1] - 2026-07-28
|
|
800
|
+
|
|
801
|
+
### Fixed
|
|
802
|
+
|
|
803
|
+
- **A package-manager banner on stdout no longer fails the JSON-reading slots.**
|
|
804
|
+
pnpm verifies dependencies before `exec`/`run` and narrates it on **stdout**
|
|
805
|
+
(`Already up to date`, `Done in Xms using pnpm vN`) whenever no outer pnpm
|
|
806
|
+
process has already done so. That preamble landed ahead of the tool's own
|
|
807
|
+
JSON, so `checkride` invoked directly — `node dist/cli.js`, which is what the
|
|
808
|
+
bundled triage reader and any non-pnpm-wrapped call does — failed `dead`,
|
|
809
|
+
`dupes` and `health` with "fallow did not emit valid JSON" while the tools
|
|
810
|
+
themselves exited 0, and silently cost `lint`, `struct` and `attw` their JSON
|
|
811
|
+
artifacts. The same gate under `pnpm run check` passed, which is what made it
|
|
812
|
+
hard to see. Fixed at both ends: checkride now passes
|
|
813
|
+
`--config.verify-deps-before-run=false` ahead of `exec`/`run` under pnpm (the
|
|
814
|
+
only form that works — `--silent` does not suppress it, and after `exec` pnpm
|
|
815
|
+
reads it as the tool's argument), and every stdout-JSON parse tolerates up to
|
|
816
|
+
ten leading non-JSON lines, because a consumer's launcher is not checkride's
|
|
817
|
+
to pin. The `<slot>.json` artifact is written from the first JSON character
|
|
818
|
+
on, so it parses on its own; nothing inside the tool's output is altered.
|
|
819
|
+
- **`summary.json`'s `output_file` no longer names files that were never
|
|
820
|
+
written.** It was copied from the adapter's declaration, so any run where a
|
|
821
|
+
JSON-declaring tool emitted something else — a warning first, a crash, or the
|
|
822
|
+
banner above — left the summary pointing at a `<slot>.json` that did not
|
|
823
|
+
exist while the real bytes sat in `<slot>.stdout.txt` beside it. It is now
|
|
824
|
+
what the run actually wrote, and `null` when that is nothing (including for a
|
|
825
|
+
skipped check, which writes nothing at all). This is the field's documented
|
|
826
|
+
meaning; see [docs/contract.md](./docs/contract.md).
|
|
827
|
+
|
|
828
|
+
## [0.8.0] - 2026-07-28
|
|
829
|
+
|
|
830
|
+
### Added
|
|
831
|
+
|
|
832
|
+
- **A bundled Claude Code plugin, shipped from the package root.** The published
|
|
833
|
+
package now carries `.claude-plugin/plugin.json` and a `skills/` directory, so
|
|
834
|
+
`checkride` installs as a plugin from the same npm tarball you already depend
|
|
835
|
+
on (`/plugin marketplace add robmclarty/agent-tools`, then
|
|
836
|
+
`/plugin install checkride@robmclarty`). It is bundled rather than published
|
|
837
|
+
separately so the readers version in lockstep with the `.check/` contract they
|
|
838
|
+
parse; a test asserts the manifest's version equals `package.json`'s. Nothing
|
|
839
|
+
about the CLI, the exit codes or the `.check/` contract changes, installed or
|
|
840
|
+
not. See [docs/plugin.md](./docs/plugin.md).
|
|
841
|
+
- **`/checkride:check` — triage a red gate.** Runs the repo's own `check` script
|
|
842
|
+
(the definition of done, which may carry deliberate `--only`/`--skip`), branches
|
|
843
|
+
on the promised 0/1/2 exit split, and names one root cause plus what it is
|
|
844
|
+
deliberately not reading. It covers the contract corners a hand-read of
|
|
845
|
+
`summary.json` gets wrong — exit 2 versus exit 1, vacuous green, a narrow run's
|
|
846
|
+
green, `baselined` counts, `skipped` slots, `exit_code: -1`, an unexpected
|
|
847
|
+
`schema_version`, stale artifacts, and a red gate whose compound `check` script
|
|
848
|
+
died before checkride ran — then opens exactly one raw file. Artifacts are
|
|
849
|
+
measured, never opened: a green 17-slot run renders in about 2 kB.
|
|
850
|
+
- **`/checkride:qa` — read the quality signal.** Folds `mutation.json`,
|
|
851
|
+
`dead.json`, `dupes.json` and `health.json` into a report under 8 kB (2.2 MB of
|
|
852
|
+
mutation data becomes a ranked page), runs nothing at all, and opens with a
|
|
853
|
+
present / stale / not-opted-in ledger naming the command or config entry that
|
|
854
|
+
would produce each missing artifact — three of the four come from opt-in slots,
|
|
855
|
+
so partial data is the normal case.
|
|
856
|
+
- **Two dependency-free readers behind those skills**, shipped prebuilt in
|
|
857
|
+
`dist/` so a plugin install needs no build step, and runnable without Claude
|
|
858
|
+
Code: `node node_modules/checkride/dist/triage/cli.js` and
|
|
859
|
+
`node node_modules/checkride/dist/qa/cli.js`, each taking an optional repo
|
|
860
|
+
path. Both use `node:` builtins only. Every artifact read is bounded, dated
|
|
861
|
+
against the run that claims it (an artifact older than the run's start is
|
|
862
|
+
labelled stale with its age, never silently dropped), and resolved by the
|
|
863
|
+
documented `output_file` → `<slot>.json` → `<slot>.stdout.txt`/`.stderr.txt`
|
|
864
|
+
convention, which matters because `output_file` is null for 8 of this repo's
|
|
865
|
+
17 slots — `test` among them.
|
|
866
|
+
- **`examples/` — eight runnable projects, each demonstrating one thing.** They
|
|
867
|
+
are standalone packages rather than workspace members, so you `cd` in, install,
|
|
868
|
+
and run one without the rest of the repo: `agent-loop` (what an agent reads
|
|
869
|
+
when the gate is red), `existing-repo-baseline` (adopting checkride on a repo
|
|
870
|
+
that already has findings), `custom-checks` (the escape hatch — a bespoke
|
|
871
|
+
formatter ahead of the built-ins, `detect`-gated checks that stand down),
|
|
872
|
+
`shared-preset` (org-wide policy as one versioned package), `polyglot` (a
|
|
873
|
+
Python repo where the TypeScript built-ins stand down and ast-grep enforces
|
|
874
|
+
boundaries in Python), and `module-boundaries` / `dal-boundaries` /
|
|
875
|
+
`dal-boundaries-declarative` for boundary enforcement — the last expressing a
|
|
876
|
+
data-access policy with fallow zones and ast-grep rules alone, no custom script.
|
|
877
|
+
Each declares the exit code it expects and `test/e2e/examples.e2e.test.ts`
|
|
878
|
+
asserts it on every push, so an example that stops behaving as its README
|
|
879
|
+
claims fails CI.
|
|
880
|
+
|
|
881
|
+
### Changed
|
|
882
|
+
|
|
883
|
+
- **The AGENTS.md stanza written by `checkride init` and `checkride agent-setup`
|
|
884
|
+
names `/checkride:check`.** Exactly one added line; the prose procedure is
|
|
885
|
+
unchanged and still works standalone in a repo that never installs the plugin.
|
|
886
|
+
|
|
887
|
+
### Internal
|
|
888
|
+
|
|
889
|
+
- `/version` now folds an existing `## [Unreleased]` changelog section into the
|
|
890
|
+
new release heading instead of inserting above it.
|
|
891
|
+
|
|
892
|
+
## [0.7.0] - 2026-07-22
|
|
893
|
+
|
|
894
|
+
### Changed
|
|
895
|
+
|
|
896
|
+
- **checkride is now licensed [Apache-2.0](./LICENSE), previously MIT.** No code
|
|
897
|
+
or API changed; the terms you receive it under did. Apache-2.0 adds an express
|
|
898
|
+
patent grant and, via section 5, makes contributions inbound under the same
|
|
899
|
+
license without a CLA — the reason for the switch, ahead of accepting outside
|
|
900
|
+
contributions. Consumers redistributing checkride should note the section 4
|
|
901
|
+
conditions (include the license, mark modified files); there is deliberately
|
|
902
|
+
no `NOTICE` file, so nothing to propagate. `checkride init --license` still
|
|
903
|
+
defaults to `MIT` for scaffolded projects — that remains the user's choice and
|
|
904
|
+
is unaffected.
|
|
905
|
+
|
|
906
|
+
### Internal
|
|
907
|
+
|
|
908
|
+
- `CONTRIBUTING.md` gains a licensing section: how section 5 works, why an
|
|
909
|
+
employer may own contributions written on their time, and DCO sign-off via
|
|
910
|
+
`git commit -s`.
|
|
911
|
+
|
|
912
|
+
## [0.6.0] - 2026-07-21
|
|
913
|
+
|
|
914
|
+
### Added
|
|
915
|
+
|
|
916
|
+
- **`optIn` override to configure a slot without opting it into the default
|
|
917
|
+
run.** A slot entry (both the `{ use }` and custom `{ command }` forms) now
|
|
918
|
+
takes `optIn?: boolean`: `true` configures the slot but keeps it reachable
|
|
919
|
+
only via `--all`/`--include` (so giving `attw` a profile no longer drags
|
|
920
|
+
`attw`/`build`/`pack`/`smoke` into a plain `checkride` run), and `false`
|
|
921
|
+
forces a slot into the default run. Resolution-side only, so selection, the
|
|
922
|
+
vacuous-green warning, and `doctor` all adjust automatically.
|
|
923
|
+
- **Config-driven `exclude` and `allowlist` for the built-in `links` check.**
|
|
924
|
+
`exclude` adds directory names to skip on top of the built-in set, and
|
|
925
|
+
`allowlist` takes regex sources whose matching link targets count as valid
|
|
926
|
+
(for generated or illustrative markdown that never resolves on disk).
|
|
927
|
+
Patterns are compiled and validated at config load, so a bad one is a
|
|
928
|
+
friendly exit-2 error rather than a mid-run crash. Both are declared in the
|
|
929
|
+
JSON schema.
|
|
930
|
+
- **`profile` shortcut for the `attw` slot.** A `profile` string on the config
|
|
931
|
+
entry appends `--profile <name>` to attw's invocation instead of re-typing
|
|
932
|
+
the full args array (a no-op on other slots; a non-string is a friendly
|
|
933
|
+
config error).
|
|
934
|
+
|
|
935
|
+
### Changed
|
|
936
|
+
|
|
937
|
+
- **`publint` and `attw` stand down when their tool isn't installed.** Both
|
|
938
|
+
were always-available and hard-failed an `--all` run with "Command not found"
|
|
939
|
+
on a repo that never installed them; they now gate on their package
|
|
940
|
+
(`publint`, `@arethetypeswrong/cli`) and skip cleanly when absent, matching
|
|
941
|
+
checkride's skip-not-fail principle for opt-in slots. Naming one explicitly
|
|
942
|
+
in config still forces it to run.
|
|
943
|
+
|
|
944
|
+
### Fixed
|
|
945
|
+
|
|
946
|
+
- **A single duplicate fingerprint no longer disables baselining for a whole
|
|
947
|
+
fallow slot.** The baseline gate guarded against un-fingerprintable findings by
|
|
948
|
+
comparing the deduplicated key count to the raw issue count, but that
|
|
949
|
+
comparison could not tell a finding that produced *no* key apart from two
|
|
950
|
+
findings that produced the *same* key. One key collision (two `<arrow>`
|
|
951
|
+
functions in a file, or two symbol-less `unused_class_members`) made the slot
|
|
952
|
+
look untracked, so `checkride baseline` followed by `checkride` stayed red
|
|
953
|
+
forever. The gate now tracks an explicit un-keyed count instead, so a collision
|
|
954
|
+
merely coarsens the baseline (grandfathering one finding grandfathers its twin)
|
|
955
|
+
rather than disabling it, while a genuinely un-keyable finding still holds the
|
|
956
|
+
slot red.
|
|
957
|
+
- **Colliding fallow fingerprints are now disambiguated.** Anonymous health
|
|
958
|
+
functions (`<arrow>`, `<anonymous>`) and symbol-less dead-code findings
|
|
959
|
+
(`unused_class_members`) append their line and column, so a real sibling
|
|
960
|
+
finding can no longer be masked by a baseline captured for a different one.
|
|
961
|
+
Named functions and symbol-bearing findings keep their line-free keys.
|
|
962
|
+
|
|
963
|
+
This changes the fingerprint strings for the `dead` and `health` slots, which
|
|
964
|
+
**invalidates existing `checkride.baseline.json` entries for those slots**.
|
|
965
|
+
Re-run `checkride baseline` to recapture them.
|
|
966
|
+
|
|
967
|
+
### Internal
|
|
968
|
+
|
|
969
|
+
- **Cleared all `pnpm audit` advisories** via `pnpm-workspace.yaml` overrides:
|
|
970
|
+
`brace-expansion` (GHSA-3jxr-9vmj-r5cp) and `js-yaml` (GHSA-52cp-r559-cp3m)
|
|
971
|
+
were high, `qs` (GHSA-q8mj-m7cp-5q26) and `markdown-it` (GHSA-6v5v-wf23-fmfq)
|
|
972
|
+
moderate. Each is a transitive **dev** dependency (of stryker and
|
|
973
|
+
markdownlint-cli2), so none reached the published package; each override is
|
|
974
|
+
scoped to its advisory's vulnerable range and capped to the compatible major.
|
|
975
|
+
- Docs: added a deep-modules guide and a fleet-presets guide for org-wide
|
|
976
|
+
rollout, reframed swappable module boundaries as the default, dropped the
|
|
977
|
+
"TypeScript-only" framing from the README, homepage, and npm package
|
|
978
|
+
description, clarified the README for first-time readers, and pointed
|
|
979
|
+
`package.json` `homepage` at the GitHub Pages site.
|
|
980
|
+
|
|
981
|
+
## [0.5.3] - 2026-07-19
|
|
982
|
+
|
|
983
|
+
### Internal
|
|
984
|
+
|
|
985
|
+
- **Docs: `docs/ordering-in-practice.md`** — a worked guide to `order`
|
|
986
|
+
scheduling waves, now with a second example where waves encode a *dependency*
|
|
987
|
+
(`build` produces `dist/`; the publish bundle — `publint`, `attw`, `pack`,
|
|
988
|
+
`smoke`, `snippets-dist` — consumes it) rather than just a duration tier.
|
|
989
|
+
- Adopt explicit ordering waves in checkride's own `checkride.config.json`, so
|
|
990
|
+
the gate config states its execution plan instead of leaning on adapter
|
|
991
|
+
defaults and pool arithmetic.
|
|
992
|
+
- CI: bump `upload-pages-artifact` to v5 and drop the Node 20 pages runtime.
|
|
993
|
+
- Expand the `cspell` dictionary with words the docs introduced.
|
|
994
|
+
|
|
7
995
|
## [0.5.2] - 2026-07-19
|
|
8
996
|
|
|
9
997
|
### Internal
|
|
@@ -465,6 +1453,21 @@ The first real release. (`0.0.0` was a name-claim placeholder.)
|
|
|
465
1453
|
- Flags: `--only`, `--skip`, `--bail`, `--json`, `--changed`, `--all`,
|
|
466
1454
|
`--include`.
|
|
467
1455
|
|
|
1456
|
+
[0.10.2]: https://www.npmjs.com/package/checkride/v/0.10.2
|
|
1457
|
+
[0.10.1]: https://www.npmjs.com/package/checkride/v/0.10.1
|
|
1458
|
+
[0.10.0]: https://www.npmjs.com/package/checkride/v/0.10.0
|
|
1459
|
+
[0.9.6]: https://www.npmjs.com/package/checkride/v/0.9.6
|
|
1460
|
+
[0.9.5]: https://www.npmjs.com/package/checkride/v/0.9.5
|
|
1461
|
+
[0.9.4]: https://www.npmjs.com/package/checkride/v/0.9.4
|
|
1462
|
+
[0.9.3]: https://www.npmjs.com/package/checkride/v/0.9.3
|
|
1463
|
+
[0.9.2]: https://www.npmjs.com/package/checkride/v/0.9.2
|
|
1464
|
+
[0.9.1]: https://www.npmjs.com/package/checkride/v/0.9.1
|
|
1465
|
+
[0.9.0]: https://www.npmjs.com/package/checkride/v/0.9.0
|
|
1466
|
+
[0.8.1]: https://www.npmjs.com/package/checkride/v/0.8.1
|
|
1467
|
+
[0.8.0]: https://www.npmjs.com/package/checkride/v/0.8.0
|
|
1468
|
+
[0.7.0]: https://www.npmjs.com/package/checkride/v/0.7.0
|
|
1469
|
+
[0.6.0]: https://www.npmjs.com/package/checkride/v/0.6.0
|
|
1470
|
+
[0.5.3]: https://www.npmjs.com/package/checkride/v/0.5.3
|
|
468
1471
|
[0.5.2]: https://www.npmjs.com/package/checkride/v/0.5.2
|
|
469
1472
|
[0.5.1]: https://www.npmjs.com/package/checkride/v/0.5.1
|
|
470
1473
|
[0.5.0]: https://www.npmjs.com/package/checkride/v/0.5.0
|