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.
Files changed (278) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +15 -13
  3. package/dist/cli-core.js +19 -2
  4. package/dist/lib/check.js +46 -13
  5. package/dist/lib/intent.js +1 -1
  6. package/dist/lib/latch.js +3 -3
  7. package/dist/lib/reentry.js +70 -0
  8. package/dist/verbs/agent.js +2 -2
  9. package/dist/verbs/doctor.js +4 -4
  10. package/dist/verbs/finish.js +1 -1
  11. package/dist/verbs/recover.js +297 -0
  12. package/dist/verbs/revert.js +2 -2
  13. package/dist/verbs/spike.js +8 -2
  14. package/hooks/post-edit.sh +4 -4
  15. package/hooks/pre-bash-commit.sh +3 -3
  16. package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
  17. package/node_modules/checkride/CHANGELOG.md +1003 -0
  18. package/node_modules/checkride/LICENSE +201 -21
  19. package/node_modules/checkride/README.md +171 -30
  20. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
  21. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
  22. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
  23. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
  24. package/node_modules/checkride/dist/adapters.d.ts +18 -6
  25. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  26. package/node_modules/checkride/dist/adapters.js +42 -23
  27. package/node_modules/checkride/dist/adapters.js.map +1 -1
  28. package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
  29. package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
  30. package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
  31. package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
  32. package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
  33. package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
  34. package/node_modules/checkride/dist/agent-setup/command.js +77 -0
  35. package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
  36. package/node_modules/checkride/dist/agent-setup/cursor.d.ts +61 -0
  37. package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
  38. package/node_modules/checkride/dist/agent-setup/cursor.js +189 -0
  39. package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
  40. package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
  41. package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
  42. package/node_modules/checkride/dist/agent-setup/files.js +80 -0
  43. package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
  44. package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
  45. package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
  46. package/node_modules/checkride/dist/agent-setup/harness.js +84 -0
  47. package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
  48. package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
  49. package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
  50. package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
  51. package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
  52. package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
  53. package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
  54. package/node_modules/checkride/dist/agent-setup/index.js +9 -4
  55. package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
  56. package/node_modules/checkride/dist/agent-setup/scripts.d.ts +58 -0
  57. package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
  58. package/node_modules/checkride/dist/agent-setup/scripts.js +299 -0
  59. package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
  60. package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
  61. package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
  62. package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
  63. package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
  64. package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
  65. package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
  66. package/node_modules/checkride/dist/artifacts/configured.js +45 -0
  67. package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
  68. package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
  69. package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
  70. package/node_modules/checkride/dist/artifacts/format.js +56 -0
  71. package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
  72. package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
  73. package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
  74. package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
  75. package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
  76. package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
  77. package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
  78. package/node_modules/checkride/dist/artifacts/index.js +19 -0
  79. package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
  80. package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
  81. package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
  82. package/node_modules/checkride/dist/artifacts/json.js +47 -0
  83. package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
  84. package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
  85. package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
  86. package/node_modules/checkride/dist/artifacts/raw.js +91 -0
  87. package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
  88. package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
  89. package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
  90. package/node_modules/checkride/dist/artifacts/summary.js +108 -0
  91. package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
  92. package/node_modules/checkride/dist/atomic.d.ts +16 -1
  93. package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
  94. package/node_modules/checkride/dist/atomic.js +25 -3
  95. package/node_modules/checkride/dist/atomic.js.map +1 -1
  96. package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
  97. package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
  98. package/node_modules/checkride/dist/baseline/fallow.js +161 -58
  99. package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
  100. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +7 -7
  101. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  102. package/node_modules/checkride/dist/baseline/fingerprint.js +16 -19
  103. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  104. package/node_modules/checkride/dist/baseline/index.d.ts +4 -4
  105. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  106. package/node_modules/checkride/dist/baseline/index.js +4 -4
  107. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  108. package/node_modules/checkride/dist/baseline/store.d.ts +12 -4
  109. package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
  110. package/node_modules/checkride/dist/baseline/store.js +16 -9
  111. package/node_modules/checkride/dist/baseline/store.js.map +1 -1
  112. package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
  113. package/node_modules/checkride/dist/baseline-command.js +7 -7
  114. package/node_modules/checkride/dist/baseline-command.js.map +1 -1
  115. package/node_modules/checkride/dist/cli.d.ts +17 -4
  116. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  117. package/node_modules/checkride/dist/cli.js +259 -18
  118. package/node_modules/checkride/dist/cli.js.map +1 -1
  119. package/node_modules/checkride/dist/config.d.ts +84 -8
  120. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  121. package/node_modules/checkride/dist/config.js +122 -18
  122. package/node_modules/checkride/dist/config.js.map +1 -1
  123. package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
  124. package/node_modules/checkride/dist/digest/digest.js +5 -5
  125. package/node_modules/checkride/dist/digest/index.d.ts +2 -2
  126. package/node_modules/checkride/dist/digest/index.js +2 -2
  127. package/node_modules/checkride/dist/doctor.d.ts +25 -6
  128. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  129. package/node_modules/checkride/dist/doctor.js +193 -27
  130. package/node_modules/checkride/dist/doctor.js.map +1 -1
  131. package/node_modules/checkride/dist/gate.d.ts +135 -0
  132. package/node_modules/checkride/dist/gate.d.ts.map +1 -0
  133. package/node_modules/checkride/dist/gate.js +490 -0
  134. package/node_modules/checkride/dist/gate.js.map +1 -0
  135. package/node_modules/checkride/dist/init.d.ts +72 -12
  136. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  137. package/node_modules/checkride/dist/init.js +324 -81
  138. package/node_modules/checkride/dist/init.js.map +1 -1
  139. package/node_modules/checkride/dist/json.d.ts +21 -0
  140. package/node_modules/checkride/dist/json.d.ts.map +1 -0
  141. package/node_modules/checkride/dist/json.js +21 -0
  142. package/node_modules/checkride/dist/json.js.map +1 -0
  143. package/node_modules/checkride/dist/links.d.ts +31 -5
  144. package/node_modules/checkride/dist/links.d.ts.map +1 -1
  145. package/node_modules/checkride/dist/links.js +141 -32
  146. package/node_modules/checkride/dist/links.js.map +1 -1
  147. package/node_modules/checkride/dist/node-pin.d.ts +129 -0
  148. package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
  149. package/node_modules/checkride/dist/node-pin.js +211 -0
  150. package/node_modules/checkride/dist/node-pin.js.map +1 -0
  151. package/node_modules/checkride/dist/orchestrator.d.ts +40 -10
  152. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  153. package/node_modules/checkride/dist/orchestrator.js +196 -102
  154. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  155. package/node_modules/checkride/dist/pack.d.ts +8 -9
  156. package/node_modules/checkride/dist/pack.d.ts.map +1 -1
  157. package/node_modules/checkride/dist/pack.js +53 -17
  158. package/node_modules/checkride/dist/pack.js.map +1 -1
  159. package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
  160. package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
  161. package/node_modules/checkride/dist/pm/detect.js +6 -1
  162. package/node_modules/checkride/dist/pm/detect.js.map +1 -1
  163. package/node_modules/checkride/dist/pm/index.d.ts +6 -2
  164. package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
  165. package/node_modules/checkride/dist/pm/index.js +6 -2
  166. package/node_modules/checkride/dist/pm/index.js.map +1 -1
  167. package/node_modules/checkride/dist/pm/launch.d.ts +51 -0
  168. package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
  169. package/node_modules/checkride/dist/pm/launch.js +92 -0
  170. package/node_modules/checkride/dist/pm/launch.js.map +1 -0
  171. package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
  172. package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
  173. package/node_modules/checkride/dist/pm/tools.js +124 -0
  174. package/node_modules/checkride/dist/pm/tools.js.map +1 -0
  175. package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
  176. package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
  177. package/node_modules/checkride/dist/pm/translate.js +78 -18
  178. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  179. package/node_modules/checkride/dist/proc.d.ts +30 -0
  180. package/node_modules/checkride/dist/proc.d.ts.map +1 -0
  181. package/node_modules/checkride/dist/proc.js +42 -0
  182. package/node_modules/checkride/dist/proc.js.map +1 -0
  183. package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
  184. package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
  185. package/node_modules/checkride/dist/qa/cli.js +24 -0
  186. package/node_modules/checkride/dist/qa/cli.js.map +1 -0
  187. package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
  188. package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
  189. package/node_modules/checkride/dist/qa/dead.js +88 -0
  190. package/node_modules/checkride/dist/qa/dead.js.map +1 -0
  191. package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
  192. package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
  193. package/node_modules/checkride/dist/qa/dupes.js +62 -0
  194. package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
  195. package/node_modules/checkride/dist/qa/health.d.ts +85 -0
  196. package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
  197. package/node_modules/checkride/dist/qa/health.js +109 -0
  198. package/node_modules/checkride/dist/qa/health.js.map +1 -0
  199. package/node_modules/checkride/dist/qa/index.d.ts +23 -0
  200. package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
  201. package/node_modules/checkride/dist/qa/index.js +22 -0
  202. package/node_modules/checkride/dist/qa/index.js.map +1 -0
  203. package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
  204. package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
  205. package/node_modules/checkride/dist/qa/mutation.js +195 -0
  206. package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
  207. package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
  208. package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
  209. package/node_modules/checkride/dist/qa/qa.js +158 -0
  210. package/node_modules/checkride/dist/qa/qa.js.map +1 -0
  211. package/node_modules/checkride/dist/qa/read.d.ts +40 -0
  212. package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
  213. package/node_modules/checkride/dist/qa/read.js +47 -0
  214. package/node_modules/checkride/dist/qa/read.js.map +1 -0
  215. package/node_modules/checkride/dist/qa/render.d.ts +26 -0
  216. package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
  217. package/node_modules/checkride/dist/qa/render.js +310 -0
  218. package/node_modules/checkride/dist/qa/render.js.map +1 -0
  219. package/node_modules/checkride/dist/security.d.ts +45 -0
  220. package/node_modules/checkride/dist/security.d.ts.map +1 -0
  221. package/node_modules/checkride/dist/security.js +86 -0
  222. package/node_modules/checkride/dist/security.js.map +1 -0
  223. package/node_modules/checkride/dist/smoke.d.ts +5 -5
  224. package/node_modules/checkride/dist/smoke.js +5 -5
  225. package/node_modules/checkride/dist/snippets.d.ts +26 -29
  226. package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
  227. package/node_modules/checkride/dist/snippets.js +26 -30
  228. package/node_modules/checkride/dist/snippets.js.map +1 -1
  229. package/node_modules/checkride/dist/tool-json.d.ts +33 -0
  230. package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
  231. package/node_modules/checkride/dist/tool-json.js +58 -0
  232. package/node_modules/checkride/dist/tool-json.js.map +1 -0
  233. package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
  234. package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
  235. package/node_modules/checkride/dist/triage/cli.js +26 -0
  236. package/node_modules/checkride/dist/triage/cli.js.map +1 -0
  237. package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
  238. package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
  239. package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
  240. package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
  241. package/node_modules/checkride/dist/triage/env.d.ts +54 -0
  242. package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
  243. package/node_modules/checkride/dist/triage/env.js +81 -0
  244. package/node_modules/checkride/dist/triage/env.js.map +1 -0
  245. package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
  246. package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
  247. package/node_modules/checkride/dist/triage/gate.js +121 -0
  248. package/node_modules/checkride/dist/triage/gate.js.map +1 -0
  249. package/node_modules/checkride/dist/triage/index.d.ts +22 -0
  250. package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
  251. package/node_modules/checkride/dist/triage/index.js +20 -0
  252. package/node_modules/checkride/dist/triage/index.js.map +1 -0
  253. package/node_modules/checkride/dist/triage/render.d.ts +19 -0
  254. package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
  255. package/node_modules/checkride/dist/triage/render.js +419 -0
  256. package/node_modules/checkride/dist/triage/render.js.map +1 -0
  257. package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
  258. package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
  259. package/node_modules/checkride/dist/triage/triage.js +131 -0
  260. package/node_modules/checkride/dist/triage/triage.js.map +1 -0
  261. package/node_modules/checkride/package.json +17 -15
  262. package/node_modules/checkride/schema/checkride.config.schema.json +62 -5
  263. package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
  264. package/node_modules/checkride/skills/check/SKILL.md +257 -0
  265. package/node_modules/checkride/skills/qa/SKILL.md +231 -0
  266. package/node_modules/checkride/templates/shared/cspell.json +1 -0
  267. package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
  268. package/package.json +2 -2
  269. package/skills/build/SKILL.md +23 -19
  270. package/skills/finish/SKILL.md +3 -1
  271. package/skills/harvest/SKILL.md +2 -1
  272. package/skills/plan/SKILL.md +8 -7
  273. package/skills/recover/SKILL.md +28 -0
  274. package/skills/refine/SKILL.md +5 -4
  275. package/skills/spike/SKILL.md +1 -1
  276. package/skills/step/SKILL.md +4 -4
  277. package/skills/verify/SKILL.md +19 -14
  278. package/templates/intent.md +8 -7
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Assembling the triage report — everything the skill reasons over, and nothing
3
+ * it would have to open a file to learn.
4
+ *
5
+ * The shape is deliberately a *model*, not text: the run's provenance, one row
6
+ * per slot with its raw output located and sized, what the run did and did not
7
+ * cover, and the traps (vacuous green, narrow green, baselined findings, stale
8
+ * artifacts) each surfaced as its own field. Rendering is `./render.ts`'s job.
9
+ *
10
+ * Two facts drive almost all of it. First, the summary is an index, not
11
+ * evidence: it is overwritten by every run, so triage runs the gate itself and
12
+ * then checks that the summary on disk is the one that run wrote. Second, sizes
13
+ * are the product: `mutation.json` is 2.3 MB in this repo, and a reader that
14
+ * reports contents instead of bytes has spent the context it exists to save.
15
+ */
16
+ import { join } from 'node:path';
17
+ import { CHECK_DIR, configuredSlots, listArtifacts, readSummary, resolveRawOutput, runWindowStart, statArtifact } from '../artifacts/index.js';
18
+ import { DIGEST_FILE } from '../digest/index.js';
19
+ import { foldDoctor } from './doctor-fold.js';
20
+ import { isHarnessProblem, runGate } from './gate.js';
21
+ /**
22
+ * mtime can be second-granular on older filesystems, so a summary written a
23
+ * moment after the gate started could otherwise read as older than it. The
24
+ * tolerance is far smaller than any real gate run, so it cannot launder a
25
+ * genuinely stale summary into a fresh one.
26
+ */
27
+ const MTIME_TOLERANCE_MS = 1000;
28
+ function statusOf(check) {
29
+ if (check.skipped === true)
30
+ return 'skipped';
31
+ return check.ok ? 'pass' : 'fail';
32
+ }
33
+ async function buildRow(checkDir, check, windowStart) {
34
+ // A skipped slot produced no output; any file under its name is a leftover
35
+ // from an earlier run, and offering it would be the stale read this module
36
+ // exists to prevent.
37
+ const raw = check.skipped === true ? null : await resolveRawOutput(checkDir, check.name, check.output_file, windowStart);
38
+ return {
39
+ slot: check.name,
40
+ adapter: check.adapter,
41
+ status: statusOf(check),
42
+ exitCode: check.exit_code,
43
+ durationMs: check.duration_ms,
44
+ baselined: check.baselined ?? null,
45
+ reason: check.reason ?? null,
46
+ raw,
47
+ };
48
+ }
49
+ function buildCoverage(cwd, rows) {
50
+ const configured = configuredSlots(cwd);
51
+ const present = new Set(rows.map((r) => r.slot));
52
+ return {
53
+ ran: rows.filter((r) => r.status !== 'skipped').map((r) => r.slot),
54
+ skipped: rows.filter((r) => r.status === 'skipped').map((r) => r.slot),
55
+ configured,
56
+ uncovered: (configured ?? []).filter((slot) => !present.has(slot)),
57
+ };
58
+ }
59
+ /** Whether the summary on disk is the one the gate just wrote. */
60
+ function writtenByThisRun(summary, gate) {
61
+ if (summary.state === 'missing' || summary.state === 'unreadable')
62
+ return null;
63
+ if (gate.verdict === 'not-run')
64
+ return null;
65
+ return summary.mtimeMs >= gate.startedMs - MTIME_TOLERANCE_MS;
66
+ }
67
+ /** Every stale file the run measured, deduped — a stale read is the classic wrong answer. */
68
+ function collectStale(rows, digest) {
69
+ const measured = [...rows.flatMap((r) => r.raw?.candidates ?? []), ...(digest ? [digest] : [])];
70
+ const stale = new Map();
71
+ for (const file of measured) {
72
+ if (file.freshness.state === 'stale')
73
+ stale.set(file.path, file);
74
+ }
75
+ return [...stale.values()];
76
+ }
77
+ /**
78
+ * When the summary is gone, an anchor for judging what in `.check/` is a
79
+ * leftover. The contract window comes from the summary's own `timestamp`, so an
80
+ * unparseable summary leaves none — but the reader *ran the gate* and knows
81
+ * when it started, which is a strictly better anchor than giving up and
82
+ * labelling every file `?`. Not applied to the parsed path, where the
83
+ * documented derivation in `../artifacts/freshness.ts` is the contract.
84
+ */
85
+ function gateAnchor(gate) {
86
+ return gate.verdict === 'not-run' ? null : gate.startedMs - MTIME_TOLERANCE_MS;
87
+ }
88
+ /** Read everything the run left behind, measuring and never opening. */
89
+ async function readArtifacts(cwd, gate) {
90
+ const checkDir = join(cwd, CHECK_DIR);
91
+ const summary = await readSummary(cwd);
92
+ const parsed = summary.state === 'ok';
93
+ const windowStart = parsed ? runWindowStart(summary.summary) : null;
94
+ const anchor = parsed ? windowStart : gateAnchor(gate);
95
+ const checks = summary.state === 'ok' ? summary.summary.checks : [];
96
+ const [rows, digest, listing] = await Promise.all([
97
+ Promise.all(checks.map((check) => buildRow(checkDir, check, windowStart))),
98
+ statArtifact(checkDir, DIGEST_FILE, anchor),
99
+ parsed ? Promise.resolve(null) : listArtifacts(checkDir, anchor),
100
+ ]);
101
+ return { summary, windowStart, rows, digest, checkDir: listing };
102
+ }
103
+ function isVacuous(summary) {
104
+ return summary.state === 'ok' && summary.summary.ok && summary.summary.checks_run === 0;
105
+ }
106
+ /**
107
+ * Run the gate, then read what it wrote. On the broken-harness branch `doctor`
108
+ * is folded in first, because there the environment is the finding and the
109
+ * slot table is not evidence of anything.
110
+ */
111
+ export async function triage(cwd, env) {
112
+ const gate = await runGate(cwd, env);
113
+ const doctor = isHarnessProblem(gate.verdict) ? await foldDoctor(cwd, env) : null;
114
+ const { summary, windowStart, rows, digest, checkDir } = await readArtifacts(cwd, gate);
115
+ return {
116
+ cwd,
117
+ gate,
118
+ doctor,
119
+ summary,
120
+ fromThisRun: writtenByThisRun(summary, gate),
121
+ windowStart,
122
+ rows,
123
+ failing: rows.filter((r) => r.status === 'fail'),
124
+ coverage: buildCoverage(cwd, rows),
125
+ vacuous: isVacuous(summary),
126
+ digest,
127
+ stale: collectStale(rows, digest),
128
+ checkDir,
129
+ };
130
+ }
131
+ //# sourceMappingURL=triage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage.js","sourceRoot":"","sources":["../../src/triage/triage.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "checkride",
3
- "version": "0.5.2",
4
- "description": "An agent harness for TypeScript repositories: one command that is the definition of done, plus mechanically enforced module boundaries.",
5
- "license": "MIT",
3
+ "version": "0.10.2",
4
+ "description": "An agent harness for code repositories: one command that is the definition of done, plus mechanically enforced module boundaries. TypeScript-first, not TypeScript-only.",
5
+ "license": "Apache-2.0",
6
6
  "author": "Rob McLarty",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/robmclarty/checkride.git"
