onto-mcp 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/.onto/authority/core-lens-registry.yaml +134 -0
  2. package/.onto/authority/core-lexicon.yaml +1562 -0
  3. package/.onto/authority/diagnostic-codes.yaml +94 -0
  4. package/.onto/domains/accounting/competency_qs.md +384 -0
  5. package/.onto/domains/accounting/concepts.md +186 -0
  6. package/.onto/domains/accounting/conciseness_rules.md +160 -0
  7. package/.onto/domains/accounting/dependency_rules.md +239 -0
  8. package/.onto/domains/accounting/domain_scope.md +213 -0
  9. package/.onto/domains/accounting/extension_cases.md +416 -0
  10. package/.onto/domains/accounting/logic_rules.md +226 -0
  11. package/.onto/domains/accounting/structure_spec.md +298 -0
  12. package/.onto/domains/accounting-kr/competency_qs.md +562 -0
  13. package/.onto/domains/accounting-kr/concepts.md +187 -0
  14. package/.onto/domains/accounting-kr/conciseness_rules.md +125 -0
  15. package/.onto/domains/accounting-kr/dependency_rules.md +93 -0
  16. package/.onto/domains/accounting-kr/domain_scope.md +140 -0
  17. package/.onto/domains/accounting-kr/extension_cases.md +343 -0
  18. package/.onto/domains/accounting-kr/logic_rules.md +160 -0
  19. package/.onto/domains/accounting-kr/structure_spec.md +85 -0
  20. package/.onto/domains/business/competency_qs.md +263 -0
  21. package/.onto/domains/business/concepts.md +200 -0
  22. package/.onto/domains/business/conciseness_rules.md +135 -0
  23. package/.onto/domains/business/dependency_rules.md +113 -0
  24. package/.onto/domains/business/domain_scope.md +240 -0
  25. package/.onto/domains/business/extension_cases.md +249 -0
  26. package/.onto/domains/business/logic_rules.md +134 -0
  27. package/.onto/domains/business/structure_spec.md +114 -0
  28. package/.onto/domains/finance/competency_qs.md +362 -0
  29. package/.onto/domains/finance/concepts.md +194 -0
  30. package/.onto/domains/finance/conciseness_rules.md +155 -0
  31. package/.onto/domains/finance/dependency_rules.md +171 -0
  32. package/.onto/domains/finance/domain_scope.md +215 -0
  33. package/.onto/domains/finance/extension_cases.md +350 -0
  34. package/.onto/domains/finance/logic_rules.md +191 -0
  35. package/.onto/domains/finance/structure_spec.md +182 -0
  36. package/.onto/domains/llm-native-development/competency_qs.md +430 -0
  37. package/.onto/domains/llm-native-development/concepts.md +242 -0
  38. package/.onto/domains/llm-native-development/conciseness_rules.md +163 -0
  39. package/.onto/domains/llm-native-development/dependency_rules.md +216 -0
  40. package/.onto/domains/llm-native-development/domain_scope.md +197 -0
  41. package/.onto/domains/llm-native-development/extension_cases.md +474 -0
  42. package/.onto/domains/llm-native-development/logic_rules.md +123 -0
  43. package/.onto/domains/llm-native-development/prompt_interface.md +49 -0
  44. package/.onto/domains/llm-native-development/structure_spec.md +245 -0
  45. package/.onto/domains/market-intelligence/competency_qs.md +274 -0
  46. package/.onto/domains/market-intelligence/concepts.md +233 -0
  47. package/.onto/domains/market-intelligence/conciseness_rules.md +165 -0
  48. package/.onto/domains/market-intelligence/dependency_rules.md +197 -0
  49. package/.onto/domains/market-intelligence/domain_scope.md +231 -0
  50. package/.onto/domains/market-intelligence/extension_cases.md +425 -0
  51. package/.onto/domains/market-intelligence/logic_rules.md +247 -0
  52. package/.onto/domains/market-intelligence/structure_spec.md +209 -0
  53. package/.onto/domains/ontology/competency_qs.md +394 -0
  54. package/.onto/domains/ontology/concepts.md +172 -0
  55. package/.onto/domains/ontology/conciseness_rules.md +134 -0
  56. package/.onto/domains/ontology/dependency_rules.md +125 -0
  57. package/.onto/domains/ontology/domain_scope.md +114 -0
  58. package/.onto/domains/ontology/extension_cases.md +501 -0
  59. package/.onto/domains/ontology/logic_rules.md +114 -0
  60. package/.onto/domains/ontology/problem_framing_profile.md +67 -0
  61. package/.onto/domains/ontology/structure_spec.md +115 -0
  62. package/.onto/domains/palantir-foundry/RESEARCH_NOTES.md +911 -0
  63. package/.onto/domains/palantir-foundry/competency_qs.md +191 -0
  64. package/.onto/domains/palantir-foundry/competitive_comparison.md +329 -0
  65. package/.onto/domains/palantir-foundry/concepts.md +197 -0
  66. package/.onto/domains/palantir-foundry/conciseness_rules.md +245 -0
  67. package/.onto/domains/palantir-foundry/dependency_rules.md +135 -0
  68. package/.onto/domains/palantir-foundry/domain_scope.md +395 -0
  69. package/.onto/domains/palantir-foundry/extension_cases.md +210 -0
  70. package/.onto/domains/palantir-foundry/logic_rules.md +172 -0
  71. package/.onto/domains/palantir-foundry/structure_spec.md +291 -0
  72. package/.onto/domains/software-engineering/competency_qs.md +538 -0
  73. package/.onto/domains/software-engineering/concepts.md +238 -0
  74. package/.onto/domains/software-engineering/conciseness_rules.md +167 -0
  75. package/.onto/domains/software-engineering/dependency_rules.md +216 -0
  76. package/.onto/domains/software-engineering/domain_scope.md +183 -0
  77. package/.onto/domains/software-engineering/extension_cases.md +551 -0
  78. package/.onto/domains/software-engineering/logic_rules.md +240 -0
  79. package/.onto/domains/software-engineering/problem_framing_profile.md +68 -0
  80. package/.onto/domains/software-engineering/structure_spec.md +185 -0
  81. package/.onto/domains/ui-design/competency_qs.md +567 -0
  82. package/.onto/domains/ui-design/concepts.md +194 -0
  83. package/.onto/domains/ui-design/conciseness_rules.md +190 -0
  84. package/.onto/domains/ui-design/dependency_rules.md +323 -0
  85. package/.onto/domains/ui-design/domain_scope.md +340 -0
  86. package/.onto/domains/ui-design/extension_cases.md +563 -0
  87. package/.onto/domains/ui-design/logic_rules.md +349 -0
  88. package/.onto/domains/ui-design/structure_spec.md +252 -0
  89. package/.onto/domains/visual-design/competency_qs.md +472 -0
  90. package/.onto/domains/visual-design/concepts.md +147 -0
  91. package/.onto/domains/visual-design/conciseness_rules.md +186 -0
  92. package/.onto/domains/visual-design/dependency_rules.md +282 -0
  93. package/.onto/domains/visual-design/domain_scope.md +290 -0
  94. package/.onto/domains/visual-design/extension_cases.md +480 -0
  95. package/.onto/domains/visual-design/logic_rules.md +232 -0
  96. package/.onto/domains/visual-design/structure_spec.md +213 -0
  97. package/.onto/principles/llm-native-development-guideline.md +401 -0
  98. package/.onto/principles/llm-runtime-interface-principles.md +665 -0
  99. package/.onto/principles/non-specialist-communication-guideline.md +74 -0
  100. package/.onto/principles/ontology-as-code-guideline.md +243 -0
  101. package/.onto/principles/ontology-as-code-naming-charter.md +130 -0
  102. package/.onto/principles/product-locality-principle.md +129 -0
  103. package/.onto/principles/productization-charter.md +569 -0
  104. package/.onto/processes/evolve/material-kind-adapter-contract.md +113 -0
  105. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +366 -0
  106. package/.onto/processes/reconstruct/source-profile-contract.md +107 -0
  107. package/.onto/processes/reconstruct/source-profiles/code.md +72 -0
  108. package/.onto/processes/reconstruct/source-profiles/database.md +74 -0
  109. package/.onto/processes/reconstruct/source-profiles/document.md +71 -0
  110. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +79 -0
  111. package/.onto/processes/review/binding-contract.md +270 -0
  112. package/.onto/processes/review/execution-preparation-artifacts.md +281 -0
  113. package/.onto/processes/review/interpretation-contract.md +245 -0
  114. package/.onto/processes/review/issue-stance-deliberation-contract.md +761 -0
  115. package/.onto/processes/review/lens-prompt-contract.md +402 -0
  116. package/.onto/processes/review/lens-registry.md +127 -0
  117. package/.onto/processes/review/pre-dispatch-contracts.md +428 -0
  118. package/.onto/processes/review/productized-live-path.md +398 -0
  119. package/.onto/processes/review/prompt-execution-runner-contract.md +187 -0
  120. package/.onto/processes/review/record-contract.md +427 -0
  121. package/.onto/processes/review/record-field-mapping.md +337 -0
  122. package/.onto/processes/review/review-context-manifest-contract.md +356 -0
  123. package/.onto/processes/review/review-execution-ux-contract.md +809 -0
  124. package/.onto/processes/review/review-target-profile-contract.md +259 -0
  125. package/.onto/processes/review/shared-phenomenon-contract.md +129 -0
  126. package/.onto/processes/review/synthesize-prompt-contract.md +343 -0
  127. package/.onto/processes/shared/target-material-kind-contract.md +198 -0
  128. package/.onto/roles/axiology.md +81 -0
  129. package/.onto/roles/conciseness.md +36 -0
  130. package/.onto/roles/coverage.md +34 -0
  131. package/.onto/roles/dependency.md +37 -0
  132. package/.onto/roles/evolution.md +35 -0
  133. package/.onto/roles/logic.md +104 -0
  134. package/.onto/roles/pragmatics.md +32 -0
  135. package/.onto/roles/semantics.md +36 -0
  136. package/.onto/roles/structure.md +33 -0
  137. package/.onto/roles/synthesize.md +92 -0
  138. package/AGENTS.md +240 -0
  139. package/CLAUDE.md +39 -0
  140. package/README.md +287 -0
  141. package/bin/onto +92 -0
  142. package/dist/cli.js +101 -0
  143. package/dist/core-api/reconstruct-api.js +222 -0
  144. package/dist/core-api/review-api.js +1271 -0
  145. package/dist/core-runtime/cli/assemble-review-record.js +431 -0
  146. package/dist/core-runtime/cli/bootstrap-review-binding.js +186 -0
  147. package/dist/core-runtime/cli/codex-nested-dispatch.js +226 -0
  148. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +390 -0
  149. package/dist/core-runtime/cli/codex-nested-teamlead-executor.js +464 -0
  150. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +335 -0
  151. package/dist/core-runtime/cli/codex-review-unit-executor.js +228 -0
  152. package/dist/core-runtime/cli/complete-review-session.js +64 -0
  153. package/dist/core-runtime/cli/complexity-assessment.js +153 -0
  154. package/dist/core-runtime/cli/coordinator-helpers.js +583 -0
  155. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +167 -0
  156. package/dist/core-runtime/cli/coordinator-state-machine.js +794 -0
  157. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +615 -0
  158. package/dist/core-runtime/cli/e2e-start-review-session.test.js +312 -0
  159. package/dist/core-runtime/cli/health.js +44 -0
  160. package/dist/core-runtime/cli/inline-http-review-unit-executor.js +656 -0
  161. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +567 -0
  162. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +104 -0
  163. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +952 -0
  164. package/dist/core-runtime/cli/migrate-session-roots.js +118 -0
  165. package/dist/core-runtime/cli/mock-review-unit-executor.js +285 -0
  166. package/dist/core-runtime/cli/onto-tools.js +369 -0
  167. package/dist/core-runtime/cli/prepare-review-session.js +272 -0
  168. package/dist/core-runtime/cli/render-review-final-output.js +350 -0
  169. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +106 -0
  170. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +268 -0
  171. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +136 -0
  172. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +201 -0
  173. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +192 -0
  174. package/dist/core-runtime/cli/review-invoke.js +2030 -0
  175. package/dist/core-runtime/cli/run-review-prompt-execution.js +2152 -0
  176. package/dist/core-runtime/cli/session-root-guard.js +168 -0
  177. package/dist/core-runtime/cli/spawn-watcher.js +173 -0
  178. package/dist/core-runtime/cli/spawn-watcher.test.js +457 -0
  179. package/dist/core-runtime/cli/start-review-session.js +68 -0
  180. package/dist/core-runtime/cli/strip-wrapping-code-fence.js +56 -0
  181. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +79 -0
  182. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +412 -0
  183. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +351 -0
  184. package/dist/core-runtime/cli/topology-executor-mapping.js +139 -0
  185. package/dist/core-runtime/cli/topology-executor-mapping.test.js +173 -0
  186. package/dist/core-runtime/cli/write-review-interpretation.js +81 -0
  187. package/dist/core-runtime/config/onto-config-cli.js +278 -0
  188. package/dist/core-runtime/config/onto-config-key-path.js +288 -0
  189. package/dist/core-runtime/config/onto-config-key-path.test.js +195 -0
  190. package/dist/core-runtime/config/onto-config-preview.js +108 -0
  191. package/dist/core-runtime/config/onto-config-preview.test.js +132 -0
  192. package/dist/core-runtime/discovery/config-chain.js +118 -0
  193. package/dist/core-runtime/discovery/config-chain.test.js +103 -0
  194. package/dist/core-runtime/discovery/config-profile.js +199 -0
  195. package/dist/core-runtime/discovery/config-profile.test.js +233 -0
  196. package/dist/core-runtime/discovery/host-detection.js +33 -0
  197. package/dist/core-runtime/discovery/host-detection.test.js +186 -0
  198. package/dist/core-runtime/discovery/installation-paths.js +21 -0
  199. package/dist/core-runtime/discovery/installation-paths.test.js +65 -0
  200. package/dist/core-runtime/discovery/lens-registry.js +60 -0
  201. package/dist/core-runtime/discovery/lens-registry.test.js +81 -0
  202. package/dist/core-runtime/discovery/onto-home.js +71 -0
  203. package/dist/core-runtime/discovery/path-normalization.js +28 -0
  204. package/dist/core-runtime/discovery/path-normalization.test.js +22 -0
  205. package/dist/core-runtime/discovery/plugin-path.js +72 -0
  206. package/dist/core-runtime/discovery/plugin-path.test.js +95 -0
  207. package/dist/core-runtime/discovery/project-root.js +47 -0
  208. package/dist/core-runtime/discovery/settings-chain.js +353 -0
  209. package/dist/core-runtime/discovery/walk-up.js +17 -0
  210. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +344 -0
  211. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +915 -0
  212. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +564 -0
  213. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +708 -0
  214. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +165 -0
  215. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +227 -0
  216. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +59 -0
  217. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +205 -0
  218. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +16 -0
  219. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +9 -0
  220. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +298 -0
  221. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +70 -0
  222. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +46 -0
  223. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +73 -0
  224. package/dist/core-runtime/evolve/adapters/registry.js +47 -0
  225. package/dist/core-runtime/evolve/adapters/registry.test.js +67 -0
  226. package/dist/core-runtime/evolve/cli.js +256 -0
  227. package/dist/core-runtime/evolve/commands/align.js +194 -0
  228. package/dist/core-runtime/evolve/commands/align.test.js +82 -0
  229. package/dist/core-runtime/evolve/commands/apply.js +161 -0
  230. package/dist/core-runtime/evolve/commands/apply.test.js +138 -0
  231. package/dist/core-runtime/evolve/commands/close.js +39 -0
  232. package/dist/core-runtime/evolve/commands/close.test.js +99 -0
  233. package/dist/core-runtime/evolve/commands/defer.js +40 -0
  234. package/dist/core-runtime/evolve/commands/defer.test.js +134 -0
  235. package/dist/core-runtime/evolve/commands/draft.js +323 -0
  236. package/dist/core-runtime/evolve/commands/draft.test.js +178 -0
  237. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +208 -0
  238. package/dist/core-runtime/evolve/commands/error-messages.js +125 -0
  239. package/dist/core-runtime/evolve/commands/error-messages.test.js +167 -0
  240. package/dist/core-runtime/evolve/commands/propose-align.js +222 -0
  241. package/dist/core-runtime/evolve/commands/propose-align.test.js +136 -0
  242. package/dist/core-runtime/evolve/commands/reconstruct.js +330 -0
  243. package/dist/core-runtime/evolve/commands/reconstruct.test.js +278 -0
  244. package/dist/core-runtime/evolve/commands/shared.js +22 -0
  245. package/dist/core-runtime/evolve/commands/stale-check.js +103 -0
  246. package/dist/core-runtime/evolve/commands/stale-check.test.js +84 -0
  247. package/dist/core-runtime/evolve/commands/start.js +887 -0
  248. package/dist/core-runtime/evolve/commands/start.test.js +396 -0
  249. package/dist/core-runtime/evolve/config/project-config.js +99 -0
  250. package/dist/core-runtime/evolve/config/project-config.test.js +170 -0
  251. package/dist/core-runtime/evolve/renderers/align-packet.js +280 -0
  252. package/dist/core-runtime/evolve/renderers/align-packet.test.js +332 -0
  253. package/dist/core-runtime/evolve/renderers/draft-packet.js +303 -0
  254. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +377 -0
  255. package/dist/core-runtime/evolve/renderers/format.js +5 -0
  256. package/dist/core-runtime/evolve/renderers/scope-md.js +237 -0
  257. package/dist/core-runtime/evolve/renderers/scope-md.test.js +306 -0
  258. package/dist/core-runtime/govern/cli.js +369 -0
  259. package/dist/core-runtime/govern/cli.test.js +314 -0
  260. package/dist/core-runtime/govern/drift-engine.js +103 -0
  261. package/dist/core-runtime/govern/drift-engine.test.js +319 -0
  262. package/dist/core-runtime/govern/promote-principle.js +206 -0
  263. package/dist/core-runtime/govern/promote-principle.test.js +368 -0
  264. package/dist/core-runtime/govern/queue.js +81 -0
  265. package/dist/core-runtime/govern/types.js +16 -0
  266. package/dist/core-runtime/install/cli.js +530 -0
  267. package/dist/core-runtime/install/detect.js +128 -0
  268. package/dist/core-runtime/install/detect.test.js +155 -0
  269. package/dist/core-runtime/install/gitignore-update.js +74 -0
  270. package/dist/core-runtime/install/gitignore-update.test.js +64 -0
  271. package/dist/core-runtime/install/install-integration.test.js +373 -0
  272. package/dist/core-runtime/install/prompts.js +389 -0
  273. package/dist/core-runtime/install/prompts.test.js +293 -0
  274. package/dist/core-runtime/install/types.js +26 -0
  275. package/dist/core-runtime/install/validation.js +295 -0
  276. package/dist/core-runtime/install/validation.test.js +313 -0
  277. package/dist/core-runtime/install/writer.js +254 -0
  278. package/dist/core-runtime/install/writer.test.js +218 -0
  279. package/dist/core-runtime/learning/extractor.js +461 -0
  280. package/dist/core-runtime/learning/feedback.js +179 -0
  281. package/dist/core-runtime/learning/health-report.js +165 -0
  282. package/dist/core-runtime/learning/health-report.test.js +169 -0
  283. package/dist/core-runtime/learning/loader.js +388 -0
  284. package/dist/core-runtime/learning/loader.test.js +102 -0
  285. package/dist/core-runtime/learning/promote/apply-state.js +240 -0
  286. package/dist/core-runtime/learning/promote/audit-obligation.js +195 -0
  287. package/dist/core-runtime/learning/promote/collector.js +432 -0
  288. package/dist/core-runtime/learning/promote/degraded-state.js +125 -0
  289. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +166 -0
  290. package/dist/core-runtime/learning/promote/e2e-promote.test.js +6385 -0
  291. package/dist/core-runtime/learning/promote/health-snapshot.js +150 -0
  292. package/dist/core-runtime/learning/promote/insight-reclassifier.js +544 -0
  293. package/dist/core-runtime/learning/promote/judgment-auditor.js +517 -0
  294. package/dist/core-runtime/learning/promote/panel-reviewer.js +1158 -0
  295. package/dist/core-runtime/learning/promote/promote-executor.js +1675 -0
  296. package/dist/core-runtime/learning/promote/promoter.js +307 -0
  297. package/dist/core-runtime/learning/promote/retirement.js +122 -0
  298. package/dist/core-runtime/learning/promote/types.js +23 -0
  299. package/dist/core-runtime/learning/prompt-sections.js +51 -0
  300. package/dist/core-runtime/learning/shared/artifact-registry-init.js +45 -0
  301. package/dist/core-runtime/learning/shared/artifact-registry.js +254 -0
  302. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +73 -0
  303. package/dist/core-runtime/learning/shared/audit-state.js +99 -0
  304. package/dist/core-runtime/learning/shared/duplicate-check.js +28 -0
  305. package/dist/core-runtime/learning/shared/llm-caller.js +831 -0
  306. package/dist/core-runtime/learning/shared/llm-caller.test.js +601 -0
  307. package/dist/core-runtime/learning/shared/llm-tool-loop.js +393 -0
  308. package/dist/core-runtime/learning/shared/mode.js +25 -0
  309. package/dist/core-runtime/learning/shared/paths.js +84 -0
  310. package/dist/core-runtime/learning/shared/paths.test.js +79 -0
  311. package/dist/core-runtime/learning/shared/patterns.js +37 -0
  312. package/dist/core-runtime/learning/shared/recoverability.js +355 -0
  313. package/dist/core-runtime/learning/shared/recovery-context.js +374 -0
  314. package/dist/core-runtime/learning/shared/scope.js +1 -0
  315. package/dist/core-runtime/learning/shared/semantic-classifier.js +94 -0
  316. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +42 -0
  317. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +37 -0
  318. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +39 -0
  319. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +41 -0
  320. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +38 -0
  321. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +43 -0
  322. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +113 -0
  323. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +36 -0
  324. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +48 -0
  325. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +43 -0
  326. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +64 -0
  327. package/dist/core-runtime/learning/usage-tracker.js +190 -0
  328. package/dist/core-runtime/learning/usage-tracker.test.js +176 -0
  329. package/dist/core-runtime/llm/llm-caller.js +649 -0
  330. package/dist/core-runtime/llm/llm-tool-loop.js +401 -0
  331. package/dist/core-runtime/llm/model-switcher.js +62 -0
  332. package/dist/core-runtime/logger.js +22 -0
  333. package/dist/core-runtime/onboard/detect-review-axes.js +122 -0
  334. package/dist/core-runtime/onboard/detect-review-axes.test.js +127 -0
  335. package/dist/core-runtime/onboard/write-review-block.js +188 -0
  336. package/dist/core-runtime/onboard/write-review-block.test.js +240 -0
  337. package/dist/core-runtime/readers/brownfield-builder.js +150 -0
  338. package/dist/core-runtime/readers/brownfield-builder.test.js +136 -0
  339. package/dist/core-runtime/readers/code-chunk-collector.js +53 -0
  340. package/dist/core-runtime/readers/code-chunk-collector.test.js +136 -0
  341. package/dist/core-runtime/readers/file-utils.js +240 -0
  342. package/dist/core-runtime/readers/file-utils.test.js +146 -0
  343. package/dist/core-runtime/readers/lexicon-citation-check.js +93 -0
  344. package/dist/core-runtime/readers/lexicon-citation-check.test.js +77 -0
  345. package/dist/core-runtime/readers/mcp-figma.js +30 -0
  346. package/dist/core-runtime/readers/mcp-figma.test.js +82 -0
  347. package/dist/core-runtime/readers/mcp-generic.js +31 -0
  348. package/dist/core-runtime/readers/mcp-generic.test.js +76 -0
  349. package/dist/core-runtime/readers/ontology-index.js +148 -0
  350. package/dist/core-runtime/readers/ontology-index.test.js +245 -0
  351. package/dist/core-runtime/readers/ontology-query.js +168 -0
  352. package/dist/core-runtime/readers/ontology-query.test.js +311 -0
  353. package/dist/core-runtime/readers/ontology-resolve.js +48 -0
  354. package/dist/core-runtime/readers/ontology-resolve.test.js +48 -0
  355. package/dist/core-runtime/readers/patterns/index.js +7 -0
  356. package/dist/core-runtime/readers/review-log.js +213 -0
  357. package/dist/core-runtime/readers/review-log.test.js +313 -0
  358. package/dist/core-runtime/readers/scan-local.js +102 -0
  359. package/dist/core-runtime/readers/scan-local.test.js +102 -0
  360. package/dist/core-runtime/readers/scan-tarball.js +121 -0
  361. package/dist/core-runtime/readers/scan-tarball.test.js +283 -0
  362. package/dist/core-runtime/readers/scan-vault.js +34 -0
  363. package/dist/core-runtime/readers/scan-vault.test.js +81 -0
  364. package/dist/core-runtime/readers/types.js +42 -0
  365. package/dist/core-runtime/readers/types.test.js +94 -0
  366. package/dist/core-runtime/readers/viewpoint-collectors.js +229 -0
  367. package/dist/core-runtime/reconstruct/artifact-types.js +1 -0
  368. package/dist/core-runtime/reconstruct/directive-validation.js +123 -0
  369. package/dist/core-runtime/reconstruct/materialize-preparation.js +251 -0
  370. package/dist/core-runtime/reconstruct/record.js +198 -0
  371. package/dist/core-runtime/reconstruct/run.js +578 -0
  372. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +356 -0
  373. package/dist/core-runtime/reconstruct/source-observations.js +62 -0
  374. package/dist/core-runtime/reconstruct/source-profiles.js +73 -0
  375. package/dist/core-runtime/release-channel/release-channel.js +90 -0
  376. package/dist/core-runtime/review/artifact-types.js +13 -0
  377. package/dist/core-runtime/review/citation-audit.js +204 -0
  378. package/dist/core-runtime/review/citation-audit.test.js +165 -0
  379. package/dist/core-runtime/review/controlled-lens-deliberation.js +125 -0
  380. package/dist/core-runtime/review/execution-plan-resolver.js +247 -0
  381. package/dist/core-runtime/review/execution-plan-resolver.test.js +243 -0
  382. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +246 -0
  383. package/dist/core-runtime/review/execution-topology-resolver.js +401 -0
  384. package/dist/core-runtime/review/execution-topology-resolver.test.js +315 -0
  385. package/dist/core-runtime/review/failure-records.js +57 -0
  386. package/dist/core-runtime/review/inline-context-embedder.js +141 -0
  387. package/dist/core-runtime/review/inline-context-embedder.test.js +154 -0
  388. package/dist/core-runtime/review/issue-artifact-runtime.js +859 -0
  389. package/dist/core-runtime/review/legacy-mode-policy.js +88 -0
  390. package/dist/core-runtime/review/lens-completion-policy.js +17 -0
  391. package/dist/core-runtime/review/materializers-effort-persist.test.js +79 -0
  392. package/dist/core-runtime/review/materializers.js +963 -0
  393. package/dist/core-runtime/review/ontology-path-classifier.js +179 -0
  394. package/dist/core-runtime/review/ontology-path-classifier.test.js +216 -0
  395. package/dist/core-runtime/review/packet-boundary-policy.js +215 -0
  396. package/dist/core-runtime/review/packet-boundary-policy.test.js +107 -0
  397. package/dist/core-runtime/review/participating-lens-paths.js +61 -0
  398. package/dist/core-runtime/review/participating-lens-paths.test.js +73 -0
  399. package/dist/core-runtime/review/review-artifact-utils.js +287 -0
  400. package/dist/core-runtime/review/review-config-legacy-translate.js +244 -0
  401. package/dist/core-runtime/review/review-config-legacy-translate.test.js +161 -0
  402. package/dist/core-runtime/review/review-config-validator.js +289 -0
  403. package/dist/core-runtime/review/review-config-validator.test.js +236 -0
  404. package/dist/core-runtime/review/review-execution-profile.js +193 -0
  405. package/dist/core-runtime/review/review-execution-route.js +52 -0
  406. package/dist/core-runtime/review/review-progress-contract.js +123 -0
  407. package/dist/core-runtime/review/review-record-validation.js +251 -0
  408. package/dist/core-runtime/review/review-result-classification.js +379 -0
  409. package/dist/core-runtime/review/review-state-machine.js +39 -0
  410. package/dist/core-runtime/review/route-visibility.js +125 -0
  411. package/dist/core-runtime/review/shape-pipeline-audit.test.js +311 -0
  412. package/dist/core-runtime/review/shape-to-topology-id.js +117 -0
  413. package/dist/core-runtime/review/shape-to-topology-id.test.js +132 -0
  414. package/dist/core-runtime/review/topology-shape-derivation.js +155 -0
  415. package/dist/core-runtime/review/topology-shape-derivation.test.js +195 -0
  416. package/dist/core-runtime/scope-runtime/constants.js +12 -0
  417. package/dist/core-runtime/scope-runtime/constraint-pool.js +166 -0
  418. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +674 -0
  419. package/dist/core-runtime/scope-runtime/domain-validation-log.js +135 -0
  420. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +156 -0
  421. package/dist/core-runtime/scope-runtime/eval-persistence.js +65 -0
  422. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +84 -0
  423. package/dist/core-runtime/scope-runtime/event-pipeline.js +64 -0
  424. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +450 -0
  425. package/dist/core-runtime/scope-runtime/event-store.js +39 -0
  426. package/dist/core-runtime/scope-runtime/event-store.test.js +95 -0
  427. package/dist/core-runtime/scope-runtime/gate-guard.js +348 -0
  428. package/dist/core-runtime/scope-runtime/gate-guard.test.js +1047 -0
  429. package/dist/core-runtime/scope-runtime/hash.js +4 -0
  430. package/dist/core-runtime/scope-runtime/hash.test.js +33 -0
  431. package/dist/core-runtime/scope-runtime/id.js +4 -0
  432. package/dist/core-runtime/scope-runtime/id.test.js +17 -0
  433. package/dist/core-runtime/scope-runtime/reducer.js +297 -0
  434. package/dist/core-runtime/scope-runtime/reducer.test.js +759 -0
  435. package/dist/core-runtime/scope-runtime/scope-manager.js +161 -0
  436. package/dist/core-runtime/scope-runtime/state-machine.js +309 -0
  437. package/dist/core-runtime/scope-runtime/state-machine.test.js +704 -0
  438. package/dist/core-runtime/scope-runtime/types.js +116 -0
  439. package/dist/core-runtime/scope-runtime/types.test.js +69 -0
  440. package/dist/core-runtime/target-material-kind.js +256 -0
  441. package/dist/core-runtime/translate/render-for-user.js +169 -0
  442. package/dist/core-runtime/translate/render-for-user.test.js +122 -0
  443. package/dist/mcp/server.js +1011 -0
  444. package/dist/mcp/tool-schemas.js +93 -0
  445. package/dist/providers/capability-contract.js +1 -0
  446. package/package.json +68 -0
  447. package/settings.example.json +33 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * /apply command orchestration.
