@planu/cli 4.14.1 → 5.1.1

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 (452) hide show
  1. package/CHANGELOG.md +82 -0
  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 +12 -3
  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 +33 -19
  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 +12 -1
  46. package/dist/engine/evidence-gates/artifact-reader.js +174 -4
  47. package/dist/engine/evidence-gates/evidence-autofill.d.ts +10 -0
  48. package/dist/engine/evidence-gates/evidence-autofill.js +148 -0
  49. package/dist/engine/evidence-gates/evidence-skeletons.d.ts +19 -0
  50. package/dist/engine/evidence-gates/evidence-skeletons.js +69 -0
  51. package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
  52. package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
  53. package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
  54. package/dist/engine/evidence-index/done-drift.d.ts +1 -1
  55. package/dist/engine/evidence-index/done-drift.js +40 -18
  56. package/dist/engine/execution/context.js +3 -0
  57. package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
  58. package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
  59. package/dist/engine/execution/job-runtime.d.ts +30 -0
  60. package/dist/engine/execution/job-runtime.js +114 -6
  61. package/dist/engine/execution/operation-journal.js +10 -4
  62. package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
  63. package/dist/engine/execution/public-job-sanitizer.js +31 -0
  64. package/dist/engine/execution/validate-job-executor.d.ts +12 -1
  65. package/dist/engine/execution/validate-job-executor.js +549 -86
  66. package/dist/engine/execution/validate-job-payload.js +107 -50
  67. package/dist/engine/execution/validate-job-recovery.js +1 -1
  68. package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
  69. package/dist/engine/execution/validate-worker-runtime.js +90 -2
  70. package/dist/engine/execution/validate-worker-thread.js +17 -24
  71. package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
  72. package/dist/engine/handoff-artifacts/schemas.js +33 -0
  73. package/dist/engine/handoff-packager.d.ts +19 -0
  74. package/dist/engine/handoff-packager.js +415 -13
  75. package/dist/engine/hooks/effort-configurator.js +1 -1
  76. package/dist/engine/impact-detector/test-break-predictor.js +3 -3
  77. package/dist/engine/impact-detector/tool-registration.js +1 -1
  78. package/dist/engine/implementation-contract/renderer.js +23 -8
  79. package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
  80. package/dist/engine/lifecycle-reconciliation-io.js +180 -0
  81. package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
  82. package/dist/engine/lifecycle-reconciliation.js +406 -0
  83. package/dist/engine/local-first/tool-classification.js +2 -0
  84. package/dist/engine/macos-keychain-native-port.d.ts +5 -0
  85. package/dist/engine/macos-keychain-native-port.js +19 -7
  86. package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
  87. package/dist/engine/migrations/remove-commercial-state.js +343 -0
  88. package/dist/engine/minimality/policy-loader.js +247 -6
  89. package/dist/engine/network-policy.js +1 -0
  90. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  91. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +20 -20
  92. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  93. package/dist/engine/planu-core.darwin-x64.node.sbom.json +20 -20
  94. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  95. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +20 -20
  96. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  97. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +20 -20
  98. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  99. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +20 -20
  100. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  101. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +20 -20
  102. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  103. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +20 -20
  104. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  105. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +20 -20
  106. package/dist/engine/readiness-checker.d.ts +1 -16
  107. package/dist/engine/readiness-checker.js +27 -88
  108. package/dist/engine/reverse-engineer/api-detector.js +2 -13
  109. package/dist/engine/reverse-engineer/complexity-analyzer.js +2 -13
  110. package/dist/engine/reverse-engineer/config-analyzer.js +2 -13
  111. package/dist/engine/reverse-engineer/dependency-graph.js +2 -13
  112. package/dist/engine/reverse-engineer/test-analyzer.js +2 -13
  113. package/dist/engine/reverse-engineer/walk-ignore.d.ts +3 -0
  114. package/dist/engine/reverse-engineer/walk-ignore.js +26 -0
  115. package/dist/engine/runtime-policy.d.ts +2 -0
  116. package/dist/engine/runtime-policy.js +42 -5
  117. package/dist/engine/runtime-security/rate-limiter.js +0 -1
  118. package/dist/engine/safety/contained-project-file.d.ts +3 -0
  119. package/dist/engine/safety/contained-project-file.js +23 -0
  120. package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
  121. package/dist/engine/scope-boundaries/scope-validator.js +45 -7
  122. package/dist/engine/session/checkpoint-writer.js +0 -9
  123. package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
  124. package/dist/engine/spec-format/acceptance-criteria.js +82 -22
  125. package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
  126. package/dist/engine/spec-format/markdown-sections.js +105 -0
  127. package/dist/engine/spec-format/read-technical-section.js +33 -5
  128. package/dist/engine/spec-format/technical-md-populator.js +3 -2
  129. package/dist/engine/spec-format/text-fences.js +20 -2
  130. package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
  131. package/dist/engine/spec-format/unified-spec-builder.js +202 -0
  132. package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
  133. package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
  134. package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
  135. package/dist/engine/spec-migrator/filesystem-import.js +121 -14
  136. package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
  137. package/dist/engine/spec-migrator/index.d.ts +1 -0
  138. package/dist/engine/spec-migrator/index.js +1 -0
  139. package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
  140. package/dist/engine/spec-state-machine/transition-spec.js +10 -1
  141. package/dist/engine/spec-state-syncer.js +1 -1
  142. package/dist/engine/technical-enricher/index.js +16 -8
  143. package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
  144. package/dist/engine/technical-enricher/render-enriched.js +42 -14
  145. package/dist/engine/telemetry/health-checker.js +53 -17
  146. package/dist/engine/telemetry/telemetry-client.js +26 -8
  147. package/dist/engine/text-signal-boundaries.d.ts +2 -0
  148. package/dist/engine/text-signal-boundaries.js +25 -2
  149. package/dist/engine/time/relative-date.d.ts +3 -0
  150. package/dist/engine/time/relative-date.js +16 -0
  151. package/dist/engine/timing/budget.d.ts +11 -0
  152. package/dist/engine/timing/budget.js +31 -9
  153. package/dist/engine/token-optimizer/analytics.d.ts +1 -1
  154. package/dist/engine/token-optimizer/analytics.js +5 -6
  155. package/dist/engine/usage-tracker/core.d.ts +2 -2
  156. package/dist/engine/usage-tracker/core.js +1 -2
  157. package/dist/engine/usage-tracker/index.d.ts +0 -3
  158. package/dist/engine/usage-tracker/index.js +0 -3
  159. package/dist/engine/usage-tracker/stats.d.ts +2 -2
  160. package/dist/engine/usage-tracker/stats.js +1 -28
  161. package/dist/engine/validation/durable-validation.d.ts +1 -1
  162. package/dist/engine/validation/durable-validation.js +3 -82
  163. package/dist/engine/validation/validation-freshness.d.ts +11 -1
  164. package/dist/engine/validation/validation-freshness.js +231 -91
  165. package/dist/engine/validation/validation-receipt.d.ts +9 -6
  166. package/dist/engine/validation/validation-receipt.js +84 -24
  167. package/dist/engine/validation/validation-source-digest.d.ts +2 -0
  168. package/dist/engine/validation/validation-source-digest.js +86 -0
  169. package/dist/engine/validation/validation-submission.d.ts +5 -4
  170. package/dist/engine/validation/validation-submission.js +25 -11
  171. package/dist/engine/validation/validation-worktree.d.ts +21 -0
  172. package/dist/engine/validation/validation-worktree.js +260 -0
  173. package/dist/engine/validation-impact-planner.d.ts +14 -2
  174. package/dist/engine/validation-impact-planner.js +0 -3
  175. package/dist/engine/validator/dor-dod.d.ts +1 -0
  176. package/dist/engine/validator/dor-dod.js +5 -3
  177. package/dist/engine/validator/extractors.d.ts +1 -5
  178. package/dist/engine/validator/extractors.js +24 -69
  179. package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
  180. package/dist/engine/validator/spec-compliance-runner.js +429 -211
  181. package/dist/engine/validator.js +35 -22
  182. package/dist/index.js +4 -16
  183. package/dist/resources/usage-stats-resource.js +2 -18
  184. package/dist/security/adapters/linux-secret-service.js +7 -3
  185. package/dist/security/adapters/windows-credential-manager.js +6 -4
  186. package/dist/server/routes/specs.js +13 -1
  187. package/dist/storage/base-store.d.ts +1 -1
  188. package/dist/storage/base-store.js +1 -1
  189. package/dist/storage/feedback-remote.d.ts +2 -3
  190. package/dist/storage/feedback-remote.js +45 -31
  191. package/dist/storage/global-store.d.ts +1 -17
  192. package/dist/storage/global-store.js +1 -42
  193. package/dist/storage/index.d.ts +0 -2
  194. package/dist/storage/index.js +0 -2
  195. package/dist/storage/migrations/canonical-storage.js +124 -11
  196. package/dist/storage/runtime-db.d.ts +9 -0
  197. package/dist/storage/runtime-db.js +64 -8
  198. package/dist/storage/spec-store.d.ts +14 -2
  199. package/dist/storage/spec-store.js +104 -14
  200. package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
  201. package/dist/storage/status-store/atomic-status-write.js +57 -0
  202. package/dist/storage/status-store/file-lock.js +30 -11
  203. package/dist/storage/status-store/self-healing.js +3 -3
  204. package/dist/storage/status-store/version-sync.d.ts +2 -1
  205. package/dist/storage/status-store/version-sync.js +35 -40
  206. package/dist/storage/usage-store.d.ts +1 -5
  207. package/dist/storage/usage-store.js +0 -35
  208. package/dist/tools/auto-update-handler.d.ts +3 -3
  209. package/dist/tools/auto-update-handler.js +3 -3
  210. package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
  211. package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
  212. package/dist/tools/challenge-spec/scenario-collector.js +1 -1
  213. package/dist/tools/challenge-spec/scenarios-data.js +1 -1
  214. package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
  215. package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
  216. package/dist/tools/challenge-spec-helpers.d.ts +10 -1
  217. package/dist/tools/challenge-spec-helpers.js +63 -22
  218. package/dist/tools/challenge-spec.js +23 -8
  219. package/dist/tools/check-readiness.js +38 -14
  220. package/dist/tools/configure-telemetry.js +30 -2
  221. package/dist/tools/create-spec/spec-builder.d.ts +7 -0
  222. package/dist/tools/create-spec/spec-builder.js +19 -15
  223. package/dist/tools/create-spec.d.ts +1 -0
  224. package/dist/tools/create-spec.js +584 -309
  225. package/dist/tools/git/branch-ops.d.ts +1 -1
  226. package/dist/tools/git/branch-ops.js +3 -2
  227. package/dist/tools/init-project/handler.js +10 -12
  228. package/dist/tools/init-project/host-assets-writer.js +26 -1
  229. package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
  230. package/dist/tools/init-project/migration-runner.d.ts +2 -4
  231. package/dist/tools/init-project/migration-runner.js +3 -2
  232. package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
  233. package/dist/tools/init-project/portable-index-reconciler.js +14 -0
  234. package/dist/tools/init-project/result-builder.js +1 -3
  235. package/dist/tools/jobs/handlers.js +38 -6
  236. package/dist/tools/list-specs.js +5 -26
  237. package/dist/tools/merge-risk-handler.js +2 -2
  238. package/dist/tools/package-handoff.js +2 -0
  239. package/dist/tools/reconcile-spec.js +0 -27
  240. package/dist/tools/register-agent-squad-tools.js +2 -2
  241. package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
  242. package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
  243. package/dist/tools/register-sdd-tools.d.ts +1 -1
  244. package/dist/tools/register-sdd-tools.js +2 -29
  245. package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
  246. package/dist/tools/register-spec-tools/core-spec-tools.js +29 -19
  247. package/dist/tools/safe-handler.d.ts +5 -13
  248. package/dist/tools/safe-handler.js +7 -23
  249. package/dist/tools/schemas/index.d.ts +1 -1
  250. package/dist/tools/schemas/index.js +1 -1
  251. package/dist/tools/schemas/output-schemas.d.ts +0 -23
  252. package/dist/tools/schemas/output-schemas.js +0 -28
  253. package/dist/tools/schemas/spec.d.ts +4 -0
  254. package/dist/tools/schemas/spec.js +3 -0
  255. package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
  256. package/dist/tools/schemas/validate-output-schema.js +7 -0
  257. package/dist/tools/spec-prompt-handler.js +3 -3
  258. package/dist/tools/sync-spec-state-handler.d.ts +1 -1
  259. package/dist/tools/sync-spec-state-handler.js +50 -2
  260. package/dist/tools/tool-entry.d.ts +4 -4
  261. package/dist/tools/tool-entry.js +3 -3
  262. package/dist/tools/tool-registry/core-tools.js +31 -15
  263. package/dist/tools/tool-registry/group-infra.js +27 -110
  264. package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
  265. package/dist/tools/tool-registry/inventory.d.ts +2 -0
  266. package/dist/tools/tool-registry/inventory.js +2 -0
  267. package/dist/tools/tool-registry-helpers.d.ts +3 -3
  268. package/dist/tools/tool-registry-helpers.js +3 -3
  269. package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
  270. package/dist/tools/tool-usage-report/registered-tools.js +3 -15
  271. package/dist/tools/update-status/approval-gates.js +3 -0
  272. package/dist/tools/update-status/batch.d.ts +6 -2
  273. package/dist/tools/update-status/batch.js +58 -1
  274. package/dist/tools/update-status/dod-gates.d.ts +16 -1
  275. package/dist/tools/update-status/dod-gates.js +191 -1
  276. package/dist/tools/update-status/done-receipt-verifier.d.ts +8 -0
  277. package/dist/tools/update-status/done-receipt-verifier.js +75 -27
  278. package/dist/tools/update-status/evidence-gate.d.ts +4 -0
  279. package/dist/tools/update-status/evidence-gate.js +103 -3
  280. package/dist/tools/update-status/file-sync.d.ts +4 -3
  281. package/dist/tools/update-status/file-sync.js +57 -6
  282. package/dist/tools/update-status/index.d.ts +25 -1
  283. package/dist/tools/update-status/index.js +292 -78
  284. package/dist/tools/update-status/response-builder.js +3 -3
  285. package/dist/tools/update-status/side-effects.d.ts +2 -0
  286. package/dist/tools/update-status/side-effects.js +2 -2
  287. package/dist/tools/update-status/transition-guard.js +45 -16
  288. package/dist/tools/update-status-actions.d.ts +4 -2
  289. package/dist/tools/update-status-actions.js +16 -14
  290. package/dist/tools/usage-report.js +1 -16
  291. package/dist/tools/usage-stats.js +1 -18
  292. package/dist/tools/usage-tracking.d.ts +4 -0
  293. package/dist/tools/usage-tracking.js +20 -0
  294. package/dist/tools/validate-lint.d.ts +1 -0
  295. package/dist/tools/validate-lint.js +1 -1
  296. package/dist/tools/validate.d.ts +2 -1
  297. package/dist/tools/validate.js +114 -34
  298. package/dist/tools/workspace-dashboard-handler.js +38 -0
  299. package/dist/transports/http-transport.js +6 -8
  300. package/dist/transports/mcp-types.d.ts +0 -2
  301. package/dist/transports/session-context.js +1 -1
  302. package/dist/types/cascade-hooks.d.ts +1 -1
  303. package/dist/types/ci.d.ts +0 -4
  304. package/dist/types/clarification-token.d.ts +1 -1
  305. package/dist/types/claude-code-runtime.d.ts +0 -27
  306. package/dist/types/commercial-migration.d.ts +17 -0
  307. package/dist/types/commercial-migration.js +2 -0
  308. package/dist/types/common/primitives.d.ts +0 -2
  309. package/dist/types/core-bridge.d.ts +4 -10
  310. package/dist/types/dashboard.d.ts +3 -7
  311. package/dist/types/docs.d.ts +0 -1
  312. package/dist/types/durable-job.d.ts +4 -4
  313. package/dist/types/durable-validation.d.ts +100 -2
  314. package/dist/types/durable-validation.js +165 -1
  315. package/dist/types/errors/structured-error.d.ts +3 -3
  316. package/dist/types/estimation.d.ts +2 -0
  317. package/dist/types/evidence-autofill.d.ts +34 -0
  318. package/dist/types/evidence-autofill.js +2 -0
  319. package/dist/types/evidence-gates.d.ts +9 -1
  320. package/dist/types/execution.d.ts +2 -0
  321. package/dist/types/gemini-integration.d.ts +0 -11
  322. package/dist/types/handoff-artifacts.d.ts +41 -0
  323. package/dist/types/hooks-advanced.d.ts +0 -13
  324. package/dist/types/impact-detection.d.ts +2 -2
  325. package/dist/types/index.d.ts +3 -6
  326. package/dist/types/index.js +2 -5
  327. package/dist/types/kiro-integration.d.ts +0 -6
  328. package/dist/types/network-policy.d.ts +1 -1
  329. package/dist/types/project/lint-scaffolder.d.ts +1 -1
  330. package/dist/types/runtime-policy.d.ts +11 -0
  331. package/dist/types/session-safeguard.d.ts +0 -31
  332. package/dist/types/spec/canonical-repair.d.ts +81 -0
  333. package/dist/types/spec/canonical-repair.js +2 -0
  334. package/dist/types/spec/core.d.ts +20 -0
  335. package/dist/types/spec/index.d.ts +1 -0
  336. package/dist/types/spec/index.js +1 -0
  337. package/dist/types/spec/inputs.d.ts +35 -1
  338. package/dist/types/spec-format.d.ts +25 -1
  339. package/dist/types/telemetry.d.ts +4 -1
  340. package/dist/types/transition-log.d.ts +1 -1
  341. package/dist/types/transport.d.ts +0 -2
  342. package/dist/types/usage.d.ts +1 -36
  343. package/dist/types/usage.js +1 -1
  344. package/dist/types/validate-worker.d.ts +6 -0
  345. package/dist/types/validation-receipt.d.ts +1 -1
  346. package/dist/types/validation.d.ts +10 -4
  347. package/package.json +17 -12
  348. package/planu-native.json +1 -1
  349. package/planu-plugin.json +23 -40
  350. package/scripts/lib/pending-release-file.mjs +93 -0
  351. package/src/i18n/messages/en.json +0 -41
  352. package/src/i18n/messages/es.json +0 -41
  353. package/src/i18n/messages/pt.json +0 -41
  354. package/dist/cli/commands/activate.d.ts +0 -14
  355. package/dist/cli/commands/activate.js +0 -174
  356. package/dist/config/license-plans.json +0 -251
  357. package/dist/config/product-public.ts +0 -148
  358. package/dist/config/server-instructions.ts +0 -121
  359. package/dist/config/version-defaults.ts +0 -76
  360. package/dist/config/version.ts +0 -21
  361. package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
  362. package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
  363. package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
  364. package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
  365. package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
  366. package/dist/engine/hooks/full-spectrum-generator.js +0 -117
  367. package/dist/engine/license-validator/config-loader.d.ts +0 -5
  368. package/dist/engine/license-validator/config-loader.js +0 -16
  369. package/dist/engine/license-validator/core.d.ts +0 -15
  370. package/dist/engine/license-validator/core.js +0 -151
  371. package/dist/engine/license-validator/fingerprint.d.ts +0 -3
  372. package/dist/engine/license-validator/fingerprint.js +0 -17
  373. package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
  374. package/dist/engine/license-validator/lemon-squeezy.js +0 -55
  375. package/dist/engine/license-validator.d.ts +0 -5
  376. package/dist/engine/license-validator.js +0 -6
  377. package/dist/engine/product-intelligence/index.d.ts +0 -7
  378. package/dist/engine/product-intelligence/index.js +0 -211
  379. package/dist/engine/session/auto-save.d.ts +0 -18
  380. package/dist/engine/session/auto-save.js +0 -98
  381. package/dist/engine/session/context-snapshot.d.ts +0 -8
  382. package/dist/engine/session/context-snapshot.js +0 -49
  383. package/dist/engine/session/memory-sync.d.ts +0 -12
  384. package/dist/engine/session/memory-sync.js +0 -34
  385. package/dist/engine/session/session-diff.d.ts +0 -11
  386. package/dist/engine/session/session-diff.js +0 -42
  387. package/dist/engine/session/session-merge.d.ts +0 -12
  388. package/dist/engine/session/session-merge.js +0 -55
  389. package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
  390. package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
  391. package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
  392. package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
  393. package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
  394. package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
  395. package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
  396. package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
  397. package/dist/engine/spec-changelog/core.d.ts +0 -16
  398. package/dist/engine/spec-changelog/core.js +0 -176
  399. package/dist/engine/spec-changelog/diff.d.ts +0 -18
  400. package/dist/engine/spec-changelog/diff.js +0 -121
  401. package/dist/engine/spec-changelog/index.d.ts +0 -3
  402. package/dist/engine/spec-changelog/index.js +0 -4
  403. package/dist/engine/trial-engine.d.ts +0 -5
  404. package/dist/engine/trial-engine.js +0 -14
  405. package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
  406. package/dist/engine/usage-tracker/rate-check.js +0 -18
  407. package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
  408. package/dist/engine/usage-tracker/rate-limiter.js +0 -73
  409. package/dist/engine/usage-tracker/trial.d.ts +0 -27
  410. package/dist/engine/usage-tracker/trial.js +0 -49
  411. package/dist/engine/webhook/event-handlers.d.ts +0 -25
  412. package/dist/engine/webhook/event-handlers.js +0 -89
  413. package/dist/engine/webhook/index.d.ts +0 -4
  414. package/dist/engine/webhook/index.js +0 -5
  415. package/dist/engine/webhook/server.d.ts +0 -12
  416. package/dist/engine/webhook/server.js +0 -249
  417. package/dist/engine/webhook/signature.d.ts +0 -16
  418. package/dist/engine/webhook/signature.js +0 -49
  419. package/dist/storage/license-store.d.ts +0 -6
  420. package/dist/storage/license-store.js +0 -63
  421. package/dist/storage/session-store.d.ts +0 -20
  422. package/dist/storage/session-store.js +0 -136
  423. package/dist/tools/activate-license.d.ts +0 -6
  424. package/dist/tools/activate-license.js +0 -132
  425. package/dist/tools/checkpoint-handler.d.ts +0 -3
  426. package/dist/tools/checkpoint-handler.js +0 -130
  427. package/dist/tools/export-session.d.ts +0 -3
  428. package/dist/tools/export-session.js +0 -90
  429. package/dist/tools/license-gate.d.ts +0 -18
  430. package/dist/tools/license-gate.js +0 -222
  431. package/dist/tools/license-status.d.ts +0 -3
  432. package/dist/tools/license-status.js +0 -93
  433. package/dist/tools/list-sessions.d.ts +0 -3
  434. package/dist/tools/list-sessions.js +0 -46
  435. package/dist/tools/planu-session-checkpoint.d.ts +0 -6
  436. package/dist/tools/planu-session-checkpoint.js +0 -42
  437. package/dist/tools/restore-session.d.ts +0 -3
  438. package/dist/tools/restore-session.js +0 -71
  439. package/dist/tools/spec-history.d.ts +0 -3
  440. package/dist/tools/spec-history.js +0 -59
  441. package/dist/tools/webhook.d.ts +0 -6
  442. package/dist/tools/webhook.js +0 -150
  443. package/dist/types/changelog.d.ts +0 -49
  444. package/dist/types/changelog.js +0 -3
  445. package/dist/types/licensing.d.ts +0 -109
  446. package/dist/types/licensing.js +0 -4
  447. package/dist/types/product-intelligence.d.ts +0 -60
  448. package/dist/types/product-intelligence.js +0 -3
  449. package/dist/types/session.d.ts +0 -84
  450. package/dist/types/session.js +0 -3
  451. package/dist/types/webhook.d.ts +0 -62
  452. package/dist/types/webhook.js +0 -4
