harness-everything 0.3.6 → 0.4.1

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 (580) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +8 -7
  4. package/.github/workflows/behavioral-evals.yml +3 -3
  5. package/.github/workflows/ci.yml +49 -28
  6. package/.github/workflows/release.yml +52 -27
  7. package/.nvmrc +1 -0
  8. package/.releaserc.json +113 -0
  9. package/AGENTS.md +44 -24
  10. package/BENCHMARK_SOP.md +38 -18
  11. package/CHANGELOG.md +196 -3
  12. package/CONTRIBUTING.md +37 -9
  13. package/Dockerfile +1 -1
  14. package/PRIVACY.md +25 -0
  15. package/README.md +126 -91
  16. package/RELEASING.md +93 -0
  17. package/SUPPORT.md +27 -0
  18. package/TERMS.md +21 -0
  19. package/VERIFICATION.md +192 -284
  20. package/behavioral-evals/README.md +21 -0
  21. package/behavioral-evals/case-validator.js +194 -0
  22. package/behavioral-evals/cases/baseline-environment-detection.yaml +21 -0
  23. package/behavioral-evals/cases/baseline-eval-harness.yaml +27 -0
  24. package/behavioral-evals/cases/baseline-fable-discipline.yaml +31 -0
  25. package/behavioral-evals/cases/baseline-find-skills-exposure.yaml +49 -0
  26. package/behavioral-evals/cases/baseline-find-skills.yaml +21 -0
  27. package/behavioral-evals/cases/baseline-git-commit.yaml +2 -2
  28. package/behavioral-evals/cases/baseline-install-cognitive-os.yaml +24 -0
  29. package/behavioral-evals/cases/baseline-multi-agent-workspace.yaml +28 -0
  30. package/behavioral-evals/cases/baseline-rewrite-commits.yaml +34 -0
  31. package/behavioral-evals/cases/baseline-self-evolve.yaml +31 -0
  32. package/behavioral-evals/cases/baseline-skill-creator.yaml +20 -0
  33. package/behavioral-evals/cases/baseline-skill-style.yaml +21 -0
  34. package/behavioral-evals/cases/baseline-to-spec.yaml +34 -0
  35. package/behavioral-evals/cases/baseline-to-tickets.yaml +24 -0
  36. package/behavioral-evals/cases/baseline-todo-driven-workflow.yaml +32 -0
  37. package/behavioral-evals/cases/baseline-using-git-worktrees.yaml +15 -0
  38. package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +2 -2
  39. package/behavioral-evals/cases/pressure-complexity-skip.yaml +2 -2
  40. package/behavioral-evals/cases/pressure-scope-bypass.yaml +7 -2
  41. package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +7 -4
  42. package/behavioral-evals/cases/scope-discipline.yaml +2 -1
  43. package/behavioral-evals/cases/verify-before-done.yaml +7 -5
  44. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/case.yaml +31 -0
  45. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/historical-case.yaml +31 -0
  46. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/provenance.json +42 -0
  47. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/result.sanitized.json +20 -0
  48. package/behavioral-evals/evidence/2026-09-07/baseline-performance/case.yaml +34 -0
  49. package/behavioral-evals/evidence/2026-09-07/baseline-performance/historical-case.yaml +34 -0
  50. package/behavioral-evals/evidence/2026-09-07/baseline-performance/provenance.json +42 -0
  51. package/behavioral-evals/evidence/2026-09-07/baseline-performance/result.sanitized.json +20 -0
  52. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/case.yaml +28 -0
  53. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/historical-case.yaml +28 -0
  54. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/provenance.json +42 -0
  55. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/result.sanitized.json +20 -0
  56. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/case.yaml +33 -0
  57. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/historical-case.yaml +33 -0
  58. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/provenance.json +42 -0
  59. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/result.sanitized.json +20 -0
  60. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/case.yaml +28 -0
  61. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/historical-case.yaml +18 -0
  62. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/provenance.json +30 -0
  63. package/behavioral-evals/evidence/2026-09-07/live-run-blocker.md +16 -0
  64. package/behavioral-evals/evidence/2026-09-07/manifest.json +88 -0
  65. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/case.yaml +33 -0
  66. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/historical-case.yaml +28 -0
  67. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/provenance.json +30 -0
  68. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/case.yaml +50 -0
  69. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/historical-case.yaml +50 -0
  70. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/provenance.json +42 -0
  71. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/result.sanitized.json +20 -0
  72. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/case.yaml +35 -0
  73. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/historical-case.yaml +35 -0
  74. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/provenance.json +42 -0
  75. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/result.sanitized.json +20 -0
  76. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/case.yaml +43 -0
  77. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/historical-case.yaml +43 -0
  78. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/provenance.json +42 -0
  79. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/result.sanitized.json +24 -0
  80. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/case.yaml +43 -0
  81. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/historical-case.yaml +43 -0
  82. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/provenance.json +42 -0
  83. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/result.sanitized.json +20 -0
  84. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/case.yaml +43 -0
  85. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/historical-case.yaml +43 -0
  86. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/provenance.json +42 -0
  87. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/result.sanitized.json +24 -0
  88. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/case.yaml +50 -0
  89. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/historical-case.yaml +47 -0
  90. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/provenance.json +42 -0
  91. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/result.sanitized.json +24 -0
  92. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/case.yaml +50 -0
  93. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/historical-case.yaml +47 -0
  94. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/provenance.json +42 -0
  95. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/result.sanitized.json +20 -0
  96. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/case.yaml +50 -0
  97. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/historical-case.yaml +47 -0
  98. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/provenance.json +42 -0
  99. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/result.sanitized.json +24 -0
  100. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/case.yaml +30 -0
  101. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/historical-case.yaml +23 -0
  102. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/provenance.json +30 -0
  103. package/behavioral-evals/evidence/2026-09-07/triage-matrix.json +426 -0
  104. package/behavioral-evals/evidence/2026-09-07/triage-matrix.md +24 -0
  105. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/case.yaml +19 -0
  106. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/historical-case.yaml +18 -0
  107. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/provenance.json +30 -0
  108. package/behavioral-evals/evidence/2026-09-07/verify-before-done/case.yaml +33 -0
  109. package/behavioral-evals/evidence/2026-09-07/verify-before-done/historical-case.yaml +31 -0
  110. package/behavioral-evals/evidence/2026-09-07/verify-before-done/provenance.json +42 -0
  111. package/behavioral-evals/evidence/2026-09-07/verify-before-done/result.sanitized.json +20 -0
  112. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/case.yaml +33 -0
  113. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/historical-case.yaml +31 -0
  114. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/provenance.json +42 -0
  115. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/result.sanitized.json +20 -0
  116. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/case.yaml +33 -0
  117. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/historical-case.yaml +31 -0
  118. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/provenance.json +42 -0
  119. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/result.sanitized.json +20 -0
  120. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/case.yaml +33 -0
  121. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/historical-case.yaml +33 -0
  122. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/provenance.json +42 -0
  123. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/result.sanitized.json +480 -0
  124. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/transcript.sanitized.jsonl +95 -0
  125. package/behavioral-evals/evidence-tool.js +443 -0
  126. package/behavioral-evals/fixtures/claude-denied.jsonl +3 -0
  127. package/behavioral-evals/fixtures/claude-legacy.json +1 -0
  128. package/behavioral-evals/fixtures/claude-oral-only.jsonl +2 -0
  129. package/behavioral-evals/fixtures/claude-success.jsonl +5 -0
  130. package/behavioral-evals/fixtures/claude-truncated.jsonl +3 -0
  131. package/behavioral-evals/fixtures/opencode-success.jsonl +6 -0
  132. package/behavioral-evals/results/2026-08-31-baseline-add-feature.json +20 -0
  133. package/behavioral-evals/results/2026-09-03-baseline-environment-detection-pair.json +133 -0
  134. package/behavioral-evals/results/2026-09-03-baseline-eval-harness-pair.json +141 -0
  135. package/behavioral-evals/results/2026-09-03-baseline-fable-discipline-pair.json +149 -0
  136. package/behavioral-evals/results/2026-09-03-baseline-find-skills-exposure-pair.json +141 -0
  137. package/behavioral-evals/results/2026-09-03-baseline-find-skills-pair.json +141 -0
  138. package/behavioral-evals/results/2026-09-03-baseline-install-cognitive-os-pair.json +133 -0
  139. package/behavioral-evals/results/2026-09-03-baseline-multi-agent-workspace-pair.json +141 -0
  140. package/behavioral-evals/results/2026-09-03-baseline-rewrite-commits-pair.json +95 -0
  141. package/behavioral-evals/results/2026-09-03-baseline-self-evolve-pair.json +141 -0
  142. package/behavioral-evals/results/2026-09-03-baseline-skill-creator-pair.json +141 -0
  143. package/behavioral-evals/results/2026-09-03-baseline-skill-style-pair.json +133 -0
  144. package/behavioral-evals/results/2026-09-03-baseline-to-spec-pair.json +133 -0
  145. package/behavioral-evals/results/2026-09-03-baseline-to-tickets-pair.json +141 -0
  146. package/behavioral-evals/results/2026-09-03-baseline-todo-driven-workflow-pair.json +141 -0
  147. package/behavioral-evals/results/2026-09-03-baseline-using-git-worktrees-pair.json +133 -0
  148. package/behavioral-evals/results/2026-09-03-summary-both.json +41 -0
  149. package/behavioral-evals/results/2026-09-04-baseline-find-skills-pair.json +141 -0
  150. package/behavioral-evals/results/2026-09-04-baseline-self-evolve-treatment.json +80 -0
  151. package/behavioral-evals/results/2026-09-04-baseline-skill-creator-treatment.json +80 -0
  152. package/behavioral-evals/results/2026-09-04-summary-both.json +41 -0
  153. package/behavioral-evals/run-with-plugin.js +15 -31
  154. package/behavioral-evals/run.js +446 -105
  155. package/behavioral-evals/transcript-parser.js +591 -0
  156. package/bin/cli.js +24 -12
  157. package/ci/ab-test-cases/grill-me-adversarial.yaml +14 -4
  158. package/ci/ab-test-cases/tdd-test-first.yaml +13 -6
  159. package/ci/ab-test-cases/verification-loop-gates.yaml +20 -7
  160. package/ci/ab-test-cases/verify-before-claim-cites.yaml +7 -6
  161. package/ci/ab-test-harness.js +113 -38
  162. package/ci/action-gate-platform-doc.test.js +31 -0
  163. package/ci/action-gate.test.js +260 -0
  164. package/ci/cases/case2.json +2 -2
  165. package/ci/consistency-check.js +51 -5
  166. package/ci/description-collision.js +15 -6
  167. package/ci/disclosure-check.js +225 -0
  168. package/ci/doc-capability-consistency.test.js +180 -0
  169. package/ci/fixtures/phase4/parallel-disjoint.stages.json +32 -0
  170. package/ci/fixtures/phase4/parallel-read-only.stages.json +32 -0
  171. package/ci/installer-roundtrip.test.js +159 -0
  172. package/ci/invariant-routing.test.js +74 -0
  173. package/ci/issue99-runtime.test.js +218 -0
  174. package/ci/mechanism-2aa-codex-action-gate-policy.test.js +117 -0
  175. package/ci/mechanism-2ab-openai-plugin-sync-integrity.test.js +80 -0
  176. package/ci/mechanism-2j-behavioral-runner.test.js +43 -1
  177. package/ci/mechanism-2k-reference-check.test.js +57 -2
  178. package/ci/mechanism-2l-multi-agent-workspace.test.js +202 -6
  179. package/ci/mechanism-2m-self-repo-guard.test.js +65 -0
  180. package/ci/mechanism-2n-opencode-plugin.test.js +461 -0
  181. package/ci/mechanism-2o-state-home-isolation.test.js +183 -0
  182. package/ci/mechanism-2p-installer-links.test.js +183 -0
  183. package/ci/mechanism-2q-installer-global-shared-store.test.js +132 -0
  184. package/ci/mechanism-2r-uninstall-flags.test.js +148 -0
  185. package/ci/mechanism-2s-tdd-quality.test.js +203 -0
  186. package/ci/mechanism-2t-disclosure.test.js +90 -0
  187. package/ci/mechanism-2t-evidence-triage.test.js +200 -0
  188. package/ci/mechanism-2t-issue-20.test.js +75 -0
  189. package/ci/mechanism-2t-platform-skill-targets.test.js +164 -0
  190. package/ci/mechanism-2t-transcript-parser.test.js +179 -0
  191. package/ci/mechanism-2t-workspace-identity.test.js +200 -0
  192. package/ci/mechanism-2u-issue-42-regressions.test.js +239 -0
  193. package/ci/mechanism-2u-issue-44.test.js +35 -0
  194. package/ci/mechanism-2v-verifier-boundaries.test.js +138 -0
  195. package/ci/mechanism-2w-platform-compatibility.test.js +148 -0
  196. package/ci/mechanism-2x-release-automation.test.js +157 -0
  197. package/ci/mechanism-2x-release-commit-message.test.js +66 -0
  198. package/ci/mechanism-2y-release-tooling-compat.test.js +47 -0
  199. package/ci/mechanism-2z-claude-plugin-manifest.test.js +29 -0
  200. package/ci/mutation-check.js +53 -0
  201. package/ci/mutation-loader.js +57 -0
  202. package/ci/negative-controls.js +76 -46
  203. package/ci/openai-plugin-package.test.js +182 -0
  204. package/ci/openai-plugin-runtime.test.js +175 -0
  205. package/ci/openai-plugin-submission.test.js +101 -0
  206. package/ci/phase4-ensemble-memory.test.js +307 -0
  207. package/ci/reference-check.js +73 -25
  208. package/ci/release-consistency-check.js +24 -1
  209. package/ci/repository-contract.test.js +17 -0
  210. package/ci/router-phase2-safety.test.js +81 -0
  211. package/ci/router-workflow-plan.test.js +264 -0
  212. package/ci/routing-check.js +6 -5
  213. package/ci/runner.js +6 -5
  214. package/ci/skill-routing-check.js +48 -11
  215. package/ci/test-behavioral-case-validation.js +62 -0
  216. package/ci/test-helper.js +10 -0
  217. package/ci/workflow-plan-consumers-edge.test.js +72 -0
  218. package/ci/workflow-plan-consumers.test.js +276 -0
  219. package/docs/architecture.md +137 -57
  220. package/docs/audit.md +22 -20
  221. package/docs/codex-action-gate.md +90 -0
  222. package/docs/issue-20-rollup.md +47 -0
  223. package/docs/mechanism-first-skill-mesh.md +22 -20
  224. package/docs/openai-plugin.md +118 -0
  225. package/docs/philosophy.md +28 -17
  226. package/docs/platform-capabilities.md +73 -0
  227. package/docs/platform-compatibility.json +270 -0
  228. package/docs/reflection.md +37 -22
  229. package/docs/release-evidence.md +27 -0
  230. package/docs/repository-contract.md +27 -0
  231. package/docs/routing.md +157 -44
  232. package/docs/security-audit.md +38 -0
  233. package/docs/troubleshooting.md +94 -51
  234. package/docs/verifier-boundaries.md +45 -0
  235. package/docs/workflows/fable-mode.md +77 -21
  236. package/docs/workflows/find-skills.md +40 -0
  237. package/docs/workflows/grill-with-docs.md +2 -2
  238. package/docs/workflows/multi-agent-workspace.md +22 -10
  239. package/docs/workflows/self-evolve.md +33 -0
  240. package/docs/workflows/tdd.md +36 -0
  241. package/docs/workflows/todo-driven-workflow.md +33 -0
  242. package/environment-detection/SKILL.md +3 -3
  243. package/eval-harness/SKILL.md +7 -7
  244. package/eval-harness/scripts/evaluate.js +12 -10
  245. package/evals/evaluation-reports.md +14 -0
  246. package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
  247. package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
  248. package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
  249. package/evals/install-cognitive-os/tasks/positive-trigger.yaml +1 -1
  250. package/fable-discipline/SKILL.md +2 -2
  251. package/fable-mode/CONTRACT-FORMAT.md +147 -33
  252. package/fable-mode/SKILL.md +3 -3
  253. package/fable-mode/agents/fable-orchestrator.md +95 -35
  254. package/fable-mode/execution-guardrails/SKILL.md +2 -2
  255. package/fable-mode/fable-haiku/SKILL.md +3 -3
  256. package/fable-mode/fable-opus/SKILL.md +3 -3
  257. package/fable-mode/fable-sonnet/SKILL.md +3 -3
  258. package/fable-mode/scripts/ensemble-review.js +371 -0
  259. package/fable-mode/scripts/model-selector.js +134 -3
  260. package/fable-mode/scripts/workflow-plan-consumer.js +337 -0
  261. package/find-skills/SKILL.md +5 -5
  262. package/find-skills/references/discovery-flow.md +1 -1
  263. package/find-skills/scripts/use-skill.js +7 -1
  264. package/git-commit/SKILL.md +4 -11
  265. package/grill-me/SKILL.md +5 -5
  266. package/grill-with-docs/SKILL.md +7 -7
  267. package/grill-with-docs/references/session-playbook.md +6 -4
  268. package/harness-everything/SKILL.md +23 -22
  269. package/harness-everything/references/router-workflow-plan.md +128 -0
  270. package/harness-everything/references/triage-and-tiers.md +173 -96
  271. package/harness-everything/schemas/router-task-shape.schema.json +126 -0
  272. package/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
  273. package/harness-everything/scripts/bootstrap.js +13 -4
  274. package/harness-everything/scripts/ensemble-policy.js +102 -0
  275. package/harness-everything/scripts/kernel-router.js +212 -0
  276. package/harness-everything/scripts/router-contract.js +612 -0
  277. package/harness-everything/scripts/self-heal.js +29 -20
  278. package/harness-everything/scripts/tier-router.js +346 -221
  279. package/hooks/hooks.json +60 -15
  280. package/hooks/scripts/action-gate-rules.json +77 -0
  281. package/hooks/scripts/action-gate.js +528 -0
  282. package/hooks/scripts/atomic-commit-check.js +5 -5
  283. package/hooks/scripts/codex-action-gate-post.js +46 -0
  284. package/hooks/scripts/codex-permission-request.js +140 -0
  285. package/hooks/scripts/context-compact.js +4 -3
  286. package/hooks/scripts/contract-test.js +118 -51
  287. package/hooks/scripts/lib/fable-contracts.js +121 -0
  288. package/hooks/scripts/lib/harness-state.js +164 -28
  289. package/hooks/scripts/lib/platforms/claude.js +45 -62
  290. package/hooks/scripts/lib/platforms/codex.js +35 -61
  291. package/hooks/scripts/lib/platforms/continue.js +7 -7
  292. package/hooks/scripts/lib/platforms/copilot.js +29 -53
  293. package/hooks/scripts/lib/platforms/hermes.js +71 -20
  294. package/hooks/scripts/openai-session-start.js +81 -0
  295. package/hooks/scripts/rule-of-3-tracker.js +9 -8
  296. package/hooks/scripts/rule-of-3.js +29 -21
  297. package/hooks/scripts/state-persist.js +5 -4
  298. package/hooks/scripts/stop-gate.js +2 -2
  299. package/hooks/scripts/subagent-scope-guard.js +144 -38
  300. package/hooks/scripts/subagent-start.js +19 -0
  301. package/hooks/scripts/subagent-stop.js +6 -0
  302. package/improve-codebase-architecture/SKILL.md +2 -4
  303. package/improve-codebase-architecture/references/README.md +16 -0
  304. package/install-cognitive-os/SKILL.md +28 -22
  305. package/multi-agent-workspace/SKILL.md +16 -15
  306. package/multi-agent-workspace/references/architecture-guide.md +13 -9
  307. package/multi-agent-workspace/references/orchestration.md +39 -22
  308. package/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
  309. package/multi-agent-workspace/scripts/index_memory.js +79 -26
  310. package/multi-agent-workspace/scripts/path-boundary.js +59 -0
  311. package/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
  312. package/multi-agent-workspace/scripts/scaffold.js +258 -103
  313. package/multi-agent-workspace/templates/AGENTS.md +20 -0
  314. package/multi-agent-workspace/workflows/03-generation.md +8 -10
  315. package/multi-agent-workspace/workflows/04-launcher.md +5 -5
  316. package/opencode-plugin/README.md +114 -71
  317. package/opencode-plugin/hooks/verify.js +62 -12
  318. package/opencode-plugin/index.mjs +547 -0
  319. package/opencode-plugin/plugin.json +4 -12
  320. package/package.json +20 -4
  321. package/plugins/harness-everything/.codex-plugin/plugin.json +39 -0
  322. package/plugins/harness-everything/hooks/hooks.json +197 -0
  323. package/plugins/harness-everything/hooks/scripts/action-gate-rules.json +77 -0
  324. package/plugins/harness-everything/hooks/scripts/action-gate.js +528 -0
  325. package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +68 -0
  326. package/plugins/harness-everything/hooks/scripts/boundary-guard.js +55 -0
  327. package/plugins/harness-everything/hooks/scripts/codex-action-gate-post.js +46 -0
  328. package/plugins/harness-everything/hooks/scripts/codex-permission-request.js +140 -0
  329. package/plugins/harness-everything/hooks/scripts/context-compact.js +115 -0
  330. package/plugins/harness-everything/hooks/scripts/contract-test.js +146 -0
  331. package/plugins/harness-everything/hooks/scripts/depth-guard.js +65 -0
  332. package/plugins/harness-everything/hooks/scripts/lib/fable-contracts.js +121 -0
  333. package/plugins/harness-everything/hooks/scripts/lib/harness-state.js +275 -0
  334. package/plugins/harness-everything/hooks/scripts/lib/platforms/claude.js +168 -0
  335. package/plugins/harness-everything/hooks/scripts/lib/platforms/codex.js +88 -0
  336. package/plugins/harness-everything/hooks/scripts/lib/platforms/continue.js +91 -0
  337. package/plugins/harness-everything/hooks/scripts/lib/platforms/copilot.js +85 -0
  338. package/plugins/harness-everything/hooks/scripts/lib/platforms/cursor.js +89 -0
  339. package/plugins/harness-everything/hooks/scripts/lib/platforms/hermes.js +108 -0
  340. package/plugins/harness-everything/hooks/scripts/lib/platforms/index.js +17 -0
  341. package/plugins/harness-everything/hooks/scripts/lib/platforms/worktrees.js +20 -0
  342. package/plugins/harness-everything/hooks/scripts/lib/workspace.js +290 -0
  343. package/plugins/harness-everything/hooks/scripts/openai-session-start.js +81 -0
  344. package/plugins/harness-everything/hooks/scripts/reset-circuit-breaker.js +30 -0
  345. package/plugins/harness-everything/hooks/scripts/rule-of-3-tracker.js +125 -0
  346. package/plugins/harness-everything/hooks/scripts/rule-of-3.js +195 -0
  347. package/plugins/harness-everything/hooks/scripts/state-persist.js +100 -0
  348. package/plugins/harness-everything/hooks/scripts/stop-gate.js +80 -0
  349. package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +194 -0
  350. package/plugins/harness-everything/hooks/scripts/subagent-start.js +19 -0
  351. package/plugins/harness-everything/hooks/scripts/subagent-stop.js +6 -0
  352. package/plugins/harness-everything/hooks/session-start.js +81 -0
  353. package/plugins/harness-everything/plugin.json +43 -0
  354. package/plugins/harness-everything/scripts/lib/advisory-text.js +174 -0
  355. package/plugins/harness-everything/skills/environment-detection/SKILL.md +41 -0
  356. package/plugins/harness-everything/skills/environment-detection/references/shell-syntax-rules.md +76 -0
  357. package/plugins/harness-everything/skills/environment-detection/scripts/preflight.js +103 -0
  358. package/plugins/harness-everything/skills/eval-harness/SKILL.md +42 -0
  359. package/plugins/harness-everything/skills/eval-harness/references/scoring-rubric.md +68 -0
  360. package/plugins/harness-everything/skills/eval-harness/scripts/evaluate.js +79 -0
  361. package/plugins/harness-everything/skills/fable-discipline/SKILL.md +41 -0
  362. package/plugins/harness-everything/skills/fable-discipline/references/discipline-rules.md +22 -0
  363. package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +165 -0
  364. package/plugins/harness-everything/skills/fable-mode/SKILL.md +39 -0
  365. package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +159 -0
  366. package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +32 -0
  367. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +52 -0
  368. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +53 -0
  369. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +43 -0
  370. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
  371. package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +36 -0
  372. package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +26 -0
  373. package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +36 -0
  374. package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +36 -0
  375. package/plugins/harness-everything/skills/fable-mode/model-matrix.json +32 -0
  376. package/plugins/harness-everything/skills/fable-mode/references/execution-phases.md +37 -0
  377. package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +39 -0
  378. package/plugins/harness-everything/skills/fable-mode/scripts/ensemble-review.js +371 -0
  379. package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +260 -0
  380. package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +337 -0
  381. package/plugins/harness-everything/skills/find-skills/SKILL.md +48 -0
  382. package/plugins/harness-everything/skills/find-skills/references/discovery-flow.md +102 -0
  383. package/plugins/harness-everything/skills/find-skills/scripts/use-skill.js +112 -0
  384. package/plugins/harness-everything/skills/git-commit/SKILL.md +38 -0
  385. package/plugins/harness-everything/skills/git-commit/guides/ANGULAR_STYLE.md +61 -0
  386. package/plugins/harness-everything/skills/git-commit/guides/COMMIT_GENERATION.md +24 -0
  387. package/plugins/harness-everything/skills/git-commit/guides/LANGUAGE_DETECTION.md +16 -0
  388. package/plugins/harness-everything/skills/git-commit/guides/MAIN_REPO.md +15 -0
  389. package/plugins/harness-everything/skills/git-commit/guides/SUBMODULES.md +27 -0
  390. package/plugins/harness-everything/skills/git-commit/references/commit-flow.md +30 -0
  391. package/plugins/harness-everything/skills/grill-me/SKILL.md +43 -0
  392. package/plugins/harness-everything/skills/grill-me/references/grilling-playbook.md +38 -0
  393. package/plugins/harness-everything/skills/grill-with-docs/ADR-FORMAT.md +47 -0
  394. package/plugins/harness-everything/skills/grill-with-docs/CONTEXT-FORMAT.md +63 -0
  395. package/plugins/harness-everything/skills/grill-with-docs/SKILL.md +41 -0
  396. package/plugins/harness-everything/skills/grill-with-docs/references/session-playbook.md +99 -0
  397. package/plugins/harness-everything/skills/harness-everything/SKILL.md +43 -0
  398. package/plugins/harness-everything/skills/harness-everything/examples/README.md +9 -0
  399. package/plugins/harness-everything/skills/harness-everything/examples/todo-workflow.md +22 -0
  400. package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +128 -0
  401. package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +39 -0
  402. package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +193 -0
  403. package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +126 -0
  404. package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
  405. package/plugins/harness-everything/skills/harness-everything/scripts/bootstrap.js +212 -0
  406. package/plugins/harness-everything/skills/harness-everything/scripts/ensemble-policy.js +102 -0
  407. package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +212 -0
  408. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +612 -0
  409. package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +320 -0
  410. package/plugins/harness-everything/skills/harness-everything/scripts/self-heal.js +250 -0
  411. package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +459 -0
  412. package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +100 -0
  413. package/plugins/harness-everything/skills/improve-codebase-architecture/SKILL.md +38 -0
  414. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/DEEPENING.md +37 -0
  415. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/HTML-REPORT.md +123 -0
  416. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -0
  417. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/LANGUAGE.md +53 -0
  418. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
  419. package/plugins/harness-everything/skills/improve-codebase-architecture/references/README.md +16 -0
  420. package/plugins/harness-everything/skills/install-cognitive-os/SKILL.md +44 -0
  421. package/plugins/harness-everything/skills/install-cognitive-os/references/cognitive-loop.md +27 -0
  422. package/plugins/harness-everything/skills/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
  423. package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +38 -0
  424. package/plugins/harness-everything/skills/multi-agent-workspace/examples/csharp-monorepo.md +123 -0
  425. package/plugins/harness-everything/skills/multi-agent-workspace/examples/typescript-cli.md +126 -0
  426. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-claude.md +77 -0
  427. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-codex.md +146 -0
  428. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-continue.md +38 -0
  429. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-copilot.md +74 -0
  430. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
  431. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
  432. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/role-capabilities.md +108 -0
  433. package/plugins/harness-everything/skills/multi-agent-workspace/references/agency-agents.md +31 -0
  434. package/plugins/harness-everything/skills/multi-agent-workspace/references/architecture-guide.md +36 -0
  435. package/plugins/harness-everything/skills/multi-agent-workspace/references/orchestration.md +65 -0
  436. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/agency-catalog.js +190 -0
  437. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
  438. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/index_memory.js +104 -0
  439. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/path-boundary.js +59 -0
  440. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
  441. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/scaffold.js +408 -0
  442. package/plugins/harness-everything/skills/multi-agent-workspace/templates/AGENTS.md +20 -0
  443. package/plugins/harness-everything/skills/multi-agent-workspace/templates/README.md +17 -0
  444. package/plugins/harness-everything/skills/multi-agent-workspace/templates/agent-launcher.md +68 -0
  445. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-csharp.md +56 -0
  446. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-polyglot.md +62 -0
  447. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-ts.md +57 -0
  448. package/plugins/harness-everything/skills/multi-agent-workspace/templates/memory-keeper.md +37 -0
  449. package/plugins/harness-everything/skills/multi-agent-workspace/templates/requirement-analyzer.md +66 -0
  450. package/plugins/harness-everything/skills/multi-agent-workspace/templates/specialist-agent.md +34 -0
  451. package/plugins/harness-everything/skills/multi-agent-workspace/templates/task-verifier.md +56 -0
  452. package/plugins/harness-everything/skills/multi-agent-workspace/templates/tool-maker.md +33 -0
  453. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/01-init.md +22 -0
  454. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/02-analysis.md +23 -0
  455. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/03-generation.md +25 -0
  456. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/04-launcher.md +16 -0
  457. package/plugins/harness-everything/skills/repo-docs/SKILL.md +42 -0
  458. package/plugins/harness-everything/skills/repo-docs/references/process-guide.md +60 -0
  459. package/plugins/harness-everything/skills/repo-docs/templates/agents-template.md +264 -0
  460. package/plugins/harness-everything/skills/repo-docs/templates/knowledge-agents-template.md +50 -0
  461. package/plugins/harness-everything/skills/repo-docs/templates/knowledge-readme-template.md +83 -0
  462. package/plugins/harness-everything/skills/repo-docs/templates/manual-recon.md +117 -0
  463. package/plugins/harness-everything/skills/repo-docs/templates/multi-skills-readme-template.md +72 -0
  464. package/plugins/harness-everything/skills/repo-docs/templates/product-agents-template.md +78 -0
  465. package/plugins/harness-everything/skills/repo-docs/templates/product-readme-template.md +95 -0
  466. package/plugins/harness-everything/skills/repo-docs/templates/readme-template.md +136 -0
  467. package/plugins/harness-everything/skills/repo-docs/templates/single-skill-readme-template.md +70 -0
  468. package/plugins/harness-everything/skills/repo-docs/templates/skills-agents-template.md +60 -0
  469. package/plugins/harness-everything/skills/rewrite-commits/SKILL.md +37 -0
  470. package/plugins/harness-everything/skills/rewrite-commits/references/workflow.md +18 -0
  471. package/plugins/harness-everything/skills/security-review/SKILL.md +47 -0
  472. package/plugins/harness-everything/skills/security-review/guides/OWASP-PATTERNS.md +31 -0
  473. package/plugins/harness-everything/skills/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
  474. package/plugins/harness-everything/skills/security-review/references/security-checklist.md +478 -0
  475. package/plugins/harness-everything/skills/security-review/scripts/audit-secrets.js +85 -0
  476. package/plugins/harness-everything/skills/self-evolve/SKILL.md +45 -0
  477. package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +61 -0
  478. package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +272 -0
  479. package/plugins/harness-everything/skills/self-evolve/scripts/register-dynamic-skill.js +253 -0
  480. package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +125 -0
  481. package/plugins/harness-everything/skills/skill-creator/SKILL.md +47 -0
  482. package/plugins/harness-everything/skills/skill-creator/references/authoring-workflow.md +14 -0
  483. package/plugins/harness-everything/skills/skill-creator/references/dynamic-generation-contract.md +21 -0
  484. package/plugins/harness-everything/skills/skill-creator/references/quality-checklist.md +16 -0
  485. package/plugins/harness-everything/skills/skill-creator/references/quality-principles.md +81 -0
  486. package/plugins/harness-everything/skills/skill-creator/references/testing-workflow.md +27 -0
  487. package/plugins/harness-everything/skills/skill-style/SKILL.md +42 -0
  488. package/plugins/harness-everything/skills/skill-style/references/style-guide.md +62 -0
  489. package/plugins/harness-everything/skills/tdd/SKILL.md +38 -0
  490. package/plugins/harness-everything/skills/tdd/fixtures/pass-evidence.json +100 -0
  491. package/plugins/harness-everything/skills/tdd/fixtures/quality-scenarios.json +20 -0
  492. package/plugins/harness-everything/skills/tdd/guides/deep-modules.md +33 -0
  493. package/plugins/harness-everything/skills/tdd/guides/interface-design.md +31 -0
  494. package/plugins/harness-everything/skills/tdd/guides/mocking.md +59 -0
  495. package/plugins/harness-everything/skills/tdd/guides/refactoring.md +10 -0
  496. package/plugins/harness-everything/skills/tdd/guides/tests.md +61 -0
  497. package/plugins/harness-everything/skills/tdd/references/core-discipline.md +51 -0
  498. package/plugins/harness-everything/skills/tdd/references/integration-testing.md +13 -0
  499. package/plugins/harness-everything/skills/tdd/references/quality-model.md +68 -0
  500. package/plugins/harness-everything/skills/tdd/references/unit-testing.md +15 -0
  501. package/plugins/harness-everything/skills/tdd/schemas/quality-evidence.schema.json +211 -0
  502. package/plugins/harness-everything/skills/tdd/schemas/quality-report.schema.json +110 -0
  503. package/plugins/harness-everything/skills/tdd/scripts/quality-gate.js +475 -0
  504. package/plugins/harness-everything/skills/to-spec/SKILL.md +40 -0
  505. package/plugins/harness-everything/skills/to-spec/references/process.md +77 -0
  506. package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +239 -0
  507. package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +111 -0
  508. package/plugins/harness-everything/skills/to-spec/templates/cli-reference.md +31 -0
  509. package/plugins/harness-everything/skills/to-spec/templates/dev-doc.md +27 -0
  510. package/plugins/harness-everything/skills/to-spec/templates/feature-spec.md +59 -0
  511. package/plugins/harness-everything/skills/to-spec/templates/schema-doc.md +31 -0
  512. package/plugins/harness-everything/skills/to-tickets/SKILL.md +42 -0
  513. package/plugins/harness-everything/skills/to-tickets/references/publishing-and-templates.md +97 -0
  514. package/plugins/harness-everything/skills/todo-driven-workflow/SKILL.md +42 -0
  515. package/plugins/harness-everything/skills/todo-driven-workflow/references/execution-guide.md +27 -0
  516. package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +49 -0
  517. package/plugins/harness-everything/skills/using-git-worktrees/references/workflow-details.md +108 -0
  518. package/plugins/harness-everything/skills/verification-loop/SKILL.md +46 -0
  519. package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +116 -0
  520. package/plugins/harness-everything/skills/verification-loop/templates/verification-report.template.md +15 -0
  521. package/plugins/harness-everything/skills/verify-before-claim/SKILL.md +43 -0
  522. package/plugins/harness-everything/skills/verify-before-claim/references/verification-guide.md +73 -0
  523. package/plugins/harness-everything/skills/zoom-out/SKILL.md +42 -0
  524. package/plugins/harness-everything/skills/zoom-out/references/circuit-breaker.md +66 -0
  525. package/plugins/harness-everything/skills/zoom-out/templates/zoom-out-report.template.md +20 -0
  526. package/repo-docs/SKILL.md +3 -7
  527. package/rewrite-commits/SKILL.md +2 -2
  528. package/scripts/build-openai-submission.js +158 -0
  529. package/scripts/installer.js +86 -11
  530. package/scripts/lib/advisory-text.js +25 -18
  531. package/scripts/lib/execution-contract.js +70 -0
  532. package/scripts/lib/manifest.js +34 -38
  533. package/scripts/lib/path-boundary.js +90 -0
  534. package/scripts/lib/skills.js +124 -70
  535. package/scripts/lib/workspace.js +269 -7
  536. package/scripts/repository-contract.js +213 -0
  537. package/scripts/sync-current-docs.js +12 -0
  538. package/scripts/sync-openai-plugin.js +91 -0
  539. package/scripts/sync-release-version.js +212 -0
  540. package/security-review/SKILL.md +4 -7
  541. package/security-review/scripts/audit-secrets.js +9 -8
  542. package/self-evolve/SKILL.md +9 -11
  543. package/self-evolve/references/memory-resolution.md +4 -1
  544. package/self-evolve/scripts/persist-memory.js +221 -88
  545. package/self-evolve/scripts/register-dynamic-skill.js +12 -10
  546. package/self-evolve/scripts/self-regression.js +23 -8
  547. package/skill-creator/SKILL.md +12 -14
  548. package/skill-creator/references/quality-checklist.md +1 -0
  549. package/skill-style/SKILL.md +4 -4
  550. package/skill-style/references/style-guide.md +29 -3
  551. package/submission/openai/README.md +67 -0
  552. package/submission/openai/listing.json +31 -0
  553. package/submission/openai/test-cases.json +61 -0
  554. package/tdd/SKILL.md +20 -26
  555. package/tdd/fixtures/pass-evidence.json +100 -0
  556. package/tdd/fixtures/quality-scenarios.json +20 -0
  557. package/tdd/references/core-discipline.md +2 -0
  558. package/tdd/references/integration-testing.md +13 -0
  559. package/tdd/references/quality-model.md +68 -0
  560. package/tdd/references/unit-testing.md +15 -0
  561. package/tdd/schemas/quality-evidence.schema.json +211 -0
  562. package/tdd/schemas/quality-report.schema.json +110 -0
  563. package/tdd/scripts/quality-gate.js +475 -0
  564. package/to-spec/SKILL.md +3 -5
  565. package/to-spec/scripts/check-project-docs.js +42 -62
  566. package/to-spec/scripts/project-docs-resolver.js +111 -0
  567. package/to-tickets/SKILL.md +4 -4
  568. package/todo-driven-workflow/SKILL.md +6 -4
  569. package/using-git-worktrees/SKILL.md +3 -3
  570. package/verification-loop/SKILL.md +4 -4
  571. package/verify-before-claim/SKILL.md +2 -2
  572. package/zoom-out/SKILL.md +4 -4
  573. package/ab-test-deep-analysis-2026-08-27.md +0 -383
  574. package/ab-test-results-2026-08-27.md +0 -152
  575. package/ab-test-statistical-analysis-31cases.md +0 -193
  576. package/opencode-plugin/hooks/circuit-breaker.js +0 -121
  577. package/opencode-plugin/hooks/compliance.js +0 -128
  578. package/opencode-plugin/hooks/post-edit.js +0 -62
  579. package/opencode-plugin/hooks/pre-complete.js +0 -65
  580. package/opencode-plugin/test-hook-firing.js +0 -198
