plumbbob 0.9.3 → 0.10.0

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 (320) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +92 -89
  3. package/dist/cli-core.js +25 -8
  4. package/dist/cli.js +1 -1
  5. package/dist/lib/agents.js +26 -26
  6. package/dist/lib/buildlog.js +11 -11
  7. package/dist/lib/buildlogsync.js +8 -8
  8. package/dist/lib/check.js +55 -22
  9. package/dist/lib/commitbody.js +72 -0
  10. package/dist/lib/commitmsg.js +7 -7
  11. package/dist/lib/git.js +44 -15
  12. package/dist/lib/intent.js +21 -20
  13. package/dist/lib/latch.js +19 -19
  14. package/dist/lib/orient.js +36 -17
  15. package/dist/lib/reentry.js +70 -0
  16. package/dist/lib/settings.js +16 -16
  17. package/dist/lib/sidecar.js +54 -54
  18. package/dist/lib/templates.js +1 -1
  19. package/dist/verbs/agent.js +27 -27
  20. package/dist/verbs/build.js +9 -9
  21. package/dist/verbs/check.js +6 -6
  22. package/dist/verbs/checkpoint.js +48 -47
  23. package/dist/verbs/doctor.js +31 -30
  24. package/dist/verbs/finish.js +28 -41
  25. package/dist/verbs/handoff.js +9 -8
  26. package/dist/verbs/init.js +9 -9
  27. package/dist/verbs/park.js +3 -3
  28. package/dist/verbs/recover.js +297 -0
  29. package/dist/verbs/revert.js +13 -13
  30. package/dist/verbs/spike.js +25 -19
  31. package/dist/verbs/start.js +49 -19
  32. package/dist/verbs/status.js +8 -8
  33. package/dist/verbs/turn.js +16 -16
  34. package/dist/verbs/use.js +4 -4
  35. package/hooks/post-edit.sh +4 -4
  36. package/hooks/pre-bash-commit.sh +3 -3
  37. package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
  38. package/node_modules/checkride/CHANGELOG.md +1404 -0
  39. package/node_modules/checkride/LICENSE +201 -21
  40. package/node_modules/checkride/README.md +197 -38
  41. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
  42. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
  43. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
  44. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
  45. package/node_modules/checkride/dist/adapters.d.ts +30 -10
  46. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  47. package/node_modules/checkride/dist/adapters.js +62 -23
  48. package/node_modules/checkride/dist/adapters.js.map +1 -1
  49. package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
  50. package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
  51. package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
  52. package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
  53. package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
  54. package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
  55. package/node_modules/checkride/dist/agent-setup/command.js +77 -0
  56. package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
  57. package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
  58. package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
  59. package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
  60. package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
  61. package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
  62. package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
  63. package/node_modules/checkride/dist/agent-setup/files.js +80 -0
  64. package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
  65. package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
  66. package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
  67. package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
  68. package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
  69. package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
  70. package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
  71. package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
  72. package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
  73. package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
  74. package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
  75. package/node_modules/checkride/dist/agent-setup/index.js +9 -4
  76. package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
  77. package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
  78. package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
  79. package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
  80. package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
  81. package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
  82. package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
  83. package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
  84. package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
  85. package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
  86. package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
  87. package/node_modules/checkride/dist/artifacts/configured.js +45 -0
  88. package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
  89. package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
  90. package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
  91. package/node_modules/checkride/dist/artifacts/format.js +56 -0
  92. package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
  93. package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
  94. package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
  95. package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
  96. package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
  97. package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
  98. package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
  99. package/node_modules/checkride/dist/artifacts/index.js +19 -0
  100. package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
  101. package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
  102. package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
  103. package/node_modules/checkride/dist/artifacts/json.js +47 -0
  104. package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
  105. package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
  106. package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
  107. package/node_modules/checkride/dist/artifacts/raw.js +91 -0
  108. package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
  109. package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
  110. package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
  111. package/node_modules/checkride/dist/artifacts/summary.js +108 -0
  112. package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
  113. package/node_modules/checkride/dist/atomic.d.ts +16 -1
  114. package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
  115. package/node_modules/checkride/dist/atomic.js +25 -3
  116. package/node_modules/checkride/dist/atomic.js.map +1 -1
  117. package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
  118. package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
  119. package/node_modules/checkride/dist/baseline/fallow.js +163 -60
  120. package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
  121. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
  122. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  123. package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
  124. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  125. package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
  126. package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
  127. package/node_modules/checkride/dist/baseline/history.js +232 -0
  128. package/node_modules/checkride/dist/baseline/history.js.map +1 -0
  129. package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
  130. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  131. package/node_modules/checkride/dist/baseline/index.js +5 -4
  132. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  133. package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
  134. package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
  135. package/node_modules/checkride/dist/baseline/store.js +65 -24
  136. package/node_modules/checkride/dist/baseline/store.js.map +1 -1
  137. package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
  138. package/node_modules/checkride/dist/baseline-command.js +7 -7
  139. package/node_modules/checkride/dist/baseline-command.js.map +1 -1
  140. package/node_modules/checkride/dist/cli.d.ts +21 -4
  141. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  142. package/node_modules/checkride/dist/cli.js +350 -36
  143. package/node_modules/checkride/dist/cli.js.map +1 -1
  144. package/node_modules/checkride/dist/config.d.ts +114 -9
  145. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  146. package/node_modules/checkride/dist/config.js +142 -23
  147. package/node_modules/checkride/dist/config.js.map +1 -1
  148. package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
  149. package/node_modules/checkride/dist/digest/digest.js +6 -6
  150. package/node_modules/checkride/dist/digest/index.d.ts +2 -2
  151. package/node_modules/checkride/dist/digest/index.js +2 -2
  152. package/node_modules/checkride/dist/doctor.d.ts +27 -6
  153. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  154. package/node_modules/checkride/dist/doctor.js +231 -27
  155. package/node_modules/checkride/dist/doctor.js.map +1 -1
  156. package/node_modules/checkride/dist/gate.d.ts +180 -0
  157. package/node_modules/checkride/dist/gate.d.ts.map +1 -0
  158. package/node_modules/checkride/dist/gate.js +617 -0
  159. package/node_modules/checkride/dist/gate.js.map +1 -0
  160. package/node_modules/checkride/dist/init.d.ts +79 -12
  161. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  162. package/node_modules/checkride/dist/init.js +385 -81
  163. package/node_modules/checkride/dist/init.js.map +1 -1
  164. package/node_modules/checkride/dist/json.d.ts +21 -0
  165. package/node_modules/checkride/dist/json.d.ts.map +1 -0
  166. package/node_modules/checkride/dist/json.js +21 -0
  167. package/node_modules/checkride/dist/json.js.map +1 -0
  168. package/node_modules/checkride/dist/links.d.ts +31 -5
  169. package/node_modules/checkride/dist/links.d.ts.map +1 -1
  170. package/node_modules/checkride/dist/links.js +141 -32
  171. package/node_modules/checkride/dist/links.js.map +1 -1
  172. package/node_modules/checkride/dist/node-pin.d.ts +129 -0
  173. package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
  174. package/node_modules/checkride/dist/node-pin.js +211 -0
  175. package/node_modules/checkride/dist/node-pin.js.map +1 -0
  176. package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
  177. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  178. package/node_modules/checkride/dist/orchestrator.js +312 -113
  179. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  180. package/node_modules/checkride/dist/pack.d.ts +8 -9
  181. package/node_modules/checkride/dist/pack.d.ts.map +1 -1
  182. package/node_modules/checkride/dist/pack.js +53 -17
  183. package/node_modules/checkride/dist/pack.js.map +1 -1
  184. package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
  185. package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
  186. package/node_modules/checkride/dist/pm/detect.js +6 -1
  187. package/node_modules/checkride/dist/pm/detect.js.map +1 -1
  188. package/node_modules/checkride/dist/pm/index.d.ts +7 -2
  189. package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
  190. package/node_modules/checkride/dist/pm/index.js +6 -2
  191. package/node_modules/checkride/dist/pm/index.js.map +1 -1
  192. package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
  193. package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
  194. package/node_modules/checkride/dist/pm/launch.js +108 -0
  195. package/node_modules/checkride/dist/pm/launch.js.map +1 -0
  196. package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
  197. package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
  198. package/node_modules/checkride/dist/pm/tools.js +124 -0
  199. package/node_modules/checkride/dist/pm/tools.js.map +1 -0
  200. package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
  201. package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
  202. package/node_modules/checkride/dist/pm/translate.js +78 -18
  203. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  204. package/node_modules/checkride/dist/proc.d.ts +30 -0
  205. package/node_modules/checkride/dist/proc.d.ts.map +1 -0
  206. package/node_modules/checkride/dist/proc.js +42 -0
  207. package/node_modules/checkride/dist/proc.js.map +1 -0
  208. package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
  209. package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
  210. package/node_modules/checkride/dist/qa/cli.js +24 -0
  211. package/node_modules/checkride/dist/qa/cli.js.map +1 -0
  212. package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
  213. package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
  214. package/node_modules/checkride/dist/qa/dead.js +88 -0
  215. package/node_modules/checkride/dist/qa/dead.js.map +1 -0
  216. package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
  217. package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
  218. package/node_modules/checkride/dist/qa/dupes.js +62 -0
  219. package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
  220. package/node_modules/checkride/dist/qa/health.d.ts +85 -0
  221. package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
  222. package/node_modules/checkride/dist/qa/health.js +109 -0
  223. package/node_modules/checkride/dist/qa/health.js.map +1 -0
  224. package/node_modules/checkride/dist/qa/index.d.ts +23 -0
  225. package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
  226. package/node_modules/checkride/dist/qa/index.js +22 -0
  227. package/node_modules/checkride/dist/qa/index.js.map +1 -0
  228. package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
  229. package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
  230. package/node_modules/checkride/dist/qa/mutation.js +195 -0
  231. package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
  232. package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
  233. package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
  234. package/node_modules/checkride/dist/qa/qa.js +158 -0
  235. package/node_modules/checkride/dist/qa/qa.js.map +1 -0
  236. package/node_modules/checkride/dist/qa/read.d.ts +40 -0
  237. package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
  238. package/node_modules/checkride/dist/qa/read.js +47 -0
  239. package/node_modules/checkride/dist/qa/read.js.map +1 -0
  240. package/node_modules/checkride/dist/qa/render.d.ts +26 -0
  241. package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
  242. package/node_modules/checkride/dist/qa/render.js +310 -0
  243. package/node_modules/checkride/dist/qa/render.js.map +1 -0
  244. package/node_modules/checkride/dist/recover.d.ts +48 -0
  245. package/node_modules/checkride/dist/recover.d.ts.map +1 -0
  246. package/node_modules/checkride/dist/recover.js +220 -0
  247. package/node_modules/checkride/dist/recover.js.map +1 -0
  248. package/node_modules/checkride/dist/security.d.ts +45 -0
  249. package/node_modules/checkride/dist/security.d.ts.map +1 -0
  250. package/node_modules/checkride/dist/security.js +86 -0
  251. package/node_modules/checkride/dist/security.js.map +1 -0
  252. package/node_modules/checkride/dist/smoke.d.ts +6 -6
  253. package/node_modules/checkride/dist/smoke.js +6 -6
  254. package/node_modules/checkride/dist/snippets.d.ts +27 -30
  255. package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
  256. package/node_modules/checkride/dist/snippets.js +26 -30
  257. package/node_modules/checkride/dist/snippets.js.map +1 -1
  258. package/node_modules/checkride/dist/tool-json.d.ts +33 -0
  259. package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
  260. package/node_modules/checkride/dist/tool-json.js +58 -0
  261. package/node_modules/checkride/dist/tool-json.js.map +1 -0
  262. package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
  263. package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
  264. package/node_modules/checkride/dist/triage/cli.js +26 -0
  265. package/node_modules/checkride/dist/triage/cli.js.map +1 -0
  266. package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
  267. package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
  268. package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
  269. package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
  270. package/node_modules/checkride/dist/triage/env.d.ts +54 -0
  271. package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
  272. package/node_modules/checkride/dist/triage/env.js +81 -0
  273. package/node_modules/checkride/dist/triage/env.js.map +1 -0
  274. package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
  275. package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
  276. package/node_modules/checkride/dist/triage/gate.js +121 -0
  277. package/node_modules/checkride/dist/triage/gate.js.map +1 -0
  278. package/node_modules/checkride/dist/triage/index.d.ts +22 -0
  279. package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
  280. package/node_modules/checkride/dist/triage/index.js +20 -0
  281. package/node_modules/checkride/dist/triage/index.js.map +1 -0
  282. package/node_modules/checkride/dist/triage/render.d.ts +19 -0
  283. package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
  284. package/node_modules/checkride/dist/triage/render.js +419 -0
  285. package/node_modules/checkride/dist/triage/render.js.map +1 -0
  286. package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
  287. package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
  288. package/node_modules/checkride/dist/triage/triage.js +131 -0
  289. package/node_modules/checkride/dist/triage/triage.js.map +1 -0
  290. package/node_modules/checkride/package.json +18 -15
  291. package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
  292. package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
  293. package/node_modules/checkride/skills/check/SKILL.md +265 -0
  294. package/node_modules/checkride/skills/qa/SKILL.md +231 -0
  295. package/node_modules/checkride/templates/shared/cspell.json +1 -0
  296. package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
  297. package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
  298. package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
  299. package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
  300. package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
  301. package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
  302. package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
  303. package/node_modules/checkride/templates/shared/vale.ini +34 -0
  304. package/package.json +10 -9
  305. package/skills/build/SKILL.md +85 -76
  306. package/skills/doctor/SKILL.md +11 -11
  307. package/skills/finish/SKILL.md +21 -18
  308. package/skills/harvest/SKILL.md +13 -11
  309. package/skills/park/SKILL.md +10 -10
  310. package/skills/plan/SKILL.md +69 -59
  311. package/skills/recover/SKILL.md +28 -0
  312. package/skills/refine/SKILL.md +21 -18
  313. package/skills/revert/SKILL.md +6 -6
  314. package/skills/spike/SKILL.md +9 -9
  315. package/skills/status/SKILL.md +5 -5
  316. package/skills/step/SKILL.md +20 -15
  317. package/skills/verify/SKILL.md +56 -46
  318. package/templates/build-log.md +7 -7
  319. package/templates/intent.md +52 -41
  320. package/templates/spike-report.md +10 -10