@@ -1,6 +1,30 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { UpdateStatusInput, ToolResult } from '../../types/index.js';
2
+ import type { UpdateStatusInput, ToolResult, Actuals, ReconciliationInvocationContext } from '../../types/index.js';
3
+ type LifecycleAuthorityRoute = {
4
+ route: 'ordinary';
5
+ } | {
6
+ route: 'reconcile';
7
+ } | {
8
+ route: 'error';
9
+ result: ToolResult;
10
+ };
11
+ /** Select the lifecycle path before any transition, artifact, receipt, or log mutation. */
12
+ export declare function classifyLifecycleAuthority(input: UpdateStatusInput, context: ReconciliationInvocationContext, options?: {
13
+ requireDoneDigest?: boolean;
14
+ }): LifecycleAuthorityRoute;
15
+ /**
16
+ * SPEC-1356 (T-A2): a `done` transition no longer requires the caller to supply
17
+ * actuals. When the whole object is omitted, synthesize a minimal record instead
18
+ * of blocking the transition with `actuals_required`. Every caller-provided
19
+ * field — including a caller-provided partial object once the MCP input schema
20
+ * accepts one — is preserved verbatim; only genuinely unmeasured numeric fields
21
+ * default to 0, and `completedAt`/`notes` only default when absent.
22
+ */
23
+ export declare function synthesizeActuals(actuals: Partial<Actuals> | undefined, transitionedAt: string): Actuals;
24
+ /** Bind deferred lifecycle work to one normalized absolute root without persisting that path. */
25
+ export declare function computeTransitionRootBinding(projectPath: string): string;
3
26
  /** Recover durable post-commit work for every registered project after server startup. */