@@ -13,7 +13,7 @@
13
13
  * 1. Build identical case fixtures in OS temp workspaces.
14
14
  * 2. Run a control with no Harness files and a treatment with only the
15
15
  * case's named skill loaded; `--arm both` randomizes this order.
16
- * 3. Run: claude -p "<prompt>" --output-format json --max-turns N
16
+ * 3. Run: claude -p "<prompt>" --output-format stream-json --verbose --max-turns N
17
17
  * 4. Grade expectations[] against transcript text and workspace state.
18
18
  * 5. Write paired results with fixture/prompt hashes and attribution so any
19
19
  * grader verdict can be audited by a human.
@@ -23,6 +23,11 @@ const os = require('os');
23
23
  const path = require('path');
24
24
  const crypto = require('crypto');
25
25
  const { execFileSync } = require('child_process');
26
+ const {
27
+ gradeExecutionEvidence,
28
+ parseTranscriptFile,
29
+ } = require('./transcript-parser');
30
+ const { commandMatches, normalizeRepoPath } = require('../scripts/lib/execution-contract');
26
31
 
27
32
  const ROOT = path.resolve(__dirname, '..');
28
33
  const CASES_DIR = path.join(__dirname, 'cases');
@@ -32,7 +37,7 @@ const RESULTS_DIR = path.join(__dirname, 'results');
32
37
  // Minimal YAML subset parser for our case files (flat maps, lists of maps,
