@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
@@ -0,0 +1,57 @@
1
+ import { readdir, unlink } from 'node:fs/promises';
2
+ import { basename, dirname } from 'node:path';
3
+ import { reportClassifiedDegradation } from '../../errors/classified-degradation.js';
4
+ import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
5
+ /** Publish one complete canonical status snapshot with same-directory atomic replacement. */
6
+ export function atomicStatusWrite(statusPath, status) {
7
+ return atomicWriteFile(statusPath, JSON.stringify(status, null, 2));
8
+ }
9
+ function processIsDead(pid) {
10
+ try {
11
+ process.kill(pid, 0);
12
+ return false;
13
+ }
14
+ catch (error) {
15
+ if (error.code === 'ESRCH') {
16
+ return true;
17
+ }
18
+ reportClassifiedDegradation('STATUS_PROCESS_PROBE', error);
19
+ return false;
20
+ }
21
+ }
22
+ /** Remove only default atomic-write temporaries whose local owner process is dead. */
23
+ export async function cleanupDeadStatusTemps(statusPath) {
24
+ const directory = dirname(statusPath);
25
+ const prefix = `${basename(statusPath)}.tmp.`;
26
+ let entries;
27
+ try {
28
+ entries = await readdir(directory, { withFileTypes: true });
29
+ }
30
+ catch (error) {
31
+ if (error.code === 'ENOENT') {
32
+ return 0;
33
+ }
34
+ throw error;
35
+ }
36
+ let removed = 0;
37
+ for (const entry of entries) {
38
+ if (!entry.isFile() || !entry.name.startsWith(prefix)) {
39
+ continue;
40
+ }
41
+ const ownerPid = /^(\d+)\.[0-9a-f]{8}$/.exec(entry.name.slice(prefix.length))?.[1];
42
+ if (ownerPid === undefined || !processIsDead(Number(ownerPid))) {
43
+ continue;
44
+ }
45
+ try {
46
+ await unlink(`${directory}/${entry.name}`);
47
+ removed += 1;
48
+ }
49
+ catch (error) {
50
+ if (error.code !== 'ENOENT') {
51
+ throw error;
52
+ }
53
+ }
54
+ }
55
+ return removed;
56
+ }
57
+ //# sourceMappingURL=atomic-status-write.js.map
@@ -1,10 +1,10 @@
1
1
  // storage/status-store/file-lock.ts — SPEC-753: File-lock for status.json / pending.json writes
2
2
  //
3
- // Uses an in-memory async queue (same pattern as file-mutex.ts) with a 5-second timeout.
4
- // This prevents the concurrent-write race that corrupted status.json on 2026-04-24.
5
- //
6
- // Why not proper-lockfile? proper-lockfile requires process-level coordination; our server
7
- // is single-process, so in-memory serialization is sufficient and has zero dep overhead.
3
+ // Uses a local FIFO for fair in-process ordering and the existing SQLite fenced lease for
4
+ // exclusion across CLI, MCP, durable-worker, and background-side-effect processes.
5
+ import { resolve } from 'node:path';
6
+ import { acquireLock, releaseLock } from '../../engine/safety/file-mutex.js';
7
+ import { cleanupDeadStatusTemps } from './atomic-status-write.js';
8
8
  const lockQueues = new Map();
9
9
  export const STATUS_LOCK_TIMEOUT_MS = 5_000;
10
10
  export const STATUS_LOCK_ERROR_CODE = 'status_lock_timeout';
@@ -14,11 +14,12 @@ export const STATUS_LOCK_ERROR_CODE = 'status_lock_timeout';
14
14
  * Throws with code `status_lock_timeout` if the lock is not acquired within 5 s.
15
15
  */