4
27
  export declare function recoverPendingPostCommitTasksAtStartup(): Promise<void>;
5
28
  export declare function handleUpdateStatus(params: UpdateStatusInput, server?: McpServer): Promise<ToolResult>;
29
+ export {};
6
30
  //# sourceMappingURL=index.d.ts.map
@@ -15,20 +15,21 @@ import { checkApprovalGate } from '../../engine/approval-workflow.js';
15
15
  import * as approvalStore from '../../storage/approval-store.js';
16
16
  import { withApprovalSpecLock } from '../../storage/approval-operation-lock.js';
17
17
  import { isLocked, getLock } from '../../storage/spec-lock-store.js';
18
- import { checkDoneGates, checkComplianceGate, checkApprovedFormatGate, checkSpecReviewGate, writeSpecReviewArtifact, } from './dod-gates.js';
18
+ import { checkDoneGates, checkComplianceGate, checkApprovedFormatGate, checkSpecReviewGate, writeSpecReviewArtifact, deriveDoneReviewDigest, } from './dod-gates.js';
19
19
  import { checkLifecycleEvidenceTransitionGate } from './evidence-gate.js';
20
+ import { autofillTraceabilityMatrix } from '../../engine/evidence-gates/evidence-autofill.js';
20
21
  import { buildStatusResponse, buildDryRunResponse } from './response-builder.js';