@@ -0,0 +1,617 @@
1
+ /**
2
+ * `checkride gate` — the stop-gate verdict, harness-neutral.
3
+ *
4
+ * Every agent harness offers some "the turn is ending" hook, and checkride's
5
+ * gate hangs off it: run the repo's `check` script, and while it is red, refuse
6
+ * to let the turn finish. What differs between harnesses is only the *wire
7
+ * format* of that refusal — Claude Code reads stderr and blocks on exit 2,
8
+ * Cursor reads a `followup_message` on stdout and submits it as the next turn.
9
+ * The decision itself (is it dirty, is it green, which artifact should the agent
10
+ * read) is identical, so it lives here once and the generated hook scripts
11
+ * become two-line adapters that name their harness.
12
+ *
13
+ * That split is the point. Before this command, the whole body was duplicated
14
+ * shell inside `.claude/hooks/checkride-gate.sh`, untestable outside an e2e run
15
+ * and impossible to share with a second harness.
16
+ *
17
+ * The gate answers three verdicts, not two. Green and red are the pipeline's;
18
+ * the third is **could not run**, for a package manager that refused to start
19
+ * the check script at all — an `engines.node` pin the hook's Node does not
20
+ * satisfy is the common cause, and it exits non-zero exactly like a failing
21
+ * test. Reading that as red produced a permanent red no code change could clear,
22
+ * pointing at a `.check/summary.json` no run had written. So it names the cause
23
+ * instead of naming an artifact. See `./pm/launch.ts` for the classification and
24
+ * `./node-pin.ts` for the alignment that avoids it.
25
+ *
26
+ * Whether that third verdict *blocks* turns on one question: can the turn clear
27
+ * it? A missing `check` script can be fixed by the agent holding the edit tools,
28
+ * so the gate blocks and the agent fixes it. A Node the harness never put on
29
+ * `PATH` cannot, so the gate says so and stands down (see
30
+ * {@link reportStandDown}). Blocking on the second only re-asks an agent that
31
+ * has no lever to pull, which is not gating — it is a loop, and the way out of
32
+ * a loop is turning the gate off.
33
+ *
34
+ * Not to be confused with `../triage/gate.ts`, which also runs the `check`
35
+ * script: that one runs it *verbatim* as a reader's preflight and classifies the
36
+ * result for a report. This one appends `--strict --digest` because it IS the
37
+ * gate, and its output is a verdict for the harness, not prose for a human.
38
+ */
39
+ import { spawn } from 'node:child_process';
40
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
41
+ import { join } from 'node:path';
42
+ import { formatDuration, readSummary } from './artifacts/index.js';
43
+ import { loadConfig } from './config.js';
44
+ import { DIGEST_FILE } from './digest/index.js';
45
+ import { alignNode, NODE_BIN_VAR, realPinEnv, withNodeBin } from './node-pin.js';
46
+ import { detectPackageManager, launchRefusal, runScript, SPAWN_FAILED_MARKER, } from './pm/index.js';
47
+ /**
48
+ * The edit marker: touched by the harness's "a file changed" hook, checked here
49
+ * under `--if-dirty`, and cleared by a green gate. Dot-named so it can never
50
+ * collide with a slot's `<slot>.json`/`<slot>.stdout.txt` artifacts, which the
51
+ * orchestrator deletes per slot before a re-run — the marker must survive every
52
+ * run.
53
+ *
54
+ * It lives here, not in `agent-setup`, because the gate is what reads and clears
55
+ * it; the hook writers import it from this module to build the touch command.
56
+ */
57
+ export const DIRTY_MARKER = '.check/.dirty';
58
+ /**
59
+ * Whether a follow-up has already been auto-submitted this conversation: `1` if
60
+ * so, `0` if not. The generated stop-hook script reads the harness's stop payload
61
+ * and exports it; the hook writers import the name from here to spell the export.
62
+ *
63
+ * It exists for exactly one decision. Under Cursor the only channel that reaches
64
+ * a *user* is `followup_message`, and that field **submits a new turn** — so a
65
+ * stand-down spending it on every turn is the unbounded loop a stand-down exists
66
+ * to avoid. `0` means the budget of one has not been spent yet. checkride cannot
67
+ * work this out alone: the stop payload goes to the hook script, never to this
68
+ * process. See {@link reportStandDown}.
69
+ *
70
+ * **Absence is not `0`.** A script generated before this variable existed exports
71
+ * neither value, and reading absence as "go ahead" would loop under precisely the
72
+ * pairing checkride cannot detect — a new binary, an old script, and the
73
+ * `loop_limit: null` the gate's own hook entry asks for. So absence keeps the
74
+ * older and quieter behavior, and only an explicit `0` unlocks the message.
75
+ */
76
+ export const GATE_RETRY_ENV = 'CHECKRIDE_GATE_RETRY';
77
+ /** Where the gate points a red agent when `--digest` wrote nothing. */
78
+ const SUMMARY_PATH = '.check/summary.json';
79
+ /**
80
+ * Cursor's own hook config. It lives here rather than in `agent-setup/cursor.ts`
81
+ * for the same reason {@link DIRTY_MARKER} does: the gate *reads* it (see
82
+ * {@link deferredToCursor}), and the writer importing from the gate is the
83
+ * direction that has no cycle. `agent-setup/cursor.ts` re-exports it.
84
+ */
85
+ export const CURSOR_HOOKS_FILE = '.cursor/hooks.json';
86
+ /**
87
+ * What a registered checkride gate looks like inside a harness's hook config.
88
+ * Both harnesses name the script the same, so finding it in Cursor's config is
89
+ * enough to know Cursor has a native gate of its own.
90
+ */
91
+ const GATE_SCRIPT_SENTINEL = 'checkride-gate.sh';
92
+ /** Every harness whose stop-hook protocol the gate can speak. */
93
+ export const HARNESS_NAMES = ['claude', 'cursor'];
94
+ /** Script name every checkride repo exposes; `checkride init` writes it. */
95
+ const CHECK_SCRIPT = 'check';
96
+ const spawnForward = (command, args, opts) => new Promise((resolve) => {
97
+ const proc = spawn(command, [...args], {
98
+ cwd: opts.cwd,
99
+ stdio: ['ignore', 'pipe', 'pipe'],
100
+ env: { ...opts.env, FORCE_COLOR: '0', NO_COLOR: '1' },
101
+ });
102
+ proc.stdout.setEncoding('utf8');
103
+ proc.stderr.setEncoding('utf8');
104
+ proc.stdout.on('data', (chunk) => opts.stderr.write(chunk));
105
+ proc.stderr.on('data', (chunk) => opts.stderr.write(chunk));
106
+ // A gate that cannot spawn its package manager is not a crash: the harness
107
+ // gets a verdict it can act on rather than a stack trace. The message goes
108
+ // to the stream so it is both visible and classifiable — a spawn failure
109
+ // that printed nothing at all was reported as a red with no explanation.
110
+ proc.on('error', (err) => {
111
+ opts.stderr.write(`${SPAWN_FAILED_MARKER} \`${command}\`: ${err.message}\n`);
112
+ resolve(null);
113
+ });
114
+ proc.on('close', (code) => resolve(code));
115
+ });
116
+ /**
117
+ * How much of the child's output to keep for {@link launchRefusal}.
118
+ *
119
+ * The **head**, not the tail: a package manager that refuses to launch says so
120
+ * before anything else runs, so the evidence is always in the first bytes. A
121
+ * check that fails after this much output is a pipeline red, which needs no
122
+ * classification.
123
+ */
124
+ const CAPTURE_LIMIT = 8 * 1024;
125
+ /** An `Out` that forwards everything and remembers the beginning. */
126
+ function recording(out) {
127
+ let kept = '';
128
+ return {
129
+ write: (chunk) => {
130
+ if (kept.length < CAPTURE_LIMIT)
131
+ kept += chunk;
132
+ return out.write(chunk);
133
+ },
134
+ text: () => kept,
135
+ };
136
+ }
137
+ /**
138
+ * True when this `--harness claude` invocation is really Cursor running the
139
+ * repo's `.claude/settings.json` hooks, *and* Cursor has a native checkride gate
140
+ * of its own that will run for the same turn.
141
+ *
142
+ * Cursor loads third-party (Claude Code) hook configs when "Include third-party
143
+ * Plugins, Skills, and other configs" is on, maps `Stop` onto `stop`, and — the
144
+ * load-bearing sentence — runs **all** matching hooks from **every** source. A
145
+ * repo wired for both harnesses therefore fires two full pipelines into one
146
+ * `.check/` directory, concurrently and with nothing to serialize them, where
147
+ * the orchestrator clears each slot's artifacts before re-running it.
148
+ *
149
+ * So one of the two has to stand down, and it is this one: under Cursor the
150
+ * native `.cursor/hooks.json` gate is the only one that can answer in Cursor's
151
+ * protocol, which makes it authoritative there.
152
+ *
153
+ * Deliberately narrow — it defers only when a Cursor gate is *registered*, not
154
+ * merely when Cursor is running, so the failure mode of a stale environment
155
+ * variable is a duplicate run (today's behavior) rather than no gate at all.
156
+ */
157
+ function deferredToCursor(cwd, env) {
158
+ if (!env['CURSOR_PROJECT_DIR'])
159
+ return false;
160
+ try {
161
+ return readFileSync(join(cwd, CURSOR_HOOKS_FILE), 'utf8').includes(GATE_SCRIPT_SENTINEL);
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ /**
168
+ * The gate's `check`-script invocation for `pm` (the alias `agent-setup`
169
+ * ensures exists). The hook IS a gate, so it runs `--strict` (zero checks
170
+ * running is exit 2, not a pass — docs/contract.md) and `--digest` (the
171
+ * token-bounded failure excerpt is a far better landing spot for an agent than
172
+ * raw summary.json). npm alone needs `--` to reach the script with flags;
173
+ * pnpm/yarn/bun forward them directly.
174
+ */
175
+ export function checkArgs(pm, profile = null) {
176
+ return [
177
+ 'run',
178
+ CHECK_SCRIPT,
179
+ ...(pm === 'npm' ? ['--'] : []),
180
+ '--strict',
181
+ '--digest',
182
+ ...profileArgs(profile),
183
+ ];
184
+ }
185
+ /**
186
+ * The profile's flags, appended **after** the check script's own.
187
+ *
188
+ * Last wins in `parseArgs`, so a repo whose `check` script already carries
189
+ * `--only` has the profile override it rather than fight it — the profile is the
190
+ * more specific statement, made about this hook in particular.
191
+ */
192
+ function profileArgs(profile) {
193
+ if (profile === null)
194
+ return [];
195
+ return [
196
+ ...(profile.only && profile.only.length > 0 ? ['--only', profile.only.join(',')] : []),
197
+ ...(profile.skip && profile.skip.length > 0 ? ['--skip', profile.skip.join(',')] : []),
198
+ ...(profile.changed === true ? ['--changed'] : []),
199
+ ];
200
+ }
201
+ /**
202
+ * The repo's gate profile, or `null` when it configures none and the gate should
203
+ * run the whole check.
204
+ *
205
+ * A profile with nothing in it is `null` too: `"gate": {}` narrows nothing, and
206
+ * carrying it forward would put "not the full check" on a verdict that *was* the
207
+ * full check.
208
+ */
209
+ export function gateProfile(cwd, config = loadConfig(cwd)) {
210
+ const profile = config?.gate;
211
+ if (!profile)
212
+ return null;
213
+ return profileArgs(profile).length > 0 ? profile : null;
214
+ }
215
+ /**
216
+ * The repo's gate preflight, or `null` when it declares none.
217
+ *
218
+ * Read at *write* time rather than at run time, by the hook writers in
219
+ * `./agent-setup/`, and baked into the generated script as a literal path. Two
220
+ * reasons, and both are the point of the seam:
221
+ *
222
+ * 1. A preflight that only ran once checkride had started could not answer for
223
+ * the case it exists to cover — a repo whose dependencies were never
224
+ * installed, where `checkride gate` is the thing that cannot run.
225
+ * 2. Every `hooks add`/`agent-setup` re-reads this file, so the wiring is
226
+ * restored on every refresh instead of being clobbered by it. That is exactly
227
+ * the failure a `--preflight` flag would have kept: a teammate running the
228
+ * bare command would silently drop the guard.
229
+ *
230
+ * Kept separate from {@link gateProfile} because a preflight narrows nothing —
231
+ * it must never put a "NOT the full check" clause on a verdict.
232
+ */
233
+ export function gatePreflight(cwd, config = loadConfig(cwd)) {
234
+ const path = config?.gate?.preflight;
235
+ return path !== undefined && path.length > 0 ? path : null;
236
+ }
237
+ /**
238
+ * How a narrowed run must describe itself — appended to every verdict the gate
239
+ * produces while a profile is active.
240
+ *
241
+ * This is the price of the profile and it is not optional. A gate that runs two
242
+ * of eighteen slots and reports a bare `✔ green` has told the reader the work is
243
+ * done, which is exactly what it does not know. Naming the profile turns a
244
+ * silent partial pass into a stated one.
245
+ */
246
+ function profileClause(profile) {
247
+ if (profile === null)
248
+ return null;
249
+ const parts = [
250
+ profile.only && profile.only.length > 0 ? `only ${profile.only.join(', ')}` : null,
251
+ profile.skip && profile.skip.length > 0 ? `without ${profile.skip.join(', ')}` : null,
252
+ profile.changed === true ? 'affected-only' : null,
253
+ ].filter((p) => p !== null);
254
+ return `gate profile: ${parts.join(', ')} — NOT the full check`;
255
+ }
256
+ /**
257
+ * Below this, naming the slowest check is noise rather than insight — every
258
+ * check in a fast repo is "the slowest" by some millisecond.
259
+ */
260
+ const SLOWEST_FLOOR_MS = 1000;
261
+ /** The checks that actually ran, in report order. */
262
+ function ranChecks(checks) {
263
+ return checks.filter((c) => c.skipped !== true);
264
+ }
265
+ /** `15 checks, slowest test 21.4s` — what a green run is worth saying about itself. */
266
+ function greenDetail(ran) {
267
+ const count = `${ran.length} check${ran.length === 1 ? '' : 's'}`;
268
+ const slowest = ran.reduce((worst, c) => (worst === null || c.duration_ms > worst.duration_ms ? c : worst), null);
269
+ if (slowest === null || slowest.duration_ms < SLOWEST_FLOOR_MS)
270
+ return count;
271
+ return `${count}, slowest ${slowest.name} ${formatDuration(slowest.duration_ms)}`;
272
+ }
273
+ /**
274
+ * Tolerance for a summary written moments before the gate's own clock reading —
275
+ * mirrors `MTIME_TOLERANCE_MS` in `./triage/triage.ts`, where the same
276
+ * comparison decides whether the summary belongs to this run.
277
+ *
278
+ * Without it this comparison is unsound on Linux: file mtimes come from the
279
+ * kernel's coarse (timer-tick) clock while `Date.now()` does not, so a summary
280
+ * written *after* `startedAt` routinely lands a few milliseconds behind it —
281
+ * measured at ~47% of writes on overlayfs, and never once on APFS. That is why
282
+ * the gate went red only on the Linux CI legs.
283
+ */
284
+ const MTIME_TOLERANCE_MS = 1000;
285
+ /**
286
+ * Is the summary on disk the one *this* run wrote?
287
+ *
288
+ * A gate that could not run wrote none, and a report that invents "0 checks
289
+ * failed" from a missing file is exactly the vacuous green the artifact exists
290
+ * to disprove. A summary older than `startedAt` belongs to a *previous* run and
291
+ * is dropped for the same reason — not hypothetical: a check script of the shape
292
+ * `tsc --build && checkride` leaves the summary untouched when the build fails,
293
+ * so trusting whatever is on disk would report the last run's failing slots as
294
+ * this one's, confidently and wrongly. The tolerance is far smaller than any
295
+ * real gate run, so it cannot launder a genuinely stale summary into a fresh one.
296
+ */
297
+ function isFresh(read, startedAt) {
298
+ return read.state === 'ok' && read.mtimeMs >= startedAt - MTIME_TOLERANCE_MS;
299
+ }
300
+ /**
301
+ * What the run itself says, read back out of a summary already known to be this
302
+ * run's, or `null` when there is none. The caller still has the elapsed time,
303
+ * which is true regardless.
304
+ */
305
+ function runDetail(read, green) {
306
+ if (read.state !== 'ok')
307
+ return null;
308
+ const ran = ranChecks(read.summary.checks);
309
+ if (green)
310
+ return greenDetail(ran);
311
+ const failed = ran.filter((c) => !c.ok).map((c) => c.name);
312
+ // Red with nothing failing means the check script itself died (or --strict
313
+ // caught a vacuous run); say the honest thing rather than name no slots.
314
+ if (failed.length === 0)
315
+ return `${ran.length} checks ran, none failed — the check script itself failed`;
316
+ return `${failed.length} of ${ran.length} failed: ${failed.join(', ')}`;
317
+ }
318
+ /**
319
+ * The one line a human reads to know the gate ran, what it decided, and how long
320
+ * they waited on it.
321
+ *
322
+ * The elapsed time is the gate's own wall clock — package-manager startup,
323
+ * incremental build and all — not the pipeline's `total_duration_ms`, because
324
+ * the honest answer to "why did that pause" is the whole pause, not the part
325
+ * checkride chooses to measure.
326
+ */
327
+ function headline(verdict, elapsedMs, detail) {
328
+ return `checkride ${verdict} in ${formatDuration(elapsedMs)}${detail === null ? '' : ` — ${detail}`}`;
329
+ }
330
+ /** Join the run's own detail with the narrowing note, dropping whichever is absent. */
331
+ function detailWith(detail, profile) {
332
+ const parts = [detail, profileClause(profile)].filter((p) => p !== null);
333
+ return parts.length === 0 ? null : parts.join(' — ');
334
+ }
335
+ /** The sentence a red gate hands the agent, naming what to open first. */
336
+ function redMessage(cwd, pm, profile) {
337
+ const where = existsSync(join(cwd, '.check', DIGEST_FILE)) ? `.check/${DIGEST_FILE}` : SUMMARY_PATH;
338
+ // A narrowed red is still a red, but it is not the whole story: the slots the
339
+ // profile skipped were never asked, so fixing what it names may not be enough.
340
+ const narrowed = profile === null
341
+ ? ''
342
+ : ` This gate ran a profile, not the full check — run \`${runScript(pm, CHECK_SCRIPT)}\` for the rest.`;
343
+ return (`checkride: the gate is red — read ${where}, fix the failing slot, then finish ` +
344
+ `(do not stop while checkride is red). Run \`${pm} exec checkride triage\` for full triage.${narrowed}`);
345
+ }
346
+ /**
347
+ * The Node half of a refusal message: what this process is running, what the
348
+ * repo asked for, and the one lever that closes the gap.
349
+ *
350
+ * This clause is why the whole classification exists. The default cause of a
351
+ * refused launch is that the harness ran its hook in a non-login shell, so the
352
+ * hook got the machine's default interpreter rather than the contributor's —
353
+ * a fact no amount of reading `.check/` would ever reveal.
354
+ */
355
+ function nodeClause(alignment, running) {
356
+ const context = 'Agent harnesses run hooks in a non-login shell, so a hook gets the machine’s default ' +
357
+ 'Node rather than the one your terminal has.';
358
+ const pin = alignment?.pin ?? null;
359
+ if (pin === null) {
360
+ return (`${context} This process is on Node ${running} and the repo pins no ` +
361
+ `\`.nvmrc\`/\`.node-version\` for checkride to align to; add one, or set ${NODE_BIN_VAR} to the ` +
362
+ 'bin directory of a Node the repo accepts.');
363
+ }
364
+ return (`${context} This one is on Node ${running}, the repo pins ${pin.version} ` +
365
+ `(${pin.file}), and no matching install was found in a known version-manager layout — ` +
366
+ `so checkride could not align to it. Install that Node, or set ${NODE_BIN_VAR} to its bin directory.`);
367
+ }
368
+ /**
369
+ * Said on the refusals nobody in the turn can clear, and only those: the gate is
370
+ * declining to block.
371
+ *
372
+ * Stating it is the whole point. A turn that ended unverified must not look like
373
+ * a turn that passed, and the reader — who *can* fix their environment, unlike
374
+ * the agent — is the one who needs to know a check was owed and never ran.
375
+ */
376
+ const STOOD_DOWN = 'This gate is not blocking the turn: nothing here is fixable by editing the repo, so blocking ' +
377
+ 'would only re-ask an agent that has no lever to pull. Nothing was verified — fix the ' +
378
+ 'environment and run the check yourself before trusting this turn.';
379
+ /**
380
+ * The verdict for a package manager that refused to start the check script.
381
+ *
382
+ * It says the three things a red verdict would get wrong here: nothing ran, so
383
+ * `.check/` describes some earlier turn and not this one; the code is not what
384
+ * failed, so no edit will clear it; and here is the actual cause. A gate that
385
+ * sent a reader to `summary.json` for a run that never happened is the confusion
386
+ * this replaces.
387
+ *
388
+ * {@link nodeClause} rides only on an `environment` refusal. It is a paragraph
389
+ * about which interpreter this process got, and appending it to "this repo has
390
+ * no `check` script" pointed a reader at their Node version to explain a missing
391
+ * line of package.json.
392
+ */
393
+ function refusalMessage(refusal, context) {
394
+ const environment = refusal.fixable === 'environment';
395
+ return [
396
+ `checkride: the gate could not run — ${refusal.cause}.`,
397
+ 'Nothing ran: no check executed and no artifact was written, so `.check/` holds nothing from ' +
398
+ 'this turn and the code is not what to look at.',
399
+ ...(environment ? [nodeClause(context.alignment, context.running), STOOD_DOWN] : []),
400
+ `Run \`${runScript(context.pm, CHECK_SCRIPT)}\` in a terminal to see the same failure directly.`,
401
+ ].join(' ');
402
+ }
403
+ /**
404
+ * Put the pinned Node in front of the child's `PATH`, and say so on stderr.
405
+ *
406
+ * Never silent, by rule: which interpreter the whole pipeline runs on is not
407
+ * something to change behind the reader's back, and the line is what turns a
408
+ * mysteriously different result into an explained one. It goes to stderr with
409
+ * the rest of the run's progress rather than into the one-line verdict, so it is
410
+ * present on a red run too — which is exactly when it is worth knowing.
411
+ *
412
+ * Returns `env` unchanged when there is nothing to align, which is every repo
413
+ * that pins nothing and every hook that already arrived on the right Node.
414
+ */
415
+ function announceAlignment(alignment, env, stderr) {
416
+ if (alignment === null)
417
+ return env;
418
+ const { bin } = alignment;
419
+ if (bin === null)
420
+ return env;
421
+ const target = alignment.version ?? alignment.pin?.version ?? 'the pinned version';
422
+ const why = alignment.pin === null ? NODE_BIN_VAR : `${alignment.pin.file} pins ${alignment.pin.version}`;
423
+ stderr.write(`checkride: running the check on Node ${target} from ${bin} (${why}; this hook started on ${alignment.running}).\n`);
424
+ return withNodeBin(env, bin);
425
+ }
426
+ /**
427
+ * Classify a non-green run and phrase it.
428
+ *
429
+ * The one judgement here is whether the pipeline ran. A summary from this run
430
+ * proves it did, so on that branch nothing in the output can mean otherwise —
431
+ * the guard that keeps a check which merely *printed* a package-manager error
432
+ * code from being read as a launch refusal.
433
+ */
434
+ function failedVerdict(f) {
435
+ const refusal = f.fresh ? null : launchRefusal(f.output);
436
+ if (refusal === null) {
437
+ return {
438
+ status: headline('✘ red', f.elapsedMs, detailWith(f.fresh ? runDetail(f.summary, false) : null, f.profile)),
439
+ instruction: redMessage(f.cwd, f.pm, f.profile),
440
+ refusal: null,
441
+ standDown: false,
442
+ };
443
+ }
444
+ // A launch refusal names no profile: nothing ran, so how much would have run
445
+ // is beside the point.
446
+ return {
447
+ status: headline('⚠ could not run', f.elapsedMs, refusal.cause),
448
+ instruction: refusalMessage(refusal, { alignment: f.alignment, running: f.running, pm: f.pm }),
449
+ refusal: refusal.cause,
450
+ standDown: refusal.fixable === 'environment',
451
+ };
452
+ }
453
+ /**
454
+ * Answer a green run in `harness`'s protocol.
455
+ *
456
+ * Claude Code gets `{"systemMessage": …}`, which it shows the user. Without it a
457
+ * green gate is completely silent: the turn simply takes a minute longer than it
458
+ * should have, with nothing on screen to say a full pipeline just ran, which is
459
+ * indistinguishable from a hung model.
460
+ *
461
+ * Cursor gets nothing, and cannot. Its stop hook accepts one field —
462
+ * `followup_message` — and that field *submits a new turn*, so using it to
463
+ * announce a pass would put the agent back to work every time it succeeded. No
464
+ * other channel is documented for the event; see docs/cursor.md.
465
+ */
466
+ function reportGreen(harness, stdout, status) {
467
+ if (harness === 'cursor')
468
+ return;
469
+ stdout.write(`${JSON.stringify({ systemMessage: status })}\n`);
470
+ }
471
+ /**
472
+ * Answer a refusal the turn cannot possibly clear: say so, loudly, and do not
473
+ * block.
474
+ *
475
+ * This is the one place checkride chooses *not* to gate, and it is not a
476
+ * softening of the vacuous-green rule — it is that rule reaching its limit.
477
+ * Blocking exists to make an agent fix what it broke. When the cause is a Node
478
+ * the harness did not put on `PATH`, or a package manager corepack will not
479
+ * verify, there is nothing to fix from inside the turn; the block just re-asks
480
+ * the same agent forever, and the observed end of that road is a contributor
481
+ * turning the gate off. A standing gate that says "I could not run" beats a
482
+ * removed one.
483
+ *
484
+ * The message goes to whoever can act on it. Under Claude Code that is
485
+ * `systemMessage`, which the *user* sees — and the full text rides there rather
486
+ * than the one-line status, because without a `decision: "block"` there is no
487
+ * `reason` channel to carry the rest.
488
+ *
489
+ * Cursor has no equivalent. Its stop hook takes one output field,
490
+ * `followup_message`, and that *submits a new turn* — the loop being stood down
491
+ * from. This used to settle for stderr, on the reading that a stand-down nobody
492
+ * sees still beats a loop. That reading was wrong in its premise: Cursor does not
493
+ * document hook stderr as reaching the user at all, surfacing it only in a Hooks
494
+ * output panel under Customize, so "not silent" held on one harness and the
495
+ * party who can actually run an install saw nothing.
496
+ *
497
+ * So the message does go to `followup_message` — **once**. {@link GATE_RETRY_ENV}
498
+ * carries the one fact that makes that safe, since the `loop_count` deciding it
499
+ * reaches the hook script rather than this process. One nudge names the fix in
500
+ * the chat; the next turn is silent, and the loop is bounded at a single extra
501
+ * turn rather than at nothing.
502
+ */
503
+ function reportStandDown(harness, out, full, env) {
504
+ if (harness === 'cursor') {
505
+ if (env[GATE_RETRY_ENV] === '0')
506
+ out.stdout.write(`${JSON.stringify({ followup_message: full })}\n`);
507
+ }
508
+ else {
509
+ out.stdout.write(`${JSON.stringify({ systemMessage: full })}\n`);
510
+ }
511
+ out.stderr.write(`${full}\n`);
512
+ return 0;
513
+ }
514
+ /**
515
+ * Answer a run that was not green in `harness`'s protocol — a pipeline red, or a
516
+ * launch refusal the repo can fix; the wire format is the same for both, because
517
+ * both block. A refusal nothing in the turn can fix takes
518
+ * {@link reportStandDown} instead, before any of this.
519
+ *
520
+ * Claude Code gets both spellings of the same verdict, because which one lands
521
+ * depends on the hook script the repo happens to have. The JSON body
522
+ * (`decision: "block"`) is what a current script forwards on its own exit 0 —
523
+ * Claude Code parses hook JSON *only* on exit 0, and that body is the one form
524
+ * that can carry a user-visible `systemMessage` alongside the block. The stderr
525
+ * line is what a script generated before that change relies on, where the block
526
+ * rides on exit 2 and stdout is ignored. Emitting both is what lets an
527
+ * already-installed hook script keep gating across the upgrade.
528
+ *
529
+ * Cursor gets `{"followup_message": …}` on stdout and **exit 0**: it reads a
530
+ * non-zero stop hook as a *failed hook* and lets the turn end anyway, so the
531
+ * verdict cannot ride on the exit code. Cursor caps those auto-followups at five
532
+ * per script by default; the hook entry `agent-setup` writes opts out
533
+ * (`loop_limit: null`), because a gate that stops replying after five turns is
534
+ * not a gate.
535
+ */
536
+ function reportRed(harness, out, verdict, env) {
537
+ // The agent is told what to open and what not to do; the user is told what
538
+ // happened and how long it took. Handing the user the agent's marching orders
539
+ // would bury the one line they are actually reading.
540
+ const full = `${verdict.status}\n${verdict.instruction}`;
541
+ if (verdict.standDown)
542
+ return reportStandDown(harness, out, full, env);
543
+ if (harness === 'cursor') {
544
+ out.stdout.write(`${JSON.stringify({ followup_message: full })}\n`);
545
+ return 0;
546
+ }
547
+ out.stdout.write(`${JSON.stringify({ decision: 'block', reason: full, systemMessage: verdict.status })}\n`);
548
+ out.stderr.write(`${full}\n`);
549
+ return 2;
550
+ }
551
+ function gateContext(options) {
552
+ return {
553
+ cwd: options.cwd ?? process.cwd(),
554
+ harness: options.harness ?? 'claude',
555
+ stdout: options.stdout ?? process.stdout,
556
+ stderr: options.stderr ?? process.stderr,
557
+ run: options.spawn ?? spawnForward,
558
+ env: options.env ?? process.env,
559
+ pinEnv: options.pinEnv ?? realPinEnv,
560
+ now: options.now ?? Date.now,
561
+ };
562
+ }
563
+ /**
564
+ * Run the gate and answer in `harness`'s protocol.
565
+ *
566
+ * The exit code is unchanged and stays as `docs/contract.md` promises it: 2 while
567
+ * blocked under `--harness claude`, always 0 under `--harness cursor`. What each
568
+ * harness *displays* is carried by the JSON body on stdout — see
569
+ * {@link reportGreen} and {@link reportRed} — because that is the only channel
570
+ * either harness renders for a human.
571
+ */
572
+ export async function runGate(options = {}) {
573
+ const { cwd, harness, stdout, stderr, run, env, pinEnv, now } = gateContext(options);
574
+ const marker = join(cwd, DIRTY_MARKER);
575
+ // Cursor running this repo's Claude hooks alongside its own. Stand down before
576
+ // touching the marker: the native Cursor gate owns both the verdict and the
577
+ // marker for this turn.
578
+ if (harness === 'claude' && deferredToCursor(cwd, env)) {
579
+ return { exitCode: 0, ran: false, green: true, refusal: null };
580
+ }
581
+ // No edit marker → this turn touched no files → nothing to gate. Stop hooks
582
+ // fire on every turn, including pure-conversation ones; without this the gate
583
+ // taxes every reply with a full pipeline run.
584
+ if (options.ifDirty === true && !existsSync(marker)) {
585
+ return { exitCode: 0, ran: false, green: true, refusal: null };
586
+ }
587
+ const pm = options.pm ?? detectPackageManager({ cwd });
588
+ const profile = options.profile !== undefined ? options.profile : gateProfile(cwd);
589
+ const alignment = alignNode(cwd, pinEnv);
590
+ const childEnv = announceAlignment(alignment, env, stderr);
591
+ const startedAt = now();
592
+ const output = recording(stderr);
593
+ const code = await run(pm, checkArgs(pm, profile), { cwd, stderr: output, env: childEnv });
594
+ const green = code === 0;
595
+ const summary = await readSummary(cwd);
596
+ const fresh = isFresh(summary, startedAt);
597
+ if (green) {
598
+ rmSync(marker, { force: true });
599
+ const detail = detailWith(fresh ? runDetail(summary, true) : null, profile);
600
+ reportGreen(harness, stdout, headline('✔ green', now() - startedAt, detail));
601
+ return { exitCode: 0, ran: true, green: true, refusal: null };
602
+ }
603
+ const verdict = failedVerdict({
604
+ cwd,
605
+ pm,
606
+ elapsedMs: now() - startedAt,
607
+ summary,
608
+ fresh,
609
+ output: output.text(),
610
+ alignment,
611
+ running: pinEnv.running(),
612
+ profile,
613
+ });
614
+ const exitCode = reportRed(harness, { stdout, stderr }, verdict, env);
615
+ return { exitCode, ran: true, green: false, refusal: verdict.refusal };
616
+ }
617
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}