@planu/cli 4.13.0 → 5.0.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 (1066) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/LICENSE +55 -12
  3. package/README.md +33 -25
  4. package/dist/cli/commands/inspect.d.ts +4 -0
  5. package/dist/cli/commands/inspect.js +144 -0
  6. package/dist/cli/commands/install.d.ts +19 -12
  7. package/dist/cli/commands/install.js +148 -102
  8. package/dist/cli/commands/job.d.ts +3 -0
  9. package/dist/cli/commands/job.js +120 -0
  10. package/dist/cli/commands/offline.d.ts +15 -0
  11. package/dist/cli/commands/offline.js +132 -0
  12. package/dist/cli/commands/serve.js +4 -0
  13. package/dist/cli/commands/spec.js +1 -1
  14. package/dist/cli/commands/storage.d.ts +3 -0
  15. package/dist/cli/commands/storage.js +102 -0
  16. package/dist/cli/commands/uninstall.d.ts +10 -0
  17. package/dist/cli/commands/uninstall.js +93 -7
  18. package/dist/cli/commands/validate.js +1 -1
  19. package/dist/cli/index.js +2 -0
  20. package/dist/cli/router.js +9 -3
  21. package/dist/config/crash-shield-framework-rules.json +93 -0
  22. package/dist/config/environment-schema.json +147 -0
  23. package/dist/config/hardcode-exceptions.json +4 -0
  24. package/dist/config/native-release-public-key.pem +3 -0
  25. package/dist/config/native-targets.json +87 -0
  26. package/dist/config/official-sdd-tools.d.ts +4 -0
  27. package/dist/config/official-sdd-tools.js +35 -0
  28. package/dist/config/product-public.d.ts +225 -0
  29. package/dist/config/product-public.js +147 -0
  30. package/dist/config/product-public.json +283 -0
  31. package/dist/config/registries/hosts/codex.json +20 -363
  32. package/dist/config/release-compatibility.json +13 -0
  33. package/dist/config/release-policy.json +108 -0
  34. package/dist/config/runtime-policy.json +55 -0
  35. package/dist/config/skill-templates/planu-resume-work.md +2 -2
  36. package/dist/config/skill-templates/planu-validate.md +7 -4
  37. package/dist/config/subagent-templates/planu-spec-implementer.md +7 -3
  38. package/dist/config/subagent-templates/planu-validator.md +10 -5
  39. package/dist/config/technology-registry.json +120 -0
  40. package/dist/config/version.js +7 -8
  41. package/dist/core/index.d.ts +1 -1
  42. package/dist/core/spec-validator.d.ts +4 -4
  43. package/dist/core/spec-validator.js +1 -1
  44. package/dist/engine/abortable-process-runner.d.ts +20 -2
  45. package/dist/engine/abortable-process-runner.js +267 -67
  46. package/dist/engine/advanced-testing/accessibility-checker.js +12 -14
  47. package/dist/engine/advanced-testing/mutation-advisor.js +3 -4
  48. package/dist/engine/advanced-testing/property-based.js +2 -3
  49. package/dist/engine/advanced-testing/test-data-advisor.js +2 -3
  50. package/dist/engine/agent-generator.js +2 -1
  51. package/dist/engine/agent-handoff-store.js +2 -1
  52. package/dist/engine/agent-prompt-generator.js +7 -5
  53. package/dist/engine/agent-telemetry.js +2 -1
  54. package/dist/engine/ai-integration/gemini/extensions-advisor.js +41 -9
  55. package/dist/engine/ambiguity-detector.js +12 -11
  56. package/dist/engine/ambiguity-scorer.js +4 -26
  57. package/dist/engine/analyzer/db-engine.js +2 -3
  58. package/dist/engine/analyzer/detectors.js +13 -14
  59. package/dist/engine/analyzer/env-setup.js +9 -10
  60. package/dist/engine/analyzer-env-scanner/env-example.js +7 -8
  61. package/dist/engine/api-spec-generator/graphql-parser.js +2 -2
  62. package/dist/engine/api-validation/graphql-schema-validator.js +2 -1
  63. package/dist/engine/api-validation/openapi-impl-validator.js +6 -7
  64. package/dist/engine/architecture-detector.js +10 -8
  65. package/dist/engine/auth/config.d.ts +1 -1
  66. package/dist/engine/auth/config.js +4 -3
  67. package/dist/engine/auth/scope-mapper.js +1 -7
  68. package/dist/engine/auto-updater/atomic-upgrade.d.ts +8 -0
  69. package/dist/engine/auto-updater/atomic-upgrade.js +381 -0
  70. package/dist/engine/auto-updater/config-detector.js +9 -3
  71. package/dist/engine/auto-updater/config-patcher.d.ts +0 -4
  72. package/dist/engine/auto-updater/config-patcher.js +10 -15
  73. package/dist/engine/auto-updater/index.d.ts +3 -1
  74. package/dist/engine/auto-updater/index.js +44 -30
  75. package/dist/engine/auto-updater/npm-registry.js +10 -2
  76. package/dist/engine/auto-updater/release-verifier.d.ts +3 -0
  77. package/dist/engine/auto-updater/release-verifier.js +112 -0
  78. package/dist/engine/auto-updater/upgrade-lock.d.ts +6 -0
  79. package/dist/engine/auto-updater/upgrade-lock.js +28 -0
  80. package/dist/engine/auto-updater/upgrade-trust-state.d.ts +7 -0
  81. package/dist/engine/auto-updater/upgrade-trust-state.js +59 -0
  82. package/dist/engine/autopilot/audit-logger.js +2 -5
  83. package/dist/engine/autopilot/state-updater.js +4 -4
  84. package/dist/engine/best-practices-library.js +2 -1
  85. package/dist/engine/browser-validator.js +2 -1
  86. package/dist/engine/build-tool-detector.js +3 -2
  87. package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
  88. package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
  89. package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
  90. package/dist/engine/ci-generator/context-builders.js +7 -5
  91. package/dist/engine/ci-generator/planu-config.js +1 -7
  92. package/dist/engine/ci-generator/stack-detector.js +0 -2
  93. package/dist/engine/ci-generator/yaml-builder.js +0 -2
  94. package/dist/engine/clarification-gate/gate.d.ts +3 -3
  95. package/dist/engine/claude-config-auditor/rules-auditor.js +4 -3
  96. package/dist/engine/code-scanner/layer-scanner.js +6 -22
  97. package/dist/engine/code-transforms/typescript/ast-utils.js +2 -3
  98. package/dist/engine/complexity-budget/trivial-detector.js +2 -1
  99. package/dist/engine/compliance-test-generator/index.js +7 -2
  100. package/dist/engine/compliance-test-generator/test-formatter.js +2 -2
  101. package/dist/engine/config-health/mcp-config-checker.js +2 -1
  102. package/dist/engine/config-health/ts-checker-scripts.js +4 -3
  103. package/dist/engine/config-health/ts-checker.js +3 -2
  104. package/dist/engine/convention-injector.js +5 -7
  105. package/dist/engine/convention-scanner/convention-parser.js +3 -2
  106. package/dist/engine/core-bridge-duplicate-blocks.d.ts +3 -0
  107. package/dist/engine/core-bridge-duplicate-blocks.js +57 -0
  108. package/dist/engine/core-bridge-gaps.d.ts +4 -0
  109. package/dist/engine/core-bridge-gaps.js +75 -0
  110. package/dist/engine/core-bridge.d.ts +10 -10
  111. package/dist/engine/core-bridge.js +196 -283
  112. package/dist/engine/cost-guardrails.d.ts +1 -1
  113. package/dist/engine/cost-guardrails.js +1 -1
  114. package/dist/engine/cost-tracking/ledger.js +2 -5
  115. package/dist/engine/coverage-gap-analyzer.js +10 -1
  116. package/dist/engine/crash-shield/detectors/framework-detector.d.ts +2 -0
  117. package/dist/engine/crash-shield/detectors/framework-detector.js +110 -192
  118. package/dist/engine/crash-shield/detectors/typescript-detector.js +2 -1
  119. package/dist/engine/criterion-identity.d.ts +1 -0
  120. package/dist/engine/criterion-identity.js +7 -0
  121. package/dist/engine/dashboard/data-loader.js +4 -8
  122. package/dist/engine/dashboard/templates-layout.js +0 -3
  123. package/dist/engine/dashboard/templates-project.js +1 -13
  124. package/dist/engine/data-projects-gc/gc-runner.d.ts +7 -0
  125. package/dist/engine/data-projects-gc/gc-runner.js +113 -3
  126. package/dist/engine/data-projects-gc/scanner.js +5 -1
  127. package/dist/engine/decision-store.js +11 -14
  128. package/dist/engine/deleter/core.js +5 -5
  129. package/dist/engine/dep-auditor/abandonment-data.js +2 -1
  130. package/dist/engine/dep-auditor/abandonment-registry.js +3 -1
  131. package/dist/engine/dep-auditor/cve-fetcher.js +3 -0
  132. package/dist/engine/dep-auditor/dep-duplicates-detector.js +36 -4
  133. package/dist/engine/dep-auditor/license-registry.js +2 -0
  134. package/dist/engine/dependency-evaluator.d.ts +14 -0
  135. package/dist/engine/dependency-evaluator.js +81 -0
  136. package/dist/engine/deploy/providers/fly.js +7 -7
  137. package/dist/engine/deploy/providers/netlify.js +7 -7
  138. package/dist/engine/deploy/providers/railway.js +10 -12
  139. package/dist/engine/deploy/providers/vercel.js +7 -9
  140. package/dist/engine/deprecation-scanner/pattern-loader.js +0 -2
  141. package/dist/engine/desktop-detector.js +2 -3
  142. package/dist/engine/detect-duplication.js +2 -1
  143. package/dist/engine/detectors/advanced-framework-detector/css-state-detector.js +2 -1
  144. package/dist/engine/detectors/advanced-framework-detector/legacy-detector.js +2 -3
  145. package/dist/engine/detectors/advanced-framework-detector/modern-detector.js +3 -4
  146. package/dist/engine/detectors/agent-framework-detector.js +3 -2
  147. package/dist/engine/detectors/ai-native-detector.js +4 -6
  148. package/dist/engine/detectors/cache-db-detector.js +4 -3
  149. package/dist/engine/detectors/cicd-detector.js +4 -3
  150. package/dist/engine/detectors/deployment-detector.js +11 -12
  151. package/dist/engine/detectors/frontend-perf-detector.js +14 -9
  152. package/dist/engine/detectors/library-detector-multilang-a.js +16 -3
  153. package/dist/engine/detectors/library-detector-node.js +14 -4
  154. package/dist/engine/detectors/library-detector-python.js +7 -1
  155. package/dist/engine/detectors/library-detector.js +0 -2
  156. package/dist/engine/detectors/llm-detector/provider-maps.js +3 -2
  157. package/dist/engine/detectors/mcp-candidate-detector.js +12 -16
  158. package/dist/engine/detectors/mfe-detector.js +6 -7
  159. package/dist/engine/detectors/monitoring-detector.js +4 -3
  160. package/dist/engine/detectors/otel-detector.js +3 -2
  161. package/dist/engine/detectors/otel-ecosystem-detectors.d.ts +2 -2
  162. package/dist/engine/detectors/otel-ecosystem-detectors.js +4 -3
  163. package/dist/engine/detectors/platform-specialty-detector.js +3 -5
  164. package/dist/engine/detectors/template-engine-detector.js +3 -4
  165. package/dist/engine/doc-generator/builders.js +2 -3
  166. package/dist/engine/doc-generator/mobile-distribution-guide.js +13 -17
  167. package/dist/engine/doc-generator/portal/dirty-flag-map.d.ts +2 -2
  168. package/dist/engine/doc-generator/portal/portal-regenerator.js +3 -47
  169. package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
  170. package/dist/engine/docs-site-generator/data-collector.js +7 -28
  171. package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
  172. package/dist/engine/domain-detector.js +3 -6
  173. package/dist/engine/dynamic-knowledge/knowledge-cache.js +3 -4
  174. package/dist/engine/dynamic-knowledge/knowledge-merger.js +17 -2
  175. package/dist/engine/dynamic-knowledge/web-researcher.js +15 -14
  176. package/dist/engine/dynamic-migration-detector/manifest-readers-extended.js +0 -2
  177. package/dist/engine/e2e-test-generator.js +4 -3
  178. package/dist/engine/ecosystem-absorber/knowledge-cache.js +2 -1
  179. package/dist/engine/ecosystem-absorber/platform-crawler.js +2 -2
  180. package/dist/engine/escalator/retry-counter.js +2 -1
  181. package/dist/engine/evidence-gates/artifact-reader.d.ts +16 -1
  182. package/dist/engine/evidence-gates/artifact-reader.js +220 -23
  183. package/dist/engine/evidence-gates/lifecycle-gate.d.ts +2 -1
  184. package/dist/engine/evidence-gates/lifecycle-gate.js +58 -3
  185. package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
  186. package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
  187. package/dist/engine/evidence-index/done-drift.d.ts +1 -1
  188. package/dist/engine/evidence-index/done-drift.js +40 -18
  189. package/dist/engine/execution/context.d.ts +19 -0
  190. package/dist/engine/execution/context.js +130 -0
  191. package/dist/engine/execution/deadline-io.d.ts +21 -0
  192. package/dist/engine/execution/deadline-io.js +45 -0
  193. package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
  194. package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
  195. package/dist/engine/execution/job-executor.d.ts +16 -0
  196. package/dist/engine/execution/job-executor.js +140 -0
  197. package/dist/engine/execution/job-runtime.d.ts +108 -0
  198. package/dist/engine/execution/job-runtime.js +328 -0
  199. package/dist/engine/execution/native-relevance-scorer.d.ts +6 -0
  200. package/dist/engine/execution/native-relevance-scorer.js +64 -0
  201. package/dist/engine/execution/native-worker-dispatch.d.ts +3 -0
  202. package/dist/engine/execution/native-worker-dispatch.js +46 -0
  203. package/dist/engine/execution/native-worker-protocol.d.ts +6 -0
  204. package/dist/engine/execution/native-worker-protocol.js +33 -0
  205. package/dist/engine/execution/native-worker-runtime.d.ts +4 -0
  206. package/dist/engine/execution/native-worker-runtime.js +177 -0
  207. package/dist/engine/execution/native-worker-thread.d.ts +2 -0
  208. package/dist/engine/execution/native-worker-thread.js +66 -0
  209. package/dist/engine/execution/operation-journal.d.ts +66 -0
  210. package/dist/engine/execution/operation-journal.js +260 -0
  211. package/dist/engine/execution/outbox-worker.d.ts +31 -0
  212. package/dist/engine/execution/outbox-worker.js +192 -0
  213. package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
  214. package/dist/engine/execution/public-job-sanitizer.js +31 -0
  215. package/dist/engine/execution/validate-job-executor.d.ts +35 -0
  216. package/dist/engine/execution/validate-job-executor.js +805 -0
  217. package/dist/engine/execution/validate-job-payload.d.ts +5 -0
  218. package/dist/engine/execution/validate-job-payload.js +144 -0
  219. package/dist/engine/execution/validate-job-recovery.d.ts +4 -0
  220. package/dist/engine/execution/validate-job-recovery.js +47 -0
  221. package/dist/engine/execution/validate-worker-runtime.d.ts +10 -0
  222. package/dist/engine/execution/validate-worker-runtime.js +188 -0
  223. package/dist/engine/execution/validate-worker-thread.d.ts +2 -0
  224. package/dist/engine/execution/validate-worker-thread.js +64 -0
  225. package/dist/engine/execution-plan/desktop-distribution.js +3 -7
  226. package/dist/engine/execution-plan/mobile-distribution.js +4 -9
  227. package/dist/engine/execution-plan/phases.js +4 -5
  228. package/dist/engine/execution-plan/plan-utils.js +8 -5
  229. package/dist/engine/facilitate/context-loader.js +3 -9
  230. package/dist/engine/figma/code-connect-fetcher.js +9 -8
  231. package/dist/engine/filesystem-watcher/index.d.ts +3 -3
  232. package/dist/engine/filesystem-watcher/index.js +5 -5
  233. package/dist/engine/framework-detector.js +14 -15
  234. package/dist/engine/git/planu-autocommit.js +8 -7
  235. package/dist/engine/git/repository-probe.d.ts +3 -0
  236. package/dist/engine/git/repository-probe.js +13 -0
  237. package/dist/engine/global-rules-manager.js +2 -2
  238. package/dist/engine/handoff-artifacts/io.js +2 -3
  239. package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.js +8 -1
  240. package/dist/engine/handoff-artifacts/schemas.d.ts +6 -1
  241. package/dist/engine/handoff-artifacts/schemas.js +38 -0
  242. package/dist/engine/handoff-packager.d.ts +19 -0
  243. package/dist/engine/handoff-packager.js +415 -13
  244. package/dist/engine/hook-generator/stack-hook-templates.js +4 -4
  245. package/dist/engine/hooks/config-loader.js +1 -1
  246. package/dist/engine/hooks/context-injector.js +4 -7
  247. package/dist/engine/hooks/effort-configurator.js +1 -1
  248. package/dist/engine/hooks/file-watcher.js +1 -14
  249. package/dist/engine/hooks-reconciler.js +6 -4
  250. package/dist/engine/impact-detector/test-break-predictor.js +3 -3
  251. package/dist/engine/impact-detector/tool-registration.js +1 -1
  252. package/dist/engine/implementation-contract/renderer.js +23 -8
  253. package/dist/engine/infrastructure/component-mapper.js +5 -7
  254. package/dist/engine/infrastructure/docker-compose-generator.js +8 -10
  255. package/dist/engine/infrastructure/index.js +6 -3
  256. package/dist/engine/infrastructure/kubernetes-generator.js +2 -2
  257. package/dist/engine/infrastructure/railway-generator.js +10 -8
  258. package/dist/engine/infrastructure/signal-detector.js +6 -8
  259. package/dist/engine/infrastructure/terraform-generator.js +6 -8
  260. package/dist/engine/legal-compliance/detector.js +3 -2
  261. package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
  262. package/dist/engine/lifecycle-reconciliation-io.js +180 -0
  263. package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
  264. package/dist/engine/lifecycle-reconciliation.js +406 -0
  265. package/dist/engine/local-first/storage-advisor.js +5 -6
  266. package/dist/engine/local-first/tool-classification.js +2 -0
  267. package/dist/engine/macos-keychain-native-port.d.ts +10 -0
  268. package/dist/engine/macos-keychain-native-port.js +38 -0
  269. package/dist/engine/marketplace-fetcher/cache.js +3 -1
  270. package/dist/engine/mcp-config/mcp-config-writer.d.ts +2 -2
  271. package/dist/engine/mcp-config/mcp-config-writer.js +10 -8
  272. package/dist/engine/migration/db-migrator.js +2 -5
  273. package/dist/engine/migration/equivalence-mapper.js +12 -13
  274. package/dist/engine/migration/service-catalogs.js +6 -7
  275. package/dist/engine/migration/service-migrator-catalogs.js +6 -7
  276. package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
  277. package/dist/engine/migrations/remove-commercial-state.js +343 -0
  278. package/dist/engine/mobile-detector-ios.js +2 -3
  279. package/dist/engine/mobile-detector.js +16 -4
  280. package/dist/engine/model-router/complexity-analyzer.js +4 -7
  281. package/dist/engine/multi-agent-review/security-analyzer.js +3 -1
  282. package/dist/engine/multi-app-detector/app-type.js +26 -21
  283. package/dist/engine/multi-app-detector/core.js +3 -2
  284. package/dist/engine/multi-teammate-review/disagreement-counter.js +2 -1
  285. package/dist/engine/multi-teammate-review/panel-orchestrator.js +0 -2
  286. package/dist/engine/mutation-config-generator.js +10 -7
  287. package/dist/engine/native-capability-policy.d.ts +4 -0
  288. package/dist/engine/native-capability-policy.js +24 -0
  289. package/dist/engine/native-platform.d.ts +3 -0
  290. package/dist/engine/native-platform.js +44 -0
  291. package/dist/engine/native-runtime-handshake.d.ts +20 -0
  292. package/dist/engine/native-runtime-handshake.js +230 -0
  293. package/dist/engine/network-policy.d.ts +27 -0
  294. package/dist/engine/network-policy.js +175 -0
  295. package/dist/engine/next-spec-resolver/dependency-filter.js +2 -22
  296. package/dist/engine/nfr-profile-detector.js +12 -16
  297. package/dist/engine/nli-keywords.js +8 -9
  298. package/dist/engine/oauth/config-guard.js +4 -5
  299. package/dist/engine/observability/local-execution-spans.d.ts +15 -0
  300. package/dist/engine/observability/local-execution-spans.js +63 -0
  301. package/dist/engine/orchestrator/runtime.js +0 -1
  302. package/dist/engine/package-registry.js +2 -6
  303. package/dist/engine/performance-analyzer.js +2 -5
  304. package/dist/engine/permissions-merger/stack-detectors.js +5 -5
  305. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +39 -0
  306. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +1310 -0
  307. package/dist/engine/planu-core.darwin-x64.node.manifest.json +39 -0
  308. package/dist/engine/planu-core.darwin-x64.node.sbom.json +1310 -0
  309. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +38 -0
  310. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +1310 -0
  311. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +38 -0
  312. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +1310 -0
  313. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +38 -0
  314. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +1310 -0
  315. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +38 -0
  316. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +1310 -0
  317. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +38 -0
  318. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +1310 -0
  319. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +38 -0
  320. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +1310 -0
  321. package/dist/engine/platform-detector/desktop.js +15 -2
  322. package/dist/engine/platform-detector/extensions.js +2 -1
  323. package/dist/engine/platform-detector/games.js +2 -1
  324. package/dist/engine/platform-detector.js +3 -2
  325. package/dist/engine/platform-engineering/idp-detector.js +2 -1
  326. package/dist/engine/plugin-installer/stack-matcher.js +9 -17
  327. package/dist/engine/plugins/lifecycle.js +6 -2
  328. package/dist/engine/prior-decisions/contradiction-detector.js +60 -30
  329. package/dist/engine/project-dna/stack-detector.js +15 -17
  330. package/dist/engine/project-graph/builder.js +11 -0
  331. package/dist/engine/project-graph/cache.js +1 -1
  332. package/dist/engine/project-graph/freshness-projection.d.ts +16 -0
  333. package/dist/engine/project-graph/freshness-projection.js +95 -0
  334. package/dist/engine/project-graph/index.d.ts +1 -0
  335. package/dist/engine/project-graph/index.js +1 -0
  336. package/dist/engine/project-graph/query.js +15 -25
  337. package/dist/engine/project-health-checker.js +3 -4
  338. package/dist/engine/project-scanner/stack-detector.js +17 -17
  339. package/dist/engine/property-test-generator.js +2 -3
  340. package/dist/engine/qa-gate.d.ts +1 -0
  341. package/dist/engine/qa-gate.js +6 -4
  342. package/dist/engine/quality-gates/gate-catalog.js +47 -46
  343. package/dist/engine/quality-gates/gate-injector.js +9 -4
  344. package/dist/engine/readiness-checker.d.ts +1 -16
  345. package/dist/engine/readiness-checker.js +27 -88
  346. package/dist/engine/registry/core.js +0 -2
  347. package/dist/engine/registry/loader.js +9 -12
  348. package/dist/engine/registry/matcher.js +129 -24
  349. package/dist/engine/resilience-detector/concurrency-detector.js +4 -4
  350. package/dist/engine/resilience-detector/middleware-detector.js +11 -13
  351. package/dist/engine/resilience-detector/validation-detector.js +17 -4
  352. package/dist/engine/reverse-engineer/config-analyzer.js +9 -8
  353. package/dist/engine/reverse-engineer/orchestrator.js +5 -9
  354. package/dist/engine/reverse-engineer/test-file-parser.js +6 -3
  355. package/dist/engine/rules-generator/index.js +6 -5
  356. package/dist/engine/runtime-policy.d.ts +9 -0
  357. package/dist/engine/runtime-policy.js +138 -0
  358. package/dist/engine/runtime-security/checkers/content-security.js +21 -6
  359. package/dist/engine/runtime-security/checkers/file-permissions.js +0 -2
  360. package/dist/engine/runtime-security/rate-limiter.js +0 -1
  361. package/dist/engine/safety/atomic-write-file.js +20 -9
  362. package/dist/engine/safety/atomic-writer.js +10 -3
  363. package/dist/engine/safety/contained-project-file.d.ts +3 -0
  364. package/dist/engine/safety/contained-project-file.js +23 -0
  365. package/dist/engine/safety/cross-process-lock.d.ts +3 -40
  366. package/dist/engine/safety/cross-process-lock.js +268 -272
  367. package/dist/engine/safety/file-mutex.d.ts +2 -9
  368. package/dist/engine/safety/file-mutex.js +155 -87
  369. package/dist/engine/safety/health-monitor.js +15 -6
  370. package/dist/engine/safety/lock-lease-store.d.ts +79 -0
  371. package/dist/engine/safety/lock-lease-store.js +372 -0
  372. package/dist/engine/safety/lock-scavenger.d.ts +2 -5
  373. package/dist/engine/safety/lock-scavenger.js +158 -76
  374. package/dist/engine/safety/lock-staleness.js +4 -1
  375. package/dist/engine/safety/obsolete-lock-guard.d.ts +8 -0
  376. package/dist/engine/safety/obsolete-lock-guard.js +31 -0
  377. package/dist/engine/safety/transaction.js +3 -3
  378. package/dist/engine/sandbox/index.js +2 -1
  379. package/dist/engine/sandbox/runner-detect.js +3 -2
  380. package/dist/engine/sandbox/runner-docker.js +4 -3
  381. package/dist/engine/scan-project/module-discoverer.js +3 -2
  382. package/dist/engine/schema-generator.js +16 -9
  383. package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
  384. package/dist/engine/scope-boundaries/scope-validator.js +45 -7
  385. package/dist/engine/security-analyzer/criteria-generator.js +2 -3
  386. package/dist/engine/self-healing/healer.js +4 -4
  387. package/dist/engine/session/checkpoint-writer.js +0 -9
  388. package/dist/engine/shared-agent-memory.js +66 -74
  389. package/dist/engine/skill-adapter/command-rewriter.js +3 -2
  390. package/dist/engine/skill-adapter/index.js +3 -3
  391. package/dist/engine/skill-adapter/placeholder-map.js +3 -2
  392. package/dist/engine/skill-bootstrap/stack-matcher.js +47 -48
  393. package/dist/engine/skill-generation/stack-content-generator.js +0 -2
  394. package/dist/engine/skill-generator/workflow-skill-generator.js +2 -1
  395. package/dist/engine/skill-registry/ttl-refresh.js +3 -1
  396. package/dist/engine/skills/skills-fetcher.js +3 -6
  397. package/dist/engine/skills-reconciler.js +19 -39
  398. package/dist/engine/spec-coverage/test-finder.js +7 -6
  399. package/dist/engine/spec-diff-engine.js +3 -3
  400. package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
  401. package/dist/engine/spec-format/acceptance-criteria.js +77 -18
  402. package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
  403. package/dist/engine/spec-format/markdown-sections.js +105 -0
  404. package/dist/engine/spec-format/read-technical-section.js +33 -5
  405. package/dist/engine/spec-format/technical-md-populator.js +3 -2
  406. package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
  407. package/dist/engine/spec-format/unified-spec-builder.js +202 -0
  408. package/dist/engine/spec-generator/api-key-resolver.d.ts +1 -1
  409. package/dist/engine/spec-generator/api-key-resolver.js +3 -24
  410. package/dist/engine/spec-generators/issue-extractor.js +9 -3
  411. package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
  412. package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
  413. package/dist/engine/spec-migrator/drift-detector.js +5 -13
  414. package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
  415. package/dist/engine/spec-migrator/filesystem-import.js +134 -23
  416. package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
  417. package/dist/engine/spec-migrator/index.d.ts +1 -0
  418. package/dist/engine/spec-migrator/index.js +1 -0
  419. package/dist/engine/spec-migrator/planu-canonical-policy.js +10 -1
  420. package/dist/engine/spec-migrator/strict-planu-cleanup.js +24 -25
  421. package/dist/engine/spec-registry/adapter.d.ts +0 -5
  422. package/dist/engine/spec-registry/adapter.js +45 -46
  423. package/dist/engine/spec-registry/client.js +2 -0
  424. package/dist/engine/spec-source-sync.js +3 -2
  425. package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
  426. package/dist/engine/spec-state-machine/transition-spec.js +10 -1
  427. package/dist/engine/spec-templates/custom-loader.js +2 -3
  428. package/dist/engine/spec-templates/templates-domain-specific.js +8 -5
  429. package/dist/engine/spec-templates/templates-perf-integration.js +2 -3
  430. package/dist/engine/stack-auditor/manifest-reader.js +0 -2
  431. package/dist/engine/staleness/stale-implementing.js +7 -7
  432. package/dist/engine/tdd-scaffold-generator.js +24 -11
  433. package/dist/engine/teammate-router/green-phase-verifier.js +2 -1
  434. package/dist/engine/teammate-router/red-phase-verifier.js +2 -1
  435. package/dist/engine/technical-enricher/index.js +16 -8
  436. package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
  437. package/dist/engine/technical-enricher/render-enriched.js +42 -14
  438. package/dist/engine/technology-registry.d.ts +11 -0
  439. package/dist/engine/technology-registry.js +142 -0
  440. package/dist/engine/telemetry/error-reporter.d.ts +1 -2
  441. package/dist/engine/telemetry/error-reporter.js +11 -18
  442. package/dist/engine/telemetry/health-checker.js +58 -31
  443. package/dist/engine/telemetry/index.d.ts +0 -1
  444. package/dist/engine/telemetry/index.js +0 -1
  445. package/dist/engine/telemetry/stance.js +3 -2
  446. package/dist/engine/telemetry/telemetry-client.d.ts +1 -5
  447. package/dist/engine/telemetry/telemetry-client.js +69 -35
  448. package/dist/engine/telemetry/telemetry-store.d.ts +3 -3
  449. package/dist/engine/telemetry/telemetry-store.js +30 -23
  450. package/dist/engine/test-framework-detector/detectors.js +8 -8
  451. package/dist/engine/test-framework-detector/strategy.js +8 -9
  452. package/dist/engine/test-generators/agent-test-templates.js +2 -3
  453. package/dist/engine/test-generators/contract-test-generator.d.ts +1 -1
  454. package/dist/engine/test-generators/contract-test-generator.js +9 -202
  455. package/dist/engine/test-generators/platform-test-templates-b.js +2 -3
  456. package/dist/engine/test-generators/platform-test-templates.js +2 -5
  457. package/dist/engine/test-mocks-generator.js +4 -3
  458. package/dist/engine/test-plan-generator.js +30 -12
  459. package/dist/engine/test-scaffold-generator/advanced-scaffold.js +4 -5
  460. package/dist/engine/test-scaffold-generator/unit-scaffold.js +5 -6
  461. package/dist/engine/text-signal-boundaries.d.ts +2 -0
  462. package/dist/engine/text-signal-boundaries.js +25 -2
  463. package/dist/engine/time/relative-date.d.ts +3 -0
  464. package/dist/engine/time/relative-date.js +16 -0
  465. package/dist/engine/timing/budget.d.ts +17 -17
  466. package/dist/engine/timing/budget.js +98 -64
  467. package/dist/engine/token-optimizer/analytics.d.ts +1 -1
  468. package/dist/engine/token-optimizer/analytics.js +5 -6
  469. package/dist/engine/tool-groups/group-manager.js +3 -3
  470. package/dist/engine/universal-rules/installer.js +1 -1
  471. package/dist/engine/universal-rules/rules/planu-modes.js +2 -0
  472. package/dist/engine/universal-rules/rules/planu-release-policy.js +1 -0
  473. package/dist/engine/update-notifier.js +8 -5
  474. package/dist/engine/usage-tracker/core.d.ts +2 -2
  475. package/dist/engine/usage-tracker/core.js +1 -2
  476. package/dist/engine/usage-tracker/index.d.ts +0 -3
  477. package/dist/engine/usage-tracker/index.js +0 -3
  478. package/dist/engine/usage-tracker/stats.d.ts +2 -2
  479. package/dist/engine/usage-tracker/stats.js +1 -28
  480. package/dist/engine/validation/approved-spec-portability.d.ts +12 -0
  481. package/dist/engine/validation/approved-spec-portability.js +107 -0
  482. package/dist/engine/validation/durable-validation.d.ts +36 -0
  483. package/dist/engine/validation/durable-validation.js +256 -0
  484. package/dist/engine/validation/validation-freshness.d.ts +46 -0
  485. package/dist/engine/validation/validation-freshness.js +567 -0
  486. package/dist/engine/validation/validation-receipt-codec.d.ts +13 -0
  487. package/dist/engine/validation/validation-receipt-codec.js +182 -0
  488. package/dist/engine/validation/validation-receipt.d.ts +62 -0
  489. package/dist/engine/validation/validation-receipt.js +342 -0
  490. package/dist/engine/validation/validation-source-digest.d.ts +2 -0
  491. package/dist/engine/validation/validation-source-digest.js +86 -0
  492. package/dist/engine/validation/validation-source-policy.d.ts +4 -0
  493. package/dist/engine/validation/validation-source-policy.js +16 -0
  494. package/dist/engine/validation/validation-submission.d.ts +37 -0
  495. package/dist/engine/validation/validation-submission.js +142 -0
  496. package/dist/engine/validation/validation-worktree.d.ts +21 -0
  497. package/dist/engine/validation/validation-worktree.js +260 -0
  498. package/dist/engine/validation-evidence-ledger.js +41 -0
  499. package/dist/engine/validation-impact-planner.d.ts +14 -2
  500. package/dist/engine/validation-impact-planner.js +23 -10
  501. package/dist/engine/validator/dor-dod.d.ts +1 -0
  502. package/dist/engine/validator/dor-dod.js +5 -3
  503. package/dist/engine/validator/extractors.d.ts +1 -5
  504. package/dist/engine/validator/extractors.js +24 -69
  505. package/dist/engine/validator/spec-compliance-runner.d.ts +13 -25
  506. package/dist/engine/validator/spec-compliance-runner.js +442 -215
  507. package/dist/engine/validator/validation-report-writer.js +1 -1
  508. package/dist/engine/validator.js +37 -23
  509. package/dist/engine/verify-integrations/index.js +1 -1
  510. package/dist/engine/version-resolver.js +5 -6
  511. package/dist/engine/web-fetcher/stack-advisor.js +156 -92
  512. package/dist/engine/web-fetcher/stack-detector.js +114 -54
  513. package/dist/engine/workspace-manager.js +3 -2
  514. package/dist/errors/classified-degradation.d.ts +3 -0
  515. package/dist/errors/classified-degradation.js +10 -0
  516. package/dist/errors/degradation-sink.d.ts +3 -0
  517. package/dist/errors/degradation-sink.js +5 -0
  518. package/dist/errors/error-taxonomy.d.ts +12 -0
  519. package/dist/errors/error-taxonomy.js +62 -0
  520. package/dist/hosts/claude-code/ux/mcp-prompts.js +10 -4
  521. package/dist/hosts/claude-code/ux/mcp-resources.js +4 -2
  522. package/dist/hosts/codex/config-scaffold.js +4 -3
  523. package/dist/hosts/gemini/config-scaffold.js +2 -1
  524. package/dist/hosts/gemini/multimodal-spec-flow.js +7 -4
  525. package/dist/index.js +23 -20
  526. package/dist/resources/patterns.js +3 -1
  527. package/dist/resources/specs.js +3 -1
  528. package/dist/resources/templates.js +3 -1
  529. package/dist/resources/usage-stats-resource.js +2 -18
  530. package/dist/security/adapters/environment.d.ts +12 -0
  531. package/dist/security/adapters/environment.js +25 -0
  532. package/dist/security/adapters/linux-secret-service.d.ts +8 -0
  533. package/dist/security/adapters/linux-secret-service.js +64 -0
  534. package/dist/security/adapters/macos-keychain.d.ts +18 -0
  535. package/dist/security/adapters/macos-keychain.js +90 -0
  536. package/dist/security/adapters/windows-credential-manager.d.ts +8 -0
  537. package/dist/security/adapters/windows-credential-manager.js +60 -0
  538. package/dist/security/redactor.d.ts +5 -0
  539. package/dist/security/redactor.js +58 -0
  540. package/dist/security/secret-field.d.ts +5 -0
  541. package/dist/security/secret-field.js +17 -0
  542. package/dist/security/secret-migration.d.ts +3 -0
  543. package/dist/security/secret-migration.js +123 -0
  544. package/dist/security/secret-provider.d.ts +23 -0
  545. package/dist/security/secret-provider.js +145 -0
  546. package/dist/security/secure-json.d.ts +3 -0
  547. package/dist/security/secure-json.js +29 -0
  548. package/dist/server/routes/specs.js +10 -0
  549. package/dist/storage/api-surfaces-store.js +2 -2
  550. package/dist/storage/approval-operation-lock.d.ts +1 -1
  551. package/dist/storage/approval-operation-lock.js +5 -3
  552. package/dist/storage/audit-trail-store.d.ts +1 -1
  553. package/dist/storage/audit-trail-store.js +48 -62
  554. package/dist/storage/autopilot-log-store.js +12 -5
  555. package/dist/storage/base-store.d.ts +9 -9
  556. package/dist/storage/base-store.js +58 -21
  557. package/dist/storage/checkpoint-store.js +5 -2
  558. package/dist/storage/comments-store.js +2 -2
  559. package/dist/storage/compliance-audit-store.js +5 -2
  560. package/dist/storage/compliance-gate-config-store.js +7 -4
  561. package/dist/storage/compliance-store.js +2 -2
  562. package/dist/storage/context-profile-store.js +2 -2
  563. package/dist/storage/crash-shield-store.js +8 -4
  564. package/dist/storage/feedback-remote.d.ts +2 -3
  565. package/dist/storage/feedback-remote.js +45 -31
  566. package/dist/storage/feedback-store.js +10 -6
  567. package/dist/storage/figma-store.js +8 -4
  568. package/dist/storage/file-mutex.js +9 -1
  569. package/dist/storage/gaps-log.js +7 -4
  570. package/dist/storage/global-projects-store.js +4 -2
  571. package/dist/storage/global-store.d.ts +1 -17
  572. package/dist/storage/global-store.js +1 -42
  573. package/dist/storage/ideas-store.js +2 -2
  574. package/dist/storage/index.d.ts +5 -2
  575. package/dist/storage/index.js +5 -2
  576. package/dist/storage/knowledge-store/knowledge.js +27 -33
  577. package/dist/storage/lessons-store.js +2 -2
  578. package/dist/storage/migrations/canonical-storage.d.ts +15 -0
  579. package/dist/storage/migrations/canonical-storage.js +712 -0
  580. package/dist/storage/oauth-store.js +36 -8
  581. package/dist/storage/path-resolver.js +16 -3
  582. package/dist/storage/pr-store.js +2 -2
  583. package/dist/storage/project-drift-store.js +20 -8
  584. package/dist/storage/project-identity.d.ts +32 -0
  585. package/dist/storage/project-identity.js +194 -0
  586. package/dist/storage/project-resolver.js +14 -7
  587. package/dist/storage/release-notes-store.js +2 -6
  588. package/dist/storage/runtime-db.d.ts +195 -0
  589. package/dist/storage/runtime-db.js +1110 -0
  590. package/dist/storage/schema-registry.d.ts +5 -0
  591. package/dist/storage/schema-registry.js +74 -0
  592. package/dist/storage/sentry-store.js +20 -9
  593. package/dist/storage/session-state-store.js +12 -5
  594. package/dist/storage/skill-registry-storage.d.ts +1 -1
  595. package/dist/storage/skill-registry-storage.js +13 -11
  596. package/dist/storage/slack-store.js +10 -5
  597. package/dist/storage/spec-store.d.ts +14 -2
  598. package/dist/storage/spec-store.js +134 -37
  599. package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
  600. package/dist/storage/status-store/atomic-status-write.js +57 -0
  601. package/dist/storage/status-store/file-lock.js +61 -20
  602. package/dist/storage/status-store/self-healing.js +13 -8
  603. package/dist/storage/status-store/version-sync.d.ts +2 -1
  604. package/dist/storage/status-store/version-sync.js +36 -39
  605. package/dist/storage/steering-store.js +2 -2
  606. package/dist/storage/storage-bundle.d.ts +5 -0
  607. package/dist/storage/storage-bundle.js +117 -0
  608. package/dist/storage/storage-catalog-validation.d.ts +3 -0
  609. package/dist/storage/storage-catalog-validation.js +20 -0
  610. package/dist/storage/storage-catalog.d.ts +25 -0
  611. package/dist/storage/storage-catalog.js +390 -0
  612. package/dist/storage/storage-layout.d.ts +24 -0
  613. package/dist/storage/storage-layout.js +101 -0
  614. package/dist/storage/storybook-store.js +2 -2
  615. package/dist/storage/supabase-store.js +24 -6
  616. package/dist/storage/tdd-policy-store.js +8 -6
  617. package/dist/storage/tech-debt-store.js +3 -3
  618. package/dist/storage/technology-selection-store.d.ts +1 -1
  619. package/dist/storage/technology-selection-store.js +6 -3
  620. package/dist/storage/token-cache-store.js +3 -3
  621. package/dist/storage/token-ledger-store.js +13 -8
  622. package/dist/storage/transition-log.js +7 -4
  623. package/dist/storage/trash-store.js +15 -6
  624. package/dist/storage/usage-store.d.ts +1 -5
  625. package/dist/storage/usage-store.js +0 -35
  626. package/dist/storage/vector-store/migrator.js +7 -2
  627. package/dist/storage/vector-store/sqlite-adapter.js +4 -1
  628. package/dist/storage/webhook-server-store.js +2 -2
  629. package/dist/tools/api-spec-generator-handler.js +3 -2
  630. package/dist/tools/auto-update-handler.d.ts +4 -4
  631. package/dist/tools/auto-update-handler.js +4 -4
  632. package/dist/tools/browser-validate-handler.js +2 -1
  633. package/dist/tools/challenge-spec/platform-challenge-scenarios-b.js +8 -5
  634. package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
  635. package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
  636. package/dist/tools/challenge-spec/scenario-collector.js +1 -1
  637. package/dist/tools/challenge-spec/scenarios-data.js +1 -1
  638. package/dist/tools/challenge-spec/scenarios-desktop.js +8 -3
  639. package/dist/tools/challenge-spec/scenarios-failure.js +7 -2
  640. package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
  641. package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
  642. package/dist/tools/challenge-spec/security-challenge-scenarios.js +3 -3
  643. package/dist/tools/challenge-spec.js +5 -5
  644. package/dist/tools/check-readiness.js +1 -1
  645. package/dist/tools/clarify-requirements/multiple-choice.js +53 -41
  646. package/dist/tools/comments-handler.js +2 -3
  647. package/dist/tools/compliance-test-handler.js +2 -2
  648. package/dist/tools/configure-telemetry.js +41 -13
  649. package/dist/tools/create-spec/auto-pipeline.js +13 -1
  650. package/dist/tools/create-spec/autopilot-analyzer.d.ts +1 -1
  651. package/dist/tools/create-spec/autopilot-analyzer.js +2 -4
  652. package/dist/tools/create-spec/mcp-tool-contract.d.ts +10 -4
  653. package/dist/tools/create-spec/mcp-tool-contract.js +7 -36
  654. package/dist/tools/create-spec/post-creation.d.ts +2 -1
  655. package/dist/tools/create-spec/post-creation.js +56 -54
  656. package/dist/tools/create-spec/relevance-scorer.d.ts +1 -1
  657. package/dist/tools/create-spec/relevance-scorer.js +17 -1
  658. package/dist/tools/create-spec/spec-builder.js +2 -23
  659. package/dist/tools/create-spec/spec-created-outbox-consumer.d.ts +14 -0
  660. package/dist/tools/create-spec/spec-created-outbox-consumer.js +176 -0
  661. package/dist/tools/create-spec.d.ts +1 -0
  662. package/dist/tools/create-spec.js +802 -732
  663. package/dist/tools/define-ui-contract/agent-interaction-contract.js +2 -4
  664. package/dist/tools/define-ui-contract-desktop/desktop-contract.js +12 -4
  665. package/dist/tools/define-ui-contract-desktop/native-contract.js +7 -3
  666. package/dist/tools/define-ui-contract-extensions.js +2 -3
  667. package/dist/tools/define-ui-contract-mobile.js +5 -9
  668. package/dist/tools/define-ui-contract-web/component-tree.js +4 -3
  669. package/dist/tools/define-ui-contract.js +7 -4
  670. package/dist/tools/deploy/deploy-spec.js +2 -4
  671. package/dist/tools/deploy/deploy-status.js +2 -4
  672. package/dist/tools/design-schema/nosql-category-schemas.js +2 -3
  673. package/dist/tools/design-schema-sql/migrations.js +3 -2
  674. package/dist/tools/design-schema-sql/tables.js +4 -1
  675. package/dist/tools/design-schema.js +4 -3
  676. package/dist/tools/feedback-handler.js +2 -4
  677. package/dist/tools/gc-data-projects.js +5 -5
  678. package/dist/tools/generate-execution-plan.js +3 -4
  679. package/dist/tools/generate-sub-agent.js +2 -1
  680. package/dist/tools/generate-tests/generators/a11y-tests-generator.js +5 -4
  681. package/dist/tools/generate-tests/generators/advanced-testing-generator.js +5 -4
  682. package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +9 -4
  683. package/dist/tools/generate-tests/generators/concurrency-test-generator.js +8 -4
  684. package/dist/tools/generate-tests/generators/database/detectors.js +9 -3
  685. package/dist/tools/generate-tests/generators/database/index.js +2 -4
  686. package/dist/tools/generate-tests/generators/event-test-generator.js +5 -4
  687. package/dist/tools/generate-tests/generators/game-tests-generator.js +2 -1
  688. package/dist/tools/generate-tests/generators/graphql-test-generator.js +11 -5
  689. package/dist/tools/generate-tests/generators/grpc-test-generator.js +2 -3
  690. package/dist/tools/generate-tests/generators/llm-security-test-generator.js +4 -3
  691. package/dist/tools/generate-tests/generators/local-first-test-generator.js +4 -3
  692. package/dist/tools/generate-tests/generators/mcp-tests-generator.js +11 -9
  693. package/dist/tools/generate-tests/generators/microservices/index.js +2 -3
  694. package/dist/tools/generate-tests/generators/otel-test-generator.js +6 -3
  695. package/dist/tools/generate-tests/generators/security/detectors.js +2 -5
  696. package/dist/tools/generate-tests/generators/security/index.js +2 -4
  697. package/dist/tools/generate-tests/generators/supply-chain-test-generator.js +2 -3
  698. package/dist/tools/generate-tests/generators/visual-regression-generator.js +14 -17
  699. package/dist/tools/generate-tests/generators/websocket-test-generator.js +2 -3
  700. package/dist/tools/generate-tests/test-helpers.js +11 -12
  701. package/dist/tools/generate-tests-content.js +4 -3
  702. package/dist/tools/generate-tests.js +7 -4
  703. package/dist/tools/git/branch-ops.d.ts +1 -1
  704. package/dist/tools/git/branch-ops.js +3 -2
  705. package/dist/tools/git/git-helpers.d.ts +0 -2
  706. package/dist/tools/git/git-helpers.js +7 -7
  707. package/dist/tools/github-pr-handler.js +10 -3
  708. package/dist/tools/heal-spec-docs.js +6 -53
  709. package/dist/tools/hook-generator-handler.js +6 -6
  710. package/dist/tools/init-constitution/platform-principles.js +4 -2
  711. package/dist/tools/init-project/agents-md-writer.js +14 -5
  712. package/dist/tools/init-project/claude-md-generator.js +19 -12
  713. package/dist/tools/init-project/handler.js +165 -75
  714. package/dist/tools/init-project/host-assets-writer.d.ts +1 -1
  715. package/dist/tools/init-project/host-assets-writer.js +30 -3
  716. package/dist/tools/init-project/lifecycle-helpers.js +3 -5
  717. package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
  718. package/dist/tools/init-project/lint-scaffolder.js +6 -2
  719. package/dist/tools/init-project/migration-runner.d.ts +8 -1
  720. package/dist/tools/init-project/migration-runner.js +68 -2
  721. package/dist/tools/init-project/per-client-files-writer.js +14 -4
  722. package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
  723. package/dist/tools/init-project/portable-index-reconciler.js +14 -0
  724. package/dist/tools/init-project/result-builder.js +1 -3
  725. package/dist/tools/init-project/rules-generator.js +10 -2
  726. package/dist/tools/init-project/scaffold-writer.d.ts +1 -1
  727. package/dist/tools/init-project/scaffold-writer.js +17 -15
  728. package/dist/tools/jobs/handlers.d.ts +15 -0
  729. package/dist/tools/jobs/handlers.js +158 -0
  730. package/dist/tools/list-specs.js +39 -59
  731. package/dist/tools/manage-plugins-handler.js +17 -9
  732. package/dist/tools/merge-risk-handler.js +2 -2
  733. package/dist/tools/migrate-tech/advanced-handlers.js +3 -2
  734. package/dist/tools/migrate-tech/build-tool-handlers.js +3 -2
  735. package/dist/tools/migrate-tech/core-handlers.js +5 -2
  736. package/dist/tools/migrate-tech/data-handlers.js +6 -3
  737. package/dist/tools/migrate-tech-advanced/rollback-handlers.js +3 -2
  738. package/dist/tools/migrate-tech-advanced/service-handlers.js +5 -2
  739. package/dist/tools/oauth-handler.js +7 -1
  740. package/dist/tools/package-handoff.js +2 -0
  741. package/dist/tools/reconcile-spec.js +0 -27
  742. package/dist/tools/register-agent-squad-tools.js +2 -2
  743. package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
  744. package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
  745. package/dist/tools/register-sdd-tools.d.ts +1 -1
  746. package/dist/tools/register-sdd-tools.js +2 -24
  747. package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
  748. package/dist/tools/register-spec-tools/core-spec-tools.js +48 -8
  749. package/dist/tools/registry/auth.js +13 -5
  750. package/dist/tools/registry/publish.js +7 -4
  751. package/dist/tools/resolve-project-path.d.ts +2 -2
  752. package/dist/tools/resolve-project-path.js +7 -7
  753. package/dist/tools/reverse-engineer/analyzer.js +14 -14
  754. package/dist/tools/rollback-release.js +3 -2
  755. package/dist/tools/safe-handler.d.ts +5 -13
  756. package/dist/tools/safe-handler.js +302 -194
  757. package/dist/tools/schema-generator-handler.js +2 -1
  758. package/dist/tools/schemas/index.d.ts +1 -1
  759. package/dist/tools/schemas/index.js +1 -1
  760. package/dist/tools/schemas/output-schemas.d.ts +88 -105
  761. package/dist/tools/schemas/output-schemas.js +12 -122
  762. package/dist/tools/schemas/plugins-schemas.js +0 -2
  763. package/dist/tools/schemas/registry.js +0 -2
  764. package/dist/tools/schemas/skill-registry-schemas.js +0 -2
  765. package/dist/tools/schemas/spec.d.ts +4 -0
  766. package/dist/tools/schemas/spec.js +3 -0
  767. package/dist/tools/schemas/validate-output-schema.d.ts +267 -0
  768. package/dist/tools/schemas/validate-output-schema.js +219 -0
  769. package/dist/tools/sentry-handler.js +6 -1
  770. package/dist/tools/skill-bootstrap-handler.js +7 -4
  771. package/dist/tools/spec-prompt-handler.js +3 -3
  772. package/dist/tools/status-handler.js +4 -0
  773. package/dist/tools/storage-bundle-handler.d.ts +9 -0
  774. package/dist/tools/storage-bundle-handler.js +29 -0
  775. package/dist/tools/storage-paths-handler.d.ts +9 -0
  776. package/dist/tools/storage-paths-handler.js +17 -0
  777. package/dist/tools/suggest-mcp-server.js +11 -5
  778. package/dist/tools/suggest-mcps.js +29 -9
  779. package/dist/tools/suggest-tooling/advanced-testing-catalog.js +4 -5
  780. package/dist/tools/suggest-tooling/build-tool-catalog.js +2 -1
  781. package/dist/tools/suggest-tooling/css-framework-catalog.js +13 -7
  782. package/dist/tools/suggest-tooling/framework-catalog.js +3 -5
  783. package/dist/tools/suggest-tooling/frontend-perf-catalog.js +2 -6
  784. package/dist/tools/suggest-tooling/handler.js +10 -4
  785. package/dist/tools/suggest-tooling/hypermedia-catalog.js +11 -3
  786. package/dist/tools/suggest-tooling/linting-catalog.js +4 -7
  787. package/dist/tools/suggest-tooling/llm-security-catalog.js +2 -1
  788. package/dist/tools/suggest-tooling/local-first-catalog.js +7 -5
  789. package/dist/tools/suggest-tooling/observability-catalog.js +3 -4
  790. package/dist/tools/suggest-tooling/otel-catalog.js +4 -1
  791. package/dist/tools/suggest-tooling/resilience-catalog.js +16 -5
  792. package/dist/tools/suggest-tooling/skills-catalog.js +10 -9
  793. package/dist/tools/suggest-tooling/supply-chain-catalog.js +2 -3
  794. package/dist/tools/supabase-handler.js +2 -1
  795. package/dist/tools/sync-spec-state-handler.d.ts +1 -1
  796. package/dist/tools/sync-spec-state-handler.js +1 -1
  797. package/dist/tools/token-intelligence-handler.js +2 -2
  798. package/dist/tools/token-recording.js +2 -2
  799. package/dist/tools/token-savings-handler.js +2 -2
  800. package/dist/tools/tool-entry.d.ts +4 -4
  801. package/dist/tools/tool-entry.js +3 -3
  802. package/dist/tools/tool-registry/core-tools.js +162 -15
  803. package/dist/tools/tool-registry/group-infra.js +37 -122
  804. package/dist/tools/tool-registry/group-quality-compliance.js +31 -38
  805. package/dist/tools/tool-registry/inventory.d.ts +2 -0
  806. package/dist/tools/tool-registry/inventory.js +2 -0
  807. package/dist/tools/tool-registry-helpers.d.ts +3 -3
  808. package/dist/tools/tool-registry-helpers.js +3 -3
  809. package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
  810. package/dist/tools/tool-usage-report/registered-tools.js +3 -15
  811. package/dist/tools/update-status/approval-gates.d.ts +13 -0
  812. package/dist/tools/update-status/approval-gates.js +84 -0
  813. package/dist/tools/update-status/batch.js +4 -2
  814. package/dist/tools/update-status/dod-gates.d.ts +6 -24
  815. package/dist/tools/update-status/dod-gates.js +60 -135
  816. package/dist/tools/update-status/done-receipt-verifier.d.ts +15 -0
  817. package/dist/tools/update-status/done-receipt-verifier.js +158 -0
  818. package/dist/tools/update-status/evidence-gate.js +190 -3
  819. package/dist/tools/update-status/file-sync.d.ts +2 -1
  820. package/dist/tools/update-status/file-sync.js +80 -18
  821. package/dist/tools/update-status/gate-harness.d.ts +7 -0
  822. package/dist/tools/update-status/gate-harness.js +8 -0
  823. package/dist/tools/update-status/index.d.ts +2 -0
  824. package/dist/tools/update-status/index.js +307 -367
  825. package/dist/tools/update-status/qa-gate.d.ts +1 -1
  826. package/dist/tools/update-status/qa-gate.js +2 -2
  827. package/dist/tools/update-status/response-builder.js +3 -3
  828. package/dist/tools/update-status/side-effects.d.ts +2 -0
  829. package/dist/tools/update-status/side-effects.js +32 -39
  830. package/dist/tools/update-status/transition-guard.d.ts +4 -2
  831. package/dist/tools/update-status/transition-guard.js +89 -25
  832. package/dist/tools/update-status-actions.d.ts +4 -2
  833. package/dist/tools/update-status-actions.js +16 -14
  834. package/dist/tools/update-status-convention-gate.js +24 -23
  835. package/dist/tools/update-status-reconcile.js +2 -2
  836. package/dist/tools/usage-report.js +1 -16
  837. package/dist/tools/usage-stats.js +1 -18
  838. package/dist/tools/usage-tracking.d.ts +4 -0
  839. package/dist/tools/usage-tracking.js +20 -0
  840. package/dist/tools/validate-api-contract-handler.js +3 -3
  841. package/dist/tools/validate-lint.d.ts +1 -0
  842. package/dist/tools/validate-lint.js +21 -14
  843. package/dist/tools/validate.d.ts +4 -1
  844. package/dist/tools/validate.js +319 -31
  845. package/dist/transports/http-transport.js +87 -66
  846. package/dist/transports/mcp-types.d.ts +0 -2
  847. package/dist/transports/middleware/with-telemetry.js +3 -2
  848. package/dist/transports/session-context.js +1 -1
  849. package/dist/types/abortable-process.d.ts +2 -0
  850. package/dist/types/advanced-framework.js +0 -2
  851. package/dist/types/atomic-upgrade.d.ts +71 -0
  852. package/dist/types/atomic-upgrade.js +2 -0
  853. package/dist/types/cascade-hooks.d.ts +1 -1
  854. package/dist/types/ci.d.ts +0 -4
  855. package/dist/types/ci.js +0 -2
  856. package/dist/types/clarification-token.d.ts +1 -1
  857. package/dist/types/claude-code-runtime.d.ts +0 -27
  858. package/dist/types/commercial-migration.d.ts +17 -0
  859. package/dist/types/commercial-migration.js +2 -0
  860. package/dist/types/common/primitives.d.ts +0 -2
  861. package/dist/types/core-bridge.d.ts +81 -10
  862. package/dist/types/crash-shield-framework-rules.d.ts +24 -0
  863. package/dist/types/crash-shield-framework-rules.js +2 -0
  864. package/dist/types/dashboard.d.ts +3 -7
  865. package/dist/types/data-gc.d.ts +16 -0
  866. package/dist/types/deploy.js +0 -2
  867. package/dist/types/docs.d.ts +0 -1
  868. package/dist/types/durable-job.d.ts +78 -0
  869. package/dist/types/durable-job.js +2 -0
  870. package/dist/types/durable-validation.d.ts +158 -0
  871. package/dist/types/durable-validation.js +166 -0
  872. package/dist/types/errors/structured-error.d.ts +3 -3
  873. package/dist/types/estimation.d.ts +4 -0
  874. package/dist/types/evidence-gates.d.ts +17 -1
  875. package/dist/types/execution.d.ts +45 -0
  876. package/dist/types/gemini-integration.d.ts +0 -11
  877. package/dist/types/handoff-artifacts.d.ts +43 -0
  878. package/dist/types/hooks-advanced.d.ts +0 -13
  879. package/dist/types/impact-detection.d.ts +2 -2
  880. package/dist/types/index.d.ts +4 -6
  881. package/dist/types/index.js +3 -5
  882. package/dist/types/infrastructure.js +0 -2
  883. package/dist/types/kiro-integration.d.ts +0 -6
  884. package/dist/types/lifecycle.js +0 -2
  885. package/dist/types/migration/canonical-storage.d.ts +37 -0
  886. package/dist/types/migration/canonical-storage.js +2 -0
  887. package/dist/types/migration/index.d.ts +1 -1
  888. package/dist/types/native-worker.d.ts +48 -0
  889. package/dist/types/native-worker.js +2 -0
  890. package/dist/types/network-policy.d.ts +12 -0
  891. package/dist/types/network-policy.js +2 -0
  892. package/dist/types/oauth.js +0 -2
  893. package/dist/types/observability.d.ts +32 -0
  894. package/dist/types/outbox-worker.d.ts +35 -0
  895. package/dist/types/outbox-worker.js +11 -0
  896. package/dist/types/permissions-config.d.ts +0 -27
  897. package/dist/types/permissions-config.js +0 -2
  898. package/dist/types/plugin-install.d.ts +0 -11
  899. package/dist/types/plugin-install.js +0 -2
  900. package/dist/types/portal.d.ts +1 -0
  901. package/dist/types/product-public.d.ts +75 -0
  902. package/dist/types/product-public.js +2 -0
  903. package/dist/types/project/inputs.d.ts +2 -0
  904. package/dist/types/project/lint-scaffolder.d.ts +1 -1
  905. package/dist/types/project/mfe-detection.js +0 -2
  906. package/dist/types/project/platform-mobile.js +0 -2
  907. package/dist/types/project/platform-specialty.js +0 -2
  908. package/dist/types/project-knowledge-graph.d.ts +7 -0
  909. package/dist/types/runtime-database.d.ts +10 -0
  910. package/dist/types/runtime-database.js +2 -0
  911. package/dist/types/runtime-policy.d.ts +51 -0
  912. package/dist/types/runtime-policy.js +2 -0
  913. package/dist/types/safety.d.ts +4 -5
  914. package/dist/types/schema-registry.d.ts +12 -0
  915. package/dist/types/schema-registry.js +2 -0
  916. package/dist/types/security.d.ts +52 -0
  917. package/dist/types/session-safeguard.d.ts +0 -31
  918. package/dist/types/skill-registry.js +0 -2
  919. package/dist/types/spec/canonical-repair.d.ts +81 -0
  920. package/dist/types/spec/canonical-repair.js +2 -0
  921. package/dist/types/spec/core.d.ts +14 -0
  922. package/dist/types/spec/index.d.ts +1 -0
  923. package/dist/types/spec/index.js +1 -0
  924. package/dist/types/spec/inputs.d.ts +31 -0
  925. package/dist/types/spec-format.d.ts +24 -0
  926. package/dist/types/spec-lock-v2.d.ts +13 -7
  927. package/dist/types/spec-lock-v2.js +1 -1
  928. package/dist/types/spec-registry.js +0 -2
  929. package/dist/types/storage-bundle.d.ts +13 -0
  930. package/dist/types/storage-bundle.js +2 -0
  931. package/dist/types/storage-catalog.d.ts +18 -0
  932. package/dist/types/storage-catalog.js +2 -0
  933. package/dist/types/team-planner.js +0 -2
  934. package/dist/types/technology-registry.d.ts +35 -0
  935. package/dist/types/technology-registry.js +2 -0
  936. package/dist/types/technology-symbols.generated.d.ts +117 -0
  937. package/dist/types/technology-symbols.generated.js +2 -0
  938. package/dist/types/telemetry.d.ts +7 -4
  939. package/dist/types/transport.d.ts +2 -2
  940. package/dist/types/usage.d.ts +1 -36
  941. package/dist/types/usage.js +1 -1
  942. package/dist/types/validate-worker.d.ts +50 -0
  943. package/dist/types/validate-worker.js +2 -0
  944. package/dist/types/validation-evidence.d.ts +1 -1
  945. package/dist/types/validation-receipt.d.ts +104 -0
  946. package/dist/types/validation-receipt.js +2 -0
  947. package/dist/types/validation.d.ts +2 -2
  948. package/package.json +36 -21
  949. package/planu-native.json +1 -1
  950. package/planu-plugin.json +23 -40
  951. package/scripts/lib/pending-release-file.mjs +93 -0
  952. package/src/i18n/messages/en.json +0 -41
  953. package/src/i18n/messages/es.json +0 -41
  954. package/src/i18n/messages/pt.json +0 -41
  955. package/dist/cli/commands/activate.d.ts +0 -14
  956. package/dist/cli/commands/activate.js +0 -174
  957. package/dist/config/license-plans.json +0 -244
  958. package/dist/config/server-instructions.ts +0 -121
  959. package/dist/config/version-defaults.ts +0 -76
  960. package/dist/config/version.ts +0 -18
  961. package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
  962. package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
  963. package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
  964. package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
  965. package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
  966. package/dist/engine/hooks/full-spectrum-generator.js +0 -117
  967. package/dist/engine/license-validator/config-loader.d.ts +0 -5
  968. package/dist/engine/license-validator/config-loader.js +0 -16
  969. package/dist/engine/license-validator/core.d.ts +0 -15
  970. package/dist/engine/license-validator/core.js +0 -151
  971. package/dist/engine/license-validator/fingerprint.d.ts +0 -3
  972. package/dist/engine/license-validator/fingerprint.js +0 -17
  973. package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
  974. package/dist/engine/license-validator/lemon-squeezy.js +0 -55
  975. package/dist/engine/license-validator.d.ts +0 -5
  976. package/dist/engine/license-validator.js +0 -6
  977. package/dist/engine/product-intelligence/index.d.ts +0 -7
  978. package/dist/engine/product-intelligence/index.js +0 -211
  979. package/dist/engine/session/auto-save.d.ts +0 -18
  980. package/dist/engine/session/auto-save.js +0 -98
  981. package/dist/engine/session/context-snapshot.d.ts +0 -8
  982. package/dist/engine/session/context-snapshot.js +0 -49
  983. package/dist/engine/session/memory-sync.d.ts +0 -12
  984. package/dist/engine/session/memory-sync.js +0 -34
  985. package/dist/engine/session/session-diff.d.ts +0 -11
  986. package/dist/engine/session/session-diff.js +0 -42
  987. package/dist/engine/session/session-merge.d.ts +0 -12
  988. package/dist/engine/session/session-merge.js +0 -55
  989. package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
  990. package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
  991. package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -16
  992. package/dist/engine/session-safeguard/learnings-buffer.js +0 -114
  993. package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
  994. package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
  995. package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
  996. package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
  997. package/dist/engine/spec-changelog/core.d.ts +0 -16
  998. package/dist/engine/spec-changelog/core.js +0 -176
  999. package/dist/engine/spec-changelog/diff.d.ts +0 -18
  1000. package/dist/engine/spec-changelog/diff.js +0 -121
  1001. package/dist/engine/spec-changelog/index.d.ts +0 -3
  1002. package/dist/engine/spec-changelog/index.js +0 -4
  1003. package/dist/engine/telemetry/geo-resolver.d.ts +0 -9
  1004. package/dist/engine/telemetry/geo-resolver.js +0 -43
  1005. package/dist/engine/trial-engine.d.ts +0 -5
  1006. package/dist/engine/trial-engine.js +0 -14
  1007. package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
  1008. package/dist/engine/usage-tracker/rate-check.js +0 -18
  1009. package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
  1010. package/dist/engine/usage-tracker/rate-limiter.js +0 -73
  1011. package/dist/engine/usage-tracker/trial.d.ts +0 -27
  1012. package/dist/engine/usage-tracker/trial.js +0 -49
  1013. package/dist/engine/webhook/event-handlers.d.ts +0 -25
  1014. package/dist/engine/webhook/event-handlers.js +0 -89
  1015. package/dist/engine/webhook/index.d.ts +0 -4
  1016. package/dist/engine/webhook/index.js +0 -5
  1017. package/dist/engine/webhook/server.d.ts +0 -12
  1018. package/dist/engine/webhook/server.js +0 -249
  1019. package/dist/engine/webhook/signature.d.ts +0 -16
  1020. package/dist/engine/webhook/signature.js +0 -49
  1021. package/dist/storage/data-dir-discovery.d.ts +0 -31
  1022. package/dist/storage/data-dir-discovery.js +0 -140
  1023. package/dist/storage/license-store.d.ts +0 -6
  1024. package/dist/storage/license-store.js +0 -91
  1025. package/dist/storage/migrations/cwd-to-absolute.d.ts +0 -25
  1026. package/dist/storage/migrations/cwd-to-absolute.js +0 -281
  1027. package/dist/storage/session-store.d.ts +0 -20
  1028. package/dist/storage/session-store.js +0 -128
  1029. package/dist/tools/activate-license.d.ts +0 -6
  1030. package/dist/tools/activate-license.js +0 -132
  1031. package/dist/tools/checkpoint-handler.d.ts +0 -3
  1032. package/dist/tools/checkpoint-handler.js +0 -130
  1033. package/dist/tools/create-spec/adapters/prior-decisions-hint.d.ts +0 -14
  1034. package/dist/tools/create-spec/adapters/prior-decisions-hint.js +0 -30
  1035. package/dist/tools/export-session.d.ts +0 -3
  1036. package/dist/tools/export-session.js +0 -90
  1037. package/dist/tools/init-project/ancestor-data-detector.d.ts +0 -22
  1038. package/dist/tools/init-project/ancestor-data-detector.js +0 -81
  1039. package/dist/tools/license-gate.d.ts +0 -18
  1040. package/dist/tools/license-gate.js +0 -222
  1041. package/dist/tools/license-status.d.ts +0 -3
  1042. package/dist/tools/license-status.js +0 -93
  1043. package/dist/tools/list-sessions.d.ts +0 -3
  1044. package/dist/tools/list-sessions.js +0 -46
  1045. package/dist/tools/planu-session-checkpoint.d.ts +0 -6
  1046. package/dist/tools/planu-session-checkpoint.js +0 -42
  1047. package/dist/tools/restore-session.d.ts +0 -3
  1048. package/dist/tools/restore-session.js +0 -71
  1049. package/dist/tools/spec-history.d.ts +0 -3
  1050. package/dist/tools/spec-history.js +0 -59
  1051. package/dist/tools/update-status/force-done-audit.d.ts +0 -2
  1052. package/dist/tools/update-status/force-done-audit.js +0 -23
  1053. package/dist/tools/webhook.d.ts +0 -6
  1054. package/dist/tools/webhook.js +0 -150
  1055. package/dist/types/changelog.d.ts +0 -49
  1056. package/dist/types/changelog.js +0 -3
  1057. package/dist/types/licensing.d.ts +0 -109
  1058. package/dist/types/licensing.js +0 -4
  1059. package/dist/types/migration/cwd-to-absolute.d.ts +0 -8
  1060. package/dist/types/migration/cwd-to-absolute.js +0 -3
  1061. package/dist/types/product-intelligence.d.ts +0 -60
  1062. package/dist/types/product-intelligence.js +0 -3
  1063. package/dist/types/session.d.ts +0 -84
  1064. package/dist/types/session.js +0 -3
  1065. package/dist/types/webhook.d.ts +0 -62
  1066. package/dist/types/webhook.js +0 -4