10
10
  },
11
- "homepage": "https://github.com/robmclarty/checkride#readme",
11
+ "homepage": "https://robmclarty.github.io/checkride/",
12
12
  "bugs": "https://github.com/robmclarty/checkride/issues",
13
13
  "type": "module",
14
14
  "keywords": [
@@ -40,6 +40,8 @@
40
40
  "dist",
41
41
  "templates",
42
42
  "schema",
43
+ ".claude-plugin",
44
+ "skills",
43
45
  "README.md",
44
46
  "CHANGELOG.md",
45
47
  "LICENSE"
@@ -62,20 +64,20 @@
62
64
  "prepublishOnly": "tsc --build && vitest run"
63
65
  },
64
66
  "devDependencies": {
65
- "@arethetypeswrong/cli": "0.18.4",
66
- "@ast-grep/cli": "0.42.1",
67
+ "@arethetypeswrong/cli": "0.18.5",
68
+ "@ast-grep/cli": "0.45.0",
67
69
  "@stryker-mutator/core": "9.6.1",
68
70
  "@stryker-mutator/vitest-runner": "9.6.1",
69
- "@types/node": "22.20.0",
70
- "@vitest/coverage-v8": "4.1.5",
71
+ "@types/node": "22.20.1",
72
+ "@vitest/coverage-v8": "4.1.10",
71
73
  "ajv": "8.20.0",
72
- "cspell": "10.0.0",
73
- "fallow": "3.5.0",
74
- "markdownlint-cli2": "0.22.1",
75
- "oxlint": "1.61.0",
76
- "oxlint-tsgolint": "0.21.1",
77
- "publint": "0.3.21",
74
+ "cspell": "10.0.1",
75
+ "fallow": "3.9.1",
76
+ "markdownlint-cli2": "0.23.1",
77
+ "oxlint": "1.74.0",
78
+ "oxlint-tsgolint": "0.25.0",
79
+ "publint": "0.3.22",
78
80
  "typescript": "6.0.3",
79
- "vitest": "4.1.5"
81
+ "vitest": "4.1.10"
80
82
  }