21
22
  import { recordDoneMetrics, syncSpecFiles, tryReconcile, recordTerminalTransitionEvent, } from './file-sync.js';
22
23
  import { appendEntry, getLastHash } from '../../storage/audit-trail-store.js';
23
24
  import { appendTransitionEvent } from '../../storage/transition-log.js';
24
25
  import { createHash, randomUUID as uuid } from 'node:crypto';
25
- import { updateFrontmatterField } from '../../engine/frontmatter-parser.js';
26
26
  import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
27
27
  import { recordForceUsage } from '../../storage/force-analytics-store.js';
28
28
  import { maybeSafePushOnDone, queuePostCommitTasks, runCascadeForResponse, } from './side-effects.js';
29
29
  import { formatKeyValue } from '../output-formatter.js';
30
30
  import { checkCodeReality } from '../../engine/code-scanner/index.js';
31
- import { join } from 'node:path';
31
+ import { isAbsolute, join, resolve } from 'node:path';
32
+ import { readSpecContent } from '../../engine/spec-reader.js';
32
33
  import { trackCost } from '../../engine/cost-tracking/operation-tracker.js';
33
34
  import { resolveProjectIdOrAutoDetect } from '../resolve-project-id.js';
34
35
  import { withToolTimeout } from '../safe-handler.js';