@@ -3,41 +3,42 @@ import { upsertToken, hashQuestions } from '../engine/clarification-gate/token-s
3
3
  import { ti } from '../i18n/index.js';
4
4
  import { knowledgeStore, specStore } from '../storage/index.js';
5
5
  import { readTechnologySelectionContract } from '../storage/technology-selection-store.js';
6
- import { formatSuccess, addNextSteps, toolResult, interactiveResult } from './response-helpers.js';
7
- import { writeFile, mkdir, rm, readFile, stat as fsStat, rename, link as hardLink, } from 'node:fs/promises';
6
+ import { toolResult, interactiveResult } from './response-helpers.js';
7
+ import { readFile, stat as fsStat } from 'node:fs/promises';
8
8
  import { createHash, randomUUID } from 'node:crypto';
9
9
  import { dirname as pathDirname, isAbsolute as pathIsAbsolute, join as pathJoin, relative as pathRelative, sep as pathSeparator, } from 'node:path';
10
- import { checkSpecReadiness } from '../engine/readiness-checker.js';
11
10
  import { buildSpecContext, buildSplitResult } from './create-spec/spec-builder.js';
12
11
  import { validateConstitution } from './create-spec/constitution-validator.js';
13
- import { setupGitBranch, checkContradictions, fireSpecCreatedHook, generatePostCreationSuggestions, formatPostCreationSuggestion, runAutopilotAsync, getAsyncAnalysisPath, } from './create-spec/post-creation.js';
14
- import { notifyStoreChange } from '../engine/doc-generator/portal/regen-hook.js';
15
- import { compactObj } from '../engine/compact-obj.js';
16
- import { runAutoPostCreatePipeline } from './create-spec/auto-pipeline.js';
12
+ import { getAsyncAnalysisPath } from './create-spec/post-creation.js';
17
13
  import { extractCriteria, generateLeanSpecContent, } from '../engine/spec-format/lean-spec-generator.js';