81
83
  }
@@ -10,6 +10,7 @@
10
10
  "type": "string",
11
11
  "description": "URL of this JSON Schema, for editor validation and autocompletion."
12
12
  },
13
+ "note": { "$ref": "#/definitions/note" },
13
14
  "extends": {
14
15
  "description": "Preset(s) to inherit — a file path (./base.json) or package specifier (@acme/preset), or an array of them. Bases merge left-to-right and the local config wins over all; objects deep-merge, arrays and scalars replace (arrays are not concatenated).",
15
16
  "oneOf": [
@@ -22,15 +23,41 @@
22
23
  "minimum": 0,
23
24
  "description": "Default per-check timeout in seconds. Unset, checks fall back to the built-in 600s cap; 0 here or on a check disables the cap."
24
25
  },
26
+ "gate": {
27
+ "type": "object",
28
+ "additionalProperties": false,
29
+ "description": "What the agent stop-gate runs, when the full check is too slow to pay for on every turn. The gate then reports the profile in its verdict, so a narrowed green is never mistaken for a full one — keep the full check binding somewhere else (a commit hook, CI).",
30
+ "properties": {
31
+ "note": { "$ref": "#/definitions/note" },
32
+ "only": {
33
+ "type": "array",
34
+ "items": { "type": "string" },
35
+ "description": "Run only these slots in the gate."
36
+ },
37
+ "skip": {
38
+ "type": "array",
39
+ "items": { "type": "string" },
40
+ "description": "Run everything but these slots in the gate."
41
+ },
42
+ "changed": {
43
+ "type": "boolean",
44
+ "description": "Affected-only mode for the gate, as --changed."
45
+ }
46
+ }
47
+ },
25
48
  "checks": {
26
49
  "type": "object",
27
- "description": "Per-slot configuration. A key that matches a catalogue slot (types, format, lint, struct, dead, dupes, health, test, docs, links, spell, mutation, security, publint, attw) configures that slot; any other key is a custom check that runs alongside the catalogue. Naming an opt-in slot (format, dupes, health, mutation, security, publint, attw) here also opts it into the default run.",
50
+ "description": "Per-slot configuration. A key that matches a catalogue slot (types, format, lint, struct, dead, dupes, health, test, docs, links, spell, mutation, security, publint, attw) configures that slot; any other key is a custom check that runs alongside the catalogue. Naming an opt-in slot (format, dupes, health, mutation, security, publint, attw) here also opts it into the default run — set optIn:true on the entry to configure it without opting in (run it only with --all/--include).",
28
51
  "additionalProperties": {
29
52
  "$ref": "#/definitions/slotConfig"
30
53
  }
31
54
  }
32
55
  },
