@planu/cli 4.14.1 → 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 (424) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/LICENSE +55 -12
  3. package/README.md +10 -2
  4. package/dist/cli/commands/install.d.ts +1 -12
  5. package/dist/cli/commands/install.js +9 -65
  6. package/dist/cli/commands/job.js +4 -3
  7. package/dist/cli/commands/spec.js +2 -2
  8. package/dist/cli/commands/validate.js +2 -2
  9. package/dist/cli/router.js +1 -3
  10. package/dist/config/environment-schema.json +1 -3
  11. package/dist/config/official-sdd-tools.d.ts +4 -0
  12. package/dist/config/official-sdd-tools.js +35 -0
  13. package/dist/config/product-public.d.ts +139 -3
  14. package/dist/config/product-public.js +8 -1
  15. package/dist/config/product-public.json +7 -36
  16. package/dist/config/registries/hosts/codex.json +20 -363
  17. package/dist/config/release-policy.json +84 -2
  18. package/dist/config/runtime-policy.json +12 -1
  19. package/dist/config/skill-templates/planu-resume-work.md +2 -2
  20. package/dist/core/index.d.ts +1 -1
  21. package/dist/core/spec-validator.d.ts +4 -4
  22. package/dist/core/spec-validator.js +1 -1
  23. package/dist/engine/abortable-process-runner.d.ts +20 -2
  24. package/dist/engine/abortable-process-runner.js +243 -74
  25. package/dist/engine/ambiguity-scorer.js +4 -26
  26. package/dist/engine/auth/scope-mapper.js +1 -7
  27. package/dist/engine/autopilot/state-updater.js +4 -4
  28. package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
  29. package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
  30. package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
  31. package/dist/engine/ci-generator/planu-config.js +1 -7
  32. package/dist/engine/clarification-gate/gate.d.ts +3 -3
  33. package/dist/engine/code-scanner/layer-scanner.js +6 -22
  34. package/dist/engine/core-bridge.d.ts +3 -3
  35. package/dist/engine/cost-guardrails.d.ts +1 -1
  36. package/dist/engine/cost-guardrails.js +1 -1
  37. package/dist/engine/criterion-identity.d.ts +1 -0
  38. package/dist/engine/criterion-identity.js +7 -0
  39. package/dist/engine/dashboard/data-loader.js +2 -7
  40. package/dist/engine/dashboard/templates-layout.js +0 -3
  41. package/dist/engine/dashboard/templates-project.js +1 -13
  42. package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
  43. package/dist/engine/docs-site-generator/data-collector.js +7 -28
  44. package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
  45. package/dist/engine/evidence-gates/artifact-reader.d.ts +10 -1
  46. package/dist/engine/evidence-gates/artifact-reader.js +115 -2
  47. package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
  48. package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
  49. package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
  50. package/dist/engine/evidence-index/done-drift.d.ts +1 -1
  51. package/dist/engine/evidence-index/done-drift.js +40 -18
  52. package/dist/engine/execution/context.js +3 -0
  53. package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
  54. package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
  55. package/dist/engine/execution/job-runtime.d.ts +30 -0
  56. package/dist/engine/execution/job-runtime.js +114 -6
  57. package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
  58. package/dist/engine/execution/public-job-sanitizer.js +31 -0
  59. package/dist/engine/execution/validate-job-executor.d.ts +12 -1
  60. package/dist/engine/execution/validate-job-executor.js +549 -86
  61. package/dist/engine/execution/validate-job-payload.js +107 -50
  62. package/dist/engine/execution/validate-job-recovery.js +1 -1
  63. package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
  64. package/dist/engine/execution/validate-worker-runtime.js +90 -2
  65. package/dist/engine/execution/validate-worker-thread.js +17 -24
  66. package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
  67. package/dist/engine/handoff-artifacts/schemas.js +33 -0
  68. package/dist/engine/handoff-packager.d.ts +19 -0
  69. package/dist/engine/handoff-packager.js +415 -13
  70. package/dist/engine/hooks/effort-configurator.js +1 -1
  71. package/dist/engine/impact-detector/test-break-predictor.js +3 -3
  72. package/dist/engine/impact-detector/tool-registration.js +1 -1
  73. package/dist/engine/implementation-contract/renderer.js +23 -8
  74. package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
  75. package/dist/engine/lifecycle-reconciliation-io.js +180 -0
  76. package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
  77. package/dist/engine/lifecycle-reconciliation.js +406 -0
  78. package/dist/engine/local-first/tool-classification.js +2 -0
  79. package/dist/engine/macos-keychain-native-port.d.ts +5 -0
  80. package/dist/engine/macos-keychain-native-port.js +19 -7
  81. package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
  82. package/dist/engine/migrations/remove-commercial-state.js +343 -0
  83. package/dist/engine/network-policy.js +1 -0
  84. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  85. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +10 -10
  86. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  87. package/dist/engine/planu-core.darwin-x64.node.sbom.json +10 -10
  88. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  89. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +10 -10
  90. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  91. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +10 -10
  92. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  93. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +10 -10
  94. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  95. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +10 -10
  96. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  97. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +10 -10
  98. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  99. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +10 -10
  100. package/dist/engine/readiness-checker.d.ts +1 -16
  101. package/dist/engine/readiness-checker.js +27 -88
  102. package/dist/engine/runtime-policy.d.ts +2 -0
  103. package/dist/engine/runtime-policy.js +42 -5
  104. package/dist/engine/runtime-security/rate-limiter.js +0 -1
  105. package/dist/engine/safety/contained-project-file.d.ts +3 -0
  106. package/dist/engine/safety/contained-project-file.js +23 -0
  107. package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
  108. package/dist/engine/scope-boundaries/scope-validator.js +45 -7
  109. package/dist/engine/session/checkpoint-writer.js +0 -9
  110. package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
  111. package/dist/engine/spec-format/acceptance-criteria.js +77 -18
  112. package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
  113. package/dist/engine/spec-format/markdown-sections.js +105 -0
  114. package/dist/engine/spec-format/read-technical-section.js +33 -5
  115. package/dist/engine/spec-format/technical-md-populator.js +3 -2
  116. package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
  117. package/dist/engine/spec-format/unified-spec-builder.js +202 -0
  118. package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
  119. package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
  120. package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
  121. package/dist/engine/spec-migrator/filesystem-import.js +121 -14
  122. package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
  123. package/dist/engine/spec-migrator/index.d.ts +1 -0
  124. package/dist/engine/spec-migrator/index.js +1 -0
  125. package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
  126. package/dist/engine/spec-state-machine/transition-spec.js +10 -1
  127. package/dist/engine/technical-enricher/index.js +16 -8
  128. package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
  129. package/dist/engine/technical-enricher/render-enriched.js +42 -14
  130. package/dist/engine/telemetry/health-checker.js +53 -17
  131. package/dist/engine/telemetry/telemetry-client.js +26 -8
  132. package/dist/engine/text-signal-boundaries.d.ts +2 -0
  133. package/dist/engine/text-signal-boundaries.js +25 -2
  134. package/dist/engine/time/relative-date.d.ts +3 -0
  135. package/dist/engine/time/relative-date.js +16 -0
  136. package/dist/engine/timing/budget.d.ts +11 -0
  137. package/dist/engine/timing/budget.js +27 -9
  138. package/dist/engine/token-optimizer/analytics.d.ts +1 -1
  139. package/dist/engine/token-optimizer/analytics.js +5 -6
  140. package/dist/engine/usage-tracker/core.d.ts +2 -2
  141. package/dist/engine/usage-tracker/core.js +1 -2
  142. package/dist/engine/usage-tracker/index.d.ts +0 -3
  143. package/dist/engine/usage-tracker/index.js +0 -3
  144. package/dist/engine/usage-tracker/stats.d.ts +2 -2
  145. package/dist/engine/usage-tracker/stats.js +1 -28
  146. package/dist/engine/validation/durable-validation.d.ts +1 -1
  147. package/dist/engine/validation/durable-validation.js +3 -82
  148. package/dist/engine/validation/validation-freshness.d.ts +11 -1
  149. package/dist/engine/validation/validation-freshness.js +231 -91
  150. package/dist/engine/validation/validation-receipt.d.ts +9 -6
  151. package/dist/engine/validation/validation-receipt.js +84 -24
  152. package/dist/engine/validation/validation-source-digest.d.ts +2 -0
  153. package/dist/engine/validation/validation-source-digest.js +86 -0
  154. package/dist/engine/validation/validation-submission.d.ts +5 -4
  155. package/dist/engine/validation/validation-submission.js +25 -11
  156. package/dist/engine/validation/validation-worktree.d.ts +21 -0
  157. package/dist/engine/validation/validation-worktree.js +260 -0
  158. package/dist/engine/validation-impact-planner.d.ts +14 -2
  159. package/dist/engine/validation-impact-planner.js +0 -3
  160. package/dist/engine/validator/dor-dod.d.ts +1 -0
  161. package/dist/engine/validator/dor-dod.js +5 -3
  162. package/dist/engine/validator/extractors.d.ts +1 -5
  163. package/dist/engine/validator/extractors.js +24 -69
  164. package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
  165. package/dist/engine/validator/spec-compliance-runner.js +429 -211
  166. package/dist/engine/validator.js +35 -22
  167. package/dist/index.js +4 -16
  168. package/dist/resources/usage-stats-resource.js +2 -18
  169. package/dist/security/adapters/linux-secret-service.js +7 -3
  170. package/dist/security/adapters/windows-credential-manager.js +6 -4
  171. package/dist/server/routes/specs.js +10 -0
  172. package/dist/storage/base-store.d.ts +1 -1
  173. package/dist/storage/base-store.js +1 -1
  174. package/dist/storage/feedback-remote.d.ts +2 -3
  175. package/dist/storage/feedback-remote.js +45 -31
  176. package/dist/storage/global-store.d.ts +1 -17
  177. package/dist/storage/global-store.js +1 -42
  178. package/dist/storage/index.d.ts +0 -2
  179. package/dist/storage/index.js +0 -2
  180. package/dist/storage/migrations/canonical-storage.js +124 -11
  181. package/dist/storage/runtime-db.d.ts +9 -0
  182. package/dist/storage/runtime-db.js +64 -8
  183. package/dist/storage/spec-store.d.ts +14 -2
  184. package/dist/storage/spec-store.js +104 -14
  185. package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
  186. package/dist/storage/status-store/atomic-status-write.js +57 -0
  187. package/dist/storage/status-store/file-lock.js +30 -11
  188. package/dist/storage/status-store/self-healing.js +3 -3
  189. package/dist/storage/status-store/version-sync.d.ts +2 -1
  190. package/dist/storage/status-store/version-sync.js +35 -40
  191. package/dist/storage/usage-store.d.ts +1 -5
  192. package/dist/storage/usage-store.js +0 -35
  193. package/dist/tools/auto-update-handler.d.ts +3 -3
  194. package/dist/tools/auto-update-handler.js +3 -3
  195. package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
  196. package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
  197. package/dist/tools/challenge-spec/scenario-collector.js +1 -1
  198. package/dist/tools/challenge-spec/scenarios-data.js +1 -1
  199. package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
  200. package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
  201. package/dist/tools/challenge-spec.js +5 -5
  202. package/dist/tools/check-readiness.js +1 -1
  203. package/dist/tools/configure-telemetry.js +30 -2
  204. package/dist/tools/create-spec/spec-builder.js +0 -11
  205. package/dist/tools/create-spec.d.ts +1 -0
  206. package/dist/tools/create-spec.js +444 -239
  207. package/dist/tools/git/branch-ops.d.ts +1 -1
  208. package/dist/tools/git/branch-ops.js +3 -2
  209. package/dist/tools/init-project/handler.js +10 -12
  210. package/dist/tools/init-project/host-assets-writer.js +26 -1
  211. package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
  212. package/dist/tools/init-project/migration-runner.d.ts +2 -4
  213. package/dist/tools/init-project/migration-runner.js +3 -2
  214. package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
  215. package/dist/tools/init-project/portable-index-reconciler.js +14 -0
  216. package/dist/tools/init-project/result-builder.js +1 -3
  217. package/dist/tools/jobs/handlers.js +38 -6
  218. package/dist/tools/list-specs.js +5 -26
  219. package/dist/tools/merge-risk-handler.js +2 -2
  220. package/dist/tools/package-handoff.js +2 -0
  221. package/dist/tools/reconcile-spec.js +0 -27
  222. package/dist/tools/register-agent-squad-tools.js +2 -2
  223. package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
  224. package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
  225. package/dist/tools/register-sdd-tools.d.ts +1 -1
  226. package/dist/tools/register-sdd-tools.js +2 -29
  227. package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
  228. package/dist/tools/register-spec-tools/core-spec-tools.js +17 -8
  229. package/dist/tools/safe-handler.d.ts +5 -13
  230. package/dist/tools/safe-handler.js +7 -23
  231. package/dist/tools/schemas/index.d.ts +1 -1
  232. package/dist/tools/schemas/index.js +1 -1
  233. package/dist/tools/schemas/output-schemas.d.ts +0 -23
  234. package/dist/tools/schemas/output-schemas.js +0 -28
  235. package/dist/tools/schemas/spec.d.ts +4 -0
  236. package/dist/tools/schemas/spec.js +3 -0
  237. package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
  238. package/dist/tools/schemas/validate-output-schema.js +7 -0
  239. package/dist/tools/spec-prompt-handler.js +3 -3
  240. package/dist/tools/sync-spec-state-handler.d.ts +1 -1
  241. package/dist/tools/sync-spec-state-handler.js +1 -1
  242. package/dist/tools/tool-entry.d.ts +4 -4
  243. package/dist/tools/tool-entry.js +3 -3
  244. package/dist/tools/tool-registry/core-tools.js +31 -15
  245. package/dist/tools/tool-registry/group-infra.js +27 -110
  246. package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
  247. package/dist/tools/tool-registry/inventory.d.ts +2 -0
  248. package/dist/tools/tool-registry/inventory.js +2 -0
  249. package/dist/tools/tool-registry-helpers.d.ts +3 -3
  250. package/dist/tools/tool-registry-helpers.js +3 -3
  251. package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
  252. package/dist/tools/tool-usage-report/registered-tools.js +3 -15
  253. package/dist/tools/update-status/approval-gates.js +3 -0
  254. package/dist/tools/update-status/done-receipt-verifier.js +40 -27
  255. package/dist/tools/update-status/evidence-gate.js +36 -1
  256. package/dist/tools/update-status/file-sync.d.ts +2 -1
  257. package/dist/tools/update-status/file-sync.js +57 -6
  258. package/dist/tools/update-status/index.d.ts +2 -0
  259. package/dist/tools/update-status/index.js +95 -58
  260. package/dist/tools/update-status/response-builder.js +3 -3
  261. package/dist/tools/update-status/side-effects.d.ts +2 -0
  262. package/dist/tools/update-status/side-effects.js +2 -2
  263. package/dist/tools/update-status/transition-guard.js +32 -15
  264. package/dist/tools/update-status-actions.d.ts +4 -2
  265. package/dist/tools/update-status-actions.js +16 -14
  266. package/dist/tools/usage-report.js +1 -16
  267. package/dist/tools/usage-stats.js +1 -18
  268. package/dist/tools/usage-tracking.d.ts +4 -0
  269. package/dist/tools/usage-tracking.js +20 -0
  270. package/dist/tools/validate-lint.d.ts +1 -0
  271. package/dist/tools/validate-lint.js +1 -1
  272. package/dist/tools/validate.d.ts +2 -1
  273. package/dist/tools/validate.js +114 -34
  274. package/dist/transports/http-transport.js +6 -8
  275. package/dist/transports/mcp-types.d.ts +0 -2
  276. package/dist/transports/session-context.js +1 -1
  277. package/dist/types/cascade-hooks.d.ts +1 -1
  278. package/dist/types/ci.d.ts +0 -4
  279. package/dist/types/clarification-token.d.ts +1 -1
  280. package/dist/types/claude-code-runtime.d.ts +0 -27
  281. package/dist/types/commercial-migration.d.ts +17 -0
  282. package/dist/types/commercial-migration.js +2 -0
  283. package/dist/types/common/primitives.d.ts +0 -2
  284. package/dist/types/core-bridge.d.ts +4 -10
  285. package/dist/types/dashboard.d.ts +3 -7
  286. package/dist/types/docs.d.ts +0 -1
  287. package/dist/types/durable-job.d.ts +4 -4
  288. package/dist/types/durable-validation.d.ts +100 -2
  289. package/dist/types/durable-validation.js +165 -1
  290. package/dist/types/errors/structured-error.d.ts +3 -3
  291. package/dist/types/estimation.d.ts +2 -0
  292. package/dist/types/evidence-gates.d.ts +9 -1
  293. package/dist/types/execution.d.ts +2 -0
  294. package/dist/types/gemini-integration.d.ts +0 -11
  295. package/dist/types/handoff-artifacts.d.ts +41 -0
  296. package/dist/types/hooks-advanced.d.ts +0 -13
  297. package/dist/types/impact-detection.d.ts +2 -2
  298. package/dist/types/index.d.ts +2 -6
  299. package/dist/types/index.js +1 -5
  300. package/dist/types/kiro-integration.d.ts +0 -6
  301. package/dist/types/network-policy.d.ts +1 -1
  302. package/dist/types/project/lint-scaffolder.d.ts +1 -1
  303. package/dist/types/runtime-policy.d.ts +11 -0
  304. package/dist/types/session-safeguard.d.ts +0 -31
  305. package/dist/types/spec/canonical-repair.d.ts +81 -0
  306. package/dist/types/spec/canonical-repair.js +2 -0
  307. package/dist/types/spec/core.d.ts +14 -0
  308. package/dist/types/spec/index.d.ts +1 -0
  309. package/dist/types/spec/index.js +1 -0
  310. package/dist/types/spec/inputs.d.ts +29 -0
  311. package/dist/types/spec-format.d.ts +24 -0
  312. package/dist/types/telemetry.d.ts +4 -1
  313. package/dist/types/transport.d.ts +0 -2
  314. package/dist/types/usage.d.ts +1 -36
  315. package/dist/types/usage.js +1 -1
  316. package/dist/types/validate-worker.d.ts +6 -0
  317. package/dist/types/validation-receipt.d.ts +1 -1
  318. package/dist/types/validation.d.ts +2 -2
  319. package/package.json +16 -12
  320. package/planu-native.json +1 -1
  321. package/planu-plugin.json +23 -40
  322. package/scripts/lib/pending-release-file.mjs +93 -0
  323. package/src/i18n/messages/en.json +0 -41
  324. package/src/i18n/messages/es.json +0 -41
  325. package/src/i18n/messages/pt.json +0 -41
  326. package/dist/cli/commands/activate.d.ts +0 -14
  327. package/dist/cli/commands/activate.js +0 -174
  328. package/dist/config/license-plans.json +0 -251
  329. package/dist/config/product-public.ts +0 -148
  330. package/dist/config/server-instructions.ts +0 -121
  331. package/dist/config/version-defaults.ts +0 -76
  332. package/dist/config/version.ts +0 -21
  333. package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
  334. package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
  335. package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
  336. package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
  337. package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
  338. package/dist/engine/hooks/full-spectrum-generator.js +0 -117
  339. package/dist/engine/license-validator/config-loader.d.ts +0 -5
  340. package/dist/engine/license-validator/config-loader.js +0 -16
  341. package/dist/engine/license-validator/core.d.ts +0 -15
  342. package/dist/engine/license-validator/core.js +0 -151
  343. package/dist/engine/license-validator/fingerprint.d.ts +0 -3
  344. package/dist/engine/license-validator/fingerprint.js +0 -17
  345. package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
  346. package/dist/engine/license-validator/lemon-squeezy.js +0 -55
  347. package/dist/engine/license-validator.d.ts +0 -5
  348. package/dist/engine/license-validator.js +0 -6
  349. package/dist/engine/product-intelligence/index.d.ts +0 -7
  350. package/dist/engine/product-intelligence/index.js +0 -211
  351. package/dist/engine/session/auto-save.d.ts +0 -18
  352. package/dist/engine/session/auto-save.js +0 -98
  353. package/dist/engine/session/context-snapshot.d.ts +0 -8
  354. package/dist/engine/session/context-snapshot.js +0 -49
  355. package/dist/engine/session/memory-sync.d.ts +0 -12
  356. package/dist/engine/session/memory-sync.js +0 -34
  357. package/dist/engine/session/session-diff.d.ts +0 -11
  358. package/dist/engine/session/session-diff.js +0 -42
  359. package/dist/engine/session/session-merge.d.ts +0 -12
  360. package/dist/engine/session/session-merge.js +0 -55
  361. package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
  362. package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
  363. package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
  364. package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
  365. package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
  366. package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
  367. package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
  368. package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
  369. package/dist/engine/spec-changelog/core.d.ts +0 -16
  370. package/dist/engine/spec-changelog/core.js +0 -176
  371. package/dist/engine/spec-changelog/diff.d.ts +0 -18
  372. package/dist/engine/spec-changelog/diff.js +0 -121
  373. package/dist/engine/spec-changelog/index.d.ts +0 -3
  374. package/dist/engine/spec-changelog/index.js +0 -4
  375. package/dist/engine/trial-engine.d.ts +0 -5
  376. package/dist/engine/trial-engine.js +0 -14
  377. package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
  378. package/dist/engine/usage-tracker/rate-check.js +0 -18
  379. package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
  380. package/dist/engine/usage-tracker/rate-limiter.js +0 -73
  381. package/dist/engine/usage-tracker/trial.d.ts +0 -27
  382. package/dist/engine/usage-tracker/trial.js +0 -49
  383. package/dist/engine/webhook/event-handlers.d.ts +0 -25
  384. package/dist/engine/webhook/event-handlers.js +0 -89
  385. package/dist/engine/webhook/index.d.ts +0 -4
  386. package/dist/engine/webhook/index.js +0 -5
  387. package/dist/engine/webhook/server.d.ts +0 -12
  388. package/dist/engine/webhook/server.js +0 -249
  389. package/dist/engine/webhook/signature.d.ts +0 -16
  390. package/dist/engine/webhook/signature.js +0 -49
  391. package/dist/storage/license-store.d.ts +0 -6
  392. package/dist/storage/license-store.js +0 -63
  393. package/dist/storage/session-store.d.ts +0 -20
  394. package/dist/storage/session-store.js +0 -136
  395. package/dist/tools/activate-license.d.ts +0 -6
  396. package/dist/tools/activate-license.js +0 -132
  397. package/dist/tools/checkpoint-handler.d.ts +0 -3
  398. package/dist/tools/checkpoint-handler.js +0 -130
  399. package/dist/tools/export-session.d.ts +0 -3
  400. package/dist/tools/export-session.js +0 -90
  401. package/dist/tools/license-gate.d.ts +0 -18
  402. package/dist/tools/license-gate.js +0 -222
  403. package/dist/tools/license-status.d.ts +0 -3
  404. package/dist/tools/license-status.js +0 -93
  405. package/dist/tools/list-sessions.d.ts +0 -3
  406. package/dist/tools/list-sessions.js +0 -46
  407. package/dist/tools/planu-session-checkpoint.d.ts +0 -6
  408. package/dist/tools/planu-session-checkpoint.js +0 -42
  409. package/dist/tools/restore-session.d.ts +0 -3
  410. package/dist/tools/restore-session.js +0 -71
  411. package/dist/tools/spec-history.d.ts +0 -3
  412. package/dist/tools/spec-history.js +0 -59
  413. package/dist/tools/webhook.d.ts +0 -6
  414. package/dist/tools/webhook.js +0 -150
  415. package/dist/types/changelog.d.ts +0 -49
  416. package/dist/types/changelog.js +0 -3
  417. package/dist/types/licensing.d.ts +0 -109
  418. package/dist/types/licensing.js +0 -4
  419. package/dist/types/product-intelligence.d.ts +0 -60
  420. package/dist/types/product-intelligence.js +0 -3
  421. package/dist/types/session.d.ts +0 -84
  422. package/dist/types/session.js +0 -3
  423. package/dist/types/webhook.d.ts +0 -62
  424. package/dist/types/webhook.js +0 -4