@@ -39,7 +40,79 @@ import { acquireLock, releaseLock, isLocked as isCrossProcessLocked, LockBusyErr
39
40
  import { projectDataDir } from '../../storage/base-store.js';
40
41
  import { assertExecutionCanCommit } from '../../engine/execution/context.js';
41
42
  import { shouldSkipGateForExplicitLegacyHarness } from './gate-harness.js';
42
- import { assertValidationFreshnessBarrier, } from '../../engine/validation/validation-freshness.js';
43
+ import { assertValidationFreshnessBarrier, closeValidationFreshnessLease, } from '../../engine/validation/validation-freshness.js';
44
+ import { currentReconciliationInvocationContext, reconcileImplementingSpec, } from '../../engine/lifecycle-reconciliation.js';
45
+ const IMPLEMENTATION_REVIEW_DIGEST = /^sha256:[a-f0-9]{64}$/u;
46
+ function lifecycleAuthorityError(code, message, fixHint) {
47
+ return {
48
+ content: [{ type: 'text', text: `${code}: ${message}\nFix: ${fixHint}` }],
49
+ isError: true,
50
+ structuredContent: { error: code, code: 422, fixHint },
51
+ };
52
+ }
53
+ /** Select the lifecycle path before any transition, artifact, receipt, or log mutation. */
54
+ export function classifyLifecycleAuthority(input, context, options = {}) {
55
+ const hasRequestId = input.reconciliationRequestId !== undefined;
56
+ const hasTransitionId = input.expectedImplementingTransitionId !== undefined;
57
+ const hasDigest = input.implementationReviewDigest !== undefined;
58
+ const hasAnyAuthority = hasRequestId || hasTransitionId || hasDigest;
59
+ if (input.status === 'done') {
60
+ if (hasRequestId || hasTransitionId) {
61
+ return {
62
+ route: 'error',
63
+ result: lifecycleAuthorityError('RECONCILIATION_TARGET_INVALID', 'Reconciliation authority is valid only for the review target.', 'Remove reconciliationRequestId and expectedImplementingTransitionId from the done request.'),
64
+ };
65
+ }
66
+ // SPEC-1356: implementationReviewDigest is optional. When the caller omits it,
67
+ // the done gate (checkDoneGates via dod-gates.deriveDoneReviewDigest) derives
68
+ // it automatically from the persisted validation-report.json — the caller no
69
+ // longer has to compute the sha256 by hand. `options.requireDoneDigest` is
70
+ // retained only so legacy/explicit-authority callers can still demand it.
71
+ if (options.requireDoneDigest === true && !hasDigest) {
72
+ return {
73
+ route: 'error',
74
+ result: lifecycleAuthorityError('DONE_REVIEW_DIGEST_REQUIRED', 'A done transition requires the implementation review digest.', 'Supply the sha256 digest of the exact persisted validation-report.json bytes.'),
75
+ };
76
+ }
77
+ if (hasDigest &&
78
+ (typeof input.implementationReviewDigest !== 'string' ||
79
+ !IMPLEMENTATION_REVIEW_DIGEST.test(input.implementationReviewDigest))) {
80
+ return {
81
+ route: 'error',
82
+ result: lifecycleAuthorityError('DONE_REVIEW_DIGEST_INVALID', 'The implementation review digest must be sha256 followed by 64 lowercase hex characters.', 'Recompute the digest from the exact persisted validation-report.json bytes.'),
83
+ };
84
+ }
85
+ return { route: 'ordinary' };
86
+ }
87
+ if (input.status === 'review' && hasAnyAuthority) {
88
+ if (!hasRequestId || !hasTransitionId || !hasDigest) {
89
+ return {
90
+ route: 'error',
91
+ result: lifecycleAuthorityError('RECONCILIATION_TUPLE_INCOMPLETE', 'Review reconciliation requires the complete authority tuple.', 'Supply reconciliationRequestId, expectedImplementingTransitionId, implementationReviewDigest, and a reason of at least 100 characters.'),
92
+ };
93
+ }
94
+ if (!input.reason || input.reason.trim().length < 100) {
95
+ return {
96
+ route: 'error',
97
+ result: lifecycleAuthorityError('RECONCILIATION_REASON_INVALID', 'Review reconciliation requires a non-blank reason of at least 100 characters.', 'Explain the reviewed implementation drift and required follow-up in at least 100 characters.'),
98
+ };
99
+ }
100
+ if (context.surface !== 'local-mcp') {
101
+ return {
102
+ route: 'error',
103
+ result: lifecycleAuthorityError('RECONCILIATION_UNTRUSTED_SURFACE', 'Reconciliation is available only through trusted local MCP.', 'Retry the complete reconciliation request through the registered local MCP tool.'),
104
+ };
105
+ }
106
+ return { route: 'reconcile' };
107
+ }
108
+ if (hasAnyAuthority) {
109
+ return {
110
+ route: 'error',
111
+ result: lifecycleAuthorityError('RECONCILIATION_TARGET_INVALID', 'Lifecycle authority fields are invalid for this target.', 'Remove the authority fields or use implementationReviewDigest only with done.'),
112
+ };
113
+ }
114
+ return { route: 'ordinary' };
115
+ }
43
116
  /**
44
117
  * SPEC-280: Silently traverse intermediate states so callers can jump forward
45
118
  * (e.g. draft → approved) without receiving an error.
@@ -52,6 +125,58 @@ function buildAutoAdvancePlan(currentStatus, targetStatus) {
52
125
  stepsExecuted,
53
126
  };
54
127
  }
128
+ /**
129
+ * SPEC-1356 (T-A3): auto-generate traceability-matrix.json when it is missing,
130
+ * BEFORE the evidence gate reads it for a 'done' transition. Never overwrites an
131
+ * existing artifact. Tolerates every internal failure (falls through to the
132
+ * ordinary evidence gate, which will report the real missing-evidence error).
133
+ *
134
+ * When autofill actually writes a fresh file, the newly-created evidence has not
135
+ * been bound by validate's durable receipt yet, so this blocks once with a clear
136
+ * "re-run validate" message instead of letting a stale-relative-to-receipt
137
+ * traceability matrix slide through the done gates silently.
138
+ */
139
+ async function runTraceabilityAutofillGate(args) {
140
+ try {
141
+ const specPath = isAbsolute(args.spec.specPath) || !args.projectPath
142
+ ? args.spec.specPath
143
+ : join(args.projectPath, args.spec.specPath);
144
+ const specBody = await readSpecContent(specPath);
145
+ if (specBody === null) {
146
+ return null;
147
+ }
148
+ const result = await autofillTraceabilityMatrix({
149
+ projectId: args.projectId,
150
+ specId: args.specId,
151
+ projectPath: args.projectPath,
152
+ specBody,
153
+ });
154
+ if (!result.written) {
155
+ return null;
156
+ }
157
+ return {
158
+ content: [
159
+ {
160
+ type: 'text',
161
+ text: `traceability-matrix.json was auto-generated at ${result.path} (${String(result.rowCount)} rows). ` +
162
+ 'Run validate to bind this evidence, then retry update_status(done).',
163
+ },
164
+ ],
165
+ isError: true,
166
+ structuredContent: {
167
+ error: 'traceability_matrix_autofilled',
168
+ code: 422,
169
+ context: { specId: args.specId, path: result.path, rowCount: result.rowCount },
170
+ fixHint: 'Run validate so the new traceability-matrix.json is bound, then retry done.',
171
+ },
172
+ };
173
+ }
174
+ catch (error) {
175
+ // reliability-optional: EVIDENCE_AUTOFILL_GATE — falls through to the ordinary evidence gate
176
+ reportClassifiedDegradation('EVIDENCE_AUTOFILL_GATE', error);
177
+ return null;
178
+ }
179
+ }
55
180
  /**
56
181
  * SPEC-301: Check if spec is locked by another agent.
57
182
  * Returns an error ToolResult if locked, or null to continue.
@@ -101,6 +226,27 @@ async function checkApprovalPolicyGate(projectId, specId, newStatus) {
101
226
  }
102
227
  return null;
103
228
  }
229
+ /**
230
+ * SPEC-1356 (T-A2): a `done` transition no longer requires the caller to supply
231
+ * actuals. When the whole object is omitted, synthesize a minimal record instead
232
+ * of blocking the transition with `actuals_required`. Every caller-provided
233
+ * field — including a caller-provided partial object once the MCP input schema
234
+ * accepts one — is preserved verbatim; only genuinely unmeasured numeric fields
235
+ * default to 0, and `completedAt`/`notes` only default when absent.
236
+ */
237
+ export function synthesizeActuals(actuals, transitionedAt) {
238
+ return {
239
+ devHours: actuals?.devHours ?? 0,
240
+ reviewHours: actuals?.reviewHours ?? 0,
241
+ tokensOpus: actuals?.tokensOpus ?? 0,
242
+ tokensSonnet: actuals?.tokensSonnet ?? 0,
243
+ apiCostUsd: actuals?.apiCostUsd ?? 0,
244
+ humanCostUsd: actuals?.humanCostUsd ?? 0,
245
+ totalCostUsd: actuals?.totalCostUsd ?? 0,
246
+ completedAt: actuals?.completedAt ?? transitionedAt,
247
+ notes: actuals?.notes ?? 'actuals unavailable — auto-filled by done gate',
248
+ };
249
+ }
104
250
  /** Compute done metrics when transitioning to 'done' with actuals provided. */
105
251
  function computeDoneMetrics(newStatus, spec, actuals) {
106
252
  if (newStatus !== 'done' || actuals === undefined || spec === null) {
@@ -199,6 +345,18 @@ function planBackgroundActions(args) {
199
345
  }
200
346
  return actions;
201
347
  }
348
+ /** Bind deferred lifecycle work to one normalized absolute root without persisting that path. */
349
+ export function computeTransitionRootBinding(projectPath) {
350
+ return createHash('sha256')
351
+ .update('planu:transition-root:v1\0')
352
+ .update(resolve(projectPath))
353
+ .digest('hex');
354
+ }
355
+ function receiptMatchesTransitionRoot(receipt, projectPath) {
356
+ return (projectPath !== undefined &&
357
+ receipt.transitionRootBinding !== undefined &&
358
+ receipt.transitionRootBinding === computeTransitionRootBinding(projectPath));
359
+ }
202
360
  function buildPostCommitTasks(args) {
203
361
  const enabled = new Set(args.actionNames);
204
362
  const tasks = [];
@@ -212,13 +370,13 @@ function buildPostCommitTasks(args) {
212
370
  if (enabled.has('implementing-side-effects')) {
213
371
  tasks.push({
214
372
  name: 'implementing-side-effects',
215
- run: () => runImplementingSideEffects(args.projectId, args.spec.id),
373
+ run: () => runImplementingSideEffects(args.projectId, args.spec.id, args.effectiveProjectPath),
216
374
  });
217
375
  }
218
376
  if (enabled.has('done-side-effects')) {
219
377
  tasks.push({
220
378
  name: 'done-side-effects',
221
- run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch),
379
+ run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch, args.effectiveProjectPath),
222
380
  });
223
381
  }
224
382
  if (enabled.has('reconcile')) {
@@ -231,7 +389,7 @@ function buildPostCommitTasks(args) {
231
389
  if (enabled.has('record-done-metrics') && actuals) {
232
390
  tasks.push({
233
391
  name: 'record-done-metrics',
234
- run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.knowledge?.projectPath),
392
+ run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.effectiveProjectPath),
235
393
  });
236
394
  }
237
395
  const effectiveProjectPath = args.effectiveProjectPath;
@@ -253,6 +411,7 @@ function buildPostCommitTasks(args) {
253
411
  projectId: args.projectId,
254
412
  spec: args.spec,
255
413
  currentStatus: args.previousStatus,
414
+ projectPath: args.effectiveProjectPath,
256
415
  }),
257
416
  });