33
38
  // block scalars via |). Avoids adding dependencies to this package.
34
39
  function parseSimpleYaml(text) {
35
- const lines = text.split('\n');
40
+ const lines = text.replace(/\r\n?/g, '\n').split('\n');
36
41
  let i = 0;
37
42
  function parseBlock(indent) {
38
43
  const obj = {};
@@ -141,7 +146,10 @@ const PRESSURE_RULES = {
141
146
  // --- validate mode ----------------------------------------------------------
142
147
  function validate(cases) {
143
148
  let failures = 0;
144
- const EXPECT_TYPES = new Set(['trace_contains', 'trace_not_contains', 'file_contains', 'file_not_exists', 'command_exit_0']);
149
+ const EXPECT_TYPES = new Set([
150
+ 'trace_contains', 'trace_not_contains', 'file_contains', 'file_not_exists', 'command_exit_0',
151
+ 'tool_call', 'tool_attempted', 'tool_completed', 'tool_executed', 'tool_denied', 'execution_evidence',
152
+ ]);
145
153
  for (const c of cases) {
146
154
  const problems = [];
147
155
  if (!c.id) problems.push('missing id');
@@ -155,7 +163,9 @@ function validate(cases) {
155
163
  if (!resolveTreatmentSkills(c).length) problems.push('treatment must load at least one named Harness skill');
156
164
  for (const e of c.expectations || []) {
157
165
  if (!EXPECT_TYPES.has(e.type)) problems.push(`unknown expectation type "${e.type}"`);
158
- if (e.value === undefined && e.command === undefined) problems.push(`expectation ${e.type} needs value or command`);
166
+ if (e.value === undefined && e.command === undefined && e.tool === undefined && e.name === undefined && e.count === undefined) {
167
+ problems.push(`expectation ${e.type} needs value, tool, name, count, or command`);
168
+ }
159
169
  }
160
170
  if (c.pressure && !/skip|don't|not|quick|minutes/i.test(c.prompt)) {
161
171
  problems.push('pressure case prompt does not read as pressure');
@@ -180,8 +190,16 @@ function validate(cases) {
180
190
 
181
191
  // --- live run mode -----------------------------------------------------------
182
192
  function buildWorkspace(c) {
193
+ if (!c || !c.fixture || !Array.isArray(c.fixture.files)) throw new Error('fixture.files must be a non-empty array');
194
+ const fixtureFiles = c.fixture.files.map((file, index) => {
195
+ const relative = normalizeRepoPath(file && file.path);
196
+ if (!relative || relative === '.git' || relative.startsWith('.git/')) {
197
+ throw new Error(`fixture.files[${index}] path must stay inside the generated workspace`);
198
+ }
199
+ return { ...file, path: relative };
200
+ });
183
201
  const ws = fs.mkdtempSync(path.join(os.tmpdir(), `harness-behavioral-${c.id}-`));
184
- for (const f of c.fixture.files) {
202
+ for (const f of fixtureFiles) {
185
203
  const target = path.join(ws, f.path);
186
204
  fs.mkdirSync(path.dirname(target), { recursive: true });
187
205
  fs.writeFileSync(target, typeof f.content === 'string' ? f.content.replace(/\n$/, '') + '\n' : String(f.content));
@@ -260,21 +278,74 @@ function buildEngineInvocation(engine, prompt, ws, maxTurns, arm = 'treatment')
260
278
  args.push(prompt);
261
279
  return { command: 'opencode', args };
262
280
  }
263
- const args = ['-p', prompt, '--output-format', 'json', '--max-turns', String(maxTurns), '--permission-mode', 'acceptEdits', '--setting-sources', 'project,local'];
281
+ // acceptEdits only auto-approves file edits, not Bash/PowerShell tool calls -
282
+ // a headless -p session has no human to answer those prompts, so any case
283
+ // needing a shell command (git, npm, node scripts) stalled with every tool
284
+ // call denied and the model just describing what it would do. This harness
285
+ // needs the same full autonomy opencode's --auto gives it, in the same kind
286
+ // of disposable os.tmpdir() fixture workspace.
287
+ const args = ['-p', prompt, '--output-format', 'stream-json', '--verbose', '--max-turns', String(maxTurns), '--dangerously-skip-permissions', '--setting-sources', 'project,local'];
288
+ const model = process.env.BEHAVIORAL_MODEL;
289
+ if (model) args.push('--model', model);
264
290
  if (arm === 'baseline') args.push('--safe-mode');
265
291
  return { command: 'claude', args };
266
292
  }
267
293
 
294
+ function expandWindowsBatchPath(value, batchPath) {
295
+ const base = path.dirname(batchPath) + path.sep;
296
+ return value
297
+ .replace(/%~dp0/gi, base)
298
+ .replace(/%dp0%/gi, base)
299
+ .replace(/^"|"$/g, '')
300
+ .trim();
301
+ }
302
+
303
+ function windowsCommandCandidates(command) {
304
+ try {
305
+ return execFileSync('where.exe', [command], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
306
+ .split(/\r?\n/)
307
+ .map(line => line.trim())
308
+ .filter(line => line && !/^INFO:/i.test(line));
309
+ } catch {
310
+ return [];
311
+ }
312
+ }
313
+
314
+ function resolveCliInvocation(invocation) {
315
+ if (process.platform !== 'win32') return invocation;
316
+ const candidates = windowsCommandCandidates(invocation.command);
317
+ const native = candidates.find(candidate => /\.exe$/i.test(candidate));
318
+ if (native) return { command: native, args: invocation.args.slice() };
319
+ const shim = candidates.find(candidate => /\.(?:cmd|bat)$/i.test(candidate));
320
+ if (shim) {
321
+ let source = '';
322
+ try { source = fs.readFileSync(shim, 'utf8'); } catch { source = ''; }
323
+ const nodeEntry = source.match(/(?:^|\s)(?:node(?:\.exe)?)(?:\s+)(?:"([^"]+\.js)"|([^\s]+\.js))/im);
324
+ if (nodeEntry) {
325
+ const entry = expandWindowsBatchPath(nodeEntry[1] || nodeEntry[2], shim);
326
+ return { command: process.execPath, args: [entry, ...invocation.args] };
327
+ }
328
+ const binaryEntry = source.match(/"?([^"\r\n]+\.exe)"?\s+%\*/i);
329
+ if (binaryEntry) {
330
+ const entry = expandWindowsBatchPath(binaryEntry[1], shim);
331
+ return { command: entry, args: invocation.args.slice() };
332
+ }
333
+ }
334
+ const direct = candidates.find(candidate => !/\.(?:cmd|bat|ps1)$/i.test(candidate));
335
+ return direct ? { command: direct, args: invocation.args.slice() } : invocation;
336
+ }
337
+
268
338
  function runHeadless(prompt, ws, maxTurns, engine, arm) {
269
339
  // Keep the transcript OUTSIDE the workspace: grader artifacts must never
270
340
  // show up in git-status-based scope checks.
271
- const outPath = path.join(path.dirname(ws), path.basename(ws) + '.transcript.json');
341
+ const outPath = path.join(path.dirname(ws), path.basename(ws) + '.transcript.jsonl');
272
342
  const invocation = buildEngineInvocation(engine, prompt, ws, maxTurns, arm);
343
+ const executable = resolveCliInvocation(invocation);
273
344
  const outputFd = fs.openSync(outPath, 'w');
274
345
  try {
275
346
  // Pass the prompt as one argv element. A shell wrapper on Windows can
276
347
  // reinterpret spaces and punctuation, truncating prompts to `Add.`.
277
- execFileSync(invocation.command, invocation.args, {
348
+ execFileSync(executable.command, executable.args, {
278
349
  cwd: ws,
279
350
  stdio: ['ignore', outputFd, 'inherit'],
280
351
  timeout: engine === 'opencode' ? 20 * 60 * 1000 : 15 * 60 * 1000,
@@ -290,92 +361,299 @@ function runHeadless(prompt, ws, maxTurns, engine, arm) {
290
361
  // Tool outputs embed installed skill text, so keyword graders over raw
291
362
  // transcripts produce false positives (a read of zoom-out/SKILL.md counts as
292
363
  // "invoked zoom-out"). Trace = assistant text + tool inputs only.
293
- function extractAgentTrace(transcriptPath) {
364
+ const COMPLETED_TOOL_STATUSES = new Set(['completed', 'complete', 'success', 'succeeded', 'done', 'finished']);
365
+ const DENIED_TOOL_STATUSES = new Set(['denied', 'rejected', 'blocked', 'cancelled', 'canceled', 'error', 'failed']);
366
+
367
+ function firstDefined(...values) {
368
+ return values.find(value => value !== undefined && value !== null);
369
+ }
370
+
371
+ function eventPart(event) {
372
+ return event && event.part && typeof event.part === 'object' ? event.part : event;
373
+ }
374
+
375
+ function eventType(event) {
376
+ return String(event && event.type || '').toLowerCase();
377
+ }
378
+
379
+ function eventStatus(event, part) {
380
+ const state = part && part.state && typeof part.state === 'object' ? part.state : {};
381
+ return String(firstDefined(state.status, part && part.status, event && event.status, '')).toLowerCase();
382
+ }
383
+
384
+ function eventToolName(event, part) {
385
+ const state = part && part.state && typeof part.state === 'object' ? part.state : {};
386
+ return String(firstDefined(part && part.tool, part && part.name, state.tool, event && event.tool, event && event.name, 'tool'));
387
+ }
388
+
389
+ function eventInput(event, part) {
390
+ const state = part && part.state && typeof part.state === 'object' ? part.state : {};
391
+ return firstDefined(state.input, part && part.input, part && part.arguments, event && event.input, event && event.arguments);
392
+ }
393
+
394
+ function eventId(event, part) {
395
+ const state = part && part.state && typeof part.state === 'object' ? part.state : {};
396
+ return firstDefined(part && (part.callID || part.callId || part.id || part.tool_use_id), state.callID, state.callId, event && (event.callID || event.callId || event.id || event.tool_use_id));
397
+ }
398
+
399
+ function isToolEvent(event, part) {
400
+ const type = eventType(event);
401
+ const partType = String(part && part.type || '').toLowerCase();
402
+ return type.includes('tool') || partType === 'tool' || partType === 'tool_use' ||
403
+ eventToolName(event, part) !== 'tool' || eventInput(event, part) !== undefined;
404
+ }
405
+
406
+ function isEditOperation(tool, input) {
407
+ const name = String(tool || '').toLowerCase();
408
+ if (/(^|[-_])(edit|write|patch|create|delete|move|rename)([-_]|$)/.test(name) ||
409
+ /^(edit|write|apply_patch|create_file|delete_file|move_file|rename_file)$/.test(name)) return true;
410
+ const command = input && typeof input === 'object' ? firstDefined(input.command, input.cmd, input.script) : input;
411
+ if (typeof command === 'string' && /(?:>>?|\btee\b|\bset-content\b|\bout-file\b|\bwritefile\b|\brename(?:-item)?\b|\bremove(?:-item)?\b|\b(?:sed|perl)\b[^\r\n]*\s-i\b)/i.test(command)) return true;
412
+ const target = input && typeof input === 'object'
413
+ ? firstDefined(input.file_path, input.filePath, input.path, input.filename)
414
+ : null;
415
+ return typeof target === 'string' && /\.(?:patch|diff)$/i.test(target);
416
+ }
417
+
418
+ function executionState(status, type) {
419
+ const normalizedType = String(type || '').toLowerCase();
420
+ if (DENIED_TOOL_STATUSES.has(status) || normalizedType.includes('denied') || normalizedType.includes('rejected')) return 'denied';
421
+ if (status && !COMPLETED_TOOL_STATUSES.has(status)) return 'attempted';
422
+ if (COMPLETED_TOOL_STATUSES.has(status) || (!status && (normalizedType.includes('result') || normalizedType.includes('completed')))) return 'executed';
423
+ return 'attempted';
424
+ }
425
+
426
+ // Keep a second, event-oriented view for ordered execution and edit evidence.
427
+ // transcript-parser.js remains the authoritative command-aware grader.
428
+ function parseTranscriptEvents(transcriptPath) {
294
429
  const raw = fs.readFileSync(transcriptPath, 'utf8');
295
- const chunks = [];
430
+ const events = [];
431
+ const pendingById = new Map();
432
+ let sequence = 0;
433
+ const addText = text => {
434
+ if (typeof text === 'string' && text) events.push({ kind: 'text', text, sequence: sequence++ });
435
+ };
436
+ const addTool = (event, part) => {
437
+ const type = eventType(event);
438
+ const input = eventInput(event, part) ?? {};
439
+ const tool = eventToolName(event, part);
440
+ const id = eventId(event, part);
441
+ const state = executionState(eventStatus(event, part), type);
442
+ const existing = id && pendingById.get(String(id));
443
+ if (existing) {
444
+ if (input && Object.keys(input).length) existing.input = input;
445
+ existing.tool = tool !== 'tool' ? tool : existing.tool;
446
+ if (state === 'executed' || state === 'denied') existing.state = state;
447
+ existing.executed = existing.state === 'executed';
448
+ existing.denied = existing.state === 'denied';
449
+ return;
450
+ }
451
+ const item = {
452
+ kind: 'tool', tool, input, id: id === undefined ? null : String(id), state,
453
+ attempted: true, executed: state === 'executed', denied: state === 'denied',
454
+ edit: isEditOperation(tool, input), sequence: sequence++, raw_type: type,
455
+ };
456
+ events.push(item);
457
+ if (id !== undefined) pendingById.set(String(id), item);
458
+ };
459
+ const visit = (value, includeText = true) => {
460
+ if (!value || typeof value !== 'object') return;
461
+ if (Array.isArray(value)) { value.forEach(item => visit(item, includeText)); return; }
462
+ const part = eventPart(value);
463
+ const type = eventType(value);
464
+ if (type === 'text' || String(part && part.type || '').toLowerCase() === 'text') {
465
+ if (includeText) addText(part && part.text !== undefined ? part.text : value.text);
466
+ if (value.part || !value.message) return;
467
+ }
468
+ if (value.part || isToolEvent(value, part)) {
469
+ if (isToolEvent(value, part)) addTool(value, part);
470
+ if (value.part) return;
471
+ }
472
+ if (value.message && value.message.content) visit(value.message.content, value.type !== 'user');
473
+ if (value.content && value.content !== value.message) visit(value.content, includeText);
474
+ if (!value.part && typeof value.result === 'string' && includeText) addText(value.result);
475
+ };
296
476
  let structured = false;
297
477
  for (const line of raw.split('\n')) {
298
478
  if (!line.trim()) continue;
299
- let e;
300
- try { e = JSON.parse(line); } catch { continue; }
301
- const p = e && e.part;
302
- if (!p) continue;
303
- if (e.type === 'text' && typeof p.text === 'string') {
304
- chunks.push(p.text);
305
- structured = true;
306
- } else if (String(e.type).includes('tool')) {
307
- const input = p.state && p.state.input !== undefined ? p.state.input : p.input;
308
- chunks.push(`[${p.tool || 'tool'}] ` + JSON.stringify(input ?? {}));
309
- structured = true;
310
- }
479
+ try {
480
+ const value = JSON.parse(line);
481
+ const before = events.length;
482
+ visit(value);
483
+ if (events.length > before) structured = true;
484
+ } catch { /* malformed/non-JSON lines are retained only by the raw fallback */ }
311
485
  }
312
- // claude -p --output-format json emits one object; grade its result text.
313
486
  if (!structured) {
314
487
  try {
315
- const obj = JSON.parse(raw);
316
- if (obj && typeof obj.result === 'string') return obj.result;
317
- } catch { /* fall through */ }
318
- return raw;
488
+ const value = JSON.parse(raw);
489
+ if (value && typeof value.result === 'string') addText(value.result);
490
+ else visit(value);
491
+ } catch { /* raw fallback handled by renderAgentTrace */ }
319
492
  }
320
- return chunks.join('\n');
493
+ return { raw, structured: events.length > 0, events };
321
494
  }
322
495
 
323
- function extractSessionMetadata(transcriptPath) {
324
- try {
325
- const raw = fs.readFileSync(transcriptPath, 'utf8');
326
- const obj = JSON.parse(raw);
327
- const cost = [obj.total_cost_usd, obj.cost_usd, obj.cost].find(value => typeof value === 'number');
328
- return {
329
- cost: cost === undefined ? null : cost,
330
- usage: obj.usage || null,
331
- duration_ms: typeof obj.duration_ms === 'number' ? obj.duration_ms : null,
332
- num_turns: typeof obj.num_turns === 'number' ? obj.num_turns : null,
333
- };
334
- } catch {
335
- return { cost: null, usage: null, duration_ms: null, num_turns: null };
496
+ function renderAgentTrace(parsed) {
497
+ if (!parsed.structured) return parsed.raw;
498
+ return parsed.events.map(event => event.kind === 'text'
499
+ ? event.text
500
+ : `[${event.tool}] ${JSON.stringify(event.input ?? {})}`).join('\n');
501
+ }
502
+
503
+ function extractAgentEvents(transcriptPath) {
504
+ return parseTranscriptEvents(transcriptPath).events;
505
+ }
506
+
507
+ function extractAgentTrace(transcriptPath) {
508
+ return renderAgentTrace(parseTranscriptEvents(transcriptPath));
509
+ }
510
+
511
+ function toolTarget(expectation) {
512
+ if (expectation.value && typeof expectation.value === 'object') return expectation.value;
513
+ if (expectation.command !== undefined) return { command: expectation.command };
514
+ if (expectation.tool !== undefined) return { tool: expectation.tool };
515
+ if (expectation.name !== undefined) return { tool: expectation.name };
516
+ if (typeof expectation.value === 'string') return { command: expectation.value };
517
+ if (typeof expectation.value === 'number') return { tool: String(expectation.value) };
518
+ return {};
519
+ }
520
+
521
+ function toolMatches(event, target) {
522
+ if (!event || event.kind !== 'tool') return false;
523
+ const expectedTool = target.tool || target.name;
524
+ if (expectedTool && String(event.tool).toLowerCase() !== String(expectedTool).toLowerCase()) return false;
525
+ if (target.command !== undefined) {
526
+ const actual = event.input && typeof event.input === 'object'
527
+ ? firstDefined(event.input.command, event.input.cmd, event.input.script)
528
+ : event.input;
529
+ if (!commandMatches(actual, target.command)) return false;
336
530
  }
531
+ return true;
532
+ }
533
+
534
+ function gradeToolExpectation(events, expectation) {
535
+ const target = toolTarget(expectation);
536
+ const type = expectation.type === 'execution_evidence' ? 'tool_executed' : expectation.type;
537
+ return events.some(event => {
538
+ if (!toolMatches(event, target)) return false;
539
+ if (type === 'tool_executed' || type === 'tool_completed') {
540
+ if (!event.executed) return false;
541
+ if (expectation.after_edit && !events.some(previous => previous.kind === 'tool' && previous.edit && previous.executed && previous.sequence < event.sequence)) return false;
542
+ } else if (type === 'tool_denied') {
543
+ if (!event.denied) return false;
544
+ } else if (type === 'tool_attempted' || type === 'tool_call') {
545
+ if (!event.attempted) return false;
546
+ }
547
+ return true;
548
+ });
549
+ }
550
+
551
+ function extractSessionMetadata(transcriptPath, engine = 'auto') {
552
+ const parsed = parseTranscriptFile(transcriptPath, engine);
553
+ return {
554
+ ...parsed.metadata,
555
+ parse_status: parsed.parseStatus,
556
+ parse_error: parsed.parseError,
557
+ format: parsed.format,
558
+ };
337
559
  }
338
560
 
339
561
  function runShellCommand(command, cwd) {
562
+ if (typeof command !== 'string' || !command.trim()) throw new Error('command must be non-empty');
340
563
  if (process.platform === 'win32') {
341
- return execFileSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', command], { cwd, stdio: 'ignore' });
564
+ const scriptDir = fs.mkdtempSync(path.join(os.tmpdir(), 'behavioral-shell-'));
565
+ const scriptPath = path.join(scriptDir, 'command.cmd');
566
+ try {
567
+ fs.writeFileSync(scriptPath, `@echo off\r\n${command}\r\n`, 'utf8');
568
+ return execFileSync(process.env.ComSpec || 'cmd.exe', ['/d', '/c', scriptPath], { cwd, stdio: 'ignore' });
569
+ } finally {
570
+ fs.rmSync(scriptDir, { recursive: true, force: true });
571
+ }
342
572
  }
343
573
  return execFileSync('/bin/sh', ['-c', command], { cwd, stdio: 'ignore' });
344
574
  }
345
575
 
346
- function grade(c, ws, transcriptPath) {
347
- const trace = extractAgentTrace(transcriptPath);
576
+ const EXECUTION_COMMAND_RE = /^(?:npm|npx|node|git|pnpm|yarn|bun|cargo|python(?:3)?|pytest|go|ruby|java|make|rm|mv|cp|mkdir|chmod|powershell|pwsh|bash|sh|cmd)(?:\s|$)/i;
577
+
578
+ function isExecutionLikeTraceValue(value) {
579
+ if (typeof value !== 'string') return false;
580
+ const text = value.trim();
581
+ return (EXECUTION_COMMAND_RE.test(text) && /\s+\S/.test(text))
582
+ || /(?:^|[/\\])[\w.-]+\.(?:c?js|mjs|py|sh|ps1|bat|cmd)\b/i.test(text);
583
+ }
584
+
585
+ function grade(c, ws, transcriptPath, engine = 'auto') {
586
+ const parsed = parseTranscriptFile(transcriptPath, engine);
587
+ const ordered = parseTranscriptEvents(transcriptPath);
588
+ const trace = renderAgentTrace(ordered);
348
589
  const results = [];
349
590
  for (const e of c.expectations) {
350
591
  let pass = false;
592
+ let status = 'fail';
593
+ let reason = null;
351
594
  try {
352
- if (e.type === 'trace_contains') pass = trace.includes(e.value);
353
- else if (e.type === 'trace_not_contains') pass = !trace.includes(e.value);
354
- else if (e.type === 'file_contains') pass = fs.readFileSync(path.join(ws, e.path), 'utf8').includes(e.value);
355
- else if (e.type === 'file_not_exists') pass = !fs.existsSync(path.join(ws, e.path));
356
- else if (e.type === 'command_exit_0') {
595
+ if (['tool_call', 'tool_attempted', 'tool_completed', 'tool_executed', 'tool_denied', 'execution_evidence'].includes(e.type)) {
596
+ const evidenceResult = gradeExecutionEvidence(e, parsed);
597
+ pass = evidenceResult.pass;
598
+ status = evidenceResult.status;
599
+ reason = evidenceResult.reason;
600
+ if (pass && e.after_edit && !gradeToolExpectation(ordered.events, e)) {
601
+ pass = false;
602
+ status = 'fail';
603
+ reason = 'expected a completed edit before the matching tool execution';
604
+ }
605
+ } else if (e.type === 'trace_contains' || e.type === 'trace_not_contains') {
606
+ if (e.type === 'trace_contains' && isExecutionLikeTraceValue(e.value)) {
607
+ status = 'inconclusive';
608
+ reason = 'execution-like trace assertions require structured tool evidence';
609
+ } else if (parsed.parseStatus !== 'parsed') {
610
+ status = 'inconclusive';
611
+ reason = parsed.parseError || 'transcript parser did not produce a complete structured trace';
612
+ } else {
613
+ pass = e.type === 'trace_contains' ? trace.includes(e.value) : !trace.includes(e.value);
614
+ status = pass ? 'pass' : 'fail';
615
+ }
616
+ } else if (e.type === 'file_contains') {
617
+ pass = fs.readFileSync(path.join(ws, e.path), 'utf8').includes(e.value);
618
+ status = pass ? 'pass' : 'fail';
619
+ } else if (e.type === 'file_not_exists') {
620
+ pass = !fs.existsSync(path.join(ws, e.path));
621
+ status = pass ? 'pass' : 'fail';
622
+ } else if (e.type === 'command_exit_0') {
357
623
  runShellCommand(e.command, ws);
358
624
  pass = true;
625
+ status = 'pass';
359
626
  }
360
627
  } catch (err) {
361
628
  pass = false;
629
+ status = status === 'inconclusive' ? status : 'fail';
630
+ reason = err.message;
362
631
  }
363
- results.push({ ...e, pass });
632
+ results.push({ ...e, pass, status, reason });
364
633
  }
365
634
  // Informational expectations are reported but never gate the outcome:
366
635
  // they capture context-dependent behavior (e.g. zoom-out only becomes
367
636
  // obligatory once the breaker actually trips) that would otherwise
368
637
  // produce meaningless failures.
369
638
  const gating = results.filter((r) => !r.informational);
370
- return { results, passed: gating.length > 0 && gating.every((g) => g.pass) };
639
+ const inconclusive = gating.some(result => result.status === 'inconclusive');
640
+ const passed = gating.length > 0 && !inconclusive && gating.every((g) => g.pass);
641
+ return {
642
+ results,
643
+ passed,
644
+ status: inconclusive ? 'inconclusive' : passed ? 'pass' : 'fail',
645
+ parsed,
646
+ trace,
647
+ executionEvidence: parsed.executionEvidence,
648
+ };
371
649
  }
372
650
 
373
651
  function resolveEngine(requested) {
374
- const has = (cmd) => {
375
- try {
376
- // Try direct path first
377
- execFileSync(cmd, ['--version'], { stdio: 'pipe' });
378
- return true;
652
+ const has = (cmd) => {
653
+ try {
654
+ const invocation = resolveCliInvocation({ command: cmd, args: [] });
655
+ execFileSync(invocation.command, [...invocation.args, '--version'], { stdio: 'pipe' });
656
+ return true;
379
657
  } catch {
380
658
  return false;
381
659
  }
@@ -394,7 +672,10 @@ function sha256(value) {
394
672
  }
395
673
 
396
674
  function countToolCalls(trace) {
397
- return (trace.match(/^\[[^\]]+\]/gm) || []).length;
675
+ const evidence = trace && (trace.executionEvidence || (trace.parsed && trace.parsed.executionEvidence));
676
+ if (evidence) return evidence.counts.attempted;
677
+ const text = typeof trace === 'string' ? trace : trace && trace.trace;
678
+ return typeof text === 'string' ? (text.match(/^\[[^\]]+\]/gm) || []).length : null;
398
679
  }
399
680
 
400
681
  function treatmentSkills(c) {
@@ -421,9 +702,11 @@ function runArm(c, engine, arm, pairId) {
421
702
  };
422
703
  try {
423
704
  const transcriptPath = runHeadless(c.prompt, ws, c.max_turns, engine, arm);
424
- const { results, passed } = grade(c, ws, transcriptPath);
425
- const trace = extractAgentTrace(transcriptPath);
426
- const metadata = extractSessionMetadata(transcriptPath);
705
+ const graded = grade(c, ws, transcriptPath, engine);
706
+ const { results, passed } = graded;
707
+ const trace = graded.trace;
708
+ const metadata = graded.parsed.metadata;
709
+ const evidence = graded.executionEvidence;
427
710
  if (attributionRequired && loadedSkills.length === 0) {
428
711
  throw new Error('treatment attribution is required but no skill was loaded');
429
712
  }
@@ -434,9 +717,18 @@ function runArm(c, engine, arm, pairId) {
434
717
  usage: metadata.usage,
435
718
  duration_ms: metadata.duration_ms,
436
719
  num_turns: metadata.num_turns,
437
- expectations: results.map(({ description, pass }) => ({ description, pass })),
438
- outcome: passed ? 'pass' : 'fail',
439
- tool_call_count: countToolCalls(trace),
720
+ model_name: metadata.model,
721
+ expectations: results.map(({ type, value, command, after_edit, description, pass, status, reason }) => ({
722
+ type, value, command, after_edit, description, pass, status, reason,
723
+ })),
724
+ outcome: graded.status,
725
+ parse_status: graded.parsed.parseStatus,
726
+ parse_error: graded.parsed.parseError,
727
+ transcript_format: graded.parsed.format,
728
+ cli_version: metadata.cli_version,
729
+ tool_call_count: countToolCalls(graded),
730
+ tool_call_counts: evidence.counts,
731
+ execution_evidence: evidence,
440
732
  trace_preview: trace.slice(0, 500),
441
733
  };
442
734
  } catch (err) {
@@ -444,18 +736,84 @@ function runArm(c, engine, arm, pairId) {
444
736
  ...record,
445
737
  outcome: 'session-error',
446
738
  error: err.message.slice(0, 500),
447
- tool_call_count: 0,
739
+ parse_status: 'unavailable',
740
+ tool_call_count: null,
741
+ tool_call_counts: { attempted: null, completed: null, denied: null, unresolved: null },
448
742
  };
449
743
  }
450
744
  }
451
745
 
746
+ function isDefinitiveArm(arm) {
747
+ return !!arm && (arm.outcome === 'pass' || arm.outcome === 'fail');
748
+ }
749
+
750
+ function isCompletedPair(record) {
751
+ return !!record && isDefinitiveArm(record.arms && record.arms.baseline)
752
+ && isDefinitiveArm(record.arms && record.arms.treatment);
753
+ }
754
+
452
755
  function pairVerdict(baseline, treatment) {
756
+ if (!isDefinitiveArm(baseline) || !isDefinitiveArm(treatment)) return 'INCONCLUSIVE';
453
757
  if (baseline.outcome === 'pass' && treatment.outcome === 'pass') return 'INCONCLUSIVE';
454
758
  if (baseline.outcome !== 'pass' && treatment.outcome === 'pass') return 'EFFECTIVE';
455
759
  if (baseline.outcome === 'pass' && treatment.outcome !== 'pass') return 'HARMFUL';
456
760
  return 'INEFFECTIVE';
457
761
  }
458
762
 
763
+ function summarizePairResults(pairResults) {
764
+ const completed = pairResults.filter(isCompletedPair);
765
+ const sessionFailures = pairResults.filter(record => Object.values(record.arms || {})
766
+ .some(arm => arm.outcome === 'session-error')).length;
767
+ const effective = completed.filter(record => record.verdict === 'EFFECTIVE').length;
768
+ const categorySummary = {};
769
+ for (const record of pairResults) {
770
+ const category = record.pressure_category || 'unclassified';
771
+ if (!categorySummary[category]) {
772
+ categorySummary[category] = { requested: 0, completed: 0, pass: 0, fail: 0 };
773
+ }
774
+ categorySummary[category].requested++;
775
+ }
776
+ for (const record of completed) {
777
+ const category = record.pressure_category || 'unclassified';
778
+ const bucket = categorySummary[category];
779
+ bucket.completed++;
780
+ if (record.arms.treatment.outcome === 'pass') bucket.pass++;
781
+ else bucket.fail++;
782
+ }
783
+ for (const bucket of Object.values(categorySummary)) {
784
+ bucket.pass_rate = bucket.completed
785
+ ? Number((bucket.pass / bucket.completed).toFixed(4))
786
+ : null;
787
+ bucket.pass_rate_ci95 = wilsonInterval(bucket.pass, bucket.completed);
788
+ }
789
+ const costs = completed.flatMap(record => Object.values(record.arms || {}).map(arm => arm.cost));
790
+ const cost = costs.length === completed.length * 2 && costs.every(value => typeof value === 'number')
791
+ ? Number(costs.reduce((sum, value) => sum + value, 0).toFixed(6))
792
+ : null;
793
+ const toolCallDelta = completed.map(record => {
794
+ const baseline = record.arms.baseline.tool_call_count;
795
+ const treatment = record.arms.treatment.tool_call_count;
796
+ return {
797
+ id: record.id,
798
+ baseline,
799
+ treatment,
800
+ treatment_minus_baseline: typeof baseline === 'number' && typeof treatment === 'number'
801
+ ? treatment - baseline
802
+ : null,
803
+ };
804
+ });
805
+ return {
806
+ cost,
807
+ completed_pairs: completed.length,
808
+ session_failures: sessionFailures,
809
+ tool_call_delta: toolCallDelta,
810
+ verdicts: Object.fromEntries(['EFFECTIVE', 'INEFFECTIVE', 'INCONCLUSIVE', 'HARMFUL']
811
+ .map(verdict => [verdict, completed.filter(record => record.verdict === verdict).length])),
812
+ effective_rate_ci95: wilsonInterval(effective, completed.length),
813
+ pressure_categories: categorySummary,
814
+ };
815
+ }
816
+
459
817
  function wilsonInterval(successes, total) {
460
818
  if (!total) return null;
461
819
  const z = 1.96;
@@ -491,7 +849,7 @@ function runLive(filter, engineArg, armArg = 'treatment') {
491
849
  id: c.id,
492
850
  pair_id: pairId,
493
851
  engine,
494
- model: engine === 'opencode' ? (process.env.BEHAVIORAL_MODEL || 'openai/gpt-5-mini') : 'claude-default',
852
+ model: engine === 'opencode' ? (process.env.BEHAVIORAL_MODEL || 'openai/gpt-5-mini') : (process.env.BEHAVIORAL_MODEL || 'claude-default'),
495
853
  pressure: !!c.pressure,
496
854
  pressure_category: c.pressure_category || null,
497
855
  sample_unit: 'paired case',
@@ -503,57 +861,26 @@ function runLive(filter, engineArg, armArg = 'treatment') {
503
861
  date: new Date().toISOString(),
504
862
  };
505
863
  pairResults.push(record);
506
- if (Object.values(arms).some(arm => arm.outcome === 'session-error')) sessionFailures++;
507
864
  for (const arm of Object.values(arms)) {
508
865
  console.log(` ${arm.arm}: ${arm.outcome} (${arm.loaded_skills.length ? arm.loaded_skills.join(', ') : 'no Harness skills'})`);
509
866
  }
867
+ if (Object.values(arms).some(arm => arm.outcome === 'session-error')) sessionFailures++;
510
868
  if (armArg === 'both') console.log(` Verdict: ${record.verdict}`);
511
869
  const suffix = armArg === 'both' ? 'pair' : armArg;
512
870
  fs.writeFileSync(path.join(RESULTS_DIR, `${record.date.slice(0, 10)}-${c.id}-${suffix}.json`), JSON.stringify(record, null, 2));
513
871
  }
514
872
 
873
+ let exitCode = sessionFailures ? 1 : 0;
515
874
  if (armArg === 'both') {
516
- const completed = pairResults.filter(r => !Object.values(r.arms).some(a => a.outcome === 'session-error'));
517
- const effective = completed.filter(r => r.verdict === 'EFFECTIVE').length;
518
- const categorySummary = {};
519
- for (const record of pairResults) {
520
- const category = record.pressure_category || 'unclassified';
521
- if (!categorySummary[category]) categorySummary[category] = { requested: 0, completed: 0, pass: 0, fail: 0 };
522
- categorySummary[category].requested++;
523
- }
524
- for (const record of completed) {
525
- const category = record.pressure_category || 'unclassified';
526
- const bucket = categorySummary[category];
527
- bucket.completed++;
528
- if (record.arms.treatment.outcome === 'pass') bucket.pass++;
529
- else bucket.fail++;
530
- }
531
- for (const bucket of Object.values(categorySummary)) {
532
- bucket.pass_rate = Number((bucket.pass / bucket.completed).toFixed(4));
533
- bucket.pass_rate_ci95 = wilsonInterval(bucket.pass, bucket.completed);
534
- }
535
- const costs = completed.flatMap(record => Object.values(record.arms).map(arm => arm.cost));
536
- const cost = costs.length === completed.length * 2 && costs.every(value => typeof value === 'number')
537
- ? Number(costs.reduce((sum, value) => sum + value, 0).toFixed(6))
538
- : null;
875
+ const aggregate = summarizePairResults(pairResults);
539
876
  const summary = {
540
877
  date: new Date().toISOString(),
541
878
  protocol: 'paired-randomized-v1',
542
879
  requested_sample_size: cases.length,
543
- completed_pairs: completed.length,
544
- session_failures: pairResults.length - completed.length,
545
- boundary_compliance: completed.length === cases.length ? 'complete' : 'incomplete',
546
- cost,
547
- tool_call_delta: completed.map(r => ({
548
- id: r.id,
549
- baseline: r.arms.baseline.tool_call_count,
550
- treatment: r.arms.treatment.tool_call_count,
551
- treatment_minus_baseline: r.arms.treatment.tool_call_count - r.arms.baseline.tool_call_count,
552
- })),
553
- verdicts: Object.fromEntries(['EFFECTIVE', 'INEFFECTIVE', 'INCONCLUSIVE', 'HARMFUL'].map(v => [v, completed.filter(r => r.verdict === v).length])),
554
- effective_rate_ci95: wilsonInterval(effective, completed.length),
555
- pressure_categories: categorySummary,
556
- interpretation: completed.length < 2
880
+ ...aggregate,
881
+ boundary_compliance: aggregate.completed_pairs === cases.length ? 'complete' : 'incomplete',
882
+ cost: aggregate.cost,
883
+ interpretation: aggregate.completed_pairs < 2
557
884
  ? 'insufficient paired samples for an effectiveness claim'
558
885
  : 'report paired outcomes; do not treat INCONCLUSIVE pairs as skill lift',
559
886
  };
@@ -561,8 +888,9 @@ function runLive(filter, engineArg, armArg = 'treatment') {
561
888
  fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2));
562
889
  console.log(`\nPaired summary: ${summaryPath}`);
563
890
  console.log(`Completed pairs: ${summary.completed_pairs}/${summary.requested_sample_size}`);
891
+ exitCode = aggregate.session_failures ? 1 : 0;
564
892
  }
565
- process.exit(sessionFailures ? 1 : 0);
893
+ process.exit(exitCode);
566
894
  }
567
895
 
568
896
  // ----------------------------------------------------------------------------
@@ -584,4 +912,17 @@ function main() {
584
912
 
585
913
  if (require.main === module) main();
586
914
 
587
- module.exports = { buildEngineInvocation, extractAgentTrace, extractSessionMetadata, grade, parseSimpleYaml, validate };
915
+ module.exports = {
916
+ buildEngineInvocation,
917
+ buildWorkspace,
918
+ countToolCalls,
919
+ extractAgentEvents,
920
+ extractAgentTrace,
921
+ extractSessionMetadata,
922
+ grade,
923
+ parseTranscriptEvents,
924
+ pairVerdict,
925
+ parseSimpleYaml,
926
+ summarizePairResults,
927
+ validate,
928
+ };