@@ -22,13 +22,12 @@ import { recordDoneMetrics, syncSpecFiles, tryReconcile, recordTerminalTransitio
22
22
  import { appendEntry, getLastHash } from '../../storage/audit-trail-store.js';
23
23
  import { appendTransitionEvent } from '../../storage/transition-log.js';
24
24
  import { createHash, randomUUID as uuid } from 'node:crypto';
25
- import { updateFrontmatterField } from '../../engine/frontmatter-parser.js';
26
25
  import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
27
26
  import { recordForceUsage } from '../../storage/force-analytics-store.js';
28
27
  import { maybeSafePushOnDone, queuePostCommitTasks, runCascadeForResponse, } from './side-effects.js';
29
28
  import { formatKeyValue } from '../output-formatter.js';
30
29
  import { checkCodeReality } from '../../engine/code-scanner/index.js';
31
- import { join } from 'node:path';
30
+ import { join, resolve } from 'node:path';
32
31
  import { trackCost } from '../../engine/cost-tracking/operation-tracker.js';
33
32
  import { resolveProjectIdOrAutoDetect } from '../resolve-project-id.js';
34
33
  import { withToolTimeout } from '../safe-handler.js';
@@ -39,7 +38,8 @@ import { acquireLock, releaseLock, isLocked as isCrossProcessLocked, LockBusyErr
39
38
  import { projectDataDir } from '../../storage/base-store.js';
40
39
  import { assertExecutionCanCommit } from '../../engine/execution/context.js';
41
40
  import { shouldSkipGateForExplicitLegacyHarness } from './gate-harness.js';
42
- import { assertValidationFreshnessBarrier, } from '../../engine/validation/validation-freshness.js';
41
+ import { assertValidationFreshnessBarrier, closeValidationFreshnessLease, } from '../../engine/validation/validation-freshness.js';
42
+ import { currentReconciliationInvocationContext, reconcileImplementingSpec, } from '../../engine/lifecycle-reconciliation.js';
43
43
  /**
44
44
  * SPEC-280: Silently traverse intermediate states so callers can jump forward
45
45
  * (e.g. draft → approved) without receiving an error.
@@ -199,6 +199,18 @@ function planBackgroundActions(args) {
199
199
  }
200
200
  return actions;
201
201
  }
202
+ /** Bind deferred lifecycle work to one normalized absolute root without persisting that path. */
203
+ export function computeTransitionRootBinding(projectPath) {
204
+ return createHash('sha256')
205
+ .update('planu:transition-root:v1\0')
206
+ .update(resolve(projectPath))
207
+ .digest('hex');
208
+ }
209
+ function receiptMatchesTransitionRoot(receipt, projectPath) {
210
+ return (projectPath !== undefined &&
211
+ receipt.transitionRootBinding !== undefined &&
212
+ receipt.transitionRootBinding === computeTransitionRootBinding(projectPath));
213
+ }
202
214
  function buildPostCommitTasks(args) {
203
215
  const enabled = new Set(args.actionNames);
204
216
  const tasks = [];
@@ -212,13 +224,13 @@ function buildPostCommitTasks(args) {
212
224
  if (enabled.has('implementing-side-effects')) {
213
225
  tasks.push({
214
226
  name: 'implementing-side-effects',
215
- run: () => runImplementingSideEffects(args.projectId, args.spec.id),
227
+ run: () => runImplementingSideEffects(args.projectId, args.spec.id, args.effectiveProjectPath),
216
228
  });
217
229
  }
218
230
  if (enabled.has('done-side-effects')) {
219
231
  tasks.push({
220
232
  name: 'done-side-effects',
221
- run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch),
233
+ run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch, args.effectiveProjectPath),
222
234
  });
223
235
  }
224
236
  if (enabled.has('reconcile')) {
@@ -231,7 +243,7 @@ function buildPostCommitTasks(args) {
231
243
  if (enabled.has('record-done-metrics') && actuals) {
232
244
  tasks.push({
233
245
  name: 'record-done-metrics',
234
- run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.knowledge?.projectPath),
246
+ run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.effectiveProjectPath),
235
247
  });
236
248
  }
237
249
  const effectiveProjectPath = args.effectiveProjectPath;
@@ -253,6 +265,7 @@ function buildPostCommitTasks(args) {
253
265
  projectId: args.projectId,
254
266
  spec: args.spec,
255
267
  currentStatus: args.previousStatus,
268
+ projectPath: args.effectiveProjectPath,
256
269
  }),
257
270
  });
