harness-everything 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (573) 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/ci.yml +23 -18
  5. package/.github/workflows/release.yml +53 -27
  6. package/.releaserc.json +113 -0
  7. package/AGENTS.md +34 -23
  8. package/BENCHMARK_SOP.md +38 -18
  9. package/CHANGELOG.md +194 -3
  10. package/CONTRIBUTING.md +17 -4
  11. package/PRIVACY.md +25 -0
  12. package/README.md +118 -87
  13. package/RELEASING.md +82 -0
  14. package/SUPPORT.md +27 -0
  15. package/TERMS.md +21 -0
  16. package/VERIFICATION.md +175 -286
  17. package/behavioral-evals/README.md +21 -0
  18. package/behavioral-evals/case-validator.js +194 -0
  19. package/behavioral-evals/cases/baseline-environment-detection.yaml +21 -0
  20. package/behavioral-evals/cases/baseline-eval-harness.yaml +27 -0
  21. package/behavioral-evals/cases/baseline-fable-discipline.yaml +31 -0
  22. package/behavioral-evals/cases/baseline-find-skills-exposure.yaml +49 -0
  23. package/behavioral-evals/cases/baseline-find-skills.yaml +21 -0
  24. package/behavioral-evals/cases/baseline-git-commit.yaml +2 -2
  25. package/behavioral-evals/cases/baseline-install-cognitive-os.yaml +24 -0
  26. package/behavioral-evals/cases/baseline-multi-agent-workspace.yaml +28 -0
  27. package/behavioral-evals/cases/baseline-rewrite-commits.yaml +34 -0
  28. package/behavioral-evals/cases/baseline-self-evolve.yaml +31 -0
  29. package/behavioral-evals/cases/baseline-skill-creator.yaml +20 -0
  30. package/behavioral-evals/cases/baseline-skill-style.yaml +21 -0
  31. package/behavioral-evals/cases/baseline-to-spec.yaml +34 -0
  32. package/behavioral-evals/cases/baseline-to-tickets.yaml +24 -0
  33. package/behavioral-evals/cases/baseline-todo-driven-workflow.yaml +32 -0
  34. package/behavioral-evals/cases/baseline-using-git-worktrees.yaml +15 -0
  35. package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +2 -2
  36. package/behavioral-evals/cases/pressure-complexity-skip.yaml +2 -2
  37. package/behavioral-evals/cases/pressure-scope-bypass.yaml +7 -2
  38. package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +7 -4
  39. package/behavioral-evals/cases/scope-discipline.yaml +2 -1
  40. package/behavioral-evals/cases/verify-before-done.yaml +7 -5
  41. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/case.yaml +31 -0
  42. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/historical-case.yaml +31 -0
  43. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/provenance.json +42 -0
  44. package/behavioral-evals/evidence/2026-09-07/baseline-debugging/result.sanitized.json +20 -0
  45. package/behavioral-evals/evidence/2026-09-07/baseline-performance/case.yaml +34 -0
  46. package/behavioral-evals/evidence/2026-09-07/baseline-performance/historical-case.yaml +34 -0
  47. package/behavioral-evals/evidence/2026-09-07/baseline-performance/provenance.json +42 -0
  48. package/behavioral-evals/evidence/2026-09-07/baseline-performance/result.sanitized.json +20 -0
  49. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/case.yaml +28 -0
  50. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/historical-case.yaml +28 -0
  51. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/provenance.json +42 -0
  52. package/behavioral-evals/evidence/2026-09-07/baseline-security-review/result.sanitized.json +20 -0
  53. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/case.yaml +33 -0
  54. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/historical-case.yaml +33 -0
  55. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/provenance.json +42 -0
  56. package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/result.sanitized.json +20 -0
  57. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/case.yaml +28 -0
  58. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/historical-case.yaml +18 -0
  59. package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/provenance.json +30 -0
  60. package/behavioral-evals/evidence/2026-09-07/live-run-blocker.md +16 -0
  61. package/behavioral-evals/evidence/2026-09-07/manifest.json +88 -0
  62. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/case.yaml +33 -0
  63. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/historical-case.yaml +28 -0
  64. package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/provenance.json +30 -0
  65. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/case.yaml +50 -0
  66. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/historical-case.yaml +50 -0
  67. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/provenance.json +42 -0
  68. package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/result.sanitized.json +20 -0
  69. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/case.yaml +35 -0
  70. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/historical-case.yaml +35 -0
  71. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/provenance.json +42 -0
  72. package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/result.sanitized.json +20 -0
  73. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/case.yaml +43 -0
  74. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/historical-case.yaml +43 -0
  75. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/provenance.json +42 -0
  76. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/result.sanitized.json +24 -0
  77. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/case.yaml +43 -0
  78. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/historical-case.yaml +43 -0
  79. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/provenance.json +42 -0
  80. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/result.sanitized.json +20 -0
  81. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/case.yaml +43 -0
  82. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/historical-case.yaml +43 -0
  83. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/provenance.json +42 -0
  84. package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/result.sanitized.json +24 -0
  85. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/case.yaml +50 -0
  86. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/historical-case.yaml +47 -0
  87. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/provenance.json +42 -0
  88. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/result.sanitized.json +24 -0
  89. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/case.yaml +50 -0
  90. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/historical-case.yaml +47 -0
  91. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/provenance.json +42 -0
  92. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/result.sanitized.json +20 -0
  93. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/case.yaml +50 -0
  94. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/historical-case.yaml +47 -0
  95. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/provenance.json +42 -0
  96. package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/result.sanitized.json +24 -0
  97. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/case.yaml +30 -0
  98. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/historical-case.yaml +23 -0
  99. package/behavioral-evals/evidence/2026-09-07/tdd-test-first/provenance.json +30 -0
  100. package/behavioral-evals/evidence/2026-09-07/triage-matrix.json +426 -0
  101. package/behavioral-evals/evidence/2026-09-07/triage-matrix.md +24 -0
  102. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/case.yaml +19 -0
  103. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/historical-case.yaml +18 -0
  104. package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/provenance.json +30 -0
  105. package/behavioral-evals/evidence/2026-09-07/verify-before-done/case.yaml +33 -0
  106. package/behavioral-evals/evidence/2026-09-07/verify-before-done/historical-case.yaml +31 -0
  107. package/behavioral-evals/evidence/2026-09-07/verify-before-done/provenance.json +42 -0
  108. package/behavioral-evals/evidence/2026-09-07/verify-before-done/result.sanitized.json +20 -0
  109. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/case.yaml +33 -0
  110. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/historical-case.yaml +31 -0
  111. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/provenance.json +42 -0
  112. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/result.sanitized.json +20 -0
  113. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/case.yaml +33 -0
  114. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/historical-case.yaml +31 -0
  115. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/provenance.json +42 -0
  116. package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/result.sanitized.json +20 -0
  117. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/case.yaml +33 -0
  118. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/historical-case.yaml +33 -0
  119. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/provenance.json +42 -0
  120. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/result.sanitized.json +480 -0
  121. package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/transcript.sanitized.jsonl +95 -0
  122. package/behavioral-evals/evidence-tool.js +443 -0
  123. package/behavioral-evals/fixtures/claude-denied.jsonl +3 -0
  124. package/behavioral-evals/fixtures/claude-legacy.json +1 -0
  125. package/behavioral-evals/fixtures/claude-oral-only.jsonl +2 -0
  126. package/behavioral-evals/fixtures/claude-success.jsonl +5 -0
  127. package/behavioral-evals/fixtures/claude-truncated.jsonl +3 -0
  128. package/behavioral-evals/fixtures/opencode-success.jsonl +6 -0
  129. package/behavioral-evals/results/2026-08-31-baseline-add-feature.json +20 -0
  130. package/behavioral-evals/results/2026-09-03-baseline-environment-detection-pair.json +133 -0
  131. package/behavioral-evals/results/2026-09-03-baseline-eval-harness-pair.json +141 -0
  132. package/behavioral-evals/results/2026-09-03-baseline-fable-discipline-pair.json +149 -0
  133. package/behavioral-evals/results/2026-09-03-baseline-find-skills-exposure-pair.json +141 -0
  134. package/behavioral-evals/results/2026-09-03-baseline-find-skills-pair.json +141 -0
  135. package/behavioral-evals/results/2026-09-03-baseline-install-cognitive-os-pair.json +133 -0
  136. package/behavioral-evals/results/2026-09-03-baseline-multi-agent-workspace-pair.json +141 -0
  137. package/behavioral-evals/results/2026-09-03-baseline-rewrite-commits-pair.json +95 -0
  138. package/behavioral-evals/results/2026-09-03-baseline-self-evolve-pair.json +141 -0
  139. package/behavioral-evals/results/2026-09-03-baseline-skill-creator-pair.json +141 -0
  140. package/behavioral-evals/results/2026-09-03-baseline-skill-style-pair.json +133 -0
  141. package/behavioral-evals/results/2026-09-03-baseline-to-spec-pair.json +133 -0
  142. package/behavioral-evals/results/2026-09-03-baseline-to-tickets-pair.json +141 -0
  143. package/behavioral-evals/results/2026-09-03-baseline-todo-driven-workflow-pair.json +141 -0
  144. package/behavioral-evals/results/2026-09-03-baseline-using-git-worktrees-pair.json +133 -0
  145. package/behavioral-evals/results/2026-09-03-summary-both.json +41 -0
  146. package/behavioral-evals/results/2026-09-04-baseline-find-skills-pair.json +141 -0
  147. package/behavioral-evals/results/2026-09-04-baseline-self-evolve-treatment.json +80 -0
  148. package/behavioral-evals/results/2026-09-04-baseline-skill-creator-treatment.json +80 -0
  149. package/behavioral-evals/results/2026-09-04-summary-both.json +41 -0
  150. package/behavioral-evals/run-with-plugin.js +15 -31
  151. package/behavioral-evals/run.js +446 -105
  152. package/behavioral-evals/transcript-parser.js +591 -0
  153. package/bin/cli.js +24 -12
  154. package/ci/ab-test-cases/grill-me-adversarial.yaml +14 -4
  155. package/ci/ab-test-cases/tdd-test-first.yaml +13 -6
  156. package/ci/ab-test-cases/verification-loop-gates.yaml +20 -7
  157. package/ci/ab-test-cases/verify-before-claim-cites.yaml +7 -6
  158. package/ci/ab-test-harness.js +113 -38
  159. package/ci/action-gate-platform-doc.test.js +31 -0
  160. package/ci/action-gate.test.js +260 -0
  161. package/ci/cases/case2.json +2 -2
  162. package/ci/consistency-check.js +51 -5
  163. package/ci/description-collision.js +15 -6
  164. package/ci/disclosure-check.js +225 -0
  165. package/ci/doc-capability-consistency.test.js +180 -0
  166. package/ci/fixtures/phase4/parallel-disjoint.stages.json +32 -0
  167. package/ci/fixtures/phase4/parallel-read-only.stages.json +32 -0
  168. package/ci/installer-roundtrip.test.js +159 -0
  169. package/ci/invariant-routing.test.js +74 -0
  170. package/ci/issue99-runtime.test.js +218 -0
  171. package/ci/mechanism-2aa-codex-action-gate-policy.test.js +117 -0
  172. package/ci/mechanism-2ab-openai-plugin-sync-integrity.test.js +80 -0
  173. package/ci/mechanism-2j-behavioral-runner.test.js +43 -1
  174. package/ci/mechanism-2k-reference-check.test.js +57 -2
  175. package/ci/mechanism-2l-multi-agent-workspace.test.js +202 -6
  176. package/ci/mechanism-2m-self-repo-guard.test.js +65 -0
  177. package/ci/mechanism-2n-opencode-plugin.test.js +461 -0
  178. package/ci/mechanism-2o-state-home-isolation.test.js +183 -0
  179. package/ci/mechanism-2p-installer-links.test.js +183 -0
  180. package/ci/mechanism-2q-installer-global-shared-store.test.js +132 -0
  181. package/ci/mechanism-2r-uninstall-flags.test.js +148 -0
  182. package/ci/mechanism-2s-tdd-quality.test.js +203 -0
  183. package/ci/mechanism-2t-disclosure.test.js +90 -0
  184. package/ci/mechanism-2t-evidence-triage.test.js +200 -0
  185. package/ci/mechanism-2t-issue-20.test.js +75 -0
  186. package/ci/mechanism-2t-platform-skill-targets.test.js +164 -0
  187. package/ci/mechanism-2t-transcript-parser.test.js +179 -0
  188. package/ci/mechanism-2t-workspace-identity.test.js +200 -0
  189. package/ci/mechanism-2u-issue-42-regressions.test.js +239 -0
  190. package/ci/mechanism-2u-issue-44.test.js +35 -0
  191. package/ci/mechanism-2v-verifier-boundaries.test.js +138 -0
  192. package/ci/mechanism-2w-platform-compatibility.test.js +148 -0
  193. package/ci/mechanism-2x-release-automation.test.js +157 -0
  194. package/ci/mechanism-2x-release-commit-message.test.js +66 -0
  195. package/ci/mechanism-2y-release-tooling-compat.test.js +47 -0
  196. package/ci/mechanism-2z-claude-plugin-manifest.test.js +29 -0
  197. package/ci/mutation-check.js +53 -0
  198. package/ci/mutation-loader.js +57 -0
  199. package/ci/negative-controls.js +76 -46
  200. package/ci/openai-plugin-package.test.js +182 -0
  201. package/ci/openai-plugin-runtime.test.js +175 -0
  202. package/ci/openai-plugin-submission.test.js +101 -0
  203. package/ci/phase4-ensemble-memory.test.js +307 -0
  204. package/ci/reference-check.js +73 -25
  205. package/ci/release-consistency-check.js +24 -1
  206. package/ci/router-phase2-safety.test.js +81 -0
  207. package/ci/router-workflow-plan.test.js +264 -0
  208. package/ci/routing-check.js +6 -5
  209. package/ci/runner.js +6 -5
  210. package/ci/skill-routing-check.js +48 -11
  211. package/ci/test-behavioral-case-validation.js +62 -0
  212. package/ci/test-helper.js +10 -0
  213. package/ci/workflow-plan-consumers-edge.test.js +72 -0
  214. package/ci/workflow-plan-consumers.test.js +276 -0
  215. package/docs/architecture.md +137 -57
  216. package/docs/audit.md +22 -20
  217. package/docs/codex-action-gate.md +90 -0
  218. package/docs/issue-20-rollup.md +47 -0
  219. package/docs/mechanism-first-skill-mesh.md +22 -20
  220. package/docs/openai-plugin.md +118 -0
  221. package/docs/philosophy.md +28 -17
  222. package/docs/platform-capabilities.md +73 -0
  223. package/docs/platform-compatibility.json +270 -0
  224. package/docs/reflection.md +37 -22
  225. package/docs/release-evidence.md +25 -0
  226. package/docs/routing.md +157 -44
  227. package/docs/security-audit.md +38 -0
  228. package/docs/troubleshooting.md +82 -46
  229. package/docs/verifier-boundaries.md +45 -0
  230. package/docs/workflows/fable-mode.md +77 -21
  231. package/docs/workflows/find-skills.md +40 -0
  232. package/docs/workflows/grill-with-docs.md +2 -2
  233. package/docs/workflows/multi-agent-workspace.md +22 -10
  234. package/docs/workflows/self-evolve.md +33 -0
  235. package/docs/workflows/tdd.md +36 -0
  236. package/docs/workflows/todo-driven-workflow.md +33 -0
  237. package/environment-detection/SKILL.md +3 -3
  238. package/eval-harness/SKILL.md +7 -7
  239. package/eval-harness/scripts/evaluate.js +12 -10
  240. package/evals/evaluation-reports.md +14 -0
  241. package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
  242. package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
  243. package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
  244. package/evals/install-cognitive-os/tasks/positive-trigger.yaml +1 -1
  245. package/fable-discipline/SKILL.md +2 -2
  246. package/fable-mode/CONTRACT-FORMAT.md +147 -33
  247. package/fable-mode/SKILL.md +3 -3
  248. package/fable-mode/agents/fable-orchestrator.md +95 -35
  249. package/fable-mode/execution-guardrails/SKILL.md +2 -2
  250. package/fable-mode/fable-haiku/SKILL.md +3 -3
  251. package/fable-mode/fable-opus/SKILL.md +3 -3
  252. package/fable-mode/fable-sonnet/SKILL.md +3 -3
  253. package/fable-mode/scripts/ensemble-review.js +371 -0
  254. package/fable-mode/scripts/model-selector.js +134 -3
  255. package/fable-mode/scripts/workflow-plan-consumer.js +337 -0
  256. package/find-skills/SKILL.md +5 -5
  257. package/find-skills/references/discovery-flow.md +1 -1
  258. package/find-skills/scripts/use-skill.js +7 -1
  259. package/git-commit/SKILL.md +4 -11
  260. package/grill-me/SKILL.md +5 -5
  261. package/grill-with-docs/SKILL.md +7 -7
  262. package/grill-with-docs/references/session-playbook.md +6 -4
  263. package/harness-everything/SKILL.md +23 -22
  264. package/harness-everything/references/router-workflow-plan.md +128 -0
  265. package/harness-everything/references/triage-and-tiers.md +173 -96
  266. package/harness-everything/schemas/router-task-shape.schema.json +126 -0
  267. package/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
  268. package/harness-everything/scripts/bootstrap.js +13 -4
  269. package/harness-everything/scripts/ensemble-policy.js +102 -0
  270. package/harness-everything/scripts/kernel-router.js +212 -0
  271. package/harness-everything/scripts/router-contract.js +612 -0
  272. package/harness-everything/scripts/self-heal.js +29 -20
  273. package/harness-everything/scripts/tier-router.js +346 -221
  274. package/hooks/hooks.json +60 -15
  275. package/hooks/scripts/action-gate-rules.json +77 -0
  276. package/hooks/scripts/action-gate.js +528 -0
  277. package/hooks/scripts/atomic-commit-check.js +5 -5
  278. package/hooks/scripts/codex-action-gate-post.js +46 -0
  279. package/hooks/scripts/codex-permission-request.js +140 -0
  280. package/hooks/scripts/context-compact.js +4 -3
  281. package/hooks/scripts/contract-test.js +118 -51
  282. package/hooks/scripts/lib/fable-contracts.js +121 -0
  283. package/hooks/scripts/lib/harness-state.js +164 -28
  284. package/hooks/scripts/lib/platforms/claude.js +45 -62
  285. package/hooks/scripts/lib/platforms/codex.js +35 -61
  286. package/hooks/scripts/lib/platforms/continue.js +7 -7
  287. package/hooks/scripts/lib/platforms/copilot.js +29 -53
  288. package/hooks/scripts/lib/platforms/hermes.js +71 -20
  289. package/hooks/scripts/openai-session-start.js +81 -0
  290. package/hooks/scripts/rule-of-3-tracker.js +9 -8
  291. package/hooks/scripts/rule-of-3.js +29 -21
  292. package/hooks/scripts/state-persist.js +5 -4
  293. package/hooks/scripts/stop-gate.js +2 -2
  294. package/hooks/scripts/subagent-scope-guard.js +144 -38
  295. package/hooks/scripts/subagent-start.js +19 -0
  296. package/hooks/scripts/subagent-stop.js +6 -0
  297. package/improve-codebase-architecture/SKILL.md +2 -4
  298. package/improve-codebase-architecture/references/README.md +16 -0
  299. package/install-cognitive-os/SKILL.md +28 -22
  300. package/multi-agent-workspace/SKILL.md +16 -15
  301. package/multi-agent-workspace/references/architecture-guide.md +13 -9
  302. package/multi-agent-workspace/references/orchestration.md +39 -22
  303. package/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
  304. package/multi-agent-workspace/scripts/index_memory.js +79 -26
  305. package/multi-agent-workspace/scripts/path-boundary.js +59 -0
  306. package/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
  307. package/multi-agent-workspace/scripts/scaffold.js +258 -103
  308. package/multi-agent-workspace/templates/AGENTS.md +20 -0
  309. package/multi-agent-workspace/workflows/03-generation.md +8 -10
  310. package/multi-agent-workspace/workflows/04-launcher.md +5 -5
  311. package/opencode-plugin/README.md +114 -71
  312. package/opencode-plugin/hooks/verify.js +62 -12
  313. package/opencode-plugin/index.mjs +547 -0
  314. package/opencode-plugin/plugin.json +4 -12
  315. package/package.json +16 -3
  316. package/plugins/harness-everything/.codex-plugin/plugin.json +39 -0
  317. package/plugins/harness-everything/hooks/hooks.json +197 -0
  318. package/plugins/harness-everything/hooks/scripts/action-gate-rules.json +77 -0
  319. package/plugins/harness-everything/hooks/scripts/action-gate.js +528 -0
  320. package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +68 -0
  321. package/plugins/harness-everything/hooks/scripts/boundary-guard.js +55 -0
  322. package/plugins/harness-everything/hooks/scripts/codex-action-gate-post.js +46 -0
  323. package/plugins/harness-everything/hooks/scripts/codex-permission-request.js +140 -0
  324. package/plugins/harness-everything/hooks/scripts/context-compact.js +115 -0
  325. package/plugins/harness-everything/hooks/scripts/contract-test.js +146 -0
  326. package/plugins/harness-everything/hooks/scripts/depth-guard.js +65 -0
  327. package/plugins/harness-everything/hooks/scripts/lib/fable-contracts.js +121 -0
  328. package/plugins/harness-everything/hooks/scripts/lib/harness-state.js +275 -0
  329. package/plugins/harness-everything/hooks/scripts/lib/platforms/claude.js +168 -0
  330. package/plugins/harness-everything/hooks/scripts/lib/platforms/codex.js +88 -0
  331. package/plugins/harness-everything/hooks/scripts/lib/platforms/continue.js +91 -0
  332. package/plugins/harness-everything/hooks/scripts/lib/platforms/copilot.js +85 -0
  333. package/plugins/harness-everything/hooks/scripts/lib/platforms/cursor.js +89 -0
  334. package/plugins/harness-everything/hooks/scripts/lib/platforms/hermes.js +108 -0
  335. package/plugins/harness-everything/hooks/scripts/lib/platforms/index.js +17 -0
  336. package/plugins/harness-everything/hooks/scripts/lib/platforms/worktrees.js +20 -0
  337. package/plugins/harness-everything/hooks/scripts/lib/workspace.js +290 -0
  338. package/plugins/harness-everything/hooks/scripts/openai-session-start.js +81 -0
  339. package/plugins/harness-everything/hooks/scripts/reset-circuit-breaker.js +30 -0
  340. package/plugins/harness-everything/hooks/scripts/rule-of-3-tracker.js +125 -0
  341. package/plugins/harness-everything/hooks/scripts/rule-of-3.js +195 -0
  342. package/plugins/harness-everything/hooks/scripts/state-persist.js +100 -0
  343. package/plugins/harness-everything/hooks/scripts/stop-gate.js +80 -0
  344. package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +194 -0
  345. package/plugins/harness-everything/hooks/scripts/subagent-start.js +19 -0
  346. package/plugins/harness-everything/hooks/scripts/subagent-stop.js +6 -0
  347. package/plugins/harness-everything/hooks/session-start.js +81 -0
  348. package/plugins/harness-everything/plugin.json +43 -0
  349. package/plugins/harness-everything/scripts/lib/advisory-text.js +174 -0
  350. package/plugins/harness-everything/skills/environment-detection/SKILL.md +41 -0
  351. package/plugins/harness-everything/skills/environment-detection/references/shell-syntax-rules.md +76 -0
  352. package/plugins/harness-everything/skills/environment-detection/scripts/preflight.js +103 -0
  353. package/plugins/harness-everything/skills/eval-harness/SKILL.md +42 -0
  354. package/plugins/harness-everything/skills/eval-harness/references/scoring-rubric.md +68 -0
  355. package/plugins/harness-everything/skills/eval-harness/scripts/evaluate.js +79 -0
  356. package/plugins/harness-everything/skills/fable-discipline/SKILL.md +41 -0
  357. package/plugins/harness-everything/skills/fable-discipline/references/discipline-rules.md +22 -0
  358. package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +165 -0
  359. package/plugins/harness-everything/skills/fable-mode/SKILL.md +39 -0
  360. package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +159 -0
  361. package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +32 -0
  362. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +52 -0
  363. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +53 -0
  364. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +43 -0
  365. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
  366. package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +36 -0
  367. package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +26 -0
  368. package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +36 -0
  369. package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +36 -0
  370. package/plugins/harness-everything/skills/fable-mode/model-matrix.json +32 -0
  371. package/plugins/harness-everything/skills/fable-mode/references/execution-phases.md +37 -0
  372. package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +39 -0
  373. package/plugins/harness-everything/skills/fable-mode/scripts/ensemble-review.js +371 -0
  374. package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +260 -0
  375. package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +337 -0
  376. package/plugins/harness-everything/skills/find-skills/SKILL.md +48 -0
  377. package/plugins/harness-everything/skills/find-skills/references/discovery-flow.md +102 -0
  378. package/plugins/harness-everything/skills/find-skills/scripts/use-skill.js +112 -0
  379. package/plugins/harness-everything/skills/git-commit/SKILL.md +38 -0
  380. package/plugins/harness-everything/skills/git-commit/guides/ANGULAR_STYLE.md +61 -0
  381. package/plugins/harness-everything/skills/git-commit/guides/COMMIT_GENERATION.md +24 -0
  382. package/plugins/harness-everything/skills/git-commit/guides/LANGUAGE_DETECTION.md +16 -0
  383. package/plugins/harness-everything/skills/git-commit/guides/MAIN_REPO.md +15 -0
  384. package/plugins/harness-everything/skills/git-commit/guides/SUBMODULES.md +27 -0
  385. package/plugins/harness-everything/skills/git-commit/references/commit-flow.md +30 -0
  386. package/plugins/harness-everything/skills/grill-me/SKILL.md +43 -0
  387. package/plugins/harness-everything/skills/grill-me/references/grilling-playbook.md +38 -0
  388. package/plugins/harness-everything/skills/grill-with-docs/ADR-FORMAT.md +47 -0
  389. package/plugins/harness-everything/skills/grill-with-docs/CONTEXT-FORMAT.md +63 -0
  390. package/plugins/harness-everything/skills/grill-with-docs/SKILL.md +41 -0
  391. package/plugins/harness-everything/skills/grill-with-docs/references/session-playbook.md +99 -0
  392. package/plugins/harness-everything/skills/harness-everything/SKILL.md +43 -0
  393. package/plugins/harness-everything/skills/harness-everything/examples/README.md +9 -0
  394. package/plugins/harness-everything/skills/harness-everything/examples/todo-workflow.md +22 -0
  395. package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +128 -0
  396. package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +39 -0
  397. package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +193 -0
  398. package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +126 -0
  399. package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
  400. package/plugins/harness-everything/skills/harness-everything/scripts/bootstrap.js +212 -0
  401. package/plugins/harness-everything/skills/harness-everything/scripts/ensemble-policy.js +102 -0
  402. package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +212 -0
  403. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +612 -0
  404. package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +320 -0
  405. package/plugins/harness-everything/skills/harness-everything/scripts/self-heal.js +250 -0
  406. package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +459 -0
  407. package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +100 -0
  408. package/plugins/harness-everything/skills/improve-codebase-architecture/SKILL.md +38 -0
  409. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/DEEPENING.md +37 -0
  410. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/HTML-REPORT.md +123 -0
  411. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -0
  412. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/LANGUAGE.md +53 -0
  413. package/plugins/harness-everything/skills/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
  414. package/plugins/harness-everything/skills/improve-codebase-architecture/references/README.md +16 -0
  415. package/plugins/harness-everything/skills/install-cognitive-os/SKILL.md +44 -0
  416. package/plugins/harness-everything/skills/install-cognitive-os/references/cognitive-loop.md +27 -0
  417. package/plugins/harness-everything/skills/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
  418. package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +38 -0
  419. package/plugins/harness-everything/skills/multi-agent-workspace/examples/csharp-monorepo.md +123 -0
  420. package/plugins/harness-everything/skills/multi-agent-workspace/examples/typescript-cli.md +126 -0
  421. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-claude.md +77 -0
  422. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-codex.md +146 -0
  423. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-continue.md +38 -0
  424. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-copilot.md +74 -0
  425. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
  426. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
  427. package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/role-capabilities.md +108 -0
  428. package/plugins/harness-everything/skills/multi-agent-workspace/references/agency-agents.md +31 -0
  429. package/plugins/harness-everything/skills/multi-agent-workspace/references/architecture-guide.md +36 -0
  430. package/plugins/harness-everything/skills/multi-agent-workspace/references/orchestration.md +65 -0
  431. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/agency-catalog.js +190 -0
  432. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
  433. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/index_memory.js +104 -0
  434. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/path-boundary.js +59 -0
  435. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
  436. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/scaffold.js +408 -0
  437. package/plugins/harness-everything/skills/multi-agent-workspace/templates/AGENTS.md +20 -0
  438. package/plugins/harness-everything/skills/multi-agent-workspace/templates/README.md +17 -0
  439. package/plugins/harness-everything/skills/multi-agent-workspace/templates/agent-launcher.md +68 -0
  440. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-csharp.md +56 -0
  441. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-polyglot.md +62 -0
  442. package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-ts.md +57 -0
  443. package/plugins/harness-everything/skills/multi-agent-workspace/templates/memory-keeper.md +37 -0
  444. package/plugins/harness-everything/skills/multi-agent-workspace/templates/requirement-analyzer.md +66 -0
  445. package/plugins/harness-everything/skills/multi-agent-workspace/templates/specialist-agent.md +34 -0
  446. package/plugins/harness-everything/skills/multi-agent-workspace/templates/task-verifier.md +56 -0
  447. package/plugins/harness-everything/skills/multi-agent-workspace/templates/tool-maker.md +33 -0
  448. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/01-init.md +22 -0
  449. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/02-analysis.md +23 -0
  450. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/03-generation.md +25 -0
  451. package/plugins/harness-everything/skills/multi-agent-workspace/workflows/04-launcher.md +16 -0
  452. package/plugins/harness-everything/skills/repo-docs/SKILL.md +42 -0
  453. package/plugins/harness-everything/skills/repo-docs/references/process-guide.md +60 -0
  454. package/plugins/harness-everything/skills/repo-docs/templates/agents-template.md +264 -0
  455. package/plugins/harness-everything/skills/repo-docs/templates/knowledge-agents-template.md +50 -0
  456. package/plugins/harness-everything/skills/repo-docs/templates/knowledge-readme-template.md +83 -0
  457. package/plugins/harness-everything/skills/repo-docs/templates/manual-recon.md +117 -0
  458. package/plugins/harness-everything/skills/repo-docs/templates/multi-skills-readme-template.md +72 -0
  459. package/plugins/harness-everything/skills/repo-docs/templates/product-agents-template.md +78 -0
  460. package/plugins/harness-everything/skills/repo-docs/templates/product-readme-template.md +95 -0
  461. package/plugins/harness-everything/skills/repo-docs/templates/readme-template.md +136 -0
  462. package/plugins/harness-everything/skills/repo-docs/templates/single-skill-readme-template.md +70 -0
  463. package/plugins/harness-everything/skills/repo-docs/templates/skills-agents-template.md +60 -0
  464. package/plugins/harness-everything/skills/rewrite-commits/SKILL.md +37 -0
  465. package/plugins/harness-everything/skills/rewrite-commits/references/workflow.md +18 -0
  466. package/plugins/harness-everything/skills/security-review/SKILL.md +47 -0
  467. package/plugins/harness-everything/skills/security-review/guides/OWASP-PATTERNS.md +31 -0
  468. package/plugins/harness-everything/skills/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
  469. package/plugins/harness-everything/skills/security-review/references/security-checklist.md +478 -0
  470. package/plugins/harness-everything/skills/security-review/scripts/audit-secrets.js +85 -0
  471. package/plugins/harness-everything/skills/self-evolve/SKILL.md +45 -0
  472. package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +61 -0
  473. package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +272 -0
  474. package/plugins/harness-everything/skills/self-evolve/scripts/register-dynamic-skill.js +253 -0
  475. package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +125 -0
  476. package/plugins/harness-everything/skills/skill-creator/SKILL.md +47 -0
  477. package/plugins/harness-everything/skills/skill-creator/references/authoring-workflow.md +14 -0
  478. package/plugins/harness-everything/skills/skill-creator/references/dynamic-generation-contract.md +21 -0
  479. package/plugins/harness-everything/skills/skill-creator/references/quality-checklist.md +16 -0
  480. package/plugins/harness-everything/skills/skill-creator/references/quality-principles.md +81 -0
  481. package/plugins/harness-everything/skills/skill-creator/references/testing-workflow.md +27 -0
  482. package/plugins/harness-everything/skills/skill-style/SKILL.md +42 -0
  483. package/plugins/harness-everything/skills/skill-style/references/style-guide.md +62 -0
  484. package/plugins/harness-everything/skills/tdd/SKILL.md +38 -0
  485. package/plugins/harness-everything/skills/tdd/fixtures/pass-evidence.json +100 -0
  486. package/plugins/harness-everything/skills/tdd/fixtures/quality-scenarios.json +20 -0
  487. package/plugins/harness-everything/skills/tdd/guides/deep-modules.md +33 -0
  488. package/plugins/harness-everything/skills/tdd/guides/interface-design.md +31 -0
  489. package/plugins/harness-everything/skills/tdd/guides/mocking.md +59 -0
  490. package/plugins/harness-everything/skills/tdd/guides/refactoring.md +10 -0
  491. package/plugins/harness-everything/skills/tdd/guides/tests.md +61 -0
  492. package/plugins/harness-everything/skills/tdd/references/core-discipline.md +51 -0
  493. package/plugins/harness-everything/skills/tdd/references/integration-testing.md +13 -0
  494. package/plugins/harness-everything/skills/tdd/references/quality-model.md +68 -0
  495. package/plugins/harness-everything/skills/tdd/references/unit-testing.md +15 -0
  496. package/plugins/harness-everything/skills/tdd/schemas/quality-evidence.schema.json +211 -0
  497. package/plugins/harness-everything/skills/tdd/schemas/quality-report.schema.json +110 -0
  498. package/plugins/harness-everything/skills/tdd/scripts/quality-gate.js +475 -0
  499. package/plugins/harness-everything/skills/to-spec/SKILL.md +40 -0
  500. package/plugins/harness-everything/skills/to-spec/references/process.md +77 -0
  501. package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +239 -0
  502. package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +111 -0
  503. package/plugins/harness-everything/skills/to-spec/templates/cli-reference.md +31 -0
  504. package/plugins/harness-everything/skills/to-spec/templates/dev-doc.md +27 -0
  505. package/plugins/harness-everything/skills/to-spec/templates/feature-spec.md +59 -0
  506. package/plugins/harness-everything/skills/to-spec/templates/schema-doc.md +31 -0
  507. package/plugins/harness-everything/skills/to-tickets/SKILL.md +42 -0
  508. package/plugins/harness-everything/skills/to-tickets/references/publishing-and-templates.md +97 -0
  509. package/plugins/harness-everything/skills/todo-driven-workflow/SKILL.md +42 -0
  510. package/plugins/harness-everything/skills/todo-driven-workflow/references/execution-guide.md +27 -0
  511. package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +49 -0
  512. package/plugins/harness-everything/skills/using-git-worktrees/references/workflow-details.md +108 -0
  513. package/plugins/harness-everything/skills/verification-loop/SKILL.md +46 -0
  514. package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +116 -0
  515. package/plugins/harness-everything/skills/verification-loop/templates/verification-report.template.md +15 -0
  516. package/plugins/harness-everything/skills/verify-before-claim/SKILL.md +43 -0
  517. package/plugins/harness-everything/skills/verify-before-claim/references/verification-guide.md +73 -0
  518. package/plugins/harness-everything/skills/zoom-out/SKILL.md +42 -0
  519. package/plugins/harness-everything/skills/zoom-out/references/circuit-breaker.md +66 -0
  520. package/plugins/harness-everything/skills/zoom-out/templates/zoom-out-report.template.md +20 -0
  521. package/repo-docs/SKILL.md +3 -7
  522. package/rewrite-commits/SKILL.md +2 -2
  523. package/scripts/build-openai-submission.js +158 -0
  524. package/scripts/installer.js +86 -11
  525. package/scripts/lib/advisory-text.js +25 -18
  526. package/scripts/lib/execution-contract.js +70 -0
  527. package/scripts/lib/manifest.js +34 -38
  528. package/scripts/lib/path-boundary.js +90 -0
  529. package/scripts/lib/skills.js +124 -70
  530. package/scripts/lib/workspace.js +269 -7
  531. package/scripts/sync-openai-plugin.js +91 -0
  532. package/scripts/sync-release-version.js +212 -0
  533. package/security-review/SKILL.md +4 -7
  534. package/security-review/scripts/audit-secrets.js +9 -8
  535. package/self-evolve/SKILL.md +9 -11
  536. package/self-evolve/references/memory-resolution.md +4 -1
  537. package/self-evolve/scripts/persist-memory.js +221 -88
  538. package/self-evolve/scripts/register-dynamic-skill.js +12 -10
  539. package/self-evolve/scripts/self-regression.js +23 -8
  540. package/skill-creator/SKILL.md +12 -14
  541. package/skill-creator/references/quality-checklist.md +1 -0
  542. package/skill-style/SKILL.md +4 -4
  543. package/skill-style/references/style-guide.md +29 -3
  544. package/submission/openai/README.md +67 -0
  545. package/submission/openai/listing.json +31 -0
  546. package/submission/openai/test-cases.json +61 -0
  547. package/tdd/SKILL.md +20 -26
  548. package/tdd/fixtures/pass-evidence.json +100 -0
  549. package/tdd/fixtures/quality-scenarios.json +20 -0
  550. package/tdd/references/core-discipline.md +2 -0
  551. package/tdd/references/integration-testing.md +13 -0
  552. package/tdd/references/quality-model.md +68 -0
  553. package/tdd/references/unit-testing.md +15 -0
  554. package/tdd/schemas/quality-evidence.schema.json +211 -0
  555. package/tdd/schemas/quality-report.schema.json +110 -0
  556. package/tdd/scripts/quality-gate.js +475 -0
  557. package/to-spec/SKILL.md +3 -5
  558. package/to-spec/scripts/check-project-docs.js +42 -62
  559. package/to-spec/scripts/project-docs-resolver.js +111 -0
  560. package/to-tickets/SKILL.md +4 -4
  561. package/todo-driven-workflow/SKILL.md +6 -4
  562. package/using-git-worktrees/SKILL.md +3 -3
  563. package/verification-loop/SKILL.md +4 -4
  564. package/verify-before-claim/SKILL.md +2 -2
  565. package/zoom-out/SKILL.md +4 -4
  566. package/ab-test-deep-analysis-2026-08-27.md +0 -383
  567. package/ab-test-results-2026-08-27.md +0 -152
  568. package/ab-test-statistical-analysis-31cases.md +0 -193
  569. package/opencode-plugin/hooks/circuit-breaker.js +0 -121
  570. package/opencode-plugin/hooks/compliance.js +0 -128
  571. package/opencode-plugin/hooks/post-edit.js +0 -62
  572. package/opencode-plugin/hooks/pre-complete.js +0 -65
  573. package/opencode-plugin/test-hook-firing.js +0 -198
