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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +92 -89
- package/dist/cli-core.js +25 -8
- package/dist/cli.js +1 -1
- package/dist/lib/agents.js +26 -26
- package/dist/lib/buildlog.js +11 -11
- package/dist/lib/buildlogsync.js +8 -8
- package/dist/lib/check.js +55 -22
- package/dist/lib/commitbody.js +72 -0
- package/dist/lib/commitmsg.js +7 -7
- package/dist/lib/git.js +44 -15
- package/dist/lib/intent.js +21 -20
- package/dist/lib/latch.js +19 -19
- package/dist/lib/orient.js +36 -17
- package/dist/lib/reentry.js +70 -0
- package/dist/lib/settings.js +16 -16
- package/dist/lib/sidecar.js +54 -54
- package/dist/lib/templates.js +1 -1
- package/dist/verbs/agent.js +27 -27
- package/dist/verbs/build.js +9 -9
- package/dist/verbs/check.js +6 -6
- package/dist/verbs/checkpoint.js +48 -47
- package/dist/verbs/doctor.js +31 -30
- package/dist/verbs/finish.js +28 -41
- package/dist/verbs/handoff.js +9 -8
- package/dist/verbs/init.js +9 -9
- package/dist/verbs/park.js +3 -3
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +13 -13
- package/dist/verbs/spike.js +25 -19
- package/dist/verbs/start.js +49 -19
- package/dist/verbs/status.js +8 -8
- package/dist/verbs/turn.js +16 -16
- package/dist/verbs/use.js +4 -4
- package/hooks/post-edit.sh +4 -4
- package/hooks/pre-bash-commit.sh +3 -3
- package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
- package/node_modules/checkride/CHANGELOG.md +1404 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +197 -38
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
- package/node_modules/checkride/dist/adapters.d.ts +30 -10
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -23
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
- package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
- package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.js +77 -0
- package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.js +80 -0
- package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.js +9 -4
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
- package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.js +45 -0
- package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.js +56 -0
- package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
- package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.js +19 -0
- package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.js +47 -0
- package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.js +91 -0
- package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.js +108 -0
- package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +16 -1
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
- package/node_modules/checkride/dist/atomic.js +25 -3
- package/node_modules/checkride/dist/atomic.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
- package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +163 -60
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
- package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/history.js +232 -0
- package/node_modules/checkride/dist/baseline/history.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +5 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +65 -24
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
- package/node_modules/checkride/dist/baseline-command.js +7 -7
- package/node_modules/checkride/dist/baseline-command.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +21 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +350 -36
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +114 -9
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +142 -23
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
- package/node_modules/checkride/dist/digest/digest.js +6 -6
- package/node_modules/checkride/dist/digest/index.d.ts +2 -2
- package/node_modules/checkride/dist/digest/index.js +2 -2
- package/node_modules/checkride/dist/doctor.d.ts +27 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +231 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +180 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +617 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +79 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +385 -81
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/json.d.ts +21 -0
- package/node_modules/checkride/dist/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/json.js +21 -0
- package/node_modules/checkride/dist/json.js.map +1 -0
- package/node_modules/checkride/dist/links.d.ts +31 -5
- package/node_modules/checkride/dist/links.d.ts.map +1 -1
- package/node_modules/checkride/dist/links.js +141 -32
- package/node_modules/checkride/dist/links.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +129 -0
- package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
- package/node_modules/checkride/dist/node-pin.js +211 -0
- package/node_modules/checkride/dist/node-pin.js.map +1 -0
- package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +312 -113
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pack.d.ts +8 -9
- package/node_modules/checkride/dist/pack.d.ts.map +1 -1
- package/node_modules/checkride/dist/pack.js +53 -17
- package/node_modules/checkride/dist/pack.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/detect.js +6 -1
- package/node_modules/checkride/dist/pm/detect.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +7 -2
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js +6 -2
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +108 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -0
- package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
- package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/tools.js +124 -0
- package/node_modules/checkride/dist/pm/tools.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +78 -18
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/proc.d.ts +30 -0
- package/node_modules/checkride/dist/proc.d.ts.map +1 -0
- package/node_modules/checkride/dist/proc.js +42 -0
- package/node_modules/checkride/dist/proc.js.map +1 -0
- package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
- package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/cli.js +24 -0
- package/node_modules/checkride/dist/qa/cli.js.map +1 -0
- package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
- package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dead.js +88 -0
- package/node_modules/checkride/dist/qa/dead.js.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.js +62 -0
- package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
- package/node_modules/checkride/dist/qa/health.d.ts +85 -0
- package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/health.js +109 -0
- package/node_modules/checkride/dist/qa/health.js.map +1 -0
- package/node_modules/checkride/dist/qa/index.d.ts +23 -0
- package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/index.js +22 -0
- package/node_modules/checkride/dist/qa/index.js.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.js +195 -0
- package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
- package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
- package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/qa.js +158 -0
- package/node_modules/checkride/dist/qa/qa.js.map +1 -0
- package/node_modules/checkride/dist/qa/read.d.ts +40 -0
- package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/read.js +47 -0
- package/node_modules/checkride/dist/qa/read.js.map +1 -0
- package/node_modules/checkride/dist/qa/render.d.ts +26 -0
- package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/render.js +310 -0
- package/node_modules/checkride/dist/qa/render.js.map +1 -0
- package/node_modules/checkride/dist/recover.d.ts +48 -0
- package/node_modules/checkride/dist/recover.d.ts.map +1 -0
- package/node_modules/checkride/dist/recover.js +220 -0
- package/node_modules/checkride/dist/recover.js.map +1 -0
- package/node_modules/checkride/dist/security.d.ts +45 -0
- package/node_modules/checkride/dist/security.d.ts.map +1 -0
- package/node_modules/checkride/dist/security.js +86 -0
- package/node_modules/checkride/dist/security.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +6 -6
- package/node_modules/checkride/dist/smoke.js +6 -6
- package/node_modules/checkride/dist/snippets.d.ts +27 -30
- package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
- package/node_modules/checkride/dist/snippets.js +26 -30
- package/node_modules/checkride/dist/snippets.js.map +1 -1
- package/node_modules/checkride/dist/tool-json.d.ts +33 -0
- package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
- package/node_modules/checkride/dist/tool-json.js +58 -0
- package/node_modules/checkride/dist/tool-json.js.map +1 -0
- package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
- package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/cli.js +26 -0
- package/node_modules/checkride/dist/triage/cli.js.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
- package/node_modules/checkride/dist/triage/env.d.ts +54 -0
- package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/env.js +81 -0
- package/node_modules/checkride/dist/triage/env.js.map +1 -0
- package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
- package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/gate.js +121 -0
- package/node_modules/checkride/dist/triage/gate.js.map +1 -0
- package/node_modules/checkride/dist/triage/index.d.ts +22 -0
- package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/index.js +20 -0
- package/node_modules/checkride/dist/triage/index.js.map +1 -0
- package/node_modules/checkride/dist/triage/render.d.ts +19 -0
- package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/render.js +419 -0
- package/node_modules/checkride/dist/triage/render.js.map +1 -0
- package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
- package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/triage.js +131 -0
- package/node_modules/checkride/dist/triage/triage.js.map +1 -0
- package/node_modules/checkride/package.json +18 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +265 -0
- package/node_modules/checkride/skills/qa/SKILL.md +231 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
- package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
- package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
- package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
- package/node_modules/checkride/templates/shared/vale.ini +34 -0
- package/package.json +10 -9
- package/skills/build/SKILL.md +85 -76
- package/skills/doctor/SKILL.md +11 -11
- package/skills/finish/SKILL.md +21 -18
- package/skills/harvest/SKILL.md +13 -11
- package/skills/park/SKILL.md +10 -10
- package/skills/plan/SKILL.md +69 -59
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +21 -18
- package/skills/revert/SKILL.md +6 -6
- package/skills/spike/SKILL.md +9 -9
- package/skills/status/SKILL.md +5 -5
- package/skills/step/SKILL.md +20 -15
- package/skills/verify/SKILL.md +56 -46
- package/templates/build-log.md +7 -7
- package/templates/intent.md +52 -41
- 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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"}
|