258
271
  }
@@ -291,7 +304,10 @@ async function replayIdempotentPostCommitTasks(args) {
291
304
  return;
292
305
  }
293
306
  const knowledge = await knowledgeStore.getKnowledge(args.projectId);
294
- const effectiveProjectPath = knowledge?.projectPath ?? args.projectPath;
307
+ const effectiveProjectPath = args.projectPath ?? knowledge?.projectPath;
308
+ if (!receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
309
+ return;
310
+ }
295
311
  const specAtTransition = { ...args.spec, status: receipt.status };
296
312
  const tasks = buildPostCommitTasks({
297
313
  actionNames: receipt.pendingBackgroundActions,
@@ -317,13 +333,17 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
317
333
  knowledgeStore.getKnowledge(projectId),
318
334
  specStore.listSpecs(projectId),
319
335
  ]);
320
- const effectiveProjectPath = knowledge?.projectPath ?? projectPath;
336
+ const effectiveProjectPath = projectPath ?? knowledge?.projectPath;
321
337
  for (const spec of specs) {
322
- const hasPendingWork = (spec.statusHistory ?? []).some((receipt) => receipt.transitionId && receipt.pendingBackgroundActions?.length);
323
- if (!hasPendingWork) {
338
+ const recoverableReceipts = (spec.statusHistory ?? []).filter((receipt) => receipt.transitionId &&
339
+ receipt.pendingBackgroundActions?.length &&
340
+ receiptMatchesTransitionRoot(receipt, effectiveProjectPath));
341
+ if (recoverableReceipts.length === 0 || !effectiveProjectPath) {
324
342
  continue;
325
343
  }
326
- const recoveryLock = await acquireLock(effectiveProjectPath ?? projectPath ?? process.cwd(), spec.id, { reason: 'recover pending post-commit tasks' });
344
+ const recoveryLock = await acquireLock(effectiveProjectPath, spec.id, {
345
+ reason: 'recover pending post-commit tasks',
346
+ });
327
347
  if (recoveryLock === null) {
328
348
  continue;
329
349
  }
@@ -335,7 +355,9 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
335
355
  continue;
336
356
  }
337
357
  for (const receipt of currentSpec.statusHistory ?? []) {
338
- if (!receipt.transitionId || !receipt.pendingBackgroundActions?.length) {
358
+ if (!receipt.transitionId ||
359
+ !receipt.pendingBackgroundActions?.length ||
360
+ !receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
339
361
  continue;
340
362
  }
341
363
  const specAtTransition = { ...currentSpec, status: receipt.status };
@@ -383,6 +405,18 @@ export async function recoverPendingPostCommitTasksAtStartup() {
383
405
  });
384
406
  }
385
407
  }
