@rpamis/comet 0.4.0-beta.3 → 0.4.0-beta.4

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 (508) hide show
  1. package/README.md +10 -0
  2. package/assets/manifest.json +2 -1
  3. package/assets/skills/comet/SKILL.md +25 -1
  4. package/assets/skills/comet/reference/comet-yaml-fields.md +5 -2
  5. package/assets/skills/comet/reference/context-recovery.md +10 -0
  6. package/assets/skills/comet/reference/scripts.md +47 -15
  7. package/assets/skills/comet/rules/comet-phase-guard.en.md +8 -0
  8. package/assets/skills/comet/rules/comet-phase-guard.md +9 -1
  9. package/assets/skills/comet/scripts/comet-resume-probe.mjs +3 -0
  10. package/assets/skills/comet/scripts/comet-runtime.mjs +1387 -424
  11. package/assets/skills/comet-archive/SKILL.md +24 -11
  12. package/assets/skills/comet-build/SKILL.md +42 -25
  13. package/assets/skills/comet-design/SKILL.md +1 -0
  14. package/assets/skills/comet-hotfix/SKILL.md +3 -1
  15. package/assets/skills/comet-open/SKILL.md +11 -1
  16. package/assets/skills/comet-tweak/SKILL.md +3 -1
  17. package/assets/skills/comet-verify/SKILL.md +29 -20
  18. package/assets/skills-zh/comet/SKILL.md +25 -1
  19. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +5 -2
  20. package/assets/skills-zh/comet/reference/context-recovery.md +10 -0
  21. package/assets/skills-zh/comet/reference/scripts.md +47 -15
  22. package/assets/skills-zh/comet-archive/SKILL.md +24 -11
  23. package/assets/skills-zh/comet-build/SKILL.md +42 -25
  24. package/assets/skills-zh/comet-design/SKILL.md +1 -0
  25. package/assets/skills-zh/comet-hotfix/SKILL.md +3 -1
  26. package/assets/skills-zh/comet-open/SKILL.md +11 -1
  27. package/assets/skills-zh/comet-tweak/SKILL.md +3 -1
  28. package/assets/skills-zh/comet-verify/SKILL.md +30 -21
  29. package/dist/app/cli/comet-banner.d.ts +25 -0
  30. package/dist/app/cli/comet-banner.d.ts.map +1 -0
  31. package/dist/app/cli/comet-banner.js +265 -0
  32. package/dist/app/cli/comet-banner.js.map +1 -0
  33. package/dist/app/cli/index.js +36 -2
  34. package/dist/app/cli/index.js.map +1 -1
  35. package/dist/app/commands/classic.d.ts +4 -0
  36. package/dist/app/commands/classic.d.ts.map +1 -0
  37. package/dist/app/commands/classic.js +11 -0
  38. package/dist/app/commands/classic.js.map +1 -0
  39. package/dist/app/commands/doctor.d.ts.map +1 -1
  40. package/dist/app/commands/doctor.js +39 -24
  41. package/dist/app/commands/doctor.js.map +1 -1
  42. package/dist/app/commands/eval.d.ts.map +1 -1
  43. package/dist/app/commands/eval.js +57 -20
  44. package/dist/app/commands/eval.js.map +1 -1
  45. package/dist/app/commands/i18n.d.ts +1 -1
  46. package/dist/app/commands/i18n.d.ts.map +1 -1
  47. package/dist/app/commands/i18n.js +12 -4
  48. package/dist/app/commands/i18n.js.map +1 -1
  49. package/dist/app/commands/init.d.ts.map +1 -1
  50. package/dist/app/commands/init.js +16 -13
  51. package/dist/app/commands/init.js.map +1 -1
  52. package/dist/app/commands/project-scope-selection.d.ts +13 -0
  53. package/dist/app/commands/project-scope-selection.d.ts.map +1 -0
  54. package/dist/app/commands/project-scope-selection.js +33 -0
  55. package/dist/app/commands/project-scope-selection.js.map +1 -0
  56. package/dist/app/commands/resume-probe.d.ts +11 -0
  57. package/dist/app/commands/resume-probe.d.ts.map +1 -0
  58. package/dist/app/commands/resume-probe.js +63 -0
  59. package/dist/app/commands/resume-probe.js.map +1 -0
  60. package/dist/app/commands/status.d.ts +31 -0
  61. package/dist/app/commands/status.d.ts.map +1 -1
  62. package/dist/app/commands/status.js +109 -20
  63. package/dist/app/commands/status.js.map +1 -1
  64. package/dist/app/commands/uninstall.d.ts +2 -0
  65. package/dist/app/commands/uninstall.d.ts.map +1 -1
  66. package/dist/app/commands/uninstall.js +217 -40
  67. package/dist/app/commands/uninstall.js.map +1 -1
  68. package/dist/app/commands/update.d.ts +6 -0
  69. package/dist/app/commands/update.d.ts.map +1 -1
  70. package/dist/app/commands/update.js +361 -68
  71. package/dist/app/commands/update.js.map +1 -1
  72. package/dist/config/repository-layout.json +4 -2
  73. package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
  74. package/dist/domains/bundle/bundle-platform.js +3 -1
  75. package/dist/domains/bundle/bundle-platform.js.map +1 -1
  76. package/dist/domains/bundle/eval-manifest-runtime.d.ts +6 -0
  77. package/dist/domains/bundle/eval-manifest-runtime.d.ts.map +1 -0
  78. package/dist/domains/bundle/eval-manifest-runtime.js +58 -0
  79. package/dist/domains/bundle/eval-manifest-runtime.js.map +1 -0
  80. package/dist/domains/bundle/types.d.ts +1 -1
  81. package/dist/domains/bundle/types.d.ts.map +1 -1
  82. package/dist/domains/comet-classic/classic-archive.d.ts +1 -0
  83. package/dist/domains/comet-classic/classic-archive.d.ts.map +1 -1
  84. package/dist/domains/comet-classic/classic-archive.js +35 -38
  85. package/dist/domains/comet-classic/classic-archive.js.map +1 -1
  86. package/dist/domains/comet-classic/classic-cli.d.ts +1 -1
  87. package/dist/domains/comet-classic/classic-cli.d.ts.map +1 -1
  88. package/dist/domains/comet-classic/classic-cli.js +3 -0
  89. package/dist/domains/comet-classic/classic-cli.js.map +1 -1
  90. package/dist/domains/comet-classic/classic-command-checks.d.ts +20 -0
  91. package/dist/domains/comet-classic/classic-command-checks.d.ts.map +1 -0
  92. package/dist/domains/comet-classic/classic-command-checks.js +97 -0
  93. package/dist/domains/comet-classic/classic-command-checks.js.map +1 -0
  94. package/dist/domains/comet-classic/classic-current-change.d.ts +19 -0
  95. package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -0
  96. package/dist/domains/comet-classic/classic-current-change.js +132 -0
  97. package/dist/domains/comet-classic/classic-current-change.js.map +1 -0
  98. package/dist/domains/comet-classic/classic-diagnostics.d.ts +2 -1
  99. package/dist/domains/comet-classic/classic-diagnostics.d.ts.map +1 -1
  100. package/dist/domains/comet-classic/classic-diagnostics.js +54 -1
  101. package/dist/domains/comet-classic/classic-diagnostics.js.map +1 -1
  102. package/dist/domains/comet-classic/classic-evidence.d.ts.map +1 -1
  103. package/dist/domains/comet-classic/classic-evidence.js +4 -0
  104. package/dist/domains/comet-classic/classic-evidence.js.map +1 -1
  105. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  106. package/dist/domains/comet-classic/classic-guard.js +68 -69
  107. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  108. package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
  109. package/dist/domains/comet-classic/classic-hook-guard.js +66 -23
  110. package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
  111. package/dist/domains/comet-classic/classic-project-config.d.ts +16 -0
  112. package/dist/domains/comet-classic/classic-project-config.d.ts.map +1 -0
  113. package/dist/domains/comet-classic/classic-project-config.js +40 -0
  114. package/dist/domains/comet-classic/classic-project-config.js.map +1 -0
  115. package/dist/domains/comet-classic/classic-resolver.d.ts.map +1 -1
  116. package/dist/domains/comet-classic/classic-resolver.js +3 -3
  117. package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
  118. package/dist/domains/comet-classic/classic-resume-probe-command.d.ts +3 -0
  119. package/dist/domains/comet-classic/classic-resume-probe-command.d.ts.map +1 -0
  120. package/dist/domains/comet-classic/classic-resume-probe-command.js +58 -0
  121. package/dist/domains/comet-classic/classic-resume-probe-command.js.map +1 -0
  122. package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts +2 -0
  123. package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts.map +1 -0
  124. package/dist/domains/comet-classic/classic-resume-probe-entry.js +4 -0
  125. package/dist/domains/comet-classic/classic-resume-probe-entry.js.map +1 -0
  126. package/dist/domains/comet-classic/classic-resume-probe.d.ts +29 -0
  127. package/dist/domains/comet-classic/classic-resume-probe.d.ts.map +1 -0
  128. package/dist/domains/comet-classic/classic-resume-probe.js +386 -0
  129. package/dist/domains/comet-classic/classic-resume-probe.js.map +1 -0
  130. package/dist/domains/comet-classic/classic-runtime-run.d.ts +2 -1
  131. package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
  132. package/dist/domains/comet-classic/classic-runtime-run.js +38 -0
  133. package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
  134. package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
  135. package/dist/domains/comet-classic/classic-state-command.js +128 -12
  136. package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
  137. package/dist/domains/comet-classic/classic-state.d.ts +3 -1
  138. package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
  139. package/dist/domains/comet-classic/classic-state.js +4 -0
  140. package/dist/domains/comet-classic/classic-state.js.map +1 -1
  141. package/dist/domains/comet-classic/classic-store.d.ts +4 -1
  142. package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
  143. package/dist/domains/comet-classic/classic-store.js +4 -3
  144. package/dist/domains/comet-classic/classic-store.js.map +1 -1
  145. package/dist/domains/comet-classic/classic-transitions.d.ts +1 -1
  146. package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
  147. package/dist/domains/comet-classic/classic-transitions.js +20 -1
  148. package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
  149. package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
  150. package/dist/domains/comet-classic/classic-validate-command.js +1 -0
  151. package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
  152. package/dist/domains/comet-classic/index.d.ts +3 -0
  153. package/dist/domains/comet-classic/index.d.ts.map +1 -1
  154. package/dist/domains/comet-classic/index.js +3 -0
  155. package/dist/domains/comet-classic/index.js.map +1 -1
  156. package/dist/domains/dashboard/collector.js +10 -2
  157. package/dist/domains/dashboard/collector.js.map +1 -1
  158. package/dist/domains/dashboard/web/assets/{index-CjU3VXVu.js → index-CIvN-PL8.js} +11 -11
  159. package/dist/domains/dashboard/web/assets/{index-BcO3_QlT.css → index-DcyRPBnT.css} +1 -1
  160. package/dist/domains/dashboard/web/index.html +2 -2
  161. package/dist/domains/engine/types.d.ts +1 -1
  162. package/dist/domains/engine/types.d.ts.map +1 -1
  163. package/dist/domains/integrations/openspec.d.ts.map +1 -1
  164. package/dist/domains/integrations/openspec.js +6 -6
  165. package/dist/domains/integrations/openspec.js.map +1 -1
  166. package/dist/domains/skill/find.d.ts.map +1 -1
  167. package/dist/domains/skill/find.js +1 -6
  168. package/dist/domains/skill/find.js.map +1 -1
  169. package/dist/domains/skill/managed-markdown.d.ts +14 -0
  170. package/dist/domains/skill/managed-markdown.d.ts.map +1 -0
  171. package/dist/domains/skill/managed-markdown.js +126 -0
  172. package/dist/domains/skill/managed-markdown.js.map +1 -0
  173. package/dist/domains/skill/platform-install.d.ts +4 -3
  174. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  175. package/dist/domains/skill/platform-install.js +96 -26
  176. package/dist/domains/skill/platform-install.js.map +1 -1
  177. package/dist/domains/skill/project-instructions.d.ts +22 -0
  178. package/dist/domains/skill/project-instructions.d.ts.map +1 -0
  179. package/dist/domains/skill/project-instructions.js +61 -0
  180. package/dist/domains/skill/project-instructions.js.map +1 -0
  181. package/dist/domains/skill/uninstall.d.ts +3 -1
  182. package/dist/domains/skill/uninstall.d.ts.map +1 -1
  183. package/dist/domains/skill/uninstall.js +86 -39
  184. package/dist/domains/skill/uninstall.js.map +1 -1
  185. package/dist/platform/install/detect.d.ts +2 -1
  186. package/dist/platform/install/detect.d.ts.map +1 -1
  187. package/dist/platform/install/detect.js +10 -1
  188. package/dist/platform/install/detect.js.map +1 -1
  189. package/dist/platform/install/platforms.d.ts +5 -1
  190. package/dist/platform/install/platforms.d.ts.map +1 -1
  191. package/dist/platform/install/platforms.js +21 -4
  192. package/dist/platform/install/platforms.js.map +1 -1
  193. package/dist/platform/install/project-registry.d.ts +37 -0
  194. package/dist/platform/install/project-registry.d.ts.map +1 -0
  195. package/dist/platform/install/project-registry.js +205 -0
  196. package/dist/platform/install/project-registry.js.map +1 -0
  197. package/eval/.env +28 -0
  198. package/eval/.env.example +52 -0
  199. package/eval/CLAUDE.md +43 -0
  200. package/eval/README.md +538 -0
  201. package/eval/langsmith/.env.example +16 -0
  202. package/eval/langsmith/README.md +93 -0
  203. package/eval/langsmith/skills/README.md +5 -0
  204. package/eval/langsmith/tasks/README.md +5 -0
  205. package/eval/langsmith/tests/conftest.py +187 -0
  206. package/eval/langsmith/tests/tasks/test_tasks.py +287 -0
  207. package/eval/langsmith/treatments/README.md +5 -0
  208. package/eval/local/README.md +80 -0
  209. package/eval/local/regression_baseline.json +13 -0
  210. package/eval/local/report-style-demo-assets/quality_cost.en.svg +41 -0
  211. package/eval/local/report-style-demo-assets/quality_cost.zh.svg +41 -0
  212. package/eval/local/report-style-demo-assets/rubric_delta.en.svg +56 -0
  213. package/eval/local/report-style-demo-assets/rubric_delta.zh.svg +56 -0
  214. package/eval/local/report-style-demo-assets/task_outcomes.en.svg +46 -0
  215. package/eval/local/report-style-demo-assets/task_outcomes.zh.svg +46 -0
  216. package/eval/local/report-style-demo.html +867 -0
  217. package/eval/local/scripts/compare_baselines.py +1018 -0
  218. package/eval/local/scripts/generate_report_style_demo_charts.py +322 -0
  219. package/eval/local/scripts/regression_check.py +171 -0
  220. package/eval/local/scripts/render_readme_benchmark_figures.py +416 -0
  221. package/eval/local/scripts/rescore_rubric.py +120 -0
  222. package/eval/local/skills/benchmarks/039-release/comet-classic-039/SKILL.md +282 -0
  223. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/auto-transition.md +27 -0
  224. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/comet-yaml-fields.md +68 -0
  225. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/context-recovery.md +35 -0
  226. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/debug-gate.md +17 -0
  227. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/decision-point.md +20 -0
  228. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/dirty-worktree.md +59 -0
  229. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/file-structure.md +28 -0
  230. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/subagent-dispatch.md +113 -0
  231. package/eval/local/skills/benchmarks/039-release/comet-classic-039/rules/comet-phase-guard.md +125 -0
  232. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-archive.sh +311 -0
  233. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-env.sh +110 -0
  234. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-guard.sh +778 -0
  235. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-handoff.sh +390 -0
  236. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-hook-guard.sh +336 -0
  237. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-state.sh +1338 -0
  238. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-yaml-validate.sh +225 -0
  239. package/eval/local/skills/benchmarks/039-release/comet-classic-039-archive/SKILL.md +100 -0
  240. package/eval/local/skills/benchmarks/039-release/comet-classic-039-build/SKILL.md +304 -0
  241. package/eval/local/skills/benchmarks/039-release/comet-classic-039-design/SKILL.md +263 -0
  242. package/eval/local/skills/benchmarks/039-release/comet-classic-039-hotfix/SKILL.md +204 -0
  243. package/eval/local/skills/benchmarks/039-release/comet-classic-039-open/SKILL.md +220 -0
  244. package/eval/local/skills/benchmarks/039-release/comet-classic-039-tweak/SKILL.md +180 -0
  245. package/eval/local/skills/benchmarks/039-release/comet-classic-039-verify/SKILL.md +234 -0
  246. package/eval/local/skills/benchmarks/040-beta/comet/SKILL.md +261 -0
  247. package/eval/local/skills/benchmarks/040-beta/comet/reference/auto-transition.md +27 -0
  248. package/eval/local/skills/benchmarks/040-beta/comet/reference/comet-yaml-fields.md +73 -0
  249. package/eval/local/skills/benchmarks/040-beta/comet/reference/context-recovery.md +45 -0
  250. package/eval/local/skills/benchmarks/040-beta/comet/reference/debug-gate.md +33 -0
  251. package/eval/local/skills/benchmarks/040-beta/comet/reference/decision-point.md +31 -0
  252. package/eval/local/skills/benchmarks/040-beta/comet/reference/dirty-worktree.md +59 -0
  253. package/eval/local/skills/benchmarks/040-beta/comet/reference/file-structure.md +28 -0
  254. package/eval/local/skills/benchmarks/040-beta/comet/reference/intent-frame.md +74 -0
  255. package/eval/local/skills/benchmarks/040-beta/comet/reference/scripts.md +69 -0
  256. package/eval/local/skills/benchmarks/040-beta/comet/reference/subagent-dispatch.md +169 -0
  257. package/eval/local/skills/benchmarks/040-beta/comet/rules/comet-phase-guard.md +130 -0
  258. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/checks.yaml +6 -0
  259. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/guardrails.yaml +13 -0
  260. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/skill.yaml +122 -0
  261. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-archive.mjs +9515 -0
  262. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-env.mjs +15 -0
  263. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-guard.mjs +10173 -0
  264. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-handoff.mjs +9491 -0
  265. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-hook-guard.mjs +9175 -0
  266. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-intent.mjs +404 -0
  267. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-state.mjs +9273 -0
  268. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-yaml-validate.mjs +7627 -0
  269. package/eval/local/skills/benchmarks/040-beta/comet-archive/SKILL.md +110 -0
  270. package/eval/local/skills/benchmarks/040-beta/comet-build/SKILL.md +315 -0
  271. package/eval/local/skills/benchmarks/040-beta/comet-design/SKILL.md +260 -0
  272. package/eval/local/skills/benchmarks/040-beta/comet-hotfix/SKILL.md +190 -0
  273. package/eval/local/skills/benchmarks/040-beta/comet-open/SKILL.md +210 -0
  274. package/eval/local/skills/benchmarks/040-beta/comet-tweak/SKILL.md +183 -0
  275. package/eval/local/skills/benchmarks/040-beta/comet-verify/SKILL.md +230 -0
  276. package/eval/local/skills/benchmarks/dependency/claude-md/comet-workflow/CLAUDE.md +12 -0
  277. package/eval/local/skills/benchmarks/dependency/openspec/openspec-apply-change/SKILL.md +156 -0
  278. package/eval/local/skills/benchmarks/dependency/openspec/openspec-archive-change/SKILL.md +114 -0
  279. package/eval/local/skills/benchmarks/dependency/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  280. package/eval/local/skills/benchmarks/dependency/openspec/openspec-continue-change/SKILL.md +118 -0
  281. package/eval/local/skills/benchmarks/dependency/openspec/openspec-explore/SKILL.md +288 -0
  282. package/eval/local/skills/benchmarks/dependency/openspec/openspec-ff-change/SKILL.md +101 -0
  283. package/eval/local/skills/benchmarks/dependency/openspec/openspec-new-change/SKILL.md +74 -0
  284. package/eval/local/skills/benchmarks/dependency/openspec/openspec-onboard/SKILL.md +554 -0
  285. package/eval/local/skills/benchmarks/dependency/openspec/openspec-propose/SKILL.md +110 -0
  286. package/eval/local/skills/benchmarks/dependency/openspec/openspec-sync-specs/SKILL.md +138 -0
  287. package/eval/local/skills/benchmarks/dependency/openspec/openspec-verify-change/SKILL.md +168 -0
  288. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/SKILL.md +159 -0
  289. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/frame-template.html +213 -0
  290. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/helper.js +167 -0
  291. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/server.cjs +723 -0
  292. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/start-server.sh +209 -0
  293. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/stop-server.sh +120 -0
  294. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
  295. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/visual-companion.md +291 -0
  296. package/eval/local/skills/benchmarks/dependency/superpowers/dispatching-parallel-agents/SKILL.md +185 -0
  297. package/eval/local/skills/benchmarks/dependency/superpowers/executing-plans/SKILL.md +70 -0
  298. package/eval/local/skills/benchmarks/dependency/superpowers/finishing-a-development-branch/SKILL.md +241 -0
  299. package/eval/local/skills/benchmarks/dependency/superpowers/receiving-code-review/SKILL.md +213 -0
  300. package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/SKILL.md +103 -0
  301. package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/code-reviewer.md +172 -0
  302. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/SKILL.md +418 -0
  303. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/implementer-prompt.md +139 -0
  304. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/review-package +44 -0
  305. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/sdd-workspace +22 -0
  306. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/task-brief +40 -0
  307. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/task-reviewer-prompt.md +188 -0
  308. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/CREATION-LOG.md +119 -0
  309. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/SKILL.md +296 -0
  310. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
  311. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting.md +115 -0
  312. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/defense-in-depth.md +122 -0
  313. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/find-polluter.sh +63 -0
  314. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/root-cause-tracing.md +169 -0
  315. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-academic.md +14 -0
  316. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-1.md +58 -0
  317. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-2.md +68 -0
  318. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-3.md +69 -0
  319. package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/SKILL.md +371 -0
  320. package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/testing-anti-patterns.md +299 -0
  321. package/eval/local/skills/benchmarks/dependency/superpowers/using-git-worktrees/SKILL.md +202 -0
  322. package/eval/local/skills/benchmarks/dependency/superpowers/verification-before-completion/SKILL.md +139 -0
  323. package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/SKILL.md +174 -0
  324. package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
  325. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/SKILL.md +689 -0
  326. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/anthropic-best-practices.md +1150 -0
  327. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  328. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/graphviz-conventions.dot +172 -0
  329. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/persuasion-principles.md +187 -0
  330. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/render-graphs.js +168 -0
  331. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
  332. package/eval/local/tasks/authoring-skill-smoke/environment/Dockerfile +2 -0
  333. package/eval/local/tasks/authoring-skill-smoke/instruction.md +4 -0
  334. package/eval/local/tasks/authoring-skill-smoke/task.toml +21 -0
  335. package/eval/local/tasks/authoring-skill-smoke/validation/test_authoring_skill_smoke.py +54 -0
  336. package/eval/local/tasks/comet-agent-memory-routing/environment/Dockerfile +28 -0
  337. package/eval/local/tasks/comet-agent-memory-routing/environment/agent_system.py +40 -0
  338. package/eval/local/tasks/comet-agent-memory-routing/environment/test_agent_system.py +29 -0
  339. package/eval/local/tasks/comet-agent-memory-routing/instruction.md +20 -0
  340. package/eval/local/tasks/comet-agent-memory-routing/task.toml +24 -0
  341. package/eval/local/tasks/comet-agent-memory-routing/validation/test_agent_memory_routing.py +24 -0
  342. package/eval/local/tasks/comet-api-cache-ttl/environment/Dockerfile +39 -0
  343. package/eval/local/tasks/comet-api-cache-ttl/environment/cache.py +39 -0
  344. package/eval/local/tasks/comet-api-cache-ttl/environment/test_cache.py +81 -0
  345. package/eval/local/tasks/comet-api-cache-ttl/instruction.md +33 -0
  346. package/eval/local/tasks/comet-api-cache-ttl/task.toml +24 -0
  347. package/eval/local/tasks/comet-api-cache-ttl/validation/test_api_cache_ttl.py +58 -0
  348. package/eval/local/tasks/comet-cross-file-refactor/environment/Dockerfile +15 -0
  349. package/eval/local/tasks/comet-cross-file-refactor/environment/cli.py +13 -0
  350. package/eval/local/tasks/comet-cross-file-refactor/environment/counting.py +13 -0
  351. package/eval/local/tasks/comet-cross-file-refactor/environment/test_textkit.py +21 -0
  352. package/eval/local/tasks/comet-cross-file-refactor/instruction.md +16 -0
  353. package/eval/local/tasks/comet-cross-file-refactor/task.toml +20 -0
  354. package/eval/local/tasks/comet-cross-file-refactor/validation/test_cross_file_refactor.py +20 -0
  355. package/eval/local/tasks/comet-dependency-confusion/environment/Dockerfile +28 -0
  356. package/eval/local/tasks/comet-dependency-confusion/environment/app.py +8 -0
  357. package/eval/local/tasks/comet-dependency-confusion/environment/settings_loader.py +14 -0
  358. package/eval/local/tasks/comet-dependency-confusion/environment/test_settings_service.py +19 -0
  359. package/eval/local/tasks/comet-dependency-confusion/instruction.md +18 -0
  360. package/eval/local/tasks/comet-dependency-confusion/task.toml +24 -0
  361. package/eval/local/tasks/comet-dependency-confusion/validation/test_dependency_confusion.py +24 -0
  362. package/eval/local/tasks/comet-fix-median/environment/Dockerfile +50 -0
  363. package/eval/local/tasks/comet-fix-median/environment/stats.py +54 -0
  364. package/eval/local/tasks/comet-fix-median/environment/test_stats.py +68 -0
  365. package/eval/local/tasks/comet-fix-median/instruction.md +25 -0
  366. package/eval/local/tasks/comet-fix-median/task.toml +24 -0
  367. package/eval/local/tasks/comet-fix-median/validation/test_fix_median.py +60 -0
  368. package/eval/local/tasks/comet-framework-selection/environment/Dockerfile +28 -0
  369. package/eval/local/tasks/comet-framework-selection/environment/architecture.py +12 -0
  370. package/eval/local/tasks/comet-framework-selection/environment/test_architecture.py +30 -0
  371. package/eval/local/tasks/comet-framework-selection/instruction.md +22 -0
  372. package/eval/local/tasks/comet-framework-selection/task.toml +24 -0
  373. package/eval/local/tasks/comet-framework-selection/validation/test_framework_selection.py +24 -0
  374. package/eval/local/tasks/comet-full-workflow/environment/Dockerfile +53 -0
  375. package/eval/local/tasks/comet-full-workflow/environment/test_wordcount.py +29 -0
  376. package/eval/local/tasks/comet-full-workflow/environment/wordcount.py +40 -0
  377. package/eval/local/tasks/comet-full-workflow/instruction.md +18 -0
  378. package/eval/local/tasks/comet-full-workflow/task.toml +24 -0
  379. package/eval/local/tasks/comet-full-workflow/validation/test_full_workflow.py +171 -0
  380. package/eval/local/tasks/comet-graph-execution-review/environment/Dockerfile +28 -0
  381. package/eval/local/tasks/comet-graph-execution-review/environment/pipeline.py +19 -0
  382. package/eval/local/tasks/comet-graph-execution-review/environment/test_pipeline.py +53 -0
  383. package/eval/local/tasks/comet-graph-execution-review/instruction.md +21 -0
  384. package/eval/local/tasks/comet-graph-execution-review/task.toml +24 -0
  385. package/eval/local/tasks/comet-graph-execution-review/validation/test_graph_execution_review.py +24 -0
  386. package/eval/local/tasks/comet-human-approval-flow/environment/Dockerfile +15 -0
  387. package/eval/local/tasks/comet-human-approval-flow/environment/approvals.py +17 -0
  388. package/eval/local/tasks/comet-human-approval-flow/environment/test_approvals.py +30 -0
  389. package/eval/local/tasks/comet-human-approval-flow/instruction.md +15 -0
  390. package/eval/local/tasks/comet-human-approval-flow/task.toml +20 -0
  391. package/eval/local/tasks/comet-human-approval-flow/validation/test_human_approval_flow.py +20 -0
  392. package/eval/local/tasks/comet-layered-streaming-fix/environment/Dockerfile +20 -0
  393. package/eval/local/tasks/comet-layered-streaming-fix/environment/chat_app.py +19 -0
  394. package/eval/local/tasks/comet-layered-streaming-fix/environment/test_chat_app.py +15 -0
  395. package/eval/local/tasks/comet-layered-streaming-fix/instruction.md +14 -0
  396. package/eval/local/tasks/comet-layered-streaming-fix/task.toml +20 -0
  397. package/eval/local/tasks/comet-layered-streaming-fix/validation/test_layered_streaming_fix.py +20 -0
  398. package/eval/local/tasks/comet-noise-distractor/environment/Dockerfile +15 -0
  399. package/eval/local/tasks/comet-noise-distractor/environment/distractor.md +4 -0
  400. package/eval/local/tasks/comet-noise-distractor/environment/invoice.py +5 -0
  401. package/eval/local/tasks/comet-noise-distractor/environment/test_invoice.py +18 -0
  402. package/eval/local/tasks/comet-noise-distractor/instruction.md +17 -0
  403. package/eval/local/tasks/comet-noise-distractor/task.toml +20 -0
  404. package/eval/local/tasks/comet-noise-distractor/validation/test_noise_distractor.py +20 -0
  405. package/eval/local/tasks/comet-observability-env-template/environment/Dockerfile +15 -0
  406. package/eval/local/tasks/comet-observability-env-template/environment/env_writer.py +17 -0
  407. package/eval/local/tasks/comet-observability-env-template/environment/test_env_writer.py +17 -0
  408. package/eval/local/tasks/comet-observability-env-template/instruction.md +20 -0
  409. package/eval/local/tasks/comet-observability-env-template/task.toml +20 -0
  410. package/eval/local/tasks/comet-observability-env-template/validation/test_observability_env_template.py +46 -0
  411. package/eval/local/tasks/comet-perf-dedupe/environment/Dockerfile +39 -0
  412. package/eval/local/tasks/comet-perf-dedupe/environment/dedupe.py +22 -0
  413. package/eval/local/tasks/comet-perf-dedupe/environment/test_dedupe.py +43 -0
  414. package/eval/local/tasks/comet-perf-dedupe/instruction.md +25 -0
  415. package/eval/local/tasks/comet-perf-dedupe/task.toml +24 -0
  416. package/eval/local/tasks/comet-perf-dedupe/validation/test_perf_dedupe.py +40 -0
  417. package/eval/local/tasks/comet-persistence-threading/environment/Dockerfile +15 -0
  418. package/eval/local/tasks/comet-persistence-threading/environment/session_store.py +15 -0
  419. package/eval/local/tasks/comet-persistence-threading/environment/test_session_store.py +21 -0
  420. package/eval/local/tasks/comet-persistence-threading/instruction.md +14 -0
  421. package/eval/local/tasks/comet-persistence-threading/task.toml +20 -0
  422. package/eval/local/tasks/comet-persistence-threading/validation/test_persistence_threading.py +20 -0
  423. package/eval/local/tasks/comet-refactor-counter/environment/Dockerfile +39 -0
  424. package/eval/local/tasks/comet-refactor-counter/environment/test_text_processor.py +57 -0
  425. package/eval/local/tasks/comet-refactor-counter/environment/text_processor.py +49 -0
  426. package/eval/local/tasks/comet-refactor-counter/instruction.md +31 -0
  427. package/eval/local/tasks/comet-refactor-counter/task.toml +24 -0
  428. package/eval/local/tasks/comet-refactor-counter/validation/test_refactor_counter.py +75 -0
  429. package/eval/local/tasks/comet-robust-config/environment/Dockerfile +39 -0
  430. package/eval/local/tasks/comet-robust-config/environment/config_loader.py +56 -0
  431. package/eval/local/tasks/comet-robust-config/environment/test_config_loader.py +72 -0
  432. package/eval/local/tasks/comet-robust-config/instruction.md +30 -0
  433. package/eval/local/tasks/comet-robust-config/task.toml +24 -0
  434. package/eval/local/tasks/comet-robust-config/validation/test_robust_config.py +37 -0
  435. package/eval/local/tasks/generic-skill-smoke/environment/Dockerfile +2 -0
  436. package/eval/local/tasks/generic-skill-smoke/instruction.md +7 -0
  437. package/eval/local/tasks/generic-skill-smoke/task.toml +21 -0
  438. package/eval/local/tasks/generic-skill-smoke/validation/test_generic_skill_smoke.py +36 -0
  439. package/eval/local/tasks/index.yaml +155 -0
  440. package/eval/local/tasks/workflow-overlay-contract/environment/Dockerfile +5 -0
  441. package/eval/local/tasks/workflow-overlay-contract/instruction.md +5 -0
  442. package/eval/local/tasks/workflow-overlay-contract/task.toml +21 -0
  443. package/eval/local/tasks/workflow-overlay-contract/validation/test_workflow_overlay_contract.py +235 -0
  444. package/eval/local/tasks/workflow-route-conformance/environment/Dockerfile +2 -0
  445. package/eval/local/tasks/workflow-route-conformance/instruction.md +5 -0
  446. package/eval/local/tasks/workflow-route-conformance/task.toml +21 -0
  447. package/eval/local/tasks/workflow-route-conformance/validation/test_workflow_route_conformance.py +84 -0
  448. package/eval/local/tests/conftest.py +1148 -0
  449. package/eval/local/tests/scaffold/test_attribution.py +48 -0
  450. package/eval/local/tests/scaffold/test_compare_baselines.py +999 -0
  451. package/eval/local/tests/scaffold/test_conftest_helpers.py +534 -0
  452. package/eval/local/tests/scaffold/test_evidence.py +15 -0
  453. package/eval/local/tests/scaffold/test_langsmith_conftest.py +244 -0
  454. package/eval/local/tests/scaffold/test_logging.py +243 -0
  455. package/eval/local/tests/scaffold/test_manifests.py +132 -0
  456. package/eval/local/tests/scaffold/test_profiles.py +866 -0
  457. package/eval/local/tests/scaffold/test_regression_check.py +19 -0
  458. package/eval/local/tests/scaffold/test_report_style_demo_charts.py +45 -0
  459. package/eval/local/tests/scaffold/test_sample_quality.py +211 -0
  460. package/eval/local/tests/scaffold/test_tasks.py +168 -0
  461. package/eval/local/tests/scaffold/test_treatments.py +244 -0
  462. package/eval/local/tests/scaffold/test_utils.py +139 -0
  463. package/eval/local/tests/tasks/test_tasks.py +445 -0
  464. package/eval/local/tests/tasks/test_validation_scripts.py +143 -0
  465. package/eval/local/treatments/comet/comet_full_039.yaml +141 -0
  466. package/eval/local/treatments/comet/comet_full_040_beta.yaml +140 -0
  467. package/eval/local/treatments/common/control.yaml +4 -0
  468. package/eval/pyproject.toml +41 -0
  469. package/eval/report-html-config.json +6 -0
  470. package/eval/scaffold/__init__.py +67 -0
  471. package/eval/scaffold/python/__init__.py +95 -0
  472. package/eval/scaffold/python/attribution.py +43 -0
  473. package/eval/scaffold/python/evidence.py +38 -0
  474. package/eval/scaffold/python/external_data_handler.py +18 -0
  475. package/eval/scaffold/python/generic_llm_judge.py +235 -0
  476. package/eval/scaffold/python/judge_config.py +168 -0
  477. package/eval/scaffold/python/llm_judge.py +191 -0
  478. package/eval/scaffold/python/logging.py +705 -0
  479. package/eval/scaffold/python/manifests.py +129 -0
  480. package/eval/scaffold/python/paper_charts.py +25 -0
  481. package/eval/scaffold/python/pass_at_k.py +107 -0
  482. package/eval/scaffold/python/paths.py +56 -0
  483. package/eval/scaffold/python/profiles.py +127 -0
  484. package/eval/scaffold/python/report_outputs.py +1391 -0
  485. package/eval/scaffold/python/sample_quality.py +339 -0
  486. package/eval/scaffold/python/schema.py +39 -0
  487. package/eval/scaffold/python/skill_parser.py +469 -0
  488. package/eval/scaffold/python/tasks.py +330 -0
  489. package/eval/scaffold/python/treatments.py +271 -0
  490. package/eval/scaffold/python/utils.py +366 -0
  491. package/eval/scaffold/python/validation/__init__.py +21 -0
  492. package/eval/scaffold/python/validation/authoring_rubric.py +326 -0
  493. package/eval/scaffold/python/validation/comet_workflow.py +187 -0
  494. package/eval/scaffold/python/validation/core.py +264 -0
  495. package/eval/scaffold/python/validation/dataset.py +337 -0
  496. package/eval/scaffold/python/validation/docker.py +106 -0
  497. package/eval/scaffold/python/validation/evaluator.py +549 -0
  498. package/eval/scaffold/python/validation/generic_rubric.py +169 -0
  499. package/eval/scaffold/python/validation/rubric.py +740 -0
  500. package/eval/scaffold/python/validation/runner.py +237 -0
  501. package/eval/scaffold/python/validation/scripts.py +58 -0
  502. package/eval/scaffold/python/validation/tracing.py +313 -0
  503. package/eval/scaffold/shell/common.sh +126 -0
  504. package/eval/scaffold/shell/docker.sh +482 -0
  505. package/eval/scaffold/shell/run-claude-loop.sh +181 -0
  506. package/eval/scaffold/shell/setup.sh +259 -0
  507. package/eval/simulator-instruction.md +9 -0
  508. package/package.json +24 -2