258
417
  }
@@ -291,7 +450,10 @@ async function replayIdempotentPostCommitTasks(args) {
291
450
  return;
292
451
  }
293
452
  const knowledge = await knowledgeStore.getKnowledge(args.projectId);
294
- const effectiveProjectPath = knowledge?.projectPath ?? args.projectPath;
453
+ const effectiveProjectPath = args.projectPath ?? knowledge?.projectPath;
454
+ if (!receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
455
+ return;
456
+ }
295
457
  const specAtTransition = { ...args.spec, status: receipt.status };
296
458
  const tasks = buildPostCommitTasks({
297
459
  actionNames: receipt.pendingBackgroundActions,
@@ -317,13 +479,17 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
317
479
  knowledgeStore.getKnowledge(projectId),
318
480
  specStore.listSpecs(projectId),
319
481
  ]);
320
- const effectiveProjectPath = knowledge?.projectPath ?? projectPath;
482
+ const effectiveProjectPath = projectPath ?? knowledge?.projectPath;
321
483
  for (const spec of specs) {
322
- const hasPendingWork = (spec.statusHistory ?? []).some((receipt) => receipt.transitionId && receipt.pendingBackgroundActions?.length);
323
- if (!hasPendingWork) {
484
+ const recoverableReceipts = (spec.statusHistory ?? []).filter((receipt) => receipt.transitionId &&
485
+ receipt.pendingBackgroundActions?.length &&
486
+ receiptMatchesTransitionRoot(receipt, effectiveProjectPath));
487
+ if (recoverableReceipts.length === 0 || !effectiveProjectPath) {
324
488
  continue;
325
489
  }
326
- const recoveryLock = await acquireLock(effectiveProjectPath ?? projectPath ?? process.cwd(), spec.id, { reason: 'recover pending post-commit tasks' });
490
+ const recoveryLock = await acquireLock(effectiveProjectPath, spec.id, {
491
+ reason: 'recover pending post-commit tasks',
492
+ });
327
493
  if (recoveryLock === null) {
328
494
  continue;
329
495
  }
@@ -335,7 +501,9 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
335
501
  continue;
336
502
  }
337
503
  for (const receipt of currentSpec.statusHistory ?? []) {
338
- if (!receipt.transitionId || !receipt.pendingBackgroundActions?.length) {
504
+ if (!receipt.transitionId ||
505
+ !receipt.pendingBackgroundActions?.length ||
506
+ !receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
339
507
  continue;
340
508
  }
341
509
  const specAtTransition = { ...currentSpec, status: receipt.status };
@@ -383,6 +551,18 @@ export async function recoverPendingPostCommitTasksAtStartup() {
383
551
  });
384
552
  }
385
553
  }
554
+ async function runTransitionMaintenance(transitionProjectPath) {
555
+ if (transitionProjectPath) {
556
+ try {
557
+ const { runStrictPlanuCleanup } = await import('../../engine/spec-migrator/index.js');
558
+ await runStrictPlanuCleanup(transitionProjectPath);
559
+ }
560
+ catch (error) {
561
+ /* reliability-cleanup: PRE_TRANSITION_STRICT_CLEANUP — validate remains fail-closed */
562
+ reportClassifiedDegradation('PRE_TRANSITION_STRICT_CLEANUP', error);
563
+ }
564
+ }
565
+ }
386
566
  function buildPersistenceFailureResponse(args) {
387
567
  const committed = args.recoveryRequired ? null : false;
388
568
  const message = args.recoveryRequired
@@ -469,7 +649,7 @@ async function commitTransitionWithNarrowLock(args) {
469
649
  expectedSpec: freshSpec,
470
650
  });
471
651
  const updatedSpec = transitionRecord.spec;