408
+ async function runTransitionMaintenance(transitionProjectPath) {
409
+ if (transitionProjectPath) {
410
+ try {
411
+ const { runStrictPlanuCleanup } = await import('../../engine/spec-migrator/index.js');
412
+ await runStrictPlanuCleanup(transitionProjectPath);
413
+ }
414
+ catch (error) {
415
+ /* reliability-cleanup: PRE_TRANSITION_STRICT_CLEANUP — validate remains fail-closed */
416
+ reportClassifiedDegradation('PRE_TRANSITION_STRICT_CLEANUP', error);
417
+ }
418
+ }
419
+ }
386
420
  function buildPersistenceFailureResponse(args) {
387
421
  const committed = args.recoveryRequired ? null : false;
388
422
  const message = args.recoveryRequired
@@ -469,7 +503,7 @@ async function commitTransitionWithNarrowLock(args) {
469
503
  expectedSpec: freshSpec,
470
504
  });
471
505
  const updatedSpec = transitionRecord.spec;
472
- const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath);
506
+ const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath, args.qualityWarnings);
473
507
  if (!syncResult.warning) {
474
508
  return { ok: true, transitionRecord, updatedSpec };
475
509
  }
@@ -581,24 +615,29 @@ export async function handleUpdateStatus(params, server) {
581
615
  }