33
56
  "definitions": {
57
+ "note": {
58
+ "type": "string",
59
+ "description": "Internal documentation for whoever reads this config next. Never rendered in the CLI output or written to .check/summary.json — use description for the user-facing text a run prints beside the check."
60
+ },
34
61
  "slotConfig": {
35
62
  "description": "How to fill a slot: an adapter name, false to disable it, an adapter with overrides, or a custom check.",
36
63
  "oneOf": [
@@ -58,12 +85,34 @@
58
85
  },
59
86
  "command": { "type": "string" },
60
87
  "args": { "type": "array", "items": { "type": "string" } },
61
- "description": { "type": "string" },
88
+ "description": {
89
+ "type": "string",
90
+ "description": "User-facing text rendered beside this check in the CLI output. For maintainer commentary that should not render, use note."
91
+ },
92
+ "note": { "$ref": "#/definitions/note" },
62
93
  "outputFile": { "type": ["string", "null"] },
63
94
  "changedArgs": { "type": "array", "items": { "type": "string" } },
64
95
  "fixArgs": { "type": "array", "items": { "type": "string" } },
65
96
  "timeout": { "type": "number", "minimum": 0 },
66
- "order": { "$ref": "#/definitions/order" }
97
+ "order": { "$ref": "#/definitions/order" },
98
+ "optIn": {
99
+ "type": "boolean",
100
+ "description": "Override the slot's opt-in status. true configures the slot without opting it into the default run (reach it with --all/--include); false forces it into the default run."
101
+ },
102
+ "exclude": {
103
+ "type": "array",
104
+ "items": { "type": "string" },
105
+ "description": "Links slot only: extra directory names to skip while walking for markdown, on top of the built-in exclude set (e.g. docs, research, .ridgeline)."
106
+ },
107
+ "allowlist": {
108
+ "type": "array",
109
+ "items": { "type": "string" },
110
+ "description": "Links slot only: regex sources for link targets to treat as always valid (deliberately illustrative links that never resolve on disk)."
111
+ },
112
+ "profile": {
113
+ "type": "string",
114
+ "description": "attw slot only: append --profile <name> (e.g. esm-only) to the attw invocation — a shortcut for retyping the full args."
115
+ }
67
116
  }
68
117
  },
