@rpamis/comet 0.4.0-beta.2 → 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 (509) hide show
  1. package/README.md +27 -18
  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 +6 -4
  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 +1664 -512
  11. package/assets/skills/comet-archive/SKILL.md +24 -11
  12. package/assets/skills/comet-build/SKILL.md +40 -31
  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 -4
  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 +40 -31
  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 +37 -3
  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 +1 -0
  40. package/dist/app/commands/doctor.d.ts.map +1 -1
  41. package/dist/app/commands/doctor.js +100 -35
  42. package/dist/app/commands/doctor.js.map +1 -1
  43. package/dist/app/commands/eval.d.ts.map +1 -1
  44. package/dist/app/commands/eval.js +57 -20
  45. package/dist/app/commands/eval.js.map +1 -1
  46. package/dist/app/commands/i18n.d.ts +1 -1
  47. package/dist/app/commands/i18n.d.ts.map +1 -1
  48. package/dist/app/commands/i18n.js +12 -4
  49. package/dist/app/commands/i18n.js.map +1 -1
  50. package/dist/app/commands/init.d.ts.map +1 -1
  51. package/dist/app/commands/init.js +16 -13
  52. package/dist/app/commands/init.js.map +1 -1
  53. package/dist/app/commands/project-scope-selection.d.ts +13 -0
  54. package/dist/app/commands/project-scope-selection.d.ts.map +1 -0
  55. package/dist/app/commands/project-scope-selection.js +33 -0
  56. package/dist/app/commands/project-scope-selection.js.map +1 -0
  57. package/dist/app/commands/resume-probe.d.ts +11 -0
  58. package/dist/app/commands/resume-probe.d.ts.map +1 -0
  59. package/dist/app/commands/resume-probe.js +63 -0
  60. package/dist/app/commands/resume-probe.js.map +1 -0
  61. package/dist/app/commands/status.d.ts +31 -0
  62. package/dist/app/commands/status.d.ts.map +1 -1
  63. package/dist/app/commands/status.js +109 -20
  64. package/dist/app/commands/status.js.map +1 -1
  65. package/dist/app/commands/uninstall.d.ts +2 -0
  66. package/dist/app/commands/uninstall.d.ts.map +1 -1
  67. package/dist/app/commands/uninstall.js +217 -40
  68. package/dist/app/commands/uninstall.js.map +1 -1
  69. package/dist/app/commands/update.d.ts +6 -0
  70. package/dist/app/commands/update.d.ts.map +1 -1
  71. package/dist/app/commands/update.js +361 -68
  72. package/dist/app/commands/update.js.map +1 -1
  73. package/dist/config/repository-layout.json +4 -2
  74. package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
  75. package/dist/domains/bundle/bundle-platform.js +3 -1
  76. package/dist/domains/bundle/bundle-platform.js.map +1 -1
  77. package/dist/domains/bundle/eval-manifest-runtime.d.ts +6 -0
  78. package/dist/domains/bundle/eval-manifest-runtime.d.ts.map +1 -0
  79. package/dist/domains/bundle/eval-manifest-runtime.js +58 -0
  80. package/dist/domains/bundle/eval-manifest-runtime.js.map +1 -0
  81. package/dist/domains/bundle/types.d.ts +1 -1
  82. package/dist/domains/bundle/types.d.ts.map +1 -1
  83. package/dist/domains/comet-classic/classic-archive.d.ts +1 -0
  84. package/dist/domains/comet-classic/classic-archive.d.ts.map +1 -1
  85. package/dist/domains/comet-classic/classic-archive.js +35 -38
  86. package/dist/domains/comet-classic/classic-archive.js.map +1 -1
  87. package/dist/domains/comet-classic/classic-cli.d.ts +1 -1
  88. package/dist/domains/comet-classic/classic-cli.d.ts.map +1 -1
  89. package/dist/domains/comet-classic/classic-cli.js +3 -0
  90. package/dist/domains/comet-classic/classic-cli.js.map +1 -1
  91. package/dist/domains/comet-classic/classic-command-checks.d.ts +20 -0
  92. package/dist/domains/comet-classic/classic-command-checks.d.ts.map +1 -0
  93. package/dist/domains/comet-classic/classic-command-checks.js +97 -0
  94. package/dist/domains/comet-classic/classic-command-checks.js.map +1 -0
  95. package/dist/domains/comet-classic/classic-current-change.d.ts +19 -0
  96. package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -0
  97. package/dist/domains/comet-classic/classic-current-change.js +132 -0
  98. package/dist/domains/comet-classic/classic-current-change.js.map +1 -0
  99. package/dist/domains/comet-classic/classic-diagnostics.d.ts +2 -1
  100. package/dist/domains/comet-classic/classic-diagnostics.d.ts.map +1 -1
  101. package/dist/domains/comet-classic/classic-diagnostics.js +54 -1
  102. package/dist/domains/comet-classic/classic-diagnostics.js.map +1 -1
  103. package/dist/domains/comet-classic/classic-evidence.d.ts.map +1 -1
  104. package/dist/domains/comet-classic/classic-evidence.js +4 -0
  105. package/dist/domains/comet-classic/classic-evidence.js.map +1 -1
  106. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  107. package/dist/domains/comet-classic/classic-guard.js +101 -141
  108. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  109. package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
  110. package/dist/domains/comet-classic/classic-hook-guard.js +67 -29
  111. package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
  112. package/dist/domains/comet-classic/classic-project-config.d.ts +16 -0
  113. package/dist/domains/comet-classic/classic-project-config.d.ts.map +1 -0
  114. package/dist/domains/comet-classic/classic-project-config.js +40 -0
  115. package/dist/domains/comet-classic/classic-project-config.js.map +1 -0
  116. package/dist/domains/comet-classic/classic-resolver.d.ts.map +1 -1
  117. package/dist/domains/comet-classic/classic-resolver.js +3 -3
  118. package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
  119. package/dist/domains/comet-classic/classic-resume-probe-command.d.ts +3 -0
  120. package/dist/domains/comet-classic/classic-resume-probe-command.d.ts.map +1 -0
  121. package/dist/domains/comet-classic/classic-resume-probe-command.js +58 -0
  122. package/dist/domains/comet-classic/classic-resume-probe-command.js.map +1 -0
  123. package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts +2 -0
  124. package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts.map +1 -0
  125. package/dist/domains/comet-classic/classic-resume-probe-entry.js +4 -0
  126. package/dist/domains/comet-classic/classic-resume-probe-entry.js.map +1 -0
  127. package/dist/domains/comet-classic/classic-resume-probe.d.ts +29 -0
  128. package/dist/domains/comet-classic/classic-resume-probe.d.ts.map +1 -0
  129. package/dist/domains/comet-classic/classic-resume-probe.js +386 -0
  130. package/dist/domains/comet-classic/classic-resume-probe.js.map +1 -0
  131. package/dist/domains/comet-classic/classic-runtime-run.d.ts +2 -1
  132. package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
  133. package/dist/domains/comet-classic/classic-runtime-run.js +269 -4
  134. package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
  135. package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
  136. package/dist/domains/comet-classic/classic-state-command.js +129 -15
  137. package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
  138. package/dist/domains/comet-classic/classic-state.d.ts +3 -3
  139. package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
  140. package/dist/domains/comet-classic/classic-state.js +4 -6
  141. package/dist/domains/comet-classic/classic-state.js.map +1 -1
  142. package/dist/domains/comet-classic/classic-store.d.ts +4 -1
  143. package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
  144. package/dist/domains/comet-classic/classic-store.js +27 -9
  145. package/dist/domains/comet-classic/classic-store.js.map +1 -1
  146. package/dist/domains/comet-classic/classic-transitions.d.ts +1 -1
  147. package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
  148. package/dist/domains/comet-classic/classic-transitions.js +20 -1
  149. package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
  150. package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
  151. package/dist/domains/comet-classic/classic-validate-command.js +1 -0
  152. package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
  153. package/dist/domains/comet-classic/index.d.ts +3 -0
  154. package/dist/domains/comet-classic/index.d.ts.map +1 -1
  155. package/dist/domains/comet-classic/index.js +3 -0
  156. package/dist/domains/comet-classic/index.js.map +1 -1
  157. package/dist/domains/dashboard/collector.js +10 -2
  158. package/dist/domains/dashboard/collector.js.map +1 -1
  159. package/dist/domains/dashboard/web/assets/{index-CjU3VXVu.js → index-CIvN-PL8.js} +11 -11
  160. package/dist/domains/dashboard/web/assets/{index-BcO3_QlT.css → index-DcyRPBnT.css} +1 -1
  161. package/dist/domains/dashboard/web/index.html +16 -16
  162. package/dist/domains/engine/types.d.ts +1 -1
  163. package/dist/domains/engine/types.d.ts.map +1 -1
  164. package/dist/domains/integrations/openspec.d.ts.map +1 -1
  165. package/dist/domains/integrations/openspec.js +6 -6
  166. package/dist/domains/integrations/openspec.js.map +1 -1
  167. package/dist/domains/skill/find.d.ts.map +1 -1
  168. package/dist/domains/skill/find.js +1 -6
  169. package/dist/domains/skill/find.js.map +1 -1
  170. package/dist/domains/skill/managed-markdown.d.ts +14 -0
  171. package/dist/domains/skill/managed-markdown.d.ts.map +1 -0
  172. package/dist/domains/skill/managed-markdown.js +126 -0
  173. package/dist/domains/skill/managed-markdown.js.map +1 -0
  174. package/dist/domains/skill/platform-install.d.ts +4 -3
  175. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  176. package/dist/domains/skill/platform-install.js +96 -26
  177. package/dist/domains/skill/platform-install.js.map +1 -1
  178. package/dist/domains/skill/project-instructions.d.ts +22 -0
  179. package/dist/domains/skill/project-instructions.d.ts.map +1 -0
  180. package/dist/domains/skill/project-instructions.js +61 -0
  181. package/dist/domains/skill/project-instructions.js.map +1 -0
  182. package/dist/domains/skill/uninstall.d.ts +3 -1
  183. package/dist/domains/skill/uninstall.d.ts.map +1 -1
  184. package/dist/domains/skill/uninstall.js +86 -39
  185. package/dist/domains/skill/uninstall.js.map +1 -1
  186. package/dist/platform/install/detect.d.ts +2 -1
  187. package/dist/platform/install/detect.d.ts.map +1 -1
  188. package/dist/platform/install/detect.js +10 -1
  189. package/dist/platform/install/detect.js.map +1 -1
  190. package/dist/platform/install/platforms.d.ts +5 -1
  191. package/dist/platform/install/platforms.d.ts.map +1 -1
  192. package/dist/platform/install/platforms.js +21 -4
  193. package/dist/platform/install/platforms.js.map +1 -1
  194. package/dist/platform/install/project-registry.d.ts +37 -0
  195. package/dist/platform/install/project-registry.d.ts.map +1 -0
  196. package/dist/platform/install/project-registry.js +205 -0
  197. package/dist/platform/install/project-registry.js.map +1 -0
  198. package/eval/.env +28 -0
  199. package/eval/.env.example +52 -0
  200. package/eval/CLAUDE.md +43 -0
  201. package/eval/README.md +538 -0
  202. package/eval/langsmith/.env.example +16 -0
  203. package/eval/langsmith/README.md +93 -0
  204. package/eval/langsmith/skills/README.md +5 -0
  205. package/eval/langsmith/tasks/README.md +5 -0
  206. package/eval/langsmith/tests/conftest.py +187 -0
  207. package/eval/langsmith/tests/tasks/test_tasks.py +287 -0
  208. package/eval/langsmith/treatments/README.md +5 -0
  209. package/eval/local/README.md +80 -0
  210. package/eval/local/regression_baseline.json +13 -0
  211. package/eval/local/report-style-demo-assets/quality_cost.en.svg +41 -0
  212. package/eval/local/report-style-demo-assets/quality_cost.zh.svg +41 -0
  213. package/eval/local/report-style-demo-assets/rubric_delta.en.svg +56 -0
  214. package/eval/local/report-style-demo-assets/rubric_delta.zh.svg +56 -0
  215. package/eval/local/report-style-demo-assets/task_outcomes.en.svg +46 -0
  216. package/eval/local/report-style-demo-assets/task_outcomes.zh.svg +46 -0
  217. package/eval/local/report-style-demo.html +867 -0
  218. package/eval/local/scripts/compare_baselines.py +1018 -0
  219. package/eval/local/scripts/generate_report_style_demo_charts.py +322 -0
  220. package/eval/local/scripts/regression_check.py +171 -0
  221. package/eval/local/scripts/render_readme_benchmark_figures.py +416 -0
  222. package/eval/local/scripts/rescore_rubric.py +120 -0
  223. package/eval/local/skills/benchmarks/039-release/comet-classic-039/SKILL.md +282 -0
  224. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/auto-transition.md +27 -0
  225. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/comet-yaml-fields.md +68 -0
  226. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/context-recovery.md +35 -0
  227. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/debug-gate.md +17 -0
  228. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/decision-point.md +20 -0
  229. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/dirty-worktree.md +59 -0
  230. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/file-structure.md +28 -0
  231. package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/subagent-dispatch.md +113 -0
  232. package/eval/local/skills/benchmarks/039-release/comet-classic-039/rules/comet-phase-guard.md +125 -0
  233. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-archive.sh +311 -0
  234. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-env.sh +110 -0
  235. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-guard.sh +778 -0
  236. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-handoff.sh +390 -0
  237. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-hook-guard.sh +336 -0
  238. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-state.sh +1338 -0
  239. package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-yaml-validate.sh +225 -0
  240. package/eval/local/skills/benchmarks/039-release/comet-classic-039-archive/SKILL.md +100 -0
  241. package/eval/local/skills/benchmarks/039-release/comet-classic-039-build/SKILL.md +304 -0
  242. package/eval/local/skills/benchmarks/039-release/comet-classic-039-design/SKILL.md +263 -0
  243. package/eval/local/skills/benchmarks/039-release/comet-classic-039-hotfix/SKILL.md +204 -0
  244. package/eval/local/skills/benchmarks/039-release/comet-classic-039-open/SKILL.md +220 -0
  245. package/eval/local/skills/benchmarks/039-release/comet-classic-039-tweak/SKILL.md +180 -0
  246. package/eval/local/skills/benchmarks/039-release/comet-classic-039-verify/SKILL.md +234 -0
  247. package/eval/local/skills/benchmarks/040-beta/comet/SKILL.md +261 -0
  248. package/eval/local/skills/benchmarks/040-beta/comet/reference/auto-transition.md +27 -0
  249. package/eval/local/skills/benchmarks/040-beta/comet/reference/comet-yaml-fields.md +73 -0
  250. package/eval/local/skills/benchmarks/040-beta/comet/reference/context-recovery.md +45 -0
  251. package/eval/local/skills/benchmarks/040-beta/comet/reference/debug-gate.md +33 -0
  252. package/eval/local/skills/benchmarks/040-beta/comet/reference/decision-point.md +31 -0
  253. package/eval/local/skills/benchmarks/040-beta/comet/reference/dirty-worktree.md +59 -0
  254. package/eval/local/skills/benchmarks/040-beta/comet/reference/file-structure.md +28 -0
  255. package/eval/local/skills/benchmarks/040-beta/comet/reference/intent-frame.md +74 -0
  256. package/eval/local/skills/benchmarks/040-beta/comet/reference/scripts.md +69 -0
  257. package/eval/local/skills/benchmarks/040-beta/comet/reference/subagent-dispatch.md +169 -0
  258. package/eval/local/skills/benchmarks/040-beta/comet/rules/comet-phase-guard.md +130 -0
  259. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/checks.yaml +6 -0
  260. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/guardrails.yaml +13 -0
  261. package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/skill.yaml +122 -0
  262. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-archive.mjs +9515 -0
  263. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-env.mjs +15 -0
  264. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-guard.mjs +10173 -0
  265. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-handoff.mjs +9491 -0
  266. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-hook-guard.mjs +9175 -0
  267. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-intent.mjs +404 -0
  268. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-state.mjs +9273 -0
  269. package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-yaml-validate.mjs +7627 -0
  270. package/eval/local/skills/benchmarks/040-beta/comet-archive/SKILL.md +110 -0
  271. package/eval/local/skills/benchmarks/040-beta/comet-build/SKILL.md +315 -0
  272. package/eval/local/skills/benchmarks/040-beta/comet-design/SKILL.md +260 -0
  273. package/eval/local/skills/benchmarks/040-beta/comet-hotfix/SKILL.md +190 -0
  274. package/eval/local/skills/benchmarks/040-beta/comet-open/SKILL.md +210 -0
  275. package/eval/local/skills/benchmarks/040-beta/comet-tweak/SKILL.md +183 -0
  276. package/eval/local/skills/benchmarks/040-beta/comet-verify/SKILL.md +230 -0
  277. package/eval/local/skills/benchmarks/dependency/claude-md/comet-workflow/CLAUDE.md +12 -0
  278. package/eval/local/skills/benchmarks/dependency/openspec/openspec-apply-change/SKILL.md +156 -0
  279. package/eval/local/skills/benchmarks/dependency/openspec/openspec-archive-change/SKILL.md +114 -0
  280. package/eval/local/skills/benchmarks/dependency/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  281. package/eval/local/skills/benchmarks/dependency/openspec/openspec-continue-change/SKILL.md +118 -0
  282. package/eval/local/skills/benchmarks/dependency/openspec/openspec-explore/SKILL.md +288 -0
  283. package/eval/local/skills/benchmarks/dependency/openspec/openspec-ff-change/SKILL.md +101 -0
  284. package/eval/local/skills/benchmarks/dependency/openspec/openspec-new-change/SKILL.md +74 -0
  285. package/eval/local/skills/benchmarks/dependency/openspec/openspec-onboard/SKILL.md +554 -0
  286. package/eval/local/skills/benchmarks/dependency/openspec/openspec-propose/SKILL.md +110 -0
  287. package/eval/local/skills/benchmarks/dependency/openspec/openspec-sync-specs/SKILL.md +138 -0
  288. package/eval/local/skills/benchmarks/dependency/openspec/openspec-verify-change/SKILL.md +168 -0
  289. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/SKILL.md +159 -0
  290. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/frame-template.html +213 -0
  291. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/helper.js +167 -0
  292. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/server.cjs +723 -0
  293. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/start-server.sh +209 -0
  294. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/stop-server.sh +120 -0
  295. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
  296. package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/visual-companion.md +291 -0
  297. package/eval/local/skills/benchmarks/dependency/superpowers/dispatching-parallel-agents/SKILL.md +185 -0
  298. package/eval/local/skills/benchmarks/dependency/superpowers/executing-plans/SKILL.md +70 -0
  299. package/eval/local/skills/benchmarks/dependency/superpowers/finishing-a-development-branch/SKILL.md +241 -0
  300. package/eval/local/skills/benchmarks/dependency/superpowers/receiving-code-review/SKILL.md +213 -0
  301. package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/SKILL.md +103 -0
  302. package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/code-reviewer.md +172 -0
  303. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/SKILL.md +418 -0
  304. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/implementer-prompt.md +139 -0
  305. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/review-package +44 -0
  306. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/sdd-workspace +22 -0
  307. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/task-brief +40 -0
  308. package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/task-reviewer-prompt.md +188 -0
  309. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/CREATION-LOG.md +119 -0
  310. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/SKILL.md +296 -0
  311. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
  312. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting.md +115 -0
  313. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/defense-in-depth.md +122 -0
  314. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/find-polluter.sh +63 -0
  315. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/root-cause-tracing.md +169 -0
  316. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-academic.md +14 -0
  317. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-1.md +58 -0
  318. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-2.md +68 -0
  319. package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-3.md +69 -0
  320. package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/SKILL.md +371 -0
  321. package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/testing-anti-patterns.md +299 -0
  322. package/eval/local/skills/benchmarks/dependency/superpowers/using-git-worktrees/SKILL.md +202 -0
  323. package/eval/local/skills/benchmarks/dependency/superpowers/verification-before-completion/SKILL.md +139 -0
  324. package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/SKILL.md +174 -0
  325. package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
  326. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/SKILL.md +689 -0
  327. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/anthropic-best-practices.md +1150 -0
  328. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  329. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/graphviz-conventions.dot +172 -0
  330. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/persuasion-principles.md +187 -0
  331. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/render-graphs.js +168 -0
  332. package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
  333. package/eval/local/tasks/authoring-skill-smoke/environment/Dockerfile +2 -0
  334. package/eval/local/tasks/authoring-skill-smoke/instruction.md +4 -0
  335. package/eval/local/tasks/authoring-skill-smoke/task.toml +21 -0
  336. package/eval/local/tasks/authoring-skill-smoke/validation/test_authoring_skill_smoke.py +54 -0
  337. package/eval/local/tasks/comet-agent-memory-routing/environment/Dockerfile +28 -0
  338. package/eval/local/tasks/comet-agent-memory-routing/environment/agent_system.py +40 -0
  339. package/eval/local/tasks/comet-agent-memory-routing/environment/test_agent_system.py +29 -0
  340. package/eval/local/tasks/comet-agent-memory-routing/instruction.md +20 -0
  341. package/eval/local/tasks/comet-agent-memory-routing/task.toml +24 -0
  342. package/eval/local/tasks/comet-agent-memory-routing/validation/test_agent_memory_routing.py +24 -0
  343. package/eval/local/tasks/comet-api-cache-ttl/environment/Dockerfile +39 -0
  344. package/eval/local/tasks/comet-api-cache-ttl/environment/cache.py +39 -0
  345. package/eval/local/tasks/comet-api-cache-ttl/environment/test_cache.py +81 -0
  346. package/eval/local/tasks/comet-api-cache-ttl/instruction.md +33 -0
  347. package/eval/local/tasks/comet-api-cache-ttl/task.toml +24 -0
  348. package/eval/local/tasks/comet-api-cache-ttl/validation/test_api_cache_ttl.py +58 -0
  349. package/eval/local/tasks/comet-cross-file-refactor/environment/Dockerfile +15 -0
  350. package/eval/local/tasks/comet-cross-file-refactor/environment/cli.py +13 -0
  351. package/eval/local/tasks/comet-cross-file-refactor/environment/counting.py +13 -0
  352. package/eval/local/tasks/comet-cross-file-refactor/environment/test_textkit.py +21 -0
  353. package/eval/local/tasks/comet-cross-file-refactor/instruction.md +16 -0
  354. package/eval/local/tasks/comet-cross-file-refactor/task.toml +20 -0
  355. package/eval/local/tasks/comet-cross-file-refactor/validation/test_cross_file_refactor.py +20 -0
  356. package/eval/local/tasks/comet-dependency-confusion/environment/Dockerfile +28 -0
  357. package/eval/local/tasks/comet-dependency-confusion/environment/app.py +8 -0
  358. package/eval/local/tasks/comet-dependency-confusion/environment/settings_loader.py +14 -0
  359. package/eval/local/tasks/comet-dependency-confusion/environment/test_settings_service.py +19 -0
  360. package/eval/local/tasks/comet-dependency-confusion/instruction.md +18 -0
  361. package/eval/local/tasks/comet-dependency-confusion/task.toml +24 -0
  362. package/eval/local/tasks/comet-dependency-confusion/validation/test_dependency_confusion.py +24 -0
  363. package/eval/local/tasks/comet-fix-median/environment/Dockerfile +50 -0
  364. package/eval/local/tasks/comet-fix-median/environment/stats.py +54 -0
  365. package/eval/local/tasks/comet-fix-median/environment/test_stats.py +68 -0
  366. package/eval/local/tasks/comet-fix-median/instruction.md +25 -0
  367. package/eval/local/tasks/comet-fix-median/task.toml +24 -0
  368. package/eval/local/tasks/comet-fix-median/validation/test_fix_median.py +60 -0
  369. package/eval/local/tasks/comet-framework-selection/environment/Dockerfile +28 -0
  370. package/eval/local/tasks/comet-framework-selection/environment/architecture.py +12 -0
  371. package/eval/local/tasks/comet-framework-selection/environment/test_architecture.py +30 -0
  372. package/eval/local/tasks/comet-framework-selection/instruction.md +22 -0
  373. package/eval/local/tasks/comet-framework-selection/task.toml +24 -0
  374. package/eval/local/tasks/comet-framework-selection/validation/test_framework_selection.py +24 -0
  375. package/eval/local/tasks/comet-full-workflow/environment/Dockerfile +53 -0
  376. package/eval/local/tasks/comet-full-workflow/environment/test_wordcount.py +29 -0
  377. package/eval/local/tasks/comet-full-workflow/environment/wordcount.py +40 -0
  378. package/eval/local/tasks/comet-full-workflow/instruction.md +18 -0
  379. package/eval/local/tasks/comet-full-workflow/task.toml +24 -0
  380. package/eval/local/tasks/comet-full-workflow/validation/test_full_workflow.py +171 -0
  381. package/eval/local/tasks/comet-graph-execution-review/environment/Dockerfile +28 -0
  382. package/eval/local/tasks/comet-graph-execution-review/environment/pipeline.py +19 -0
  383. package/eval/local/tasks/comet-graph-execution-review/environment/test_pipeline.py +53 -0
  384. package/eval/local/tasks/comet-graph-execution-review/instruction.md +21 -0
  385. package/eval/local/tasks/comet-graph-execution-review/task.toml +24 -0
  386. package/eval/local/tasks/comet-graph-execution-review/validation/test_graph_execution_review.py +24 -0
  387. package/eval/local/tasks/comet-human-approval-flow/environment/Dockerfile +15 -0
  388. package/eval/local/tasks/comet-human-approval-flow/environment/approvals.py +17 -0
  389. package/eval/local/tasks/comet-human-approval-flow/environment/test_approvals.py +30 -0
  390. package/eval/local/tasks/comet-human-approval-flow/instruction.md +15 -0
  391. package/eval/local/tasks/comet-human-approval-flow/task.toml +20 -0
  392. package/eval/local/tasks/comet-human-approval-flow/validation/test_human_approval_flow.py +20 -0
  393. package/eval/local/tasks/comet-layered-streaming-fix/environment/Dockerfile +20 -0
  394. package/eval/local/tasks/comet-layered-streaming-fix/environment/chat_app.py +19 -0
  395. package/eval/local/tasks/comet-layered-streaming-fix/environment/test_chat_app.py +15 -0
  396. package/eval/local/tasks/comet-layered-streaming-fix/instruction.md +14 -0
  397. package/eval/local/tasks/comet-layered-streaming-fix/task.toml +20 -0
  398. package/eval/local/tasks/comet-layered-streaming-fix/validation/test_layered_streaming_fix.py +20 -0
  399. package/eval/local/tasks/comet-noise-distractor/environment/Dockerfile +15 -0
  400. package/eval/local/tasks/comet-noise-distractor/environment/distractor.md +4 -0
  401. package/eval/local/tasks/comet-noise-distractor/environment/invoice.py +5 -0
  402. package/eval/local/tasks/comet-noise-distractor/environment/test_invoice.py +18 -0
  403. package/eval/local/tasks/comet-noise-distractor/instruction.md +17 -0
  404. package/eval/local/tasks/comet-noise-distractor/task.toml +20 -0
  405. package/eval/local/tasks/comet-noise-distractor/validation/test_noise_distractor.py +20 -0
  406. package/eval/local/tasks/comet-observability-env-template/environment/Dockerfile +15 -0
  407. package/eval/local/tasks/comet-observability-env-template/environment/env_writer.py +17 -0
  408. package/eval/local/tasks/comet-observability-env-template/environment/test_env_writer.py +17 -0
  409. package/eval/local/tasks/comet-observability-env-template/instruction.md +20 -0
  410. package/eval/local/tasks/comet-observability-env-template/task.toml +20 -0
  411. package/eval/local/tasks/comet-observability-env-template/validation/test_observability_env_template.py +46 -0
  412. package/eval/local/tasks/comet-perf-dedupe/environment/Dockerfile +39 -0
  413. package/eval/local/tasks/comet-perf-dedupe/environment/dedupe.py +22 -0
  414. package/eval/local/tasks/comet-perf-dedupe/environment/test_dedupe.py +43 -0
  415. package/eval/local/tasks/comet-perf-dedupe/instruction.md +25 -0
  416. package/eval/local/tasks/comet-perf-dedupe/task.toml +24 -0
  417. package/eval/local/tasks/comet-perf-dedupe/validation/test_perf_dedupe.py +40 -0
  418. package/eval/local/tasks/comet-persistence-threading/environment/Dockerfile +15 -0
  419. package/eval/local/tasks/comet-persistence-threading/environment/session_store.py +15 -0
  420. package/eval/local/tasks/comet-persistence-threading/environment/test_session_store.py +21 -0
  421. package/eval/local/tasks/comet-persistence-threading/instruction.md +14 -0
  422. package/eval/local/tasks/comet-persistence-threading/task.toml +20 -0
  423. package/eval/local/tasks/comet-persistence-threading/validation/test_persistence_threading.py +20 -0
  424. package/eval/local/tasks/comet-refactor-counter/environment/Dockerfile +39 -0
  425. package/eval/local/tasks/comet-refactor-counter/environment/test_text_processor.py +57 -0
  426. package/eval/local/tasks/comet-refactor-counter/environment/text_processor.py +49 -0
  427. package/eval/local/tasks/comet-refactor-counter/instruction.md +31 -0
  428. package/eval/local/tasks/comet-refactor-counter/task.toml +24 -0
  429. package/eval/local/tasks/comet-refactor-counter/validation/test_refactor_counter.py +75 -0
  430. package/eval/local/tasks/comet-robust-config/environment/Dockerfile +39 -0
  431. package/eval/local/tasks/comet-robust-config/environment/config_loader.py +56 -0
  432. package/eval/local/tasks/comet-robust-config/environment/test_config_loader.py +72 -0
  433. package/eval/local/tasks/comet-robust-config/instruction.md +30 -0
  434. package/eval/local/tasks/comet-robust-config/task.toml +24 -0
  435. package/eval/local/tasks/comet-robust-config/validation/test_robust_config.py +37 -0
  436. package/eval/local/tasks/generic-skill-smoke/environment/Dockerfile +2 -0
  437. package/eval/local/tasks/generic-skill-smoke/instruction.md +7 -0
  438. package/eval/local/tasks/generic-skill-smoke/task.toml +21 -0
  439. package/eval/local/tasks/generic-skill-smoke/validation/test_generic_skill_smoke.py +36 -0
  440. package/eval/local/tasks/index.yaml +155 -0
  441. package/eval/local/tasks/workflow-overlay-contract/environment/Dockerfile +5 -0
  442. package/eval/local/tasks/workflow-overlay-contract/instruction.md +5 -0
  443. package/eval/local/tasks/workflow-overlay-contract/task.toml +21 -0
  444. package/eval/local/tasks/workflow-overlay-contract/validation/test_workflow_overlay_contract.py +235 -0
  445. package/eval/local/tasks/workflow-route-conformance/environment/Dockerfile +2 -0
  446. package/eval/local/tasks/workflow-route-conformance/instruction.md +5 -0
  447. package/eval/local/tasks/workflow-route-conformance/task.toml +21 -0
  448. package/eval/local/tasks/workflow-route-conformance/validation/test_workflow_route_conformance.py +84 -0
  449. package/eval/local/tests/conftest.py +1148 -0
  450. package/eval/local/tests/scaffold/test_attribution.py +48 -0
  451. package/eval/local/tests/scaffold/test_compare_baselines.py +999 -0
  452. package/eval/local/tests/scaffold/test_conftest_helpers.py +534 -0
  453. package/eval/local/tests/scaffold/test_evidence.py +15 -0
  454. package/eval/local/tests/scaffold/test_langsmith_conftest.py +244 -0
  455. package/eval/local/tests/scaffold/test_logging.py +243 -0
  456. package/eval/local/tests/scaffold/test_manifests.py +132 -0
  457. package/eval/local/tests/scaffold/test_profiles.py +866 -0
  458. package/eval/local/tests/scaffold/test_regression_check.py +19 -0
  459. package/eval/local/tests/scaffold/test_report_style_demo_charts.py +45 -0
  460. package/eval/local/tests/scaffold/test_sample_quality.py +211 -0
  461. package/eval/local/tests/scaffold/test_tasks.py +168 -0
  462. package/eval/local/tests/scaffold/test_treatments.py +244 -0
  463. package/eval/local/tests/scaffold/test_utils.py +139 -0
  464. package/eval/local/tests/tasks/test_tasks.py +445 -0
  465. package/eval/local/tests/tasks/test_validation_scripts.py +143 -0
  466. package/eval/local/treatments/comet/comet_full_039.yaml +141 -0
  467. package/eval/local/treatments/comet/comet_full_040_beta.yaml +140 -0
  468. package/eval/local/treatments/common/control.yaml +4 -0
  469. package/eval/pyproject.toml +41 -0
  470. package/eval/report-html-config.json +6 -0
  471. package/eval/scaffold/__init__.py +67 -0
  472. package/eval/scaffold/python/__init__.py +95 -0
  473. package/eval/scaffold/python/attribution.py +43 -0
  474. package/eval/scaffold/python/evidence.py +38 -0
  475. package/eval/scaffold/python/external_data_handler.py +18 -0
  476. package/eval/scaffold/python/generic_llm_judge.py +235 -0
  477. package/eval/scaffold/python/judge_config.py +168 -0
  478. package/eval/scaffold/python/llm_judge.py +191 -0
  479. package/eval/scaffold/python/logging.py +705 -0
  480. package/eval/scaffold/python/manifests.py +129 -0
  481. package/eval/scaffold/python/paper_charts.py +25 -0
  482. package/eval/scaffold/python/pass_at_k.py +107 -0
  483. package/eval/scaffold/python/paths.py +56 -0
  484. package/eval/scaffold/python/profiles.py +127 -0
  485. package/eval/scaffold/python/report_outputs.py +1391 -0
  486. package/eval/scaffold/python/sample_quality.py +339 -0
  487. package/eval/scaffold/python/schema.py +39 -0
  488. package/eval/scaffold/python/skill_parser.py +469 -0
  489. package/eval/scaffold/python/tasks.py +330 -0
  490. package/eval/scaffold/python/treatments.py +271 -0
  491. package/eval/scaffold/python/utils.py +366 -0
  492. package/eval/scaffold/python/validation/__init__.py +21 -0
  493. package/eval/scaffold/python/validation/authoring_rubric.py +326 -0
  494. package/eval/scaffold/python/validation/comet_workflow.py +187 -0
  495. package/eval/scaffold/python/validation/core.py +264 -0
  496. package/eval/scaffold/python/validation/dataset.py +337 -0
  497. package/eval/scaffold/python/validation/docker.py +106 -0
  498. package/eval/scaffold/python/validation/evaluator.py +549 -0
  499. package/eval/scaffold/python/validation/generic_rubric.py +169 -0
  500. package/eval/scaffold/python/validation/rubric.py +740 -0
  501. package/eval/scaffold/python/validation/runner.py +237 -0
  502. package/eval/scaffold/python/validation/scripts.py +58 -0
  503. package/eval/scaffold/python/validation/tracing.py +313 -0
  504. package/eval/scaffold/shell/common.sh +126 -0
  505. package/eval/scaffold/shell/docker.sh +482 -0
  506. package/eval/scaffold/shell/run-claude-loop.sh +181 -0
  507. package/eval/scaffold/shell/setup.sh +259 -0
  508. package/eval/simulator-instruction.md +9 -0
  509. package/package.json +24 -2