472
- const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath);
652
+ const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath, args.qualityWarnings);
473
653
  if (!syncResult.warning) {
474
654
  return { ok: true, transitionRecord, updatedSpec };
475
655
  }
@@ -571,6 +751,13 @@ async function resolveOrchestrationPlan(newStatus, specScope, specId, projectPat
571
751
  export async function handleUpdateStatus(params, server) {
572
752
  return trackCost(params.projectPath?.trim() ?? '', 'update_status', async () => {
573
753
  const { specId, status: newStatus, actuals, reviewNotes } = params;
754
+ // SPEC-1356: implementationReviewDigest is derived automatically by the done
755
+ // gate when omitted (see dod-gates.deriveDoneReviewDigest), so it is never
756
+ // required here.
757
+ const authorityRoute = classifyLifecycleAuthority(params, currentReconciliationInvocationContext());
758
+ if (authorityRoute.route === 'error') {
759
+ return authorityRoute.result;
760
+ }
574
761
  // SPEC-509: auto-detect project from git root when projectPath/projectId are omitted
575
762
  const resolved = await resolveProjectIdOrAutoDetect({
576
763
  projectId: params.projectId,
@@ -581,24 +768,27 @@ export async function handleUpdateStatus(params, server) {
581
768
  }
582
769
  const projectId = resolved.projectId;
583
770
  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
- }
771
+ const knowledge = await knowledgeStore.getKnowledge(projectId);
772
+ const transitionProjectPath = (params.projectPath?.trim()
773
+ ? effectiveProjectPath
774
+ : (knowledge?.projectPath ?? effectiveProjectPath)) || undefined;
775
+ if (authorityRoute.route === 'reconcile') {
776
+ return reconcileImplementingSpec({
777
+ input: params,
778
+ projectId,
779
+ projectPath: transitionProjectPath,
780
+ context: currentReconciliationInvocationContext(),
781
+ syncSpecFiles,
782
+ });
593
783
  }
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
- });
784
+ if (newStatus !== 'done') {
785
+ await runTransitionMaintenance(transitionProjectPath);
786
+ }
787
+ let doneFreshnessLease;
598
788
  return (async () => {
599
789
  try {
600
790
  // SPEC-301: Reject status changes when spec is locked by another holder (legacy manual lock)
601
- const lockError = await checkSpecLockGate(effectiveProjectPath, specId);
791
+ const lockError = await checkSpecLockGate(transitionProjectPath, specId);
602
792
  if (lockError) {
603
793
  return lockError;
604
794
  }
@@ -608,7 +798,7 @@ export async function handleUpdateStatus(params, server) {
608
798
  if (isDryRun) {
609
799
  // SPEC-731: dry_run checks lock availability without acquiring it.
610
800
  // isLocked returns null when no lockfile exists (available), or a LockInfo when locked.
611
- const lockInfo = await isCrossProcessLocked(effectiveProjectPath, specId).catch((error) => {
801
+ const lockInfo = await isCrossProcessLocked(transitionProjectPath ?? projectId, specId).catch((error) => {
612
802
  // reliability-optional: DRY_RUN_LOCK_PROBE — null conservatively avoids stale lock claims
613
803
  reportClassifiedDegradation('DRY_RUN_LOCK_PROBE', error);
614
804
  return null;
@@ -647,29 +837,16 @@ export async function handleUpdateStatus(params, server) {
647
837
  await replayIdempotentPostCommitTasks({
648
838
  projectId,
649
839
  spec,
650
- projectPath: effectiveProjectPath,
840
+ projectPath: transitionProjectPath,
651
841
  });
652
842
  const idempotentResult = checkIdempotentOrTransition(projectId, spec, newStatus);
653
843
  if (idempotentResult) {
654
844
  return idempotentResult;
655
845
  }
656
846
  }
657
- if (newStatus === 'done' && actuals === undefined) {
658
- return {
659
- content: [
660
- {
661
- type: 'text',
662
- text: 'actuals_required: provide measured values for done. Use zero only when a metric is explicitly unavailable; Planu does not estimate provider tokens or costs.',
663
- },
664
- ],
665
- isError: true,
666
- structuredContent: {
667
- error: 'actuals_required',
668
- code: 422,
669
- fixHint: 'Provide actuals with measured values or zero for unavailable metrics. Values are preserved without estimation.',
670
- },
671
- };
672
- }
847
+ // SPEC-1356 (T-A2): actuals are no longer required to reach done. When
848
+ // omitted, synthesizeActuals() below fills in a defaulted record instead
849
+ // of blocking the transition — see synthesizeActuals doc comment.
673
850
  // SPEC-280/SPEC-1122: plan every intermediate state in memory. No status is
674
851
  // persisted until all gates for the complete path have passed.
675
852
  const { plannedStatuses, stepsExecuted } = buildAutoAdvancePlan(spec.status, newStatus);
@@ -784,10 +961,8 @@ export async function handleUpdateStatus(params, server) {
784
961
  if (elicitResult) {
785
962
  return elicitResult;
786
963
  }
787
- // Load knowledge for project path (needed for DoD gates, validate trigger and HTML regen)
788
- const knowledge = await knowledgeStore.getKnowledge(projectId);
789
964
  // Resolve effective project path once — used across all gates below
790
- const effectiveGatePath = knowledge?.projectPath ?? effectiveProjectPath;
965
+ const effectiveGatePath = transitionProjectPath;
791
966
  // SPEC-1044: SDD model-routing + context continuity hard gate.
792
967
  const sddRoutingGate = {
793
968
  blockResult: null,
@@ -812,6 +987,26 @@ export async function handleUpdateStatus(params, server) {
812
987
  sddRoutingGate.gateResults.sddModelRouting = 'forced';
813
988
  }
814
989
  }
990
+ // SPEC-1356 (T-A3): auto-generate traceability-matrix.json before the
991
+ // evidence gate below reads it, so a spec never fails 'done' purely
992
+ // because nobody hand-wrote the traceability artifact. Never overwrites
993
+ // an existing file. Freshness caveat: when autofill actually writes a
994
+ // new file, its evidence has not yet been bound by validate's durable
995
+ // receipt, so we block once and ask the caller to re-run validate
996
+ // rather than risk certifying stale bindings. See evidence-autofill.ts
997
+ // for the full rationale.
998
+ if (plannedStatuses.includes('done') &&
999
+ !shouldSkipGateForExplicitLegacyHarness('evidence')) {
1000
+ const autofillGate = await runTraceabilityAutofillGate({
1001
+ spec,
1002
+ specId,
1003
+ projectId,
1004
+ projectPath: effectiveGatePath,
1005
+ });
1006
+ if (autofillGate) {
1007
+ return autofillGate;
1008
+ }
1009
+ }
815
1010
  // SPEC-1054: BDD/SDD evidence gates. Non-trivial specs must carry
816
1011
  // Discovery before approval, task-plan before implementation, and
817
1012
  // traceability/contract evidence before done.
@@ -830,6 +1025,21 @@ export async function handleUpdateStatus(params, server) {
830
1025
  }
831
1026
  }
832
1027
  }
1028
+ // SPEC-1356 (T-A1): implementationReviewDigest is optional. When the
1029
+ // caller omits it, derive it automatically from the persisted
1030
+ // validation-report.json instead of requiring the caller to hand-compute
1031
+ // a sha256. Failure to derive it fails closed with a clear "run validate
1032
+ // first" message — identical strength to the explicit-digest path.
1033
+ let resolvedImplementationReviewDigest = params.implementationReviewDigest;
1034
+ if (newStatus === 'done' &&
1035
+ resolvedImplementationReviewDigest === undefined &&
1036
+ !shouldSkipGateForExplicitLegacyHarness('evidence')) {
1037
+ const derived = await deriveDoneReviewDigest(specId, projectId);
1038
+ if ('error' in derived) {
1039
+ return derived.error;
1040
+ }
1041
+ resolvedImplementationReviewDigest = derived.digest;
1042
+ }
833
1043
  // ---------------------------------------------------------------------------
834
1044
  // BATCH A (parallel): code-reality + done-gates — independent of each other
835
1045
  // SPEC-441: Code reality check before transitioning to 'implementing'
@@ -839,7 +1049,6 @@ export async function handleUpdateStatus(params, server) {
839
1049
  // security and repository scans belong to the durable validation worker.
840
1050
  let codeRealityWarning = null;
841
1051
  let forcedBypassWarning = null;
842
- let doneFreshnessLease;
843
1052
  const [codeRealityResult, doneGatesResult] = await Promise.all([
844
1053
  // Code reality: only relevant for 'implementing'
845
1054
  newStatus === 'implementing' && !reverseTransition && effectiveGatePath
@@ -851,7 +1060,7 @@ export async function handleUpdateStatus(params, server) {
851
1060
  : Promise.resolve(null),
852
1061
  // Done gates: only relevant for 'done'
853
1062
  newStatus === 'done' && !shouldSkipGateForExplicitLegacyHarness('evidence')
854
- ? checkDoneGates(spec, specId, projectId, effectiveGatePath, params.force, params.forceStatusReason ?? params.reason ?? 'No force reason provided')
1063
+ ? checkDoneGates(spec, specId, projectId, effectiveGatePath, params.force, params.forceStatusReason ?? params.reason ?? 'No force reason provided', undefined, resolvedImplementationReviewDigest)
855
1064
  : Promise.resolve(null),
856
1065
  ]);
857
1066
  // Process code reality result
@@ -933,15 +1142,23 @@ export async function handleUpdateStatus(params, server) {
933
1142
  return specReviewWriteError;
934
1143
  }
935
1144
  }
936
- // Explicit actuals are preserved verbatim. Provider usage and cost are never inferred.
937
- const resolvedActuals = actuals;
1145
+ // Explicit actuals are preserved verbatim. Provider usage and cost are
1146
+ // never inferred — synthesizeActuals only fills gaps the caller left
1147
+ // unmeasured (SPEC-1356 T-A2).
1148
+ const resolvedActuals = newStatus === 'done' || actuals !== undefined
1149
+ ? synthesizeActuals(actuals, new Date().toISOString())
1150
+ : undefined;
938
1151
  // Run only fast, read-only transition checks before persistence.
939
1152
  const implActions = newStatus === 'implementing'
940
- ? await runImplementingActions(projectId, specId, { deferSideEffects: true })
1153
+ ? await runImplementingActions(projectId, specId, {
1154
+ deferSideEffects: true,
1155
+ projectPath: effectiveGatePath,
1156
+ })
941
1157
  : null;
942
1158
  const doneActions = newStatus === 'done'
943
1159
  ? await runDoneActions(projectId, specId, spec.gitBranch, {
944
1160
  deferSideEffects: true,
1161
+ projectPath: effectiveGatePath,
945
1162
  })
946
1163
  : null;
947
1164
  const trigger = reverseTransition
@@ -1089,13 +1306,20 @@ export async function handleUpdateStatus(params, server) {
1089
1306
  hasProjectPath: Boolean(effectiveGatePath),
1090
1307
  shouldAutoStage,
1091
1308
  });
1309
+ const transitionRootBinding = plannedBackgroundActions.length > 0 && effectiveGatePath
1310
+ ? computeTransitionRootBinding(effectiveGatePath)
1311
+ : undefined;
1312
+ const qualityWarningsToWrite = newStatus === 'approved'
1313
+ ? [...readinessGate.qualityWarnings, ...formatGate.qualityWarnings]
1314
+ : [];
1092
1315
  const persistence = await commitTransitionWithNarrowLock({
1093
- projectPath: effectiveProjectPath || projectId,
1316
+ projectPath: effectiveGatePath ?? projectId,
1094
1317
  projectId,
1095
1318
  specId,
1096
1319
  newStatus,
1097
1320
  originalStatus,
1098
1321
  expectedSpec: spec,
1322
+ qualityWarnings: qualityWarningsToWrite,
1099
1323
  options: {
1100
1324
  trigger: trigger,
1101
1325
  actor,
@@ -1103,6 +1327,7 @@ export async function handleUpdateStatus(params, server) {
1103
1327
  reason: params.reason,
1104
1328
  updates: nonStatusUpdates,
1105
1329
  pendingBackgroundActions: plannedBackgroundActions,
1330
+ transitionRootBinding,
1106
1331
  expectedSpec: spec,
1107
1332
  },
1108
1333
  ...(doneFreshnessLease ? { freshnessLease: doneFreshnessLease } : {}),
@@ -1114,6 +1339,9 @@ export async function handleUpdateStatus(params, server) {
1114
1339
  const frontmatterSyncWarnings = [];
1115
1340
  const transitionId = transitionRecord.transitionId;
1116
1341
  const committedAt = transitionRecord.timestamp;
1342
+ if (newStatus === 'done') {
1343
+ await runTransitionMaintenance(transitionProjectPath);
1344
+ }
1117
1345
  await appendTransitionEvent({
1118
1346
  projectId,
1119
1347
  specId,
@@ -1145,29 +1373,6 @@ export async function handleUpdateStatus(params, server) {
1145
1373
  reportClassifiedDegradation('REOPEN_EVENT_APPEND', err);
1146
1374
  });
1147
1375
  }
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
1376
  // SPEC-723: Record terminal transition in hash-chained log (best-effort, fire-and-forget)
1172
1377
  // SPEC-734: Thread enriched payload — sessionId, modelId, gateResults
1173
1378
  if (newStatus === 'done' || newStatus === 'discarded') {
@@ -1197,6 +1402,7 @@ export async function handleUpdateStatus(params, server) {
1197
1402
  modelId: params.modelId,
1198
1403
  gateResults: Object.keys(terminalGateResults).length > 0 ? terminalGateResults : undefined,
1199
1404
  meta: transitionEvidenceMeta,
1405
+ projectPath: effectiveGatePath,
1200
1406
  }).catch((error) => {
1201
1407
  /* reliability-optional: TERMINAL_VALIDATION_REPORT — canonical receipt is authoritative */
1202
1408
  reportClassifiedDegradation('TERMINAL_VALIDATION_REPORT', error);
@@ -1335,6 +1541,14 @@ export async function handleUpdateStatus(params, server) {
1335
1541
  isError: true,
1336
1542
  };
1337
1543
  }
1544
+ finally {
1545
+ if (doneFreshnessLease) {
1546
+ await closeValidationFreshnessLease(doneFreshnessLease).catch((error) => {
1547
+ /* reliability-cleanup: freshness expiry remains the recovery boundary */
1548
+ reportClassifiedDegradation('VALIDATION_FRESHNESS_CHECK_FAILED', error);
1549
+ });
1550
+ }
1551
+ }
1338
1552
  })();
1339
1553
  }); // end trackCost
1340
1554
  }