18
14
  import { generateLeanTechnicalContent, } from '../engine/spec-format/lean-technical-generator.js';
19
15
  import { extractFilesFromSpecBody } from '../engine/spec-format/technical-md-populator.js';
20
- import { buildUnifiedSpecContent } from '../engine/spec-format/unified-spec-builder.js';
21
- import { appendImplementationContractIfMissing } from '../engine/implementation-contract/index.js';
16
+ import { buildCanonicalUnifiedSpecContent, validateUnifiedSpecCandidate, } from '../engine/spec-format/unified-spec-builder.js';
17
+ import { buildImplementationContractSection } from '../engine/implementation-contract/index.js';
22
18
  import { validateEnglishOnlySpecText } from '../engine/spec-language/english-only.js';
23
19
  import { FallbackGenerator } from '../engine/spec-generator/index.js';
24
20
  import { analyzeProjectForSpec, getEmptyAutopilotResult, } from './create-spec/autopilot-analyzer.js';
25
- import { AutopilotSummaryCollector } from '../engine/autopilot/summary-collector.js';
26
21
  import { trackCost } from '../engine/cost-tracking/operation-tracker.js';
27
22
  import { analyzeSimplicity } from '../engine/simplicity-detector.js';
28
23
  import { withBudget, unwrapBudget, withTotalBudget } from '../engine/timing/budget.js';