@@ -126,17 +126,17 @@ var require_visit = __commonJS({
126
126
  visit.BREAK = BREAK;
127
127
  visit.SKIP = SKIP;
128
128
  visit.REMOVE = REMOVE;
129
- function visit_(key, node, visitor, path18) {
130
- const ctrl = callVisitor(key, node, visitor, path18);
129
+ function visit_(key, node, visitor, path22) {
130
+ const ctrl = callVisitor(key, node, visitor, path22);
131
131
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
132
- replaceNode(key, path18, ctrl);
133
- return visit_(key, ctrl, visitor, path18);
132
+ replaceNode(key, path22, ctrl);
133
+ return visit_(key, ctrl, visitor, path22);
134
134
  }
135
135
  if (typeof ctrl !== "symbol") {
136
136
  if (identity.isCollection(node)) {
137
- path18 = Object.freeze(path18.concat(node));
137
+ path22 = Object.freeze(path22.concat(node));
138
138
  for (let i = 0; i < node.items.length; ++i) {
139
- const ci = visit_(i, node.items[i], visitor, path18);
139
+ const ci = visit_(i, node.items[i], visitor, path22);
140
140
  if (typeof ci === "number")
141
141
  i = ci - 1;
142
142
  else if (ci === BREAK)
@@ -147,13 +147,13 @@ var require_visit = __commonJS({
147
147
  }
148
148
  }
149
149
  } else if (identity.isPair(node)) {
150
- path18 = Object.freeze(path18.concat(node));
151
- const ck = visit_("key", node.key, visitor, path18);
150
+ path22 = Object.freeze(path22.concat(node));
151
+ const ck = visit_("key", node.key, visitor, path22);
152
152
  if (ck === BREAK)
153
153
  return BREAK;
154
154
  else if (ck === REMOVE)
155
155
  node.key = null;
156
- const cv = visit_("value", node.value, visitor, path18);
156
+ const cv = visit_("value", node.value, visitor, path22);
157
157
  if (cv === BREAK)
158
158
  return BREAK;
159
159
  else if (cv === REMOVE)
@@ -174,17 +174,17 @@ var require_visit = __commonJS({
174
174
  visitAsync.BREAK = BREAK;
175
175
  visitAsync.SKIP = SKIP;
176
176
  visitAsync.REMOVE = REMOVE;
177
- async function visitAsync_(key, node, visitor, path18) {
178
- const ctrl = await callVisitor(key, node, visitor, path18);
177
+ async function visitAsync_(key, node, visitor, path22) {
178
+ const ctrl = await callVisitor(key, node, visitor, path22);
179
179
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
180
- replaceNode(key, path18, ctrl);
181
- return visitAsync_(key, ctrl, visitor, path18);
180
+ replaceNode(key, path22, ctrl);
181
+ return visitAsync_(key, ctrl, visitor, path22);
182
182
  }
183
183
  if (typeof ctrl !== "symbol") {
184
184
  if (identity.isCollection(node)) {
185
- path18 = Object.freeze(path18.concat(node));
185
+ path22 = Object.freeze(path22.concat(node));
186
186
  for (let i = 0; i < node.items.length; ++i) {
187
- const ci = await visitAsync_(i, node.items[i], visitor, path18);
187
+ const ci = await visitAsync_(i, node.items[i], visitor, path22);
188
188
  if (typeof ci === "number")
189
189
  i = ci - 1;
190
190
  else if (ci === BREAK)
@@ -195,13 +195,13 @@ var require_visit = __commonJS({
195
195
  }
196
196
  }
197
197
  } else if (identity.isPair(node)) {
198
- path18 = Object.freeze(path18.concat(node));
199
- const ck = await visitAsync_("key", node.key, visitor, path18);
198
+ path22 = Object.freeze(path22.concat(node));
199
+ const ck = await visitAsync_("key", node.key, visitor, path22);
200
200
  if (ck === BREAK)
201
201
  return BREAK;
202
202
  else if (ck === REMOVE)
203
203
  node.key = null;
204
- const cv = await visitAsync_("value", node.value, visitor, path18);
204
+ const cv = await visitAsync_("value", node.value, visitor, path22);
205
205
  if (cv === BREAK)
206
206
  return BREAK;
207
207
  else if (cv === REMOVE)
@@ -228,23 +228,23 @@ var require_visit = __commonJS({
228
228
  }
229
229
  return visitor;
230
230
  }
231
- function callVisitor(key, node, visitor, path18) {
231
+ function callVisitor(key, node, visitor, path22) {
232
232
  if (typeof visitor === "function")
233
- return visitor(key, node, path18);
233
+ return visitor(key, node, path22);
234
234
  if (identity.isMap(node))
235
- return visitor.Map?.(key, node, path18);
235
+ return visitor.Map?.(key, node, path22);
236
236
  if (identity.isSeq(node))
237
- return visitor.Seq?.(key, node, path18);
237
+ return visitor.Seq?.(key, node, path22);
238
238
  if (identity.isPair(node))
239
- return visitor.Pair?.(key, node, path18);
239
+ return visitor.Pair?.(key, node, path22);
240
240
  if (identity.isScalar(node))
241
- return visitor.Scalar?.(key, node, path18);
241
+ return visitor.Scalar?.(key, node, path22);
242
242
  if (identity.isAlias(node))
243
- return visitor.Alias?.(key, node, path18);
243
+ return visitor.Alias?.(key, node, path22);
244
244
  return void 0;
245
245
  }
246
- function replaceNode(key, path18, node) {
247
- const parent = path18[path18.length - 1];
246
+ function replaceNode(key, path22, node) {
247
+ const parent = path22[path22.length - 1];
248
248
  if (identity.isCollection(parent)) {
249
249
  parent.items[key] = node;
250
250
  } else if (identity.isPair(parent)) {
@@ -854,10 +854,10 @@ var require_Collection = __commonJS({
854
854
  var createNode = require_createNode();
855
855
  var identity = require_identity();
856
856
  var Node = require_Node();
857
- function collectionFromPath(schema, path18, value) {
857
+ function collectionFromPath(schema, path22, value) {
858
858
  let v = value;
859
- for (let i = path18.length - 1; i >= 0; --i) {
860
- const k = path18[i];
859
+ for (let i = path22.length - 1; i >= 0; --i) {
860
+ const k = path22[i];
861
861
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
862
862
  const a = [];
863
863
  a[k] = v;
@@ -876,7 +876,7 @@ var require_Collection = __commonJS({
876
876
  sourceObjects: /* @__PURE__ */ new Map()
877
877
  });
878
878
  }
879
- var isEmptyPath = (path18) => path18 == null || typeof path18 === "object" && !!path18[Symbol.iterator]().next().done;
879
+ var isEmptyPath = (path22) => path22 == null || typeof path22 === "object" && !!path22[Symbol.iterator]().next().done;
880
880
  var Collection = class extends Node.NodeBase {
881
881
  constructor(type, schema) {
882
882
  super(type);
@@ -906,11 +906,11 @@ var require_Collection = __commonJS({
906
906
  * be a Pair instance or a `{ key, value }` object, which may not have a key
907
907
  * that already exists in the map.
908
908
  */
909
- addIn(path18, value) {
910
- if (isEmptyPath(path18))
909
+ addIn(path22, value) {
910
+ if (isEmptyPath(path22))
911
911
  this.add(value);
912
912
  else {
913
- const [key, ...rest] = path18;
913
+ const [key, ...rest] = path22;
914
914
  const node = this.get(key, true);
915
915
  if (identity.isCollection(node))
916
916
  node.addIn(rest, value);
@@ -924,8 +924,8 @@ var require_Collection = __commonJS({
924
924
  * Removes a value from the collection.
925
925
  * @returns `true` if the item was found and removed.
926
926
  */
927
- deleteIn(path18) {
928
- const [key, ...rest] = path18;
927
+ deleteIn(path22) {
928
+ const [key, ...rest] = path22;
929
929
  if (rest.length === 0)
930
930
  return this.delete(key);
931
931
  const node = this.get(key, true);
@@ -939,8 +939,8 @@ var require_Collection = __commonJS({
939
939
  * scalar values from their surrounding node; to disable set `keepScalar` to
940
940
  * `true` (collections are always returned intact).
941
941
  */
942
- getIn(path18, keepScalar) {
943
- const [key, ...rest] = path18;
942
+ getIn(path22, keepScalar) {
943
+ const [key, ...rest] = path22;
944
944
  const node = this.get(key, true);
945
945
  if (rest.length === 0)
946
946
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -958,8 +958,8 @@ var require_Collection = __commonJS({
958
958
  /**
959
959
  * Checks if the collection includes a value with the key `key`.
960
960
  */
961
- hasIn(path18) {
962
- const [key, ...rest] = path18;
961
+ hasIn(path22) {
962
+ const [key, ...rest] = path22;
963
963
  if (rest.length === 0)
964
964
  return this.has(key);
965
965
  const node = this.get(key, true);
@@ -969,8 +969,8 @@ var require_Collection = __commonJS({
969
969
  * Sets a value in this collection. For `!!set`, `value` needs to be a
970
970
  * boolean to add/remove the item from the set.
971
971
  */
972
- setIn(path18, value) {
973
- const [key, ...rest] = path18;
972
+ setIn(path22, value) {
973
+ const [key, ...rest] = path22;
974
974
  if (rest.length === 0) {
975
975
  this.set(key, value);
976
976
  } else {
@@ -3485,9 +3485,9 @@ var require_Document = __commonJS({
3485
3485
  this.contents.add(value);
3486
3486
  }
3487
3487
  /** Adds a value to the document. */
3488
- addIn(path18, value) {
3488
+ addIn(path22, value) {
3489
3489
  if (assertCollection(this.contents))
3490
- this.contents.addIn(path18, value);
3490
+ this.contents.addIn(path22, value);
3491
3491
  }
3492
3492
  /**
3493
3493
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -3562,14 +3562,14 @@ var require_Document = __commonJS({
3562
3562
  * Removes a value from the document.
3563
3563
  * @returns `true` if the item was found and removed.
3564
3564
  */
3565
- deleteIn(path18) {
3566
- if (Collection.isEmptyPath(path18)) {
3565
+ deleteIn(path22) {
3566
+ if (Collection.isEmptyPath(path22)) {
3567
3567
  if (this.contents == null)
3568
3568
  return false;
3569
3569
  this.contents = null;
3570
3570
  return true;
3571
3571
  }
3572
- return assertCollection(this.contents) ? this.contents.deleteIn(path18) : false;
3572
+ return assertCollection(this.contents) ? this.contents.deleteIn(path22) : false;
3573
3573
  }
3574
3574
  /**
3575
3575
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -3584,10 +3584,10 @@ var require_Document = __commonJS({
3584
3584
  * scalar values from their surrounding node; to disable set `keepScalar` to
3585
3585
  * `true` (collections are always returned intact).
3586
3586
  */
3587
- getIn(path18, keepScalar) {
3588
- if (Collection.isEmptyPath(path18))
3587
+ getIn(path22, keepScalar) {
3588
+ if (Collection.isEmptyPath(path22))
3589
3589
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3590
- return identity.isCollection(this.contents) ? this.contents.getIn(path18, keepScalar) : void 0;
3590
+ return identity.isCollection(this.contents) ? this.contents.getIn(path22, keepScalar) : void 0;
3591
3591
  }
3592
3592
  /**
3593
3593
  * Checks if the document includes a value with the key `key`.
@@ -3598,10 +3598,10 @@ var require_Document = __commonJS({
3598
3598
  /**
3599
3599
  * Checks if the document includes a value at `path`.
3600
3600
  */
3601
- hasIn(path18) {
3602
- if (Collection.isEmptyPath(path18))
3601
+ hasIn(path22) {
3602
+ if (Collection.isEmptyPath(path22))
3603
3603
  return this.contents !== void 0;
3604
- return identity.isCollection(this.contents) ? this.contents.hasIn(path18) : false;
3604
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path22) : false;
3605
3605
  }
3606
3606
  /**
3607
3607
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -3618,13 +3618,13 @@ var require_Document = __commonJS({
3618
3618
  * Sets a value in this document. For `!!set`, `value` needs to be a
3619
3619
  * boolean to add/remove the item from the set.
3620
3620
  */
3621
- setIn(path18, value) {
3622
- if (Collection.isEmptyPath(path18)) {
3621
+ setIn(path22, value) {
3622
+ if (Collection.isEmptyPath(path22)) {
3623
3623
  this.contents = value;
3624
3624
  } else if (this.contents == null) {
3625
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path18), value);
3625
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path22), value);
3626
3626
  } else if (assertCollection(this.contents)) {
3627
- this.contents.setIn(path18, value);
3627
+ this.contents.setIn(path22, value);
3628
3628
  }
3629
3629
  }
3630
3630
  /**
@@ -5584,9 +5584,9 @@ var require_cst_visit = __commonJS({
5584
5584
  visit.BREAK = BREAK;
5585
5585
  visit.SKIP = SKIP;
5586
5586
  visit.REMOVE = REMOVE;
5587
- visit.itemAtPath = (cst, path18) => {
5587
+ visit.itemAtPath = (cst, path22) => {
5588
5588
  let item = cst;
5589
- for (const [field2, index] of path18) {
5589
+ for (const [field2, index] of path22) {
5590
5590
  const tok = item?.[field2];
5591
5591
  if (tok && "items" in tok) {
5592
5592
  item = tok.items[index];
@@ -5595,23 +5595,23 @@ var require_cst_visit = __commonJS({
5595
5595
  }
5596
5596
  return item;
5597
5597
  };
5598
- visit.parentCollection = (cst, path18) => {
5599
- const parent = visit.itemAtPath(cst, path18.slice(0, -1));
5600
- const field2 = path18[path18.length - 1][0];
5598
+ visit.parentCollection = (cst, path22) => {
5599
+ const parent = visit.itemAtPath(cst, path22.slice(0, -1));
5600
+ const field2 = path22[path22.length - 1][0];
5601
5601
  const coll = parent?.[field2];
5602
5602
  if (coll && "items" in coll)
5603
5603
  return coll;
5604
5604
  throw new Error("Parent collection not found");
5605
5605
  };
5606
- function _visit(path18, item, visitor) {
5607
- let ctrl = visitor(item, path18);
5606
+ function _visit(path22, item, visitor) {
5607
+ let ctrl = visitor(item, path22);
5608
5608
  if (typeof ctrl === "symbol")
5609
5609
  return ctrl;
5610
5610
  for (const field2 of ["key", "value"]) {
5611
5611
  const token = item[field2];
5612
5612
  if (token && "items" in token) {
5613
5613
  for (let i = 0; i < token.items.length; ++i) {
5614
- const ci = _visit(Object.freeze(path18.concat([[field2, i]])), token.items[i], visitor);
5614
+ const ci = _visit(Object.freeze(path22.concat([[field2, i]])), token.items[i], visitor);
5615
5615
  if (typeof ci === "number")
5616
5616
  i = ci - 1;
5617
5617
  else if (ci === BREAK)
@@ -5622,10 +5622,10 @@ var require_cst_visit = __commonJS({
5622
5622
  }
5623
5623
  }
5624
5624
  if (typeof ctrl === "function" && field2 === "key")
5625
- ctrl = ctrl(item, path18);
5625
+ ctrl = ctrl(item, path22);
5626
5626
  }
5627
5627
  }
5628
- return typeof ctrl === "function" ? ctrl(item, path18) : ctrl;
5628
+ return typeof ctrl === "function" ? ctrl(item, path22) : ctrl;
5629
5629
  }
5630
5630
  exports.visit = visit;
5631
5631
  }
@@ -6927,14 +6927,14 @@ var require_parser = __commonJS({
6927
6927
  case "scalar":
6928
6928
  case "single-quoted-scalar":
6929
6929
  case "double-quoted-scalar": {
6930
- const fs17 = this.flowScalar(this.type);
6930
+ const fs21 = this.flowScalar(this.type);
6931
6931
  if (atNextItem || it.value) {
6932
- map.items.push({ start, key: fs17, sep: [] });
6932
+ map.items.push({ start, key: fs21, sep: [] });
6933
6933
  this.onKeyLine = true;
6934
6934
  } else if (it.sep) {
6935
- this.stack.push(fs17);
6935
+ this.stack.push(fs21);
6936
6936
  } else {
6937
- Object.assign(it, { key: fs17, sep: [] });
6937
+ Object.assign(it, { key: fs21, sep: [] });
6938
6938
  this.onKeyLine = true;
6939
6939
  }
6940
6940
  return;
@@ -7062,13 +7062,13 @@ var require_parser = __commonJS({
7062
7062
  case "scalar":
7063
7063
  case "single-quoted-scalar":
7064
7064
  case "double-quoted-scalar": {
7065
- const fs17 = this.flowScalar(this.type);
7065
+ const fs21 = this.flowScalar(this.type);
7066
7066
  if (!it || it.value)
7067
- fc.items.push({ start: [], key: fs17, sep: [] });
7067
+ fc.items.push({ start: [], key: fs21, sep: [] });
7068
7068
  else if (it.sep)
7069
- this.stack.push(fs17);
7069
+ this.stack.push(fs21);
7070
7070
  else
7071
- Object.assign(it, { key: fs17, sep: [] });
7071
+ Object.assign(it, { key: fs21, sep: [] });
7072
7072
  return;
7073
7073
  }
7074
7074
  case "flow-map-end":
@@ -7257,7 +7257,7 @@ var require_public_api = __commonJS({
7257
7257
  return docs;
7258
7258
  return Object.assign([], { empty: true }, composer$1.streamInfo());
7259
7259
  }
7260
- function parseDocument6(source, options = {}) {
7260
+ function parseDocument7(source, options = {}) {
7261
7261
  const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
7262
7262
  const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
7263
7263
  const composer$1 = new composer.Composer(options);
@@ -7283,7 +7283,7 @@ var require_public_api = __commonJS({
7283
7283
  } else if (options === void 0 && reviver && typeof reviver === "object") {
7284
7284
  options = reviver;
7285
7285
  }
7286
- const doc = parseDocument6(src, options);
7286
+ const doc = parseDocument7(src, options);
7287
7287
  if (!doc)
7288
7288
  return null;
7289
7289
  doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
@@ -7319,7 +7319,7 @@ var require_public_api = __commonJS({
7319
7319
  }
7320
7320
  exports.parse = parse2;
7321
7321
  exports.parseAllDocuments = parseAllDocuments;
7322
- exports.parseDocument = parseDocument6;
7322
+ exports.parseDocument = parseDocument7;
7323
7323
  exports.stringify = stringify;
7324
7324
  }
7325
7325
  });
@@ -7573,6 +7573,10 @@ Valid format: lowercase kebab-case (a-z, 0-9, single hyphens)`;
7573
7573
  if (name.includes("..")) return "Change name cannot contain '..' (path traversal not allowed)";
7574
7574
  return null;
7575
7575
  }
7576
+ function assertOpenSpecChangeName(name) {
7577
+ const error = openSpecChangeNameError(name);
7578
+ if (error) throw new Error(error);
7579
+ }
7576
7580
  async function resolveClassicChangeDirectory(name) {
7577
7581
  const active = `openspec/changes/${name}`;
7578
7582
  if (await exists(filesystemPath(active))) {
@@ -7622,26 +7626,26 @@ function projectRootFor(changeDir) {
7622
7626
  }
7623
7627
  throw new Error(`Classic change is not inside an openspec directory: ${changeDir}`);
7624
7628
  }
7625
- function relativeSource(projectRoot, file) {
7626
- return path2.relative(projectRoot, file).split(path2.sep).join("/");
7629
+ function relativeSource(projectRoot2, file) {
7630
+ return path2.relative(projectRoot2, file).split(path2.sep).join("/");
7627
7631
  }
7628
- async function linkedFileEvidence(projectRoot, code, relativePath2) {
7632
+ async function linkedFileEvidence(projectRoot2, code, relativePath2) {
7629
7633
  if (!relativePath2) return { code, satisfied: false };
7630
- const file = path2.resolve(projectRoot, relativePath2);
7634
+ const file = path2.resolve(projectRoot2, relativePath2);
7631
7635
  return {
7632
7636
  code,
7633
7637
  satisfied: await fileExists(file),
7634
- source: relativeSource(projectRoot, file)
7638
+ source: relativeSource(projectRoot2, file)
7635
7639
  };
7636
7640
  }
7637
- async function directFileEvidence(projectRoot, code, file) {
7641
+ async function directFileEvidence(projectRoot2, code, file) {
7638
7642
  return {
7639
7643
  code,
7640
7644
  satisfied: await fileExists(file),
7641
- source: relativeSource(projectRoot, file)
7645
+ source: relativeSource(projectRoot2, file)
7642
7646
  };
7643
7647
  }
7644
- async function deltaSpecEvidence(projectRoot, changeDir) {
7648
+ async function deltaSpecEvidence(projectRoot2, changeDir) {
7645
7649
  const specsDir = path2.join(changeDir, "specs");
7646
7650
  let entries;
7647
7651
  try {
@@ -7657,11 +7661,11 @@ async function deltaSpecEvidence(projectRoot, changeDir) {
7657
7661
  return {
7658
7662
  code: "openspec.delta-spec",
7659
7663
  satisfied: existing.length > 0,
7660
- source: existing[0] ? relativeSource(projectRoot, existing[0]) : void 0,
7664
+ source: existing[0] ? relativeSource(projectRoot2, existing[0]) : void 0,
7661
7665
  detail: `${existing.length} delta spec${existing.length === 1 ? "" : "s"}`
7662
7666
  };
7663
7667
  }
7664
- async function taskEvidence(projectRoot, tasksFile) {
7668
+ async function taskEvidence(projectRoot2, tasksFile) {
7665
7669
  let source;
7666
7670
  try {
7667
7671
  source = await fs2.readFile(tasksFile, "utf8");
@@ -7676,7 +7680,7 @@ async function taskEvidence(projectRoot, tasksFile) {
7676
7680
  return {
7677
7681
  code: "build.tasks-complete",
7678
7682
  satisfied: tasks.length > 0 && complete === tasks.length,
7679
- source: relativeSource(projectRoot, tasksFile),
7683
+ source: relativeSource(projectRoot2, tasksFile),
7680
7684
  detail: `${complete} of ${tasks.length} tasks complete`
7681
7685
  };
7682
7686
  }
@@ -7684,29 +7688,33 @@ function evidenceSatisfied(evidence, code) {
7684
7688
  return evidence.some((item) => item.code === code && item.satisfied);
7685
7689
  }
7686
7690
  async function collectClassicEvidence(changeDir, projection) {
7687
- const projectRoot = projectRootFor(changeDir);
7691
+ const projectRoot2 = projectRootFor(changeDir);
7688
7692
  const classic = projection.classic;
7689
7693
  const proposal = path2.join(changeDir, "proposal.md");
7690
7694
  const design = path2.join(changeDir, "design.md");
7691
7695
  const tasks = path2.join(changeDir, "tasks.md");
7692
7696
  const checkpoint = projection.run ? path2.resolve(changeDir, projection.run.checkpointRef) : path2.join(changeDir, ".comet", "checkpoint.json");
7693
7697
  const evidence = await Promise.all([
7694
- directFileEvidence(projectRoot, "openspec.proposal", proposal),
7695
- directFileEvidence(projectRoot, "openspec.design", design),
7696
- directFileEvidence(projectRoot, "openspec.tasks", tasks),
7697
- deltaSpecEvidence(projectRoot, changeDir),
7698
- linkedFileEvidence(projectRoot, "design.document", classic?.designDoc ?? null),
7699
- linkedFileEvidence(projectRoot, "build.plan", classic?.plan ?? null),
7700
- taskEvidence(projectRoot, tasks),
7701
- linkedFileEvidence(projectRoot, "verification.report", classic?.verificationReport ?? null),
7702
- linkedFileEvidence(projectRoot, "design.handoff", classic?.handoffContext ?? null),
7703
- directFileEvidence(projectRoot, "run.checkpoint", checkpoint)
7698
+ directFileEvidence(projectRoot2, "openspec.proposal", proposal),
7699
+ directFileEvidence(projectRoot2, "openspec.design", design),
7700
+ directFileEvidence(projectRoot2, "openspec.tasks", tasks),
7701
+ deltaSpecEvidence(projectRoot2, changeDir),
7702
+ linkedFileEvidence(projectRoot2, "design.document", classic?.designDoc ?? null),
7703
+ linkedFileEvidence(projectRoot2, "build.plan", classic?.plan ?? null),
7704
+ taskEvidence(projectRoot2, tasks),
7705
+ linkedFileEvidence(projectRoot2, "verification.report", classic?.verificationReport ?? null),
7706
+ linkedFileEvidence(projectRoot2, "design.handoff", classic?.handoffContext ?? null),
7707
+ directFileEvidence(projectRoot2, "run.checkpoint", checkpoint)
7704
7708
  ]);
7705
7709
  const handoff = evidence.find((item) => item.code === "design.handoff");
7706
7710
  if (handoff && !classic?.handoffHash) {
7707
7711
  handoff.satisfied = false;
7708
7712
  handoff.detail = "handoff hash is missing";
7709
7713
  }
7714
+ evidence.push({
7715
+ code: "archive.confirmed",
7716
+ satisfied: classic?.archiveConfirmation === "confirmed"
7717
+ });
7710
7718
  return evidence;
7711
7719
  }
7712
7720
 
@@ -7753,11 +7761,11 @@ function resolveVerify(profile, classic, evidence) {
7753
7761
  }
7754
7762
  return `${profile}.verify.branch`;
7755
7763
  }
7756
- function resolveArchive(profile, classic, evidence) {
7764
+ function resolveArchive(profile, classic) {
7757
7765
  if (classic.verifyResult !== "pass") {
7758
7766
  throw new Error("archive requires verify_result=pass");
7759
7767
  }
7760
- return evidenceSatisfied(evidence, "archive.confirmed") ? `${profile}.archive.execute` : `${profile}.archive.confirm`;
7768
+ return classic.archiveConfirmation === "confirmed" ? `${profile}.archive.execute` : `${profile}.archive.confirm`;
7761
7769
  }
7762
7770
  function resolveClassicStepId(classic, evidence) {
7763
7771
  const profile = profileFor(classic);
@@ -7778,7 +7786,7 @@ function resolveClassicStepId(classic, evidence) {
7778
7786
  case "verify":
7779
7787
  return resolveVerify(profile, classic, evidence);
7780
7788
  case "archive":
7781
- return resolveArchive(profile, classic, evidence);
7789
+ return resolveArchive(profile, classic);
7782
7790
  }
7783
7791
  }
7784
7792
 
@@ -7804,6 +7812,7 @@ var ISOLATIONS = ["branch", "worktree"];
7804
7812
  var VERIFY_MODES = ["light", "full"];
7805
7813
  var VERIFY_RESULTS = ["pending", "pass", "fail"];
7806
7814
  var BRANCH_STATUSES = ["pending", "handled"];
7815
+ var ARCHIVE_CONFIRMATIONS = ["pending", "confirmed"];
7807
7816
  var CLASSIC_WIRE_KEYS = [
7808
7817
  "workflow",
7809
7818
  "language",
@@ -7825,10 +7834,9 @@ var CLASSIC_WIRE_KEYS = [
7825
7834
  "branch_status",
7826
7835
  "created_at",
7827
7836
  "verified_at",
7837
+ "archive_confirmation",
7828
7838
  "archived",
7829
7839
  "direct_override",
7830
- "build_command",
7831
- "verify_command",
7832
7840
  "handoff_context",
7833
7841
  "handoff_hash",
7834
7842
  "classic_profile",
@@ -7935,10 +7943,9 @@ function classicStateFromDocument(doc) {
7935
7943
  branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
7936
7944
  createdAt: nullableString(doc, "created_at"),
7937
7945
  verifiedAt: nullableString(doc, "verified_at"),
7946
+ archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS),
7938
7947
  archived: booleanValue(doc, "archived", false),
7939
7948
  directOverride: booleanValue(doc, "direct_override"),
7940
- buildCommand: nullableString(doc, "build_command"),
7941
- verifyCommand: nullableString(doc, "verify_command"),
7942
7949
  handoffContext: relativePath(doc, "handoff_context"),
7943
7950
  handoffHash: sha256(doc, "handoff_hash"),
7944
7951
  classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES),
@@ -8000,10 +8007,9 @@ function classicStateToDocument(state) {
8000
8007
  branch_status: state.branchStatus,
8001
8008
  created_at: state.createdAt,
8002
8009
  verified_at: state.verifiedAt,
8010
+ archive_confirmation: state.archiveConfirmation,
8003
8011
  archived: state.archived,
8004
8012
  direct_override: state.directOverride,
8005
- build_command: state.buildCommand,
8006
- verify_command: state.verifyCommand,
8007
8013
  handoff_context: state.handoffContext,
8008
8014
  handoff_hash: state.handoffHash,
8009
8015
  classic_profile: state.classicProfile,
@@ -8058,6 +8064,16 @@ function stripLegacyRunFields(document) {
8058
8064
  ];
8059
8065
  for (const key of LEGACY_RUN_KEYS) document.delete(key);
8060
8066
  }
8067
+ function stripLegacyCommandFields(document) {
8068
+ let changed = false;
8069
+ for (const key of ["build_command", "verify_command"]) {
8070
+ if (document.has(key)) {
8071
+ document.delete(key);
8072
+ changed = true;
8073
+ }
8074
+ }
8075
+ return changed;
8076
+ }
8061
8077
  async function readDocument(file) {
8062
8078
  let source;
8063
8079
  try {
@@ -8073,23 +8089,29 @@ async function readDocument(file) {
8073
8089
  documentRecord(document);
8074
8090
  return document;
8075
8091
  }
8076
- async function readClassicState(changeDir) {
8077
- const document = await readDocument(path4.join(changeDir, ".comet.yaml"));
8078
- const doc = documentRecord(document);
8092
+ async function readClassicState(changeDir, options = {}) {
8093
+ const shouldMigrate = options.migrate !== false;
8094
+ const file = path4.join(changeDir, ".comet.yaml");
8095
+ const document = await readDocument(file);
8096
+ let doc = documentRecord(document);
8097
+ let migrated = stripLegacyCommandFields(document);
8098
+ if (migrated) doc = documentRecord(document);
8079
8099
  let run = await readRunState(changeDir);
8080
8100
  if (!run && doc.run_id && doc.skill) {
8081
8101
  const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports));
8082
8102
  run = runStateFromDocument2(doc);
8083
- if (run) {
8103
+ if (run && shouldMigrate) {
8084
8104
  await writeRunState(changeDir, run);
8085
8105
  stripLegacyRunFields(document);
8086
- const file = path4.join(changeDir, ".comet.yaml");
8087
- const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
8088
- await fs4.writeFile(temporary, document.toString(), "utf8");
8089
- await fs4.rename(temporary, file);
8106
+ migrated = true;
8090
8107
  }
8091
8108
  }
8092
- return parseClassicStateDocument(doc, run);
8109
+ if (migrated && shouldMigrate) {
8110
+ const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
8111
+ await fs4.writeFile(temporary, document.toString(), "utf8");
8112
+ await fs4.rename(temporary, file);
8113
+ }
8114
+ return parseClassicStateDocument(documentRecord(document), run);
8093
8115
  }
8094
8116
  async function readLegacyState(changeDir) {
8095
8117
  const document = await readDocument(path4.join(changeDir, ".comet.yaml"));
@@ -8821,13 +8843,13 @@ function artifactKey(code) {
8821
8843
  return code.replaceAll(".", "_").replaceAll("-", "_");
8822
8844
  }
8823
8845
  async function migrationArtifacts(changeDir, evidence) {
8824
- const projectRoot = projectRootFor2(changeDir);
8846
+ const projectRoot2 = projectRootFor2(changeDir);
8825
8847
  const artifacts = Object.fromEntries(
8826
8848
  evidence.filter((item) => item.satisfied && item.source).map((item) => [artifactKey(item.code), item.source])
8827
8849
  );
8828
8850
  const progress = path9.join(changeDir, "subagent-progress.md");
8829
8851
  if (await pathExists2(progress)) {
8830
- artifacts.subagent_progress = path9.relative(projectRoot, progress).split(path9.sep).join("/");
8852
+ artifacts.subagent_progress = path9.relative(projectRoot2, progress).split(path9.sep).join("/");
8831
8853
  }
8832
8854
  const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
8833
8855
  if (handoff?.source) artifacts.handoff_context = handoff.source;
@@ -8919,11 +8941,11 @@ async function ensureClassicRun(changeDir, options) {
8919
8941
  classicMigration: CLASSIC_MIGRATION_VERSION
8920
8942
  };
8921
8943
  const artifacts = await migrationArtifacts(changeDir, evidence);
8922
- const projectRoot = projectRootFor2(changeDir);
8944
+ const projectRoot2 = projectRootFor2(changeDir);
8923
8945
  const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
8924
8946
  let context = null;
8925
8947
  if (handoff?.source) {
8926
- context = await fs8.readFile(path9.resolve(projectRoot, handoff.source), "utf8");
8948
+ context = await fs8.readFile(path9.resolve(projectRoot2, handoff.source), "utf8");
8927
8949
  await writeContext(changeDir, run.contextRef, context);
8928
8950
  createdFiles.push(path9.resolve(changeDir, run.contextRef));
8929
8951
  }
@@ -8980,6 +9002,230 @@ async function fileExists2(file) {
8980
9002
  throw error;
8981
9003
  }
8982
9004
  }
9005
+ function embeddedClassicRuntimePackage(root) {
9006
+ return {
9007
+ root,
9008
+ packageKind: "runtime",
9009
+ definition: {
9010
+ apiVersion: "comet/v1alpha1",
9011
+ kind: "Skill",
9012
+ metadata: {
9013
+ name: "comet-classic",
9014
+ version: "1",
9015
+ description: "Internal compatibility orchestration for classic Comet full, hotfix, and tweak workflows"
9016
+ },
9017
+ goal: {
9018
+ statement: "Advance or restore a classic Comet Run without changing the user command surface",
9019
+ inputs: [
9020
+ {
9021
+ name: "classic-state",
9022
+ description: "Validated ClassicState consistent with the Run projection",
9023
+ required: true
9024
+ },
9025
+ {
9026
+ name: "evidence",
9027
+ description: "Structured evidence produced by the Classic Evidence collector",
9028
+ required: true
9029
+ }
9030
+ ],
9031
+ outputs: [
9032
+ {
9033
+ name: "run-state",
9034
+ description: "Atomically synchronized Classic and Run state",
9035
+ required: true
9036
+ }
9037
+ ],
9038
+ success: [
9039
+ "Legacy fields and Run fields remain consistent",
9040
+ "Every step invokes only a declared public Comet Skill",
9041
+ "The completed state passes its completion eval"
9042
+ ]
9043
+ },
9044
+ orchestration: {
9045
+ mode: "deterministic",
9046
+ entry: "full.open",
9047
+ steps: [
9048
+ {
9049
+ id: "full.open",
9050
+ action: { type: "invoke_skill", ref: "comet-open" },
9051
+ next: "full.design.handoff"
9052
+ },
9053
+ {
9054
+ id: "full.design.handoff",
9055
+ action: { type: "invoke_skill", ref: "comet-design" },
9056
+ next: "full.design.document"
9057
+ },
9058
+ {
9059
+ id: "full.design.document",
9060
+ action: { type: "invoke_skill", ref: "comet-design" },
9061
+ next: "full.build.plan"
9062
+ },
9063
+ {
9064
+ id: "full.build.plan",
9065
+ action: { type: "invoke_skill", ref: "comet-build" },
9066
+ next: "full.build.plan-ready"
9067
+ },
9068
+ {
9069
+ id: "full.build.plan-ready",
9070
+ action: { type: "invoke_skill", ref: "comet-build" },
9071
+ next: "full.build.configure"
9072
+ },
9073
+ {
9074
+ id: "full.build.configure",
9075
+ action: { type: "invoke_skill", ref: "comet-build" },
9076
+ next: "full.build.execute"
9077
+ },
9078
+ {
9079
+ id: "full.build.execute",
9080
+ action: { type: "invoke_skill", ref: "comet-build" },
9081
+ next: "full.build.complete"
9082
+ },
9083
+ {
9084
+ id: "full.build.complete",
9085
+ action: { type: "invoke_skill", ref: "comet-build" },
9086
+ next: "full.verify.run"
9087
+ },
9088
+ {
9089
+ id: "full.build.fix",
9090
+ action: { type: "invoke_skill", ref: "comet-build" },
9091
+ next: "full.build.execute"
9092
+ },
9093
+ {
9094
+ id: "full.verify.run",
9095
+ action: { type: "invoke_skill", ref: "comet-verify" },
9096
+ next: "full.verify.branch"
9097
+ },
9098
+ {
9099
+ id: "full.verify.branch",
9100
+ action: { type: "invoke_skill", ref: "comet-verify" },
9101
+ next: "full.archive.confirm"
9102
+ },
9103
+ {
9104
+ id: "full.archive.confirm",
9105
+ action: { type: "invoke_skill", ref: "comet-archive" },
9106
+ next: "full.archive.execute"
9107
+ },
9108
+ {
9109
+ id: "full.archive.execute",
9110
+ action: { type: "invoke_skill", ref: "comet-archive" },
9111
+ next: "completed"
9112
+ },
9113
+ {
9114
+ id: "hotfix.open",
9115
+ action: { type: "invoke_skill", ref: "comet-hotfix" },
9116
+ next: "hotfix.build.execute"
9117
+ },
9118
+ {
9119
+ id: "hotfix.build.execute",
9120
+ action: { type: "invoke_skill", ref: "comet-build" },
9121
+ next: "hotfix.build.complete"
9122
+ },
9123
+ {
9124
+ id: "hotfix.build.complete",
9125
+ action: { type: "invoke_skill", ref: "comet-build" },
9126
+ next: "hotfix.verify.run"
9127
+ },
9128
+ {
9129
+ id: "hotfix.verify.run",
9130
+ action: { type: "invoke_skill", ref: "comet-verify" },
9131
+ next: "hotfix.verify.branch"
9132
+ },
9133
+ {
9134
+ id: "hotfix.verify.branch",
9135
+ action: { type: "invoke_skill", ref: "comet-verify" },
9136
+ next: "hotfix.archive.confirm"
9137
+ },
9138
+ {
9139
+ id: "hotfix.archive.confirm",
9140
+ action: { type: "invoke_skill", ref: "comet-archive" },
9141
+ next: "hotfix.archive.execute"
9142
+ },
9143
+ {
9144
+ id: "hotfix.archive.execute",
9145
+ action: { type: "invoke_skill", ref: "comet-archive" },
9146
+ next: "completed"
9147
+ },
9148
+ {
9149
+ id: "tweak.open",
9150
+ action: { type: "invoke_skill", ref: "comet-tweak" },
9151
+ next: "tweak.build.execute"
9152
+ },
9153
+ {
9154
+ id: "tweak.build.execute",
9155
+ action: { type: "invoke_skill", ref: "comet-build" },
9156
+ next: "tweak.build.complete"
9157
+ },
9158
+ {
9159
+ id: "tweak.build.complete",
9160
+ action: { type: "invoke_skill", ref: "comet-build" },
9161
+ next: "tweak.verify.run"
9162
+ },
9163
+ {
9164
+ id: "tweak.verify.run",
9165
+ action: { type: "invoke_skill", ref: "comet-verify" },
9166
+ next: "tweak.verify.branch"
9167
+ },
9168
+ {
9169
+ id: "tweak.verify.branch",
9170
+ action: { type: "invoke_skill", ref: "comet-verify" },
9171
+ next: "tweak.archive.confirm"
9172
+ },
9173
+ {
9174
+ id: "tweak.archive.confirm",
9175
+ action: { type: "invoke_skill", ref: "comet-archive" },
9176
+ next: "tweak.archive.execute"
9177
+ },
9178
+ {
9179
+ id: "tweak.archive.execute",
9180
+ action: { type: "invoke_skill", ref: "comet-archive" },
9181
+ next: "completed"
9182
+ },
9183
+ {
9184
+ id: "completed",
9185
+ action: { type: "checkpoint" },
9186
+ completionEvals: ["classic-completed"]
9187
+ }
9188
+ ]
9189
+ },
9190
+ skills: [
9191
+ { id: "comet-open" },
9192
+ { id: "comet-design" },
9193
+ { id: "comet-build" },
9194
+ { id: "comet-verify" },
9195
+ { id: "comet-archive" },
9196
+ { id: "comet-hotfix" },
9197
+ { id: "comet-tweak" }
9198
+ ],
9199
+ agents: [],
9200
+ tools: []
9201
+ },
9202
+ guardrails: {
9203
+ allowedSkills: [
9204
+ "comet-open",
9205
+ "comet-design",
9206
+ "comet-build",
9207
+ "comet-verify",
9208
+ "comet-archive",
9209
+ "comet-hotfix",
9210
+ "comet-tweak"
9211
+ ],
9212
+ allowedAgents: [],
9213
+ allowedTools: [],
9214
+ maxIterations: 500,
9215
+ maxRetriesPerAction: 3,
9216
+ confirmationRequiredFor: []
9217
+ },
9218
+ evals: [
9219
+ {
9220
+ id: "classic-completed",
9221
+ scope: "completion",
9222
+ type: "state_equals",
9223
+ field: "status",
9224
+ equals: "completed"
9225
+ }
9226
+ ]
9227
+ };
9228
+ }
8983
9229
  async function classicRuntimeRoot() {
8984
9230
  const runtimeDirectory = path10.dirname(fileURLToPath(import.meta.url));
8985
9231
  const candidates = [
@@ -8996,7 +9242,7 @@ async function classicRuntimeRoot() {
8996
9242
  for (const candidate of candidates) {
8997
9243
  if (await directoryExists(candidate)) return candidate;
8998
9244
  }
8999
- throw new Error("Comet classic runtime package is not installed");
9245
+ return null;
9000
9246
  }
9001
9247
  async function loadClassicRuntimePackage(root) {
9002
9248
  if (await fileExists2(path10.join(root, "skill.yaml"))) {
@@ -9007,17 +9253,56 @@ async function loadClassicRuntimePackage(root) {
9007
9253
  async function ensureClassicRuntimeRun(changeDir) {
9008
9254
  const root = await classicRuntimeRoot();
9009
9255
  return ensureClassicRun(changeDir, {
9010
- skillPackage: await loadClassicRuntimePackage(root)
9256
+ skillPackage: root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)))
9011
9257
  });
9012
9258
  }
9013
9259
  async function ensureStrictClassicRuntimeRun(changeDir) {
9014
9260
  const projection = await readClassicState(changeDir);
9015
- if (projection.unknownKeys.length > 0) {
9261
+ const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
9262
+ if (unknownKeys.length > 0) {
9263
+ throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
9264
+ }
9265
+ return ensureClassicRuntimeRun(changeDir);
9266
+ }
9267
+ async function validateClassicRuntimeRun(changeDir, existingProjection) {
9268
+ const projection = existingProjection ?? await readClassicState(changeDir, { migrate: false });
9269
+ const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
9270
+ if (unknownKeys.length > 0) {
9271
+ throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
9272
+ }
9273
+ if (!projection.classic || !projection.run) {
9274
+ throw new Error("Classic runtime validation requires synchronized Classic and Run projections");
9275
+ }
9276
+ if (projection.classic.classicMigration !== CLASSIC_MIGRATION_VERSION) {
9277
+ throw new Error("Classic Run exists without a supported classic_migration marker");
9278
+ }
9279
+ const root = await classicRuntimeRoot();
9280
+ const skillPackage = root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)));
9281
+ if (projection.run.skill !== skillPackage.definition.metadata.name) {
9016
9282
  throw new Error(
9017
- `Invalid Classic state: unknown field(s): ${projection.unknownKeys.join(", ")}`
9283
+ `Classic Run skill mismatch: expected ${skillPackage.definition.metadata.name}, got ${projection.run.skill}`
9018
9284
  );
9019
9285
  }
9020
- return ensureClassicRuntimeRun(changeDir);
9286
+ const snapshot = await readSkillSnapshot(changeDir, projection.run.skillHash);
9287
+ if (snapshot.definition.metadata.name !== projection.run.skill) {
9288
+ throw new Error(
9289
+ `Classic Run snapshot skill mismatch: expected ${projection.run.skill}, got ${snapshot.definition.metadata.name}`
9290
+ );
9291
+ }
9292
+ const evidence = await collectClassicEvidence(changeDir, projection);
9293
+ const currentStep = resolveClassicStepId(projection.classic, evidence);
9294
+ if (projection.run.currentStep !== currentStep) {
9295
+ throw new Error(
9296
+ `Classic Run step mismatch: expected ${currentStep}, got ${projection.run.currentStep}`
9297
+ );
9298
+ }
9299
+ return {
9300
+ classic: projection.classic,
9301
+ run: projection.run,
9302
+ evidence,
9303
+ migrated: false,
9304
+ snapshotDir: path10.join(changeDir, ".comet", "skill-snapshots", projection.run.skillHash)
9305
+ };
9021
9306
  }
9022
9307
  async function transitionClassicRuntimeRun(changeDir, classic, run, data) {
9023
9308
  const projection = await readClassicState(changeDir);
@@ -9081,6 +9366,7 @@ var CLASSIC_TRANSITION_EVENTS = [
9081
9366
  "build-complete",
9082
9367
  "verify-pass",
9083
9368
  "verify-fail",
9369
+ "archive-confirm",
9084
9370
  "archive-reopen",
9085
9371
  "archived",
9086
9372
  "preset-escalate"
@@ -9111,6 +9397,11 @@ var CLASSIC_TRANSITION_TABLE = {
9111
9397
  from: "verify",
9112
9398
  guardRefs: ["verification-failed"]
9113
9399
  },
9400
+ "archive-confirm": {
9401
+ event: "archive-confirm",
9402
+ from: "archive",
9403
+ guardRefs: ["archive-final-confirmation"]
9404
+ },
9114
9405
  "archive-reopen": {
9115
9406
  event: "archive-reopen",
9116
9407
  from: "archive",
@@ -9119,7 +9410,7 @@ var CLASSIC_TRANSITION_TABLE = {
9119
9410
  archived: {
9120
9411
  event: "archived",
9121
9412
  from: "archive",
9122
- guardRefs: ["verify-result-pass"]
9413
+ guardRefs: ["verify-result-pass", "archive-confirmed"]
9123
9414
  },
9124
9415
  "preset-escalate": {
9125
9416
  event: "preset-escalate",
@@ -9167,6 +9458,7 @@ function applyClassicTransition(current, event, options = {}) {
9167
9458
  setField(classic, effects, "verifyResult", "pass");
9168
9459
  setField(classic, effects, "phase", "archive");
9169
9460
  setField(classic, effects, "verifiedAt", dateOnly(now));
9461
+ setField(classic, effects, "archiveConfirmation", "pending");
9170
9462
  } else if (event === "verify-fail") {
9171
9463
  setField(classic, effects, "verifyResult", "fail");
9172
9464
  setField(classic, effects, "phase", "build");
@@ -9180,15 +9472,25 @@ function applyClassicTransition(current, event, options = {}) {
9180
9472
  setField(classic, effects, "classicProfile", "full");
9181
9473
  setField(classic, effects, "phase", "design");
9182
9474
  setField(classic, effects, "designDoc", null);
9475
+ } else if (event === "archive-confirm") {
9476
+ if (classic.verifyResult !== "pass") {
9477
+ throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
9478
+ }
9479
+ if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
9480
+ setField(classic, effects, "archiveConfirmation", "confirmed");
9183
9481
  } else if (event === "archive-reopen") {
9184
9482
  if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
9185
9483
  setField(classic, effects, "verifyResult", "pending");
9186
9484
  setField(classic, effects, "phase", "verify");
9187
9485
  setField(classic, effects, "verifiedAt", null);
9486
+ setField(classic, effects, "archiveConfirmation", null);
9188
9487
  } else {
9189
9488
  if (classic.verifyResult !== "pass") {
9190
9489
  throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
9191
9490
  }
9491
+ if (classic.archiveConfirmation !== "confirmed") {
9492
+ throw new Error(`Cannot apply ${event}: archiveConfirmation must be confirmed`);
9493
+ }
9192
9494
  setField(classic, effects, "archived", true);
9193
9495
  }
9194
9496
  return { classic, effects, definition };
@@ -9251,6 +9553,33 @@ function artifactsHash(artifacts) {
9251
9553
  )
9252
9554
  );
9253
9555
  }
9556
+ function exactlyOneFinalNewline(markdown) {
9557
+ return `${markdown.replace(/\n+$/u, "")}
9558
+ `;
9559
+ }
9560
+ function annotatedMarkdown(original, archiveName, extraFields) {
9561
+ const normalized2 = original.replace(/\r\n/gu, "\n");
9562
+ const lines = normalized2.split("\n");
9563
+ const closingDelimiter = lines[0] === "---" ? lines.indexOf("---", 1) : -1;
9564
+ const extraFieldName = extraFields.match(/^([^:\n]+):/u)?.[1]?.trim();
9565
+ if (closingDelimiter !== -1) {
9566
+ const frontmatter = lines.slice(1, closingDelimiter).filter((line) => {
9567
+ const fieldName = line.match(/^([^:\n]+):/u)?.[1]?.trim();
9568
+ if (fieldName === void 0) return true;
9569
+ return fieldName !== "archived-with" && fieldName !== extraFieldName;
9570
+ });
9571
+ frontmatter.push(`archived-with: ${archiveName}`);
9572
+ if (extraFields) frontmatter.push(extraFields);
9573
+ return exactlyOneFinalNewline(
9574
+ ["---", ...frontmatter, "---", ...lines.slice(closingDelimiter + 1)].join("\n")
9575
+ );
9576
+ }
9577
+ const header = ["---", `archived-with: ${archiveName}`];
9578
+ if (extraFields) header.push(extraFields);
9579
+ if (extraFieldName !== "status") header.push("status: final");
9580
+ header.push("---");
9581
+ return exactlyOneFinalNewline([...header, normalized2].join("\n"));
9582
+ }
9254
9583
  async function findArchiveDir(change, preferred) {
9255
9584
  if (await exists2(preferred)) return preferred;
9256
9585
  const archiveRoot = "openspec/changes/archive";
@@ -9289,37 +9618,7 @@ async function annotateFrontmatter(output, file, archiveName, extraFields, dryRu
9289
9618
  return;
9290
9619
  }
9291
9620
  const original = await fs11.readFile(file, "utf8");
9292
- const firstLine = original.split(/\r?\n/u)[0] ?? "";
9293
- let updated;
9294
- if (firstLine === "---") {
9295
- const lines = original.split(/\r?\n/u);
9296
- const out = [];
9297
- let sawFirst = false;
9298
- for (const line of lines) {
9299
- if (/^archived-with:/u.test(line)) continue;
9300
- if (line === "---") {
9301
- if (!sawFirst) {
9302
- out.push(line);
9303
- sawFirst = true;
9304
- continue;
9305
- }
9306
- out.push(`archived-with: ${archiveName}`);
9307
- if (extraFields) out.push(extraFields);
9308
- out.push(line);
9309
- } else {
9310
- out.push(line);
9311
- }
9312
- }
9313
- updated = `${out.join("\n")}
9314
- `;
9315
- } else {
9316
- const header = ["---", `archived-with: ${archiveName}`];
9317
- if (extraFields) header.push(extraFields);
9318
- header.push("status: final", "---");
9319
- updated = `${header.join("\n")}
9320
- ${original}`;
9321
- if (!updated.endsWith("\n")) updated += "\n";
9322
- }
9621
+ const updated = annotatedMarkdown(original, archiveName, extraFields);
9323
9622
  await fs11.writeFile(file, updated);
9324
9623
  output.stderr.push(green(` [OK] Annotated: ${file}`));
9325
9624
  output.stepsOk += 1;
@@ -9404,6 +9703,13 @@ var classicArchiveCommand = async (args) => {
9404
9703
  if (runtime.run.pending && runtime.run.pending !== actionId) {
9405
9704
  throw new ArchiveFailure(red(`FATAL: another action is pending: ${runtime.run.pending}`));
9406
9705
  }
9706
+ if (!recovering && !classic.archived && classic.archiveConfirmation !== "confirmed") {
9707
+ throw new ArchiveFailure(
9708
+ red(
9709
+ `FATAL: archive_confirmation is '${classic.archiveConfirmation ?? "null"}', expected 'confirmed'. Run final archive confirmation first.`
9710
+ )
9711
+ );
9712
+ }
9407
9713
  if (!recovering) {
9408
9714
  const action = {
9409
9715
  id: actionId,
@@ -9475,7 +9781,10 @@ var classicArchiveCommand = async (args) => {
9475
9781
  archive_directory: archiveDir
9476
9782
  };
9477
9783
  await writeArtifacts(archiveDir, archivedProjection.run.artifactsRef, artifacts);
9478
- const archiveTransition = applyClassicTransition(archivedProjection.classic, "archived");
9784
+ const archiveTransition = applyClassicTransition(
9785
+ recovering && archivedProjection.classic.archiveConfirmation !== "confirmed" ? { ...archivedProjection.classic, archiveConfirmation: "confirmed" } : archivedProjection.classic,
9786
+ "archived"
9787
+ );
9479
9788
  const archivedClassic = archiveTransition.classic;
9480
9789
  let transitionedRun = archivedProjection.run;
9481
9790
  if (archivedProjection.run.currentStep !== "completed" || archivedProjection.run.status !== "completed") {
@@ -9573,11 +9882,98 @@ var classicArchiveCommand = async (args) => {
9573
9882
  };
9574
9883
 
9575
9884
  // domains/comet-classic/classic-guard.ts
9576
- var import_yaml4 = __toESM(require_dist(), 1);
9885
+ var import_yaml5 = __toESM(require_dist(), 1);
9577
9886
  import { spawnSync as spawnSync2 } from "child_process";
9578
9887
  import { createHash as createHash4 } from "crypto";
9579
- import { existsSync, promises as fs13, readFileSync } from "fs";
9580
- import path14 from "path";
9888
+ import { existsSync, promises as fs15, readFileSync } from "fs";
9889
+ import path16 from "path";
9890
+
9891
+ // domains/comet-classic/classic-command-checks.ts
9892
+ import path13 from "path";
9893
+ function validateScope(scope) {
9894
+ if (scope !== "build" && scope !== "verify") {
9895
+ throw new Error(`Invalid command check scope: '${String(scope)}'`);
9896
+ }
9897
+ }
9898
+ function projectRoot(changeDir) {
9899
+ return path13.resolve(changeDir, "..", "..", "..");
9900
+ }
9901
+ function normalizedCwd(changeDir, cwd = ".") {
9902
+ if (cwd.trim().length === 0) throw new Error("Command check cwd cannot be blank");
9903
+ const root = projectRoot(changeDir);
9904
+ const target = path13.resolve(root, cwd);
9905
+ if (target !== root && !target.startsWith(root + path13.sep)) {
9906
+ throw new Error(`Command check cwd must resolve within the project root: '${cwd}'`);
9907
+ }
9908
+ return path13.relative(root, target).replaceAll("\\", "/") || ".";
9909
+ }
9910
+ function validRecord(changeDir, event) {
9911
+ if (event.type !== "command_check_recorded") return null;
9912
+ const data = event.data;
9913
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
9914
+ const { scope, command, exitCode, cwd } = data;
9915
+ if (scope !== "build" && scope !== "verify" || typeof command !== "string" || command.trim().length === 0 || !Number.isInteger(exitCode) || typeof cwd !== "string") {
9916
+ return null;
9917
+ }
9918
+ let normalized2;
9919
+ try {
9920
+ normalized2 = normalizedCwd(changeDir, cwd);
9921
+ } catch {
9922
+ return null;
9923
+ }
9924
+ return {
9925
+ sequence: event.sequence,
9926
+ timestamp: event.timestamp,
9927
+ runId: event.runId,
9928
+ scope,
9929
+ command,
9930
+ exitCode,
9931
+ cwd: normalized2
9932
+ };
9933
+ }
9934
+ async function recordCommandCheck(changeDir, run, input) {
9935
+ validateScope(input.scope);
9936
+ if (typeof input.command !== "string" || input.command.trim().length === 0) {
9937
+ throw new Error("Command check command cannot be blank");
9938
+ }
9939
+ if (!Number.isInteger(input.exitCode)) {
9940
+ throw new Error("Command check exitCode must be an integer");
9941
+ }
9942
+ const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
9943
+ const recorded = {
9944
+ sequence: trajectory.reduce((maximum, event) => Math.max(maximum, event.sequence), 0) + 1,
9945
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9946
+ runId: run.runId,
9947
+ scope: input.scope,
9948
+ command: input.command,
9949
+ exitCode: input.exitCode,
9950
+ cwd: normalizedCwd(changeDir, input.cwd)
9951
+ };
9952
+ await appendTrajectory(changeDir, run.trajectoryRef, {
9953
+ sequence: recorded.sequence,
9954
+ timestamp: recorded.timestamp,
9955
+ type: "command_check_recorded",
9956
+ runId: recorded.runId,
9957
+ data: {
9958
+ scope: recorded.scope,
9959
+ command: recorded.command,
9960
+ exitCode: recorded.exitCode,
9961
+ cwd: recorded.cwd
9962
+ }
9963
+ });
9964
+ return recorded;
9965
+ }
9966
+ async function latestCommandCheck(changeDir, run, scope) {
9967
+ validateScope(scope);
9968
+ const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
9969
+ for (let index = trajectory.length - 1; index >= 0; index -= 1) {
9970
+ const event = trajectory[index];
9971
+ if (event.runId !== run.runId) continue;
9972
+ const record = validRecord(changeDir, event);
9973
+ if (record?.scope === scope) return record;
9974
+ }
9975
+ return null;
9976
+ }
9581
9977
 
9582
9978
  // domains/comet-classic/classic-runtime-evals.ts
9583
9979
  var STEP_EVIDENCE = {
@@ -9672,7 +10068,7 @@ async function inspectClassicChange(changeDir, name) {
9672
10068
  // domains/comet-classic/classic-validate-command.ts
9673
10069
  var import_yaml3 = __toESM(require_dist(), 1);
9674
10070
  import { promises as fs12 } from "fs";
9675
- import path13 from "path";
10071
+ import path14 from "path";
9676
10072
  var GREEN2 = "\x1B[32m";
9677
10073
  var RED2 = "\x1B[31m";
9678
10074
  var YELLOW2 = "\x1B[33m";
@@ -9704,6 +10100,7 @@ var ENUMS = {
9704
10100
  auto_transition: ["true", "false"],
9705
10101
  verify_result: ["pending", "pass", "fail"],
9706
10102
  branch_status: ["pending", "handled"],
10103
+ archive_confirmation: ["pending", "confirmed"],
9707
10104
  archived: ["true", "false"],
9708
10105
  direct_override: ["true", "false"],
9709
10106
  classic_profile: ["full", "hotfix", "tweak"],
@@ -9742,7 +10139,7 @@ var classicValidateCommand = async (args) => {
9742
10139
  };
9743
10140
  }
9744
10141
  const { directory, label } = await resolveClassicChangeDirectory(name);
9745
- const yamlFile = path13.join(directory, ".comet.yaml");
10142
+ const yamlFile = path14.join(directory, ".comet.yaml");
9746
10143
  const lines = [`[VALIDATE] ${label}/.comet.yaml`];
9747
10144
  let errors = 0;
9748
10145
  let warnings = 0;
@@ -9793,7 +10190,7 @@ var classicValidateCommand = async (args) => {
9793
10190
  }
9794
10191
  for (const field2 of ["design_doc", "plan", "handoff_context"]) {
9795
10192
  const value = text(record[field2]);
9796
- if (value && !await exists3(path13.resolve(value))) {
10193
+ if (value && !await exists3(path14.resolve(value))) {
9797
10194
  fail3(`${field2}='${value}' does not exist on disk`);
9798
10195
  }
9799
10196
  }
@@ -9815,6 +10212,62 @@ var classicValidateCommand = async (args) => {
9815
10212
  return { exitCode: 0, stderr: lines.join("\n") };
9816
10213
  };
9817
10214
 
10215
+ // domains/comet-classic/classic-project-config.ts
10216
+ var import_yaml4 = __toESM(require_dist(), 1);
10217
+ import os from "os";
10218
+ import { promises as fs14 } from "fs";
10219
+ import path15 from "path";
10220
+
10221
+ // platform/fs/file-system.ts
10222
+ import { promises as fs13 } from "fs";
10223
+ async function fileExists3(filePath) {
10224
+ try {
10225
+ await fs13.access(filePath);
10226
+ return true;
10227
+ } catch {
10228
+ return false;
10229
+ }
10230
+ }
10231
+ async function readDir(dirPath) {
10232
+ try {
10233
+ return await fs13.readdir(dirPath);
10234
+ } catch (error) {
10235
+ const code = error?.code;
10236
+ if (code === "ENOENT" || code === "ENOTDIR") {
10237
+ return [];
10238
+ }
10239
+ throw error;
10240
+ }
10241
+ }
10242
+
10243
+ // domains/comet-classic/classic-project-config.ts
10244
+ function configCandidates(options = {}) {
10245
+ const cwd = options.cwd ?? process.cwd();
10246
+ const homeDir = options.homeDir ?? os.homedir();
10247
+ const candidates = [
10248
+ { file: path15.resolve(cwd, ".comet", "config.yaml"), source: ".comet/config.yaml" },
10249
+ {
10250
+ file: path15.resolve(homeDir, ".comet", "config.yaml"),
10251
+ source: "~/.comet/config.yaml"
10252
+ }
10253
+ ];
10254
+ return candidates.filter(
10255
+ (candidate, index) => candidates.findIndex((entry2) => entry2.file === candidate.file) === index
10256
+ );
10257
+ }
10258
+ async function readClassicConfigValue(field2, options = {}) {
10259
+ for (const candidate of configCandidates(options)) {
10260
+ if (!await fileExists3(candidate.file)) continue;
10261
+ const document = (0, import_yaml4.parseDocument)(await fs14.readFile(candidate.file, "utf8"), {
10262
+ uniqueKeys: false
10263
+ });
10264
+ const value = document.get(field2);
10265
+ if (value === null || value === void 0) continue;
10266
+ return { value: String(value), source: candidate.source };
10267
+ }
10268
+ return null;
10269
+ }
10270
+
9818
10271
  // domains/comet-classic/classic-guard.ts
9819
10272
  var GREEN3 = "\x1B[32m";
9820
10273
  var RED3 = "\x1B[31m";
@@ -9879,7 +10332,7 @@ var GuardOutput = class {
9879
10332
  };
9880
10333
  async function exists4(file) {
9881
10334
  try {
9882
- await fs13.access(file);
10335
+ await fs15.access(file);
9883
10336
  return true;
9884
10337
  } catch (error) {
9885
10338
  if (error.code === "ENOENT") return false;
@@ -9888,7 +10341,7 @@ async function exists4(file) {
9888
10341
  }
9889
10342
  async function nonempty(file) {
9890
10343
  try {
9891
- return (await fs13.stat(file)).size > 0;
10344
+ return (await fs15.stat(file)).size > 0;
9892
10345
  } catch (error) {
9893
10346
  if (error.code === "ENOENT") return false;
9894
10347
  throw error;
@@ -9901,32 +10354,9 @@ function validateChangeName2(name) {
9901
10354
  async function resolveChangeDir(name) {
9902
10355
  return (await resolveClassicChangeDirectory(name)).label;
9903
10356
  }
9904
- function stripInlineComment(value) {
9905
- let out = "";
9906
- let quote = "";
9907
- for (let i = 0; i < value.length; i += 1) {
9908
- const c = value[i];
9909
- if (quote === "") {
9910
- if (c === '"' || c === "'") {
9911
- quote = c;
9912
- } else if (c === "#" && (i === 0 || /\s/u.test(value[i - 1]))) {
9913
- return out.replace(/\s+$/u, "");
9914
- }
9915
- } else if (c === quote) {
9916
- quote = "";
9917
- }
9918
- out += c;
9919
- }
9920
- return out;
9921
- }
9922
- function stripWrappingQuotes(value) {
9923
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1);
9924
- if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
9925
- return value;
9926
- }
9927
10357
  async function readField(changeDir, field2) {
9928
- const file = path14.join(changeDir, ".comet.yaml");
9929
- const document = (0, import_yaml4.parseDocument)(await fs13.readFile(file, "utf8"), { uniqueKeys: false });
10358
+ const file = path16.join(changeDir, ".comet.yaml");
10359
+ const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
9930
10360
  if (document.errors.length > 0) {
9931
10361
  throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
9932
10362
  }
@@ -9939,24 +10369,7 @@ async function readField(changeDir, field2) {
9939
10369
  async function projectConfigValue(field2, changeDir) {
9940
10370
  const changeValue = await readField(changeDir, field2);
9941
10371
  if (changeValue && changeValue !== "null") return changeValue;
9942
- for (const config of [
9943
- ".comet/config.yaml",
9944
- ".comet.yaml",
9945
- "comet.yaml",
9946
- ".comet.yml",
9947
- "comet.yml"
9948
- ]) {
9949
- if (!await exists4(config)) continue;
9950
- for (const line of (await fs13.readFile(config, "utf8")).split(/\r?\n/u)) {
9951
- if (new RegExp(`^${field2}:`, "u").test(line)) {
9952
- const value = stripWrappingQuotes(
9953
- stripInlineComment(line.replace(new RegExp(`^${field2}:\\s*`, "u"), ""))
9954
- );
9955
- if (value && value !== "null") return value;
9956
- }
9957
- }
9958
- }
9959
- return "";
10372
+ return (await readClassicConfigValue(field2))?.value ?? "";
9960
10373
  }
9961
10374
  async function configuredLanguage(changeDir) {
9962
10375
  const language = await projectConfigValue("language", changeDir);
@@ -9984,7 +10397,7 @@ function countEnglishWords(source) {
9984
10397
  }
9985
10398
  async function documentLanguageMatchesConfigured(changeDir, file) {
9986
10399
  const language = await configuredLanguage(changeDir);
9987
- const source = stripFencedCodeBlocks(await fs13.readFile(file, "utf8"));
10400
+ const source = stripFencedCodeBlocks(await fs15.readFile(file, "utf8"));
9988
10401
  const cjk = countCjkChars(source);
9989
10402
  const englishWords = countEnglishWords(source);
9990
10403
  if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
@@ -10001,68 +10414,6 @@ Next: regenerate or rewrite this artifact in English while preserving necessary
10001
10414
  }
10002
10415
  return pass();
10003
10416
  }
10004
- function runCommandString(command) {
10005
- if (!command) return { status: 1, output: red2("ERROR: build/verify command is empty") };
10006
- const split = splitCommandChain(command);
10007
- if (typeof split === "string") {
10008
- return {
10009
- status: 1,
10010
- output: `${red2(`ERROR: build/verify command contains shell metacharacters: ${command}`)}
10011
- ${red2(
10012
- split
10013
- )}`
10014
- };
10015
- }
10016
- const output = [];
10017
- for (const part of split) {
10018
- const segment = part.trim();
10019
- if (!segment) {
10020
- return { status: 1, output: red2("ERROR: build/verify command contains an empty && step") };
10021
- }
10022
- const result3 = spawnSync2(segment, { shell: true, encoding: "utf8", timeout: 3e5 });
10023
- const combined = `${result3.stdout ?? ""}${result3.stderr ?? ""}`.replace(/\n+$/u, "");
10024
- output.push(`${red2(`+ ${segment}`)}${combined ? `
10025
- ${combined}` : ""}`);
10026
- if (result3.status !== 0) {
10027
- return { status: result3.status ?? 1, output: output.join("\n") };
10028
- }
10029
- }
10030
- return { status: 0, output: output.join("\n") };
10031
- }
10032
- function splitCommandChain(command) {
10033
- const parts = [];
10034
- let current = "";
10035
- let quote = "";
10036
- for (let i = 0; i < command.length; i += 1) {
10037
- const c = command[i];
10038
- if (c === "$" || c === "`") {
10039
- return "Allowed: command words, quotes, paths, and && between sequential commands";
10040
- }
10041
- if (quote) {
10042
- current += c;
10043
- if (c === quote) quote = "";
10044
- continue;
10045
- }
10046
- if (c === '"' || c === "'") {
10047
- quote = c;
10048
- current += c;
10049
- continue;
10050
- }
10051
- if (c === "&" && command[i + 1] === "&") {
10052
- parts.push(current);
10053
- current = "";
10054
- i += 1;
10055
- continue;
10056
- }
10057
- if (c === ";" || c === "|" || c === "&") {
10058
- return "Allowed: command words, quotes, paths, and && between sequential commands";
10059
- }
10060
- current += c;
10061
- }
10062
- if (quote) return "Command has an unmatched quote";
10063
- parts.push(current);
10064
- return parts;
10065
- }
10066
10417
  function hashFile(file) {
10067
10418
  return createHash4("sha256").update(readFileSync(file)).digest("hex");
10068
10419
  }
@@ -10070,7 +10421,7 @@ async function handoffSourceFiles(changeDir) {
10070
10421
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
10071
10422
  const specs = `${changeDir}/specs`;
10072
10423
  if (await exists4(specs)) {
10073
- for (const entry2 of (await fs13.readdir(specs)).sort()) {
10424
+ for (const entry2 of (await fs15.readdir(specs)).sort()) {
10074
10425
  const spec = `${specs}/${entry2}/spec.md`;
10075
10426
  if (await exists4(spec)) files.push(spec);
10076
10427
  }
@@ -10090,20 +10441,30 @@ async function preflight(changeDir, name) {
10090
10441
  if (!await exists4(changeDir)) {
10091
10442
  throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
10092
10443
  }
10093
- if (!await exists4(path14.join(changeDir, ".comet.yaml"))) {
10444
+ if (!await exists4(path16.join(changeDir, ".comet.yaml"))) {
10094
10445
  throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
10095
10446
  }
10096
- const result3 = await classicValidateCommand([name], { json: false });
10097
- if (result3.exitCode !== 0) {
10098
- if (result3.stderr)
10099
- process.stderr.write(result3.stderr.endsWith("\n") ? result3.stderr : `${result3.stderr}
10447
+ const result5 = await classicValidateCommand([name], { json: false });
10448
+ if (result5.exitCode !== 0) {
10449
+ if (result5.stderr)
10450
+ process.stderr.write(result5.stderr.endsWith("\n") ? result5.stderr : `${result5.stderr}
10100
10451
  `);
10101
10452
  throw new GuardFailure(red2("FATAL: .comet.yaml schema validation failed"));
10102
10453
  }
10454
+ const projection = await readClassicState(changeDir);
10455
+ const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
10456
+ if (unknownKeys.length > 0) {
10457
+ throw new GuardFailure(
10458
+ red2(`FATAL: .comet.yaml has unknown field(s): ${unknownKeys.join(", ")}`)
10459
+ );
10460
+ }
10103
10461
  }
10104
10462
  function pushCheck(output, outcome) {
10105
10463
  if (outcome.passed) {
10106
10464
  output.stderr.push(green2(` [PASS] ${outcome.description}`));
10465
+ if (outcome.detail) {
10466
+ for (const line of outcome.detail.split("\n")) output.stderr.push(green2(` ${line}`));
10467
+ }
10107
10468
  } else {
10108
10469
  output.stderr.push(red2(` [FAIL] ${outcome.description}`));
10109
10470
  if (outcome.detail) {
@@ -10114,11 +10475,11 @@ function pushCheck(output, outcome) {
10114
10475
  function check(description, run) {
10115
10476
  return async () => {
10116
10477
  try {
10117
- const result3 = await run();
10478
+ const result5 = await run();
10118
10479
  return {
10119
10480
  description,
10120
- passed: result3.passed,
10121
- detail: ("detail" in result3 ? result3.detail : "") ?? ""
10481
+ passed: result5.passed,
10482
+ detail: ("detail" in result5 ? result5.detail : "") ?? ""
10122
10483
  };
10123
10484
  } catch (error) {
10124
10485
  return {
@@ -10129,8 +10490,8 @@ function check(description, run) {
10129
10490
  }
10130
10491
  };
10131
10492
  }
10132
- function pass() {
10133
- return { passed: true };
10493
+ function pass(detail) {
10494
+ return { passed: true, ...detail ? { detail } : {} };
10134
10495
  }
10135
10496
  function fail(detail) {
10136
10497
  return { passed: false, detail };
@@ -10144,46 +10505,103 @@ async function runChecks(output, builders) {
10144
10505
  }
10145
10506
  return blocked2;
10146
10507
  }
10508
+ var INFERRED_COMMAND_SOURCES = [
10509
+ "package.json with a build script",
10510
+ "pom.xml",
10511
+ "Cargo.toml"
10512
+ ];
10513
+ async function removedProjectCommandField(field2) {
10514
+ const config = path16.join(".comet", "config.yaml");
10515
+ if (!await exists4(config)) return false;
10516
+ const document = (0, import_yaml5.parseDocument)(await fs15.readFile(config, "utf8"));
10517
+ if (document.errors.length > 0) {
10518
+ throw new Error(
10519
+ `.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
10520
+ );
10521
+ }
10522
+ const value = document.toJS();
10523
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, field2);
10524
+ }
10525
+ function removedProjectCommandRun(field2) {
10526
+ return {
10527
+ status: 1,
10528
+ output: `${field2} has been removed from .comet/config.yaml. Delete this field and run any required ${field2 === "build_command" ? "build" : "verification"} command manually before retrying.`
10529
+ };
10530
+ }
10147
10531
  function runInferred(command) {
10148
- const result3 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
10532
+ const result5 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
10149
10533
  return {
10150
- status: result3.status ?? 1,
10151
- output: `${result3.stdout ?? ""}${result3.stderr ?? ""}`.replace(/\n+$/u, "")
10534
+ status: result5.status ?? 1,
10535
+ output: `${result5.stdout ?? ""}${result5.stderr ?? ""}`.replace(/\n+$/u, "")
10152
10536
  };
10153
10537
  }
10154
- async function buildPasses(changeDir) {
10155
- if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
10156
- const configured = await projectConfigValue("build_command", changeDir);
10157
- if (configured) return runCommandString(configured);
10158
- if (await exists4("package.json") && /"build"/u.test(await fs13.readFile("package.json", "utf8"))) {
10159
- return runInferred("npm run build");
10538
+ async function inferredBuildCommand() {
10539
+ if (await exists4("package.json") && (() => {
10540
+ const parsed = JSON.parse(readFileSync("package.json", "utf8"));
10541
+ return typeof parsed.scripts?.build === "string";
10542
+ })()) {
10543
+ return "npm run build";
10160
10544
  }
10161
10545
  if (await exists4("pom.xml")) {
10162
10546
  if (process.platform === "win32") {
10163
- if (existsSync("mvnw.cmd")) return runInferred("mvnw.cmd compile -q");
10164
- return runInferred("mvn.cmd compile -q");
10547
+ if (existsSync("mvnw.cmd")) return "mvnw.cmd compile -q";
10548
+ return "mvn.cmd compile -q";
10165
10549
  }
10166
- if (existsSync("mvnw")) return runInferred("./mvnw compile -q");
10167
- return runInferred("mvn compile -q");
10550
+ if (existsSync("mvnw")) return "./mvnw compile -q";
10551
+ return "mvn compile -q";
10168
10552
  }
10169
- if (await exists4("Cargo.toml")) return runInferred("cargo build");
10170
- return { status: 1, output: "" };
10553
+ if (await exists4("Cargo.toml")) return "cargo build";
10554
+ return null;
10555
+ }
10556
+ function evidenceDetail(record) {
10557
+ return `Evidence: recorded command-check at ${record.timestamp}; command: ${record.command}; cwd: ${record.cwd}`;
10171
10558
  }
10172
- async function verificationCommandPasses(changeDir) {
10173
- if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
10174
- const configured = await projectConfigValue("verify_command", changeDir);
10175
- if (configured) return runCommandString(configured);
10176
- return buildPasses(changeDir);
10559
+ function recoveryCommand(change, scope, command) {
10560
+ return `comet state record-check ${change} ${scope} --command "${command}" --exit-code 0`;
10561
+ }
10562
+ async function commandCheckPasses(changeDir, change, run, scope) {
10563
+ if (process.env.COMET_SKIP_BUILD === "1") {
10564
+ return { status: 0, output: "SKIPPED via COMET_SKIP_BUILD=1" };
10565
+ }
10566
+ const removedFields = scope === "build" ? ["build_command"] : ["verify_command", "build_command"];
10567
+ for (const removedField of removedFields) {
10568
+ if (await removedProjectCommandField(removedField)) {
10569
+ return removedProjectCommandRun(removedField);
10570
+ }
10571
+ }
10572
+ const inferred = scope === "build" ? await inferredBuildCommand() : null;
10573
+ if (inferred) return runInferred(inferred);
10574
+ const recorded = await latestCommandCheck(changeDir, run, scope);
10575
+ if (!recorded) {
10576
+ return {
10577
+ status: 1,
10578
+ output: scope === "build" ? `No inferred build command or recorded build check. Detection searched: ${INFERRED_COMMAND_SOURCES.join(", ")}.
10579
+ Next: run the required command, then record it with:
10580
+ ${recoveryCommand(change, scope, "<command>")}` : `No recorded verify check.
10581
+ Next: run the required verification command, then record it with:
10582
+ ${recoveryCommand(change, scope, "<command>")}`
10583
+ };
10584
+ }
10585
+ if (recorded.exitCode !== 0) {
10586
+ return {
10587
+ status: recorded.exitCode,
10588
+ output: `Latest recorded ${scope} check failed with exit code ${recorded.exitCode}.
10589
+ ${evidenceDetail(recorded)}
10590
+ Next: rerun the command successfully, then record it with:
10591
+ ${recoveryCommand(change, scope, recorded.command)}`
10592
+ };
10593
+ }
10594
+ return { status: 0, output: evidenceDetail(recorded) };
10177
10595
  }
10178
10596
  async function tasksAllDone(changeDir) {
10179
- const tasks = path14.join(changeDir, "tasks.md");
10597
+ const tasks = path16.join(changeDir, "tasks.md");
10180
10598
  if (!await exists4(tasks)) {
10181
10599
  return fail(
10182
10600
  `tasks.md is missing at ${tasks}
10183
10601
  Next: restore or create tasks.md for this change before leaving build.`
10184
10602
  );
10185
10603
  }
10186
- const source = await fs13.readFile(tasks, "utf8");
10604
+ const source = await fs15.readFile(tasks, "utf8");
10187
10605
  if (!/- \[x\]/u.test(source)) {
10188
10606
  return fail(
10189
10607
  "tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
@@ -10200,9 +10618,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
10200
10618
  return pass();
10201
10619
  }
10202
10620
  async function tasksHasAny(changeDir) {
10203
- const tasks = path14.join(changeDir, "tasks.md");
10621
+ const tasks = path16.join(changeDir, "tasks.md");
10204
10622
  if (!await exists4(tasks)) return false;
10205
- return /- \[/u.test(await fs13.readFile(tasks, "utf8"));
10623
+ return /- \[/u.test(await fs15.readFile(tasks, "utf8"));
10206
10624
  }
10207
10625
  async function planTasksAllDone(changeDir) {
10208
10626
  const plan = await readField(changeDir, "plan");
@@ -10213,7 +10631,7 @@ async function planTasksAllDone(changeDir) {
10213
10631
  Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
10214
10632
  );
10215
10633
  }
10216
- const source = await fs13.readFile(plan, "utf8");
10634
+ const source = await fs15.readFile(plan, "utf8");
10217
10635
  const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
10218
10636
  if (unfinished.length > 0) {
10219
10637
  return fail(
@@ -10302,7 +10720,7 @@ async function archivedIsTrue(changeDir) {
10302
10720
  return await readField(changeDir, "archived") === "true";
10303
10721
  }
10304
10722
  async function designDocFrontmatterHas(designDoc, field2, expected) {
10305
- const source = (await fs13.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
10723
+ const source = (await fs15.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
10306
10724
  let inFrontmatter = false;
10307
10725
  for (const line of source.split(/\r?\n/u)) {
10308
10726
  if (!inFrontmatter) {
@@ -10367,7 +10785,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
10367
10785
  const markdown = `${context.replace(/\.json$/u, "")}.md`;
10368
10786
  if (!await nonempty(markdown))
10369
10787
  return fail(`design handoff markdown is missing or empty: ${markdown}`);
10370
- const source = await fs13.readFile(markdown, "utf8");
10788
+ const source = await fs15.readFile(markdown, "utf8");
10371
10789
  const problems = [];
10372
10790
  if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
10373
10791
  problems.push("handoff markdown is missing Generated-by marker");
@@ -10394,7 +10812,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
10394
10812
  const context = await readField(changeDir, "handoff_context");
10395
10813
  if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
10396
10814
  if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
10397
- const source = await fs13.readFile(context, "utf8");
10815
+ const source = await fs15.readFile(context, "utf8");
10398
10816
  const problems = [];
10399
10817
  let parsed;
10400
10818
  try {
@@ -10431,19 +10849,19 @@ async function guardOpenChecks(output, changeDir) {
10431
10849
  const checks = [
10432
10850
  check(
10433
10851
  "proposal.md exists and non-empty",
10434
- async () => await nonempty(path14.join(changeDir, "proposal.md")) ? pass() : fail("")
10852
+ async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
10435
10853
  ),
10436
10854
  check(
10437
10855
  "proposal.md matches configured language",
10438
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "proposal.md"))
10856
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
10439
10857
  ),
10440
10858
  check(
10441
10859
  "tasks.md exists and non-empty",
10442
- async () => await nonempty(path14.join(changeDir, "tasks.md")) ? pass() : fail("")
10860
+ async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
10443
10861
  ),
10444
10862
  check(
10445
10863
  "tasks.md matches configured language",
10446
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "tasks.md"))
10864
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
10447
10865
  ),
10448
10866
  check(
10449
10867
  "tasks.md has at least one task",
@@ -10456,11 +10874,11 @@ async function guardOpenChecks(output, changeDir) {
10456
10874
  0,
10457
10875
  check(
10458
10876
  "design.md exists and non-empty",
10459
- async () => await nonempty(path14.join(changeDir, "design.md")) ? pass() : fail("")
10877
+ async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
10460
10878
  ),
10461
10879
  check(
10462
10880
  "design.md matches configured language",
10463
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "design.md"))
10881
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
10464
10882
  )
10465
10883
  );
10466
10884
  }
@@ -10472,27 +10890,27 @@ async function guardDesignChecks(output, changeDir, change) {
10472
10890
  const builders = [
10473
10891
  check(
10474
10892
  "proposal.md exists",
10475
- async () => await nonempty(path14.join(changeDir, "proposal.md")) ? pass() : fail("")
10893
+ async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
10476
10894
  ),
10477
10895
  check(
10478
10896
  "proposal.md matches configured language",
10479
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "proposal.md"))
10897
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
10480
10898
  ),
10481
10899
  check(
10482
10900
  "design.md exists",
10483
- async () => await nonempty(path14.join(changeDir, "design.md")) ? pass() : fail("")
10901
+ async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
10484
10902
  ),
10485
10903
  check(
10486
10904
  "design.md matches configured language",
10487
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "design.md"))
10905
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
10488
10906
  ),
10489
10907
  check(
10490
10908
  "tasks.md exists",
10491
- async () => await nonempty(path14.join(changeDir, "tasks.md")) ? pass() : fail("")
10909
+ async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
10492
10910
  ),
10493
10911
  check(
10494
10912
  "tasks.md matches configured language",
10495
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "tasks.md"))
10913
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
10496
10914
  ),
10497
10915
  check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
10498
10916
  check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
@@ -10541,7 +10959,7 @@ async function guardDesignChecks(output, changeDir, change) {
10541
10959
  }
10542
10960
  return blocked2;
10543
10961
  }
10544
- async function guardBuildChecks(output, changeDir, change) {
10962
+ async function guardBuildChecks(output, changeDir, change, run) {
10545
10963
  return runChecks(output, [
10546
10964
  check("isolation selected", () => isolationSelected(changeDir, change)),
10547
10965
  check("build_mode selected", () => buildModeSelected(changeDir, change)),
@@ -10553,11 +10971,11 @@ async function guardBuildChecks(output, changeDir, change) {
10553
10971
  check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
10554
10972
  check(
10555
10973
  "proposal.md exists",
10556
- async () => await nonempty(path14.join(changeDir, "proposal.md")) ? pass() : fail("")
10974
+ async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
10557
10975
  ),
10558
10976
  check(
10559
10977
  "proposal.md matches configured language",
10560
- () => documentLanguageMatchesConfigured(changeDir, path14.join(changeDir, "proposal.md"))
10978
+ () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
10561
10979
  ),
10562
10980
  check("Superpowers plan matches configured language", async () => {
10563
10981
  const plan = await readField(changeDir, "plan");
@@ -10567,19 +10985,19 @@ async function guardBuildChecks(output, changeDir, change) {
10567
10985
  // Build check runs last — only after all config checks pass — to avoid
10568
10986
  // wasting time on a build that would be rejected by a config failure.
10569
10987
  check("Build passes", async () => {
10570
- const buildResult = await buildPasses(changeDir);
10571
- return buildResult.status === 0 ? pass() : fail(buildResult.output);
10988
+ const buildResult = await commandCheckPasses(changeDir, change, run, "build");
10989
+ return buildResult.status === 0 ? pass(buildResult.output) : fail(buildResult.output);
10572
10990
  })
10573
10991
  ]);
10574
10992
  }
10575
- async function guardVerifyChecks(output, changeDir) {
10993
+ async function guardVerifyChecks(output, changeDir, change, run) {
10576
10994
  return runChecks(output, [
10577
10995
  check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
10578
10996
  // Verification command runs after tasks check — no point running tests
10579
10997
  // if tasks.md is incomplete.
10580
10998
  check("Verification passes", async () => {
10581
- const verifyResult = await verificationCommandPasses(changeDir);
10582
- return verifyResult.status === 0 ? pass() : fail(verifyResult.output);
10999
+ const verifyResult = await commandCheckPasses(changeDir, change, run, "verify");
11000
+ return verifyResult.status === 0 ? pass(verifyResult.output) : fail(verifyResult.output);
10583
11001
  }),
10584
11002
  check(
10585
11003
  "verification_report exists",
@@ -10601,11 +11019,11 @@ async function guardArchiveChecks(output, changeDir) {
10601
11019
  check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
10602
11020
  check(
10603
11021
  "proposal.md exists",
10604
- async () => await nonempty(path14.join(changeDir, "proposal.md")) ? pass() : fail("")
11022
+ async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
10605
11023
  ),
10606
11024
  check(
10607
11025
  "design.md exists",
10608
- async () => await nonempty(path14.join(changeDir, "design.md")) ? pass() : fail("")
11026
+ async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
10609
11027
  ),
10610
11028
  check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
10611
11029
  ]);
@@ -10613,8 +11031,8 @@ async function guardArchiveChecks(output, changeDir) {
10613
11031
  async function applyStateUpdate(output, change, changeDir, phase, context) {
10614
11032
  const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
10615
11033
  if (!event) return;
10616
- const result3 = applyClassicTransition(context.classic, event);
10617
- await transitionClassicRuntimeRun(changeDir, result3.classic, context.run, {
11034
+ const result5 = applyClassicTransition(context.classic, event);
11035
+ await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
10618
11036
  event,
10619
11037
  phase,
10620
11038
  source: "comet-guard"
@@ -10624,15 +11042,15 @@ async function applyStateUpdate(output, change, changeDir, phase, context) {
10624
11042
  event,
10625
11043
  source: "comet-guard",
10626
11044
  from: context.classic,
10627
- to: result3.classic,
10628
- effects: result3.effects
11045
+ to: result5.classic,
11046
+ effects: result5.effects
10629
11047
  });
10630
- for (const effect of result3.effects) {
11048
+ for (const effect of result5.effects) {
10631
11049
  output.stderr.push(green2(`[SET] ${wireField(effect.field)}=${wireValue(effect.to)}`));
10632
11050
  }
10633
11051
  output.stderr.push(green2(`[TRANSITION] ${event}`));
10634
11052
  const template = APPLY_MESSAGE[phase];
10635
- const message = phase === "open" ? template.replace("PLACEHOLDER", result3.classic.phase) : template;
11053
+ const message = phase === "open" ? template.replace("PLACEHOLDER", result5.classic.phase) : template;
10636
11054
  output.stderr.push(green2(message));
10637
11055
  }
10638
11056
  var classicGuardCommand = async (args, options) => {
@@ -10662,8 +11080,10 @@ Valid phases: open, design, build, verify, archive`
10662
11080
  let blocked2;
10663
11081
  if (phase === "open") blocked2 = await guardOpenChecks(output, changeDir);
10664
11082
  else if (phase === "design") blocked2 = await guardDesignChecks(output, changeDir, change);
10665
- else if (phase === "build") blocked2 = await guardBuildChecks(output, changeDir, change);
10666
- else if (phase === "verify") blocked2 = await guardVerifyChecks(output, changeDir);
11083
+ else if (phase === "build")
11084
+ blocked2 = await guardBuildChecks(output, changeDir, change, runContext.run);
11085
+ else if (phase === "verify")
11086
+ blocked2 = await guardVerifyChecks(output, changeDir, change, runContext.run);
10667
11087
  else blocked2 = await guardArchiveChecks(output, changeDir);
10668
11088
  if (blocked2) {
10669
11089
  output.stderr.push("");
@@ -10686,10 +11106,10 @@ Valid phases: open, design, build, verify, archive`
10686
11106
  };
10687
11107
 
10688
11108
  // domains/comet-classic/classic-handoff.ts
10689
- var import_yaml5 = __toESM(require_dist(), 1);
11109
+ var import_yaml6 = __toESM(require_dist(), 1);
10690
11110
  import { createHash as createHash5 } from "crypto";
10691
- import { promises as fs14, readFileSync as readFileSync2 } from "fs";
10692
- import path15 from "path";
11111
+ import { promises as fs16, readFileSync as readFileSync2 } from "fs";
11112
+ import path17 from "path";
10693
11113
  var GREEN4 = "\x1B[32m";
10694
11114
  var RED4 = "\x1B[31m";
10695
11115
  var YELLOW4 = "\x1B[33m";
@@ -10723,7 +11143,7 @@ var HandoffOutput = class {
10723
11143
  };
10724
11144
  async function exists5(file) {
10725
11145
  try {
10726
- await fs14.access(file);
11146
+ await fs16.access(file);
10727
11147
  return true;
10728
11148
  } catch (error) {
10729
11149
  if (error.code === "ENOENT") return false;
@@ -10732,7 +11152,7 @@ async function exists5(file) {
10732
11152
  }
10733
11153
  async function nonempty2(file) {
10734
11154
  try {
10735
- return (await fs14.stat(file)).size > 0;
11155
+ return (await fs16.stat(file)).size > 0;
10736
11156
  } catch (error) {
10737
11157
  if (error.code === "ENOENT") return false;
10738
11158
  throw error;
@@ -10761,7 +11181,7 @@ async function handoffSourceFiles2(changeDir) {
10761
11181
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
10762
11182
  const specs = `${changeDir}/specs`;
10763
11183
  if (await exists5(specs)) {
10764
- for (const entry2 of (await fs14.readdir(specs)).sort()) {
11184
+ for (const entry2 of (await fs16.readdir(specs)).sort()) {
10765
11185
  const spec = `${specs}/${entry2}/spec.md`;
10766
11186
  if (await exists5(spec)) files.push(spec);
10767
11187
  }
@@ -10809,7 +11229,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
10809
11229
  ];
10810
11230
  for (const file of await handoffSourceFiles2(changeDir)) {
10811
11231
  if (!await exists5(file)) continue;
10812
- const content = await fs14.readFile(file, "utf8");
11232
+ const content = await fs16.readFile(file, "utf8");
10813
11233
  const total = lineCount(content);
10814
11234
  lines.push(
10815
11235
  `## ${file}`,
@@ -10834,7 +11254,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
10834
11254
  }
10835
11255
  lines.push("");
10836
11256
  }
10837
- await fs14.writeFile(output, lines.join("\n"));
11257
+ await fs16.writeFile(output, lines.join("\n"));
10838
11258
  }
10839
11259
  async function writeJsonContext(changeDir, change, mode, contextHash, output) {
10840
11260
  const entries = [];
@@ -10857,7 +11277,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
10857
11277
  "}",
10858
11278
  ""
10859
11279
  ].join("\n");
10860
- await fs14.writeFile(output, document);
11280
+ await fs16.writeFile(output, document);
10861
11281
  }
10862
11282
  async function writeSpecProjectionForFile(file, content) {
10863
11283
  return [
@@ -10897,11 +11317,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
10897
11317
  const specs = `${changeDir}/specs`;
10898
11318
  let projected = false;
10899
11319
  if (await exists5(specs)) {
10900
- for (const entry2 of (await fs14.readdir(specs)).sort()) {
11320
+ for (const entry2 of (await fs16.readdir(specs)).sort()) {
10901
11321
  const spec = `${specs}/${entry2}/spec.md`;
10902
11322
  if (!await exists5(spec)) continue;
10903
11323
  projected = true;
10904
- lines.push(...await writeSpecProjectionForFile(spec, await fs14.readFile(spec, "utf8")));
11324
+ lines.push(...await writeSpecProjectionForFile(spec, await fs16.readFile(spec, "utf8")));
10905
11325
  }
10906
11326
  }
10907
11327
  if (!projected) {
@@ -10910,7 +11330,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
10910
11330
  lines.push(
10911
11331
  "Full source files remain canonical. If a required heading or scenario is missing here, regenerate the handoff or read the source spec directly. Supporting files (proposal, design, tasks) are referenced by hash only."
10912
11332
  );
10913
- await fs14.writeFile(output, lines.join("\n"));
11333
+ await fs16.writeFile(output, lines.join("\n"));
10914
11334
  }
10915
11335
  async function writeSpecJsonContext(changeDir, change, contextHash, output) {
10916
11336
  const entries = [];
@@ -10919,7 +11339,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
10919
11339
  const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
10920
11340
  entries.push({ path: file, sha256: hashFile2(file), role });
10921
11341
  }
10922
- await fs14.writeFile(
11342
+ await fs16.writeFile(
10923
11343
  output,
10924
11344
  `${JSON.stringify(
10925
11345
  {
@@ -10938,8 +11358,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
10938
11358
  );
10939
11359
  }
10940
11360
  async function readField2(changeDir, field2) {
10941
- const file = path15.join(changeDir, ".comet.yaml");
10942
- const document = (0, import_yaml5.parseDocument)(await fs14.readFile(file, "utf8"), { uniqueKeys: false });
11361
+ const file = path17.join(changeDir, ".comet.yaml");
11362
+ const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
10943
11363
  if (document.errors.length > 0) {
10944
11364
  throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
10945
11365
  }
@@ -10973,7 +11393,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
10973
11393
  readCheckpoint(changeDir, run.checkpointRef)
10974
11394
  ]);
10975
11395
  if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
10976
- if (context !== await fs14.readFile(contextMd, "utf8")) return false;
11396
+ if (context !== await fs16.readFile(contextMd, "utf8")) return false;
10977
11397
  if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
10978
11398
  return false;
10979
11399
  }
@@ -11096,7 +11516,7 @@ var classicHandoffCommand = async (args) => {
11096
11516
  run: pendingRun,
11097
11517
  unknownKeys: (await readClassicState(changeDir)).unknownKeys
11098
11518
  });
11099
- await fs14.mkdir(handoffDir, { recursive: true });
11519
+ await fs16.mkdir(handoffDir, { recursive: true });
11100
11520
  if (handoffMode === "beta") {
11101
11521
  await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
11102
11522
  await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
@@ -11104,7 +11524,7 @@ var classicHandoffCommand = async (args) => {
11104
11524
  await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
11105
11525
  await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
11106
11526
  }
11107
- const context = await fs14.readFile(contextMd, "utf8");
11527
+ const context = await fs16.readFile(contextMd, "utf8");
11108
11528
  await writeContext(changeDir, pendingRun.contextRef, context);
11109
11529
  const artifacts = {
11110
11530
  ...await readArtifacts(changeDir, pendingRun.artifactsRef),
@@ -11161,10 +11581,142 @@ var classicHandoffCommand = async (args) => {
11161
11581
  };
11162
11582
 
11163
11583
  // domains/comet-classic/classic-hook-guard.ts
11164
- import { existsSync as existsSync2, promises as fs15, readFileSync as readFileSync3 } from "fs";
11165
- import path16 from "path";
11166
- function result(exitCode, message) {
11167
- return { exitCode, stderr: message + "\n" };
11584
+ import { existsSync as existsSync2, promises as fs18, readFileSync as readFileSync3 } from "fs";
11585
+ import path19 from "path";
11586
+
11587
+ // domains/comet-classic/classic-current-change.ts
11588
+ import { execFileSync } from "child_process";
11589
+ import { randomUUID as randomUUID6 } from "crypto";
11590
+ import { promises as fs17 } from "fs";
11591
+ import path18 from "path";
11592
+ function currentChangeFile(projectRoot2) {
11593
+ return path18.join(projectRoot2, ".comet", "current-change.json");
11594
+ }
11595
+ function currentBranch(projectRoot2) {
11596
+ try {
11597
+ const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
11598
+ cwd: projectRoot2,
11599
+ encoding: "utf8",
11600
+ stdio: ["ignore", "pipe", "ignore"]
11601
+ }).trim();
11602
+ return branch && branch !== "HEAD" ? branch : null;
11603
+ } catch {
11604
+ return null;
11605
+ }
11606
+ }
11607
+ function changeDirectory(projectRoot2, changeName) {
11608
+ return path18.join(projectRoot2, "openspec", "changes", changeName);
11609
+ }
11610
+ async function validateActiveChange(projectRoot2, changeName) {
11611
+ assertOpenSpecChangeName(changeName);
11612
+ const changeDir = changeDirectory(projectRoot2, changeName);
11613
+ try {
11614
+ await fs17.access(path18.join(changeDir, ".comet.yaml"));
11615
+ } catch (error) {
11616
+ if (error.code === "ENOENT") {
11617
+ throw new Error(
11618
+ `Cannot select current change '${changeName}': active change state not found`,
11619
+ {
11620
+ cause: error
11621
+ }
11622
+ );
11623
+ }
11624
+ throw error;
11625
+ }
11626
+ const projection = await readClassicState(changeDir, { migrate: false });
11627
+ if (!projection.classic) {
11628
+ throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`);
11629
+ }
11630
+ if (projection.classic.archived) {
11631
+ throw new Error(`Cannot select current change '${changeName}': change is archived`);
11632
+ }
11633
+ }
11634
+ function parseSelection(source) {
11635
+ let value;
11636
+ try {
11637
+ value = JSON.parse(source);
11638
+ } catch (error) {
11639
+ throw new Error(
11640
+ `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
11641
+ { cause: error }
11642
+ );
11643
+ }
11644
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
11645
+ throw new Error("current change selection must be a JSON object");
11646
+ }
11647
+ const record = value;
11648
+ if (record.version !== 1) {
11649
+ throw new Error("current change selection version must be 1");
11650
+ }
11651
+ if (typeof record.change !== "string") {
11652
+ throw new Error("current change selection change must be a string");
11653
+ }
11654
+ assertOpenSpecChangeName(record.change);
11655
+ if (record.branch !== null && typeof record.branch !== "string") {
11656
+ throw new Error("current change selection branch must be a string or null");
11657
+ }
11658
+ return {
11659
+ version: 1,
11660
+ change: record.change,
11661
+ branch: record.branch
11662
+ };
11663
+ }
11664
+ async function selectCurrentChange(projectRoot2, changeName) {
11665
+ await validateActiveChange(projectRoot2, changeName);
11666
+ const selection = {
11667
+ version: 1,
11668
+ change: changeName,
11669
+ branch: currentBranch(projectRoot2)
11670
+ };
11671
+ const file = currentChangeFile(projectRoot2);
11672
+ const temporary = `${file}.${randomUUID6()}.tmp`;
11673
+ await fs17.mkdir(path18.dirname(file), { recursive: true });
11674
+ try {
11675
+ await fs17.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
11676
+ await fs17.rename(temporary, file);
11677
+ } catch (error) {
11678
+ await fs17.rm(temporary, { force: true });
11679
+ throw error;
11680
+ }
11681
+ return selection;
11682
+ }
11683
+ async function resolveCurrentChange(projectRoot2) {
11684
+ let source;
11685
+ try {
11686
+ source = await fs17.readFile(currentChangeFile(projectRoot2), "utf8");
11687
+ } catch (error) {
11688
+ if (error.code === "ENOENT") return { status: "missing" };
11689
+ return {
11690
+ status: "stale",
11691
+ reason: `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`
11692
+ };
11693
+ }
11694
+ let selection;
11695
+ try {
11696
+ selection = parseSelection(source);
11697
+ await validateActiveChange(projectRoot2, selection.change);
11698
+ } catch (error) {
11699
+ return {
11700
+ status: "stale",
11701
+ reason: error instanceof Error ? error.message : String(error)
11702
+ };
11703
+ }
11704
+ const branch = currentBranch(projectRoot2);
11705
+ if (selection.branch !== null && branch !== selection.branch) {
11706
+ return {
11707
+ status: "stale",
11708
+ reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${branch ?? "detached HEAD"}'`
11709
+ };
11710
+ }
11711
+ return { status: "selected", selection };
11712
+ }
11713
+ async function clearCurrentChange(projectRoot2) {
11714
+ await fs17.rm(currentChangeFile(projectRoot2), { force: true });
11715
+ }
11716
+
11717
+ // domains/comet-classic/classic-hook-guard.ts
11718
+ function result(exitCode, message) {
11719
+ return { exitCode, stderr: message + "\n" };
11168
11720
  }
11169
11721
  function allowed(message) {
11170
11722
  return result(0, `[COMET-HOOK] allowed: ${message}`);
@@ -11187,52 +11739,52 @@ function normalized(value) {
11187
11739
  function parseProjectRoot(args) {
11188
11740
  const index = args.indexOf("--project-root");
11189
11741
  const value = index >= 0 ? args[index + 1] : void 0;
11190
- return path16.resolve(value && !value.startsWith("--") ? value : process.cwd());
11742
+ return path19.resolve(value && !value.startsWith("--") ? value : process.cwd());
11191
11743
  }
11192
- function relativeToProjectRoot(target, projectRoot) {
11193
- const relative = normalized(path16.relative(projectRoot, target));
11744
+ function relativeToProjectRoot(target, projectRoot2) {
11745
+ const relative = normalized(path19.relative(projectRoot2, target));
11194
11746
  if (relative === "") return "";
11195
- if (relative.startsWith("../") || relative === ".." || path16.isAbsolute(relative)) return null;
11747
+ if (relative.startsWith("../") || relative === ".." || path19.isAbsolute(relative)) return null;
11196
11748
  return relative;
11197
11749
  }
11198
11750
  async function physicalPathForPossiblyMissingTarget(target) {
11199
- const resolved = path16.resolve(target);
11200
- const root = path16.parse(resolved).root;
11751
+ const resolved = path19.resolve(target);
11752
+ const root = path19.parse(resolved).root;
11201
11753
  const missingSegments = [];
11202
11754
  let cursor = resolved;
11203
11755
  while (cursor && cursor !== root) {
11204
11756
  try {
11205
- const physicalBase = await fs15.realpath(cursor);
11206
- return path16.join(physicalBase, ...missingSegments.reverse());
11757
+ const physicalBase = await fs18.realpath(cursor);
11758
+ return path19.join(physicalBase, ...missingSegments.reverse());
11207
11759
  } catch (error) {
11208
11760
  const code = error.code;
11209
11761
  if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
11210
- missingSegments.push(path16.basename(cursor));
11211
- cursor = path16.dirname(cursor);
11762
+ missingSegments.push(path19.basename(cursor));
11763
+ cursor = path19.dirname(cursor);
11212
11764
  }
11213
11765
  }
11214
11766
  try {
11215
- const physicalRoot = await fs15.realpath(root);
11216
- return path16.join(physicalRoot, ...missingSegments.reverse());
11767
+ const physicalRoot = await fs18.realpath(root);
11768
+ return path19.join(physicalRoot, ...missingSegments.reverse());
11217
11769
  } catch {
11218
11770
  return null;
11219
11771
  }
11220
11772
  }
11221
- async function projectRelative(target, projectRoot) {
11222
- const rawCandidate = path16.isAbsolute(target) ? target : path16.resolve(process.cwd(), target);
11773
+ async function projectRelative(target, projectRoot2) {
11774
+ const rawCandidate = path19.isAbsolute(target) ? target : path19.resolve(process.cwd(), target);
11223
11775
  let candidate = normalized(rawCandidate);
11224
- const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot);
11776
+ const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
11225
11777
  if (rootRelative !== null) return rootRelative;
11226
11778
  try {
11227
11779
  const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
11228
- const physicalRoot = await fs15.realpath(projectRoot);
11780
+ const physicalRoot = await fs18.realpath(projectRoot2);
11229
11781
  if (physicalCandidate) {
11230
11782
  const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
11231
11783
  if (physicalRootRelative !== null) return physicalRootRelative;
11232
11784
  candidate = normalized(physicalCandidate);
11233
11785
  }
11234
11786
  } catch {
11235
- if (!path16.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
11787
+ if (!path19.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
11236
11788
  }
11237
11789
  return candidate.replace(/^\.\//u, "");
11238
11790
  }
@@ -11256,28 +11808,22 @@ async function loadGoverningChange(changeDir) {
11256
11808
  };
11257
11809
  }
11258
11810
  }
11259
- async function activeChanges(projectRoot) {
11260
- const changesDir = path16.join(projectRoot, "openspec", "changes");
11811
+ async function activeChanges(projectRoot2) {
11812
+ const changesDir = path19.join(projectRoot2, "openspec", "changes");
11261
11813
  const governingChanges = [];
11262
11814
  if (!existsSync2(changesDir)) return governingChanges;
11263
- for (const entry2 of (await fs15.readdir(changesDir, { withFileTypes: true })).sort(
11815
+ for (const entry2 of (await fs18.readdir(changesDir, { withFileTypes: true })).sort(
11264
11816
  (left, right) => left.name.localeCompare(right.name)
11265
11817
  )) {
11266
11818
  if (!entry2.isDirectory() || entry2.name === "archive") continue;
11267
- const changeDir = path16.join(changesDir, entry2.name);
11268
- if (!existsSync2(path16.join(changeDir, ".comet.yaml"))) continue;
11819
+ const changeDir = path19.join(changesDir, entry2.name);
11820
+ if (!existsSync2(path19.join(changeDir, ".comet.yaml"))) continue;
11269
11821
  const governing = await loadGoverningChange(changeDir);
11270
11822
  if (!governing || governing.archived) continue;
11271
11823
  governingChanges.push(governing);
11272
11824
  }
11273
11825
  return governingChanges;
11274
11826
  }
11275
- function blocksSourceWrites(governing) {
11276
- if (governing.phase === "open" || governing.phase === "design" || governing.phase === "archive") {
11277
- return true;
11278
- }
11279
- return governing.phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc;
11280
- }
11281
11827
  function isSuperpowersArtifactPath(relativePath2) {
11282
11828
  return relativePath2.startsWith("docs/superpowers/");
11283
11829
  }
@@ -11285,7 +11831,7 @@ function allowsSuperpowersArtifacts(governing) {
11285
11831
  return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
11286
11832
  }
11287
11833
  function governingChangeName(governing) {
11288
- return governing.changeDir ? path16.basename(governing.changeDir) : null;
11834
+ return governing.changeDir ? path19.basename(governing.changeDir) : null;
11289
11835
  }
11290
11836
  var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
11291
11837
  "design",
@@ -11316,8 +11862,8 @@ function matchesSuperpowersArtifactName(relativePath2, changeName) {
11316
11862
  const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u");
11317
11863
  return pattern.test(stem);
11318
11864
  }
11319
- async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
11320
- const active = await activeChanges(projectRoot);
11865
+ async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
11866
+ const active = await activeChanges(projectRoot2);
11321
11867
  const recorded = active.find(
11322
11868
  (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
11323
11869
  );
@@ -11332,18 +11878,41 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
11332
11878
  if (named) return named;
11333
11879
  return null;
11334
11880
  }
11335
- async function repoSourceGoverningChange(projectRoot) {
11336
- const active = await activeChanges(projectRoot);
11337
- return active.find(blocksSourceWrites) ?? active[0] ?? null;
11881
+ async function repoSourceGoverningChange(projectRoot2, relativePath2) {
11882
+ const active = await activeChanges(projectRoot2);
11883
+ if (active.length === 0) return null;
11884
+ const current = await resolveCurrentChange(projectRoot2);
11885
+ if (current.status === "stale") {
11886
+ return { blockedResult: blockedStaleSelection(relativePath2, current.reason) };
11887
+ }
11888
+ if (current.status === "selected") {
11889
+ const selected = active.find(
11890
+ (governing) => governingChangeName(governing) === current.selection.change
11891
+ );
11892
+ if (selected) return selected;
11893
+ return {
11894
+ blockedResult: blockedStaleSelection(
11895
+ relativePath2,
11896
+ `selected change '${current.selection.change}' is no longer active`
11897
+ )
11898
+ };
11899
+ }
11900
+ if (active.length === 1) return active[0];
11901
+ return {
11902
+ blockedResult: blockedMultipleChanges(
11903
+ relativePath2,
11904
+ active.map((governing) => governingChangeName(governing)).filter(Boolean)
11905
+ )
11906
+ };
11338
11907
  }
11339
- async function governingChange(relativePath2, projectRoot) {
11908
+ async function governingChange(relativePath2, projectRoot2) {
11340
11909
  const prefix = "openspec/changes/";
11341
11910
  if (relativePath2.startsWith(prefix)) {
11342
11911
  const rest = relativePath2.slice(prefix.length);
11343
11912
  const [name] = rest.split("/");
11344
11913
  if (name && name !== "archive") {
11345
- const changeDir = path16.join(projectRoot, "openspec", "changes", name);
11346
- const stateFile2 = path16.join(changeDir, ".comet.yaml");
11914
+ const changeDir = path19.join(projectRoot2, "openspec", "changes", name);
11915
+ const stateFile2 = path19.join(changeDir, ".comet.yaml");
11347
11916
  if (existsSync2(stateFile2)) {
11348
11917
  const governing = await loadGoverningChange(changeDir);
11349
11918
  if (governing) return governing;
@@ -11353,18 +11922,18 @@ async function governingChange(relativePath2, projectRoot) {
11353
11922
  }
11354
11923
  }
11355
11924
  if (isSuperpowersArtifactPath(relativePath2)) {
11356
- const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot);
11925
+ const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
11357
11926
  if (superpowers) return { ...superpowers, superpowersArtifact: "matched" };
11358
- const fallback = await repoSourceGoverningChange(projectRoot);
11927
+ const fallback = (await activeChanges(projectRoot2))[0] ?? null;
11359
11928
  return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
11360
11929
  }
11361
- return repoSourceGoverningChange(projectRoot);
11930
+ return repoSourceGoverningChange(projectRoot2, relativePath2);
11362
11931
  }
11363
11932
  function isRootMarkdown(relativePath2) {
11364
11933
  return !relativePath2.includes("/") && relativePath2.endsWith(".md");
11365
11934
  }
11366
11935
  function isCometConfig(relativePath2) {
11367
- return relativePath2 === ".comet.yaml" || relativePath2 === "comet.yaml" || relativePath2 === ".comet.yml" || relativePath2 === "comet.yml" || relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
11936
+ return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
11368
11937
  }
11369
11938
  function isSuperpowersWorkspace(relativePath2) {
11370
11939
  return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/");
@@ -11462,22 +12031,47 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
11462
12031
  ].join("\n")
11463
12032
  );
11464
12033
  }
12034
+ function blockedMultipleChanges(relativePath2, changeNames) {
12035
+ return result(
12036
+ 2,
12037
+ [
12038
+ "",
12039
+ "╔══════════════════════════════════════════╗",
12040
+ "║ COMET PHASE GUARD — WRITE BLOCKED ║",
12041
+ "╚══════════════════════════════════════════╝",
12042
+ "",
12043
+ " BLOCKED: multiple active changes require a current change",
12044
+ ` Target file: ${relativePath2}`,
12045
+ ` Active changes: ${changeNames.join(", ")}`,
12046
+ "",
12047
+ " NEXT: run comet state select <change-name>, then retry the source write",
12048
+ ""
12049
+ ].join("\n")
12050
+ );
12051
+ }
12052
+ function blockedStaleSelection(relativePath2, reason) {
12053
+ return result(
12054
+ 2,
12055
+ [
12056
+ "",
12057
+ "╔══════════════════════════════════════════╗",
12058
+ "║ COMET PHASE GUARD — WRITE BLOCKED ║",
12059
+ "╚══════════════════════════════════════════╝",
12060
+ "",
12061
+ " BLOCKED: current change selection is stale or invalid",
12062
+ ` Target file: ${relativePath2}`,
12063
+ ` Reason: ${reason}`,
12064
+ "",
12065
+ " NEXT: run comet state select <change-name>, then retry the source write",
12066
+ ""
12067
+ ].join("\n")
12068
+ );
12069
+ }
11465
12070
  var classicHookGuardCommand = async (args) => {
11466
- const projectRoot = parseProjectRoot(args);
12071
+ const projectRoot2 = parseProjectRoot(args);
11467
12072
  const target = inputTarget();
11468
12073
  if (!target) return allowed("no file path in tool input");
11469
- const relativePath2 = await projectRelative(target, projectRoot);
11470
- let governing;
11471
- try {
11472
- governing = await governingChange(relativePath2, projectRoot);
11473
- } catch (error) {
11474
- return result(
11475
- 2,
11476
- `[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
11477
- );
11478
- }
11479
- if (!governing) return allowed("no active comet change");
11480
- if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
12074
+ const relativePath2 = await projectRelative(target, projectRoot2);
11481
12075
  if (isCometConfig(relativePath2)) {
11482
12076
  return allowed(`${relativePath2} (whitelist: comet config)`);
11483
12077
  }
@@ -11490,6 +12084,18 @@ var classicHookGuardCommand = async (args) => {
11490
12084
  if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) {
11491
12085
  return allowed(`${relativePath2} (whitelist: root markdown)`);
11492
12086
  }
12087
+ let governing;
12088
+ try {
12089
+ governing = await governingChange(relativePath2, projectRoot2);
12090
+ } catch (error) {
12091
+ return result(
12092
+ 2,
12093
+ `[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
12094
+ );
12095
+ }
12096
+ if (!governing) return allowed("no active comet change");
12097
+ if ("blockedResult" in governing) return governing.blockedResult;
12098
+ if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
11493
12099
  const phase = governing.phase;
11494
12100
  const openSpec = openSpecAllowed(relativePath2, phase);
11495
12101
  if (openSpec) return allowed(openSpec);
@@ -11878,12 +12484,438 @@ var classicIntentCommand = async (args, _options) => {
11878
12484
  }
11879
12485
  };
11880
12486
 
12487
+ // domains/comet-classic/classic-resume-probe.ts
12488
+ import path20 from "path";
12489
+ import { promises as fs19 } from "fs";
12490
+ import { spawn } from "child_process";
12491
+ var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
12492
+ function isRecord2(value) {
12493
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12494
+ }
12495
+ function normalizeInput(input) {
12496
+ if (!isRecord2(input)) {
12497
+ throw new Error("Invalid CometResumeProbeInput: input must be an object");
12498
+ }
12499
+ if (input.schema_version !== COMET_RESUME_PROBE_SCHEMA_VERSION) {
12500
+ throw new Error(
12501
+ `Invalid CometResumeProbeInput: schema_version must be ${COMET_RESUME_PROBE_SCHEMA_VERSION}`
12502
+ );
12503
+ }
12504
+ if (typeof input.utterance !== "string") {
12505
+ throw new Error("Invalid CometResumeProbeInput: utterance must be a string");
12506
+ }
12507
+ const context = isRecord2(input.agent_context) ? input.agent_context : {};
12508
+ return {
12509
+ schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
12510
+ utterance: input.utterance,
12511
+ locale: typeof input.locale === "string" ? input.locale : "unknown",
12512
+ agent_context: {
12513
+ non_trivial_work: context.non_trivial_work === true,
12514
+ already_in_comet_flow: context.already_in_comet_flow === true
12515
+ }
12516
+ };
12517
+ }
12518
+ function result3(action, change, confidence, reason, evidence = []) {
12519
+ return {
12520
+ schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
12521
+ action,
12522
+ changeName: change?.name ?? null,
12523
+ phase: change?.phase ?? null,
12524
+ nextCommand: action === "auto_resume" || action === "ask_user" ? change?.nextCommand ?? null : null,
12525
+ confidence,
12526
+ reason,
12527
+ evidence
12528
+ };
12529
+ }
12530
+ async function readIfExists(filePath) {
12531
+ if (!await fileExists3(filePath)) return "";
12532
+ return fs19.readFile(filePath, "utf8");
12533
+ }
12534
+ async function changeSearchText(changeDir, classic) {
12535
+ const files = ["proposal.md", "design.md", "tasks.md"];
12536
+ const parts = [classic.name, classic.workflow, classic.phase];
12537
+ for (const file of files) {
12538
+ parts.push(await readIfExists(path20.join(changeDir, file)));
12539
+ }
12540
+ return parts.join("\n").toLowerCase();
12541
+ }
12542
+ function nextCommandForPhase2(phase) {
12543
+ switch (phase) {
12544
+ case "open":
12545
+ return "/comet-open";
12546
+ case "design":
12547
+ return "/comet-design";
12548
+ case "build":
12549
+ return "/comet-build";
12550
+ case "verify":
12551
+ return "/comet-verify";
12552
+ case "archive":
12553
+ return "/comet-archive";
12554
+ default:
12555
+ return null;
12556
+ }
12557
+ }
12558
+ function diagnosticFromProjection(changeDir, name, projection) {
12559
+ const classic = projection.classic;
12560
+ const unknownKeys = projection.unknownKeys.filter((key) => key !== "run_id");
12561
+ if (!classic) {
12562
+ return {
12563
+ name,
12564
+ valid: false,
12565
+ workflow: "unknown",
12566
+ phase: "invalid",
12567
+ currentStep: null,
12568
+ nextCommand: null,
12569
+ runtimeMode: "invalid",
12570
+ runtimeEval: null,
12571
+ evidence: [],
12572
+ error: `${changeDir} does not contain valid Comet state`
12573
+ };
12574
+ }
12575
+ if (unknownKeys.length > 0) {
12576
+ return {
12577
+ name,
12578
+ valid: false,
12579
+ workflow: classic.workflow,
12580
+ phase: classic.phase,
12581
+ currentStep: null,
12582
+ nextCommand: null,
12583
+ runtimeMode: "invalid",
12584
+ runtimeEval: null,
12585
+ evidence: [],
12586
+ error: `unknown field(s): ${unknownKeys.join(", ")}`
12587
+ };
12588
+ }
12589
+ return {
12590
+ name,
12591
+ valid: true,
12592
+ workflow: classic.workflow,
12593
+ phase: classic.phase,
12594
+ currentStep: null,
12595
+ nextCommand: nextCommandForPhase2(classic.phase),
12596
+ runtimeMode: "engine-projection",
12597
+ runtimeEval: null,
12598
+ evidence: []
12599
+ };
12600
+ }
12601
+ async function hasOpenSpecChangeFiles(changeDir) {
12602
+ return await fileExists3(path20.join(changeDir, "proposal.md")) || await fileExists3(path20.join(changeDir, "design.md")) || await fileExists3(path20.join(changeDir, "tasks.md"));
12603
+ }
12604
+ async function discoverActiveChanges(projectRoot2) {
12605
+ const changesDir = path20.join(projectRoot2, "openspec", "changes");
12606
+ if (!await fileExists3(changesDir)) return [];
12607
+ const entries = await readDir(changesDir);
12608
+ const changes = [];
12609
+ for (const entry2 of entries) {
12610
+ if (entry2 === "archive") continue;
12611
+ const changeDir = path20.join(changesDir, entry2);
12612
+ const stat = await fs19.stat(changeDir).catch(() => null);
12613
+ if (!stat?.isDirectory()) continue;
12614
+ const hasCometState = await fileExists3(path20.join(changeDir, ".comet.yaml"));
12615
+ if (!hasCometState) {
12616
+ if (!await hasOpenSpecChangeFiles(changeDir)) continue;
12617
+ const missingStateChange = {
12618
+ name: entry2,
12619
+ workflow: "unknown",
12620
+ phase: "invalid",
12621
+ nextCommand: null,
12622
+ diagnostic: {
12623
+ name: entry2,
12624
+ valid: false,
12625
+ workflow: "unknown",
12626
+ phase: "invalid",
12627
+ currentStep: null,
12628
+ nextCommand: null,
12629
+ runtimeMode: "invalid",
12630
+ runtimeEval: null,
12631
+ evidence: [],
12632
+ error: "missing Comet state"
12633
+ },
12634
+ buildPause: null,
12635
+ hasClassicProjection: false,
12636
+ verifyResult: null,
12637
+ text: "",
12638
+ missingCometState: true
12639
+ };
12640
+ missingStateChange.text = await changeSearchText(changeDir, missingStateChange);
12641
+ changes.push(missingStateChange);
12642
+ continue;
12643
+ }
12644
+ const projection = await readClassicState(changeDir, { migrate: false });
12645
+ const classic = projection.classic;
12646
+ const diagnostic = diagnosticFromProjection(changeDir, entry2, projection);
12647
+ const hasClassicProjection = Boolean(classic);
12648
+ const phase = classic?.phase ?? diagnostic.phase;
12649
+ const workflow = classic?.workflow ?? diagnostic.workflow;
12650
+ if (phase === "archive" || classic?.archived) continue;
12651
+ const change = {
12652
+ name: entry2,
12653
+ workflow,
12654
+ phase,
12655
+ nextCommand: diagnostic.nextCommand,
12656
+ diagnostic,
12657
+ buildPause: classic?.buildPause ?? null,
12658
+ hasClassicProjection,
12659
+ verifyResult: classic?.verifyResult ?? null,
12660
+ text: "",
12661
+ missingCometState: false
12662
+ };
12663
+ change.text = await changeSearchText(changeDir, change);
12664
+ changes.push(change);
12665
+ }
12666
+ return changes;
12667
+ }
12668
+ var RESUME_WORDS = [
12669
+ "continue",
12670
+ "resume",
12671
+ "carry on",
12672
+ "finish",
12673
+ "run it",
12674
+ "commit",
12675
+ "verify",
12676
+ "archive",
12677
+ "继续",
12678
+ "接着",
12679
+ "恢复",
12680
+ "跑完",
12681
+ "提交",
12682
+ "验证",
12683
+ "归档",
12684
+ "修刚才"
12685
+ ];
12686
+ var QUESTION_WORDS = [
12687
+ "what",
12688
+ "why",
12689
+ "how",
12690
+ "explain",
12691
+ "summarize",
12692
+ "reliable",
12693
+ "靠谱吗",
12694
+ "是什么",
12695
+ "为什么",
12696
+ "解释",
12697
+ "总结",
12698
+ "取名",
12699
+ "命名"
12700
+ ];
12701
+ var GENERIC_RELATED_TOKENS = /* @__PURE__ */ new Set([
12702
+ "add",
12703
+ "build",
12704
+ "cache",
12705
+ "change",
12706
+ "code",
12707
+ "design",
12708
+ "docs",
12709
+ "file",
12710
+ "fix",
12711
+ "implement",
12712
+ "plan",
12713
+ "readme",
12714
+ "task",
12715
+ "test",
12716
+ "update",
12717
+ "修改",
12718
+ "更新",
12719
+ "修复",
12720
+ "添加",
12721
+ "文档",
12722
+ "任务",
12723
+ "计划",
12724
+ "实现"
12725
+ ]);
12726
+ var OPT_OUT_WORDS = [
12727
+ "do not resume",
12728
+ "don't resume",
12729
+ "without comet",
12730
+ "skip comet",
12731
+ "不要恢复",
12732
+ "不走 comet",
12733
+ "不要走 comet",
12734
+ "直接解释",
12735
+ "只回答"
12736
+ ];
12737
+ function includesAny(text2, words) {
12738
+ return words.some((word) => text2.includes(word));
12739
+ }
12740
+ function hasDecisionPoint(change) {
12741
+ if (change.missingCometState) return true;
12742
+ if (!change.hasClassicProjection) return true;
12743
+ if (!change.diagnostic.valid) return true;
12744
+ if (change.phase === "archive") return true;
12745
+ if (change.verifyResult === "fail") return true;
12746
+ if (change.diagnostic.runtimeEval && !change.diagnostic.runtimeEval.passed) return true;
12747
+ if (change.phase !== "build") return false;
12748
+ if (change.buildPause === "plan-ready") return true;
12749
+ return false;
12750
+ }
12751
+ function relatedEvidence(utterance, change) {
12752
+ const text2 = utterance.toLowerCase();
12753
+ const evidence = [];
12754
+ if (text2.includes(change.name.toLowerCase())) {
12755
+ evidence.push({ source: "user", quote: change.name });
12756
+ }
12757
+ const tokens = change.text.split(/[^a-zA-Z0-9_\-\u4e00-\u9fff/]+/u).map((token) => token.trim().toLowerCase()).filter((token) => token.length >= 4 && !GENERIC_RELATED_TOKENS.has(token));
12758
+ const matched = [...new Set(tokens.filter((token) => text2.includes(token)))].slice(0, 3);
12759
+ for (const token of matched) {
12760
+ evidence.push({ source: "repo", quote: token });
12761
+ }
12762
+ return evidence;
12763
+ }
12764
+ async function gitDirtyFiles(projectRoot2) {
12765
+ return new Promise((resolve) => {
12766
+ const child = spawn("git", ["status", "--short", "--untracked-files=all"], {
12767
+ cwd: projectRoot2,
12768
+ stdio: ["ignore", "pipe", "ignore"],
12769
+ shell: false
12770
+ });
12771
+ const chunks = [];
12772
+ child.stdout.on("data", (chunk) => {
12773
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
12774
+ });
12775
+ child.on("error", () => resolve([]));
12776
+ child.on("exit", (code) => {
12777
+ if (code !== 0) {
12778
+ resolve([]);
12779
+ return;
12780
+ }
12781
+ const dirtyFiles = Buffer.concat(chunks).toString("utf8").split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
12782
+ resolve(dirtyFiles);
12783
+ });
12784
+ });
12785
+ }
12786
+ async function resolveCometResumeProbe(projectRoot2, rawInput) {
12787
+ const input = normalizeInput(rawInput);
12788
+ const utterance = input.utterance.trim();
12789
+ const lower = utterance.toLowerCase();
12790
+ if (input.agent_context.already_in_comet_flow) {
12791
+ return result3("out_of_scope", null, "low", "already in Comet flow");
12792
+ }
12793
+ if (includesAny(lower, OPT_OUT_WORDS)) {
12794
+ return result3("out_of_scope", null, "low", "user opted out of Comet resume", [
12795
+ { source: "user", quote: utterance }
12796
+ ]);
12797
+ }
12798
+ const changes = await discoverActiveChanges(projectRoot2);
12799
+ if (changes.length === 0) {
12800
+ return result3("none", null, "none", "no active Comet changes");
12801
+ }
12802
+ const dirtyFiles = await gitDirtyFiles(projectRoot2);
12803
+ if (changes.length > 1) {
12804
+ const named = changes.find((change2) => lower.includes(change2.name.toLowerCase()));
12805
+ if (!named) {
12806
+ return result3("ask_user", null, "low", "multiple active changes require a change name");
12807
+ }
12808
+ if (dirtyFiles.length > 0) {
12809
+ return result3("ask_user", named, "low", "uncommitted worktree changes require attribution", [
12810
+ { source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
12811
+ ]);
12812
+ }
12813
+ return hasDecisionPoint(named) ? result3("ask_user", named, "low", "active change is at a decision point") : result3("auto_resume", named, "high", "request names an active change", [
12814
+ { source: "user", quote: named.name }
12815
+ ]);
12816
+ }
12817
+ const [change] = changes;
12818
+ if (dirtyFiles.length > 0) {
12819
+ return result3("ask_user", change, "low", "uncommitted worktree changes require attribution", [
12820
+ { source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
12821
+ ]);
12822
+ }
12823
+ if (hasDecisionPoint(change)) {
12824
+ if (change.missingCometState) {
12825
+ return result3("ask_user", change, "low", "active OpenSpec change is missing Comet state");
12826
+ }
12827
+ return result3("ask_user", change, "low", "active change is at a decision point", [
12828
+ { source: "state", quote: `phase: ${change.phase}` }
12829
+ ]);
12830
+ }
12831
+ const resumeLike = includesAny(lower, RESUME_WORDS);
12832
+ const questionLike = !input.agent_context.non_trivial_work && includesAny(lower, QUESTION_WORDS);
12833
+ if (questionLike && !resumeLike) {
12834
+ return result3("out_of_scope", change, "low", "user asked a question without workflow work");
12835
+ }
12836
+ const evidence = relatedEvidence(utterance, change);
12837
+ if (resumeLike || evidence.length > 0) {
12838
+ return result3("auto_resume", change, "high", "single active change and request is related", [
12839
+ { source: "state", quote: `phase: ${change.phase}` },
12840
+ ...evidence
12841
+ ]);
12842
+ }
12843
+ if (input.agent_context.non_trivial_work) {
12844
+ return result3(
12845
+ "ask_user",
12846
+ change,
12847
+ "low",
12848
+ "single active change exists but request looks unrelated"
12849
+ );
12850
+ }
12851
+ return result3("out_of_scope", change, "low", "request is not workflow work");
12852
+ }
12853
+
12854
+ // domains/comet-classic/classic-resume-probe-command.ts
12855
+ function result4(exitCode, stdout, stderr) {
12856
+ return {
12857
+ exitCode,
12858
+ ...stdout === void 0 ? {} : { stdout },
12859
+ ...stderr === void 0 ? {} : { stderr }
12860
+ };
12861
+ }
12862
+ function usage2() {
12863
+ return result4(
12864
+ 64,
12865
+ void 0,
12866
+ "Usage: comet-resume-probe.mjs probe <input-json>\nUsage: comet-resume-probe.mjs probe --stdin"
12867
+ );
12868
+ }
12869
+ async function readStdin2() {
12870
+ const chunks = [];
12871
+ for await (const chunk of process.stdin) {
12872
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
12873
+ }
12874
+ return Buffer.concat(chunks).toString("utf8");
12875
+ }
12876
+ function rawUtteranceInput(utterance) {
12877
+ return {
12878
+ schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
12879
+ utterance,
12880
+ locale: "unknown",
12881
+ agent_context: {
12882
+ non_trivial_work: true,
12883
+ already_in_comet_flow: false
12884
+ }
12885
+ };
12886
+ }
12887
+ function parseStdinInput(source) {
12888
+ try {
12889
+ return JSON.parse(source);
12890
+ } catch {
12891
+ return rawUtteranceInput(source);
12892
+ }
12893
+ }
12894
+ var classicResumeProbeCommand = async (args) => {
12895
+ const [subcommand, input] = args;
12896
+ if (subcommand !== "probe") return usage2();
12897
+ const fromStdin = input === "--stdin";
12898
+ const source = fromStdin ? await readStdin2() : input;
12899
+ if (!source) return usage2();
12900
+ try {
12901
+ const parsedInput = fromStdin ? parseStdinInput(source) : JSON.parse(source);
12902
+ const resolution = await resolveCometResumeProbe(process.cwd(), parsedInput);
12903
+ return result4(0, `${JSON.stringify(resolution, null, 2)}
12904
+ `);
12905
+ } catch (error) {
12906
+ if (error instanceof SyntaxError) {
12907
+ return result4(1, void 0, `Invalid JSON: ${error.message}`);
12908
+ }
12909
+ return result4(1, void 0, error instanceof Error ? error.message : String(error));
12910
+ }
12911
+ };
12912
+
11881
12913
  // domains/comet-classic/classic-state-command.ts
11882
- var import_yaml6 = __toESM(require_dist(), 1);
12914
+ var import_yaml7 = __toESM(require_dist(), 1);
11883
12915
  import { spawnSync as spawnSync3 } from "child_process";
11884
- import { randomUUID as randomUUID6 } from "crypto";
11885
- import { existsSync as existsSync3, promises as fs16 } from "fs";
11886
- import path17 from "path";
12916
+ import { randomUUID as randomUUID7 } from "crypto";
12917
+ import { existsSync as existsSync3, promises as fs20 } from "fs";
12918
+ import path21 from "path";
11887
12919
  init_state();
11888
12920
  var GREEN5 = "\x1B[32m";
11889
12921
  var RED5 = "\x1B[31m";
@@ -11895,6 +12927,7 @@ var ARTIFACT_LANGUAGES2 = ["en", "zh-CN"];
11895
12927
  var EVENTS = CLASSIC_TRANSITION_EVENTS;
11896
12928
  var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
11897
12929
  ...RUN_WIRE_KEYS,
12930
+ "archive_confirmation",
11898
12931
  "classic_profile",
11899
12932
  "classic_migration"
11900
12933
  ]);
@@ -11915,6 +12948,7 @@ var FIELD_ENUMS = {
11915
12948
  auto_transition: ["true", "false"],
11916
12949
  verify_result: ["pending", "pass", "fail"],
11917
12950
  branch_status: ["pending", "handled"],
12951
+ archive_confirmation: ["pending", "confirmed"],
11918
12952
  archived: ["true", "false"],
11919
12953
  direct_override: ["true", "false"],
11920
12954
  classic_profile: PROFILES,
@@ -11930,6 +12964,7 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
11930
12964
  phase: "phase",
11931
12965
  verificationReport: "verification_report",
11932
12966
  verifiedAt: "verified_at",
12967
+ archiveConfirmation: "archive_confirmation",
11933
12968
  verifyResult: "verify_result",
11934
12969
  workflow: "workflow"
11935
12970
  };
@@ -11991,7 +13026,7 @@ function validateRelativePath(value, field2) {
11991
13026
  }
11992
13027
  async function exists6(file) {
11993
13028
  try {
11994
- await fs16.access(file);
13029
+ await fs20.access(file);
11995
13030
  return true;
11996
13031
  } catch (error) {
11997
13032
  if (error.code === "ENOENT") return false;
@@ -12000,39 +13035,39 @@ async function exists6(file) {
12000
13035
  }
12001
13036
  async function nonempty3(file) {
12002
13037
  try {
12003
- return (await fs16.stat(file)).size > 0;
13038
+ return (await fs20.stat(file)).size > 0;
12004
13039
  } catch (error) {
12005
13040
  if (error.code === "ENOENT") return false;
12006
13041
  throw error;
12007
13042
  }
12008
13043
  }
12009
- async function changeDirectory(name) {
13044
+ async function changeDirectory2(name) {
12010
13045
  return resolveClassicChangeDirectory(name);
12011
13046
  }
12012
13047
  async function readDocument2(file) {
12013
13048
  let source;
12014
13049
  try {
12015
- source = await fs16.readFile(file, "utf8");
13050
+ source = await fs20.readFile(file, "utf8");
12016
13051
  } catch (error) {
12017
13052
  if (error.code === "ENOENT") {
12018
13053
  fail2(
12019
- `ERROR: .comet.yaml not found at ${path17.relative(process.cwd(), file).replaceAll("\\", "/")}`
13054
+ `ERROR: .comet.yaml not found at ${path21.relative(process.cwd(), file).replaceAll("\\", "/")}`
12020
13055
  );
12021
13056
  }
12022
13057
  throw error;
12023
13058
  }
12024
- const document = (0, import_yaml6.parseDocument)(source, { uniqueKeys: false });
13059
+ const document = (0, import_yaml7.parseDocument)(source, { uniqueKeys: false });
12025
13060
  if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
12026
13061
  return document;
12027
13062
  }
12028
13063
  async function atomicWrite2(file, content) {
12029
- await fs16.mkdir(path17.dirname(file), { recursive: true });
12030
- const temporary = `${file}.${randomUUID6()}.tmp`;
13064
+ await fs20.mkdir(path21.dirname(file), { recursive: true });
13065
+ const temporary = `${file}.${randomUUID7()}.tmp`;
12031
13066
  try {
12032
- await fs16.writeFile(temporary, content, "utf8");
12033
- await fs16.rename(temporary, file);
13067
+ await fs20.writeFile(temporary, content, "utf8");
13068
+ await fs20.rename(temporary, file);
12034
13069
  } catch (error) {
12035
- await fs16.rm(temporary, { force: true });
13070
+ await fs20.rm(temporary, { force: true });
12036
13071
  throw error;
12037
13072
  }
12038
13073
  }
@@ -12108,10 +13143,14 @@ function sparseClassicState(record) {
12108
13143
  branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
12109
13144
  createdAt: nullableRecordString(record, "created_at"),
12110
13145
  verifiedAt: nullableRecordString(record, "verified_at"),
13146
+ archiveConfirmation: enumRecordValue(
13147
+ record,
13148
+ "archive_confirmation",
13149
+ ["pending", "confirmed"],
13150
+ null
13151
+ ),
12111
13152
  archived: nullableRecordBoolean(record, "archived") ?? false,
12112
13153
  directOverride: nullableRecordBoolean(record, "direct_override"),
12113
- buildCommand: nullableRecordString(record, "build_command"),
12114
- verifyCommand: nullableRecordString(record, "verify_command"),
12115
13154
  handoffContext: nullableRecordString(record, "handoff_context"),
12116
13155
  handoffHash: nullableRecordString(record, "handoff_hash"),
12117
13156
  classicProfile: enumRecordValue(record, "classic_profile", PROFILES, workflow),
@@ -12119,17 +13158,13 @@ function sparseClassicState(record) {
12119
13158
  };
12120
13159
  }
12121
13160
  async function projectConfigValue2(field2) {
12122
- const file = path17.resolve(".comet", "config.yaml");
12123
- if (!await exists6(file)) return null;
12124
- const document = await readDocument2(file);
12125
- const value = document.get(field2);
12126
- return value === null || value === void 0 ? null : scalar(value);
13161
+ return (await readClassicConfigValue(field2))?.value ?? null;
12127
13162
  }
12128
13163
  async function projectLanguageDefault() {
12129
13164
  if (process.env.COMET_LANGUAGE)
12130
13165
  return validateLanguage(process.env.COMET_LANGUAGE, "COMET_LANGUAGE");
12131
- const value = await projectConfigValue2("language");
12132
- if (value) return validateLanguage(value, ".comet/config.yaml");
13166
+ const configured = await readClassicConfigValue("language");
13167
+ if (configured) return validateLanguage(configured.value, configured.source);
12133
13168
  return "en";
12134
13169
  }
12135
13170
  async function contextCompression() {
@@ -12157,14 +13192,14 @@ Valid values: off, standard, thorough`);
12157
13192
  return value === "null" ? null : value;
12158
13193
  }
12159
13194
  function gitOutput(args) {
12160
- const result3 = spawnSync3("git", args, { encoding: "utf8" });
12161
- return result3.status === 0 ? result3.stdout.trim() : null;
13195
+ const result5 = spawnSync3("git", args, { encoding: "utf8" });
13196
+ return result5.status === 0 ? result5.stdout.trim() : null;
12162
13197
  }
12163
13198
  async function stateFile(name) {
12164
- const change = await changeDirectory(name);
13199
+ const change = await changeDirectory2(name);
12165
13200
  return {
12166
13201
  ...change,
12167
- file: path17.join(change.directory, ".comet.yaml")
13202
+ file: path21.join(change.directory, ".comet.yaml")
12168
13203
  };
12169
13204
  }
12170
13205
  async function readField3(name, field2) {
@@ -12182,7 +13217,7 @@ async function readField3(name, field2) {
12182
13217
  return scalar(value);
12183
13218
  }
12184
13219
  function parsedValue(field2, value) {
12185
- const document = (0, import_yaml6.parseDocument)(`${field2}: ${value}
13220
+ const document = (0, import_yaml7.parseDocument)(`${field2}: ${value}
12186
13221
  `);
12187
13222
  if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
12188
13223
  return document.get(field2);
@@ -12204,7 +13239,7 @@ function validateSetValue(field2, value) {
12204
13239
  }
12205
13240
  async function setField2(output, name, field2, value, options = {}) {
12206
13241
  if (MACHINE_OWNED_FIELDS.has(field2) && !options.machineOwned) {
12207
- fail2(`ERROR: '${field2}' is a machine-owned Run field and cannot be set directly`);
13242
+ fail2(`ERROR: '${field2}' is a machine-owned field and cannot be set directly`);
12208
13243
  }
12209
13244
  if (!SETTABLE_FIELDS.has(field2) && !MACHINE_OWNED_FIELDS.has(field2)) {
12210
13245
  fail2(`ERROR: Unknown field: '${field2}'`);
@@ -12267,10 +13302,10 @@ async function init(output, name, workflow) {
12267
13302
  validateEnum(workflow, PROFILES);
12268
13303
  const { file, label, directory } = await stateFile(name);
12269
13304
  if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
12270
- await fs16.mkdir(directory, { recursive: true });
13305
+ await fs20.mkdir(directory, { recursive: true });
12271
13306
  const preset = workflow !== "full";
12272
13307
  const reviewMode = preset ? "off" : await reviewModeDefault();
12273
- const document = new import_yaml6.Document({
13308
+ const document = new import_yaml7.Document({
12274
13309
  workflow,
12275
13310
  language: await projectLanguageDefault(),
12276
13311
  phase: "open",
@@ -12291,10 +13326,11 @@ async function init(output, name, workflow) {
12291
13326
  branch_status: "pending",
12292
13327
  created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
12293
13328
  verified_at: null,
13329
+ archive_confirmation: null,
12294
13330
  archived: false
12295
13331
  });
12296
13332
  await atomicWrite2(file, document.toString());
12297
- output.stderr.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
13333
+ output.stdout.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
12298
13334
  }
12299
13335
  async function requirePhase(name, expected) {
12300
13336
  const actual = await readField3(name, "phase");
@@ -12345,13 +13381,13 @@ async function requireOpenArtifacts(name) {
12345
13381
  const { directory } = await stateFile(name);
12346
13382
  const workflow = await readField3(name, "workflow");
12347
13383
  for (const artifact of ["proposal.md", "tasks.md"]) {
12348
- if (!await nonempty3(path17.join(directory, artifact))) {
13384
+ if (!await nonempty3(path21.join(directory, artifact))) {
12349
13385
  fail2(
12350
13386
  `ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
12351
13387
  );
12352
13388
  }
12353
13389
  }
12354
- if (workflow === "full" && !await nonempty3(path17.join(directory, "design.md"))) {
13390
+ if (workflow === "full" && !await nonempty3(path21.join(directory, "design.md"))) {
12355
13391
  fail2(
12356
13392
  `ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
12357
13393
  );
@@ -12359,14 +13395,14 @@ async function requireOpenArtifacts(name) {
12359
13395
  }
12360
13396
  async function requireDesignEvidence(name) {
12361
13397
  const designDoc = await readField3(name, "design_doc");
12362
- if (!designDoc || designDoc === "null" || !await nonempty3(path17.resolve(designDoc))) {
13398
+ if (!designDoc || designDoc === "null" || !await nonempty3(path21.resolve(designDoc))) {
12363
13399
  fail2(
12364
13400
  `ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
12365
13401
  );
12366
13402
  }
12367
13403
  }
12368
13404
  async function writeSparseTransitionEffects(directory, effects) {
12369
- const file = path17.join(directory, ".comet.yaml");
13405
+ const file = path21.join(directory, ".comet.yaml");
12370
13406
  const document = await readDocument2(file);
12371
13407
  for (const effect of effects) {
12372
13408
  const field2 = wireField2(effect.field);
@@ -12381,21 +13417,21 @@ async function applyTransitionEvent(output, name, event) {
12381
13417
  let sparse = false;
12382
13418
  if (!classic) {
12383
13419
  if (projection.run) fail2("ERROR: Classic state projection is missing");
12384
- const document = await readDocument2(path17.join(directory, ".comet.yaml"));
13420
+ const document = await readDocument2(path21.join(directory, ".comet.yaml"));
12385
13421
  classic = sparseClassicState(document.toJS());
12386
13422
  sparse = true;
12387
13423
  }
12388
- const result3 = applyClassicTransition(classic, event);
13424
+ const result5 = applyClassicTransition(classic, event);
12389
13425
  if (projection.run) {
12390
- await transitionClassicRuntimeRun(directory, result3.classic, projection.run, {
13426
+ await transitionClassicRuntimeRun(directory, result5.classic, projection.run, {
12391
13427
  event,
12392
13428
  source: "comet-state"
12393
13429
  });
12394
13430
  } else if (sparse) {
12395
- await writeSparseTransitionEffects(directory, result3.effects);
13431
+ await writeSparseTransitionEffects(directory, result5.effects);
12396
13432
  } else {
12397
13433
  await writeClassicState(directory, {
12398
- classic: result3.classic,
13434
+ classic: result5.classic,
12399
13435
  run: null,
12400
13436
  unknownKeys: projection.unknownKeys
12401
13437
  });
@@ -12405,10 +13441,10 @@ async function applyTransitionEvent(output, name, event) {
12405
13441
  event,
12406
13442
  source: "comet-state",
12407
13443
  from: classic,
12408
- to: result3.classic,
12409
- effects: result3.effects
13444
+ to: result5.classic,
13445
+ effects: result5.effects
12410
13446
  });
12411
- for (const effect of result3.effects) {
13447
+ for (const effect of result5.effects) {
12412
13448
  output.stderr.push(green4(`[SET] ${wireField2(effect.field)}=${wireValue2(effect.to)}`));
12413
13449
  }
12414
13450
  output.stderr.push(green4(`[TRANSITION] ${event}`));
@@ -12428,7 +13464,7 @@ async function transition(output, name, event) {
12428
13464
  } else if (event === "verify-pass") {
12429
13465
  await requirePhase(name, "verify");
12430
13466
  const report = await readField3(name, "verification_report");
12431
- if (!report || !await exists6(path17.resolve(report))) {
13467
+ if (!report || !await exists6(path21.resolve(report))) {
12432
13468
  fail2(
12433
13469
  `ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
12434
13470
  );
@@ -12438,6 +13474,14 @@ async function transition(output, name, event) {
12438
13474
  }
12439
13475
  } else if (event === "verify-fail") {
12440
13476
  await requirePhase(name, "verify");
13477
+ } else if (event === "archive-confirm") {
13478
+ await requirePhase(name, "archive");
13479
+ if (await readField3(name, "verify_result") !== "pass") {
13480
+ fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
13481
+ }
13482
+ if (await readField3(name, "archived") === "true") {
13483
+ fail2(`ERROR: Cannot transition '${name}': already archived`);
13484
+ }
12441
13485
  } else if (event === "preset-escalate") {
12442
13486
  await requirePhase(name, "build");
12443
13487
  const workflow = await readField3(name, "workflow");
@@ -12456,6 +13500,11 @@ async function transition(output, name, event) {
12456
13500
  if (await readField3(name, "verify_result") !== "pass") {
12457
13501
  fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
12458
13502
  }
13503
+ if (await readField3(name, "archive_confirmation") !== "confirmed") {
13504
+ fail2(
13505
+ `ERROR: Cannot transition '${name}': archive_confirmation must be confirmed before archiving`
13506
+ );
13507
+ }
12459
13508
  }
12460
13509
  await applyTransitionEvent(output, name, event);
12461
13510
  }
@@ -12482,9 +13531,9 @@ async function next(output, name) {
12482
13531
  async function taskCheckoff(output, taskFile, taskText) {
12483
13532
  validateRelativePath(taskFile, "task file");
12484
13533
  if (!taskText) fail2("ERROR: Task text cannot be empty");
12485
- const file = path17.resolve(taskFile);
13534
+ const file = path21.resolve(taskFile);
12486
13535
  if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
12487
- const lines = (await fs16.readFile(file, "utf8")).split(/\r?\n/u);
13536
+ const lines = (await fs20.readFile(file, "utf8")).split(/\r?\n/u);
12488
13537
  const matches = lines.filter(
12489
13538
  (line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
12490
13539
  );
@@ -12522,21 +13571,21 @@ async function check2(output, name, phase) {
12522
13571
  designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
12523
13572
  );
12524
13573
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
12525
- (await nonempty3(path17.join(directory, artifact)) ? pass2 : reject)(
12526
- `${artifact} ${await nonempty3(path17.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13574
+ (await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
13575
+ `${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
12527
13576
  );
12528
13577
  }
12529
13578
  } else if (phase === "build") {
12530
13579
  const workflow = await readField3(name, "workflow");
12531
13580
  const designDoc = await readField3(name, "design_doc");
12532
13581
  if (workflow === "full") {
12533
- (designDoc && designDoc !== "null" && await exists6(path17.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
13582
+ (designDoc && designDoc !== "null" && await exists6(path21.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
12534
13583
  } else {
12535
13584
  pass2(`workflow=${workflow} (design_doc not required)`);
12536
13585
  }
12537
13586
  for (const artifact of ["proposal.md", "tasks.md"]) {
12538
- (await nonempty3(path17.join(directory, artifact)) ? pass2 : reject)(
12539
- `${artifact} ${await nonempty3(path17.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13587
+ (await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
13588
+ `${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
12540
13589
  );
12541
13590
  }
12542
13591
  } else if (phase === "verify") {
@@ -12558,7 +13607,7 @@ async function check2(output, name, phase) {
12558
13607
  }
12559
13608
  function fieldStatus(field2, value, file) {
12560
13609
  if (!value || value === "null") return ` - ${field2}: PENDING`;
12561
- if (file && !existsSync3(path17.resolve(file))) {
13610
+ if (file && !existsSync3(path21.resolve(file))) {
12562
13611
  return ` - ${field2}: BROKEN (path ${value} does not exist)`;
12563
13612
  }
12564
13613
  return ` - ${field2}: DONE (${value})`;
@@ -12567,7 +13616,7 @@ async function recoverOpen(output, directory) {
12567
13616
  output.stdout.push(" Artifacts:");
12568
13617
  let complete = 0;
12569
13618
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
12570
- const done = await nonempty3(path17.join(directory, artifact));
13619
+ const done = await nonempty3(path21.join(directory, artifact));
12571
13620
  if (done) complete += 1;
12572
13621
  output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
12573
13622
  }
@@ -12580,7 +13629,7 @@ async function recoverDesign(output, name, directory) {
12580
13629
  output.stdout.push(" Artifacts:");
12581
13630
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
12582
13631
  output.stdout.push(
12583
- ` - ${artifact}: ${await nonempty3(path17.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
13632
+ ` - ${artifact}: ${await nonempty3(path21.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
12584
13633
  );
12585
13634
  }
12586
13635
  const handoff = await readField3(name, "handoff_context");
@@ -12594,11 +13643,11 @@ async function recoverDesign(output, name, directory) {
12594
13643
  fieldStatus("design_doc", design, design),
12595
13644
  ""
12596
13645
  );
12597
- if (design && design !== "null" && await exists6(path17.resolve(design))) {
13646
+ if (design && design !== "null" && await exists6(path21.resolve(design))) {
12598
13647
  output.stdout.push(
12599
13648
  "Recovery action: Design Doc already created and linked. Run guard to transition to build."
12600
13649
  );
12601
- } else if (handoff && handoff !== "null" && await exists6(path17.resolve(handoff))) {
13650
+ } else if (handoff && handoff !== "null" && await exists6(path21.resolve(handoff))) {
12602
13651
  output.stdout.push(
12603
13652
  "Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
12604
13653
  );
@@ -12628,7 +13677,7 @@ async function recoverBuild(output, name, directory, workflow) {
12628
13677
  decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
12629
13678
  }
12630
13679
  output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
12631
- const tasks = path17.join(directory, "tasks.md");
13680
+ const tasks = path21.join(directory, "tasks.md");
12632
13681
  if (!await exists6(tasks)) {
12633
13682
  output.stdout.push(
12634
13683
  " Tasks: tasks.md MISSING",
@@ -12637,14 +13686,14 @@ async function recoverBuild(output, name, directory, workflow) {
12637
13686
  );
12638
13687
  return;
12639
13688
  }
12640
- const lines = (await fs16.readFile(tasks, "utf8")).split(/\r?\n/u);
13689
+ const lines = (await fs20.readFile(tasks, "utf8")).split(/\r?\n/u);
12641
13690
  const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
12642
13691
  const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
12643
13692
  const pending = total - done;
12644
13693
  let planTotal = 0;
12645
13694
  let planDone = 0;
12646
- if (plan && plan !== "null" && await exists6(path17.resolve(plan))) {
12647
- const planLines = (await fs16.readFile(path17.resolve(plan), "utf8")).split(/\r?\n/u);
13695
+ if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
13696
+ const planLines = (await fs20.readFile(path21.resolve(plan), "utf8")).split(/\r?\n/u);
12648
13697
  planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
12649
13698
  planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
12650
13699
  }
@@ -12716,27 +13765,29 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
12716
13765
  return "Recovery action: All tasks done. Run guard to transition to verify.";
12717
13766
  }
12718
13767
  async function recoverVerify(output, name) {
12719
- const result3 = await readField3(name, "verify_result");
13768
+ const result5 = await readField3(name, "verify_result");
12720
13769
  const mode = await readField3(name, "verify_mode");
12721
13770
  const report = await readField3(name, "verification_report");
12722
13771
  const branch = await readField3(name, "branch_status");
12723
13772
  output.stdout.push(
12724
13773
  " Verification:",
12725
- fieldStatus("verify_result", result3),
13774
+ fieldStatus("verify_result", result5),
12726
13775
  fieldStatus("verify_mode", mode),
12727
13776
  fieldStatus("verification_report", report, report),
12728
13777
  fieldStatus("branch_status", branch),
12729
13778
  "",
12730
- result3 === "pass" && branch === "handled" ? "Recovery action: Verification complete. Run guard to transition to archive." : result3 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
13779
+ result5 === "pass" && branch === "handled" ? "Recovery action: Verification complete. Run guard to transition to archive." : result5 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
12731
13780
  );
12732
13781
  }
12733
13782
  async function recoverArchive(output, name) {
13783
+ const archiveConfirmation = await readField3(name, "archive_confirmation");
12734
13784
  output.stdout.push(
12735
13785
  " Archive:",
12736
13786
  fieldStatus("verify_result", await readField3(name, "verify_result")),
13787
+ fieldStatus("archive_confirmation", archiveConfirmation),
12737
13788
  fieldStatus("archived", await readField3(name, "archived")),
12738
13789
  "",
12739
- "Recovery action: Run /comet-archive to complete archiving."
13790
+ archiveConfirmation === "confirmed" ? "Recovery action: Archive is confirmed. Run /comet-archive to complete archiving." : "Recovery action: Ask for final archive confirmation in /comet-archive before running the archive command."
12740
13791
  );
12741
13792
  }
12742
13793
  async function recover(output, name) {
@@ -12771,19 +13822,19 @@ async function scale(output, name) {
12771
13822
  validateChangeName4(name);
12772
13823
  const { file, directory, label } = await stateFile(name);
12773
13824
  if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
12774
- const tasksFile = path17.join(directory, "tasks.md");
12775
- const taskCount = await exists6(tasksFile) ? (await fs16.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
12776
- const specs = path17.join(directory, "specs");
13825
+ const tasksFile = path21.join(directory, "tasks.md");
13826
+ const taskCount = await exists6(tasksFile) ? (await fs20.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
13827
+ const specs = path21.join(directory, "specs");
12777
13828
  let deltaSpecs = 0;
12778
13829
  if (await exists6(specs)) {
12779
- for (const entry2 of await fs16.readdir(specs)) {
12780
- if (await exists6(path17.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
13830
+ for (const entry2 of await fs20.readdir(specs)) {
13831
+ if (await exists6(path21.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
12781
13832
  }
12782
13833
  }
12783
13834
  const plan = await readField3(name, "plan");
12784
13835
  let baseRef = "";
12785
- if (plan && plan !== "null" && await exists6(path17.resolve(plan))) {
12786
- const match = (await fs16.readFile(path17.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
13836
+ if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
13837
+ const match = (await fs20.readFile(path21.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
12787
13838
  baseRef = match?.[1].trim() ?? "";
12788
13839
  }
12789
13840
  if (!baseRef) baseRef = await readField3(name, "base_ref");
@@ -12793,19 +13844,102 @@ async function scale(output, name) {
12793
13844
  ...baseRef && baseRef !== "null" ? [`${baseRef}...HEAD`] : ["HEAD"]
12794
13845
  ]);
12795
13846
  const changedFiles = changed ? changed.split(/\r?\n/u).filter(Boolean).length : 0;
12796
- const result3 = taskCount > 3 || deltaSpecs > 1 || changedFiles > 8 ? "full" : "light";
12797
- await setField2(new CommandOutput(), name, "verify_mode", result3);
13847
+ const result5 = taskCount > 3 || deltaSpecs > 1 || changedFiles > 8 ? "full" : "light";
13848
+ await setField2(new CommandOutput(), name, "verify_mode", result5);
12798
13849
  output.stderr.push(
12799
13850
  `=== Scale Assessment: ${name} ===`,
12800
13851
  ` Tasks: ${taskCount} (threshold: 3)`,
12801
13852
  ` Delta specs: ${deltaSpecs} capabilities (threshold: 1)`,
12802
13853
  ` Changed files: ${changedFiles} (threshold: 8)`,
12803
- ` → Result: ${result3}`,
12804
- green4(`[SCALE] verify_mode=${result3}`)
13854
+ ` → Result: ${result5}`,
13855
+ green4(`[SCALE] verify_mode=${result5}`)
12805
13856
  );
12806
13857
  }
12807
- function required(args, count, usage2) {
12808
- if (args.length < count) fail2(usage2);
13858
+ function parseRecordCheckOptions(args) {
13859
+ let command;
13860
+ let exitCodeText;
13861
+ let cwd;
13862
+ for (let index = 0; index < args.length; index += 2) {
13863
+ const option = args[index];
13864
+ if (!["--command", "--exit-code", "--cwd"].includes(option)) {
13865
+ fail2(`ERROR: Unknown option: ${option}`);
13866
+ }
13867
+ const value = args[index + 1];
13868
+ if (value === void 0) fail2(`ERROR: Missing value for option: ${option}`);
13869
+ if (option === "--command") command = value;
13870
+ else if (option === "--exit-code") exitCodeText = value;
13871
+ else cwd = value;
13872
+ }
13873
+ if (command === void 0) fail2("ERROR: Missing option: --command");
13874
+ if (exitCodeText === void 0) fail2("ERROR: Missing option: --exit-code");
13875
+ if (!/^-?\d+$/u.test(exitCodeText)) fail2("ERROR: --exit-code must be an integer");
13876
+ return { command, exitCode: Number(exitCodeText), ...cwd === void 0 ? {} : { cwd } };
13877
+ }
13878
+ async function recordCheck(output, name, scopeText, args) {
13879
+ validateChangeName4(name);
13880
+ if (scopeText !== "build" && scopeText !== "verify") {
13881
+ fail2(`ERROR: Invalid command check scope: '${scopeText}'`);
13882
+ }
13883
+ const options = parseRecordCheckOptions(args);
13884
+ const { label, directory, file } = await stateFile(name);
13885
+ if (label !== `openspec/changes/${name}` || !await exists6(file)) {
13886
+ fail2(`ERROR: command checks require an active change: ${name}`);
13887
+ }
13888
+ try {
13889
+ const projection = await readClassicState(directory, { migrate: false });
13890
+ if (!projection.classic || !projection.run) {
13891
+ throw new Error("command checks require an existing synchronized Classic Run");
13892
+ }
13893
+ const { run } = await validateClassicRuntimeRun(directory, projection);
13894
+ const recorded = await recordCommandCheck(directory, run, {
13895
+ scope: scopeText,
13896
+ ...options
13897
+ });
13898
+ output.stderr.push(
13899
+ green4(
13900
+ `[RECORDED] ${recorded.scope} exit=${recorded.exitCode} cwd=${recorded.cwd} command=${recorded.command}`
13901
+ )
13902
+ );
13903
+ } catch (error) {
13904
+ fail2(`ERROR: ${error.message}`);
13905
+ }
13906
+ }
13907
+ function required(args, count, usage3) {
13908
+ if (args.length < count) fail2(usage3);
13909
+ }
13910
+ function requiredExact(args, count, usage3) {
13911
+ if (args.length !== count) fail2(usage3);
13912
+ }
13913
+ async function selectChange(output, name) {
13914
+ validateChangeName4(name);
13915
+ try {
13916
+ const selection = await selectCurrentChange(process.cwd(), name);
13917
+ output.stderr.push(
13918
+ green4(
13919
+ `[SELECTED] current change: ${selection.change}${selection.branch ? ` (branch: ${selection.branch})` : ""}`
13920
+ )
13921
+ );
13922
+ } catch (error) {
13923
+ fail2(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
13924
+ }
13925
+ }
13926
+ async function currentChange(output) {
13927
+ const resolution = await resolveCurrentChange(process.cwd());
13928
+ if (resolution.status === "selected") {
13929
+ output.stdout.push(resolution.selection.change);
13930
+ return;
13931
+ }
13932
+ if (resolution.status === "missing") {
13933
+ fail2("ERROR: no current change selected\nUse: comet-state.mjs select <change-name>");
13934
+ }
13935
+ fail2(
13936
+ `ERROR: current change selection is stale: ${resolution.reason}
13937
+ Use: comet-state.mjs select <change-name>`
13938
+ );
13939
+ }
13940
+ async function clearSelection(output) {
13941
+ await clearCurrentChange(process.cwd());
13942
+ output.stderr.push(green4("[CLEARED] current change selection"));
12809
13943
  }
12810
13944
  var classicStateCommand = async (args) => {
12811
13945
  const output = new CommandOutput();
@@ -12832,9 +13966,25 @@ var classicStateCommand = async (args) => {
12832
13966
  } else if (subcommand === "scale") {
12833
13967
  required(rest, 1, "Usage: comet-state.mjs scale <change-name>");
12834
13968
  await scale(output, rest[0]);
13969
+ } else if (subcommand === "record-check") {
13970
+ required(
13971
+ rest,
13972
+ 2,
13973
+ "Usage: comet state record-check <change> <build|verify> --command <text> --exit-code <int> [--cwd <path>]"
13974
+ );
13975
+ await recordCheck(output, rest[0], rest[1], rest.slice(2));
12835
13976
  } else if (subcommand === "task-checkoff") {
12836
13977
  required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
12837
13978
  await taskCheckoff(output, rest[0], rest[1]);
13979
+ } else if (subcommand === "select") {
13980
+ requiredExact(rest, 1, "Usage: comet-state.mjs select <change-name>");
13981
+ await selectChange(output, rest[0]);
13982
+ } else if (subcommand === "current") {
13983
+ requiredExact(rest, 0, "Usage: comet-state.mjs current");
13984
+ await currentChange(output);
13985
+ } else if (subcommand === "clear-selection") {
13986
+ requiredExact(rest, 0, "Usage: comet-state.mjs clear-selection");
13987
+ await clearSelection(output);
12838
13988
  } else if (subcommand === "next") {
12839
13989
  required(rest, 1, "Usage: comet-state.mjs next <change-name>");
12840
13990
  await next(output, rest[0]);
@@ -12859,7 +14009,8 @@ var CLASSIC_COMMANDS = [
12859
14009
  "handoff",
12860
14010
  "archive",
12861
14011
  "hook-guard",
12862
- "intent"
14012
+ "intent",
14013
+ "resume-probe"
12863
14014
  ];
12864
14015
  var DEFAULT_HANDLERS = {
12865
14016
  state: classicStateCommand,
@@ -12868,7 +14019,8 @@ var DEFAULT_HANDLERS = {
12868
14019
  handoff: classicHandoffCommand,
12869
14020
  archive: classicArchiveCommand,
12870
14021
  "hook-guard": classicHookGuardCommand,
12871
- intent: classicIntentCommand
14022
+ intent: classicIntentCommand,
14023
+ "resume-probe": classicResumeProbeCommand
12872
14024
  };
12873
14025
  function isClassicCommand(value) {
12874
14026
  return CLASSIC_COMMANDS.includes(value);
@@ -12903,14 +14055,14 @@ async function dispatch(command, args, options, handlers) {
12903
14055
  };
12904
14056
  }
12905
14057
  }
12906
- function jsonResult(command, result3) {
14058
+ function jsonResult(command, result5) {
12907
14059
  return {
12908
- exitCode: result3.exitCode,
14060
+ exitCode: result5.exitCode,
12909
14061
  stdout: JSON.stringify({
12910
14062
  command: command ?? null,
12911
- exitCode: result3.exitCode,
12912
- ...result3.stdout === void 0 ? {} : { stdout: result3.stdout },
12913
- ...result3.stderr === void 0 ? {} : { stderr: result3.stderr }
14063
+ exitCode: result5.exitCode,
14064
+ ...result5.stdout === void 0 ? {} : { stdout: result5.stdout },
14065
+ ...result5.stderr === void 0 ? {} : { stderr: result5.stderr }
12914
14066
  }) + "\n"
12915
14067
  };
12916
14068
  }
@@ -12918,15 +14070,15 @@ async function runClassicCli(argv, handlers = DEFAULT_HANDLERS) {
12918
14070
  const json = argv.includes("--json");
12919
14071
  const args = argv.filter((argument) => argument !== "--json");
12920
14072
  const command = args.shift();
12921
- const result3 = await dispatch(command, args, { json }, handlers);
12922
- return json ? jsonResult(command, result3) : result3;
14073
+ const result5 = await dispatch(command, args, { json }, handlers);
14074
+ return json ? jsonResult(command, result5) : result5;
12923
14075
  }
12924
14076
  async function main(argv = process.argv.slice(2)) {
12925
- const result3 = await runClassicCli(argv);
12926
- if (result3.stdout) process.stdout.write(result3.stdout);
12927
- if (result3.stderr)
12928
- process.stderr.write(result3.stderr + (result3.stderr.endsWith("\n") ? "" : "\n"));
12929
- return result3.exitCode;
14077
+ const result5 = await runClassicCli(argv);
14078
+ if (result5.stdout) process.stdout.write(result5.stdout);
14079
+ if (result5.stderr)
14080
+ process.stderr.write(result5.stderr + (result5.stderr.endsWith("\n") ? "" : "\n"));
14081
+ return result5.exitCode;
12930
14082
  }
12931
14083
  var entry = process.argv[1];
12932
14084
  if (entry && import.meta.url === pathToFileURL(entry).href) {