@@ -0,0 +1,183 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const helper = require('./test-helper');
5
+
6
+ console.log('\n[2p] Canonical-store link/copy install + link-aware uninstall (issue #49)...');
7
+
8
+ const skills = require('../scripts/lib/skills');
9
+ const manifest = require('../scripts/lib/manifest');
10
+
11
+ const harnessSourceDir = path.resolve(__dirname, '..');
12
+ const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2p-ws-'));
13
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2p-home-'));
14
+
15
+ function target(name, dirName) {
16
+ const home_ = path.join(ws, dirName);
17
+ return { path: path.join(home_, 'skills'), label: `${dirName}/skills/`, manifestPath: manifest.getManifestPath(home_) };
18
+ }
19
+
20
+ // --- 1. Link mode: two targets sharing one canonical copy ---
21
+ const claudeTarget = target('claude', '.claude');
22
+ const cursorTarget = target('cursor', '.cursor');
23
+ const canonicalDir = skills.getCanonicalSkillsDir({ isGlobal: false, workspaceRoot: ws, userHome: home });
24
+
25
+ skills.installSkillsToTargets({
26
+ chosenSkills: ['tdd'],
27
+ targetDirs: [claudeTarget, cursorTarget],
28
+ harnessSourceDir,
29
+ packageVersion: '9.9.9',
30
+ canonicalDir,
31
+ linkMode: 'auto',
32
+ });
33
+
34
+ const claudeSkillDir = path.join(claudeTarget.path, 'tdd');
35
+ const cursorSkillDir = path.join(cursorTarget.path, 'tdd');
36
+ const canonicalSkillDir = path.join(canonicalDir, 'tdd');
37
+
38
+ helper.check(
39
+ '2p. both platform targets are real links (symlink/junction), not physical copies',
40
+ skills.isLinkPath(claudeSkillDir) && skills.isLinkPath(cursorSkillDir),
41
+ `claude isLink=${skills.isLinkPath(claudeSkillDir)} cursor isLink=${skills.isLinkPath(cursorSkillDir)}`
42
+ );
43
+ helper.check(
44
+ '2p. the canonical copy itself is a physical directory, not a link',
45
+ fs.existsSync(canonicalSkillDir) && !skills.isLinkPath(canonicalSkillDir),
46
+ `canonical exists=${fs.existsSync(canonicalSkillDir)} isLink=${skills.isLinkPath(canonicalSkillDir)}`
47
+ );
48
+ helper.check(
49
+ '2p. content is readable through the link and matches source',
50
+ fs.existsSync(path.join(claudeSkillDir, 'SKILL.md')) &&
51
+ fs.readFileSync(path.join(claudeSkillDir, 'SKILL.md'), 'utf8') === fs.readFileSync(path.join(harnessSourceDir, 'tdd', 'SKILL.md'), 'utf8'),
52
+ 'linked SKILL.md content did not match source'
53
+ );
54
+
55
+ const claudeManifestEntry = manifest.readManifest(claudeTarget.manifestPath).skills[0];
56
+ helper.check(
57
+ '2p. manifest records kind + canonicalPath for a linked entry',
58
+ claudeManifestEntry && ['symlink', 'junction'].includes(claudeManifestEntry.kind) && claudeManifestEntry.canonicalPath === canonicalSkillDir,
59
+ `Got: ${JSON.stringify(claudeManifestEntry)}`
60
+ );
61
+
62
+ // --- 2. getInstalledSkills surfaces both links, and the canonical dir is
63
+ // NOT double-listed as its own separate skill (advisor-flagged legacy-scan
64
+ // collision fix). ---
65
+ {
66
+ const installed = skills.getInstalledSkills(ws, home);
67
+ const tddEntries = installed.filter(s => s.id === 'tdd');
68
+ helper.check(
69
+ '2p. getInstalledSkills lists exactly the 2 linked entries, not the canonical dir as a 3rd',
70
+ tddEntries.length === 2,
71
+ `Got ${tddEntries.length}: ${JSON.stringify(tddEntries.map(e => e.dirPath))}`
72
+ );
73
+ }
74
+
75
+ // --- 3. Removing one link leaves the canonical copy (still referenced by
76
+ // the other link) intact; removing the last link removes it too. ---
77
+ {
78
+ const installedBefore = skills.getInstalledSkills(ws, home);
79
+ const claudeEntry = installedBefore.find(s => s.dirPath === claudeSkillDir);
80
+ skills.removeSkill(claudeEntry, { workspaceRoot: ws, userHome: home });
81
+
82
+ helper.check(
83
+ '2p. removing one link deletes only that link, canonical copy survives (still referenced)',
84
+ !fs.existsSync(claudeSkillDir) && fs.existsSync(canonicalSkillDir),
85
+ `claude exists=${fs.existsSync(claudeSkillDir)} canonical exists=${fs.existsSync(canonicalSkillDir)}`
86
+ );
87
+
88
+ const installedAfterFirst = skills.getInstalledSkills(ws, home);
89
+ const cursorEntry = installedAfterFirst.find(s => s.dirPath === cursorSkillDir);
90
+ skills.removeSkill(cursorEntry, { workspaceRoot: ws, userHome: home });
91
+
92
+ helper.check(
93
+ '2p. removing the LAST link also removes the now-orphaned canonical copy',
94
+ !fs.existsSync(cursorSkillDir) && !fs.existsSync(canonicalSkillDir),
95
+ `cursor exists=${fs.existsSync(cursorSkillDir)} canonical exists=${fs.existsSync(canonicalSkillDir)}`
96
+ );
97
+ }
98
+
99
+ // --- 4. --copy mode produces independent physical copies, no canonical
100
+ // store, no links at all. ---
101
+ {
102
+ const copyTarget = target('codex', '.codex');
103
+ skills.installSkillsToTargets({
104
+ chosenSkills: ['tdd'],
105
+ targetDirs: [copyTarget],
106
+ harnessSourceDir,
107
+ packageVersion: '9.9.9',
108
+ canonicalDir: skills.getCanonicalSkillsDir({ isGlobal: false, workspaceRoot: ws, userHome: home }),
109
+ linkMode: 'copy',
110
+ });
111
+ const copySkillDir = path.join(copyTarget.path, 'tdd');
112
+ helper.check(
113
+ '2p. --copy mode installs a physical directory, not a link',
114
+ fs.existsSync(copySkillDir) && !skills.isLinkPath(copySkillDir),
115
+ `exists=${fs.existsSync(copySkillDir)} isLink=${skills.isLinkPath(copySkillDir)}`
116
+ );
117
+ const entry = manifest.readManifest(copyTarget.manifestPath).skills[0];
118
+ helper.check(
119
+ '2p. --copy mode manifest entry has no kind/canonicalPath',
120
+ entry && !entry.kind && !entry.canonicalPath,
121
+ `Got: ${JSON.stringify(entry)}`
122
+ );
123
+ const installedEntry = skills.getInstalledSkills(ws, home).find(s => s.dirPath === copySkillDir);
124
+ skills.removeSkill(installedEntry, { workspaceRoot: ws, userHome: home });
125
+ helper.check('2p. --copy mode entry uninstalls cleanly', !fs.existsSync(copySkillDir), 'copy-mode dir still exists after removeSkill');
126
+ }
127
+
128
+ // --- 5. Dangling link (canonical target manually deleted) is still
129
+ // detected and cleaned up rather than silently ignored. ---
130
+ {
131
+ const danglingTarget = target('continue', '.continue');
132
+ skills.installSkillsToTargets({
133
+ chosenSkills: ['tdd'],
134
+ targetDirs: [danglingTarget],
135
+ harnessSourceDir,
136
+ packageVersion: '9.9.9',
137
+ canonicalDir: skills.getCanonicalSkillsDir({ isGlobal: false, workspaceRoot: ws, userHome: home }),
138
+ linkMode: 'symlink',
139
+ });
140
+ const linkDir = path.join(danglingTarget.path, 'tdd');
141
+ const canonicalTddDir = path.join(skills.getCanonicalSkillsDir({ isGlobal: false, workspaceRoot: ws, userHome: home }), 'tdd');
142
+ fs.rmSync(canonicalTddDir, { recursive: true, force: true }); // simulate manual/out-of-band deletion
143
+
144
+ helper.check(
145
+ '2p. a dangling link is still detected as present (pathPresent) even though its target is gone',
146
+ skills.pathPresent(linkDir) && !fs.existsSync(linkDir),
147
+ `pathPresent=${skills.pathPresent(linkDir)} existsSync=${fs.existsSync(linkDir)}`
148
+ );
149
+
150
+ const installed = skills.getInstalledSkills(ws, home);
151
+ const danglingEntry = installed.find(s => s.dirPath === linkDir);
152
+ helper.check('2p. getInstalledSkills still surfaces the dangling link for cleanup', !!danglingEntry, 'dangling link entry missing from getInstalledSkills');
153
+ if (danglingEntry) {
154
+ skills.removeSkill(danglingEntry, { workspaceRoot: ws, userHome: home });
155
+ helper.check(
156
+ '2p. removeSkill cleans up the dangling link and its manifest row',
157
+ !skills.isLinkPath(linkDir) && manifest.readManifest(danglingTarget.manifestPath).skills.length === 0,
158
+ `isLink=${skills.isLinkPath(linkDir)} manifest=${JSON.stringify(manifest.readManifest(danglingTarget.manifestPath))}`
159
+ );
160
+ }
161
+ }
162
+
163
+ // --- 6. A legacy manifest entry with no canonicalPath (pre-#49 install)
164
+ // still uninstalls exactly as before - no forced migration required. ---
165
+ {
166
+ const legacyTarget = target('copilot', '.github');
167
+ fs.mkdirSync(legacyTarget.path, { recursive: true });
168
+ const legacyDir = path.join(legacyTarget.path, 'tdd');
169
+ skills.copyDir(path.join(harnessSourceDir, 'tdd'), legacyDir);
170
+ manifest.recordSkillInstall(legacyTarget.manifestPath, '0.3.0', 'tdd', legacyDir); // no linkInfo - pre-#49 shape
171
+
172
+ const entry = skills.getInstalledSkills(ws, home).find(s => s.dirPath === legacyDir);
173
+ helper.check('2p. a pre-#49 manifest entry (no kind/canonicalPath) is still discovered', !!entry, 'legacy entry not found');
174
+ if (entry) {
175
+ skills.removeSkill(entry, { workspaceRoot: ws, userHome: home });
176
+ helper.check('2p. a pre-#49 entry uninstalls cleanly with no canonical-store side effects', !fs.existsSync(legacyDir), 'legacy dir still exists');
177
+ }
178
+ }
179
+
180
+ fs.rmSync(ws, { recursive: true, force: true });
181
+ fs.rmSync(home, { recursive: true, force: true });
182
+
183
+ helper.finish();
@@ -0,0 +1,132 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { spawnSync } = require('child_process');
5
+ const helper = require('./test-helper');
6
+
7
+ console.log('\n[2q] Global scope: canonical store reuses the pre-existing shared ~/.agents/skills (issue #49)...');
8
+
9
+ const skills = require('../scripts/lib/skills');
10
+ const manifest = require('../scripts/lib/manifest');
11
+
12
+ const harnessSourceDir = path.resolve(__dirname, '..');
13
+
14
+ const installerHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2q-installer-home-'));
15
+ const installerCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2q-installer-cwd-'));
16
+ const installerResult = spawnSync(
17
+ process.execPath,
18
+ [path.join(harnessSourceDir, 'scripts', 'installer.js'), 'install', '--global', '--yes', '--no-skills', '--claude'],
19
+ {
20
+ cwd: installerCwd,
21
+ env: { ...process.env, HOME: installerHome, USERPROFILE: installerHome, APPDATA: path.join(installerHome, 'AppData', 'Roaming') },
22
+ encoding: 'utf8'
23
+ }
24
+ );
25
+ helper.check(
26
+ '2q. global installer works from a non-git directory',
27
+ installerResult.status === 0 && fs.existsSync(path.join(installerHome, '.claude', 'settings.json')),
28
+ `status=${installerResult.status} stdout=${installerResult.stdout} stderr=${installerResult.stderr}`
29
+ );
30
+ fs.rmSync(installerHome, { recursive: true, force: true });
31
+ fs.rmSync(installerCwd, { recursive: true, force: true });
32
+
33
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2q-home-'));
34
+ const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2q-ws-'));
35
+
36
+ // Mirrors what cursor.js/codex.js/continue.js/copilot.js/hermes.js's
37
+ // getSkillsTarget({isGlobal:true}) all independently return today: the SAME
38
+ // path + SAME manifest, deduped in installer.js's main() into one targetDirs
39
+ // entry before installSkillsToTargets ever runs.
40
+ const globalAgentsDir = path.join(home, '.agents');
41
+ const sharedTarget = {
42
+ path: path.join(globalAgentsDir, 'skills'),
43
+ label: '~/.agents/skills/',
44
+ manifestPath: manifest.getManifestPath(globalAgentsDir),
45
+ };
46
+ // claude.js's global target is separate: ~/.claude/skills/.
47
+ const claudeGlobalTarget = {
48
+ path: path.join(home, '.claude', 'skills'),
49
+ label: '~/.claude/skills/',
50
+ manifestPath: manifest.getManifestPath(path.join(home, '.claude')),
51
+ };
52
+
53
+ const canonicalDir = skills.getCanonicalSkillsDir({ isGlobal: true, workspaceRoot: ws, userHome: home });
54
+ helper.check(
55
+ '2q. global canonical dir IS the pre-existing shared ~/.agents/skills path, not a new location',
56
+ canonicalDir === sharedTarget.path,
57
+ `canonicalDir=${canonicalDir} sharedTarget.path=${sharedTarget.path}`
58
+ );
59
+
60
+ skills.installSkillsToTargets({
61
+ chosenSkills: ['tdd'],
62
+ targetDirs: [sharedTarget, claudeGlobalTarget],
63
+ harnessSourceDir,
64
+ packageVersion: '9.9.9',
65
+ canonicalDir,
66
+ linkMode: 'auto',
67
+ });
68
+
69
+ const sharedSkillDir = path.join(sharedTarget.path, 'tdd');
70
+ const claudeSkillDir = path.join(claudeGlobalTarget.path, 'tdd');
71
+
72
+ helper.check(
73
+ '2q. the shared target (cursor/codex/continue/copilot/hermes stand-in) gets the PHYSICAL copy, not a link to itself',
74
+ fs.existsSync(sharedSkillDir) && !skills.isLinkPath(sharedSkillDir),
75
+ `exists=${fs.existsSync(sharedSkillDir)} isLink=${skills.isLinkPath(sharedSkillDir)}`
76
+ );
77
+ helper.check(
78
+ "2q. Claude's separate global target links into the shared store instead of duplicating it",
79
+ skills.isLinkPath(claudeSkillDir),
80
+ `isLink=${skills.isLinkPath(claudeSkillDir)}`
81
+ );
82
+
83
+ const sharedManifestEntry = manifest.readManifest(sharedTarget.manifestPath).skills.find(s => s.id === 'tdd');
84
+ helper.check(
85
+ '2q. the shared-store manifest entry records a plain copy (no kind/canonicalPath)',
86
+ sharedManifestEntry && !sharedManifestEntry.kind && !sharedManifestEntry.canonicalPath,
87
+ `Got: ${JSON.stringify(sharedManifestEntry)}`
88
+ );
89
+
90
+ // --- Removing Claude's link must NOT delete the shared store: 5 other
91
+ // platforms still depend on it directly (their own manifest row IS that
92
+ // path), even though nothing else "links" to it. ---
93
+ {
94
+ const installed = skills.getInstalledSkills(ws, home);
95
+ const claudeEntry = installed.find(s => s.dirPath === claudeSkillDir);
96
+ skills.removeSkill(claudeEntry, { workspaceRoot: ws, userHome: home });
97
+
98
+ helper.check(
99
+ "2q. removing Claude's link leaves the shared store intact (still independently in use)",
100
+ !fs.existsSync(claudeSkillDir) && fs.existsSync(sharedSkillDir),
101
+ `claude exists=${fs.existsSync(claudeSkillDir)} shared exists=${fs.existsSync(sharedSkillDir)}`
102
+ );
103
+ }
104
+
105
+ // --- Removing the shared store directly must also clean up any dependent
106
+ // link (symmetric case - re-link Claude first to set this up). ---
107
+ {
108
+ skills.installSkillsToTargets({
109
+ chosenSkills: ['tdd'],
110
+ targetDirs: [claudeGlobalTarget],
111
+ harnessSourceDir,
112
+ packageVersion: '9.9.9',
113
+ canonicalDir,
114
+ linkMode: 'auto',
115
+ });
116
+ helper.check('2q. (setup) Claude re-linked to the shared store', skills.isLinkPath(claudeSkillDir), 'relink failed');
117
+
118
+ const installed = skills.getInstalledSkills(ws, home);
119
+ const sharedEntry = installed.find(s => s.dirPath === sharedSkillDir);
120
+ skills.removeSkill(sharedEntry, { workspaceRoot: ws, userHome: home });
121
+
122
+ helper.check(
123
+ "2q. removing the shared store directly also sweeps Claude's now-dangling dependent link",
124
+ !fs.existsSync(sharedSkillDir) && !skills.isLinkPath(claudeSkillDir) && !fs.existsSync(claudeSkillDir),
125
+ `shared exists=${fs.existsSync(sharedSkillDir)} claude isLink=${skills.isLinkPath(claudeSkillDir)} claude exists=${fs.existsSync(claudeSkillDir)}`
126
+ );
127
+ }
128
+
129
+ fs.rmSync(home, { recursive: true, force: true });
130
+ fs.rmSync(ws, { recursive: true, force: true });
131
+
132
+ helper.finish();
@@ -0,0 +1,148 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { spawnSync } = require('child_process');
5
+ const helper = require('./test-helper');
6
+
7
+ console.log('\n[2r] Uninstall CLI flag handling: no silent no-op, combined local+global (issue #48)...');
8
+
9
+ const repoRoot = path.resolve(__dirname, '..');
10
+ const installerPath = path.join(repoRoot, 'scripts', 'installer.js');
11
+
12
+ function freshWorkspace(prefix) {
13
+ const ws = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
14
+ fs.mkdirSync(path.join(ws, '.git')); // pins getWorkspaceRoot() deterministically
15
+ return ws;
16
+ }
17
+
18
+ function run(cwd, args, env) {
19
+ // spawnSync with stdio 'pipe' has no attached TTY - process.stdin.isTTY is
20
+ // undefined, exactly like a piped/non-interactive invocation (npx in CI,
21
+ // some shells, etc.) - the scenario issue #48 is about.
22
+ return spawnSync(process.execPath, [installerPath, ...args], { cwd, env, encoding: 'utf8' });
23
+ }
24
+
25
+ function seedLocalInstall(ws, skillId) {
26
+ fs.mkdirSync(path.join(ws, '.claude'), { recursive: true });
27
+ fs.writeFileSync(path.join(ws, '.claude', 'settings.json'), '{}', 'utf8');
28
+ if (skillId) {
29
+ const skillDir = path.join(ws, '.claude', 'skills', skillId);
30
+ fs.mkdirSync(skillDir, { recursive: true });
31
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\nname: x\nauthor: Miya Daniel\n---\n', 'utf8');
32
+ const manifest = require('../scripts/lib/manifest');
33
+ manifest.recordSkillInstall(manifest.getManifestPath(path.join(ws, '.claude')), require('../package.json').version, skillId, skillDir);
34
+ }
35
+ }
36
+
37
+ function seedGlobalInstall(fakeHome, skillId) {
38
+ fs.mkdirSync(path.join(fakeHome, '.claude'), { recursive: true });
39
+ fs.writeFileSync(path.join(fakeHome, '.claude', 'settings.json'), '{}', 'utf8');
40
+ if (skillId) {
41
+ const skillDir = path.join(fakeHome, '.claude', 'skills', skillId);
42
+ fs.mkdirSync(skillDir, { recursive: true });
43
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\nname: x\nauthor: Miya Daniel\n---\n', 'utf8');
44
+ const manifest = require('../scripts/lib/manifest');
45
+ manifest.recordSkillInstall(manifest.getManifestPath(path.join(fakeHome, '.claude')), require('../package.json').version, skillId, skillDir);
46
+ }
47
+ }
48
+
49
+ // --- 1. Bare `uninstall`, no flags, no TTY: must fail loudly, not silently
50
+ // claim success while removing nothing. ---
51
+ {
52
+ const ws = freshWorkspace('harness-2r-noop-');
53
+ const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2r-noop-home-'));
54
+ seedLocalInstall(ws, 'dummy-skill');
55
+ const env = { ...process.env, HOME: fakeHome, USERPROFILE: fakeHome };
56
+
57
+ const res = run(ws, ['uninstall'], env);
58
+
59
+ helper.check('2r. bare uninstall with no TTY exits non-zero instead of silently succeeding', res.status !== 0, `status=${res.status} stdout=${res.stdout} stderr=${res.stderr}`);
60
+ helper.check('2r. ...and prints a hint about the flags to pass', /--local|--skills|--global/.test(res.stderr), `stderr=${res.stderr}`);
61
+ helper.check(
62
+ '2r. ...and actually removed nothing (settings.json + skill both still present)',
63
+ fs.existsSync(path.join(ws, '.claude', 'settings.json')) && fs.existsSync(path.join(ws, '.claude', 'skills', 'dummy-skill')),
64
+ 'local install was modified despite no flags being passed'
65
+ );
66
+
67
+ fs.rmSync(ws, { recursive: true, force: true });
68
+ fs.rmSync(fakeHome, { recursive: true, force: true });
69
+ }
70
+
71
+ // --- 2. `uninstall --local --skills` (no -y): removes exactly local config +
72
+ // local skills, in one non-interactive run, no menu. ---
73
+ {
74
+ const ws = freshWorkspace('harness-2r-local-');
75
+ const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2r-local-home-'));
76
+ seedLocalInstall(ws, 'dummy-skill');
77
+ const env = { ...process.env, HOME: fakeHome, USERPROFILE: fakeHome };
78
+
79
+ const res = run(ws, ['uninstall', '--local', '--skills'], env);
80
+
81
+ helper.check('2r. `uninstall --local --skills` exits 0', res.status === 0, `status=${res.status} stdout=${res.stdout} stderr=${res.stderr}`);
82
+ helper.check(
83
+ '2r. ...and actually removed the local skill',
84
+ !fs.existsSync(path.join(ws, '.claude', 'skills', 'dummy-skill')),
85
+ `skill dir still exists, stdout=${res.stdout}`
86
+ );
87
+
88
+ fs.rmSync(ws, { recursive: true, force: true });
89
+ fs.rmSync(fakeHome, { recursive: true, force: true });
90
+ }
91
+
92
+ // --- 3. `uninstall --local --global --skills` in ONE run removes both
93
+ // scopes together (issue #48 point 3) - no need for two separate invocations. ---
94
+ {
95
+ const ws = freshWorkspace('harness-2r-both-');
96
+ const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2r-both-home-'));
97
+ seedLocalInstall(ws, 'local-skill');
98
+ seedGlobalInstall(fakeHome, 'global-skill');
99
+ const env = { ...process.env, HOME: fakeHome, USERPROFILE: fakeHome };
100
+
101
+ const res = run(ws, ['uninstall', '--local', '--global', '--skills'], env);
102
+
103
+ helper.check('2r. `uninstall --local --global --skills` exits 0', res.status === 0, `status=${res.status} stdout=${res.stdout} stderr=${res.stderr}`);
104
+ helper.check(
105
+ '2r. ...local skill removed',
106
+ !fs.existsSync(path.join(ws, '.claude', 'skills', 'local-skill')),
107
+ `local skill still exists, stdout=${res.stdout}`
108
+ );
109
+ helper.check(
110
+ '2r. ...AND global skill removed in the SAME run',
111
+ !fs.existsSync(path.join(fakeHome, '.claude', 'skills', 'global-skill')),
112
+ `global skill still exists, stdout=${res.stdout}`
113
+ );
114
+
115
+ fs.rmSync(ws, { recursive: true, force: true });
116
+ fs.rmSync(fakeHome, { recursive: true, force: true });
117
+ }
118
+
119
+ // --- 4. Point 2 regression: a second bare `uninstall --local` after a first
120
+ // run that intentionally left skills in place still finds and can remove
121
+ // them (was previously masked by the same no-op bug as scenario 1). ---
122
+ {
123
+ const ws = freshWorkspace('harness-2r-second-run-');
124
+ const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-2r-second-run-home-'));
125
+ seedLocalInstall(ws, 'dummy-skill');
126
+ const env = { ...process.env, HOME: fakeHome, USERPROFILE: fakeHome };
127
+
128
+ const first = run(ws, ['uninstall', '--local'], env); // config only, skills intentionally kept
129
+ helper.check('2r. first run (--local only) exits 0', first.status === 0, `status=${first.status} stdout=${first.stdout}`);
130
+ helper.check(
131
+ '2r. ...and leaves the skill in place as requested',
132
+ fs.existsSync(path.join(ws, '.claude', 'skills', 'dummy-skill')),
133
+ 'skill was removed even though --skills was not passed'
134
+ );
135
+
136
+ const second = run(ws, ['uninstall', '--skills'], env);
137
+ helper.check('2r. second run (--skills) exits 0', second.status === 0, `status=${second.status} stdout=${second.stdout} stderr=${second.stderr}`);
138
+ helper.check(
139
+ '2r. ...and this time removes the skill left over from the first run',
140
+ !fs.existsSync(path.join(ws, '.claude', 'skills', 'dummy-skill')),
141
+ `skill still exists after second run, stdout=${second.stdout}`
142
+ );
143
+
144
+ fs.rmSync(ws, { recursive: true, force: true });
145
+ fs.rmSync(fakeHome, { recursive: true, force: true });
146
+ }
147
+
148
+ helper.finish();
@@ -0,0 +1,203 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { spawnSync } = require('child_process');
5
+ const helper = require('./test-helper');
6
+ const { evaluateEvidence } = require('../tdd/scripts/quality-gate');
7
+
8
+ console.log('\n[2s] TDD quality evidence gate (issue #58)...');
9
+
10
+ const classes = {
11
+ inputCompleteness: ['required', 'optional', 'omitted', 'empty', 'null', 'min', 'max', 'boundary'],
12
+ outputCompleteness: ['value', 'type', 'schema', 'required-field', 'prohibited-extra-field', 'side-effect'],
13
+ errorHandling: ['error-type', 'error-code', 'message', 'state-cleanup', 'rollback', 'no-unintended-output'],
14
+ unexpectedInput: ['malformed', 'unknown', 'extra', 'unsupported', 'out-of-range', 'duplicate', 'wrong-type']
15
+ };
16
+
17
+ const scenarioCatalog = JSON.parse(fs.readFileSync(path.join(helper.root, 'tdd', 'fixtures', 'quality-scenarios.json'), 'utf8'));
18
+ const requiredScenarios = ['PASS', 'unit profile', 'integration profile', 'weighted integration failure', 'weighted unit failure', 'NON_CONFORMANT', 'SOURCE_DEFECT', 'SOURCE_CONFLICT', 'malformed input', 'unexpected input', 'error handling', 'nondeterministic output', 'auditable N/A', 'skipped test'];
19
+ helper.check('2s. fixture catalog covers every required outcome', requiredScenarios.every(name => scenarioCatalog.scenarios.some(scenario => scenario.name === name)));
20
+
21
+ function checks(prefix, names) {
22
+ return names.map((name, index) => ({
23
+ id: `${prefix}-${index + 1}`,
24
+ class: name,
25
+ status: 'PASS',
26
+ evidence: `tests/example.test.js#${prefix}-${name}`
27
+ }));
28
+ }
29
+
30
+ function validEvidence() {
31
+ const sideEffects = {
32
+ createdFiles: [], changedFiles: [], deletedFiles: [],
33
+ persistentState: {}, events: [], logs: [], externalCalls: []
34
+ };
35
+ return {
36
+ schemaVersion: '1.0.0',
37
+ requirements: [{
38
+ requirementId: 'REQ-001',
39
+ testProfile: 'integration',
40
+ source: {
41
+ type: 'SPEC', path: 'docs/example.md', section: 'Expected behavior', status: 'AUTHORITATIVE'
42
+ },
43
+ sourceConformance: 'PASS',
44
+ sourceValidity: 'VALID',
45
+ dimensions: {
46
+ positiveParameters: { applicable: true, checks: checks('positive', ['supported-partition']) },
47
+ negativeParameters: { applicable: true, checks: checks('negative', ['rejected-partition']) },
48
+ inputCompleteness: { applicable: true, checks: checks('input', classes.inputCompleteness) },
49
+ outputCompleteness: { applicable: true, checks: checks('output', classes.outputCompleteness) },
50
+ errorHandling: { applicable: true, checks: checks('error', classes.errorHandling) },
51
+ unexpectedInput: { applicable: true, checks: checks('unexpected', classes.unexpectedInput) },
52
+ determinism: {
53
+ applicable: true,
54
+ runs: [
55
+ { id: 'run-1', input: { value: 1 }, exitStatus: 0, errorCategory: null, output: { value: 2 }, sideEffects },
56
+ { id: 'run-2', input: { value: 1 }, exitStatus: 0, errorCategory: null, output: { value: 2 }, sideEffects }
57
+ ],
58
+ normalizations: []
59
+ },
60
+ sourceTraceability: { applicable: true }
61
+ },
62
+ skippedTests: [],
63
+ naDeclarations: []
64
+ }]
65
+ };
66
+ }
67
+
68
+ function clone(value) {
69
+ return JSON.parse(JSON.stringify(value));
70
+ }
71
+
72
+ function evaluate(mutator) {
73
+ const evidence = validEvidence();
74
+ if (mutator) mutator(evidence.requirements[0], evidence);
75
+ return evaluateEvidence(evidence);
76
+ }
77
+
78
+ const pass = evaluate();
79
+ helper.check('2s. complete profile-routed evidence passes with a 100 score', pass.result === 'PASS' && pass.score === 100 && pass.requirements[0].testProfile === 'integration');
80
+ helper.check(
81
+ '2s. report publishes the canonical weights and pass threshold',
82
+ pass.scoring.requiredScore === 100 && pass.scoring.weights.positiveParameters === 0.15 && pass.scoring.weights.determinism === 0.10
83
+ );
84
+
85
+ const weightedIntegration = evaluate(req => { req.dimensions.positiveParameters.checks[0].status = 'FAIL'; });
86
+ helper.check('2s. integration score follows the canonical weighted formula exactly', weightedIntegration.score === 85);
87
+
88
+ const missingProfile = evaluate(req => { delete req.testProfile; });
89
+ helper.check('2s. every requirement must declare its selected test profile', missingProfile.result === 'FAIL' && missingProfile.errors.some(e => /testProfile/.test(e)));
90
+
91
+ const unknownProfile = evaluate(req => { req.testProfile = 'contract'; });
92
+ helper.check('2s. only unit and integration profiles are accepted', unknownProfile.result === 'FAIL' && unknownProfile.errors.some(e => /unit or integration/.test(e)));
93
+
94
+ const unitProfile = evaluate(req => {
95
+ req.testProfile = 'unit';
96
+ req.dimensions.determinism = { applicable: false, reason: 'This isolated unit does not cross an integration boundary.' };
97
+ });
98
+ helper.check('2s. unit evidence may mark integration determinism N/A with a reason', unitProfile.result === 'PASS' && unitProfile.requirements[0].dimensions.determinism.applicable === false);
99
+
100
+ const weightedUnit = evaluate(req => {
101
+ req.testProfile = 'unit';
102
+ req.dimensions.determinism = { applicable: false, reason: 'This isolated unit does not cross an integration boundary.' };
103
+ req.dimensions.positiveParameters.checks[0].status = 'FAIL';
104
+ });
105
+ helper.check('2s. unit score renormalizes only the applicable canonical weights', weightedUnit.score === 83.33);
106
+
107
+ const integrationWithoutRepeat = evaluate(req => {
108
+ req.dimensions.determinism = { applicable: false, reason: 'Not measured.' };
109
+ });
110
+ helper.check('2s. integration evidence cannot bypass repeated determinism checks', integrationWithoutRepeat.result === 'FAIL' && integrationWithoutRepeat.errors.some(e => /integration.*determinism/.test(e)));
111
+ helper.check(
112
+ '2s. report exposes raw counts, percentages, and mandatory gates',
113
+ pass.dimensions.inputCompleteness.passed === 8 &&
114
+ pass.dimensions.inputCompleteness.required === 8 &&
115
+ pass.dimensions.inputCompleteness.percentage === 100 &&
116
+ Object.values(pass.mandatoryGates).every(Boolean)
117
+ );
118
+
119
+ const incompleteInput = evaluate(req => { req.dimensions.inputCompleteness.checks.pop(); });
120
+ helper.check('2s. missing an applicable input class is rejected', incompleteInput.result === 'FAIL' && incompleteInput.errors.some(e => /inputCompleteness.*boundary/.test(e)));
121
+
122
+ const incompleteOutput = evaluate(req => { req.dimensions.outputCompleteness.checks[0].status = 'FAIL'; });
123
+ helper.check('2s. incomplete output validation is rejected', incompleteOutput.result === 'FAIL' && incompleteOutput.mandatoryGates.allRequiredInputOutputValidated === false);
124
+
125
+ const failedUnexpected = evaluate(req => { req.dimensions.unexpectedInput.checks[0].status = 'FAIL'; });
126
+ helper.check('2s. failed unexpected-input behavior is rejected', failedUnexpected.result === 'FAIL' && failedUnexpected.dimensions.unexpectedInput.percentage < 100);
127
+
128
+ const untestedError = evaluate(req => { req.dimensions.errorHandling.checks[0].status = 'SKIPPED'; });
129
+ helper.check('2s. untested and skipped error paths are rejected and visible', untestedError.result === 'FAIL' && untestedError.skippedTests.length === 1 && untestedError.mandatoryGates.allDeclaredErrorPathsTested === false);
130
+
131
+ const nondeterministic = evaluate(req => { req.dimensions.determinism.runs[1].output.value = 3; });
132
+ helper.check('2s. unequal repeated integration output is rejected', nondeterministic.result === 'FAIL' && nondeterministic.dimensions.determinism.matchingRuns === 1);
133
+
134
+ const nonConformant = evaluate(req => { req.sourceConformance = 'NON_CONFORMANT'; });
135
+ helper.check('2s. implementation/source mismatches fail', nonConformant.result === 'FAIL' && nonConformant.mandatoryGates.noSourceMismatch === false);
136
+
137
+ const sourceDefect = evaluate(req => { req.sourceValidity = 'SOURCE_DEFECT'; req.sourceNotes = 'The approved example appears internally wrong.'; });
138
+ helper.check('2s. source defects are reported separately without inventing a mismatch', sourceDefect.result === 'PASS' && sourceDefect.requirements[0].sourceValidity === 'SOURCE_DEFECT');
139
+
140
+ const sourceConflict = evaluate(req => {
141
+ req.sourceValidity = 'SOURCE_CONFLICT';
142
+ req.sourceNotes = 'ADR-1 and SPEC-2 disagree.';
143
+ req.additionalSources = [{ type: 'ADR', path: 'docs/adr/001.md', section: 'Contract', status: 'AUTHORITATIVE' }];
144
+ });
145
+ helper.check('2s. unresolved authoritative source conflicts fail', sourceConflict.result === 'FAIL' && sourceConflict.mandatoryGates.noSourceConflict === false && sourceConflict.dimensions.sourceTraceability.traced === 2);
146
+
147
+ const inferred = evaluate(req => { req.source = { type: 'INFERENCE', status: 'INFERRED', inferenceBasis: 'Observed public API and user-approved acceptance criteria.' }; });
148
+ helper.check('2s. inferred expectations remain explicit and traceable', inferred.result === 'PASS' && inferred.dimensions.sourceTraceability.percentage === 100);
149
+
150
+ const badNa = evaluate(req => { req.dimensions.negativeParameters = { applicable: false }; });
151
+ helper.check('2s. N/A declarations without a written reason are rejected', badNa.result === 'FAIL' && badNa.errors.some(e => /negativeParameters.*reason/.test(e)));
152
+
153
+ const goodNa = evaluate(req => { req.dimensions.negativeParameters = { applicable: false, reason: 'This parameterless behavior has no rejected partition.' }; });
154
+ helper.check('2s. reasoned N/A stays visible and does not lower applicable scores', goodNa.result === 'PASS' && goodNa.naDeclarations.length === 1);
155
+
156
+ const normalized = evaluate(req => {
157
+ req.dimensions.determinism.runs[0].output.generatedId = 'a';
158
+ req.dimensions.determinism.runs[1].output.generatedId = 'b';
159
+ req.dimensions.determinism.normalizations = [{ path: 'output.generatedId', rule: 'generated ID may vary', sourceSection: 'docs/example.md#Volatile fields' }];
160
+ });
161
+ helper.check('2s. explicitly source-backed volatile fields may be normalized', normalized.result === 'PASS');
162
+
163
+ const malformed = clone(validEvidence());
164
+ malformed.requirements = 'not-an-array';
165
+ const malformedReport = evaluateEvidence(malformed);
166
+ helper.check('2s. malformed evidence fails closed', malformedReport.result === 'FAIL' && malformedReport.errors.length > 0);
167
+
168
+ const incompleteSideEffects = evaluate(req => { delete req.dimensions.determinism.runs[1].sideEffects.externalCalls; });
169
+ helper.check('2s. incomplete deterministic side-effect evidence is rejected', incompleteSideEffects.result === 'FAIL' && incompleteSideEffects.errors.some(e => /externalCalls is required/.test(e)));
170
+
171
+ const duplicate = clone(validEvidence());
172
+ duplicate.requirements.push(clone(duplicate.requirements[0]));
173
+ const duplicateReport = evaluateEvidence(duplicate);
174
+ helper.check('2s. duplicate requirement IDs are rejected', duplicateReport.result === 'FAIL' && duplicateReport.errors.some(e => /Duplicate requirementId/.test(e)));
175
+
176
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-tdd-quality-'));
177
+ const inputPath = path.join(tmp, 'evidence.json');
178
+ const outputPath = path.join(tmp, 'report.json');
179
+ fs.writeFileSync(inputPath, JSON.stringify(validEvidence()), 'utf8');
180
+ const cli = spawnSync(process.execPath, [path.join(helper.root, 'tdd', 'scripts', 'quality-gate.js'), inputPath, '--output', outputPath], { encoding: 'utf8' });
181
+ helper.check('2s. CLI emits per-requirement human scores and structured JSON', cli.status === 0 && /TDD Quality Gate: PASS/.test(cli.stdout) && /REQ-001 \[integration\]: PASS 100%/.test(cli.stdout) && fs.existsSync(outputPath));
182
+ const failInputPath = path.join(tmp, 'non-conformant.json');
183
+ const failOutputPath = path.join(tmp, 'non-conformant-report.json');
184
+ const failEvidence = validEvidence();
185
+ failEvidence.requirements[0].sourceConformance = 'NON_CONFORMANT';
186
+ fs.writeFileSync(failInputPath, JSON.stringify(failEvidence), 'utf8');
187
+ const failedCli = spawnSync(process.execPath, [path.join(helper.root, 'tdd', 'scripts', 'quality-gate.js'), failInputPath, '--output', failOutputPath], { encoding: 'utf8' });
188
+ let failedReport = null;
189
+ try { failedReport = JSON.parse(fs.readFileSync(failOutputPath, 'utf8')); } catch (e) { /* asserted below */ }
190
+ helper.check(
191
+ '2s. CLI returns exit 1 and writes structured JSON for a valid FAIL report',
192
+ failedCli.status === 1 && /TDD Quality Gate: FAIL/.test(failedCli.stdout) && failedReport && failedReport.result === 'FAIL'
193
+ );
194
+ const malformedPath = path.join(tmp, 'malformed.json');
195
+ fs.writeFileSync(malformedPath, '{', 'utf8');
196
+ const malformedCli = spawnSync(process.execPath, [path.join(helper.root, 'tdd', 'scripts', 'quality-gate.js'), malformedPath], { encoding: 'utf8' });
197
+ helper.check('2s. CLI returns exit 2 for malformed JSON', malformedCli.status === 2 && /TDD Quality Gate: FAIL/.test(malformedCli.stderr));
198
+ fs.rmSync(tmp, { recursive: true, force: true });
199
+
200
+ const shippedFixture = JSON.parse(fs.readFileSync(path.join(helper.root, 'tdd', 'fixtures', 'pass-evidence.json'), 'utf8'));
201
+ helper.check('2s. shipped PASS evidence is executable', evaluateEvidence(shippedFixture).result === 'PASS');
202
+
203
+ helper.finish();