69
118
  "order": {
@@ -84,7 +133,11 @@
84
133
  "description": "Executable to spawn."
85
134
  },
86
135
  "args": { "type": "array", "items": { "type": "string" } },
87
- "description": { "type": "string" },
136
+ "description": {
137
+ "type": "string",
138
+ "description": "User-facing text rendered beside this check in the CLI output. For maintainer commentary that should not render, use note."
139
+ },
140
+ "note": { "$ref": "#/definitions/note" },
88
141
  "outputFile": { "type": ["string", "null"] },
89
142
  "changedArgs": { "type": "array", "items": { "type": "string" } },
90
143
  "fixArgs": { "type": "array", "items": { "type": "string" } },
@@ -95,7 +148,11 @@
95
148
  "items": { "type": "string" },
96
149
  "description": "Marker files that gate a non-catalogue custom check: it runs only when at least one listed file exists, and is skipped otherwise. Ignored on an entry that fills a catalogue slot."
97
150
  },
98
- "order": { "$ref": "#/definitions/order" }
151
+ "order": { "$ref": "#/definitions/order" },
152
+ "optIn": {
153
+ "type": "boolean",
154
+ "description": "true holds this custom check out of the default run — it runs only under --all/--include <name>. Absent runs it by default."
155
+ }
99
156
  }