3
+ *
4
+ * State-dependent behavior:
5
+ * - compiled → apply.started (start applying delta-set)
6
+ * - compiled → apply.completed (delta-set applied successfully)
7
+ * - compiled → apply.decision_gap_found (edge case found during apply → constraints_resolved)
8
+ * - applied → validation.started (begin validation)
9
+ * - applied → validation.completed (validate() + record result)
10
+ *
11
+ * Apply and validation are agent-driven.
12
+ * This module provides the event recording orchestration.
13
+ */
14
+ import { readEvents } from "../../scope-runtime/event-store.js";
15
+ import { reduce } from "../../scope-runtime/reducer.js";
16
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
17
+ import { wrapGateError } from "./error-messages.js";
18
+ import { refreshScopeMd } from "./shared.js";
19
+ import { validate } from "../adapters/code-product/validators/validate.js";
20
+ import { loadProjectConfig } from "../config/project-config.js";
21
+ // ─── Main ───
22
+ export function executeApply(paths, action, options) {
23
+ switch (action.type) {
24
+ case "start_apply":
25
+ return handleStartApply(paths, action, options?.projectRoot);
26
+ case "complete_apply":
27
+ return handleCompleteApply(paths, action);
28
+ case "report_gap":
29
+ return handleReportGap(paths, action);
30
+ case "start_validation":
31
+ return handleStartValidation(paths, action);
32
+ case "complete_validation":
33
+ return handleCompleteValidation(paths, action);
34
+ }
35
+ }
36
+ // ─── Action Handlers ───
37
+ // Note: apply.started is a self-transition (compiled → compiled).
38
+ // If the process is interrupted before complete_apply, this event can be
39
+ // re-recorded on resume without state corruption. Duplicate self-transitions are acceptable.
40
+ function handleStartApply(paths, action, projectRoot) {
41
+ const config = projectRoot ? loadProjectConfig(projectRoot) : { default_sources: [] };
42
+ const result = appendScopeEvent(paths, {
43
+ type: "apply.started",
44
+ actor: "agent",
45
+ payload: { build_spec_hash: action.buildSpecHash },
46
+ }, { apply_enabled: config.apply_enabled });
47
+ if (!result.success)
48
+ return { success: false, reason: wrapGateError(result.reason) };
49
+ refreshScopeMd(paths, result.state);
50
+ return {
51
+ success: true,
52
+ nextState: result.next_state,
53
+ message: "Apply가 시작되었습니다. delta-set의 변경 사항을 적용하세요.",
54
+ };
55
+ }
56
+ function handleCompleteApply(paths, action) {
57
+ const result = appendScopeEvent(paths, {
58
+ type: "apply.completed",
59
+ actor: "agent",
60
+ payload: { result: action.result },
61
+ });
62
+ if (!result.success)
63
+ return { success: false, reason: wrapGateError(result.reason) };
64
+ refreshScopeMd(paths, result.state);
65
+ return {
66
+ success: true,
67
+ nextState: result.next_state,
68
+ message: "구현이 완료되었습니다. validation을 시작하세요.",
69
+ };
70
+ }
71
+ function handleReportGap(paths, action) {
72
+ // 1. constraint.discovered 먼저 기록 (참조 무결성)
73
+ const discoverResult = appendScopeEvent(paths, {
74
+ type: "constraint.discovered",
75
+ actor: "system",
76
+ payload: action.constraintPayload,
77
+ });
78
+ if (!discoverResult.success)
79
+ return { success: false, reason: wrapGateError(discoverResult.reason) };
80
+ // 2. apply.decision_gap_found 기록 → constraints_resolved로 역전이
81
+ const gapResult = appendScopeEvent(paths, {
82
+ type: "apply.decision_gap_found",
83
+ actor: "agent",
84
+ payload: action.gapPayload,
85
+ });
86
+ if (!gapResult.success)
87
+ return { success: false, reason: wrapGateError(gapResult.reason) };
88
+ refreshScopeMd(paths, gapResult.state);
89
+ return {
90
+ success: true,
91
+ nextState: gapResult.next_state,
92
+ message: `Gap이 발견되었습니다 (${action.gapPayload.new_constraint_id}). Draft에서 재결정 후 재compile이 필요합니다.`,
93
+ };
94
+ }
95
+ // Note: validation.started is a self-transition (applied → applied).
96
+ // Same resilience pattern as apply.started above.
97
+ function handleStartValidation(paths, action) {
98
+ const result = appendScopeEvent(paths, {
99
+ type: "validation.started",
100
+ actor: "agent",
101
+ payload: { validation_plan_hash: action.validationPlanHash },
102
+ });
103
+ if (!result.success)
104
+ return { success: false, reason: wrapGateError(result.reason) };
105
+ refreshScopeMd(paths, result.state);
106
+ return {
107
+ success: true,
108
+ nextState: result.next_state,
109
+ message: "Validation이 시작되었습니다. 각 VAL 항목을 검증하세요.",
110
+ };
111
+ }
112
+ function handleCompleteValidation(paths, action) {
113
+ // 1. validate() 순수 함수 호출
114
+ const state = reduce(readEvents(paths.events));
115
+ const validateOutput = validate({
116
+ state,
117
+ plan: action.plan,
118
+ results: action.results,
119
+ actualPlanHash: action.actualPlanHash,
120
+ });
121
+ if (!validateOutput.success) {
122
+ return { success: false, reason: wrapGateError(validateOutput.reason) };
123
+ }
124
+ // 2. validation.completed 이벤트 기록
125
+ const result = appendScopeEvent(paths, {
126
+ type: "validation.completed",
127
+ actor: "agent",
128
+ payload: {
129
+ result: validateOutput.result,
130
+ pass_count: validateOutput.pass_count,
131
+ fail_count: validateOutput.fail_count,
132
+ items: validateOutput.items,
133
+ },
134
+ });
135
+ if (!result.success)
136
+ return { success: false, reason: wrapGateError(result.reason) };
137
+ refreshScopeMd(paths, result.state);
138
+ if (validateOutput.result === "pass") {
139
+ return {
140
+ success: true,
141
+ nextState: result.next_state,
142
+ message: "모든 검증이 통과했습니다. 결과를 확인하시고, 종료하려면 '완료'라고 말씀해 주세요.",
143
+ data: {
144
+ result: validateOutput.result,
145
+ pass_count: validateOutput.pass_count,
146
+ fail_count: validateOutput.fail_count,
147
+ },
148
+ };
149
+ }
150
+ return {
151
+ success: true,
152
+ nextState: result.next_state,
153
+ message: `검증 실패: ${validateOutput.fail_count}건. 해당 constraint에 대해 재결정이 필요합니다.`,
154
+ data: {
155
+ result: validateOutput.result,
156
+ pass_count: validateOutput.pass_count,
157
+ fail_count: validateOutput.fail_count,
158
+ },
159
+ };
160
+ }
161
+ // refreshScopeMd is imported from ./shared.ts (UF-CONCISENESS-SCOPE-MD consolidated)
@@ -0,0 +1,138 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { executeApply } from "./apply.js";
6
+ import { createScope } from "../../scope-runtime/scope-manager.js";
7
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
8
+ import { readEvents } from "../../scope-runtime/event-store.js";
9
+ import { reduce } from "../../scope-runtime/reducer.js";
10
+ let tmpDir;
11
+ beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), "sprint-apply-")); });
12
+ afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); });
13
+ /** Set up scope through compiled state */
14
+ function setupCompiled() {
15
+ writeFileSync(join(tmpDir, ".sprint-kit.yaml"), "apply_enabled: true\ndefault_sources: []\n", "utf-8");
16
+ const paths = createScope(tmpDir, "SC-APPLY-001");
17
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "Test", description: "d", entry_mode: "experience" } });
18
+ appendScopeEvent(paths, { type: "grounding.started", actor: "system", payload: { sources: [{ type: "add-dir", path_or_url: "/test" }] } });
19
+ appendScopeEvent(paths, { type: "grounding.completed", actor: "system", payload: { snapshot_revision: 1, source_hashes: { "add-dir:/test": "h1" }, perspective_summary: { experience: 1, code: 1, policy: 1 } } });
20
+ appendScopeEvent(paths, { type: "align.proposed", actor: "system", payload: { packet_path: "build/align-packet.md", packet_hash: "h", snapshot_revision: 1 } });
21
+ appendScopeEvent(paths, { type: "align.locked", actor: "user", payload: { locked_direction: "test", locked_scope_boundaries: { in: ["a"], out: ["b"] }, locked_in_out: true } });
22
+ appendScopeEvent(paths, { type: "surface.generated", actor: "system", payload: { surface_type: "experience", surface_path: "surface/preview/", content_hash: "sf1", based_on_snapshot: 1 } });
23
+ appendScopeEvent(paths, { type: "surface.confirmed", actor: "user", payload: { final_surface_path: "surface/preview/", final_content_hash: "sf1", total_revisions: 0 } });
24
+ appendScopeEvent(paths, { type: "constraint.discovered", actor: "system", payload: { constraint_id: "CST-001", perspective: "code", summary: "test constraint", severity: "required", discovery_stage: "draft_phase2", decision_owner: "product_owner", impact_if_ignored: "fails", source_refs: [{ source: "test.ts", detail: "d" }] } });
25
+ appendScopeEvent(paths, { type: "constraint.decision_recorded", actor: "user", payload: { constraint_id: "CST-001", decision: "inject", selected_option: "fix", decision_owner: "product_owner", rationale: "필수" } });
26
+ appendScopeEvent(paths, { type: "target.locked", actor: "system", payload: { surface_hash: "sf1", constraint_decisions: [{ constraint_id: "CST-001", decision: "inject" }] } });
27
+ appendScopeEvent(paths, { type: "compile.started", actor: "system", payload: { snapshot_revision: 1, surface_hash: "sf1" } });
28
+ appendScopeEvent(paths, { type: "compile.completed", actor: "system", payload: { build_spec_path: "build/build-spec.md", build_spec_hash: "bs1", brownfield_detail_path: "build/brownfield-detail.md", brownfield_detail_hash: "bf1", delta_set_path: "build/delta-set.json", delta_set_hash: "ds1", validation_plan_path: "build/validation-plan.md", validation_plan_hash: "vp1" } });
29
+ appendScopeEvent(paths, { type: "pre_apply.review_completed", actor: "agent", payload: { verdict: "pass", findings: [] } });
30
+ appendScopeEvent(paths, { type: "prd.review_completed", actor: "agent", payload: { verdict: "pass", perspectives: ["prd_logic", "prd_structure", "prd_dependency", "prd_semantics", "prd_pragmatics", "prd_evolution", "prd_coverage", "prd_conciseness"], findings: [] } });
31
+ return paths;
32
+ }
33
+ /** Set up scope through applied state */
34
+ function setupApplied() {
35
+ const paths = setupCompiled();
36
+ appendScopeEvent(paths, { type: "apply.started", actor: "agent", payload: { build_spec_hash: "bs1" } }, { apply_enabled: true });
37
+ appendScopeEvent(paths, { type: "apply.completed", actor: "agent", payload: { result: "success" } });
38
+ return paths;
39
+ }
40
+ describe("executeApply", () => {
41
+ it("start_apply → compiled (self transition)", () => {
42
+ const paths = setupCompiled();
43
+ const result = executeApply(paths, { type: "start_apply", buildSpecHash: "bs1" }, { projectRoot: tmpDir });
44
+ expect(result.success).toBe(true);
45
+ if (!result.success)
46
+ return;
47
+ expect(result.nextState).toBe("compiled");
48
+ const state = reduce(readEvents(paths.events));
49
+ expect(state.current_state).toBe("compiled");
50
+ });
51
+ it("complete_apply → applied", () => {
52
+ const paths = setupCompiled();
53
+ executeApply(paths, { type: "start_apply", buildSpecHash: "bs1" });
54
+ const result = executeApply(paths, { type: "complete_apply", result: "success" });
55
+ expect(result.success).toBe(true);
56
+ if (!result.success)
57
+ return;
58
+ expect(result.nextState).toBe("applied");
59
+ const state = reduce(readEvents(paths.events));
60
+ expect(state.current_state).toBe("applied");
61
+ });
62
+ it("full happy path: start → complete → validate → pass", () => {
63
+ const paths = setupCompiled();
64
+ // start_apply
65
+ const startResult = executeApply(paths, { type: "start_apply", buildSpecHash: "bs1" }, { projectRoot: tmpDir });
66
+ expect(startResult.success).toBe(true);
67
+ // complete_apply
68
+ const completeResult = executeApply(paths, { type: "complete_apply", result: "success" });
69
+ expect(completeResult.success).toBe(true);
70
+ // start_validation
71
+ const valStartResult = executeApply(paths, { type: "start_validation", validationPlanHash: "vp1" });
72
+ expect(valStartResult.success).toBe(true);
73
+ // complete_validation (pass)
74
+ const valCompleteResult = executeApply(paths, {
75
+ type: "complete_validation",
76
+ plan: [{ val_id: "VAL-001", related_cst: "CST-001", decision_type: "inject", target: "test.ts", method: "manual", pass_criteria: "works", fail_action: "fix" }],
77
+ results: [{ val_id: "VAL-001", related_cst: "CST-001", result: "pass", detail: "확인 완료" }],
78
+ actualPlanHash: "vp1",
79
+ });
80
+ expect(valCompleteResult.success).toBe(true);
81
+ if (!valCompleteResult.success)
82
+ return;
83
+ expect(valCompleteResult.nextState).toBe("validated");
84
+ const state = reduce(readEvents(paths.events));
85
+ expect(state.current_state).toBe("validated");
86
+ });
87
+ it("report_gap → constraints_resolved (backward transition)", () => {
88
+ const paths = setupCompiled();
89
+ executeApply(paths, { type: "start_apply", buildSpecHash: "bs1" });
90
+ const result = executeApply(paths, {
91
+ type: "report_gap",
92
+ constraintPayload: {
93
+ constraint_id: "CST-002",
94
+ perspective: "code",
95
+ summary: "edge case 발견",
96
+ severity: "required",
97
+ discovery_stage: "apply",
98
+ decision_owner: "product_owner",
99
+ impact_if_ignored: "data loss",
100
+ source_refs: [{ source: "handler.ts", detail: "line 42" }],
101
+ },
102
+ gapPayload: {
103
+ new_constraint_id: "CST-002",
104
+ description: "미결정 edge case 발견",
105
+ },
106
+ });
107
+ expect(result.success).toBe(true);
108
+ if (!result.success)
109
+ return;
110
+ expect(result.nextState).toBe("constraints_resolved");
111
+ const state = reduce(readEvents(paths.events));
112
+ expect(state.current_state).toBe("constraints_resolved");
113
+ });
114
+ it("validation fail → constraints_resolved", () => {
115
+ const paths = setupApplied();
116
+ // start_validation
117
+ executeApply(paths, { type: "start_validation", validationPlanHash: "vp1" });
118
+ // complete_validation (fail)
119
+ const result = executeApply(paths, {
120
+ type: "complete_validation",
121
+ plan: [{ val_id: "VAL-001", related_cst: "CST-001", decision_type: "inject", target: "test.ts", method: "manual", pass_criteria: "works", fail_action: "fix" }],
122
+ results: [{ val_id: "VAL-001", related_cst: "CST-001", result: "fail", detail: "구현 누락" }],
123
+ actualPlanHash: "vp1",
124
+ });
125
+ expect(result.success).toBe(true);
126
+ if (!result.success)
127
+ return;
128
+ expect(result.nextState).toBe("constraints_resolved");
129
+ expect(result.data?.result).toBe("fail");
130
+ const state = reduce(readEvents(paths.events));
131
+ expect(state.current_state).toBe("constraints_resolved");
132
+ });
133
+ it("start_apply fails when not in compiled state", () => {
134
+ const paths = setupApplied(); // already applied
135
+ const result = executeApply(paths, { type: "start_apply", buildSpecHash: "bs1" }, { projectRoot: tmpDir });
136
+ expect(result.success).toBe(false);
137
+ });
138
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * /close command orchestration.
3
+ *
4
+ * Closes a validated scope:
5
+ * - validated → scope.closed → closed
6
+ *
7
+ * Only allowed when current state is "validated".
8
+ */
9
+ import { readEvents } from "../../scope-runtime/event-store.js";
10
+ import { reduce } from "../../scope-runtime/reducer.js";
11
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
12
+ import { wrapGateError } from "./error-messages.js";
13
+ import { refreshScopeMd } from "./shared.js";
14
+ // ─── Main ───
15
+ export function executeClose(paths) {
16
+ const events = readEvents(paths.events);
17
+ const state = reduce(events);
18
+ if (state.current_state !== "validated") {
19
+ return {
20
+ success: false,
21
+ reason: `현재 상태가 ${state.current_state}입니다. /close는 validated 상태에서만 실행할 수 있습니다.`,
22
+ };
23
+ }
24
+ const result = appendScopeEvent(paths, {
25
+ type: "scope.closed",
26
+ actor: "user",
27
+ payload: {},
28
+ });
29
+ if (!result.success)
30
+ return { success: false, reason: wrapGateError(result.reason) };
31
+ refreshScopeMd(paths, result.state);
32
+ return {
33
+ success: true,
34
+ nextState: "closed",
35
+ message: "Scope가 종료되었습니다.",
36
+ };
37
+ }
38
+ // refreshScopeMd is imported from ./shared.ts (UF-CONCISENESS-SCOPE-MD consolidated)
39
+ // executeDefer lives in ./defer.ts so deferral has its own runtime adapter surface.
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { executeClose } from "./close.js";
6
+ import { createScope } from "../../scope-runtime/scope-manager.js";
7
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
8
+ import { readEvents } from "../../scope-runtime/event-store.js";
9
+ import { reduce } from "../../scope-runtime/reducer.js";
10
+ let tmpDir;
11
+ beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), "sprint-close-")); });
12
+ afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); });
13
+ /** Set up scope through validated state */
14
+ function setupValidated() {
15
+ const paths = createScope(tmpDir, "SC-CLOSE-001");
16
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "Test", description: "d", entry_mode: "experience" } });
17
+ appendScopeEvent(paths, { type: "grounding.started", actor: "system", payload: { sources: [{ type: "add-dir", path_or_url: "/test" }] } });
18
+ appendScopeEvent(paths, { type: "grounding.completed", actor: "system", payload: { snapshot_revision: 1, source_hashes: { "add-dir:/test": "h1" }, perspective_summary: { experience: 1, code: 1, policy: 1 } } });
19
+ appendScopeEvent(paths, { type: "align.proposed", actor: "system", payload: { packet_path: "build/align-packet.md", packet_hash: "h", snapshot_revision: 1 } });
20
+ appendScopeEvent(paths, { type: "align.locked", actor: "user", payload: { locked_direction: "test", locked_scope_boundaries: { in: ["a"], out: ["b"] }, locked_in_out: true } });
21
+ appendScopeEvent(paths, { type: "surface.generated", actor: "system", payload: { surface_type: "experience", surface_path: "surface/preview/", content_hash: "sf1", based_on_snapshot: 1 } });
22
+ appendScopeEvent(paths, { type: "surface.confirmed", actor: "user", payload: { final_surface_path: "surface/preview/", final_content_hash: "sf1", total_revisions: 0 } });
23
+ appendScopeEvent(paths, { type: "constraint.discovered", actor: "system", payload: { constraint_id: "CST-001", perspective: "code", summary: "test constraint", severity: "required", discovery_stage: "draft_phase2", decision_owner: "product_owner", impact_if_ignored: "fails", source_refs: [{ source: "test.ts", detail: "d" }] } });
24
+ appendScopeEvent(paths, { type: "constraint.decision_recorded", actor: "user", payload: { constraint_id: "CST-001", decision: "inject", selected_option: "fix", decision_owner: "product_owner", rationale: "필수" } });
25
+ appendScopeEvent(paths, { type: "target.locked", actor: "system", payload: { surface_hash: "sf1", constraint_decisions: [{ constraint_id: "CST-001", decision: "inject" }] } });
26
+ appendScopeEvent(paths, { type: "compile.started", actor: "system", payload: { snapshot_revision: 1, surface_hash: "sf1" } });
27
+ appendScopeEvent(paths, { type: "compile.completed", actor: "system", payload: { build_spec_path: "build/build-spec.md", build_spec_hash: "bs1", brownfield_detail_path: "build/brownfield-detail.md", brownfield_detail_hash: "bf1", delta_set_path: "build/delta-set.json", delta_set_hash: "ds1", validation_plan_path: "build/validation-plan.md", validation_plan_hash: "vp1" } });
28
+ appendScopeEvent(paths, { type: "apply.started", actor: "agent", payload: { build_spec_hash: "bs1" } });
29
+ appendScopeEvent(paths, { type: "apply.completed", actor: "agent", payload: { result: "success" } });
30
+ appendScopeEvent(paths, { type: "validation.started", actor: "agent", payload: { validation_plan_hash: "vp1" } });
31
+ appendScopeEvent(paths, { type: "validation.completed", actor: "agent", payload: { result: "pass", pass_count: 1, fail_count: 0, items: [{ val_id: "VAL-001", related_cst: "CST-001", result: "pass", detail: "확인 완료" }] } });
32
+ return paths;
33
+ }
34
+ describe("executeClose", () => {
35
+ it("validated → closed", () => {
36
+ const paths = setupValidated();
37
+ const state = reduce(readEvents(paths.events));
38
+ expect(state.current_state).toBe("validated");
39
+ const result = executeClose(paths);
40
+ expect(result.success).toBe(true);
41
+ if (!result.success)
42
+ return;
43
+ expect(result.nextState).toBe("closed");
44
+ const updatedState = reduce(readEvents(paths.events));
45
+ expect(updatedState.current_state).toBe("closed");
46
+ });
47
+ it("fails when not in validated state (draft)", () => {
48
+ const paths = createScope(tmpDir, "SC-CLOSE-002");
49
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "T", description: "d", entry_mode: "experience" } });
50
+ const result = executeClose(paths);
51
+ expect(result.success).toBe(false);
52
+ if (result.success)
53
+ return;
54
+ expect(result.reason).toContain("validated");
55
+ });
56
+ it("fails when in compiled state", () => {
57
+ const paths = createScope(tmpDir, "SC-CLOSE-003");
58
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "Test", description: "d", entry_mode: "experience" } });
59
+ appendScopeEvent(paths, { type: "grounding.started", actor: "system", payload: { sources: [{ type: "add-dir", path_or_url: "/test" }] } });
60
+ appendScopeEvent(paths, { type: "grounding.completed", actor: "system", payload: { snapshot_revision: 1, source_hashes: { "add-dir:/test": "h1" }, perspective_summary: { experience: 1, code: 1, policy: 1 } } });
61
+ appendScopeEvent(paths, { type: "align.proposed", actor: "system", payload: { packet_path: "build/align-packet.md", packet_hash: "h", snapshot_revision: 1 } });
62
+ appendScopeEvent(paths, { type: "align.locked", actor: "user", payload: { locked_direction: "test", locked_scope_boundaries: { in: ["a"], out: ["b"] }, locked_in_out: true } });
63
+ appendScopeEvent(paths, { type: "surface.generated", actor: "system", payload: { surface_type: "experience", surface_path: "surface/preview/", content_hash: "sf1", based_on_snapshot: 1 } });
64
+ appendScopeEvent(paths, { type: "surface.confirmed", actor: "user", payload: { final_surface_path: "surface/preview/", final_content_hash: "sf1", total_revisions: 0 } });
65
+ appendScopeEvent(paths, { type: "constraint.discovered", actor: "system", payload: { constraint_id: "CST-001", perspective: "code", summary: "c", severity: "required", discovery_stage: "draft_phase2", decision_owner: "product_owner", impact_if_ignored: "f", source_refs: [{ source: "t", detail: "d" }] } });
66
+ appendScopeEvent(paths, { type: "constraint.decision_recorded", actor: "user", payload: { constraint_id: "CST-001", decision: "inject", selected_option: "fix", decision_owner: "product_owner", rationale: "r" } });
67
+ appendScopeEvent(paths, { type: "target.locked", actor: "system", payload: { surface_hash: "sf1", constraint_decisions: [{ constraint_id: "CST-001", decision: "inject" }] } });
68
+ appendScopeEvent(paths, { type: "compile.started", actor: "system", payload: { snapshot_revision: 1, surface_hash: "sf1" } });
69
+ appendScopeEvent(paths, { type: "compile.completed", actor: "system", payload: { build_spec_path: "build/build-spec.md", build_spec_hash: "bs1", brownfield_detail_path: "build/brownfield-detail.md", brownfield_detail_hash: "bf1", delta_set_path: "build/delta-set.json", delta_set_hash: "ds1", validation_plan_path: "build/validation-plan.md", validation_plan_hash: "vp1" } });
70
+ const result = executeClose(paths);
71
+ expect(result.success).toBe(false);
72
+ if (result.success)
73
+ return;
74
+ expect(result.reason).toContain("validated");
75
+ });
76
+ it("fails when in applied state", () => {
77
+ const paths = createScope(tmpDir, "SC-CLOSE-004");
78
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "Test", description: "d", entry_mode: "experience" } });
79
+ appendScopeEvent(paths, { type: "grounding.started", actor: "system", payload: { sources: [{ type: "add-dir", path_or_url: "/test" }] } });
80
+ appendScopeEvent(paths, { type: "grounding.completed", actor: "system", payload: { snapshot_revision: 1, source_hashes: { "add-dir:/test": "h1" }, perspective_summary: { experience: 1, code: 1, policy: 1 } } });
81
+ appendScopeEvent(paths, { type: "align.proposed", actor: "system", payload: { packet_path: "build/align-packet.md", packet_hash: "h", snapshot_revision: 1 } });
82
+ appendScopeEvent(paths, { type: "align.locked", actor: "user", payload: { locked_direction: "test", locked_scope_boundaries: { in: ["a"], out: ["b"] }, locked_in_out: true } });
83
+ appendScopeEvent(paths, { type: "surface.generated", actor: "system", payload: { surface_type: "experience", surface_path: "surface/preview/", content_hash: "sf1", based_on_snapshot: 1 } });
84
+ appendScopeEvent(paths, { type: "surface.confirmed", actor: "user", payload: { final_surface_path: "surface/preview/", final_content_hash: "sf1", total_revisions: 0 } });
85
+ appendScopeEvent(paths, { type: "constraint.discovered", actor: "system", payload: { constraint_id: "CST-001", perspective: "code", summary: "c", severity: "required", discovery_stage: "draft_phase2", decision_owner: "product_owner", impact_if_ignored: "f", source_refs: [{ source: "t", detail: "d" }] } });
86
+ appendScopeEvent(paths, { type: "constraint.decision_recorded", actor: "user", payload: { constraint_id: "CST-001", decision: "inject", selected_option: "fix", decision_owner: "product_owner", rationale: "r" } });
87
+ appendScopeEvent(paths, { type: "target.locked", actor: "system", payload: { surface_hash: "sf1", constraint_decisions: [{ constraint_id: "CST-001", decision: "inject" }] } });
88
+ appendScopeEvent(paths, { type: "compile.started", actor: "system", payload: { snapshot_revision: 1, surface_hash: "sf1" } });
89
+ appendScopeEvent(paths, { type: "compile.completed", actor: "system", payload: { build_spec_path: "build/build-spec.md", build_spec_hash: "bs1", brownfield_detail_path: "build/brownfield-detail.md", brownfield_detail_hash: "bf1", delta_set_path: "build/delta-set.json", delta_set_hash: "ds1", validation_plan_path: "build/validation-plan.md", validation_plan_hash: "vp1" } });
90
+ appendScopeEvent(paths, { type: "apply.started", actor: "agent", payload: { build_spec_hash: "bs1" } });
91
+ appendScopeEvent(paths, { type: "apply.completed", actor: "agent", payload: { result: "success" } });
92
+ const result = executeClose(paths);
93
+ expect(result.success).toBe(false);
94
+ if (result.success)
95
+ return;
96
+ expect(result.reason).toContain("validated");
97
+ });
98
+ });
99
+ // executeDefer tests have moved to ./defer.test.ts
@@ -0,0 +1,40 @@
1
+ /**
2
+ * /defer command orchestration.
3
+ *
4
+ * Defers a non-terminal scope with a reason and a resume condition:
5
+ * any non-terminal state → scope.deferred → deferred
6
+ *
7
+ * Rejected when the scope is already in a terminal state (closed, deferred,
8
+ * rejected). Separated from close.ts so that deferral has its own runtime
9
+ * surface (BL-061: executeDefer existed but was not reachable from the evolve runtime adapter).
10
+ */
11
+ import { readEvents } from "../../scope-runtime/event-store.js";
12
+ import { reduce } from "../../scope-runtime/reducer.js";
13
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
14
+ import { wrapGateError } from "./error-messages.js";
15
+ import { refreshScopeMd } from "./shared.js";
16
+ // ─── Main ───
17
+ export function executeDefer(paths, reason, resumeCondition) {
18
+ const events = readEvents(paths.events);
19
+ const state = reduce(events);
20
+ const terminalStates = ["closed", "deferred", "rejected"];
21
+ if (terminalStates.includes(state.current_state)) {
22
+ return {
23
+ success: false,
24
+ reason: `현재 상태가 ${state.current_state}입니다. 이미 종료된 scope는 보류할 수 없습니다.`,
25
+ };
26
+ }
27
+ const result = appendScopeEvent(paths, {
28
+ type: "scope.deferred",
29
+ actor: "user",
30
+ payload: { reason, resume_condition: resumeCondition },
31
+ });
32
+ if (!result.success)
33
+ return { success: false, reason: wrapGateError(result.reason) };
34
+ refreshScopeMd(paths, result.state);
35
+ return {
36
+ success: true,
37
+ nextState: "deferred",
38
+ message: "Scope가 보류되었습니다.",
39
+ };
40
+ }
@@ -0,0 +1,134 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { executeDefer } from "./defer.js";
6
+ import { executeClose } from "./close.js";
7
+ import { createScope } from "../../scope-runtime/scope-manager.js";
8
+ import { appendScopeEvent } from "../../scope-runtime/event-pipeline.js";
9
+ import { readEvents } from "../../scope-runtime/event-store.js";
10
+ import { reduce } from "../../scope-runtime/reducer.js";
11
+ import { handleEvolveCli } from "../cli.js";
12
+ let tmpDir;
13
+ beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), "sprint-defer-")); });
14
+ afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); });
15
+ /** Set up a scope through validated state so terminal-state tests can close it. */
16
+ function setupValidated(scopeId) {
17
+ const paths = createScope(tmpDir, scopeId);
18
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "Test", description: "d", entry_mode: "experience" } });
19
+ appendScopeEvent(paths, { type: "grounding.started", actor: "system", payload: { sources: [{ type: "add-dir", path_or_url: "/test" }] } });
20
+ appendScopeEvent(paths, { type: "grounding.completed", actor: "system", payload: { snapshot_revision: 1, source_hashes: { "add-dir:/test": "h1" }, perspective_summary: { experience: 1, code: 1, policy: 1 } } });
21
+ appendScopeEvent(paths, { type: "align.proposed", actor: "system", payload: { packet_path: "build/align-packet.md", packet_hash: "h", snapshot_revision: 1 } });
22
+ appendScopeEvent(paths, { type: "align.locked", actor: "user", payload: { locked_direction: "test", locked_scope_boundaries: { in: ["a"], out: ["b"] }, locked_in_out: true } });
23
+ appendScopeEvent(paths, { type: "surface.generated", actor: "system", payload: { surface_type: "experience", surface_path: "surface/preview/", content_hash: "sf1", based_on_snapshot: 1 } });
24
+ appendScopeEvent(paths, { type: "surface.confirmed", actor: "user", payload: { final_surface_path: "surface/preview/", final_content_hash: "sf1", total_revisions: 0 } });
25
+ appendScopeEvent(paths, { type: "constraint.discovered", actor: "system", payload: { constraint_id: "CST-001", perspective: "code", summary: "c", severity: "required", discovery_stage: "draft_phase2", decision_owner: "product_owner", impact_if_ignored: "f", source_refs: [{ source: "t", detail: "d" }] } });
26
+ appendScopeEvent(paths, { type: "constraint.decision_recorded", actor: "user", payload: { constraint_id: "CST-001", decision: "inject", selected_option: "fix", decision_owner: "product_owner", rationale: "r" } });
27
+ appendScopeEvent(paths, { type: "target.locked", actor: "system", payload: { surface_hash: "sf1", constraint_decisions: [{ constraint_id: "CST-001", decision: "inject" }] } });
28
+ appendScopeEvent(paths, { type: "compile.started", actor: "system", payload: { snapshot_revision: 1, surface_hash: "sf1" } });
29
+ appendScopeEvent(paths, { type: "compile.completed", actor: "system", payload: { build_spec_path: "build/build-spec.md", build_spec_hash: "bs1", brownfield_detail_path: "build/brownfield-detail.md", brownfield_detail_hash: "bf1", delta_set_path: "build/delta-set.json", delta_set_hash: "ds1", validation_plan_path: "build/validation-plan.md", validation_plan_hash: "vp1" } });
30
+ appendScopeEvent(paths, { type: "apply.started", actor: "agent", payload: { build_spec_hash: "bs1" } });
31
+ appendScopeEvent(paths, { type: "apply.completed", actor: "agent", payload: { result: "success" } });
32
+ appendScopeEvent(paths, { type: "validation.started", actor: "agent", payload: { validation_plan_hash: "vp1" } });
33
+ appendScopeEvent(paths, { type: "validation.completed", actor: "agent", payload: { result: "pass", pass_count: 1, fail_count: 0, items: [{ val_id: "VAL-001", related_cst: "CST-001", result: "pass", detail: "확인 완료" }] } });
34
+ return paths;
35
+ }
36
+ describe("executeDefer", () => {
37
+ it("defers from any non-terminal state", () => {
38
+ const paths = createScope(tmpDir, "SC-DEFER-001");
39
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "T", description: "d", entry_mode: "experience" } });
40
+ const result = executeDefer(paths, "방향 재정립 필요", "다음 분기 재개");
41
+ expect(result.success).toBe(true);
42
+ if (!result.success)
43
+ return;
44
+ expect(result.nextState).toBe("deferred");
45
+ const state = reduce(readEvents(paths.events));
46
+ expect(state.current_state).toBe("deferred");
47
+ });
48
+ it("fails from terminal state (closed)", () => {
49
+ const paths = setupValidated("SC-DEFER-002");
50
+ executeClose(paths); // → closed
51
+ const result = executeDefer(paths, "test", "test");
52
+ expect(result.success).toBe(false);
53
+ if (result.success)
54
+ return;
55
+ expect(result.reason).toContain("closed");
56
+ });
57
+ it("fails when already deferred", () => {
58
+ const paths = createScope(tmpDir, "SC-DEFER-003");
59
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "T", description: "d", entry_mode: "experience" } });
60
+ executeDefer(paths, "first", "cond");
61
+ const result = executeDefer(paths, "second", "cond");
62
+ expect(result.success).toBe(false);
63
+ if (result.success)
64
+ return;
65
+ expect(result.reason).toContain("deferred");
66
+ });
67
+ });
68
+ describe("onto evolve defer — CLI surface", () => {
69
+ let logs;
70
+ let errors;
71
+ let logSpy;
72
+ let errorSpy;
73
+ beforeEach(() => {
74
+ logs = [];
75
+ errors = [];
76
+ logSpy = vi.spyOn(console, "log").mockImplementation((msg) => {
77
+ logs.push(String(msg));
78
+ });
79
+ errorSpy = vi.spyOn(console, "error").mockImplementation((msg) => {
80
+ errors.push(String(msg));
81
+ });
82
+ });
83
+ afterEach(() => {
84
+ logSpy.mockRestore();
85
+ errorSpy.mockRestore();
86
+ });
87
+ it("routes defer subcommand to executeDefer and succeeds", async () => {
88
+ const scopesDir = join(tmpDir, "scopes");
89
+ const paths = createScope(scopesDir, "SC-CLI-DEFER-001");
90
+ appendScopeEvent(paths, { type: "scope.created", actor: "user", payload: { title: "T", description: "d", entry_mode: "experience" } });
91
+ const code = await handleEvolveCli(tmpDir, [
92
+ "defer",
93
+ "--scope-id", "SC-CLI-DEFER-001",
94
+ "--scopes-dir", scopesDir,
95
+ "--reason", "방향 재정립",
96
+ "--resume-condition", "다음 분기",
97
+ ]);
98
+ expect(code).toBe(0);
99
+ const state = reduce(readEvents(paths.events));
100
+ expect(state.current_state).toBe("deferred");
101
+ const output = logs.join("\n");
102
+ expect(output).toContain("\"success\": true");
103
+ expect(output).toContain("\"nextState\": \"deferred\"");
104
+ });
105
+ it("rejects defer when --reason missing", async () => {
106
+ const scopesDir = join(tmpDir, "scopes");
107
+ createScope(scopesDir, "SC-CLI-DEFER-002");
108
+ const code = await handleEvolveCli(tmpDir, [
109
+ "defer",
110
+ "--scope-id", "SC-CLI-DEFER-002",
111
+ "--scopes-dir", scopesDir,
112
+ "--resume-condition", "cond",
113
+ ]);
114
+ expect(code).toBe(1);
115
+ expect(errors.some(e => e.includes("--reason") && e.includes("--resume-condition"))).toBe(true);
116
+ });
117
+ it("rejects defer when --scope-id missing", async () => {
118
+ const code = await handleEvolveCli(tmpDir, [
119
+ "defer",
120
+ "--reason", "r",
121
+ "--resume-condition", "c",
122
+ ]);
123
+ expect(code).toBe(1);
124
+ expect(errors.some(e => e.includes("--scope-id"))).toBe(true);
125
+ });
126
+ it("lists defer in --help output", async () => {
127
+ const code = await handleEvolveCli(tmpDir, ["--help"]);
128
+ expect(code).toBe(0);
129
+ const output = logs.join("\n");
130
+ expect(output).toContain("defer --scope-id");
131
+ expect(output).toContain("--reason");
132
+ expect(output).toContain("--resume-condition");
133
+ });
134
+ });