29
24
  import { measureStep } from '../engine/timing/structured-log.js';
30
25
  import { resolveProjectIdOrAutoDetect } from './resolve-project-id.js';
31
26
  import { hashProjectPath } from '../storage/base-store.js';
32
- import { findSimilarSpecs } from '../engine/spec-searcher.js';
33
- import { scoreSpecQuality } from '../engine/spec-quality-scorer.js';
34
- import { runPriorDecisionsHint } from './create-spec/adapters/prior-decisions-hint.js';
35
- import { adviseSimilarSpecs } from '../engine/complexity-budget/index.js';
36
27
  import { issuePlannerToken } from '../engine/reviewer-tokens/issuer.js';
28
+ import { getRuntimePolicy } from '../engine/runtime-policy.js';
37
29
  import { generateInteractiveQuestions } from './create-spec/question-generator.js';
38
30
  import { buildCriterionGroundingRecords, filterGroundedCriteria, getAdvisoryCriteria, getContractCriteria, } from '../engine/spec-grounding/contract.js';
39
31
  import { checkGenericSpecOutput } from '../engine/spec-quality/generic-output-gate.js';
40
32
  import { calculateActionableSpecMetrics, shouldExposeMetric, } from '../engine/spec-metrics/actionable-metrics.js';
33
+ import { RuntimeDatabase } from '../storage/runtime-db.js';
34
+ import { BoundaryFailure } from '../errors/error-taxonomy.js';
35
+ import { SpecAcFormatEnum, SpecScopeEnum, SpecTargetEnum, SpecTypeEnum } from './schemas/spec.js';
36
+ import { SPEC_CREATED_POST_COMMIT_TASKS } from '../types/outbox-worker.js';
37
+ import { resolveStorageLayout } from '../storage/storage-layout.js';
38
+ import { OperationJournal, recoverOperationJournal, } from '../engine/execution/operation-journal.js';
39
+ import { assertExecutionCanCommit } from '../engine/execution/context.js';
40
+ import { cleanupExecutionArtifact, executionLink, executionMkdir, executionRemove, executionRename, executionWriteFileExclusive, } from '../engine/execution/deadline-io.js';
41
+ import { atomicWriteFile } from '../engine/safety/atomic-write-file.js';
41
42
  /** SPEC-584: Persist a clarification token when interactive questions are emitted. Best-effort. */