@@ -0,0 +1,866 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from scaffold.python.profiles import (
6
+ AUTHORING_SKILL_PROFILE,
7
+ COMET_WORKFLOW_PROFILE,
8
+ GENERIC_PROFILE,
9
+ get_profile,
10
+ list_profiles,
11
+ resolve_profile_name,
12
+ run_profile_rubric,
13
+ )
14
+ from scaffold.python.tasks import load_task
15
+
16
+
17
+ def test_profile_registry_exposes_generic_and_comet_workflow():
18
+ assert list_profiles() == ["authoring-skill", "comet-workflow", "generic"]
19
+
20
+ generic = get_profile(GENERIC_PROFILE)
21
+ comet = get_profile(COMET_WORKFLOW_PROFILE)
22
+ authoring = get_profile(AUTHORING_SKILL_PROFILE)
23
+
24
+ assert generic.name == "generic"
25
+ assert comet.name == "comet-workflow"
26
+ assert authoring.name == "authoring-skill"
27
+ assert "completion" in generic.rubric_dimensions
28
+ assert "main_flow" in comet.rubric_dimensions
29
+ assert "generated_package" in authoring.rubric_dimensions
30
+
31
+
32
+ def test_get_profile_rejects_unknown_names():
33
+ with pytest.raises(KeyError, match="Profile not found: unknown"):
34
+ get_profile("unknown")
35
+
36
+
37
+ def test_resolve_profile_name_prefers_cli_override():
38
+ task = load_task("comet-full-workflow")
39
+
40
+ assert resolve_profile_name(task, override="generic") == "generic"
41
+
42
+
43
+ def test_resolve_profile_name_uses_task_profile_by_default():
44
+ task = load_task("comet-full-workflow")
45
+
46
+ assert resolve_profile_name(task) == "comet-workflow"
47
+
48
+
49
+ def test_comet_profile_requires_comet_skill_invocation(tmp_path: Path):
50
+ outputs = {
51
+ "completion": {"passed": ["median fixed"], "failed": []},
52
+ "events": {
53
+ "skills_invoked": [],
54
+ "num_turns": 1,
55
+ "tool_calls": [],
56
+ "duration_seconds": 5,
57
+ "commands_run": [],
58
+ },
59
+ "interaction": {"mode": "auto_user", "max_turns": 3},
60
+ }
61
+
62
+ passed, failed = run_profile_rubric("comet-workflow", tmp_path, outputs)
63
+
64
+ assert "Required skill not invoked: comet" in failed
65
+ assert any("[RUBRIC] skill_invocation: 0.00" in msg for msg in passed)
66
+
67
+
68
+ def test_comet_profile_requires_nested_and_dependency_skill_invocations(tmp_path: Path):
69
+ outputs = {
70
+ "completion": {"passed": ["median fixed"], "failed": []},
71
+ "events": {
72
+ "skills_invoked": ["comet"],
73
+ "num_turns": 1,
74
+ "tool_calls": [],
75
+ "duration_seconds": 5,
76
+ "commands_run": [],
77
+ },
78
+ "interaction": {"mode": "auto_user", "max_turns": 3},
79
+ }
80
+
81
+ passed, failed = run_profile_rubric("comet-workflow", tmp_path, outputs)
82
+
83
+ assert "Required nested Comet stage skill not invoked" in failed
84
+ assert "Required OpenSpec dependency skill not invoked" in failed
85
+ assert "Required Superpowers dependency skill not invoked" in failed
86
+ assert any("[RUBRIC] skill_invocation: 0.20" in msg for msg in passed)
87
+ assert any("comet_stage=missing" in msg for msg in passed)
88
+
89
+
90
+ def test_comet_profile_scores_observed_nested_and_dependency_skill_invocations(
91
+ tmp_path: Path,
92
+ ):
93
+ outputs = {
94
+ "completion": {"passed": ["median fixed"], "failed": []},
95
+ "events": {
96
+ "skills_invoked": [
97
+ "comet",
98
+ "comet-hotfix",
99
+ "openspec-new-change",
100
+ "comet-verify",
101
+ "verification-before-completion",
102
+ ],
103
+ "num_turns": 1,
104
+ "tool_calls": [],
105
+ "duration_seconds": 5,
106
+ "commands_run": [],
107
+ },
108
+ "interaction": {"mode": "auto_user", "max_turns": 3},
109
+ }
110
+
111
+ passed, failed = run_profile_rubric("comet-workflow", tmp_path, outputs)
112
+
113
+ assert "Required nested Comet stage skill not invoked" not in failed
114
+ assert "Required OpenSpec dependency skill not invoked" not in failed
115
+ assert "Required Superpowers dependency skill not invoked" not in failed
116
+ assert any("[RUBRIC] skill_invocation: 1.00" in msg for msg in passed)
117
+ assert any("comet_stage=comet-hotfix, comet-verify" in msg for msg in passed)
118
+ assert any("openspec=openspec-new-change" in msg for msg in passed)
119
+ assert any("superpowers=verification-before-completion" in msg for msg in passed)
120
+
121
+
122
+ def test_comet_profile_scores_hotfix_with_hotfix_specific_rubric(tmp_path: Path):
123
+ change_dir = tmp_path / "openspec" / "changes" / "archive" / "2026-07-01-fix-median"
124
+ change_dir.mkdir(parents=True)
125
+ comet_dir = change_dir / ".comet"
126
+ comet_dir.mkdir()
127
+ (comet_dir / "checkpoint.json").write_text(
128
+ '{"runId":"r1","contextHash":null,"artifactsHash":"abc","createdAt":"2026-07-01"}',
129
+ encoding="utf-8",
130
+ )
131
+ (comet_dir / "run-state.json").write_text(
132
+ '{"status":"completed","currentStep":"completed"}',
133
+ encoding="utf-8",
134
+ )
135
+ (comet_dir / "state-events.jsonl").write_text(
136
+ '{"to":{"workflow":"hotfix","phase":"archive","archived":true}}\n',
137
+ encoding="utf-8",
138
+ )
139
+ (comet_dir / "trajectory.jsonl").write_text("{}\n", encoding="utf-8")
140
+ (change_dir / "proposal.md").write_text(
141
+ "\n".join(f"line {i}" for i in range(12)),
142
+ encoding="utf-8",
143
+ )
144
+ (change_dir / "design.md").write_text("Focused hotfix design.", encoding="utf-8")
145
+ (change_dir / "tasks.md").write_text(
146
+ "- [x] Reproduce median bug\n- [x] Fix even median\n",
147
+ encoding="utf-8",
148
+ )
149
+ (tmp_path / "test_stats.py").write_text("def test_even():\n assert True\n", encoding="utf-8")
150
+
151
+ outputs = {
152
+ "completion": {"passed": ["median fixed"], "failed": []},
153
+ "events": {
154
+ "skills_invoked": [
155
+ "comet",
156
+ "comet-hotfix",
157
+ "openspec-new-change",
158
+ "comet-verify",
159
+ "verification-before-completion",
160
+ "comet-archive",
161
+ ],
162
+ "commands_run": [
163
+ "node comet-state.mjs set fix-median verify_mode light",
164
+ "node comet-state.mjs transition fix-median verify-pass",
165
+ ],
166
+ "files_created": [
167
+ "openspec/changes/archive/2026-07-01-fix-median/proposal.md",
168
+ "openspec/changes/archive/2026-07-01-fix-median/tasks.md",
169
+ "openspec/changes/archive/2026-07-01-fix-median/.comet/state-events.jsonl",
170
+ "openspec/changes/archive/2026-07-01-fix-median/verification.md",
171
+ ],
172
+ "files_modified": [],
173
+ "num_turns": 1,
174
+ "tool_calls": [],
175
+ "duration_seconds": 5,
176
+ },
177
+ "interaction": {"mode": "auto_user", "max_turns": 3},
178
+ }
179
+
180
+ passed, _ = run_profile_rubric("comet-workflow", tmp_path, outputs)
181
+
182
+ assert any("[RUBRIC] main_flow: 1.00 - workflow=hotfix" in msg for msg in passed)
183
+ assert any("[RUBRIC] decision_point_compliance: 1.00" in msg for msg in passed)
184
+ assert any("no hotfix decision mutations observed" in msg for msg in passed)
185
+ assert any("[RUBRIC] artifact_quality: 1.00" in msg for msg in passed)
186
+ assert any("workflow=hotfix" in msg and "tasks=2 boxes" in msg for msg in passed)
187
+ assert any("[RUBRIC] recovery_resilience: 1.00" in msg for msg in passed)
188
+
189
+
190
+ def test_comet_profile_scores_tweak_with_tweak_specific_rubric(tmp_path: Path):
191
+ change_dir = tmp_path / "openspec" / "changes" / "archive" / "2026-07-01-adjust-copy"
192
+ change_dir.mkdir(parents=True)
193
+ (change_dir / ".comet").mkdir()
194
+ (change_dir / ".comet.yaml").write_text(
195
+ "classic_profile: tweak\nphase: archive\nverify_result: pass\n",
196
+ encoding="utf-8",
197
+ )
198
+ (change_dir / "proposal.md").write_text(
199
+ "\n".join(f"line {i}" for i in range(12)),
200
+ encoding="utf-8",
201
+ )
202
+ (change_dir / "tasks.md").write_text("- [x] Apply copy tweak\n", encoding="utf-8")
203
+ (tmp_path / "test_copy.py").write_text("def test_copy():\n assert True\n", encoding="utf-8")
204
+
205
+ outputs = {
206
+ "completion": {"passed": ["copy adjusted"], "failed": []},
207
+ "events": {
208
+ "skills_invoked": [
209
+ "comet",
210
+ "comet-tweak",
211
+ "openspec-new-change",
212
+ "openspec-apply-change",
213
+ "comet-verify",
214
+ "verification-before-completion",
215
+ "comet-archive",
216
+ ],
217
+ "commands_run": ["node comet-state.mjs set adjust-copy verify_mode light"],
218
+ "files_created": [
219
+ "openspec/changes/archive/2026-07-01-adjust-copy/proposal.md",
220
+ "openspec/changes/archive/2026-07-01-adjust-copy/tasks.md",
221
+ "openspec/changes/archive/2026-07-01-adjust-copy/.comet/state-events.jsonl",
222
+ "openspec/changes/archive/2026-07-01-adjust-copy/verification.md",
223
+ ],
224
+ "files_modified": [],
225
+ "num_turns": 1,
226
+ "tool_calls": [],
227
+ "duration_seconds": 5,
228
+ },
229
+ "interaction": {"mode": "auto_user", "max_turns": 3},
230
+ }
231
+
232
+ passed, _ = run_profile_rubric("comet-workflow", tmp_path, outputs)
233
+
234
+ assert any("[RUBRIC] main_flow: 1.00 - workflow=tweak" in msg for msg in passed)
235
+ assert any("[RUBRIC] artifact_quality: 1.00" in msg for msg in passed)
236
+ assert any("workflow=tweak" in msg and "tasks=1 boxes" in msg for msg in passed)
237
+
238
+
239
+ def test_comet_profile_scores_full_with_full_specific_rubric(tmp_path: Path):
240
+ change_dir = tmp_path / "openspec" / "changes" / "archive" / "2026-07-01-add-api"
241
+ change_dir.mkdir(parents=True)
242
+ (change_dir / ".comet" / "handoff").mkdir(parents=True)
243
+ (change_dir / ".comet.yaml").write_text(
244
+ "workflow: full\nphase: archive\nverify_result: pass\n",
245
+ encoding="utf-8",
246
+ )
247
+ (change_dir / ".comet" / "handoff" / "design-context.md").write_text(
248
+ "context",
249
+ encoding="utf-8",
250
+ )
251
+ (change_dir / "proposal.md").write_text(
252
+ "\n".join(f"line {i}" for i in range(12)),
253
+ encoding="utf-8",
254
+ )
255
+ (change_dir / "design.md").write_text(
256
+ "Tradeoff and alternative option with risk to consider.",
257
+ encoding="utf-8",
258
+ )
259
+ (change_dir / "tasks.md").write_text(
260
+ "- [x] Design API\n- [x] Implement API\n- [x] Verify API\n",
261
+ encoding="utf-8",
262
+ )
263
+ (tmp_path / "test_api.py").write_text("def test_api():\n assert True\n", encoding="utf-8")
264
+
265
+ outputs = {
266
+ "completion": {"passed": ["api added"], "failed": []},
267
+ "events": {
268
+ "skills_invoked": [
269
+ "comet",
270
+ "comet-open",
271
+ "openspec-new-change",
272
+ "comet-design",
273
+ "brainstorming",
274
+ "comet-build",
275
+ "writing-plans",
276
+ "comet-verify",
277
+ "verification-before-completion",
278
+ "comet-archive",
279
+ ],
280
+ "commands_run": [
281
+ "node comet-state.mjs set add-api build_mode executing-plans",
282
+ "node comet-state.mjs transition add-api verify-pass",
283
+ ],
284
+ "tool_calls": [{"tool": "AskUserQuestion", "input": {}}],
285
+ "files_created": [
286
+ "openspec/changes/archive/2026-07-01-add-api/proposal.md",
287
+ "openspec/changes/archive/2026-07-01-add-api/tasks.md",
288
+ "docs/superpowers/specs/add-api.md",
289
+ "docs/superpowers/plans/add-api.md",
290
+ "openspec/changes/archive/2026-07-01-add-api/verification.md",
291
+ ],
292
+ "files_modified": [],
293
+ "num_turns": 1,
294
+ "duration_seconds": 5,
295
+ },
296
+ "interaction": {"mode": "auto_user", "max_turns": 3},
297
+ }
298
+
299
+ passed, _ = run_profile_rubric("comet-workflow", tmp_path, outputs)
300
+
301
+ assert any("[RUBRIC] main_flow: 1.00 - workflow=full" in msg for msg in passed)
302
+ assert any("[RUBRIC] artifact_quality: 1.00" in msg for msg in passed)
303
+ assert any("workflow=full" in msg and "design=deep" in msg for msg in passed)
304
+
305
+
306
+ def test_generic_profile_scores_completion_skill_artifact_and_efficiency(tmp_path: Path):
307
+ (tmp_path / "result.md").write_text("done")
308
+ outputs = {
309
+ "completion": {"passed": ["validator ok"], "failed": []},
310
+ "events": {
311
+ "skills_invoked": ["target-skill"],
312
+ "num_turns": 3,
313
+ "tool_calls": [{"tool": "Read", "input": {}}],
314
+ "duration_seconds": 12,
315
+ "commands_run": [],
316
+ },
317
+ "required_skills": ["target-skill"],
318
+ "expected_artifacts": ["result.md"],
319
+ "interaction": {"mode": "none"},
320
+ }
321
+
322
+ passed, failed = run_profile_rubric("generic", tmp_path, outputs)
323
+
324
+ assert failed == []
325
+ assert any("[RUBRIC] completion: 1.00" in msg for msg in passed)
326
+ assert any("[RUBRIC] skill_invocation: 1.00" in msg for msg in passed)
327
+ assert any("[RUBRIC] artifact_presence: 1.00" in msg for msg in passed)
328
+ assert any("[RUBRIC] weighted_score:" in msg for msg in passed)
329
+
330
+
331
+ def test_generic_profile_can_fail_required_skill_invocation(tmp_path: Path):
332
+ outputs = {
333
+ "completion": {"passed": [], "failed": ["validator failed"]},
334
+ "events": {"skills_invoked": [], "commands_run": []},
335
+ "required_skills": ["target-skill"],
336
+ "expected_artifacts": [],
337
+ "require_skill_invocation": True,
338
+ "interaction": {"mode": "none"},
339
+ }
340
+
341
+ passed, failed = run_profile_rubric("generic", tmp_path, outputs)
342
+
343
+ assert any("Required skill not invoked: target-skill" in msg for msg in failed)
344
+ assert any("[RUBRIC] skill_invocation: 0.00" in msg for msg in passed)
345
+
346
+
347
+ def test_authoring_profile_scores_generated_package_and_engine_contract(tmp_path: Path):
348
+ package = tmp_path / "authoring-skill"
349
+ (package / "reference").mkdir(parents=True)
350
+ (package / "comet").mkdir(parents=True)
351
+ node_skill = tmp_path / "authoring-skill-open"
352
+ node_skill.mkdir()
353
+ (package / "SKILL.md").write_text(
354
+ "# Demo\n\n## Workflow Nodes\n- `authoring-skill-open`\n\n## 用户停顿点\n- Confirm before exit.\n\n## 自动推进与恢复\n- scripts/workflow-guard.mjs\n\n## 参考\n- `reference/workflow-protocol.json`\n- `reference/resolved-skills.json`\n",
355
+ encoding="utf-8",
356
+ )
357
+ (node_skill / "SKILL.md").write_text(
358
+ "# Node\n\n## Node Goal\n- open\n",
359
+ encoding="utf-8",
360
+ )
361
+ (package / "reference" / "resolved-skills.json").write_text(
362
+ '{"sourceSummaries":[{"name":"demo-source"}]}',
363
+ encoding="utf-8",
364
+ )
365
+ (package / "reference" / "workflow-protocol.json").write_text(
366
+ '{"name":"authoring-skill","nodes":[{"id":"open","disabled":false}]}',
367
+ encoding="utf-8",
368
+ )
369
+ (package / "reference" / "authoring-lanes.json").write_text(
370
+ '{"lanes":[{"lane":"skill-core"},{"lane":"script-contract"},{"lane":"reference"},{"lane":"pause-points"},{"lane":"eval"},{"lane":"skill-review"}],"review":{"passed":true,"blockingFindings":[]}}',
371
+ encoding="utf-8",
372
+ )
373
+ (package / "reference" / "skill-review.md").write_text(
374
+ "# Skill Review\n\nStatus: Review passed\n",
375
+ encoding="utf-8",
376
+ )
377
+ for name in ("skill.yaml", "guardrails.yaml", "checks.yaml"):
378
+ (package / "comet" / name).write_text("name: demo\n", encoding="utf-8")
379
+ (package / "comet" / "eval.yaml").write_text(
380
+ "evaluation:\n"
381
+ " recommendedTasks:\n"
382
+ " - workflow-route-conformance\n"
383
+ " generatedNodeSkills:\n"
384
+ " - authoring-skill-open\n"
385
+ " routeConformance:\n"
386
+ " task: workflow-route-conformance\n"
387
+ " expectedNodeOrder:\n"
388
+ " - open\n",
389
+ encoding="utf-8",
390
+ )
391
+
392
+ outputs = {
393
+ "completion": {"passed": ["validator ok"], "failed": []},
394
+ "events": {
395
+ "skills_invoked": ["comet-any"],
396
+ "num_turns": 4,
397
+ "tool_calls": [{"tool": "Read", "input": {}}],
398
+ "duration_seconds": 20,
399
+ "commands_run": [],
400
+ },
401
+ "required_skills": ["comet-any"],
402
+ "expected_artifacts": [],
403
+ "interaction": {"mode": "auto_user", "max_turns": 8},
404
+ "skill_package_path": str(package),
405
+ "generated_node_skills": ["authoring-skill-open"],
406
+ "route_conformance_expected_node_order": ["open"],
407
+ }
408
+
409
+ passed, failed = run_profile_rubric("authoring-skill", tmp_path, outputs)
410
+
411
+ assert failed == []
412
+ assert any("[RUBRIC] generated_package: 1.00" in msg for msg in passed)
413
+ assert any("[RUBRIC] resolved_skill_evidence: 1.00" in msg for msg in passed)
414
+ assert any("[RUBRIC] engine_contract: 1.00" in msg for msg in passed)
415
+ assert any("[RUBRIC] workflow_route_conformance: 1.00" in msg for msg in passed)
416
+ assert any("[RUBRIC] authoring_lanes: 1.00" in msg for msg in passed)
417
+ assert any("[RUBRIC] review_gate: 1.00" in msg for msg in passed)
418
+ assert any("[RUBRIC] weighted_score:" in msg for msg in passed)
419
+
420
+
421
+ def test_authoring_profile_allows_lightweight_package_without_engine_files(tmp_path: Path):
422
+ package = tmp_path / "authoring-skill"
423
+ (package / "reference").mkdir(parents=True)
424
+ node_skill = tmp_path / "authoring-skill-open"
425
+ node_skill.mkdir()
426
+ (package / "SKILL.md").write_text(
427
+ "# Demo\n\n## Workflow Nodes\n- `authoring-skill-open`\n\n## 用户停顿点\n- Confirm before exit.\n\n## 自动推进与恢复\n- scripts/workflow-guard.mjs\n\n## 参考\n- `reference/workflow-protocol.json`\n- `reference/resolved-skills.json`\n",
428
+ encoding="utf-8",
429
+ )
430
+ (node_skill / "SKILL.md").write_text(
431
+ "# Node\n\n## Node Goal\n- open\n",
432
+ encoding="utf-8",
433
+ )
434
+ (package / "reference" / "resolved-skills.json").write_text(
435
+ '{"sourceSummaries":[{"name":"demo-source"}]}',
436
+ encoding="utf-8",
437
+ )
438
+ (package / "reference" / "workflow-protocol.json").write_text(
439
+ '{"name":"authoring-skill","nodes":[{"id":"open","disabled":false}]}',
440
+ encoding="utf-8",
441
+ )
442
+ (package / "reference" / "authoring-lanes.json").write_text(
443
+ '{"lanes":[{"lane":"skill-core"},{"lane":"script-contract"},{"lane":"reference"},{"lane":"pause-points"},{"lane":"eval"},{"lane":"skill-review"}],"review":{"passed":true,"blockingFindings":[]}}',
444
+ encoding="utf-8",
445
+ )
446
+ (package / "reference" / "skill-review.md").write_text(
447
+ "# Skill Review\n\nStatus: Review passed\n",
448
+ encoding="utf-8",
449
+ )
450
+
451
+ outputs = {
452
+ "completion": {"passed": ["validator ok"], "failed": []},
453
+ "events": {
454
+ "skills_invoked": ["comet-any"],
455
+ "num_turns": 2,
456
+ "tool_calls": [],
457
+ "duration_seconds": 10,
458
+ "commands_run": [],
459
+ },
460
+ "required_skills": ["comet-any"],
461
+ "expected_artifacts": [],
462
+ "interaction": {"mode": "auto_user", "max_turns": 8},
463
+ "skill_package_path": str(package),
464
+ "generated_node_skills": ["authoring-skill-open"],
465
+ "route_conformance_expected_node_order": ["open"],
466
+ }
467
+
468
+ passed, failed = run_profile_rubric("authoring-skill", tmp_path, outputs)
469
+
470
+ assert failed == []
471
+ assert any("[RUBRIC] engine_contract: 1.00 - Engine disabled for lightweight package" in msg for msg in passed)
472
+
473
+
474
+ # ---------------------------------------------------------------------------
475
+ # N/A dimension scoring tests
476
+ # ---------------------------------------------------------------------------
477
+
478
+
479
+ def test_generic_rubric_na_dimensions_emit_na_format(tmp_path: Path):
480
+ """When required_skills and expected_artifacts are empty, those dimensions
481
+ should emit N/A instead of a numeric score."""
482
+ outputs = {
483
+ "completion": {"passed": ["ok"], "failed": []},
484
+ "events": {
485
+ "skills_invoked": [],
486
+ "num_turns": 1,
487
+ "tool_calls": [],
488
+ "duration_seconds": 5,
489
+ "commands_run": [],
490
+ },
491
+ "required_skills": [],
492
+ "expected_artifacts": [],
493
+ "interaction": {"mode": "none"},
494
+ }
495
+
496
+ passed, failed = run_profile_rubric("generic", tmp_path, outputs)
497
+
498
+ assert any("[RUBRIC] skill_invocation: N/A -" in msg for msg in passed)
499
+ assert any("[RUBRIC] artifact_presence: N/A -" in msg for msg in passed)
500
+ # Numeric dimensions should still have scores.
501
+ assert any("[RUBRIC] completion: 1.00" in msg for msg in passed)
502
+ assert any("[RUBRIC] efficiency:" in msg for msg in passed)
503
+ assert not any("skill_invocation: 0.50" in msg for msg in passed)
504
+ assert not any("artifact_presence: 0.50" in msg for msg in passed)
505
+
506
+
507
+ def test_generic_rubric_skips_na_dimensions_from_weighted_score(tmp_path: Path):
508
+ """The weighted score should only average over applicable dimensions,
509
+ not dilute with 0.5 for unconfigured ones."""
510
+ outputs_all_na = {
511
+ "completion": {"passed": ["ok"], "failed": []},
512
+ "events": {
513
+ "skills_invoked": [],
514
+ "num_turns": 1,
515
+ "tool_calls": [],
516
+ "duration_seconds": 5,
517
+ "commands_run": [],
518
+ },
519
+ "required_skills": [],
520
+ "expected_artifacts": [],
521
+ "interaction": {"mode": "none"},
522
+ }
523
+ outputs_with_skills = {
524
+ "completion": {"passed": ["ok"], "failed": []},
525
+ "events": {
526
+ "skills_invoked": ["target-skill"],
527
+ "num_turns": 1,
528
+ "tool_calls": [],
529
+ "duration_seconds": 5,
530
+ "commands_run": [],
531
+ },
532
+ "required_skills": ["target-skill"],
533
+ "expected_artifacts": ["result.md"],
534
+ "interaction": {"mode": "none"},
535
+ }
536
+ (tmp_path / "result.md").write_text("done")
537
+
538
+ passed_na, _ = run_profile_rubric("generic", tmp_path, outputs_all_na)
539
+ passed_full, _ = run_profile_rubric("generic", tmp_path, outputs_with_skills)
540
+
541
+ def _extract_weighted(passed_list: list[str]) -> float:
542
+ for msg in passed_list:
543
+ if "[RUBRIC] weighted_score:" in msg:
544
+ return float(msg.split("weighted_score:")[1].strip())
545
+ return 0.0
546
+
547
+ score_na = _extract_weighted(passed_na)
548
+ score_full = _extract_weighted(passed_full)
549
+
550
+ # Both should produce valid scores between 0 and 1.
551
+ assert 0.0 <= score_na <= 1.0
552
+ assert 0.0 <= score_full <= 1.0
553
+ # When all checks pass and no N/A dimensions, score should be 1.0.
554
+ assert score_full == 1.00
555
+
556
+
557
+ def test_generic_rubric_with_required_skills_scores_numeric(tmp_path: Path):
558
+ """When required_skills is configured, skill_invocation should be numeric."""
559
+ outputs = {
560
+ "completion": {"passed": ["ok"], "failed": []},
561
+ "events": {
562
+ "skills_invoked": ["target-skill"],
563
+ "num_turns": 1,
564
+ "tool_calls": [],
565
+ "duration_seconds": 5,
566
+ "commands_run": [],
567
+ },
568
+ "required_skills": ["target-skill"],
569
+ "expected_artifacts": [],
570
+ "interaction": {"mode": "none"},
571
+ }
572
+
573
+ passed, _ = run_profile_rubric("generic", tmp_path, outputs)
574
+
575
+ assert any("[RUBRIC] skill_invocation: 1.00" in msg for msg in passed)
576
+ assert any("[RUBRIC] artifact_presence: N/A -" in msg for msg in passed)
577
+
578
+
579
+ def test_comet_control_marks_workflow_dimensions_not_applicable(tmp_path: Path):
580
+ """CONTROL should score business completion without requiring Comet Skill use."""
581
+ outputs = {
582
+ "treatment_name": "CONTROL",
583
+ "business_completion": {"passed": ["sentence_feature"], "failed": []},
584
+ "workflow_completion": {"passed": [], "failed": ["tests_exist: No test files found"]},
585
+ "events": {
586
+ "skills_invoked": [],
587
+ "num_turns": 2,
588
+ "tool_calls": [],
589
+ "duration_seconds": 5,
590
+ "commands_run": [],
591
+ },
592
+ }
593
+
594
+ passed, failed = run_profile_rubric("comet-workflow", tmp_path, outputs)
595
+
596
+ assert failed == []
597
+ assert any("[RUBRIC] main_flow: N/A -" in msg for msg in passed)
598
+ assert any("[RUBRIC] gate_guard: N/A -" in msg for msg in passed)
599
+ assert any("[RUBRIC] skill_invocation: N/A -" in msg for msg in passed)
600
+ assert any("[RUBRIC] spec_drift: N/A -" in msg for msg in passed)
601
+ assert any("[RUBRIC] business_completion: 1.00" in msg for msg in passed)
602
+ assert any("[RUBRIC] workflow_completion: N/A -" in msg for msg in passed)
603
+ assert any("[RUBRIC] efficiency:" in msg for msg in passed)
604
+ assert any("[RUBRIC] weighted_score: 1.00" in msg for msg in passed)
605
+
606
+
607
+ def test_comet_profile_splits_business_and_workflow_completion(tmp_path: Path):
608
+ outputs = {
609
+ "business_completion": {
610
+ "passed": ["sentence_feature"],
611
+ "failed": ["business_rule: failed"],
612
+ },
613
+ "workflow_completion": {
614
+ "passed": ["openspec_artifacts"],
615
+ "failed": ["tests_exist: No test files found"],
616
+ },
617
+ "events": {
618
+ "skills_invoked": [
619
+ "comet",
620
+ "comet-hotfix",
621
+ "openspec-new-change",
622
+ "verification-before-completion",
623
+ ],
624
+ "commands_run": [],
625
+ "files_created": [],
626
+ "files_modified": [],
627
+ "num_turns": 1,
628
+ "tool_calls": [],
629
+ "duration_seconds": 5,
630
+ },
631
+ }
632
+
633
+ passed, _ = run_profile_rubric("comet-workflow", tmp_path, outputs)
634
+
635
+ assert any("[RUBRIC] business_completion: 0.50" in msg for msg in passed)
636
+ assert any("[RUBRIC] workflow_completion: 0.50" in msg for msg in passed)
637
+ assert not any("[RUBRIC] completion:" in msg for msg in passed)
638
+
639
+
640
+ # ---------------------------------------------------------------------------
641
+ # LLM judge prompt tests
642
+ # ---------------------------------------------------------------------------
643
+
644
+
645
+ def test_generic_llm_judge_prompt_includes_custom_criteria(tmp_path: Path):
646
+ """The judge prompt should include rubric_criteria from task config."""
647
+ from scaffold.python.generic_llm_judge import _build_generic_judge_prompt
648
+
649
+ (tmp_path / "output.txt").write_text("hello world")
650
+ outputs = {
651
+ "completion": {"passed": ["ok"], "failed": []},
652
+ "rubric_criteria": [
653
+ "The function handles edge cases",
654
+ "Error messages are user-friendly",
655
+ ],
656
+ }
657
+
658
+ prompt = _build_generic_judge_prompt(tmp_path, outputs)
659
+
660
+ assert "The function handles edge cases" in prompt
661
+ assert "Error messages are user-friendly" in prompt
662
+ assert "custom_0" in prompt
663
+ assert "custom_1" in prompt
664
+ assert "task_completion" in prompt
665
+ assert "output_quality" in prompt
666
+ assert "instruction_adherence" in prompt
667
+
668
+
669
+ def test_generic_llm_judge_prompt_without_custom_criteria(tmp_path: Path):
670
+ """Without custom criteria, prompt should have exactly 3 standard dimensions."""
671
+ from scaffold.python.generic_llm_judge import _build_generic_judge_prompt
672
+
673
+ (tmp_path / "output.txt").write_text("hello world")
674
+ outputs = {"completion": {"passed": ["ok"], "failed": []}}
675
+
676
+ prompt = _build_generic_judge_prompt(tmp_path, outputs)
677
+
678
+ assert "EXACTLY 3 lines" in prompt
679
+ assert "custom_0" not in prompt
680
+ assert "task_completion" in prompt
681
+
682
+
683
+ def test_generic_llm_judge_collects_workspace_files(tmp_path: Path):
684
+ """The artifact collector should find non-hidden files."""
685
+ from scaffold.python.generic_llm_judge import _collect_workspace_artifacts
686
+
687
+ (tmp_path / "result.md").write_text("# Result\nDone")
688
+ (tmp_path / "src").mkdir()
689
+ (tmp_path / "src" / "main.py").write_text("print('hello')")
690
+ (tmp_path / ".git").mkdir()
691
+ (tmp_path / ".git" / "config").write_text("ignored")
692
+
693
+ artifacts = _collect_workspace_artifacts(tmp_path)
694
+
695
+ assert "result.md" in artifacts
696
+ assert str(Path("src") / "main.py") in artifacts
697
+ assert ".git" not in artifacts
698
+
699
+
700
+ def test_judge_env_requires_explicit_judge_model(monkeypatch):
701
+ """LLM judge must not silently reuse the subject model."""
702
+ from scaffold.python.judge_config import build_judge_invocation
703
+
704
+ monkeypatch.setenv("ANTHROPIC_MODEL", "subject-model")
705
+ monkeypatch.delenv("BENCH_JUDGE_MODEL", raising=False)
706
+
707
+ try:
708
+ build_judge_invocation()
709
+ except ValueError as exc:
710
+ assert "BENCH_JUDGE_MODEL" in str(exc)
711
+ else:
712
+ raise AssertionError("expected missing BENCH_JUDGE_MODEL to fail")
713
+
714
+
715
+ def test_judge_env_maps_independent_provider(monkeypatch):
716
+ """Judge subprocess env should use BENCH_JUDGE_* instead of subject ANTHROPIC_*."""
717
+ from scaffold.python.judge_config import build_judge_invocation
718
+
719
+ monkeypatch.setenv("ANTHROPIC_MODEL", "subject-model")
720
+ monkeypatch.setenv("ANTHROPIC_BASE_URL", "https://subject.example")
721
+ monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "subject-token")
722
+ monkeypatch.setenv("ANTHROPIC_API_KEY", "subject-key")
723
+ monkeypatch.setenv("BENCH_JUDGE_MODEL", "judge-model")
724
+ monkeypatch.setenv("BENCH_JUDGE_BASE_URL", "https://judge.example")
725
+ monkeypatch.setenv("BENCH_JUDGE_AUTH_TOKEN", "judge-token")
726
+
727
+ invocation = build_judge_invocation()
728
+
729
+ assert invocation.model_flag == ["--model", "judge-model"]
730
+ assert invocation.env["ANTHROPIC_MODEL"] == "judge-model"
731
+ assert invocation.env["ANTHROPIC_BASE_URL"] == "https://judge.example"
732
+ assert invocation.env["ANTHROPIC_AUTH_TOKEN"] == "judge-token"
733
+ assert "ANTHROPIC_API_KEY" not in invocation.env
734
+ assert "subject-model" not in invocation.env.values()
735
+ assert "subject-token" not in invocation.env.values()
736
+
737
+
738
+ def test_judge_provider_uses_direct_http_when_base_url_is_configured(monkeypatch):
739
+ """Dedicated judge providers should not require Claude CLI compatibility."""
740
+ import json
741
+ from unittest.mock import patch
742
+
743
+ from scaffold.python.judge_config import run_judge_prompt
744
+
745
+ monkeypatch.setenv("BENCH_JUDGE_MODEL", "judge-model")
746
+ monkeypatch.setenv("BENCH_JUDGE_BASE_URL", "https://judge.example/api/anthropic")
747
+ monkeypatch.setenv("BENCH_JUDGE_AUTH_TOKEN", "judge-token")
748
+
749
+ class FakeResponse:
750
+ def __enter__(self):
751
+ return self
752
+
753
+ def __exit__(self, *_args):
754
+ return False
755
+
756
+ def read(self):
757
+ return json.dumps(
758
+ {
759
+ "content": [
760
+ {
761
+ "type": "text",
762
+ "text": "[RUBRIC-JUDGE] task_completion: 1.00 - ok",
763
+ }
764
+ ]
765
+ }
766
+ ).encode("utf-8")
767
+
768
+ with patch("urllib.request.urlopen", return_value=FakeResponse()) as urlopen:
769
+ output = run_judge_prompt("score this")
770
+
771
+ request = urlopen.call_args.args[0]
772
+ assert request.full_url == "https://judge.example/api/anthropic/v1/messages"
773
+ assert request.headers["Authorization"] == "Bearer judge-token"
774
+ assert json.loads(request.data.decode("utf-8"))["model"] == "judge-model"
775
+ assert output == "[RUBRIC-JUDGE] task_completion: 1.00 - ok"
776
+
777
+
778
+ def test_comet_llm_judge_reports_skipped_without_success_status(monkeypatch, tmp_path: Path):
779
+ """Missing judge config should be visible and not reported as successful."""
780
+ from scaffold.python.llm_judge import judge_messages
781
+
782
+ change = tmp_path / "openspec" / "changes" / "demo"
783
+ change.mkdir(parents=True)
784
+ (change / "proposal.md").write_text("proposal")
785
+ monkeypatch.setenv("BENCH_LLM_JUDGE", "1")
786
+ monkeypatch.setenv("ANTHROPIC_MODEL", "subject-model")
787
+ monkeypatch.delenv("BENCH_JUDGE_MODEL", raising=False)
788
+
789
+ messages = judge_messages(tmp_path)
790
+
791
+ assert messages == [
792
+ "[RUBRIC-JUDGE] status: skipped - BENCH_JUDGE_MODEL is required when BENCH_LLM_JUDGE=1"
793
+ ]
794
+ assert not any("enabled_and_successful" in msg for msg in messages)
795
+
796
+
797
+ def test_comet_profile_does_not_mark_skipped_judge_successful(monkeypatch, tmp_path: Path):
798
+ """Profile rubric should not add success status when judge config is missing."""
799
+ from scaffold.python.profiles import run_profile_rubric
800
+
801
+ change = tmp_path / "openspec" / "changes" / "demo"
802
+ change.mkdir(parents=True)
803
+ (change / "proposal.md").write_text("proposal")
804
+ monkeypatch.setenv("BENCH_LLM_JUDGE", "1")
805
+ monkeypatch.setenv("ANTHROPIC_MODEL", "subject-model")
806
+ monkeypatch.delenv("BENCH_JUDGE_MODEL", raising=False)
807
+
808
+ passed, _ = run_profile_rubric(
809
+ "comet-workflow",
810
+ tmp_path,
811
+ {"completion": {"passed": ["ok"], "failed": []}},
812
+ )
813
+
814
+ assert any("[RUBRIC-JUDGE] status: skipped" in msg for msg in passed)
815
+ assert not any("[RUBRIC-JUDGE] status: enabled_and_successful" in msg for msg in passed)
816
+
817
+
818
+ def test_generic_llm_judge_parses_output(tmp_path: Path):
819
+ """judge_generic_artifacts should parse [RUBRIC-JUDGE] lines correctly."""
820
+ from unittest.mock import patch
821
+ from scaffold.python.generic_llm_judge import judge_generic_artifacts
822
+
823
+ mock_output = (
824
+ "[RUBRIC-JUDGE] task_completion: 0.80 - output present and mostly correct\n"
825
+ "[RUBRIC-JUDGE] output_quality: 0.90 - well-structured code\n"
826
+ "[RUBRIC-JUDGE] instruction_adherence: 1.00 - all constraints followed\n"
827
+ )
828
+ outputs = {"completion": {"passed": [], "failed": []}}
829
+
830
+ with patch(
831
+ "scaffold.python.generic_llm_judge._run_judge",
832
+ return_value=mock_output,
833
+ ):
834
+ scores = judge_generic_artifacts(tmp_path, outputs)
835
+
836
+ assert scores["task_completion"] == (0.80, "output present and mostly correct")
837
+ assert scores["output_quality"] == (0.90, "well-structured code")
838
+ assert scores["instruction_adherence"] == (1.00, "all constraints followed")
839
+
840
+
841
+ def test_generic_llm_judge_parses_custom_dimensions(tmp_path: Path):
842
+ """Custom rubric criteria should produce custom_N dimensions in output."""
843
+ from unittest.mock import patch
844
+ from scaffold.python.generic_llm_judge import judge_generic_artifacts
845
+
846
+ mock_output = (
847
+ "[RUBRIC-JUDGE] task_completion: 1.00 - done\n"
848
+ "[RUBRIC-JUDGE] output_quality: 0.80 - good\n"
849
+ "[RUBRIC-JUDGE] instruction_adherence: 1.00 - ok\n"
850
+ "[RUBRIC-JUDGE] custom_0: 0.90 - handles edge cases well\n"
851
+ "[RUBRIC-JUDGE] custom_1: 0.70 - error messages could improve\n"
852
+ )
853
+ outputs = {
854
+ "completion": {"passed": [], "failed": []},
855
+ "rubric_criteria": ["edge cases", "error messages"],
856
+ }
857
+
858
+ with patch(
859
+ "scaffold.python.generic_llm_judge._run_judge",
860
+ return_value=mock_output,
861
+ ):
862
+ scores = judge_generic_artifacts(tmp_path, outputs)
863
+
864
+ assert "custom_0" in scores
865
+ assert "custom_1" in scores
866
+ assert scores["custom_0"] == (0.90, "handles edge cases well")