100
157
  }
101
158
  }
@@ -27,7 +27,7 @@
27
27
  "total_duration_ms": {
28
28
  "type": "number",
29
29
  "minimum": 0,
30
- "description": "Sum of all check durations in milliseconds."
30
+ "description": "Wall-clock duration of the whole execution phase, in milliseconds — not the sum of the per-check durations, which is larger whenever checks ran concurrently. The two are equal only when execution was sequential (including any --bail run). The run STARTED at timestamp - total_duration_ms; readers use that to judge whether an artifact belongs to this run."
31
31
  },
32
32
  "checks": {
33
33
  "type": "array",
@@ -0,0 +1,257 @@
1
+ ---
2
+ name: check
3
+ description: Triage a red checkride gate and name one root cause. Use whenever `pnpm check` (or `npm/yarn/bun run check`) fails, a checkride gate is red, CI is failing on the gate, or you need to know what to fix first. Runs the gate itself, reads a bounded report instead of megabytes of `.check/` artifacts, and covers the contract corners a plain summary read gets wrong — exit 2 vs exit 1, vacuous green, narrow green, baselined findings, skipped slots, spawn failures and stale artifacts.
4
+ argument-hint: "[repo-path]"
5
+ allowed-tools: Read, Grep, Glob, Bash(node *), Bash(pnpm *), Bash(npm run *), Bash(yarn *), Bash(bun run *), Bash(npx checkride*), Bash(wc *)
6
+ ---
7
+
8
+ # checkride: triage a red gate
9
+
10
+ checkride's gate is the definition of done: exit 0 means the work is complete,
11
+ anything else means it is not. This skill is the procedure for the "anything
12
+ else" — it turns a red gate into **one named root cause** plus an explicit list
13
+ of what it is deliberately not reading yet.
14
+
15
+ It replaces reading `.check/summary.json` by hand. That habit is wrong in two
16
+ directions at once: it is unbounded (`mutation.json` runs to 2.3 MB in
17
+ checkride's own repo, `test.json` to 650 KB, and opening one spends the context
18
+ you need for the fix), and it is credulous (every run overwrites `summary.json`,
19
+ so an `ok: true` on disk may describe three of seventeen slots from fourteen
20
+ minutes ago).
21
+
22
+ ## 1. Run the reader
23
+
24
+ ```bash
25
+ pnpm exec checkride triage
26
+ ```
27
+
28
+ Substitute the repo's package manager (`npx checkride`, `yarn checkride`, `bunx
29
+ checkride`). Pass a repo path as the one optional argument to triage somewhere
30
+ other than the current directory.
31
+
32
+ If checkride is not a dependency of the target repo, run the copy the plugin
33
+ ships instead: `node "${CLAUDE_PLUGIN_ROOT}/dist/triage/cli.js"`. Both entry
34
+ points render the identical report.
35
+
36
+ What it does, in order: runs the repo's **own** `check` script (that script is
37
+ the definition of done and may carry deliberate `--only` / `--skip` / `--changed`
38
+ that a direct `checkride` invocation would bypass), branches on the promised
39
+ 0/1/2 exit split, then reads what the run wrote. On exit 2 it also folds in
40
+ `checkride doctor --json`, so the broken-harness branch arrives with its
41
+ diagnosis attached.
42
+
43
+ Every artifact is measured, never opened. A green 17-slot run renders in about
44
+ 2 KB. It exits 0 whenever it produced a report — the gate's verdict is in the
45
+ Markdown, not in the process status, so a red repo never looks like a broken
46
+ reader.
47
+
48
+ **Do not open any `.check/` file before this report tells you which one.** That
49
+ is the whole point: the report is the index, and you open exactly one thing from
50
+ it.
51
+
52
+ ## 2. Read the header before you read any finding
53
+
54
+ The report opens with four lines. They are not preamble — three of the five ways
55
+ to be confidently wrong about a checkride run are visible only here.
56
+
57
+ ```text
58
+ - gate: `pnpm run check` → exit 0 (20.5s)
59
+ - script: `tsc --build && node dist/cli.js`
60
+ - summary: `.check/summary.json` — `schema_version` 1, written by this run, 19.8s
61
+ - covered: 17 slot(s) ran, 0 skipped
62
+ ```
63
+
64
+ - **`script`** — the repo's literal `check` script. If it contains `--only`,
65
+ `--skip` or `--changed`, this gate narrows the run on purpose and a green is
66
+ green *for those slots only*. The report says so under `coverage`.
67
+ - **`summary`** — provenance. `written by this run` is the good case. **`left by
68
+ an EARLIER run`** means the table below describes something else entirely and
69
+ every row in it is suspect. `schema_version` anything but 1 means **stop**: the
70
+ reader is pinned to schema 1, additive-only guarantees hold within a version
71
+ and promise nothing across one, so guessing is how you invent a finding. **`NOT
72
+ written by checkride`** (no `schema_version` at all) means this repo's gate is
73
+ a different tool — see below.
74
+ - **`covered`** — how much was verified. Compare it against the `coverage`
75
+ section, which names the slots `checkride.config.json` configures but this run
76
+ never touched. **`unknown`** is not `0`: it means no summary could be parsed,
77
+ so nothing about coverage was measured either way.
78
+
79
+ **Always open your report with the covered-slot count and the run's age.** A
80
+ finding without its coverage is an assertion about the whole repo built from a
81
+ fraction of it.
82
+
83
+ ## 3. Branch on the verdict
84
+
85
+ | verdict | what it means | what to do |
86
+ | --- | --- | --- |
87
+ | **harness broken** | exit 2 — checkride reserves it for "the harness broke or was misused" | Read the `doctor` section and the `gate output` tail. **No check result from this run is evidence of anything.** Report *what doctor found* — the named requirement, what was found, what was expected — never just "the harness is broken". |
88
+ | **off-contract exit** | not 0, 1 or 2 (a 127, a signal death, a wrapper that died before checkride ran) | Same posture as exit 2. Read the `gate output` tail: something in the `check` script failed around checkride, not inside it. |
89
+ | **red** | exit 1 — at least one check failed | Go to step 4. This is the normal path. |
90
+ | **red, but no slot explains it** | exit 1 with an empty `failing slots` section | The failure happened *outside* checkride. Read the `gate output` tail, which the report includes on this branch. See below — do not triage the table. |
91
+ | **red, and the summary cannot be read** | exit 1, and `.check/summary.json` exists but this reader cannot parse it | There is **no table at all**, and its absence is not evidence. Read the `gate output` tail and the `.check/ contents` listing; report *only* what those show. See below. |
92
+ | **green, with no index** | exit 0, same unparseable summary | The repo's own gate passed — report that, and nothing more. Attaching a slot count or a "clean" claim to it is inventing coverage. |
93
+ | **vacuous green** | exit 0 with `checks_run: 0` | A failure wearing a pass: nothing was verified. Find out why no check was selected (a typo'd `--only`, an empty config) before believing anything. Treat it as red. |
94
+ | **green, but narrow** | exit 0 over a subset | Say which slots were covered and which were not. Do not report the work as done on this evidence. |
95
+ | **green** | exit 0, full coverage | Still check the caveats for `baselined` and `skipped` before saying clean — see step 7. |
96
+ | **gate not run** | no `check` script in this repo | Nothing here defines done. `checkride init` adds it. Anything in `.check/` is from whatever last wrote it. |
97
+
98
+ ### Red with an empty `failing slots` section
99
+
100
+ The verdict reads **`red, but no slot explains it`**: the gate exited 1 but
101
+ checkride never ran, so nothing in the table came from this run. `check` scripts
102
+ are compound — `tsc --build && node dist/cli.js` is the standard shape
103
+ `checkride init` writes — and when the command *before* checkride dies, the `&&`
104
+ short-circuits and no summary is written.
105
+
106
+ The `summary` header line is the second tell: **`left by an EARLIER run`**. The
107
+ table below it describes something else entirely, and the coverage numbers belong
108
+ to that other run too.
109
+
110
+ **The evidence is already in the report.** On this branch it renders a `gate
111
+ output` section — a capped tail of both captured streams — because that is the
112
+ only record of the failure that exists. Triage it there rather than re-running
113
+ anything.
114
+
115
+ Read **both** blocks. Which stream carries the diagnosis is the failing tool's
116
+ choice, not a convention you can rely on: `tsc` reports errors on **stdout**
117
+ while `stderr` holds nothing but pnpm's `$ tsc --build && node dist/cli.js`
118
+ command echo. A near-empty `stderr` block is not "no output".
119
+
120
+ Only if both blocks are absent, re-run the failing prefix directly (`pnpm exec
121
+ tsc --build` for the shape above) and triage what it prints. Either way: do not
122
+ report any slot in the table as passing.
123
+
124
+ ### A summary this reader cannot parse
125
+
126
+ Distinct from the branch above, and the distinction is the whole point. There
127
+ the summary is **absent** — checkride never ran, so it wrote nothing. Here a
128
+ `.check/summary.json` exists and cannot be parsed, which says *nothing at all*
129
+ about what ran. The verdict reads **`red, and the summary cannot be read`** (or
130
+ **`green, with no index`**), and the header names which of three it is:
131
+
132
+ - **`NOT written by checkride`** — no `schema_version`. The repo's gate is a
133
+ homegrown script that writes a checkride-shaped `.check/`. Its artifacts follow
134
+ no contract, so their names and freshness are all you can trust about them.
135
+ - **`schema_version` is N, not 1** — checkride wrote it, but a newer one.
136
+ - **unreadable** — truncated, or not JSON.
137
+
138
+ On this branch the report gives you two things and no table: the `gate output`
139
+ tail, and a **`.check/ contents`** listing of every file with its size and age.
140
+ `fresh` there means written after the gate started; `stale` means a leftover that
141
+ describes no part of this run. Triage from those, and open at most one file.
142
+
143
+ What you must not do is fill the gap. `covered: unknown` is not `covered: 0`;
144
+ `baselined` and `skipped` are both **unknown**, and because a baselined slot
145
+ *passes* and a skipped one reports nothing, "no caveats raised" and "no caveats
146
+ could be raised" look identical. No slot can be reported as passing on this run.
147
+
148
+ ## 4. Order the failures — this is the judgment
149
+
150
+ The report lists failing slots in **pipeline order** and says explicitly that
151
+ this is not importance order. Re-ranking them is the part no script can do.
152
+
153
+ When several slots fail at once, most of them are usually one cause and its
154
+ shadows. The cheap tell: **does the failing slot consume something an earlier
155
+ failing slot produces?**
156
+
157
+ | when this fails | these failures are usually its shadow | because |
158
+ | --- | --- | --- |
159
+ | `types` | `build`, `typecheck-tests`, `test` | `build` is normally the same `tsc` invocation, and a type error is the same defect surfacing at runtime |
160
+ | `types` on a *syntax* error | also `lint`, `struct`, `dead`, `dupes`, `health` | unparseable source breaks every tool that parses it, which a merely semantic type error does not |
161
+ | `build` | `publint`, `attw`, `pack`, `smoke`, `snippets` | they all inspect `dist/`, which is now stale or absent |
162
+ | `test` | nothing | no slot consumes a test result |
163
+
164
+ Two rules that outrank the table:
165
+
166
+ 1. **A slot with `exit_code: -1` is not a finding.** It failed to spawn or timed
167
+ out and reported nothing. That is a harness problem wearing a failure's
168
+ clothes, and it comes first.
169
+ 2. **The prose lane is independent.** `docs`, `spell` and `links` never share a
170
+ cause with the code lane. `types` red *and* `spell` red is two pieces of work,
171
+ not one — say so rather than folding them together.
172
+
173
+ Worked shape: `types`, `build` and `test` all red, 41 test failures. That is one
174
+ root cause — the type error — and two shadows. Naming the 41 test failures is
175
+ noise; fixing the type error is the work. Say which one you picked and why the
176
+ others are downstream.
177
+
178
+ ## 5. If everything failing is auto-fixable, say so before proposing edits
179
+
180
+ `checkride fix` runs every active adapter's fix command. The slots it can fix
181
+ are `format` (prettier, biome), `lint` (oxlint, biome, eslint), `docs`
182
+ (markdownlint) and `dead` (fallow, knip). Nothing else — `types`, `test`,
183
+ `spell`, `links`, `struct` and the packaging slots have no fix command.
184
+
185
+ - **Every failing slot is in that set** → propose `pnpm exec checkride fix`
186
+ followed by a re-run, before writing a single hand edit. Hand-editing what a
187
+ formatter would rewrite is wasted work.
188
+ - **Only some are** → the root cause is elsewhere. Name it first; mention `fix`
189
+ as cleanup for the rest.
190
+
191
+ Two caveats to state when you propose it: `fix` writes to files across the whole
192
+ repo, not just the failing slots, so it wants a clean git tree or a reviewed
193
+ diff; and `dead`'s fix *deletes* code it believes is unused — read that part of
194
+ the diff rather than trusting it.
195
+
196
+ ## 6. Open exactly one file
197
+
198
+ The report's `failing slots` section names each failure's raw output with its
199
+ size. Open the one belonging to your chosen root cause, and nothing else.
200
+
201
+ - If `.check/digest.md` is listed and **fresh**, read it first — it is
202
+ checkride's own bounded excerpt of this run's failing slots, capped at 8 KB.
203
+ Its existence always means the run that wrote it had failures. If it is
204
+ labelled stale, it belongs to an earlier run; do not read it as this one's.
205
+ - Prefer the file the report chose. When a slot has both a `.json` and a
206
+ `.stdout.txt`, the reader already picked the small one (`test.stdout.txt` is
207
+ 6 KB against `test.json`'s 650 KB) and the `(+1)` marks the one it passed over.
208
+ - **If the chosen file gives you a count but not a location, open the `(+1)`.**
209
+ The reader prefers stdout because checkride's own stream discipline puts machine
210
+ output there and progress on stderr — but not every tool obeys it.
211
+ markdownlint-cli2 inverts it outright: `docs.stdout.txt` says
212
+ `Summary: 1 issue in 1 file` while `docs.stderr.txt` carries the
213
+ `file:line:rule` you actually need. A summary line is not a finding; go get the
214
+ finding.
215
+ - **Check the size before opening.** Anything past ~50 KB gets `Grep`, not
216
+ `Read` — search for the error text rather than loading the file.
217
+ - If a failing slot's raw output is `—`, the tool wrote nothing the reader could
218
+ locate. Do not report "no output": re-run that slot alone
219
+ (`pnpm exec checkride --only <slot>`) and read what it prints.
220
+
221
+ ## 7. Report
222
+
223
+ Lead with provenance, then one cause, then what you left closed:
224
+
225
+ - **Gate** — the verdict, the covered-slot count, and the run's age.
226
+ - **Root cause** — one slot, one sentence, grounded in the file you opened.
227
+ - **Downstream, not read** — the failing slots you believe are shadows of it,
228
+ and the one-line reason. Naming these is as much the product as the cause is.
229
+ - **Separate work** — failing slots that are genuinely independent (usually the
230
+ prose lane).
231
+ - **Masked** — every caveat the report raised, because each one means a green is
232
+ less green than it looks:
233
+ - `baselined: N` — that many current findings are grandfathered by
234
+ `checkride.baseline.json`. **A baselined slot passing is not clean, and you
235
+ must say the count rather than reporting clean.** Never add to a baseline to
236
+ make a check pass.
237
+ - `skipped` plus its reason — that slot verified nothing this run.
238
+ - stale artifacts — leftovers from an earlier run, not this one's output.
239
+ - uncovered slots — configured in `checkride.config.json`, absent from this run.
240
+ - **Next** — the fix command, or the specific hand edit.
241
+
242
+ Do not produce a fixed-size list of findings. One cause is the target; two is
243
+ fine when the lanes are genuinely independent; five means you re-ranked nothing.
244
+
245
+ ## What this skill does not do
246
+
247
+ - **It does not fix.** It runs the gate and reads. Proposing `checkride fix` or a
248
+ hand edit is the deliverable; applying it is the next request.
249
+ - **It does not normalize tool output.** The raw file is the truth and the
250
+ summary is only an index — that is checkride's thesis, and reformatting a
251
+ tool's own bytes into something prettier is how a triage loses the detail that
252
+ mattered.
253
+ - **It does not read `.check/` contents on spec.** Sizes and locations first,
254
+ then exactly one file. If you find yourself opening a second artifact before
255
+ naming a cause, that is exploration, not triage.
256
+ - **It does not touch the baseline.** `checkride baseline` hides findings
257
+ permanently; that is the human's call, never a triage step.