42
43
  async function persistClarificationToken(earlyReturn, projectId, toolName) {
43
44
  try {
@@ -88,31 +89,13 @@ function handleClarification(_server, description, knowledge, autopilot, params)
88
89
  function hasFileEntries(files) {
89
90
  return files.create.length + files.modify.length + files.test.length > 0;
90
91
  }
91
- function mergeTechnicalFiles(primary, fallback) {
92
- return {
93
- create: mergeFileEntries(primary.create, fallback.create),
94
- modify: mergeFileEntries(primary.modify, fallback.modify),
95
- test: mergeFileEntries(primary.test, fallback.test),
96
- };
97
- }
98
- function mergeFileEntries(primary, fallback) {
99
- const byPath = new Map();
100
- for (const file of [...primary, ...fallback]) {
101
- if (!byPath.has(file.path)) {
102
- byPath.set(file.path, file);
103
- }
104
- }
105
- return [...byPath.values()];
106
- }
107
92
  async function resolveTechnicalFiles(input) {
108
93
  const generatedSource = [input.generatedTechnicalSection, input.generatedSpecBody]
109
94
  .filter((part) => part.trim().length > 0)
110
95
  .join('\n\n');
111
96
  const extracted = await extractFilesFromSpecBody(generatedSource, input.projectPath);
112
97
  if (extracted !== null && hasFileEntries(extracted)) {
113
- return input.fallbackReason === undefined
114
- ? mergeTechnicalFiles(extracted, input.autopilot.suggestedFiles)
115
- : extracted;
98
+ return extracted;
116
99
  }
117
100
  return { create: [], modify: [], test: [] };
118
101
  }
@@ -185,23 +168,6 @@ const HIGH_RISK_WARNING = 'High-risk spec — consider: ' +
185
168
  '1) Is the approach technically feasible within the project constraints? ' +
186
169
  '2) What edge cases (network failure, concurrent writes, empty state) could break this? ' +
187
170
  '3) Which existing specs or external services does this depend on?';
188
- async function runQualityScore(spec) {
189
- try {
190
- const report = await scoreSpecQuality(spec);
191
- return {
192
- total: report.score.total,
193
- grade: report.score.grade,
194
- completeness: report.score.completeness,
195
- testability: report.score.testability,
196
- ambiguity: report.score.ambiguity,
197
- risk: report.score.risk,
198
- };
199
- }
200
- catch {
201
- /* best-effort — never block spec creation */
202
- return null;
203
- }
204
- }
205
171
  function runSimplicityCheck(text, hours) {
206
172
  try {
207
173
  return analyzeSimplicity(text, hours);
@@ -221,37 +187,15 @@ function buildAdvisoryCompat() {
221
187
  };
222
188
  }
223
189
  /** Persist only out-of-scope items supplied explicitly by the caller. */
224
- function resolveOutOfScope(provided) {
225
- return provided?.filter((item) => item.trim().length > 0) ?? [];
226
- }
227
- /** SPEC-614 AC4: suggest complexity category based on similar historical specs (best-effort). */
228
- async function runComplexityAdvice(projectId, currentSpecId, tags, targetCriteriaCount) {
229
- try {
230
- const allSpecs = await specStore.listSpecs(projectId);
231
- const historicals = allSpecs
232
- .filter((s) => s.id !== currentSpecId)
233
- .slice(-200)
234
- .map((s) => ({
235
- specId: s.id,
236
- criteriaCount: s.reviewNotes?.length ?? 0,
237
- filesCount: s.impactAnalysis?.affectedFiles.length ?? 0,
238
- estimatedHours: s.estimation.devHours,
239
- actualHours: s.actuals?.devHours,
240
- tags: s.tags,
241
- }));
242
- const advice = adviseSimilarSpecs(targetCriteriaCount, tags, historicals);
243
- if (advice.similarSpecsCount === 0) {
244
- return null;
245
- }
246
- return {
247
- suggestedCategory: advice.suggestedCategory,
248
- reasoning: advice.reasoning,
249
- similarSpecsCount: advice.similarSpecsCount,
250
- };
251
- }
252
- catch {
253
- return null;
190
+ function resolveOutOfScope(provided, source = '') {
191
+ const explicit = provided?.filter((item) => item.trim().length > 0) ?? [];
192
+ if (explicit.length > 0) {
193
+ return explicit;
254
194
  }
195
+ const section = /(?:^|\n)##\s+Out of scope\s*\n([\s\S]*?)(?=\n##\s|$)/i.exec(source)?.[1];
196
+ return section
197
+ ? Array.from(section.matchAll(/^[-*]\s+(.+)$/gm), (match) => match[1]?.trim() ?? '').filter(Boolean)
198
+ : [];
255
199
  }
256
200
  /** SPEC-783: Synthesize agent team findings into unified spec.md ## Technical section. */
257
201
  async function handleAgentTeamSynthesis(specId, projectPath, findings) {
@@ -296,7 +240,7 @@ async function handleAgentTeamSynthesis(specId, projectPath, findings) {
296
240
  const synthesized = synthesizeFindings('', findings);
297
241
  updatedContent = `${specContent.trimEnd()}\n\n## Technical\n\n${synthesized}\n`;
298
242
  }
299
- await writeFile(specPath, updatedContent, 'utf8');
243
+ await atomicWriteFile(specPath, updatedContent, { encoding: 'utf8' });
300
244
  return {
301
245
  content: [
302
246
  {
@@ -311,14 +255,139 @@ async function handleAgentTeamSynthesis(specId, projectPath, findings) {
311
255
  return { content: [{ type: 'text', text: `Synthesis failed: ${msg}` }], isError: true };
312
256
  }
313
257
  }
314
- /** SPEC-770: Derive idempotency key from title and projectPath. */
315
- function computeIdempotencyKey(title, projectPath) {
316
- return createHash('sha256').update(`${title}::${projectPath}`).digest('hex');
258
+ /** Derive a bounded storage key from an explicit caller key or the legacy request identity. */
259
+ function computeIdempotencyKey(title, projectPath, explicitKey) {
260
+ const identity = explicitKey ? `explicit::${explicitKey}` : `${title}::${projectPath}`;
261
+ return createHash('sha256').update(identity).digest('hex');
262
+ }
263
+ function createSpecRequestDigest(params, projectPath) {
264
+ return createHash('sha256')
265
+ .update(JSON.stringify({ ...params, projectPath, idempotencyKey: undefined }))
266
+ .digest('hex');
267
+ }
268
+ function computeOperationKey(params, projectPath, dedupeKey) {
269
+ const identity = params.idempotencyKey
270
+ ? `explicit::${params.idempotencyKey}`
271
+ : `implicit::${dedupeKey}::${params.description}::${randomUUID()}`;
272
+ return createHash('sha256').update(`${identity}::${projectPath}`).digest('hex');
317
273
  }
318
- const IDEMPOTENCY_CLAIM_STALE_MS = 60_000;
319
- const IDEMPOTENCY_CLAIM_WAIT_MS = 25_000;
320
- const IDEMPOTENCY_CLAIM_POLL_MS = 20;
321
- const IDEMPOTENCY_MATCH_WINDOW_MS = 10 * 60 * 1000;
274
+ function finishRecoveredOperation(journal, key, result) {
275
+ let entry = journal.get('create_spec', key);
276
+ if (entry?.state === 'intent') {
277
+ entry = journal.prepared('create_spec', key);
278
+ }
279
+ if (entry?.state === 'prepared') {
280
+ entry = journal.committed('create_spec', key, result);
281
+ }
282
+ if (entry?.state === 'committed') {
283
+ entry = journal.acknowledged('create_spec', key);
284
+ }
285
+ return entry?.result ?? result;
286
+ }
287
+ function buildCommittedCreateResult(input) {
288
+ const advisorySignals = [];
289
+ if (input.advisoryCriteria.length > 0) {
290
+ advisorySignals.push(makeAdvisorySignal({
291
+ key: 'ungrounded-contract-items',
292
+ kind: 'quality',
293
+ message: `${String(input.advisoryCriteria.length)} ungrounded contract item(s) kept advisory-only.`,
294
+ source: 'validator',
295
+ evidence: input.advisoryCriteria.slice(0, 10),
296
+ confidence: 0.8,
297
+ surface: 'structuredContent',
298
+ value: input.advisoryCriteria,
299
+ }));
300
+ }
301
+ const splitResult = buildSplitResult(input.splitSuggestion, input.experienceLevel);
302
+ if (splitResult) {
303
+ advisorySignals.push(makeAdvisorySignal({
304
+ key: 'split-suggestion',
305
+ kind: 'complexity',
306
+ message: 'Spec may benefit from splitting.',
307
+ source: 'heuristic',
308
+ evidence: ['spec-splitter heuristic'],
309
+ confidence: 0.5,
310
+ surface: 'structuredContent',
311
+ value: splitResult,
312
+ }));
313
+ }
314
+ if (input.simplicityResult) {
315
+ advisorySignals.push(makeAdvisorySignal({
316
+ key: 'simplicity-check',
317
+ kind: 'simplicity',
318
+ message: `Simplicity recommendation: ${input.simplicityResult.recommendation}.`,
319
+ source: 'heuristic',
320
+ evidence: input.simplicityResult.signals.map((signal) => signal.type),
321
+ confidence: 0.55,
322
+ surface: 'structuredContent',
323
+ value: input.simplicityResult,
324
+ deprecatedAlias: 'simplicityCheck',
325
+ }));
326
+ }
327
+ const spec = input.spec;
328
+ const riskWarning = spec.risk === 'high' || spec.difficulty >= 4 ? HIGH_RISK_WARNING : undefined;
329
+ if (riskWarning) {
330
+ advisorySignals.push(makeAdvisorySignal({
331
+ key: 'risk-warning',
332
+ kind: 'challenge',
333
+ message: 'High-risk heuristic warning generated.',
334
+ source: 'heuristic',
335
+ evidence: [`risk:${spec.risk}`, `difficulty:${String(spec.difficulty)}`],
336
+ confidence: 0.6,
337
+ surface: 'structuredContent',
338
+ value: riskWarning,
339
+ deprecatedAlias: 'riskWarning',
340
+ }));
341
+ }
342
+ return toolResult(`Spec ${spec.id} created and persisted.`, {
343
+ operationId: input.operationId,
344
+ /** @deprecated Operation-journal durability; this is not Git evidence. */
345
+ committed: true,
346
+ persisted: true,
347
+ gitCommitPerformed: false,
348
+ pendingAnalysis: true,
349
+ specId: spec.id,
350
+ title: spec.title,
351
+ slug: spec.slug,
352
+ type: spec.type,
353
+ scope: spec.scope,
354
+ difficulty: spec.difficulty,
355
+ risk: spec.risk,
356
+ tags: spec.tags,
357
+ target: spec.target,
358
+ status: spec.status,
359
+ gitBranch: spec.gitBranch,
360
+ specPath: spec.specPath,
361
+ ...(input.duplicate
362
+ ? { duplicateWarning: ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug }) }
363
+ : {}),
364
+ ...(input.constitutionWarnings.length > 0
365
+ ? { constitutionWarnings: input.constitutionWarnings }
366
+ : {}),
367
+ ...(input.clarificationSession
368
+ ? {
369
+ linkedClarificationSession: input.clarificationSession.id,
370
+ clarificationTopic: input.clarificationSession.topic,
371
+ clarificationAnswersUsed: Object.keys(input.clarificationSession.answers).length,
372
+ }
373
+ : {
374
+ clarificationNote: 'Spec created directly from the supplied description; no clarification session was linked.',
375
+ }),
376
+ ...(input.contradictionHint ? { contradictionHint: input.contradictionHint } : {}),
377
+ ...(input.actionableMetrics.length > 0 ? { actionableMetrics: input.actionableMetrics } : {}),
378
+ ...(input.simplicityResult ? { simplicityCheck: input.simplicityResult } : {}),
379
+ ...(riskWarning ? { riskWarning } : {}),
380
+ ...(input.plannerToken ? { plannerToken: input.plannerToken } : {}),
381
+ ...(advisorySignals.length > 0 ? { advisorySignals } : {}),
382
+ compat: buildAdvisoryCompat(),
383
+ message: ti('tools.create_spec.success', { id: spec.id, title: spec.title }),
384
+ });
385
+ }
386
+ const CREATE_SPEC_POLICY = getRuntimePolicy().createSpec;
387
+ const IDEMPOTENCY_CLAIM_STALE_MS = CREATE_SPEC_POLICY.idempotencyClaimStaleMs;
388
+ const IDEMPOTENCY_CLAIM_WAIT_MS = CREATE_SPEC_POLICY.idempotencyClaimWaitMs;
389
+ const IDEMPOTENCY_CLAIM_POLL_MS = CREATE_SPEC_POLICY.idempotencyClaimPollMs;
390
+ const IDEMPOTENCY_MATCH_WINDOW_MS = CREATE_SPEC_POLICY.idempotencyMatchWindowMs;
322
391
  function getIdempotencyEvidencePath(projectPath, key) {
323
392
  const analysisPath = getAsyncAnalysisPath(projectPath, 'SPEC-000');
324
393
  const projectDataPath = pathDirname(pathDirname(analysisPath));
@@ -438,9 +507,8 @@ async function commitIdempotencyEvidence(projectPath, key, claim, spec, specPath
438
507
  committedAt,
439
508
  };
440
509
  try {
441
- await writeFile(evidencePath, JSON.stringify(evidence, null, 2), {
510
+ await executionWriteFileExclusive(evidencePath, JSON.stringify(evidence, null, 2), {
442
511
  encoding: 'utf-8',
443
- flag: 'wx',
444
512
  });
445
513
  }
446
514
  catch (error) {
@@ -464,10 +532,66 @@ async function releaseIdempotencyClaim(projectPath, key, ownerId) {
464
532
  await quarantineAndDeleteIdempotencyClaim(claimPath, raw);
465
533
  }
466
534
  }
535
+ async function decideCreateSpecRecovery(entry) {
536
+ const payload = entry.payload;
537
+ if (entry.payloadVersion !== 1 || payload?.operationVersion !== 1) {
538
+ return { action: 'quarantine', reason: 'Unsupported create_spec recovery payload' };
539
+ }
540
+ if (entry.state === 'intent') {
541
+ if (payload.ownerId) {
542
+ await releaseIdempotencyClaim(payload.projectPath, payload.idempotencyKey, payload.ownerId);
543
+ }
544
+ return { action: 'rollback', reason: 'Intent has no prepared durable side effects' };
545
+ }
546
+ if (!payload.projectId ||
547
+ !payload.specId ||
548
+ !payload.specPath ||
549
+ !payload.specContentDigest ||
550
+ !payload.result) {
551
+ return { action: 'quarantine', reason: 'Prepared payload is incomplete' };
552
+ }
553
+ const [content, storedSpec, evidenceRaw] = await Promise.all([
554
+ readFile(payload.specPath, 'utf8').catch(() => ''),
555
+ specStore.getSpec(payload.projectId, payload.specId).catch(() => null),
556
+ readFile(getIdempotencyEvidencePath(payload.projectPath, payload.idempotencyKey), 'utf8').catch(() => ''),
557
+ ]);
558
+ const evidence = parseIdempotencyEvidence(evidenceRaw);
559
+ const isCommitted = createHash('sha256').update(content).digest('hex') === payload.specContentDigest &&
560
+ storedSpec?.id === payload.specId &&
561
+ evidence?.specId === payload.specId &&
562
+ (!payload.ownerId || !evidence.ownerId || evidence.ownerId === payload.ownerId);
563
+ if (isCommitted) {
564
+ return {
565
+ action: 'commit',
566
+ result: payload.result,
567
+ outbox: {
568
+ topic: 'spec.created',
569
+ payload: {
570
+ schemaVersion: 1,
571
+ specId: payload.specId,
572
+ projectId: payload.projectId,
573
+ projectPath: payload.projectPath,
574
+ postCommitTasks: SPEC_CREATED_POST_COMMIT_TASKS,
575
+ },
576
+ },
577
+ };
578
+ }
579
+ const noDurableArtifacts = content.length === 0 && storedSpec === null && evidence === undefined;
580
+ if (noDurableArtifacts) {
581
+ if (payload.ownerId) {
582
+ await releaseIdempotencyClaim(payload.projectPath, payload.idempotencyKey, payload.ownerId);
583
+ }
584
+ return { action: 'rollback', reason: 'Prepared mutation has no durable artifacts' };
585
+ }
586
+ return {
587
+ action: 'quarantine',
588
+ reason: 'Prepared create_spec artifacts are partial or inconsistent',
589
+ };
590
+ }
467
591
  async function quarantineAndDeleteIdempotencyClaim(claimPath, expectedRaw) {
468
592
  const quarantinePath = `${claimPath}.${randomUUID()}.quarantine`;
469
593
  try {
470
- await rename(claimPath, quarantinePath);
594
+ await executionRename(claimPath, quarantinePath);
471
595
  }
472
596
  catch (error) {
473
597
  if (error.code === 'ENOENT') {
@@ -477,18 +601,18 @@ async function quarantineAndDeleteIdempotencyClaim(claimPath, expectedRaw) {
477
601
  }
478
602
  const quarantinedRaw = await readFile(quarantinePath, 'utf-8').catch(() => '');
479
603
  if (quarantinedRaw === expectedRaw) {
480
- await rm(quarantinePath, { force: true });
604
+ await executionRemove(quarantinePath, { force: true });
481
605
  return true;
482
606
  }
483
607
  try {
484
- await hardLink(quarantinePath, claimPath);
608
+ await executionLink(quarantinePath, claimPath);
485
609
  }
486
610
  catch (error) {
487
611
  if (error.code !== 'EEXIST') {
488
612
  throw error;
489
613
  }
490
614
  }
491
- await rm(quarantinePath, { force: true });
615
+ await executionRemove(quarantinePath, { force: true });
492
616
  return false;
493
617
  }
494
618
  async function waitForIdempotencyOwner(projectPath, key, claim) {
@@ -514,7 +638,7 @@ async function waitForIdempotencyOwner(projectPath, key, claim) {
514
638
  }
515
639
  async function acquireIdempotencyClaim(projectPath, key, title) {
516
640
  const claimPath = getIdempotencyClaimPath(projectPath, key);
517
- await mkdir(pathDirname(claimPath), { recursive: true });
641
+ await executionMkdir(pathDirname(claimPath), { recursive: true });
518
642
  for (let attempt = 0; attempt < 4; attempt += 1) {
519
643
  const claim = {
520
644
  version: 2,
@@ -525,9 +649,8 @@ async function acquireIdempotencyClaim(projectPath, key, title) {
525
649
  claimedAt: new Date().toISOString(),
526
650
  };
527
651
  try {
528
- await writeFile(claimPath, JSON.stringify(claim, null, 2), {
652
+ await executionWriteFileExclusive(claimPath, JSON.stringify(claim, null, 2), {
529
653
  encoding: 'utf-8',
530
- flag: 'wx',
531
654
  });
532
655
  const racedCommit = await findByIdempotencyEvidence(projectPath, key, Date.now() - IDEMPOTENCY_MATCH_WINDOW_MS);
533
656
  if (racedCommit !== undefined) {
@@ -536,7 +659,7 @@ async function acquireIdempotencyClaim(projectPath, key, title) {
536
659
  }
537
660
  const expiredEvidencePath = getIdempotencyEvidencePath(projectPath, key);
538
661
  if ((await fsStat(expiredEvidencePath).catch(() => null)) !== null) {
539
- await rm(expiredEvidencePath, { force: true });
662
+ await executionRemove(expiredEvidencePath, { force: true });
540
663
  }
541
664
  return { kind: 'owner', claim };
542
665
  }
@@ -600,28 +723,27 @@ async function buildIdempotencyMatchResult(projectPath, existing) {
600
723
  };
601
724
  }
602
725
  const FRONTMATTER_ID_RE = /^id:\s*(SPEC-\d+)/m;
603
- const FRONTMATTER_TITLE_RE = /^title:\s*"?([^"\n]+?)"?\s*$/m;
604
726
  const FRONTMATTER_STATUS_RE = /^status:\s*(\S+)/m;
605
- /** SPEC-770: Scan store and filesystem for a spec with matching idempotency key within windowMs. */
606
- async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEMPOTENCY_MATCH_WINDOW_MS) {
607
- const cutoff = Date.now() - windowMs;
608
- // Fast path: check store (always authoritative for successfully created specs)
727
+ async function findStoreByIdempotencyKey(projectId, key, cutoff) {
609
728
  try {
610
729
  const existing = await specStore.listSpecs(projectId);
611
- const match = existing.find((s) => s.idempotencyKey === key && new Date(s.createdAt).getTime() > cutoff);
612
- if (match !== undefined) {
613
- return match;
614
- }
730
+ return existing.find((s) => s.idempotencyKey === key && new Date(s.createdAt).getTime() > cutoff);
615
731
  }
616
732
  catch {
617
- /* best-effort */
733
+ return undefined;
734
+ }
735
+ }
736
+ /** SPEC-770: Scan store and filesystem for a spec with matching idempotency key within windowMs. */
737
+ async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEMPOTENCY_MATCH_WINDOW_MS) {
738
+ const cutoff = Date.now() - windowMs;
739
+ const storeMatch = await findStoreByIdempotencyKey(projectId, key, cutoff);
740
+ if (storeMatch) {
741
+ return storeMatch;
618
742
  }
619
- // Transactional fallback: the request key stays in external project data, not spec.md.
620
743
  const evidenceMatch = await findByIdempotencyEvidence(projectPath, key, cutoff);
621
744
  if (evidenceMatch !== undefined) {
622
745
  return evidenceMatch;
623
746
  }
624
- // Compatibility fallback for specs created before external evidence was introduced.
625
747
  try {
626
748
  const { glob } = await import('glob');
627
749
  const { join: joinPath } = await import('node:path');
@@ -651,7 +773,7 @@ async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEM
651
773
  return undefined;
652
774
  }
653
775
  /** SPEC-770: Scan for recently written spec.md files (for possibleDuplicates in timeout response). */
654
- async function findRecentlyWrittenSpecs(projectPath, windowMs) {
776
+ async function findRecentlyWrittenSpecs(projectPath, windowMs, requestedTitle) {
655
777
  try {
656
778
  const { glob } = await import('glob');
657
779
  const { join: joinPath } = await import('node:path');
@@ -665,9 +787,11 @@ async function findRecentlyWrittenSpecs(projectPath, windowMs) {
665
787
  }
666
788
  const content = await readFile(specFile, 'utf-8').catch(() => '');
667
789
  const idMatch = FRONTMATTER_ID_RE.exec(content);
668
- const titleMatch = FRONTMATTER_TITLE_RE.exec(content);
669
- if (idMatch?.[1] !== undefined) {
670
- results.push({ id: idMatch[1], title: titleMatch?.[1] ?? '' });
790
+ const title = extractFrontmatterTitle(content);
791
+ if (idMatch?.[1] !== undefined &&
792
+ normalizeDuplicateTitle(title) !== '' &&
793
+ normalizeDuplicateTitle(title) === normalizeDuplicateTitle(requestedTitle)) {
794
+ results.push({ id: idMatch[1], title });
671
795
  }
672
796
  }
673
797
  return results;
@@ -676,6 +800,69 @@ async function findRecentlyWrittenSpecs(projectPath, windowMs) {
676
800
  return [];
677
801
  }
678
802
  }
803
+ function extractFrontmatterTitle(content) {
804
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content)?.[1];
805
+ const rawTitle = frontmatter ? /^title:\s*(.*?)\s*$/m.exec(frontmatter)?.[1] : undefined;
806
+ if (!rawTitle) {
807
+ return '';
808
+ }
809
+ if (rawTitle.startsWith('"') && rawTitle.endsWith('"')) {
810
+ try {
811
+ const parsed = JSON.parse(rawTitle);
812
+ return typeof parsed === 'string' ? parsed : '';
813
+ }
814
+ catch {
815
+ return '';
816
+ }
817
+ }
818
+ if (rawTitle.startsWith("'") && rawTitle.endsWith("'")) {
819
+ return rawTitle.slice(1, -1).replace(/''/g, "'");
820
+ }
821
+ return rawTitle;
822
+ }
823
+ function normalizeDuplicateTitle(value) {
824
+ return value
825
+ .normalize('NFKC')
826
+ .toLocaleLowerCase('en-US')
827
+ .replace(/[\p{P}]+/gu, ' ')
828
+ .replace(/\s+/g, ' ')
829
+ .trim();
830
+ }
831
+ const CREATE_SPEC_ENUMS = {
832
+ type: SpecTypeEnum,
833
+ scope: SpecScopeEnum,
834
+ target: SpecTargetEnum,
835
+ acFormat: SpecAcFormatEnum,
836
+ };
837
+ function validateCreateSpecEnums(input) {
838
+ for (const [field, schema] of Object.entries(CREATE_SPEC_ENUMS)) {
839
+ const received = input[field];
840
+ if (received === undefined || schema.safeParse(received).success) {
841
+ continue;
842
+ }
843
+ const boundedReceived = received === null || ['string', 'number', 'boolean'].includes(typeof received)
844
+ ? received
845
+ : typeof received;
846
+ return {
847
+ content: [
848
+ {
849
+ type: 'text',
850
+ text: `${field} must be one of: ${schema.options.join(', ')}.`,
851
+ },
852
+ ],
853
+ isError: true,
854
+ structuredContent: {
855
+ error: 'INVALID_INPUT',
856
+ code: 'INVALID_INPUT',
857
+ status: 422,
858
+ field,
859
+ received: boundedReceived,
860
+ allowed: schema.options,
861
+ },
862
+ };
863
+ }
864
+ return null;
865
+ }
679
866
  const DESCRIPTION_MAX_CHARS = 10_000;
680
867
  const DESCRIPTION_EXCEED_MSG = 'Description exceeds 10000 chars. Create with a summary (<3k) and use reconcile_spec to add the rest.';
681
868
  function checkEnglishOnlyInput(params) {
@@ -700,8 +887,273 @@ function checkEnglishOnlyInput(params) {
700
887
  },
701
888
  };
702
889
  }
890
+ function quotePosixShellArgument(value) {
891
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
892
+ }
893
+ function earlyPreparation(result, clarificationProjectId) {
894
+ return {
895
+ kind: 'early',
896
+ result,
897
+ ...(clarificationProjectId ? { clarificationProjectId } : {}),
898
+ };
899
+ }
900
+ export async function resolveGroundedVerificationCommands(projectPath, testPaths) {
901
+ if (testPaths.length === 0) {
902
+ return [];
903
+ }
904
+ const packageJsonRaw = await readFile(pathJoin(projectPath, 'package.json'), 'utf8').catch(() => '');
905
+ if (!packageJsonRaw) {
906
+ return [];
907
+ }
908
+ try {
909
+ const manifest = JSON.parse(packageJsonRaw);
910
+ if (typeof manifest.packageManager !== 'string') {
911
+ return [];
912
+ }
913
+ const packageManager = /^(pnpm|npm|yarn|bun)@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.exec(manifest.packageManager)?.[1];
914
+ if (!packageManager) {
915
+ return [];
916
+ }
917
+ if (typeof manifest.scripts?.test !== 'string' || manifest.scripts.test.trim() === '') {
918
+ return [];
919
+ }
920
+ return [`${packageManager} run test -- ${testPaths.map(quotePosixShellArgument).join(' ')}`];
921
+ }
922
+ catch {
923
+ return [];
924
+ }
925
+ }
926
+ async function prepareCreateSpecCandidate(initialParams, server) {
927
+ let params = initialParams;
928
+ const buildResult = await measureStep('buildSpecContext', () => buildSpecContext(params));
929
+ if (!buildResult.ok) {
930
+ return earlyPreparation({
931
+ content: [{ type: 'text', text: buildResult.errorMessage }],
932
+ isError: true,
933
+ });
934
+ }
935
+ const context = buildResult.context;
936
+ const { spec, estimation, projectId } = context;
937
+ const knowledge = await measureStep('loadKnowledge', () => knowledgeStore.getKnowledge(projectId));
938
+ const clarificationSession = params.clarificationSessionId
939
+ ? await knowledgeStore.getClarification(projectId, params.clarificationSessionId)
940
+ : null;
941
+ const constitutionCheck = await measureStep('validateConstitution', () => validateConstitution(projectId, spec.title, spec.tags));
942
+ if (constitutionCheck.blocked && constitutionCheck.errorResult) {
943
+ return earlyPreparation(constitutionCheck.errorResult);
944
+ }
945
+ const inlineAnalysis = await withBudget('create-spec-inline-analysis', getRuntimePolicy().createSpec.inlineAnalysisBudgetMs, (signal) => analyzeProjectForSpec(params.projectPath ?? '', params.description, spec.title, knowledge, signal));
946
+ const autopilot = unwrapBudget(inlineAnalysis, getEmptyAutopilotResult());
947
+ if (autopilot.isIdea) {
948
+ return earlyPreparation({
949
+ content: [
950
+ {
951
+ type: 'text',
952
+ text: `💡 **Idea captured**: "${params.description}"\n\nThe description is too brief for a full spec. Use \`capture_idea\` to save it to the backlog, or provide more detail (>10 words) to create a spec.`,
953
+ },
954
+ ],
955
+ });
956
+ }
957
+ const clarificationResult = handleClarification(server, params.description, knowledge, autopilot, params);
958
+ if (clarificationResult !== null) {
959
+ if ('earlyReturn' in clarificationResult) {
960
+ return earlyPreparation(clarificationResult.earlyReturn, projectId);
961
+ }
962
+ params = clarificationResult.params;
963
+ }
964
+ const filteredCriteria = filterGroundedCriteria(autopilot.suggestedCriteria);
965
+ const technologyContract = await readTechnologySelectionContract(params.projectPath ?? '');
966
+ const contractNote = technologyContract
967
+ ? [
968
+ '',
969
+ 'Technology Contract:',
970
+ `- Mode: ${technologyContract.mode}`,
971
+ technologyContract.language ? `- Language: ${technologyContract.language}` : '',
972
+ technologyContract.framework
973
+ ? `- Framework: ${technologyContract.framework}`
974
+ : '- Framework: none or not selected',
975
+ technologyContract.platform ? `- Platform: ${technologyContract.platform}` : '',
976
+ technologyContract.projectType ? `- Project type: ${technologyContract.projectType}` : '',
977
+ '- Agents must not choose a different stack unless the user approves a new contract.',
978
+ ]
979
+ .filter(Boolean)
980
+ .join('\n')
981
+ : '';
982
+ const generatedSpec = await measureStep('generateSpecBody', () => new FallbackGenerator().generate({
983
+ title: spec.title,
984
+ description: `${params.description}${contractNote}`,
985
+ type: spec.type,
986
+ scope: spec.scope,
987
+ target: spec.target,
988
+ acFormat: params.acFormat,
989
+ projectContext: {
990
+ language: technologyContract?.language ?? knowledge?.language ?? undefined,
991
+ framework: technologyContract?.framework ?? knowledge?.framework ?? undefined,
992
+ architecture: knowledge?.architecture.primary ?? undefined,
993
+ },
994
+ }));
995
+ spec.generation = generatedSpec.generation;
996
+ spec.qualityWarnings =
997
+ generatedSpec.qualityWarnings.length > 0 ? generatedSpec.qualityWarnings : undefined;
998
+ const baseCriteria = extractCriteria(generatedSpec.specBody).map((criterion) => criterion.text);
999
+ const groundingCriteria = buildCriterionGroundingRecords({
1000
+ criteria: [...baseCriteria, ...filteredCriteria],
1001
+ userInput: params.description,
1002
+ generatedEvidence: [generatedSpec.generation.modelId ?? generatedSpec.generation.method],
1003
+ });
1004
+ const contractCriteria = getContractCriteria(groundingCriteria);
1005
+ const advisoryCriteria = getAdvisoryCriteria(groundingCriteria).map((record) => record.text);
1006
+ if (contractCriteria.length === 0) {
1007
+ return earlyPreparation({
1008
+ content: [
1009
+ {
1010
+ type: 'text',
1011
+ text: 'create_spec did not persist a spec because no acceptance criterion was grounded in the user request. ' +
1012
+ 'Needs decision: provide at least one explicit, verifiable criterion as a checkbox or Given/When/Then outcome.',
1013
+ },
1014
+ ],
1015
+ isError: true,
1016
+ structuredContent: {
1017
+ error: 'MISSING_GROUNDED_ACCEPTANCE_CRITERIA',
1018
+ missingDecision: 'Provide at least one explicit, verifiable acceptance criterion.',
1019
+ persisted: false,
1020
+ },
1021
+ });
1022
+ }
1023
+ const actionableMetrics = calculateActionableSpecMetrics({
1024
+ criteria: [...baseCriteria, ...filteredCriteria],
1025
+ groundingRecords: groundingCriteria,
1026
+ }).filter(shouldExposeMetric);
1027
+ const technicalFiles = await measureStep('resolveTechnicalFiles', () => resolveTechnicalFiles({
1028
+ generatedSpecBody: generatedSpec.specBody,
1029
+ generatedTechnicalSection: generatedSpec.technicalSection,
1030
+ projectPath: params.projectPath ?? '',
1031
+ }));
1032
+ const groundedTechnical = await measureStep('groundTechnicalFiles', () => groundTechnicalFiles({
1033
+ files: technicalFiles,
1034
+ projectPath: params.projectPath ?? '',
1035
+ userInput: params.description,
1036
+ autopilot,
1037
+ }));
1038
+ const outOfScope = resolveOutOfScope(params.outOfScope, params.description);
1039
+ const scenarioTestPaths = groundedTechnical.files.test.map((file) => file.path);
1040
+ const criteria = contractCriteria.map((record) => ({ text: record.text, done: false }));
1041
+ const leanSpec = generateLeanSpecContent({
1042
+ spec,
1043
+ description: generatedSpec.specBody,
1044
+ estimation,
1045
+ criteriaOverride: criteria,
1046
+ groundingCriteria: contractCriteria,
1047
+ groundingTechnicalReferences: groundedTechnical.records,
1048
+ acFormat: params.acFormat,
1049
+ scenarioTestPaths,
1050
+ });
1051
+ const leanTechnical = generateLeanTechnicalContent({
1052
+ specId: spec.id,
1053
+ filesToCreate: groundedTechnical.files.create,
1054
+ filesToModify: groundedTechnical.files.modify,
1055
+ filesToTest: groundedTechnical.files.test,
1056
+ includeDecisionRequiredPlaceholder: spec.scope !== 'trivial',
1057
+ });
1058
+ const verificationCommands = await resolveGroundedVerificationCommands(params.projectPath ?? '', scenarioTestPaths);
1059
+ const implementationContract = buildImplementationContractSection({
1060
+ description: generatedSpec.specBody,
1061
+ criteria,
1062
+ files: groundedTechnical.files,
1063
+ outOfScope,
1064
+ verificationCommands,
1065
+ });
1066
+ const unifiedSpec = buildCanonicalUnifiedSpecContent({
1067
+ leanSpecBody: leanSpec,
1068
+ sourceDescription: params.description,
1069
+ technicalBody: leanTechnical,
1070
+ implementationContract,
1071
+ criteria,
1072
+ files: groundedTechnical.files,
1073
+ outOfScope,
1074
+ });
1075
+ const candidateValidation = validateUnifiedSpecCandidate(unifiedSpec, {
1076
+ groundedFilePaths: groundedTechnical.records.map((record) => record.path),
1077
+ });
1078
+ if (!candidateValidation.valid) {
1079
+ return earlyPreparation({
1080
+ content: [
1081
+ {
1082
+ type: 'text',
1083
+ text: `Unified spec candidate is invalid: ${candidateValidation.issues
1084
+ .slice(0, 3)
1085
+ .map((issue) => issue.message)
1086
+ .join('; ')}`,
1087
+ },
1088
+ ],
1089
+ isError: true,
1090
+ structuredContent: {
1091
+ error: 'SPEC_FORMAT_INVALID',
1092
+ code: 422,
1093
+ persisted: false,
1094
+ issues: candidateValidation.issues,
1095
+ },
1096
+ });
1097
+ }
1098
+ const genericOutputGate = checkGenericSpecOutput(unifiedSpec);
1099
+ if (!genericOutputGate.passed) {
1100
+ return earlyPreparation({
1101
+ content: [
1102
+ {
1103
+ type: 'text',
1104
+ text: 'Spec quality gate blocked generic output before persistence. ' +
1105
+ genericOutputGate.issues
1106
+ .slice(0, 3)
1107
+ .map((issue) => `${issue.phrase}: ${issue.reason}`)
1108
+ .join('; '),
1109
+ },
1110
+ ],
1111
+ isError: true,
1112
+ structuredContent: {
1113
+ error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
1114
+ sddConstitutionRuleId: 'sdd.no-generic-output',
1115
+ issues: genericOutputGate.issues,
1116
+ fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior.',
1117
+ },
1118
+ });
1119
+ }
1120
+ if (outOfScope.length > 0) {
1121
+ spec.outOfScope = outOfScope;
1122
+ }
1123
+ return {
1124
+ kind: 'ready',
1125
+ prepared: {
1126
+ params,
1127
+ context,
1128
+ knowledge,
1129
+ clarificationSession,
1130
+ constitutionWarnings: constitutionCheck.warnings,
1131
+ advisoryCriteria,
1132
+ actionableMetrics,
1133
+ unifiedSpec,
1134
+ simplicityResult: runSimplicityCheck([params.description, ...filteredCriteria].join('\n'), estimation.devHours),
1135
+ },
1136
+ };
1137
+ }
703
1138
  // eslint-disable-next-line max-lines-per-function
704
1139
  export async function handleCreateSpec(inputParams, server) {
1140
+ const enumValidation = validateCreateSpecEnums(inputParams);
1141
+ if (enumValidation) {
1142
+ return enumValidation;
1143
+ }
1144
+ if (inputParams.idempotencyKey !== undefined &&
1145
+ !/^[A-Za-z0-9._:-]{8,128}$/.test(inputParams.idempotencyKey)) {
1146
+ return {
1147
+ content: [
1148
+ {
1149
+ type: 'text',
1150
+ text: 'idempotencyKey must contain 8-128 letters, digits, dots, underscores, colons or hyphens.',
1151
+ },
1152
+ ],
1153
+ isError: true,
1154
+ structuredContent: { error: 'INVALID_IDEMPOTENCY_KEY', code: 422 },
1155
+ };
1156
+ }
705
1157
  // SPEC-781: Friendly error for description exceeding 10000 chars (replaces cryptic Zod error)
706
1158
  if (inputParams.description && inputParams.description.length > DESCRIPTION_MAX_CHARS) {
707
1159
  return {
@@ -746,28 +1198,157 @@ export async function handleCreateSpec(inputParams, server) {
746
1198
  if (languageGate) {
747
1199
  return languageGate;
748
1200
  }
1201
+ // SPEC-584: Gate check — block if pending clarification token exists and no answers provided
1202
+ const gateError = await runClarificationGate(resolvedPath, 'create_spec', inputParams.clarificationAnswers);
1203
+ if (gateError) {
1204
+ return gateError;
1205
+ }
1206
+ const idempotencyKey = computeIdempotencyKey(inputParams.title, resolvedPath, inputParams.idempotencyKey);
1207
+ const cutoff = Date.now() - IDEMPOTENCY_MATCH_WINDOW_MS;
1208
+ const storeOnlyCandidate = await findStoreByIdempotencyKey(hashProjectPath(resolvedPath), idempotencyKey, cutoff);
1209
+ let preparation;
1210
+ try {
1211
+ preparation = await prepareCreateSpecCandidate(resolvedInputParams, server);
1212
+ }
1213
+ catch (error) {
1214
+ const message = error instanceof Error ? error.message : String(error);
1215
+ return {
1216
+ content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
1217
+ isError: true,
1218
+ };
1219
+ }
1220
+ if (preparation.kind === 'early') {
1221
+ if (preparation.clarificationProjectId) {
1222
+ await persistClarificationToken(preparation.result, preparation.clarificationProjectId, 'create_spec');
1223
+ }
1224
+ return preparation.result;
1225
+ }
1226
+ const prepared = preparation.prepared;
1227
+ prepared.context.spec.idempotencyKey = idempotencyKey;
1228
+ if (storeOnlyCandidate) {
1229
+ const committedEvidence = await findByIdempotencyEvidence(resolvedPath, idempotencyKey, cutoff);
1230
+ if (!committedEvidence) {
1231
+ return {
1232
+ content: [
1233
+ {
1234
+ type: 'text',
1235
+ text: `A partial create_spec transaction exists for ${storeOnlyCandidate.id}: the store row has no committed idempotency evidence. Recovery is required before retrying.`,
1236
+ },
1237
+ ],
1238
+ isError: true,
1239
+ structuredContent: {
1240
+ error: 'PARTIAL_IDEMPOTENCY_STATE',
1241
+ code: 409,
1242
+ persisted: false,
1243
+ specId: storeOnlyCandidate.id,
1244
+ },
1245
+ };
1246
+ }
1247
+ }
749
1248
  // SPEC-665: Fire-and-forget TTL refresh for conventions.json (never blocks create_spec)
750
1249
  void import('../engine/conventions-ttl.js').then(({ triggerConventionsTtlRefresh }) => {
751
1250
  void triggerConventionsTtlRefresh(resolvedPath).catch(() => {
752
1251
  /* best-effort */
753
1252
  });
754
1253
  });
755
- // SPEC-584: Gate check — block if pending clarification token exists and no answers provided
756
- const gateError = await runClarificationGate(resolvedPath, 'create_spec', inputParams.clarificationAnswers);
757
- if (gateError) {
758
- return gateError;
759
- }
760
1254
  // SPEC-770: Idempotency check — return existing spec if same title+projectPath within 10 minutes
761
- const idempotencyKey = computeIdempotencyKey(inputParams.title, resolvedPath);
1255
+ const operationKey = computeOperationKey(resolvedInputParams, resolvedPath, idempotencyKey);
1256
+ const runtimeDatabase = new RuntimeDatabase({ path: resolveStorageLayout().runtimeDatabase });
1257
+ const operationJournal = new OperationJournal(runtimeDatabase, createHash('sha256').update(hashProjectPath(resolvedPath)).digest('hex').slice(0, 32));
1258
+ let journalEntry;
1259
+ try {
1260
+ await recoverOperationJournal({
1261
+ journal: operationJournal,
1262
+ operation: 'create_spec',
1263
+ staleBefore: new Date(Date.now() - IDEMPOTENCY_CLAIM_STALE_MS).toISOString(),
1264
+ decide: decideCreateSpecRecovery,
1265
+ });
1266
+ journalEntry = operationJournal.begin('create_spec', operationKey, inputParams.idempotencyKey
1267
+ ? createSpecRequestDigest(resolvedInputParams, resolvedPath)
1268
+ : operationKey, {
1269
+ payloadVersion: 1,
1270
+ payload: {
1271
+ operationVersion: 1,
1272
+ projectPath: resolvedPath,
1273
+ idempotencyKey,
1274
+ },
1275
+ });
1276
+ if (journalEntry.state === 'rolled-back') {
1277
+ journalEntry = operationJournal.restartRolledBack('create_spec', operationKey);
1278
+ journalEntry = operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
1279
+ operationVersion: 1,
1280
+ projectPath: resolvedPath,
1281
+ idempotencyKey,
1282
+ });
1283
+ }
1284
+ }
1285
+ catch (error) {
1286
+ runtimeDatabase.close();
1287
+ if (error instanceof BoundaryFailure &&
1288
+ error.kind === 'Conflict' &&
1289
+ error.operation === 'operation begin') {
1290
+ return {
1291
+ content: [
1292
+ {
1293
+ type: 'text',
1294
+ text: 'The idempotency key is already bound to a different create_spec request.',
1295
+ },
1296
+ ],
1297
+ isError: true,
1298
+ structuredContent: {
1299
+ error: 'IDEMPOTENCY_CONFLICT',
1300
+ code: 'IDEMPOTENCY_CONFLICT',
1301
+ status: 409,
1302
+ persisted: false,
1303
+ },
1304
+ };
1305
+ }
1306
+ throw error;
1307
+ }
1308
+ if ((journalEntry.state === 'committed' || journalEntry.state === 'acknowledged') &&
1309
+ journalEntry.result) {
1310
+ if (journalEntry.state === 'committed') {
1311
+ operationJournal.acknowledged('create_spec', operationKey);
1312
+ }
1313
+ runtimeDatabase.close();
1314
+ return journalEntry.result;
1315
+ }
1316
+ if (journalEntry.state === 'quarantined') {
1317
+ runtimeDatabase.close();
1318
+ return {
1319
+ content: [
1320
+ {
1321
+ type: 'text',
1322
+ text: `The previous create_spec attempt is quarantined: ${journalEntry.recoveryReason ?? 'artifact mismatch'}.`,
1323
+ },
1324
+ ],
1325
+ isError: true,
1326
+ structuredContent: { recoveryQuarantined: true, operationId: operationKey },
1327
+ };
1328
+ }
762
1329
  const existingByKey = await findByIdempotencyKey(resolvedPath, idempotencyKey, hashProjectPath(resolvedPath));
763
1330
  if (existingByKey) {
764
- return buildIdempotencyMatchResult(resolvedPath, existingByKey);
1331
+ const result = await buildIdempotencyMatchResult(resolvedPath, existingByKey);
1332
+ finishRecoveredOperation(operationJournal, operationKey, result);
1333
+ runtimeDatabase.close();
1334
+ return result;
1335
+ }
1336
+ let claimResolution;
1337
+ try {
1338
+ claimResolution = await acquireIdempotencyClaim(resolvedPath, idempotencyKey, inputParams.title);
1339
+ }
1340
+ catch (error) {
1341
+ runtimeDatabase.close();
1342
+ throw error;
765
1343
  }
766
- const claimResolution = await acquireIdempotencyClaim(resolvedPath, idempotencyKey, inputParams.title);
767
1344
  if (claimResolution.kind === 'existing') {
768
- return buildIdempotencyMatchResult(resolvedPath, claimResolution.spec);
1345
+ const result = await buildIdempotencyMatchResult(resolvedPath, claimResolution.spec);
1346
+ finishRecoveredOperation(operationJournal, operationKey, result);
1347
+ runtimeDatabase.close();
1348
+ return result;
769
1349
  }
770
1350
  if (claimResolution.kind === 'pending') {
1351
+ runtimeDatabase.close();
771
1352
  return {
772
1353
  content: [
773
1354
  {
@@ -785,254 +1366,101 @@ export async function handleCreateSpec(inputParams, server) {
785
1366
  retainForInFlightWork: false,
786
1367
  };
787
1368
  try {
788
- // eslint-disable-next-line max-lines-per-function, complexity -- orchestrator: one sequential gate + response-build block per lifecycle step; splitting requires threading 15+ shared variables
1369
+ operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
1370
+ operationVersion: 1,
1371
+ projectPath: resolvedPath,
1372
+ idempotencyKey,
1373
+ ownerId: idempotencyClaim.ownerId,
1374
+ });
789
1375
  return await trackCost(resolvedInputParams.projectPath ?? '', 'create_spec', async () => {
790
- // Allow internal re-enrichment without mutating function parameter
791
- let params = resolvedInputParams;
792
- const { description, clarificationSessionId } = params;
793
1376
  try {
794
1377
  // SPEC-713: Phase 1 — Critical path: build + persist spec within 25s hard ceiling.
795
1378
  // Only the steps required to produce spec.md on disk are inside this budget.
796
- // Post-creation enrichment (git, contradictions, quality scores, etc.) runs outside.
797
- const criticalResult = await withTotalBudget('create_spec-critical', 25_000, async () => {
798
- // Build spec context (ID, estimation, diagrams, scope filters)
799
- const buildResult = await measureStep('buildSpecContext', () => buildSpecContext(params));
800
- if (!buildResult.ok) {
801
- return {
802
- ok: false,
803
- earlyReturn: {
804
- content: [{ type: 'text', text: buildResult.errorMessage }],
805
- isError: true,
806
- },
807
- };
808
- }
809
- const { spec, specDir, specPath, technicalPath, estimation, splitSuggestion, duplicate, projectId, agentTeamPlan, } = buildResult.context;
810
- // Kept in external store/evidence for retries; the value-only serializer omits it.
811
- spec.idempotencyKey = idempotencyKey;
812
- // Load project knowledge and clarification session
813
- const knowledge = await measureStep('loadKnowledge', () => knowledgeStore.getKnowledge(projectId));
814
- const clarificationSession = clarificationSessionId
815
- ? await knowledgeStore.getClarification(projectId, clarificationSessionId)
816
- : null;
817
- // Validate against Constitution
818
- const constitutionCheck = await measureStep('validateConstitution', () => validateConstitution(projectId, spec.title, spec.tags));
819
- if (constitutionCheck.blocked && constitutionCheck.errorResult) {
820
- return { ok: false, earlyReturn: constitutionCheck.errorResult };
821
- }
822
- // SPEC-461 Phase 3: Autopilot — analyze project to enrich spec (best-effort)
823
- // SPEC-560: Wrapped with 5s timeout to prevent hangs on large projects (2000+ files)
824
- // SPEC-713: measureStep for Phase 0 diagnostic; withBudget enforces 5s ceiling
825
- const autopilotResult = await withBudget('autopilot-analyzer', 5_000, () => measureStep('autopilot-analyzer', () => analyzeProjectForSpec(params.projectPath ?? '', description, spec.title, knowledge)));
826
- const autopilot = unwrapBudget(autopilotResult, getEmptyAutopilotResult());
827
- // If very vague (<10 words), capture as idea instead of spec
828
- if (autopilot.isIdea) {
829
- return {
830
- ok: false,
831
- earlyReturn: {
832
- content: [
833
- {
834
- type: 'text',
835
- text: `💡 **Idea captured**: "${description}"\n\nThe description is too brief for a full spec. Use \`capture_idea\` to save it to the backlog, or provide more detail (>10 words) to create a spec.`,
836
- },
837
- ],
838
- },
839
- };
1379
+ // Durable post-commit work is delegated to the spec.created outbox consumer.
1380
+ const criticalResult = await withTotalBudget('create_spec-critical', getRuntimePolicy().createSpec.criticalTimeoutMs, async (criticalSignal) => {
1381
+ const { params, context, knowledge, clarificationSession, constitutionWarnings, advisoryCriteria, actionableMetrics, unifiedSpec, simplicityResult: committedSimplicity, } = prepared;
1382
+ const { spec, specDir, specPath, splitSuggestion, duplicate, projectId } = context;
1383
+ let committedPlannerToken;
1384
+ try {
1385
+ committedPlannerToken = await issuePlannerToken(resolvedPath, spec.id, params.sessionId ?? 'unknown-session', params.modelId ?? 'unknown-model', params.host ?? 'unknown-host');
840
1386
  }
841
- // SPEC-463 / SPEC-471: Interactive clarification
842
- const clarificationResult = handleClarification(server, description, knowledge, autopilot, params);
843
- if (clarificationResult !== null) {
844
- if ('earlyReturn' in clarificationResult) {
845
- // SPEC-584: Persist token so the gate blocks retries without answers
846
- await persistClarificationToken(clarificationResult.earlyReturn, projectId, 'create_spec');
847
- return { ok: false, earlyReturn: clarificationResult.earlyReturn };
848
- }
849
- params = clarificationResult.params;
1387
+ catch {
1388
+ // Best-effort evidence must not block durable spec creation.
850
1389
  }
851
- const filteredCriteria = filterGroundedCriteria(autopilot.suggestedCriteria);
852
- const technologyContract = await readTechnologySelectionContract(params.projectPath ?? '');
853
- const contractNote = technologyContract
854
- ? [
855
- '',
856
- 'Technology Contract:',
857
- `- Mode: ${technologyContract.mode}`,
858
- technologyContract.language ? `- Language: ${technologyContract.language}` : '',
859
- technologyContract.framework
860
- ? `- Framework: ${technologyContract.framework}`
861
- : '- Framework: none or not selected',
862
- technologyContract.platform ? `- Platform: ${technologyContract.platform}` : '',
863
- technologyContract.projectType
864
- ? `- Project type: ${technologyContract.projectType}`
865
- : '',
866
- '- Agents must not choose a different stack unless the user approves a new contract.',
867
- ]
868
- .filter(Boolean)
869
- .join('\n')
870
- : '';
871
- const specGenerator = new FallbackGenerator();
872
- const generatedSpec = await measureStep('generateSpecBody', () => specGenerator.generate({
873
- title: spec.title,
874
- description: `${description}${contractNote}`,
875
- type: spec.type,
876
- scope: spec.scope,
877
- target: spec.target,
878
- acFormat: params.acFormat,
879
- projectContext: {
880
- language: technologyContract?.language ?? knowledge?.language ?? undefined,
881
- framework: technologyContract?.framework ?? knowledge?.framework ?? undefined,
882
- architecture: knowledge?.architecture.primary ?? undefined,
883
- },
884
- }));
885
- spec.generation = generatedSpec.generation;
886
- spec.qualityWarnings =
887
- generatedSpec.qualityWarnings.length > 0 ? generatedSpec.qualityWarnings : undefined;
888
- const baseCriteria = extractCriteria(generatedSpec.specBody).map((criterion) => criterion.text);
889
- const groundingCriteria = buildCriterionGroundingRecords({
890
- criteria: [...baseCriteria, ...filteredCriteria],
891
- userInput: description,
892
- generatedEvidence: [
893
- generatedSpec.generation.modelId ?? generatedSpec.generation.method,
894
- ],
895
- });
896
- const contractCriteria = getContractCriteria(groundingCriteria);
897
- const advisoryCriteria = getAdvisoryCriteria(groundingCriteria).map((record) => record.text);
898
- if (contractCriteria.length === 0) {
899
- return {
900
- ok: false,
901
- earlyReturn: {
902
- content: [
903
- {
904
- type: 'text',
905
- text: 'create_spec did not persist a spec because no acceptance criterion was grounded in the user request. ' +
906
- 'Needs decision: provide at least one explicit, verifiable criterion as a checkbox or Given/When/Then outcome.',
907
- },
908
- ],
909
- isError: true,
910
- structuredContent: {
911
- error: 'MISSING_GROUNDED_ACCEPTANCE_CRITERIA',
912
- missingDecision: 'Provide at least one explicit, verifiable acceptance criterion.',
913
- persisted: false,
914
- },
915
- },
916
- };
917
- }
918
- const actionableMetrics = calculateActionableSpecMetrics({
919
- criteria: [...baseCriteria, ...filteredCriteria],
920
- groundingRecords: groundingCriteria,
921
- }).filter(shouldExposeMetric);
922
- const technicalFiles = await measureStep('resolveTechnicalFiles', () => resolveTechnicalFiles({
923
- generatedSpecBody: generatedSpec.specBody,
924
- generatedTechnicalSection: generatedSpec.technicalSection,
925
- projectPath: params.projectPath ?? '',
926
- autopilot,
927
- fallbackReason: generatedSpec.fallbackReason,
928
- }));
929
- const groundedTechnical = await measureStep('groundTechnicalFiles', () => groundTechnicalFiles({
930
- files: technicalFiles,
931
- projectPath: params.projectPath ?? '',
932
- userInput: description,
933
- autopilot,
934
- }));
935
- const outOfScope = resolveOutOfScope(params.outOfScope);
936
- const scenarioTestPaths = groundedTechnical.files.test.map((file) => file.path);
937
- const leanSpec = generateLeanSpecContent({
1390
+ const committedResult = buildCommittedCreateResult({
938
1391
  spec,
939
- description: generatedSpec.specBody,
940
- estimation,
941
- criteriaOverride: contractCriteria.map((record) => ({
942
- text: record.text,
943
- done: false,
944
- })),
945
- groundingCriteria: contractCriteria,
946
- groundingTechnicalReferences: groundedTechnical.records,
947
- acFormat: params.acFormat,
948
- scenarioTestPaths,
949
- });
950
- const leanTechnical = generateLeanTechnicalContent({
951
- specId: spec.id,
952
- filesToCreate: groundedTechnical.files.create,
953
- filesToModify: groundedTechnical.files.modify,
954
- filesToTest: groundedTechnical.files.test,
955
- includeDecisionRequiredPlaceholder: spec.scope !== 'trivial',
956
- });
957
- // SPEC-709: write unified spec.md from origin — no separate technical.md.
958
- // The legacy two-file output is preserved by appending the technical body
959
- // as a `## Technical` section inside spec.md.
960
- const unifiedWithoutContract = buildUnifiedSpecContent(leanSpec, leanTechnical);
961
- const unifiedSpec = appendImplementationContractIfMissing(unifiedWithoutContract, {
962
- description: generatedSpec.specBody,
963
- criteria: contractCriteria.map((record) => ({ text: record.text, done: false })),
964
- files: groundedTechnical.files,
965
- outOfScope,
966
- verificationCommands: [],
1392
+ operationId: operationKey,
1393
+ duplicate,
1394
+ constitutionWarnings,
1395
+ clarificationSession,
1396
+ advisoryCriteria,
1397
+ actionableMetrics,
1398
+ splitSuggestion,
1399
+ experienceLevel: knowledge?.experienceLevel,
1400
+ contradictionHint: undefined,
1401
+ simplicityResult: committedSimplicity,
1402
+ plannerToken: committedPlannerToken,
967
1403
  });
968
- const genericOutputGate = checkGenericSpecOutput(unifiedSpec);
969
- if (!genericOutputGate.passed) {
970
- return {
971
- ok: false,
972
- earlyReturn: {
973
- content: [
974
- {
975
- type: 'text',
976
- text: 'Spec quality gate blocked generic output before persistence. ' +
977
- genericOutputGate.issues
978
- .slice(0, 3)
979
- .map((issue) => `${issue.phrase}: ${issue.reason}`)
980
- .join('; '),
981
- },
982
- ],
983
- isError: true,
984
- structuredContent: {
985
- error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
986
- sddConstitutionRuleId: 'sdd.no-generic-output',
987
- issues: genericOutputGate.issues,
988
- fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior.',
989
- },
990
- },
991
- };
992
- }
1404
+ let storeCreated = false;
993
1405
  try {
994
- await measureStep('mkdir-specDir', () => mkdir(specDir, { recursive: true }));
1406
+ assertExecutionCanCommit(criticalSignal);
1407
+ operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
1408
+ operationVersion: 1,
1409
+ projectPath: resolvedPath,
1410
+ idempotencyKey,
1411
+ ownerId: idempotencyClaim.ownerId,
1412
+ projectId,
1413
+ specId: spec.id,
1414
+ specPath,
1415
+ specContentDigest: createHash('sha256').update(unifiedSpec).digest('hex'),
1416
+ result: committedResult,
1417
+ });
1418
+ operationJournal.prepared('create_spec', operationKey);
1419
+ await measureStep('mkdir-specDir', () => executionMkdir(specDir, { recursive: true }));
995
1420
  // SPEC-713: measure file write — this is the critical persistence step.
996
- await measureStep('writeFile-specPath', () => writeFile(specPath, unifiedSpec, 'utf-8'));
1421
+ assertExecutionCanCommit(criticalSignal);
1422
+ await measureStep('writeFile-specPath', () => atomicWriteFile(specPath, unifiedSpec, { encoding: 'utf-8' }));
1423
+ assertExecutionCanCommit(criticalSignal);
1424
+ await measureStep('specStore-createSpec', () => specStore.createSpec(projectId, spec));
1425
+ storeCreated = true;
1426
+ assertExecutionCanCommit(criticalSignal);
997
1427
  await measureStep('commit-idempotency-evidence', () => commitIdempotencyEvidence(resolvedPath, idempotencyKey, idempotencyClaim, spec, specPath));
1428
+ operationJournal.commitWithOutbox('create_spec', operationKey, committedResult, {
1429
+ topic: 'spec.created',
1430
+ payload: {
1431
+ schemaVersion: 1,
1432
+ specId: spec.id,
1433
+ projectId,
1434
+ projectPath: resolvedPath,
1435
+ postCommitTasks: SPEC_CREATED_POST_COMMIT_TASKS,
1436
+ },
1437
+ });
998
1438
  claimLifecycle.committed = true;
999
- // SPEC-709: technical.md no longer written content lives inside spec.md.
1000
- // SPEC-461: No progress.md, no HTML reports.
1439
+ // SPEC-709/SPEC-461: unified spec.md; no progress or HTML artifacts.
1001
1440
  }
1002
1441
  catch (writeErr) {
1003
- // Clean up partial files to avoid orphaned spec directories
1004
- await rm(specDir, { recursive: true, force: true });
1442
+ const storeRolledBack = storeCreated
1443
+ ? await specStore.deleteSpec(projectId, spec.id).catch(() => false)
1444
+ : true;
1445
+ await cleanupExecutionArtifact(getIdempotencyEvidencePath(resolvedPath, idempotencyKey), { force: true });
1446
+ await cleanupExecutionArtifact(specDir, { recursive: true, force: true });
1447
+ try {
1448
+ operationJournal.resolveRecovery('create_spec', operationKey, {
1449
+ action: storeRolledBack ? 'rollback' : 'quarantine',
1450
+ reason: storeRolledBack
1451
+ ? 'create_spec persistence failed and compensating cleanup completed'
1452
+ : 'create_spec persistence failed and the spec store rollback was incomplete',
1453
+ });
1454
+ }
1455
+ catch {
1456
+ // The original persistence failure remains the primary error.
1457
+ }
1005
1458
  throw writeErr;
1006
1459
  }
1007
- // Persist only explicit out-of-scope input.
1008
- if (outOfScope.length > 0) {
1009
- spec.outOfScope = outOfScope;
1010
- }
1011
- // Persist spec in storage
1012
- // SPEC-713: measure specStore.createSpec — file lock + JSON rewrite
1013
- await measureStep('specStore-createSpec', () => specStore.createSpec(projectId, spec));
1014
1460
  return {
1015
1461
  ok: true,
1016
1462
  data: {
1017
- spec,
1018
- specDir,
1019
- specPath,
1020
- technicalPath,
1021
- estimation,
1022
- splitSuggestion,
1023
- duplicate,
1024
- projectId,
1025
- agentTeamPlan,
1026
- knowledge,
1027
- clarificationSession,
1028
- constitutionCheck,
1029
- autopilot,
1030
- filteredCriteria,
1031
- advisoryCriteria: [
1032
- ...advisoryCriteria,
1033
- ...groundedTechnical.advisoryFiles.map((file) => file.path),
1034
- ],
1035
- actionableMetrics,
1463
+ committedResult,
1036
1464
  },
1037
1465
  };
1038
1466
  }); // end withTotalBudget critical path
@@ -1041,7 +1469,7 @@ export async function handleCreateSpec(inputParams, server) {
1041
1469
  claimLifecycle.retainForInFlightWork = criticalResult.warning.includes('exceeded');
1042
1470
  // SPEC-770: Wait briefly for any in-flight spec write to complete, then scan for duplicates
1043
1471
  await new Promise((resolve) => setTimeout(resolve, 500));
1044
- const recentSpecs = await findRecentlyWrittenSpecs(resolvedPath, 2 * 60 * 1000);
1472
+ const recentSpecs = await findRecentlyWrittenSpecs(resolvedPath, 2 * 60 * 1000, resolvedInputParams.title);
1045
1473
  const possibleDuplicates = recentSpecs.length > 0 ? recentSpecs : undefined;
1046
1474
  const dupNote = possibleDuplicates
1047
1475
  ? `\n\nA spec may have been created server-side: ${possibleDuplicates.map((s) => s.id).join(', ')}. Retry with the same title to return it instead of creating a duplicate.`
@@ -1050,401 +1478,42 @@ export async function handleCreateSpec(inputParams, server) {
1050
1478
  content: [
1051
1479
  {
1052
1480
  type: 'text',
1053
- text: `❌ create_spec exceeded 25s internal budget.\n\n${criticalResult.warning}${dupNote}`,
1481
+ text: `❌ create_spec exceeded its ${String(criticalResult.ceilingMs)}ms internal budget.\n\n${criticalResult.warning}${dupNote}`,
1054
1482
  },
1055
1483
  ],
1056
1484
  isError: true,
1057
1485
  ...(possibleDuplicates ? { structuredContent: { possibleDuplicates } } : {}),
1058
1486
  };
1059
1487
  }
1060
- if (!criticalResult.value.ok) {
1061
- return criticalResult.value.earlyReturn;
1062
- }
1063
- // Destructure critical path results for use in post-creation enrichment
1064
- const { spec, specDir: _specDir, specPath,
1065
- // SPEC-1010 Bug A: no longer surfaced in the response payload (SSR back-migration).
1066
- technicalPath: _technicalPath, estimation, splitSuggestion, duplicate, projectId, agentTeamPlan, knowledge, clarificationSession, constitutionCheck, autopilot, filteredCriteria, advisoryCriteria, actionableMetrics, } = criticalResult.value.data;
1067
- // -----------------------------------------------------------------------
1068
- // Post-creation enrichment (outside 25s ceiling — best-effort, budgeted)
1069
- // -----------------------------------------------------------------------
1070
- // Auto-setup git branch (non-blocking)
1071
- // SPEC-713: withBudget 3s — git subprocess can be slow on large repos
1072
- const gitBudgetResult = await withBudget('setupGitBranch', 3_000, () => measureStep('setupGitBranch', () => setupGitBranch(projectId, spec.id)));
1073
- const gitSetupResult = unwrapBudget(gitBudgetResult, undefined);
1074
- /* v8 ignore start -- requires real git repo */
1075
- if (gitSetupResult) {
1076
- spec.gitBranch = gitSetupResult.branch;
1077
- }
1078
- /* v8 ignore stop */
1079
- // Brief contradiction check (non-blocking)
1080
- // SPEC-713: withBudget 2s — scans all specs for contradiction keywords
1081
- const contradictionResult = await withBudget('checkContradictions', 2_000, () => measureStep('checkContradictions', () => checkContradictions(projectId, spec.id, description)));
1082
- const contradictionHint = unwrapBudget(contradictionResult, undefined);
1083
- // Build result (SPEC-461: lean — no progress, HTML, diagrams, scope filters)
1084
- const result = {
1085
- // SPEC-781: async analysis running in background (external project data)
1086
- pendingAnalysis: true,
1087
- specId: spec.id,
1088
- title: spec.title,
1089
- slug: spec.slug,
1090
- type: spec.type,
1091
- scope: spec.scope,
1092
- difficulty: spec.difficulty,
1093
- risk: spec.risk,
1094
- tags: spec.tags,
1095
- target: spec.target,
1096
- status: spec.status,
1097
- gitBranch: spec.gitBranch,
1098
- specPath,
1099
- // SPEC-1010 Bug A — `technicalPath` no longer leaked in the response.
1100
- // SSR back-migration (SPEC-752) folded technical.md into spec.md as
1101
- // ## Technical; the file is never written and the field would point to
1102
- // a non-existent path. Internal Spec record still carries it for
1103
- // backwards-compat with stored data — that field is removed in PR-C.
1104
- duplicateWarning: duplicate
1105
- ? ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug })
1106
- : undefined,
1107
- ...(constitutionCheck.warnings.length > 0
1108
- ? { constitutionWarnings: constitutionCheck.warnings }
1109
- : {}),
1110
- ...(clarificationSession
1111
- ? {
1112
- linkedClarificationSession: clarificationSession.id,
1113
- clarificationTopic: clarificationSession.topic,
1114
- clarificationAnswersUsed: Object.keys(clarificationSession.answers).length,
1115
- }
1116
- : {
1117
- clarificationNote: 'Spec created directly from the supplied description; no clarification session was linked.',
1118
- }),
1119
- message: ti('tools.create_spec.success', { id: spec.id, title: spec.title }),
1120
- ...(contradictionHint ? { contradictionHint } : {}),
1121
- /* v8 ignore next -- requires real git repo */
1122
- ...(gitSetupResult ? { gitAutoSetup: gitSetupResult.data } : {}),
1123
- };
1124
- const advisorySignals = [];
1125
- if (actionableMetrics.length > 0) {
1126
- result.actionableMetrics = actionableMetrics;
1127
- }
1128
- if (advisoryCriteria.length > 0) {
1129
- advisorySignals.push(makeAdvisorySignal({
1130
- key: 'ungrounded-contract-items',
1131
- kind: 'quality',
1132
- message: `${String(advisoryCriteria.length)} ungrounded contract item(s) kept advisory-only.`,
1133
- source: 'validator',
1134
- evidence: advisoryCriteria.slice(0, 10),
1135
- confidence: 0.8,
1136
- surface: 'structuredContent',
1137
- value: advisoryCriteria,
1138
- }));
1139
- }
1140
- const splitResult = buildSplitResult(splitSuggestion, knowledge?.experienceLevel);
1141
- if (splitResult) {
1142
- advisorySignals.push(makeAdvisorySignal({
1143
- key: 'split-suggestion',
1144
- kind: 'complexity',
1145
- message: 'Spec may benefit from splitting.',
1146
- source: 'heuristic',
1147
- evidence: ['spec-splitter heuristic'],
1148
- confidence: 0.5,
1149
- surface: 'structuredContent',
1150
- value: splitResult,
1151
- }));
1152
- }
1153
- // Dispatch hook event (fire-and-forget)
1154
- fireSpecCreatedHook(projectId, spec, params.projectPath ?? '');
1155
- // SPEC-781: Fire-and-forget async analysis (external project data)
1156
- runAutopilotAsync(spec.id, params.projectPath ?? '', description);
1157
- // SPEC-713: Track warnings from budgeted post-creation steps
1158
- const budgetWarnings = [];
1159
- // SPEC-461: No per-spec HTML reports — lean format
1160
- // SPEC-713: Run post-creation checks in parallel to reduce total wall-clock time.
1161
- // Each has an individual budget; results are merged into `result` after settlement.
1162
- const [readinessResult, qualityResult, duplicatesResult, complexityResult, priorLinksResult, nextStepsResult,] = await Promise.all([
1163
- // Auto-readiness check (best-effort) — SPEC-713: 3s budget
1164
- withBudget('auto-readiness', 3_000, () => measureStep('auto-readiness', () => checkSpecReadiness(spec, 'lenient'))),
1165
- // SPEC-492: Spec quality score (0-100) — SPEC-713: 2s budget
1166
- withBudget('quality-score', 2_000, () => measureStep('quality-score', () => runQualityScore(spec).then((r) => r ?? null))),
1167
- // SPEC-514: Semantic duplicate detection — SPEC-713: 2s budget
1168
- withBudget('duplicate-detection', 2_000, () => measureStep('duplicate-detection', async () => {
1169
- const allSpecs = await specStore.listSpecs(projectId);
1170
- const recentSpecs = allSpecs.slice(-200).filter((s) => s.id !== spec.id);
1171
- return findSimilarSpecs(spec.title, recentSpecs, { threshold: 0.3, topN: 3 });
1172
- })),
1173
- // SPEC-614 AC4: complexity-category hint — SPEC-713: 2s budget
1174
- withBudget('complexity-advice', 2_000, () => measureStep('complexity-advice', () => runComplexityAdvice(projectId, spec.id, spec.tags, filteredCriteria.length))),
1175
- // SPEC-615: prior-decision links — SPEC-713: 2s budget
1176
- withBudget('prior-decisions', 2_000, () => measureStep('prior-decisions', () => runPriorDecisionsHint(projectId, description, spec.tags))),
1177
- // Post-creation suggestions — SPEC-713: 3s budget
1178
- withBudget('post-creation-suggestions', 3_000, () => measureStep('post-creation-suggestions', () => generatePostCreationSuggestions(params.projectPath ?? '', description, knowledge ?? undefined))),
1179
- ]);
1180
- // Merge parallel results into `result`
1181
- if (!readinessResult.exceeded) {
1182
- const readiness = readinessResult.value;
1183
- result.autoReadiness = { score: readiness.score, isReady: readiness.ready };
1184
- }
1185
- else {
1186
- budgetWarnings.push(readinessResult.warning);
1187
- }
1188
- const qualityValue = unwrapBudget(qualityResult, null, budgetWarnings);
1189
- if (qualityValue !== null) {
1190
- result.qualityScore = qualityValue;
1191
- }
1192
- // SPEC-485: Simplicity autopilot — detect over-engineering signals (best-effort, sync)
1193
- const simplicityResult = runSimplicityCheck([params.description, ...filteredCriteria].join('\n'), estimation.devHours);
1194
- if (simplicityResult) {
1195
- result.simplicityCheck = simplicityResult;
1196
- advisorySignals.push(makeAdvisorySignal({
1197
- key: 'simplicity-check',
1198
- kind: 'simplicity',
1199
- message: `Simplicity recommendation: ${simplicityResult.recommendation}.`,
1200
- source: 'heuristic',
1201
- evidence: simplicityResult.signals.map((signal) => signal.type),
1202
- confidence: 0.55,
1203
- surface: 'structuredContent',
1204
- value: simplicityResult,
1205
- deprecatedAlias: 'simplicityCheck',
1206
- }));
1207
- }
1208
- // SPEC-514: duplicate results
1209
- const possibleDuplicates = unwrapBudget(duplicatesResult, [], budgetWarnings);
1210
- if (possibleDuplicates.length > 0) {
1211
- result.possibleDuplicates = possibleDuplicates;
1212
- advisorySignals.push(makeAdvisorySignal({
1213
- key: 'possible-duplicates',
1214
- kind: 'duplicate',
1215
- message: `${String(possibleDuplicates.length)} possible duplicate spec(s) detected.`,
1216
- source: 'heuristic',
1217
- evidence: possibleDuplicates.map((dup) => dup.specId),
1218
- confidence: 0.5,
1219
- surface: 'structuredContent',
1220
- value: possibleDuplicates,
1221
- deprecatedAlias: 'possibleDuplicates',
1222
- }));
1223
- }
1224
- // SPEC-222 Trigger 2: Auto-challenge hint for high-risk specs
1225
- if (spec.risk === 'high' || spec.difficulty >= 4) {
1226
- result.riskWarning = HIGH_RISK_WARNING;
1227
- advisorySignals.push(makeAdvisorySignal({
1228
- key: 'risk-warning',
1229
- kind: 'challenge',
1230
- message: 'High-risk heuristic warning generated.',
1231
- source: 'heuristic',
1232
- evidence: [`risk:${spec.risk}`, `difficulty:${String(spec.difficulty)}`],
1233
- confidence: 0.6,
1234
- surface: 'structuredContent',
1235
- value: HIGH_RISK_WARNING,
1236
- deprecatedAlias: 'riskWarning',
1237
- }));
1238
- }
1239
- // SPEC-614 AC4: complexity advice
1240
- const cAdvice = unwrapBudget(complexityResult, null, budgetWarnings);
1241
- if (cAdvice) {
1242
- result.complexityAdvice = cAdvice;
1243
- advisorySignals.push(makeAdvisorySignal({
1244
- key: 'complexity-advice',
1245
- kind: 'complexity',
1246
- message: cAdvice.reasoning,
1247
- source: 'history',
1248
- evidence: [`similarSpecs:${String(cAdvice.similarSpecsCount)}`],
1249
- confidence: 0.6,
1250
- surface: 'structuredContent',
1251
- value: cAdvice,
1252
- deprecatedAlias: 'complexityAdvice',
1253
- }));
1254
- }
1255
- // SPEC-615: prior decisions
1256
- const priorLinks = unwrapBudget(priorLinksResult, [], budgetWarnings);
1257
- if (priorLinks.length > 0) {
1258
- result.priorDecisions = priorLinks.map((l) => l.decisionId);
1259
- spec.priorDecisions = result.priorDecisions;
1260
- advisorySignals.push(makeAdvisorySignal({
1261
- key: 'prior-decisions',
1262
- kind: 'prior-decision',
1263
- message: `${String(priorLinks.length)} prior decision link(s) found.`,
1264
- source: 'history',
1265
- evidence: priorLinks.map((link) => link.decisionId),
1266
- confidence: 0.65,
1267
- surface: 'structuredContent',
1268
- value: result.priorDecisions,
1269
- deprecatedAlias: 'priorDecisions',
1270
- }));
1271
- }
1272
- // Post-creation suggestions
1273
- const nextSteps = unwrapBudget(nextStepsResult, [], budgetWarnings);
1274
- if (nextSteps.length > 0) {
1275
- result.nextSteps = nextSteps;
1276
- }
1277
- // SPEC-713: Surface budget warnings in result when steps exceeded their budget
1278
- if (budgetWarnings.length > 0) {
1279
- result.budgetWarnings = budgetWarnings;
1280
- }
1281
- // SPEC-1017: No auto-regeneration of project-tree dashboard HTML.
1282
- if (params.projectPath) {
1283
- notifyStoreChange(params.projectPath, 'specs');
1284
- }
1285
- // SPEC-644: Refresh model mapping TTL in background (fire-and-forget)
1286
- void import('../engine/model-tier-resolver.js')
1287
- .then(({ triggerModelMappingRefresh }) => {
1288
- triggerModelMappingRefresh(knowledge?.projectPath ?? params.projectPath ?? '');
1289
- })
1290
- .catch(() => {
1291
- /* best-effort */
1292
- });
1293
- // Auto post-creation pipeline: challenge + readiness (SPEC-445).
1294
- // Keep it observable and bounded so users see the actual result instead of
1295
- // background work that may finish invisibly after the tool already returned.
1296
- const pipelineResult = await runAutoPostCreatePipeline(spec.id, projectId, knowledge?.projectPath ?? params.projectPath ?? '');
1297
- // Build markdown response
1298
- const lines = [
1299
- `**SPEC-${spec.id}** — ${spec.title}`,
1300
- '',
1301
- `| Field | Value |`,
1302
- `|-------|-------|`,
1303
- `| Type | ${spec.type} |`,
1304
- `| Scope | ${spec.scope} |`,
1305
- `| Difficulty | ${String(spec.difficulty)}/5 |`,
1306
- `| Risk | ${spec.risk} |`,
1307
- `| Target | ${spec.target} |`,
1308
- `| Status | ${spec.status} |`,
1309
- `| Tags | ${spec.tags.join(', ')} |`,
1310
- ];
1311
- if (spec.gitBranch) {
1312
- lines.push(`| Branch | \`${spec.gitBranch}\` |`);
1313
- }
1314
- if (duplicate) {
1315
- lines.push('', `⚠️ ${ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug })}`);
1316
- }
1317
- if (constitutionCheck.warnings.length > 0) {
1318
- lines.push('', '⚠️ **Constitution Warnings**');
1319
- constitutionCheck.warnings.forEach((w) => lines.push(`- ${w.description}`));
1320
- }
1321
- if (contradictionHint) {
1322
- lines.push('', `⚠️ ${contradictionHint}`);
1323
- }
1324
- if (possibleDuplicates.length > 0 ||
1325
- result.riskWarning ||
1326
- splitResult ||
1327
- result.qualityScore ||
1328
- simplicityResult) {
1329
- lines.push('', 'Advisory signals were computed and are available in structuredContent.advisorySignals.');
1330
- }
1331
- const allNextSteps = (result.nextSteps ?? []).map((step) => (typeof step === 'string' ? step : formatPostCreationSuggestion(step)));
1332
- const markdownText = allNextSteps.length > 0
1333
- ? addNextSteps(formatSuccess(ti('tools.create_spec.success', { id: spec.id, title: spec.title }), lines.join('\n')), allNextSteps)
1334
- : formatSuccess(ti('tools.create_spec.success', { id: spec.id, title: spec.title }), lines.join('\n'));
1335
- // SPEC-469: Build autopilot summary from analyzer results
1336
- const collector = new AutopilotSummaryCollector();
1337
- if (autopilot.detectedPatterns.length > 0) {
1338
- collector.pushOk('pattern-detection', `Detected patterns: ${autopilot.detectedPatterns.join(', ')}`);
1339
- advisorySignals.push(makeAdvisorySignal({
1340
- key: 'detected-patterns',
1341
- kind: 'domain',
1342
- message: `Detected patterns: ${autopilot.detectedPatterns.join(', ')}`,
1343
- source: 'heuristic',
1344
- evidence: autopilot.detectedPatterns,
1345
- confidence: 0.5,
1346
- surface: 'structuredContent',
1347
- value: autopilot.detectedPatterns,
1348
- }));
1349
- }
1350
- const totalSuggestedFiles = autopilot.suggestedFiles.create.length +
1351
- autopilot.suggestedFiles.modify.length +
1352
- autopilot.suggestedFiles.test.length;
1353
- if (totalSuggestedFiles > 0) {
1354
- collector.pushOk('file-analysis', `Suggested ${String(totalSuggestedFiles)} files (${String(autopilot.suggestedFiles.create.length)} create, ${String(autopilot.suggestedFiles.modify.length)} modify, ${String(autopilot.suggestedFiles.test.length)} test)`);
1355
- advisorySignals.push(makeAdvisorySignal({
1356
- key: 'suggested-files',
1357
- kind: 'file',
1358
- message: `${String(totalSuggestedFiles)} possible related file(s) detected.`,
1359
- source: 'heuristic',
1360
- evidence: [
1361
- ...autopilot.suggestedFiles.create.map((f) => f.path),
1362
- ...autopilot.suggestedFiles.modify.map((f) => f.path),
1363
- ...autopilot.suggestedFiles.test.map((f) => f.path),
1364
- ],
1365
- confidence: 0.45,
1366
- surface: 'structuredContent',
1367
- value: {
1368
- create: autopilot.suggestedFiles.create.map((f) => f.path),
1369
- modify: autopilot.suggestedFiles.modify.map((f) => f.path),
1370
- test: autopilot.suggestedFiles.test.map((f) => f.path),
1488
+ if (criticalResult.status === 'failed') {
1489
+ return {
1490
+ content: [{ type: 'text', text: criticalResult.warning }],
1491
+ isError: true,
1492
+ structuredContent: {
1493
+ error: 'OPERATION_FAILED',
1494
+ category: criticalResult.category,
1495
+ elapsedMs: criticalResult.elapsedMs,
1496
+ persisted: false,
1371
1497
  },
1372
- deprecatedAlias: 'autopilotSummary.suggestedFiles',
1373
- }));
1374
- }
1375
- if (filteredCriteria.length > 0) {
1376
- collector.pushOk('criteria-enrichment', `Evaluated ${String(filteredCriteria.length)} proposed criteria; only directly grounded items entered the contract`);
1377
- }
1378
- if (pipelineResult.challengeSummary) {
1379
- collector.pushOk('challenge', `Challenge analysis complete: ${pipelineResult.challengeSummary}`);
1380
- }
1381
- if (pipelineResult.readinessScore !== null) {
1382
- collector.pushOk('readiness', `Readiness score: ${String(pipelineResult.readinessScore)}/100`);
1383
- advisorySignals.push(makeAdvisorySignal({
1384
- key: 'readiness-score',
1385
- kind: 'readiness',
1386
- message: `Readiness score: ${String(pipelineResult.readinessScore)}/100.`,
1387
- source: 'validator',
1388
- evidence: ['auto post-creation pipeline'],
1389
- confidence: 0.6,
1390
- surface: 'structuredContent',
1391
- value: pipelineResult.readinessScore,
1392
- }));
1498
+ };
1393
1499
  }
1394
- const humanSummary = `Plan created for "${spec.title}". Review the grounded criteria before approval.`;
1395
- result.advisorySignals = advisorySignals;
1396
- result.compat = buildAdvisoryCompat();
1397
- const compactResult = compactObj(result);
1398
- // SPEC-722: Issue a planner token for this spec creation.
1399
- // Best-effort — token failure never blocks spec creation.
1400
- let plannerToken;
1401
- try {
1402
- const resolvedProjectPath = resolvedInputParams.projectPath ?? '';
1403
- if (resolvedProjectPath.length > 0) {
1404
- const sessionId = resolvedInputParams.sessionId ?? 'unknown-session';
1405
- const modelId = resolvedInputParams.modelId ?? 'unknown-model';
1406
- const host = resolvedInputParams.host ?? 'unknown-host';
1407
- plannerToken = await issuePlannerToken(resolvedProjectPath, spec.id, sessionId, modelId, host);
1408
- }
1500
+ if (!criticalResult.value.ok) {
1501
+ return criticalResult.value.earlyReturn;
1409
1502
  }
1410
- catch {
1411
- // best-effort token issuance failure must not block spec creation
1503
+ if (!claimLifecycle.committed) {
1504
+ throw new Error('create_spec critical path completed without a durable commit');
1412
1505
  }
1413
- // SPEC-1011 Bug E / fallback hardening: surface local file analysis in the response payload.
1414
- // Those paths are also written into ## Files only when used as technical evidence.
1415
- const suggestedFilesPayload = autopilot.suggestedFiles.create.length +
1416
- autopilot.suggestedFiles.modify.length +
1417
- autopilot.suggestedFiles.test.length >
1418
- 0
1419
- ? {
1420
- create: autopilot.suggestedFiles.create.map((f) => f.path),
1421
- modify: autopilot.suggestedFiles.modify.map((f) => f.path),
1422
- test: autopilot.suggestedFiles.test.map((f) => f.path),
1423
- }
1424
- : undefined;
1425
- const baseResult = toolResult(markdownText, {
1426
- ...compactResult,
1427
- advisorySignals,
1428
- compat: buildAdvisoryCompat(),
1429
- humanSummary,
1430
- ...(collector.hasEntries() ? { autopilotSummary: collector.getMessages() } : {}),
1431
- ...(suggestedFilesPayload !== undefined
1432
- ? { 'autopilotSummary.suggestedFiles': suggestedFilesPayload }
1433
- : {}),
1434
- ...(agentTeamPlan && agentTeamPlan.roles.length > 0 ? { agentTeamPlan } : {}),
1435
- ...(plannerToken !== undefined ? { plannerToken } : {}),
1436
- });
1437
- return {
1438
- ...baseResult,
1439
- content: [...baseResult.content, { type: 'text', text: humanSummary }],
1440
- };
1506
+ return finishRecoveredOperation(operationJournal, operationKey, criticalResult.value.data.committedResult);
1441
1507
  }
1442
1508
  catch (error) {
1443
1509
  const message = error instanceof Error ? error.message : String(error);
1444
- return {
1510
+ const errorResult = {
1445
1511
  content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
1446
1512
  isError: true,
1447
1513
  };
1514
+ return claimLifecycle.committed
1515
+ ? finishRecoveredOperation(operationJournal, operationKey, errorResult)
1516
+ : errorResult;
1448
1517
  }
1449
1518
  }); // end trackCost
1450
1519
  }
@@ -1452,6 +1521,7 @@ export async function handleCreateSpec(inputParams, server) {
1452
1521
  if (!claimLifecycle.committed && !claimLifecycle.retainForInFlightWork) {
1453
1522
  await releaseIdempotencyClaim(resolvedPath, idempotencyKey, idempotencyClaim.ownerId);
1454
1523
  }
1524
+ runtimeDatabase.close();
1455
1525
  }
1456
1526
  }
1457
1527
  //# sourceMappingURL=create-spec.js.map