16
16
  export async function withStatusLock(filePath, fn) {
17
+ const startedAt = Date.now();
17
18
  // Race the wait for the previous lock against the timeout
18
19
  const prev = lockQueues.get(filePath) ?? Promise.resolve();
19
20
  let release;
20
- const next = new Promise((resolve) => {
21
- release = resolve;
21
+ const next = new Promise((resolveQueue) => {
22
+ release = resolveQueue;
22
23
  });
23
24
  lockQueues.set(filePath, next);
24
25
  let timeout;
@@ -52,14 +53,32 @@ export async function withStatusLock(filePath, fn) {
52
53
  clearTimeout(timeout);
53
54
  }
54
55
  }
56
+ let lease;
55
57
  try {
58
+ const remainingMs = Math.max(1, STATUS_LOCK_TIMEOUT_MS - (Date.now() - startedAt));
59
+ try {
60
+ lease = await acquireLock(resolve(filePath), remainingMs);
61
+ }
62
+ catch (error) {
63
+ const lockError = new Error(`[Planu] ${STATUS_LOCK_ERROR_CODE}: could not acquire cross-process write-lock on ${filePath}`, { cause: error });
64
+ lockError.code = STATUS_LOCK_ERROR_CODE;
65
+ throw lockError;
66
+ }
67
+ await cleanupDeadStatusTemps(resolve(filePath));
56
68
  return await fn();
57
69
  }
58
70
  finally {
59
- release();
60
- // Clean up only if no newer lock has been queued
61
- if (lockQueues.get(filePath) === next) {
62
- lockQueues.delete(filePath);
71
+ try {
72
+ if (lease) {
73
+ await releaseLock(lease);
74
+ }
75
+ }
76
+ finally {
77
+ release();
78
+ // Clean up only if no newer lock has been queued
79
+ if (lockQueues.get(filePath) === next) {
80
+ lockQueues.delete(filePath);
81
+ }
63
82
  }
64
83
  }
65
84
  }
@@ -5,9 +5,10 @@
5
5
  // a file where a valid JSON document is followed by trailing content from a second
6
6
  // partial write — i.e. JSON.parse fails at non-zero offset.
7
7
  import { readFile, writeFile, rename, mkdir, readdir } from 'node:fs/promises';
8
- import { join, dirname } from 'node:path';
8
+ import { join } from 'node:path';
9
9
  import { reportClassifiedDegradation } from '../../errors/classified-degradation.js';
10
10
  import { PLANU_VERSION } from '../../config/version.js';
11
+ import { atomicStatusWrite } from './atomic-status-write.js';
11
12
  // ---------------------------------------------------------------------------
12
13
  // Helpers
13
14
  // ---------------------------------------------------------------------------
@@ -181,8 +182,7 @@ export async function rebuildStatusFromFrontmatters(params) {
181
182
  * Use this after `rebuildStatusFromFrontmatters` to persist the rebuild.
182
183
  */
183
184
  export async function writeRebuiltStatus(statusPath, status) {
184
- await mkdir(dirname(statusPath), { recursive: true });
185
- await writeFile(statusPath, JSON.stringify(status, null, 2), 'utf-8');
185
+ await atomicStatusWrite(statusPath, status);
186
186
  }
187
187
  // ---------------------------------------------------------------------------
188
188
  // Internal: minimal YAML frontmatter parser
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Sync the `version` field in status.json with the current runtime.
3
3
  * No-op if the version is already current or the file doesn't exist.
4
- * Idempotent within a session (tracks synced paths to avoid redundant I/O).
4
+ * The snapshot is always revalidated under the cross-process lease because another
5
+ * Planu process may replace it after any local observation.
5
6
  */
6
7
  export declare function syncVersionField(statusPath: string, force?: boolean): Promise<{
7
8
  synced: boolean;
@@ -1,57 +1,52 @@
1
1
  // storage/status-store/version-sync.ts — SPEC-753: Version sync at startup + per-session
2
2
  //
3
3
  // Ensures status.json.version always reflects the current runtime Planu version.
4
- import { readFile, writeFile, mkdir } from 'node:fs/promises';
5
- import { dirname } from 'node:path';
4
+ import { readFile } from 'node:fs/promises';
6
5
  import { PLANU_VERSION } from '../../config/version.js';
7
- /** Track which status.json paths have already been synced this server session. */
8
- const syncedPaths = new Set();
6
+ import { atomicStatusWrite } from './atomic-status-write.js';
7
+ import { withStatusLock } from './file-lock.js';
9
8
  /**
10
9
  * Sync the `version` field in status.json with the current runtime.
11
10
  * No-op if the version is already current or the file doesn't exist.
12
- * Idempotent within a session (tracks synced paths to avoid redundant I/O).
11
+ * The snapshot is always revalidated under the cross-process lease because another
12
+ * Planu process may replace it after any local observation.
13
13
  */
14
14
  export async function syncVersionField(statusPath, force = false) {
15
- if (!force && syncedPaths.has(statusPath)) {
16
- return { synced: false };
17
- }
18
- let raw;
19
- try {
20
- raw = await readFile(statusPath, 'utf-8');
21
- }
22
- catch (error) {
23
- if (error.code === 'ENOENT') {
24
- syncedPaths.add(statusPath);
25
- return { synced: false };
15
+ void force;
16
+ return withStatusLock(statusPath, async () => {
17
+ let raw;
18
+ try {
19
+ raw = await readFile(statusPath, 'utf-8');
20
+ }
21
+ catch (error) {
22
+ if (error.code === 'ENOENT') {
23
+ return { synced: false };
24
+ }
25
+ throw error;
26
+ }
27
+ let parsed;
28
+ try {
29
+ const p = JSON.parse(raw);
30
+ if (typeof p !== 'object' || p === null || Array.isArray(p)) {
31
+ return { synced: false };
32
+ }
33
+ parsed = p;
34
+ }
35
+ catch (error) {
36
+ throw new Error(`Status file is corrupt: ${statusPath}`, { cause: error });
26
37
  }
27
- throw error;
28
- }
29
- let parsed;
30
- try {
31
- const p = JSON.parse(raw);
32
- if (typeof p !== 'object' || p === null || Array.isArray(p)) {
33
- syncedPaths.add(statusPath);
38
+ const currentVersion = typeof parsed.version === 'string' ? parsed.version : undefined;
39
+ if (currentVersion === PLANU_VERSION) {
34
40
  return { synced: false };
35
41
  }
36
- parsed = p;
37
- }
38
- catch (error) {
39
- throw new Error(`Status file is corrupt: ${statusPath}`, { cause: error });
40
- }
41
- const currentVersion = typeof parsed.version === 'string' ? parsed.version : undefined;
42
- if (currentVersion === PLANU_VERSION) {
43
- syncedPaths.add(statusPath);
44
- return { synced: false };
45
- }
46
- parsed.version = PLANU_VERSION;
47
- parsed.updatedAt = new Date().toISOString();
48
- await mkdir(dirname(statusPath), { recursive: true });
49
- await writeFile(statusPath, JSON.stringify(parsed, null, 2), 'utf-8');
50
- syncedPaths.add(statusPath);
51
- return { synced: true, from: currentVersion, to: PLANU_VERSION };
42
+ parsed.version = PLANU_VERSION;
43
+ parsed.updatedAt = new Date().toISOString();
44
+ await atomicStatusWrite(statusPath, parsed);
45
+ return { synced: true, from: currentVersion, to: PLANU_VERSION };
46
+ });
52
47
  }
53
48
  /** Clear the session sync cache — TEST USE ONLY. */
54
49
  export function clearVersionSyncCache() {
55
- syncedPaths.clear();
50
+ // Compatibility no-op: correctness requires revalidating every snapshot under the lease.
56
51
  }
57
52
  //# sourceMappingURL=version-sync.js.map
@@ -1,9 +1,5 @@
1
- import type { UsageLog, UsageEvent, TrialState } from '../types/index.js';
1
+ import type { UsageLog, UsageEvent } from '../types/index.js';
2
2
  export declare function getUsageLog(): Promise<UsageLog>;
3
3
  export declare function appendUsageEvent(event: UsageEvent): Promise<void>;
4
4
  export declare function rotateUsageLog(): Promise<void>;
5
- export declare function getTrialState(): Promise<TrialState | null>;
6
- export declare function ensureTrialState(): Promise<TrialState>;
7
- export declare function getDailyCallCount(date: string): Promise<number>;
8
- export declare function getLastCallTimestamp(): Promise<string | null>;
9
5
  //# sourceMappingURL=usage-store.d.ts.map
@@ -10,9 +10,6 @@ const MAX_EVENTS = 10_000;
10
10
  function usageFile() {
11
11
  return `${globalDataDir()}/usage.json`;
12
12
  }
13
- function firstRunFile() {
14
- return `${globalDataDir()}/first-run.json`;
15
- }
16
13
  // ---------------------------------------------------------------------------
17
14
  // Usage log (data/global/usage.json)
18
15
  // ---------------------------------------------------------------------------
@@ -42,36 +39,4 @@ export async function appendUsageEvent(event) {
42
39
  export async function rotateUsageLog() {
43
40
  await withFileLock(usageFile(), rotateUsageLogLocked);
44
41
  }
45
- // ---------------------------------------------------------------------------
46
- // Trial state (data/global/first-run.json)
47
- // ---------------------------------------------------------------------------
48
- export async function getTrialState() {
49
- return readJson(firstRunFile(), null);
50
- }
51
- export async function ensureTrialState() {
52
- const existing = await getTrialState();
53
- if (existing !== null) {
54
- return existing;
55
- }
56
- const state = { firstRunAt: new Date().toISOString() };
57
- await writeJson(firstRunFile(), state);
58
- return state;
59
- }
60
- // ---------------------------------------------------------------------------
61
- // Computed helpers (derived from usage.json — no extra file)
62
- // ---------------------------------------------------------------------------
63
- export async function getDailyCallCount(date) {
64
- const log = await getUsageLog();
65
- return log.events.filter((event) => event.timestamp.startsWith(date)).length;
66
- }
67
- export async function getLastCallTimestamp() {
68
- const log = await getUsageLog();
69
- if (log.events.length === 0) {
70
- return null;
71
- }
72
- const last = log.events[log.events.length - 1];
73
- /* v8 ignore start -- always defined when length > 0 */
74
- return last?.timestamp ?? null;
75
- /* v8 ignore stop */
76
- }
77
42
  //# sourceMappingURL=usage-store.js.map
@@ -9,17 +9,17 @@ export interface PinVersionInput {
9
9
  }
10
10
  /**
11
11
  * Checks npm registry and local config for update status.
12
- * Free tool no license check.
12
+ * Checks update status without changing local state.
13
13
  */
14
14
  export declare function handleCheckUpdateStatus(): Promise<ToolResult>;
15
15
  /**
16
16
  * Resolves a verified release and pins the MCP config to that exact version.
17
- * Free tool always benefits the user.
17
+ * Enables update checks for the local installation.
18
18
  */
19
19
  export declare function handleEnableAutoUpdate(args: EnableAutoUpdateInput): Promise<ToolResult>;
20
20
  /**
21
21
  * Pins Planu to a specific version in the MCP config.
22
- * Pro tool requires license.
22
+ * Pins the local installation to an explicit version.
23
23
  */
24
24
  export declare function handlePinVersion(args: PinVersionInput): Promise<ToolResult>;
25
25
  //# sourceMappingURL=auto-update-handler.d.ts.map
@@ -7,7 +7,7 @@ import { jsonResult, errText } from './response-helpers.js';
7
7
  // ---------------------------------------------------------------------------
8
8
  /**
9
9
  * Checks npm registry and local config for update status.
10
- * Free tool no license check.
10
+ * Checks update status without changing local state.
11
11
  */
12
12
  export async function handleCheckUpdateStatus() {
13
13
  try {
@@ -20,7 +20,7 @@ export async function handleCheckUpdateStatus() {
20
20
  }
21
21
  /**
22
22
  * Resolves a verified release and pins the MCP config to that exact version.
23
- * Free tool always benefits the user.
23
+ * Enables update checks for the local installation.
24
24
  */
25
25
  export async function handleEnableAutoUpdate(args) {
26
26
  const confirm = args.confirm ?? false;
@@ -35,7 +35,7 @@ export async function handleEnableAutoUpdate(args) {
35
35
  }
36
36
  /**
37
37
  * Pins Planu to a specific version in the MCP config.
38
- * Pro tool requires license.
38
+ * Pins the local installation to an explicit version.
39
39
  */
40
40
  export async function handlePinVersion(args) {
41
41
  if (!args.version || args.version.trim() === '') {
@@ -1,8 +1,9 @@
1
1
  import type { FailureScenario, Spec, ProjectKnowledge } from '../../types/index.js';
2
+ import { type ChallengeCapabilities } from './scenarios-utils.js';
2
3
  export { EXTERNAL_CALL_KEYWORDS, VALIDATION_KEYWORDS, RATE_LIMIT_KEYWORDS, CONCURRENCY_KEYWORDS, SAGA_KEYWORDS, hasExternalCallSignal, hasValidationSignal, hasRateLimitSignal, hasConcurrencySignal, hasSagaSignal, generateValidationBoundaryScenarios, generateCascadeFailureScenarios, } from './resilience-challenge-scenarios-a.js';
3
4
  export { generateRateLimitBypassScenarios, generateConcurrencyScenarios, generateSagaFailureScenarios, } from './resilience-challenge-scenarios-b.js';
4
5
  /**
5
6
  * Main entry: generate all SPEC-034 resilience/concurrency challenges for a spec.
6
7
  */
7
- export declare function generateResilienceChallengeScenarios(spec: Spec, specContent: string, knowledge: ProjectKnowledge): FailureScenario[];
8
+ export declare function generateResilienceChallengeScenarios(spec: Spec, specContent: string, knowledge: ProjectKnowledge, capabilities?: ChallengeCapabilities): FailureScenario[];
8
9
  //# sourceMappingURL=resilience-challenge-scenarios.d.ts.map
@@ -2,32 +2,35 @@
2
2
  // Barrel re-export for resilience/concurrency challenge scenarios.
3
3
  // Signal detectors + validation + cascade: resilience-challenge-scenarios-a.ts
4
4
  // Rate limit + concurrency + saga: resilience-challenge-scenarios-b.ts
5
- import { hasValidationSignal, hasExternalCallSignal, hasRateLimitSignal, hasConcurrencySignal, hasSagaSignal, generateValidationBoundaryScenarios, generateCascadeFailureScenarios, } from './resilience-challenge-scenarios-a.js';
5
+ import { generateValidationBoundaryScenarios, generateCascadeFailureScenarios, } from './resilience-challenge-scenarios-a.js';
6
6
  import { generateRateLimitBypassScenarios, generateConcurrencyScenarios, generateSagaFailureScenarios, } from './resilience-challenge-scenarios-b.js';
7
+ import { detectChallengeCapabilities } from './scenarios-utils.js';
7
8
  export { EXTERNAL_CALL_KEYWORDS, VALIDATION_KEYWORDS, RATE_LIMIT_KEYWORDS, CONCURRENCY_KEYWORDS, SAGA_KEYWORDS, hasExternalCallSignal, hasValidationSignal, hasRateLimitSignal, hasConcurrencySignal, hasSagaSignal, generateValidationBoundaryScenarios, generateCascadeFailureScenarios, } from './resilience-challenge-scenarios-a.js';
8
9
  export { generateRateLimitBypassScenarios, generateConcurrencyScenarios, generateSagaFailureScenarios, } from './resilience-challenge-scenarios-b.js';
9
10
  /**
10
11
  * Main entry: generate all SPEC-034 resilience/concurrency challenges for a spec.
11
12
  */
12
- export function generateResilienceChallengeScenarios(spec, specContent, knowledge) {
13
+ export function generateResilienceChallengeScenarios(spec, specContent, knowledge, capabilities) {
13
14
  const scenarios = [];
14
- if (hasValidationSignal(spec, specContent)) {
15
+ const resolvedCapabilities = capabilities ?? detectChallengeCapabilities(spec, specContent);
16
+ if (resolvedCapabilities.validation &&
17
+ (resolvedCapabilities.userInput || resolvedCapabilities.networkApi)) {
15
18
  scenarios.push(...generateValidationBoundaryScenarios(spec));
16
19
  }
17
- if (hasExternalCallSignal(spec, specContent)) {
20
+ if (resolvedCapabilities.externalService || resolvedCapabilities.payment) {
18
21
  scenarios.push(...generateCascadeFailureScenarios(spec));
19
22
  }
20
- if (hasRateLimitSignal(spec, specContent)) {
23
+ if (resolvedCapabilities.rateLimit) {
21
24
  scenarios.push(...generateRateLimitBypassScenarios(spec));
22
25
  }
23
- if (hasConcurrencySignal(spec, specContent)) {
26
+ if (resolvedCapabilities.concurrency) {
24
27
  // `concurrencyModel` may be present on enriched knowledge objects (ValidationResilienceProfile mixin)
25
28
  // but is not declared on the base ProjectKnowledge interface — intentional duck-type access.
26
29
  // ProjectKnowledge has no index signature, so `as unknown` intermediate is required.
27
30
  const concurrencyModel = knowledge.concurrencyModel;
28
31
  scenarios.push(...generateConcurrencyScenarios(spec, concurrencyModel ?? 'unknown'));
29
32
  }
30
- if (hasSagaSignal(spec, specContent)) {
33
+ if (resolvedCapabilities.saga) {
31
34
  scenarios.push(...generateSagaFailureScenarios(spec));
32
35
  }
33
36
  return scenarios;
@@ -75,7 +75,7 @@ export function collectCapabilityScenarios(input) {
75
75
  'concurrency',
76
76
  'saga',
77
77
  ])) {
78
- scenarios.push(...generateResilienceChallengeScenarios(spec, specContent, knowledge));
78
+ scenarios.push(...generateResilienceChallengeScenarios(spec, specContent, knowledge, capabilities));
79
79
  }
80
80
  const agileConfig = knowledge.agileConfig;
81
81
  if (agileConfig?.process === 'xp' && capabilities.xp) {
@@ -4,7 +4,7 @@ const WRITE_SIGNALS = [
4
4
  /\b(transaction|persist|database write|save record|create record|update record|delete record)\b/i,
5
5
  /\b(order|checkout|submit form|mutation|multi-step write)\b/i,
6
6
  ];
7
- const CACHE_SIGNALS = [/\b(cache|real-time|realtime|concurrent|websocket|stale data)\b/i];
7
+ const CACHE_SIGNALS = [/\b(cache|cached|real-time|realtime|websocket|stale data)\b/i];
8
8
  const DUPLICATE_OPERATION_SIGNALS = [
9
9
  /\b(submit|form|checkout|order|payment|create record|mutation|button click)\b/i,
10
10
  /\bduplicate\s+(?:request|submission|operation|record)\b/i,
@@ -29,5 +29,5 @@ export interface ChallengeCapabilities {
29
29
  export declare function contentMentions(content: string, keywords: string[]): boolean;
30
30
  export declare function detectChallengeCapabilities(spec: Spec, specContent: string): ChallengeCapabilities;
31
31
  export declare function hasAnyChallengeCapability(capabilities: ChallengeCapabilities, names: (keyof ChallengeCapabilities)[]): boolean;
32
- export { hasAffirmedMatch, hasAnyAffirmedMatch, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
32
+ export { hasAffirmedMatch, hasAnyAffirmedMatch, stripMetaAnalysisText, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
33
33
  //# sourceMappingURL=scenarios-utils.d.ts.map
@@ -1,5 +1,5 @@
1
1
  // tools/challenge-spec/scenarios-utils.ts — Shared utilities for scenario generators
2
- import { hasAnyAffirmedMatch } from '../../engine/text-signal-boundaries.js';
2
+ import { hasAnyAffirmedMatch, stripMetaAnalysisText, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
3
3
  const CAPABILITY_SIGNALS = {
4
4
  networkApi: [
5
5
  /(?<!compiler\s)\bapi\b(?!\s+(?:compatibility|surface|package|type))/i,
@@ -15,7 +15,7 @@ const CAPABILITY_SIGNALS = {
15
15
  /\b(?:orm|prisma|drizzle|typeorm|sequelize)\b/i,
16
16
  ],
17
17
  authentication: [
18
- /\b(?:auth(?:entication)?|login|sign[ -]?in|password|jwt|oauth2?|session\s+auth)\b/i,
18
+ /\b(?:auth(?:entication|enticate)?|login|sign[ -]?in|password|jwt|oauth2?|session\s+auth)\b/i,
19
19
  ],
20
20
  authorization: [
21
21
  /\b(?:authori[sz]ation|permission|rbac|role-based|access\s+control|resource\s+ownership|tenant)\b/i,
@@ -26,10 +26,10 @@ const CAPABILITY_SIGNALS = {
26
26
  /\binputs?\s+(?:are\s+)?validat/i,
27
27
  ],
28
28
  externalService: [
29
- /\b(?:third-party|external|remote|downstream)\s+(?:service|provider|dependency|system)\b/i,
29
+ /\b(?:third-party|external|remote|downstream)\s+(?:service|provider|dependency|system|api)\b/i,
30
30
  /\bwebhook\s+(?:provider|delivery|endpoint|request)\b/i,
31
31
  ],
32
- payment: [/\b(?:payment|billing|checkout|invoice|stripe|paypal)\b/i],
32
+ payment: [/\b(?:payment|billing|invoice|stripe|paypal)\b/i],
33
33
  fileTransfer: [
34
34
  /\b(?:(?:large|binary|uploaded)\s+)?(?:file|image|media|attachment)\s+(?:upload|download|transfer|stream)\b/i,
35
35
  /\b(?:upload|download|stream)(?:ed|ing|s)?\s+(?:a\s+)?(?:(?:large|binary)\s+)?(?:file|image|media|attachment)\b/i,
@@ -43,8 +43,8 @@ const CAPABILITY_SIGNALS = {
43
43
  ],
44
44
  dataWrite: [
45
45
  /\b(?:database\s+transaction|persist(?:s|ed|ing)?\s+(?:record|entity|data)|write(?:s|ing)?\s+(?:record|entity|database))\b/i,
46
- /\b(?:state-changing|data\s+mutation|atomic\s+database\s+write)\b/i,
47
- /\b(?:transaction|atomic\s+write|update\s+(?:record|state)|modify\s+(?:record|state))\b/i,
46
+ /\b(?:state-changing|data\s+mutation|atomic\s+database\s+writes?)\b/i,
47
+ /\b(?:transaction|atomic\s+writes?|update\s+(?:record|state)|modify\s+(?:record|state))\b/i,
48
48
  ],
49
49
  cache: [/\b(?:cache|caching|cached\s+(?:data|response)|redis)\b/i],
50
50
  duplicateOperation: [
@@ -52,6 +52,7 @@ const CAPABILITY_SIGNALS = {
52
52
  ],
53
53
  concurrency: [
54
54
  /\b(?:concurren|race\s+condition|shared\s+state|parallel\s+write|atomic\s+increment|optimistic\s+lock)/i,
55
+ /\b(?:shared\b(?:\s+\w+){0,2}\s+(?:cache|resource|pool)|resource\s+(?:lock|pool))\b/i,
55
56
  /\b(?:counter|increment|balance|stock|quantity|vote|status\s+(?:update|transition))\b/i,
56
57
  /\b(?:file\s+upload|resource\s+manager|search\s+feed|list\s+endpoint|global\s+cache|session\s+state|cart\s+state|draft\s+state)\b/i,
57
58
  ],
@@ -76,8 +77,12 @@ const CAPABILITY_SIGNALS = {
76
77
  agent: [/\b(?:ai\s+agent|agent\s+contract|llm\s+agent|tool\s+call|autonomous\s+agent)\b/i],
77
78
  ddd: [/\b(?:domain-driven\s+design|ddd\s+aggregate|aggregate\s+root|bounded\s+context)\b/i],
78
79
  xp: [/\b(?:pair\s+programming|extreme\s+programming|xp\s+pair)\b/i],
79
- validation: [/\b(?:input|payload|request|schema)\s+validat/i, /\b(?:zod|joi|json\s+schema)\b/i],
80
- rateLimit: [/\b(?:rate[ -]?limit|throttl|request\s+quota)\b/i],
80
+ validation: [
81
+ /\b(?:input|payload|request|schema)\s+validat/i,
82
+ /\bvalidat(?:e|es|ed|ing|ion)\b/i,
83
+ /\b(?:zod|joi|json\s+schema)\b/i,
84
+ ],
85
+ rateLimit: [/\b(?:rate[ -]?limit(?:ing|ed|s)?|throttl|request\s+quota)\b/i],
81
86
  saga: [/\b(?:saga|compensating\s+transaction|distributed\s+transaction)\b/i],
82
87
  platform: [
83
88
  /\b(?:smart\s+contract|solidity|discord\s+bot|telegram\s+bot|iot\s+device|firmware|infrastructure\s+as\s+code|terraform|machine\s+learning\s+model)\b/i,
@@ -88,19 +93,38 @@ function keywordPattern(keyword) {
88
93
  const suffix = /^[a-z0-9]+$/i.test(keyword) && keyword.length > 3 ? '[a-z0-9_-]*' : '';
89
94
  return new RegExp(`\\b${escaped}${suffix}\\b`, 'i');
90
95
  }
96
+ function whitespaceFlexiblePattern(text) {
97
+ return text
98
+ .trim()
99
+ .split(/\s+/)
100
+ .map((token) => token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
101
+ .join('\\s+');
102
+ }
103
+ function stripReaderPrefix(spec, specContent) {
104
+ const readerPrefix = [spec.title, spec.type, spec.scope, spec.target, spec.tags.join(' ')]
105
+ .filter(Boolean)
106
+ .join(' ');
107
+ const prefixPattern = whitespaceFlexiblePattern(readerPrefix);
108
+ if (prefixPattern.length === 0) {
109
+ return specContent;
110
+ }
111
+ return specContent.replace(new RegExp(`^\\s*${prefixPattern}(?=\\s|$)`, 'u'), '').trimStart();
112
+ }
91
113
  /** Boundary-aware, affirmative replacement for advisory substring checks. */
92
114
  export function contentMentions(content, keywords) {
93
115
  return hasAnyAffirmedMatch(content, keywords.map(keywordPattern));
94
116
  }
95
117
  export function detectChallengeCapabilities(spec, specContent) {
96
- const text = `${spec.title}\n${spec.tags.join(' ')}\n${specContent}`;
118
+ const contractSource = stripReaderPrefix(spec, specContent);
119
+ const source = contractSource.trim().length > 0 ? contractSource : spec.title;
120
+ const contract = stripMetaAnalysisText(stripNonContractText(source));
97
121
  return Object.fromEntries(Object.entries(CAPABILITY_SIGNALS).map(([capability, patterns]) => [
98
122
  capability,
99
- hasAnyAffirmedMatch(text, patterns),
123
+ hasAnyAffirmedMatch(contract, patterns),
100
124
  ]));
101
125
  }
102
126
  export function hasAnyChallengeCapability(capabilities, names) {
103
127
  return names.some((name) => capabilities[name]);
104
128
  }
105
- export { hasAffirmedMatch, hasAnyAffirmedMatch, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
129
+ export { hasAffirmedMatch, hasAnyAffirmedMatch, stripMetaAnalysisText, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
106
130
  //# sourceMappingURL=scenarios-utils.js.map
@@ -19,6 +19,7 @@ import { analyzeMinimalImplementation, loadMinimalImplementationPolicy, } from '
19
19
  import { detectChallengeCapabilities, } from './challenge-spec/scenarios-utils.js';
20
20
  import { collectCapabilityScenarios } from './challenge-spec/scenario-collector.js';
21
21
  import { buildChallengeReport, parseChallengeResolutionEvidence, } from './challenge-spec/challenge-report.js';
22
+ import { extractListItems, findMarkdownSectionRange, } from '../engine/spec-format/markdown-sections.js';
22
23
  const ALL_FOCUS_AREAS = [
23
24
  'failures',
24
25
  'concurrency',
@@ -150,11 +151,10 @@ export async function handleChallengeSpec(args, server) {
150
151
  failureScenarios.push(...collectCapabilityScenarios({ spec, specContent, knowledge, focusAreas, capabilities }));
151
152
  // SPEC-612: Check for contradictions between outOfScope and acceptance criteria
152
153
  if (spec.outOfScope !== undefined && spec.outOfScope.length > 0) {
153
- const criteriaTexts = specContent
154
- .split('\n')
155
- .filter((line) => line.trim().startsWith('-') || line.trim().startsWith('*'))
156
- .map((line) => line.replace(/^[\s\-*]+/, '').trim())
157
- .filter((line) => line.length > 0);
154
+ const criteriaRange = findMarkdownSectionRange(specContent, 'Acceptance Criteria');
155
+ const criteriaTexts = criteriaRange === null
156
+ ? []
157
+ : extractListItems(specContent.slice(criteriaRange.contentStart, criteriaRange.end));
158
158
  const contradictionResult = checkScopeContradictions(spec.outOfScope, criteriaTexts);
159
159
  for (const c of contradictionResult.contradictions) {
160
160
  failureScenarios.push({
@@ -118,7 +118,7 @@ export async function handleCheckReadiness(args) {
118
118
  scoreSpecQuality(spec),
119
119
  validateSpecFormat(spec, { readinessMode: mode, skipQuality: true }),
120
120
  ]);
121
- // SPEC-716: Ground ready flag in unified ValidationResult.
121
+ // SPEC-716: Ground ready flag in unified SpecValidationResult.
122
122
  // Only non-file-access errors from validateSpecFormat are surfaced in the
123
123
  // report — MISSING_FRONTMATTER due to file-not-found is best-effort since
124
124
  // the readiness checker already handles missing files gracefully.