582
616
  const projectId = resolved.projectId;
583
617
  const effectiveProjectPath = resolved.projectPath;
584
- if (effectiveProjectPath) {
585
- try {
586
- const { runStrictPlanuCleanup } = await import('../../engine/spec-migrator/index.js');
587
- await runStrictPlanuCleanup(effectiveProjectPath);
588
- }
589
- catch (error) {
590
- /* reliability-cleanup: PRE_TRANSITION_STRICT_CLEANUP — validate remains fail-closed */
591
- reportClassifiedDegradation('PRE_TRANSITION_STRICT_CLEANUP', error);
592
- }
618
+ const knowledge = await knowledgeStore.getKnowledge(projectId);
619
+ const transitionProjectPath = (params.projectPath?.trim()
620
+ ? effectiveProjectPath
621
+ : (knowledge?.projectPath ?? effectiveProjectPath)) || undefined;
622
+ if (params.reconciliationRequestId ||
623
+ params.expectedImplementingTransitionId ||
624
+ params.implementationReviewDigest) {
625
+ return reconcileImplementingSpec({
626
+ input: params,
627
+ projectId,
628
+ projectPath: transitionProjectPath,
629
+ context: currentReconciliationInvocationContext(),
630
+ syncSpecFiles,
631
+ });
593
632
  }
594
- await recoverPendingPostCommitTasks(projectId, effectiveProjectPath).catch((error) => {
595
- /* reliability-optional: POST_COMMIT_RECOVERY_SCAN — durable tasks remain retryable */
596
- reportClassifiedDegradation('POST_COMMIT_RECOVERY_SCAN', error);
597
- });
633
+ if (newStatus !== 'done') {
634
+ await runTransitionMaintenance(transitionProjectPath);
635
+ }
636
+ let doneFreshnessLease;
598
637
  return (async () => {
599
638
  try {
600
639
  // SPEC-301: Reject status changes when spec is locked by another holder (legacy manual lock)
601
- const lockError = await checkSpecLockGate(effectiveProjectPath, specId);
640
+ const lockError = await checkSpecLockGate(transitionProjectPath, specId);
602
641
  if (lockError) {
603
642
  return lockError;
604
643
  }
@@ -608,7 +647,7 @@ export async function handleUpdateStatus(params, server) {
608
647
  if (isDryRun) {
609
648
  // SPEC-731: dry_run checks lock availability without acquiring it.
610
649
  // isLocked returns null when no lockfile exists (available), or a LockInfo when locked.
611
- const lockInfo = await isCrossProcessLocked(effectiveProjectPath, specId).catch((error) => {
650
+ const lockInfo = await isCrossProcessLocked(transitionProjectPath ?? projectId, specId).catch((error) => {
612
651
  // reliability-optional: DRY_RUN_LOCK_PROBE — null conservatively avoids stale lock claims
613
652
  reportClassifiedDegradation('DRY_RUN_LOCK_PROBE', error);
614
653
  return null;
@@ -647,7 +686,7 @@ export async function handleUpdateStatus(params, server) {
647
686
  await replayIdempotentPostCommitTasks({
648
687
  projectId,
649
688
  spec,
650
- projectPath: effectiveProjectPath,
689
+ projectPath: transitionProjectPath,
651
690
  });
652
691
  const idempotentResult = checkIdempotentOrTransition(projectId, spec, newStatus);
653
692
  if (idempotentResult) {
@@ -784,10 +823,8 @@ export async function handleUpdateStatus(params, server) {
784
823
  if (elicitResult) {
785
824
  return elicitResult;
786
825
  }
787
- // Load knowledge for project path (needed for DoD gates, validate trigger and HTML regen)
788
- const knowledge = await knowledgeStore.getKnowledge(projectId);
789
826
  // Resolve effective project path once — used across all gates below
790
- const effectiveGatePath = knowledge?.projectPath ?? effectiveProjectPath;
827
+ const effectiveGatePath = transitionProjectPath;
791
828
  // SPEC-1044: SDD model-routing + context continuity hard gate.
792
829
  const sddRoutingGate = {
793
830
  blockResult: null,
@@ -839,7 +876,6 @@ export async function handleUpdateStatus(params, server) {
839
876
  // security and repository scans belong to the durable validation worker.
840
877
  let codeRealityWarning = null;
841
878
  let forcedBypassWarning = null;
842
- let doneFreshnessLease;
843
879
  const [codeRealityResult, doneGatesResult] = await Promise.all([
844
880
  // Code reality: only relevant for 'implementing'
845
881
  newStatus === 'implementing' && !reverseTransition && effectiveGatePath
@@ -937,11 +973,15 @@ export async function handleUpdateStatus(params, server) {
937
973
  const resolvedActuals = actuals;
938
974
  // Run only fast, read-only transition checks before persistence.
939
975
  const implActions = newStatus === 'implementing'
940
- ? await runImplementingActions(projectId, specId, { deferSideEffects: true })
976
+ ? await runImplementingActions(projectId, specId, {
977
+ deferSideEffects: true,
978
+ projectPath: effectiveGatePath,
979
+ })
941
980
  : null;
942
981
  const doneActions = newStatus === 'done'
943
982
  ? await runDoneActions(projectId, specId, spec.gitBranch, {
944
983
  deferSideEffects: true,
984
+ projectPath: effectiveGatePath,
945
985
  })
946
986
  : null;
947
987
  const trigger = reverseTransition
@@ -1089,13 +1129,20 @@ export async function handleUpdateStatus(params, server) {
1089
1129
  hasProjectPath: Boolean(effectiveGatePath),
1090
1130
  shouldAutoStage,
1091
1131
  });
1132
+ const transitionRootBinding = plannedBackgroundActions.length > 0 && effectiveGatePath
1133
+ ? computeTransitionRootBinding(effectiveGatePath)
1134
+ : undefined;
1135
+ const qualityWarningsToWrite = newStatus === 'approved'
1136
+ ? [...readinessGate.qualityWarnings, ...formatGate.qualityWarnings]
1137
+ : [];
1092
1138
  const persistence = await commitTransitionWithNarrowLock({
1093
- projectPath: effectiveProjectPath || projectId,
1139
+ projectPath: effectiveGatePath ?? projectId,
1094
1140
  projectId,
1095
1141
  specId,
1096
1142
  newStatus,
1097
1143
  originalStatus,
1098
1144
  expectedSpec: spec,
1145
+ qualityWarnings: qualityWarningsToWrite,
1099
1146
  options: {
1100
1147
  trigger: trigger,
1101
1148
  actor,
@@ -1103,6 +1150,7 @@ export async function handleUpdateStatus(params, server) {
1103
1150
  reason: params.reason,
1104
1151
  updates: nonStatusUpdates,
1105
1152
  pendingBackgroundActions: plannedBackgroundActions,
1153
+ transitionRootBinding,
1106
1154
  expectedSpec: spec,
1107
1155
  },
1108
1156
  ...(doneFreshnessLease ? { freshnessLease: doneFreshnessLease } : {}),
@@ -1114,6 +1162,9 @@ export async function handleUpdateStatus(params, server) {
1114
1162
  const frontmatterSyncWarnings = [];
1115
1163
  const transitionId = transitionRecord.transitionId;
1116
1164
  const committedAt = transitionRecord.timestamp;
1165
+ if (newStatus === 'done') {
1166
+ await runTransitionMaintenance(transitionProjectPath);
1167
+ }
1117
1168
  await appendTransitionEvent({
1118
1169
  projectId,
1119
1170
  specId,
@@ -1145,29 +1196,6 @@ export async function handleUpdateStatus(params, server) {
1145
1196
  reportClassifiedDegradation('REOPEN_EVENT_APPEND', err);
1146
1197
  });
1147
1198
  }
1148
- // SPEC-769/SPEC-780: Write qualityWarnings to spec.md frontmatter when force-approved (best-effort)
1149
- const qualityWarningsToWrite = [
1150
- ...readinessGate.qualityWarnings,
1151
- ...formatGate.qualityWarnings,
1152
- ];
1153
- const specPathForWarnings = updatedSpec.specPath;
1154
- if (newStatus === 'approved' &&
1155
- qualityWarningsToWrite.length > 0 &&
1156
- specPathForWarnings) {
1157
- void (async () => {
1158
- try {
1159
- const { readFile: fsReadFile } = await import('node:fs/promises');
1160
- const content = await fsReadFile(specPathForWarnings, 'utf-8');
1161
- const warningsJson = JSON.stringify(qualityWarningsToWrite);
1162
- const updated = updateFrontmatterField(content, 'qualityWarnings', warningsJson);
1163
- await atomicWriteFile(specPathForWarnings, updated);
1164
- }
1165
- catch (error) {
1166
- // reliability-optional: QUALITY_WARNING_FRONTMATTER — canonical receipt retains warnings
1167
- reportClassifiedDegradation('QUALITY_WARNING_FRONTMATTER', error);
1168
- }
1169
- })();
1170
- }
1171
1199
  // SPEC-723: Record terminal transition in hash-chained log (best-effort, fire-and-forget)
1172
1200
  // SPEC-734: Thread enriched payload — sessionId, modelId, gateResults
1173
1201
  if (newStatus === 'done' || newStatus === 'discarded') {
@@ -1197,6 +1225,7 @@ export async function handleUpdateStatus(params, server) {
1197
1225
  modelId: params.modelId,
1198
1226
  gateResults: Object.keys(terminalGateResults).length > 0 ? terminalGateResults : undefined,
1199
1227
  meta: transitionEvidenceMeta,
1228
+ projectPath: effectiveGatePath,
1200
1229
  }).catch((error) => {
1201
1230
  /* reliability-optional: TERMINAL_VALIDATION_REPORT — canonical receipt is authoritative */
1202
1231
  reportClassifiedDegradation('TERMINAL_VALIDATION_REPORT', error);
@@ -1335,6 +1364,14 @@ export async function handleUpdateStatus(params, server) {
1335
1364
  isError: true,
1336
1365
  };
1337
1366
  }
1367
+ finally {
1368
+ if (doneFreshnessLease) {
1369
+ await closeValidationFreshnessLease(doneFreshnessLease).catch((error) => {
1370
+ /* reliability-cleanup: freshness expiry remains the recovery boundary */
1371
+ reportClassifiedDegradation('VALIDATION_FRESHNESS_CHECK_FAILED', error);
1372
+ });
1373
+ }
1374
+ }
1338
1375
  })();
1339
1376
  }); // end trackCost
1340
1377
  }
@@ -84,7 +84,7 @@ export function buildStatusResponse(result, specId, currentStatus, newStatus, re
84
84
  const toEmoji = STATUS_EMOJIS[newStatus];
85
85
  const r = result;
86
86
  const lines = [
87
- `**SPEC-${specId}** moved: ${fromEmoji} \`${currentStatus}\` → ${toEmoji} \`${newStatus}\``,
87
+ `**${specId}** moved: ${fromEmoji} \`${currentStatus}\` → ${toEmoji} \`${newStatus}\``,
88
88
  ];
89
89
  if (r.committed && r.transitionId) {
90
90
  lines.push(`\nTransition committed: \`${r.transitionId}\``);
@@ -125,7 +125,7 @@ export function buildStatusResponse(result, specId, currentStatus, newStatus, re
125
125
  lines.push(`\n✅ Validate score: ${String(r.validateScore)}/100${sourceSuffix}`);
126
126
  }
127
127
  if (r.cascade.affectedSpecs.length > 0) {
128
- lines.push(`\n🔗 Downstream specs affected: ${r.cascade.affectedSpecs.map((s) => `SPEC-${s}`).join(', ')}`);
128
+ lines.push(`\n🔗 Downstream specs affected: ${r.cascade.affectedSpecs.join(', ')}`);
129
129
  }
130
130
  if (r.codeRealityWarning) {
131
131
  lines.push(`\n${r.codeRealityWarning}`);
@@ -200,7 +200,7 @@ export function buildStatusResponse(result, specId, currentStatus, newStatus, re
200
200
  const markdownText = reconciliationMarkdown
201
201
  ? `${withNextSteps}\n\n${reconciliationMarkdown}`
202
202
  : withNextSteps;
203
- const displayTitle = specTitle ?? `SPEC-${specId}`;
203
+ const displayTitle = specTitle ?? specId;
204
204
  const humanSummary = buildUpdateStatusSummary(displayTitle, newStatus);
205
205
  const warningItems = [
206
206
  r.protectedBranchWarning,
@@ -35,6 +35,7 @@ export declare function fireAndForgetSideEffects(params: {
35
35
  projectId: string;
36
36
  spec: Spec;
37
37
  currentStatus: SpecStatus;
38
+ projectPath?: string;
38
39
  }): void;
39
40
  /**
40
41
  * SPEC-772: Run cascade and await fast hooks (fast: true) within a 2s window.
@@ -50,5 +51,6 @@ export declare function runCascadeForResponse(params: {
50
51
  projectId: string;
51
52
  spec: Spec;
52
53
  currentStatus: SpecStatus;
54
+ projectPath?: string;
53
55
  }): Promise<RunCascadeResult | null>;
54
56
  //# sourceMappingURL=side-effects.d.ts.map
@@ -193,7 +193,7 @@ function readDisabledByEnv() {
193
193
  /** Fire-and-forget side effects after a successful status transition. */
194
194
  export function fireAndForgetSideEffects(params) {
195
195
  const { knowledge, allSpecs, specId, newStatus, validateScore, projectId, spec, currentStatus } = params;
196
- const projectPath = knowledge?.projectPath;
196
+ const projectPath = params.projectPath ?? knowledge?.projectPath;
197
197
  const ctx = {
198
198
  specId,
199
199
  spec,
@@ -223,7 +223,7 @@ export function fireAndForgetSideEffects(params) {
223
223
  */
224
224
  export async function runCascadeForResponse(params) {
225
225
  const { knowledge, allSpecs, specId, newStatus, validateScore, projectId, spec, currentStatus } = params;
226
- const projectPath = knowledge?.projectPath;
226
+ const projectPath = params.projectPath ?? knowledge?.projectPath;
227
227
  const ctx = {
228
228
  specId,
229
229
  spec,
@@ -4,7 +4,7 @@ import { ti } from '../../i18n/index.js';
4
4
  import { validateDoR } from '../../engine/dor-dod.js';
5
5
  import { dispatchFeedbackEvent } from '../learn.js';
6
6
  import { scoreAmbiguityFromPath } from '../../engine/ambiguity-scorer.js';
7
- import { checkReadinessInternal } from '../../engine/readiness-checker.js';
7
+ import { checkSpecReadiness } from '../../engine/readiness-checker.js';
8
8
  import { validateEnglishOnlySpecText } from '../../engine/spec-language/english-only.js';
9
9
  import { checkGroundedSpecContract } from '../../engine/spec-grounding/contract.js';
10
10
  import { checkGenericSpecOutput } from '../../engine/spec-quality/generic-output-gate.js';
@@ -331,18 +331,21 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
331
331
  qualityWarnings: [],
332
332
  };
333
333
  }
334
- let score;
335
- let warnings;
336
- let criteriaCount;
334
+ let readiness;
337
335
  try {
338
- ({ score, warnings, criteriaCount } = checkReadinessInternal(body));
336
+ readiness = await checkSpecReadiness(spec, 'strict');
339
337
  }
340
338
  catch (error) {
341
339
  /* reliability-optional: READINESS_EVALUATION_FAILED — transition fails closed below */
342
340
  reportClassifiedDegradation('READINESS_EVALUATION_FAILED', error);
343
341
  return readinessUnavailable('READINESS_EVALUATION_FAILED', error instanceof Error ? error.message : 'readiness evaluation failed');
344
342
  }
345
- if (criteriaCount === 0) {
343
+ const { score } = readiness;
344
+ const blockers = [...readiness.issues.blockers];
345
+ const warnings = [...readiness.issues.warnings];
346
+ const issues = [...blockers, ...warnings];
347
+ const hasNoCriteria = readiness.issues.breakdown.criteria === 0;
348
+ if (hasNoCriteria) {
346
349
  if (newStatus === 'review') {
347
350
  return {
348
351
  blockResult: {
@@ -357,6 +360,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
357
360
  error: 'READINESS_GATE_BLOCKED',
358
361
  score,
359
362
  threshold: 70,
363
+ blockers,
360
364
  warnings,
361
365
  fixHint: 'Add at least 3 testable acceptance criteria before moving to review.',
362
366
  },
@@ -388,24 +392,32 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
388
392
  },
389
393
  ],
390
394
  structuredContent: {
395
+ error: 'READINESS_GATE_BLOCKED',
396
+ score,
397
+ threshold: 70,
398
+ blockers,
399
+ warnings,
391
400
  interactiveQuestions: [question],
392
401
  },
393
402
  },
394
403
  qualityWarnings: [],
395
404
  };
396
405
  }
397
- if (score >= 70) {
406
+ if (readiness.ready) {
398
407
  return { blockResult: null, qualityWarnings: [] };
399
408
  }
409
+ const diagnostic = score < 70
410
+ ? `score ${String(score)}/100 is below the 70 threshold`
411
+ : `score ${String(score)}/100 meets the threshold, but ${String(blockers.length)} canonical blocker(s) remain`;
412
+ const issuesSummary = issues.slice(0, 3).join('; ');
400
413
  if (newStatus === 'review') {
401
- const warningsSummary = warnings.slice(0, 3).join('; ');
402
414
  return {
403
415
  blockResult: {
404
416
  content: [
405
417
  {
406
418
  type: 'text',
407
- text: `Readiness gate blocked transition to review: score ${String(score)}/100 — below 70 threshold. ` +
408
- `Warnings: ${warningsSummary}`,
419
+ text: `Readiness gate blocked transition to review: ${diagnostic}. ` +
420
+ `Issues: ${issuesSummary}`,
409
421
  },
410
422
  ],
411
423
  isError: true,
@@ -413,6 +425,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
413
425
  error: 'READINESS_GATE_BLOCKED',
414
426
  score,
415
427
  threshold: 70,
428
+ blockers,
416
429
  warnings,
417
430
  fixHint: 'Complete the spec before moving from draft to review.',
418
431
  },
@@ -423,12 +436,11 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
423
436
  // Score < 70
424
437
  if (forceApprove) {
425
438
  // Caller wants to force through — proceed with warnings attached
426
- return { blockResult: null, qualityWarnings: warnings };
439
+ return { blockResult: null, qualityWarnings: issues };
427
440
  }
428
- const warningsSummary = warnings.slice(0, 3).join('; ');
429
441
  const question = {
430
442
  header: 'Readiness gate',
431
- question: `Spec readiness score ${String(score)}/100 (threshold: 70). How to proceed?`,
443
+ question: `Spec readiness ${diagnostic}. How to proceed?`,
432
444
  multiSelect: false,
433
445
  options: [
434
446
  {
@@ -437,7 +449,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
437
449
  },
438
450
  {
439
451
  label: 'view_missing',
440
- description: `View missing items: ${warningsSummary}`,
452
+ description: `View missing items: ${issuesSummary}`,
441
453
  },
442
454
  {
443
455
  label: 'force_approve',
@@ -450,10 +462,15 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
450
462
  content: [
451
463
  {
452
464
  type: 'text',
453
- text: `Readiness gate: score ${String(score)}/100 — below 70 threshold. Warnings: ${warningsSummary}`,
465
+ text: `Readiness gate: ${diagnostic}. Issues: ${issuesSummary}`,
454
466
  },
455
467
  ],
456
468
  structuredContent: {
469
+ error: 'READINESS_GATE_BLOCKED',
470
+ score,
471
+ threshold: 70,
472
+ blockers,
473
+ warnings,
457
474
  interactiveQuestions: [question],
458
475
  },
459
476
  },
@@ -1,6 +1,7 @@
1
1
  import type { ConstitutionViolation } from '../types/index.js';
2
2
  export declare function runImplementingActions(projectId: string, specId: string, options?: {
3
3
  deferSideEffects?: boolean;
4
+ projectPath?: string;
4
5
  }): Promise<{
5
6
  autoBranch: string | undefined;
6
7
  protectedBranchWarning: string | null;
@@ -8,9 +9,10 @@ export declare function runImplementingActions(projectId: string, specId: string
8
9
  autopilotSummary: string[];
9
10
  }>;
10
11
  /** Create the implementation branch and refresh derived context after commit. */
11
- export declare function runImplementingSideEffects(projectId: string, specId: string): Promise<string | undefined>;
12
+ export declare function runImplementingSideEffects(projectId: string, specId: string, transitionProjectPath?: string): Promise<string | undefined>;
12
13
  export declare function runDoneActions(projectId: string, specId: string, gitBranch: string | undefined, options?: {
13
14
  deferSideEffects?: boolean;
15
+ projectPath?: string;
14
16
  }): Promise<{
15
17
  mergeWarning: string | null;
16
18
  prSuggestion: {
@@ -22,7 +24,7 @@ export declare function runDoneActions(projectId: string, specId: string, gitBra
22
24
  autopilotSummary: string[];
23
25
  }>;
24
26
  /** Run cleanup and state refreshes only after the done transition is durable. */
25
- export declare function runDoneSideEffects(projectId: string, specId: string, gitBranch: string | undefined): Promise<void>;
27
+ export declare function runDoneSideEffects(projectId: string, specId: string, gitBranch: string | undefined, transitionProjectPath?: string): Promise<void>;
26
28
  /**
27
29
  * Best-effort constitution compliance check for status transitions.
28
30
  * Returns warnings (never blocks the transition).
@@ -8,13 +8,17 @@ function normalizeRejectedReasons(results) {
8
8
  return reason instanceof Error ? reason : new Error(String(reason));
9
9
  });
10
10
  }
11
+ function suppliedProjectPath(value) {
12
+ const trimmed = value?.trim();
13
+ return trimmed && trimmed.length > 0 ? trimmed : undefined;
14
+ }
11
15
  export async function runImplementingActions(projectId, specId, options = {}) {
12
16
  let autoBranch;
13
17
  let protectedBranchWarning = null;
14
18
  // Check if on a protected branch
15
19
  try {
16
20
  const { resolveProjectPath, git: gitCmd } = await import('./git/git-helpers.js');
17
- const projectPath = await resolveProjectPath(projectId);
21
+ const projectPath = suppliedProjectPath(options.projectPath) ?? (await resolveProjectPath(projectId));
18
22
  const { stdout } = await gitCmd(projectPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
19
23
  const currentBranch = stdout.trim();
20
24
  const protectedBranches = ['main', 'master', 'develop', 'production'];
@@ -27,7 +31,7 @@ export async function runImplementingActions(projectId, specId, options = {}) {
27
31
  }
28
32
  if (!options.deferSideEffects) {
29
33
  try {
30
- autoBranch = await runImplementingSideEffects(projectId, specId);
34
+ autoBranch = await runImplementingSideEffects(projectId, specId, options.projectPath);
31
35
  }
32
36
  catch {
33
37
  // Legacy direct callers keep best-effort behavior.
@@ -41,20 +45,20 @@ export async function runImplementingActions(projectId, specId, options = {}) {
41
45
  };
42
46
  }
43
47
  /** Create the implementation branch and refresh derived context after commit. */
44
- export async function runImplementingSideEffects(projectId, specId) {
48
+ export async function runImplementingSideEffects(projectId, specId, transitionProjectPath) {
49
+ const { resolveProjectPath } = await import('./git/git-helpers.js');
50
+ const projectPath = suppliedProjectPath(transitionProjectPath) ?? (await resolveProjectPath(projectId));
45
51
  let autoBranch;
46
52
  const foundSpec = await specStore.getSpec(projectId, specId);
47
53
  if (foundSpec && !foundSpec.gitBranch) {
48
54
  let branchResult;
49
55
  try {
50
- const { resolveProjectPath: resolveBranchPath } = await import('./git/git-helpers.js');
51
56
  const { handleCreateBranch } = await import('./git/branch-ops.js');
52
- const branchProjectPath = await resolveBranchPath(projectId);
53
- branchResult = await withAudit(branchProjectPath, 'update_status(implementing)', 'handleCreateBranch', () => handleCreateBranch(projectId, specId), (r) => ({ isError: r.isError }));
57
+ branchResult = await withAudit(projectPath, 'update_status(implementing)', 'handleCreateBranch', () => handleCreateBranch(projectId, specId, undefined, projectPath), (r) => ({ isError: r.isError }));
54
58
  }
55
59
  catch {
56
60
  const { handleCreateBranch } = await import('./git/branch-ops.js');
57
- branchResult = await handleCreateBranch(projectId, specId);
61
+ branchResult = await handleCreateBranch(projectId, specId, undefined, projectPath);
58
62
  }
59
63
  if (!branchResult.isError) {
60
64
  try {
@@ -70,8 +74,6 @@ export async function runImplementingSideEffects(projectId, specId) {
70
74
  // SPEC-600: Fire-and-forget cascade — recommend_model, orchestration plan, context check
71
75
  // -------------------------------------------------------------------------
72
76
  try {
73
- const { resolveProjectPath: resolveCascadePath } = await import('./git/git-helpers.js');
74
- const projectPath = await resolveCascadePath(projectId);
75
77
  const spec = foundSpec ?? (await specStore.getSpec(projectId, specId));
76
78
  const results = await Promise.allSettled([
77
79
  // SPEC-633: Write session state when spec moves to implementing
@@ -161,7 +163,7 @@ export async function runDoneActions(projectId, specId, gitBranch, options = {})
161
163
  (async () => {
162
164
  try {
163
165
  const { resolveProjectPath, git: gitCmd } = await import('./git/git-helpers.js');
164
- const projectPath = await resolveProjectPath(projectId);
166
+ const projectPath = suppliedProjectPath(options.projectPath) ?? (await resolveProjectPath(projectId));
165
167
  if (process.env.PLANU_ENABLE_AUTOCOMMIT === 'true') {
166
168
  await gitCmd(projectPath, ['add', 'planu/']);
167
169
  const { planuAutoCommit } = await import('../engine/git/planu-autocommit.js');
@@ -193,7 +195,7 @@ export async function runDoneActions(projectId, specId, gitBranch, options = {})
193
195
  const mergeResult = await Promise.resolve((async () => {
194
196
  try {
195
197
  const { resolveProjectPath, resolveBaseBranch, git: gitCmd, } = await import('./git/git-helpers.js');
196
- const projectPath = await resolveProjectPath(projectId);
198
+ const projectPath = suppliedProjectPath(options.projectPath) ?? (await resolveProjectPath(projectId));
197
199
  await gitCmd(projectPath, ['rev-parse', '--verify', gitBranch]);
198
200
  const baseBranch = await resolveBaseBranch(projectPath);
199
201
  if (!baseBranch) {
@@ -213,7 +215,7 @@ export async function runDoneActions(projectId, specId, gitBranch, options = {})
213
215
  mergeWarning = mergeResult;
214
216
  }
215
217
  if (!options.deferSideEffects) {
216
- void runDoneSideEffects(projectId, specId, gitBranch).catch(() => {
218
+ void runDoneSideEffects(projectId, specId, gitBranch, options.projectPath).catch(() => {
217
219
  // Legacy direct callers keep best-effort behavior.
218
220
  });
219
221
  }
@@ -231,9 +233,9 @@ export async function runDoneActions(projectId, specId, gitBranch, options = {})
231
233
  };
232
234
  }
233
235
  /** Run cleanup and state refreshes only after the done transition is durable. */
234
- export async function runDoneSideEffects(projectId, specId, gitBranch) {
236
+ export async function runDoneSideEffects(projectId, specId, gitBranch, transitionProjectPath) {
235
237
  const { resolveProjectPath } = await import('./git/git-helpers.js');
236
- const projectPath = await resolveProjectPath(projectId);
238
+ const projectPath = suppliedProjectPath(transitionProjectPath) ?? (await resolveProjectPath(projectId));
237
239
  const results = await Promise.allSettled([
238
240
  (async () => {
239
241
  const { cleanupSpecOnDone } = await import('./git/cleanup-ops.js');