@geraldmaron/construct 1.4.2 → 1.5.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 (519) hide show
  1. package/.env.example +36 -0
  2. package/README.md +41 -7
  3. package/bin/construct +1027 -64
  4. package/examples/distribution/sources/deck-one-pager.md +1 -1
  5. package/lib/acp/server.mjs +21 -7
  6. package/lib/adapters-sync.mjs +2 -1
  7. package/lib/audit-trail.mjs +185 -2
  8. package/lib/beads/auto-close.mjs +2 -1
  9. package/lib/beads/drift.mjs +2 -1
  10. package/lib/bridges/copilot-proxy.mjs +20 -5
  11. package/lib/certification/skill-inventory.mjs +10 -1
  12. package/lib/cli/approvals.mjs +147 -0
  13. package/lib/cli-commands.mjs +105 -14
  14. package/lib/comment-lint.mjs +127 -8
  15. package/lib/config/schema.mjs +6 -29
  16. package/lib/config/source-target-registry.mjs +70 -0
  17. package/lib/config/source-targets.mjs +179 -205
  18. package/lib/contracts/coverage.mjs +77 -0
  19. package/lib/contracts/validate.mjs +102 -13
  20. package/lib/contracts/violation-log.mjs +50 -4
  21. package/lib/db/migrate.mjs +69 -0
  22. package/lib/db/migrations/001_orchestration_runs.sql +9 -0
  23. package/lib/db/migrations/002_queue_provider.sql +50 -0
  24. package/lib/db/migrations/003_worker_registry.sql +17 -0
  25. package/lib/db/migrations/004_trace_events.sql +16 -0
  26. package/lib/db/migrations/005_shared_memory.sql +15 -0
  27. package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
  28. package/lib/decisions/enforced-baseline.json +0 -2
  29. package/lib/decisions/registry.mjs +3 -2
  30. package/lib/deployment/parity-contract.mjs +1 -1
  31. package/lib/deployment-mode.mjs +78 -2
  32. package/lib/diagram-export.mjs +10 -1
  33. package/lib/distill.mjs +5 -2
  34. package/lib/docs-verify.mjs +2 -1
  35. package/lib/doctor/cli.mjs +1 -0
  36. package/lib/doctor/command-on-path.mjs +24 -0
  37. package/lib/doctor/diagnosis.mjs +89 -0
  38. package/lib/doctor/embedding-health.mjs +50 -0
  39. package/lib/doctor/engine-health.mjs +93 -0
  40. package/lib/doctor/graph-validate.mjs +47 -0
  41. package/lib/doctor/index.mjs +18 -4
  42. package/lib/doctor/sidecar-providers.mjs +56 -0
  43. package/lib/doctor/watchers/consistency.mjs +93 -1
  44. package/lib/doctor/watchers/cx-budget.mjs +1 -1
  45. package/lib/doctor/watchers/graph-staleness.mjs +1 -1
  46. package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
  47. package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
  48. package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
  49. package/lib/doctor/watchers/provider-breaker.mjs +78 -0
  50. package/lib/document-export.mjs +52 -27
  51. package/lib/document-extract/docling-client.mjs +9 -5
  52. package/lib/document-extract/docling-sidecar.py +30 -0
  53. package/lib/document-extract.mjs +1 -1
  54. package/lib/document-ingest.mjs +9 -0
  55. package/lib/embed/approval-queue.mjs +184 -88
  56. package/lib/embed/authority-guard.mjs +65 -2
  57. package/lib/embed/capability-jobs.mjs +365 -0
  58. package/lib/embed/capability-lifecycle.mjs +219 -0
  59. package/lib/embed/capability-loader.mjs +303 -0
  60. package/lib/embed/capability-runtime.mjs +58 -0
  61. package/lib/embed/cli.mjs +185 -8
  62. package/lib/embed/config.mjs +4 -0
  63. package/lib/embed/daemon.mjs +125 -6
  64. package/lib/embed/demand-fetch.mjs +190 -54
  65. package/lib/embed/inbox.mjs +12 -10
  66. package/lib/embed/presets/ops-triage.mjs +236 -0
  67. package/lib/embed/presets/pm-feedback.mjs +341 -0
  68. package/lib/embed/presets/tpm.mjs +401 -0
  69. package/lib/embed/providers/jira.mjs +72 -1
  70. package/lib/embed/providers/registry.mjs +140 -33
  71. package/lib/embedded-contract/capability.mjs +4 -0
  72. package/lib/embedded-contract/execution.mjs +1 -1
  73. package/lib/embedded-contract/model-resolve.mjs +53 -3
  74. package/lib/embedded-contract/workflow-defs.mjs +48 -73
  75. package/lib/embedded-contract/workflow-invoke.mjs +144 -4
  76. package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
  77. package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
  78. package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
  79. package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
  80. package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
  81. package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
  82. package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
  83. package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
  84. package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
  85. package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
  86. package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
  87. package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
  88. package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
  89. package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
  90. package/lib/env-config.mjs +50 -9
  91. package/lib/extensions/index.mjs +27 -0
  92. package/lib/extensions/loader.mjs +120 -0
  93. package/lib/extensions/manifest-schema.mjs +141 -0
  94. package/lib/extensions/manifests/anthropic.manifest.json +12 -0
  95. package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
  96. package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
  97. package/lib/extensions/manifests/directory.manifest.json +12 -0
  98. package/lib/extensions/manifests/docling.manifest.json +37 -0
  99. package/lib/extensions/manifests/echo.manifest.json +8 -0
  100. package/lib/extensions/manifests/feedback.manifest.json +12 -0
  101. package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
  102. package/lib/extensions/manifests/github.manifest.json +52 -0
  103. package/lib/extensions/manifests/linear.manifest.json +50 -0
  104. package/lib/extensions/manifests/local.manifest.json +12 -0
  105. package/lib/extensions/manifests/ollama.manifest.json +12 -0
  106. package/lib/extensions/manifests/openai.manifest.json +12 -0
  107. package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
  108. package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
  109. package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
  110. package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
  111. package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
  112. package/lib/extensions/manifests/openrouter.manifest.json +12 -0
  113. package/lib/extensions/manifests/postgres.manifest.json +12 -0
  114. package/lib/extensions/manifests/salesforce.manifest.json +12 -0
  115. package/lib/extensions/manifests/slack.manifest.json +58 -0
  116. package/lib/extensions/manifests/whisper.manifest.json +36 -0
  117. package/lib/extensions/validate.mjs +175 -0
  118. package/lib/features.mjs +13 -9
  119. package/lib/flows/checkpoint.mjs +184 -0
  120. package/lib/flows/constants.mjs +27 -0
  121. package/lib/flows/define.mjs +146 -0
  122. package/lib/flows/engine.mjs +249 -0
  123. package/lib/flows/errors.mjs +19 -0
  124. package/lib/flows/index.mjs +27 -0
  125. package/lib/flows/joins.mjs +18 -0
  126. package/lib/flows/schema.mjs +90 -0
  127. package/lib/flows/state.mjs +31 -0
  128. package/lib/frameworks/loader.mjs +99 -0
  129. package/lib/frameworks/schema.mjs +157 -0
  130. package/lib/graph/build-from-corpus.mjs +67 -0
  131. package/lib/graph/build-from-embed.mjs +82 -0
  132. package/lib/graph/build-from-registry.mjs +164 -3
  133. package/lib/graph/cli.mjs +456 -12
  134. package/lib/graph/gap-queries.mjs +156 -0
  135. package/lib/graph/gaps.mjs +41 -0
  136. package/lib/graph/impacted.mjs +129 -0
  137. package/lib/graph/runtime-evidence.mjs +177 -0
  138. package/lib/graph/security-coverage.mjs +113 -0
  139. package/lib/graph/staleness.mjs +241 -10
  140. package/lib/graph/store.mjs +24 -7
  141. package/lib/graph/validate.mjs +161 -0
  142. package/lib/headhunt.mjs +9 -8
  143. package/lib/health-check.mjs +15 -7
  144. package/lib/hook-health.mjs +1 -1
  145. package/lib/hooks/agent-tracker.mjs +10 -4
  146. package/lib/hooks/edit-guard.mjs +3 -3
  147. package/lib/hooks/graph-impact-advisory.mjs +2 -2
  148. package/lib/hooks/guard-bash.mjs +41 -15
  149. package/lib/hooks/mcp-health-check.mjs +11 -4
  150. package/lib/hooks/model-fallback.mjs +50 -6
  151. package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
  152. package/lib/hooks/pre-compact.mjs +181 -173
  153. package/lib/hooks/rule-verifier.mjs +2 -1
  154. package/lib/hooks/session-reflect.mjs +2 -1
  155. package/lib/hooks/session-start.mjs +3 -3
  156. package/lib/host-capabilities.mjs +13 -2
  157. package/lib/host-disposition.mjs +19 -7
  158. package/lib/identity.mjs +92 -0
  159. package/lib/ingest/degraded-extract.mjs +96 -0
  160. package/lib/ingest/docling-remote.mjs +2 -1
  161. package/lib/ingest/provider-extract.mjs +7 -19
  162. package/lib/ingest/sidecar-providers.mjs +196 -0
  163. package/lib/ingest-tooling.mjs +11 -5
  164. package/lib/init-unified.mjs +55 -38
  165. package/lib/init-update.mjs +2 -1
  166. package/lib/install/legacy-global-cleanup.mjs +25 -0
  167. package/lib/intake/daemon.mjs +99 -8
  168. package/lib/intake/git-queue.mjs +110 -38
  169. package/lib/intake/queue-registry.mjs +50 -0
  170. package/lib/intake/queue.mjs +133 -17
  171. package/lib/intake/session-prelude.mjs +57 -8
  172. package/lib/integrations/intake-integrations.mjs +61 -111
  173. package/lib/intent-classifier.mjs +43 -5
  174. package/lib/libreoffice-export.mjs +8 -8
  175. package/lib/logging/rotate.mjs +5 -4
  176. package/lib/mcp/broker.mjs +332 -17
  177. package/lib/mcp/denied-store.mjs +95 -0
  178. package/lib/mcp/destructive-gate.mjs +30 -0
  179. package/lib/mcp/dispatch-envelope.mjs +199 -0
  180. package/lib/mcp/memory-bridge.mjs +2 -1
  181. package/lib/mcp/server.mjs +154 -1411
  182. package/lib/mcp/tool-definitions-memory.mjs +376 -0
  183. package/lib/mcp/tool-definitions-project.mjs +271 -0
  184. package/lib/mcp/tool-definitions-skills.mjs +311 -0
  185. package/lib/mcp/tool-definitions-workflow.mjs +398 -0
  186. package/lib/mcp/tool-definitions.mjs +25 -0
  187. package/lib/mcp/tool-registry.mjs +107 -0
  188. package/lib/mcp/tool-safety.mjs +1 -0
  189. package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
  190. package/lib/mcp/tools/orchestration-run.mjs +165 -25
  191. package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
  192. package/lib/mcp/tools/provider-write.mjs +187 -0
  193. package/lib/mcp/tools/scope.mjs +0 -3
  194. package/lib/mcp/tools/skills.mjs +1 -1
  195. package/lib/mcp/tools/storage.mjs +0 -8
  196. package/lib/mcp/transport/auth.mjs +238 -0
  197. package/lib/mcp/transport/http.mjs +136 -0
  198. package/lib/mcp/transport/mode.mjs +31 -0
  199. package/lib/mcp/transport/stdio.mjs +22 -0
  200. package/lib/mcp-catalog.json +4 -4
  201. package/lib/mcp-manager.mjs +34 -23
  202. package/lib/mcp-platform-config.mjs +31 -7
  203. package/lib/mode-capabilities.mjs +109 -0
  204. package/lib/model-router.mjs +42 -9
  205. package/lib/models/catalog.mjs +40 -1
  206. package/lib/net-guard.mjs +247 -0
  207. package/lib/observation-store.mjs +6 -2
  208. package/lib/ollama/provision-context.mjs +2 -1
  209. package/lib/opencode-config.mjs +22 -3
  210. package/lib/opencode-runtime-plugin.mjs +120 -2
  211. package/lib/oracle/actions.mjs +204 -10
  212. package/lib/oracle/cli.mjs +24 -3
  213. package/lib/oracle/dispatch.mjs +2 -1
  214. package/lib/oracle/execute.mjs +2 -2
  215. package/lib/oracle/gaps.mjs +3 -3
  216. package/lib/oracle/heartbeat.mjs +53 -0
  217. package/lib/oracle/read-model.mjs +69 -13
  218. package/lib/oracle/reconcile.mjs +3 -46
  219. package/lib/oracle/routing.mjs +37 -31
  220. package/lib/oracle/synthesize.mjs +1 -1
  221. package/lib/orchestration/classification.mjs +434 -0
  222. package/lib/orchestration/delegation-flow.mjs +132 -0
  223. package/lib/orchestration/flow-selection.mjs +418 -0
  224. package/lib/orchestration/gates.mjs +244 -0
  225. package/lib/orchestration/host-sampling.mjs +121 -0
  226. package/lib/orchestration/policy-constants.mjs +48 -0
  227. package/lib/orchestration/provider-outcome.mjs +197 -0
  228. package/lib/orchestration/readiness.mjs +114 -13
  229. package/lib/orchestration/run-store-postgres.mjs +32 -26
  230. package/lib/orchestration/run-store-sqlite.mjs +8 -14
  231. package/lib/orchestration/run-store.mjs +25 -12
  232. package/lib/orchestration/runtime.mjs +446 -39
  233. package/lib/orchestration/store.mjs +87 -12
  234. package/lib/orchestration/trace-store.mjs +125 -0
  235. package/lib/orchestration/web-capability.mjs +3 -2
  236. package/lib/orchestration/worker-runtime.mjs +162 -0
  237. package/lib/orchestration/worker.mjs +528 -89
  238. package/lib/orchestration-policy.mjs +67 -1111
  239. package/lib/packs/cli.mjs +144 -0
  240. package/lib/packs/core-pack.mjs +112 -0
  241. package/lib/packs/enablement.mjs +187 -0
  242. package/lib/packs/index.mjs +23 -0
  243. package/lib/packs/loader.mjs +176 -0
  244. package/lib/packs/manifest-schema.mjs +58 -0
  245. package/lib/packs/prompts.mjs +120 -0
  246. package/lib/packs/validate.mjs +208 -0
  247. package/lib/plugin-registry.mjs +4 -5
  248. package/lib/policy/audit-gate.mjs +42 -0
  249. package/lib/policy/consumption-budget.mjs +149 -0
  250. package/lib/policy/engine.mjs +81 -6
  251. package/lib/policy/role-authority.mjs +89 -0
  252. package/lib/prompt-composer.js +19 -3
  253. package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
  254. package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
  255. package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
  256. package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
  257. package/lib/providers/contract/adapters/github/index.mjs +38 -3
  258. package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
  259. package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
  260. package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
  261. package/lib/providers/contract/adapters/jira/manifest.json +17 -0
  262. package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
  263. package/lib/providers/contract.mjs +207 -0
  264. package/lib/providers/credential-bootstrap.mjs +7 -4
  265. package/lib/providers/credential-sources.mjs +14 -2
  266. package/lib/providers/directory/index.mjs +261 -0
  267. package/lib/providers/feedback/index.mjs +392 -0
  268. package/lib/providers/filter-audit.mjs +68 -0
  269. package/lib/providers/filter-schema.mjs +54 -0
  270. package/lib/providers/github/index.mjs +31 -0
  271. package/lib/providers/instance-config.mjs +215 -0
  272. package/lib/providers/op-locate.mjs +96 -0
  273. package/lib/providers/op-run.mjs +64 -9
  274. package/lib/providers/registry.mjs +26 -3
  275. package/lib/providers/secret-audit-wiring.mjs +6 -0
  276. package/lib/providers/secret-resolver.mjs +240 -23
  277. package/lib/publish-template.mjs +6 -3
  278. package/lib/publish-tooling.mjs +4 -0
  279. package/lib/publish.mjs +32 -4
  280. package/lib/queue/pg-queue.mjs +395 -0
  281. package/lib/reflect.mjs +2 -1
  282. package/lib/registry/assemble.mjs +28 -2
  283. package/lib/registry/consolidation.mjs +8 -1
  284. package/lib/registry/custom-scaffold.mjs +200 -0
  285. package/lib/registry/custom-schema.mjs +137 -0
  286. package/lib/registry/loader.mjs +8 -3
  287. package/lib/registry/manifests/format-engines.default.json +15 -0
  288. package/lib/registry/manifests/surface-map.default.json +46 -0
  289. package/lib/registry/retired-paths.mjs +1 -0
  290. package/lib/registry/surface-map.mjs +100 -50
  291. package/lib/render-visual-check.mjs +240 -0
  292. package/lib/resources/budget.mjs +40 -17
  293. package/lib/roles/flavor-bindings.mjs +36 -14
  294. package/lib/roles/gateway.mjs +15 -8
  295. package/lib/roots.mjs +107 -0
  296. package/lib/runtime/uv-bootstrap.mjs +32 -6
  297. package/lib/runtime/whisper-bootstrap.mjs +11 -3
  298. package/lib/runtime-env.mjs +5 -2
  299. package/lib/scheduler/index.mjs +8 -20
  300. package/lib/schema-infer.mjs +31 -2
  301. package/lib/scopes/lifecycle.mjs +29 -25
  302. package/lib/scopes/loader.mjs +49 -12
  303. package/lib/scopes/teams.mjs +1 -1
  304. package/lib/security/ingest-boundary.mjs +80 -0
  305. package/lib/security/recall-wrapper.mjs +99 -0
  306. package/lib/security/trust.mjs +132 -0
  307. package/lib/service-manager.mjs +38 -19
  308. package/lib/setup.mjs +41 -23
  309. package/lib/skills-apply.mjs +4 -2
  310. package/lib/specialists/postconditions.mjs +2 -2
  311. package/lib/state-root.mjs +147 -0
  312. package/lib/status.mjs +597 -6
  313. package/lib/storage/admin.mjs +77 -5
  314. package/lib/storage/backend-registry.mjs +73 -0
  315. package/lib/storage/backend.mjs +63 -9
  316. package/lib/storage/embeddings-openai.mjs +12 -2
  317. package/lib/storage/hybrid-query.mjs +11 -3
  318. package/lib/storage/retrieval-hardening.mjs +384 -0
  319. package/lib/storage/shared-memory.mjs +158 -0
  320. package/lib/storage/vector-client.mjs +69 -2
  321. package/lib/team/health.mjs +72 -0
  322. package/lib/telemetry/backends/local.mjs +9 -3
  323. package/lib/telemetry/client.mjs +3 -7
  324. package/lib/template-registry.mjs +10 -12
  325. package/lib/tenant/context.mjs +82 -0
  326. package/lib/tenant/isolation.mjs +129 -0
  327. package/lib/test-corpus-inventory.mjs +103 -2
  328. package/lib/uninstall/uninstall.mjs +78 -12
  329. package/lib/validator.mjs +4 -6
  330. package/lib/worker/entrypoint.mjs +2 -1
  331. package/lib/worker/run.mjs +2 -2
  332. package/lib/worker/trace.mjs +5 -11
  333. package/lib/workflow-state.mjs +52 -8
  334. package/lib/workflows/liveness.mjs +203 -0
  335. package/lib/workflows/loader.mjs +177 -0
  336. package/lib/workflows/manifest-schema.mjs +71 -0
  337. package/lib/workflows/surface-parity.mjs +118 -0
  338. package/lib/workflows/validate.mjs +127 -0
  339. package/lib/writes/control-plane.mjs +140 -0
  340. package/lib/writes/envelope.mjs +206 -0
  341. package/lib/writes/sent-log.mjs +86 -0
  342. package/lib/writes/write-intent.mjs +109 -0
  343. package/package.json +16 -8
  344. package/personas/construct.md +12 -3
  345. package/registry/capabilities.json +143 -36
  346. package/rules/common/comments.md +1 -0
  347. package/schemas/project-config.schema.json +0 -12
  348. package/schemas/unified-registry.schema.json +2 -4
  349. package/scripts/sync-specialists.mjs +284 -81
  350. package/skills/docs/adr-workflow.md +1 -1
  351. package/skills/docs/codebase-research-workflow.md +3 -3
  352. package/skills/docs/prd-workflow.md +5 -6
  353. package/skills/docs/runbook-workflow.md +2 -2
  354. package/skills/docs/user-research-workflow.md +3 -3
  355. package/skills/operating/fleet-health-routing.md +77 -0
  356. package/skills/roles/ai-engineer.md +1 -1
  357. package/skills/roles/architect.md +0 -1
  358. package/skills/roles/business-strategist.md +1 -1
  359. package/skills/roles/data-analyst.telemetry.md +1 -1
  360. package/skills/roles/data-engineer.md +1 -1
  361. package/skills/roles/data-engineer.pipeline.md +1 -1
  362. package/skills/roles/data-engineer.vector-retrieval.md +1 -2
  363. package/skills/roles/data-engineer.warehouse.md +1 -1
  364. package/skills/roles/designer.accessibility.md +1 -1
  365. package/skills/roles/designer.md +0 -1
  366. package/skills/roles/devil-advocate.md +1 -1
  367. package/skills/roles/docs-keeper.md +1 -1
  368. package/skills/roles/evaluator.md +1 -1
  369. package/skills/roles/explorer.md +1 -1
  370. package/skills/roles/platform-engineer.md +1 -1
  371. package/skills/roles/qa.ai-eval.md +1 -2
  372. package/skills/roles/qa.api-contract.md +0 -1
  373. package/skills/roles/qa.data-pipeline.md +0 -1
  374. package/skills/roles/qa.md +0 -1
  375. package/skills/roles/qa.web-ui.md +0 -1
  376. package/skills/roles/release-manager.md +1 -1
  377. package/skills/roles/researcher.md +0 -2
  378. package/skills/roles/reviewer.md +0 -3
  379. package/skills/roles/security.ai.md +1 -1
  380. package/skills/roles/security.legal-compliance.md +1 -1
  381. package/skills/roles/security.md +0 -1
  382. package/skills/roles/security.privacy.md +0 -1
  383. package/skills/roles/security.supply-chain.md +1 -1
  384. package/skills/roles/sre.md +1 -1
  385. package/skills/roles/test-automation.md +1 -1
  386. package/skills/roles/trace-reviewer.md +1 -1
  387. package/skills/roles/ux-researcher.md +1 -1
  388. package/specialists/artifact-manifest.json +36 -36
  389. package/specialists/org/contracts/accessibility-to-qa.json +11 -3
  390. package/specialists/org/contracts/any-to-business-strategist.json +19 -7
  391. package/specialists/org/contracts/any-to-debugger.json +7 -1
  392. package/specialists/org/contracts/any-to-designer.json +7 -1
  393. package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
  394. package/specialists/org/contracts/any-to-explorer.json +13 -4
  395. package/specialists/org/contracts/any-to-sre-incident.json +6 -2
  396. package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
  397. package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
  398. package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
  399. package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
  400. package/specialists/org/contracts/architect-to-engineer.json +20 -4
  401. package/specialists/org/contracts/architect-to-evaluator.json +15 -5
  402. package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
  403. package/specialists/org/contracts/architect-to-operations.json +17 -4
  404. package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
  405. package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
  406. package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
  407. package/specialists/org/contracts/engineer-to-qa.json +20 -4
  408. package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
  409. package/specialists/org/contracts/explorer-to-engineer.json +11 -3
  410. package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
  411. package/specialists/org/contracts/operations-to-user.json +53 -0
  412. package/specialists/org/contracts/operations-triage-output.json +53 -0
  413. package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
  414. package/specialists/org/contracts/product-manager-to-architect.json +30 -10
  415. package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
  416. package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
  417. package/specialists/org/contracts/qa-to-release-manager.json +11 -3
  418. package/specialists/org/contracts/researcher-to-architect.json +10 -2
  419. package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
  420. package/specialists/org/contracts/reviewer-to-security.json +17 -3
  421. package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
  422. package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
  423. package/specialists/org/contracts/user-to-construct.json +11 -4
  424. package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
  425. package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
  426. package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
  427. package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
  428. package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
  429. package/specialists/org/groups/engineering-group.json +2 -6
  430. package/specialists/org/groups/governance-group.json +2 -3
  431. package/specialists/org/groups/operations-group.json +5 -8
  432. package/specialists/org/groups/product-group.json +4 -8
  433. package/specialists/org/groups/quality-group.json +3 -7
  434. package/specialists/org/groups/strategy-group.json +6 -9
  435. package/specialists/org/models.json.example +8 -0
  436. package/specialists/org/scopes/creative.json +6 -1
  437. package/specialists/org/scopes/operations.json +7 -1
  438. package/specialists/org/scopes/research.json +6 -1
  439. package/specialists/org/scopes/rnd.json +7 -1
  440. package/specialists/org/specialists/cx-architect.json +27 -8
  441. package/specialists/org/specialists/cx-designer.json +22 -8
  442. package/specialists/org/specialists/cx-engineer.json +71 -7
  443. package/specialists/org/specialists/cx-operations.json +89 -15
  444. package/specialists/org/specialists/cx-orchestrator.json +16 -4
  445. package/specialists/org/specialists/cx-product-manager.json +28 -9
  446. package/specialists/org/specialists/cx-qa.json +16 -6
  447. package/specialists/org/specialists/cx-researcher.json +40 -7
  448. package/specialists/org/specialists/cx-reviewer.json +61 -11
  449. package/specialists/org/specialists/cx-security.json +30 -10
  450. package/specialists/org/teams/design-team.json +3 -4
  451. package/specialists/org/teams/engineering-team.json +3 -10
  452. package/specialists/org/teams/governance-team.json +3 -5
  453. package/specialists/org/teams/operations-team.json +6 -12
  454. package/specialists/org/teams/product-management-team.json +2 -3
  455. package/specialists/org/teams/quality-team.json +4 -12
  456. package/specialists/org/teams/research-team.json +3 -3
  457. package/specialists/org/teams/strategy-team.json +7 -14
  458. package/specialists/prompts/cx-architect.md +20 -1
  459. package/specialists/prompts/cx-data-analyst.md +1 -1
  460. package/specialists/prompts/cx-designer.md +12 -4
  461. package/specialists/prompts/cx-engineer.md +15 -1
  462. package/specialists/prompts/cx-operations.md +14 -2
  463. package/specialists/prompts/cx-orchestrator.md +9 -5
  464. package/specialists/prompts/cx-product-manager.md +5 -1
  465. package/specialists/prompts/cx-qa.md +6 -2
  466. package/specialists/prompts/cx-researcher.md +25 -30
  467. package/specialists/prompts/cx-reviewer.md +19 -1
  468. package/specialists/prompts/cx-security.md +5 -1
  469. package/templates/distribution/construct-brand.typ +123 -59
  470. package/templates/distribution/construct-decision.typ +6 -3
  471. package/templates/distribution/construct-pdf.typ +11 -4
  472. package/templates/distribution/construct-prd.typ +6 -3
  473. package/templates/distribution/construct-research.typ +7 -4
  474. package/lib/providers/contract/adapters/git/index.mjs +0 -115
  475. package/lib/providers/contract/adapters/slack/index.mjs +0 -175
  476. package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
  477. package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
  478. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
  479. package/specialists/org/contracts/designer-to-accessibility.json +0 -36
  480. package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
  481. package/specialists/org/contracts/qa-to-test-automation.json +0 -42
  482. package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
  483. package/specialists/org/contracts/sre-to-release-manager.json +0 -36
  484. package/specialists/org/specialists/cx-accessibility.json +0 -69
  485. package/specialists/org/specialists/cx-ai-engineer.json +0 -75
  486. package/specialists/org/specialists/cx-business-strategist.json +0 -72
  487. package/specialists/org/specialists/cx-data-engineer.json +0 -71
  488. package/specialists/org/specialists/cx-devil-advocate.json +0 -71
  489. package/specialists/org/specialists/cx-docs-keeper.json +0 -82
  490. package/specialists/org/specialists/cx-evaluator.json +0 -69
  491. package/specialists/org/specialists/cx-explorer.json +0 -69
  492. package/specialists/org/specialists/cx-legal-compliance.json +0 -74
  493. package/specialists/org/specialists/cx-oracle.json +0 -46
  494. package/specialists/org/specialists/cx-platform-engineer.json +0 -80
  495. package/specialists/org/specialists/cx-rd-lead.json +0 -73
  496. package/specialists/org/specialists/cx-release-manager.json +0 -77
  497. package/specialists/org/specialists/cx-sre.json +0 -94
  498. package/specialists/org/specialists/cx-test-automation.json +0 -69
  499. package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
  500. package/specialists/org/specialists/cx-ux-researcher.json +0 -68
  501. package/specialists/org/teams/accessibility-team.json +0 -39
  502. package/specialists/org/teams/ux-research-team.json +0 -39
  503. package/specialists/prompts/cx-accessibility.md +0 -55
  504. package/specialists/prompts/cx-ai-engineer.md +0 -124
  505. package/specialists/prompts/cx-business-strategist.md +0 -58
  506. package/specialists/prompts/cx-data-engineer.md +0 -55
  507. package/specialists/prompts/cx-devil-advocate.md +0 -59
  508. package/specialists/prompts/cx-docs-keeper.md +0 -164
  509. package/specialists/prompts/cx-evaluator.md +0 -49
  510. package/specialists/prompts/cx-explorer.md +0 -71
  511. package/specialists/prompts/cx-legal-compliance.md +0 -58
  512. package/specialists/prompts/cx-oracle.md +0 -98
  513. package/specialists/prompts/cx-platform-engineer.md +0 -97
  514. package/specialists/prompts/cx-rd-lead.md +0 -59
  515. package/specialists/prompts/cx-release-manager.md +0 -54
  516. package/specialists/prompts/cx-sre.md +0 -111
  517. package/specialists/prompts/cx-test-automation.md +0 -55
  518. package/specialists/prompts/cx-trace-reviewer.md +0 -101
  519. package/specialists/prompts/cx-ux-researcher.md +0 -53
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "postgres",
3
+ "version": "1.0.0",
4
+ "kind": "queue",
5
+ "capabilities": ["enqueue", "claim", "lease", "reclaim"],
6
+ "secretEnvKeys": ["CONSTRUCT_DATABASE_URL"],
7
+ "operations": ["enqueue", "claim", "heartbeat", "complete", "skip", "reopen"],
8
+ "securityClassification": "trusted",
9
+ "owner": "construct-core",
10
+ "compatVersion": 1,
11
+ "installSource": "optional-dependency"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "salesforce",
3
+ "version": "1.0.0",
4
+ "kind": "data-source",
5
+ "capabilities": ["read", "search"],
6
+ "secretEnvKeys": ["SALESFORCE_INSTANCE_URL", "SALESFORCE_ACCESS_TOKEN"],
7
+ "operations": ["read", "search"],
8
+ "securityClassification": "trusted",
9
+ "owner": "construct-core",
10
+ "compatVersion": 1,
11
+ "installSource": "builtin"
12
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "id": "slack",
3
+ "version": "1.0.0",
4
+ "kind": "data-source",
5
+ "capabilities": ["read", "search"],
6
+ "secretEnvKeys": ["SLACK_BOT_TOKEN", "SLACK_USER_TOKEN"],
7
+ "operations": ["read", "search"],
8
+ "securityClassification": "trusted",
9
+ "owner": "construct-core",
10
+ "compatVersion": 1,
11
+ "installSource": "builtin",
12
+ "sourceTarget": {
13
+ "provider": "slack",
14
+ "selector": {
15
+ "field": "channel",
16
+ "pattern": null,
17
+ "patternTransform": "trim",
18
+ "hint": "required Slack channel name or ID",
19
+ "normalize": "trimHash"
20
+ },
21
+ "secondaryField": {
22
+ "field": "intent",
23
+ "enum": ["internal", "risk", "decision", "external", "how-to"],
24
+ "hint": "must be one of",
25
+ "default": "internal"
26
+ },
27
+ "signature": { "template": "slack:channel:{value}:intent:{intent}", "valueMode": "lowerOrEmpty" },
28
+ "legacyEnv": { "vars": ["SLACK_CHANNELS", "SLACK_CHANNEL"], "parse": "slackChannelColon" },
29
+ "aliases": { "strategy": "simple", "displayPrefix": "Slack", "catchAll": false },
30
+ "embed": {
31
+ "mode": "groupBy",
32
+ "groupField": "intent",
33
+ "groupDefault": "internal",
34
+ "valueField": "channels",
35
+ "valueKind": "array",
36
+ "refs": ["messages"],
37
+ "oldest": 86400,
38
+ "limit": 50
39
+ },
40
+ "embedFilters": {
41
+ "valueField": "channels",
42
+ "valueKind": "array",
43
+ "defaultRefs": ["messages"],
44
+ "extraFilterKeys": ["oldest"],
45
+ "defaultLimit": 50,
46
+ "includeSecondaryField": true
47
+ },
48
+ "demandFetch": {
49
+ "targetRefs": ["messages"],
50
+ "targetOptsField": "channels",
51
+ "targetOptsKind": "array",
52
+ "targetExtraFilterKeys": ["oldest"],
53
+ "targetOldestDefault": 86400,
54
+ "targetDefaultLimit": 50,
55
+ "querySupported": false
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "whisper",
3
+ "version": "1.0.0",
4
+ "kind": "ingestion-provider",
5
+ "capabilities": ["audio-transcription"],
6
+ "docs": "Local audio/video transcription via whisper.cpp (Metal-accelerated on macOS). No npm dependency — the binary and model weights are provisioned outside npm and probed, not assumed.",
7
+ "owner": "construct-core",
8
+ "compatVersion": 1,
9
+ "installSource": "system-binary",
10
+ "installDependencies": [],
11
+ "installProbe": {
12
+ "kind": "binary-on-path",
13
+ "candidates": ["whisper-cli", "whisper-cpp", "main"],
14
+ "runtimeDirEnv": "CONSTRUCT_WHISPER_RUNTIME_DIR",
15
+ "defaultRuntimeDir": ".cx/runtime/whisper",
16
+ "cachedBinary": "bin/whisper-cli",
17
+ "description": "which(1) lookup for whisper-cli/whisper-cpp/main on PATH, falling back to a cached binary at <runtimeDir>/bin/whisper-cli."
18
+ },
19
+ "healthCheck": {
20
+ "kind": "subprocess-version",
21
+ "command": "<resolvedBinary>",
22
+ "args": ["--help"],
23
+ "timeoutMs": 10000,
24
+ "description": "whisper.cpp CLI has no --version flag; health runs the binary with --help and treats a clean exit (or the expected usage banner) as healthy. A missing binary or non-zero exit is unhealthy."
25
+ },
26
+ "degradation": {
27
+ "chain": [
28
+ { "id": "whisper", "mode": "sidecar", "description": "Local ASR transcription via whisper.cpp." },
29
+ { "id": "refuse", "mode": "error", "description": "No transcription backend available; audio/video ingest fails loud (ASR_REQUIRED) with an install hint (brew install whisper-cpp)." }
30
+ ],
31
+ "terminal": "refuse",
32
+ "resultMarking": "degraded"
33
+ },
34
+ "removalStatus": "active",
35
+ "securityClassification": "trusted"
36
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * lib/extensions/validate.mjs — extension manifest validator.
3
+ *
4
+ * Validates a plain-JS manifest object against the canonical schema defined in
5
+ * manifest-schema.mjs. All errors are returned in a structured result; this
6
+ * function never throws. Error messages are single-line, actionable, and
7
+ * prefixed with the filePath (when provided) so consumers can surface them
8
+ * directly to users.
9
+ */
10
+
11
+ import {
12
+ MANIFEST_KINDS, REQUIRED_FIELDS, OPTIONAL_FIELDS, COMPAT_VERSION,
13
+ ALLOWED_SECRET_ENV_PREFIXES, ESCALATION_SENSITIVE_KINDS,
14
+ KNOWN_REQUESTABLE_TOOL_GRANTS, BUILTIN_SPECIALIST_IDS,
15
+ } from './manifest-schema.mjs';
16
+
17
+ /** Basic semver pattern: MAJOR.MINOR.PATCH with optional pre-release/build metadata. */
18
+ const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+].+)?$/;
19
+
20
+ /** Valid id characters: lowercase alphanumeric, hyphens, dots, forward slashes. */
21
+ const ID_RE = /^[a-z0-9\-./]+$/;
22
+
23
+ /**
24
+ * validateManifest(manifest, { filePath, strict } = {})
25
+ *
26
+ * Returns { valid: true } on success, or { valid: false, errors: string[] }
27
+ * on failure. Every error string is a self-contained, single-line description
28
+ * of the problem. The filePath prefix is included in every error so callers
29
+ * can surface messages without additional decoration.
30
+ *
31
+ * Checks performed (in order):
32
+ * 1. Required fields are present.
33
+ * 2. `kind` is one of the canonical MANIFEST_KINDS.
34
+ * 3. `version` matches the semver pattern.
35
+ * 4. `id` is a non-empty string matching [a-z0-9-./]+.
36
+ * 5. `compatVersion` (if present) does not exceed COMPAT_VERSION.
37
+ *
38
+ * When strict is true, additional hardening checks are applied:
39
+ * 6. No unknown fields (fields outside REQUIRED_FIELDS + OPTIONAL_FIELDS).
40
+ * 7. secretEnvKeys entries must start with an allowed prefix.
41
+ * 8. For escalation-sensitive kinds, toolGrantsRequested must be in
42
+ * KNOWN_REQUESTABLE_TOOL_GRANTS.
43
+ * 9. Prompts targeting built-in specialists without override:true are
44
+ * rejected (prompt shadowing prevention).
45
+ * 10. owner values starting with 'cx-' must be known built-in specialist ids.
46
+ *
47
+ * @param {unknown} manifest - The parsed manifest object to validate.
48
+ * @param {{ filePath?: string, strict?: boolean }} [opts]
49
+ * @returns {{ valid: true } | { valid: false, errors: string[] }}
50
+ */
51
+ export function validateManifest(manifest, { filePath, strict = false } = {}) {
52
+ const prefix = filePath ? `${filePath}: ` : '';
53
+ const errors = [];
54
+
55
+ if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
56
+ return { valid: false, errors: [`${prefix}manifest must be a JSON object`] };
57
+ }
58
+
59
+ // Required fields
60
+ for (const field of REQUIRED_FIELDS) {
61
+ if (!(field in manifest) || manifest[field] === undefined || manifest[field] === null) {
62
+ errors.push(`${prefix}missing required field: ${field}`);
63
+ }
64
+ }
65
+
66
+ // If required fields are missing we may not have meaningful values for the
67
+ // semantic checks — collect what we can, but short-circuit gracefully.
68
+
69
+ // kind check (only if kind is present — otherwise already flagged above)
70
+ if ('kind' in manifest && manifest.kind !== undefined && manifest.kind !== null) {
71
+ if (!MANIFEST_KINDS.includes(manifest.kind)) {
72
+ errors.push(
73
+ `${prefix}unknown kind '${manifest.kind}'; expected one of: ${MANIFEST_KINDS.join(', ')}`
74
+ );
75
+ }
76
+ }
77
+
78
+ // version semver check
79
+ if ('version' in manifest && manifest.version !== undefined && manifest.version !== null) {
80
+ if (typeof manifest.version !== 'string' || !SEMVER_RE.test(manifest.version)) {
81
+ errors.push(`${prefix}version must be a semver string`);
82
+ }
83
+ }
84
+
85
+ // id format check
86
+ if ('id' in manifest && manifest.id !== undefined && manifest.id !== null) {
87
+ if (typeof manifest.id !== 'string' || manifest.id.length === 0) {
88
+ errors.push(`${prefix}id must be a non-empty string`);
89
+ } else if (!ID_RE.test(manifest.id)) {
90
+ errors.push(`${prefix}id must match [a-z0-9-./]+ (got '${manifest.id}')`);
91
+ }
92
+ }
93
+
94
+ // Forward-compat guard
95
+ if (
96
+ manifest.compatVersion !== undefined &&
97
+ manifest.compatVersion !== null
98
+ ) {
99
+ if (typeof manifest.compatVersion !== 'number' || !Number.isInteger(manifest.compatVersion)) {
100
+ errors.push(`${prefix}compatVersion must be an integer`);
101
+ } else if (manifest.compatVersion > COMPAT_VERSION) {
102
+ errors.push(
103
+ `${prefix}compatVersion ${manifest.compatVersion} exceeds supported version ${COMPAT_VERSION}; upgrade Construct to use this manifest`
104
+ );
105
+ }
106
+ }
107
+
108
+ // Strict-mode hardening checks
109
+ if (strict) {
110
+ const knownFields = new Set([...REQUIRED_FIELDS, ...OPTIONAL_FIELDS]);
111
+
112
+ // Unknown field rejection
113
+ for (const key of Object.keys(manifest)) {
114
+ if (key.startsWith('_')) continue;
115
+ if (!knownFields.has(key)) {
116
+ errors.push(`${prefix}unknown field '${key}' in strict mode`);
117
+ }
118
+ }
119
+
120
+ // Secret env key allowlist
121
+ if (manifest.secretEnvKeys && Array.isArray(manifest.secretEnvKeys)) {
122
+ for (const key of manifest.secretEnvKeys) {
123
+ const allowed = ALLOWED_SECRET_ENV_PREFIXES.some(p => key.startsWith(p));
124
+ if (!allowed) {
125
+ errors.push(`${prefix}secretEnvKey '${key}' is not allowed; must start with one of: ${ALLOWED_SECRET_ENV_PREFIXES.join(', ')}`);
126
+ }
127
+ }
128
+ }
129
+
130
+ // Capability escalation checks for sensitive kinds
131
+ if (manifest.kind && ESCALATION_SENSITIVE_KINDS.includes(manifest.kind)) {
132
+ if (manifest.toolGrantsRequested && Array.isArray(manifest.toolGrantsRequested)) {
133
+ for (const grant of manifest.toolGrantsRequested) {
134
+ if (!KNOWN_REQUESTABLE_TOOL_GRANTS.includes(grant)) {
135
+ errors.push(`${prefix}toolGrantsRequested '${grant}' is not a known tool grant`);
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ // Builtin prompt shadowing prevention
142
+ if (manifest.prompts && Array.isArray(manifest.prompts)) {
143
+ for (const [i, prompt] of manifest.prompts.entries()) {
144
+ if (
145
+ prompt &&
146
+ typeof prompt.specialist === 'string' &&
147
+ BUILTIN_SPECIALIST_IDS.includes(prompt.specialist) &&
148
+ !prompt.override
149
+ ) {
150
+ errors.push(
151
+ `${prefix}prompts[${i}]: specialist '${prompt.specialist}' is a built-in; set "override": true to shadow`
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ // Owner validation — cx- prefixed owners must be known builtins
158
+ if (
159
+ manifest.owner &&
160
+ typeof manifest.owner === 'string' &&
161
+ manifest.owner.startsWith('cx-')
162
+ ) {
163
+ if (!BUILTIN_SPECIALIST_IDS.includes(manifest.owner)) {
164
+ errors.push(
165
+ `${prefix}owner '${manifest.owner}' starts with 'cx-' but is not a known built-in specialist id`
166
+ );
167
+ }
168
+ }
169
+ }
170
+
171
+ if (errors.length > 0) {
172
+ return { valid: false, errors };
173
+ }
174
+ return { valid: true };
175
+ }
package/lib/features.mjs CHANGED
@@ -19,8 +19,12 @@ function getFeaturesFile(overrides = {}) {
19
19
  return overrides.featuresFile ?? join(configDir(getHomeDir(overrides)), 'features.json');
20
20
  }
21
21
 
22
- function getClaudeSettingsPath(overrides = {}) {
23
- return overrides.claudeSettingsPath ?? join(getHomeDir(overrides), '.claude', 'settings.json');
22
+ // Claude Code's user-scope MCP servers live in ~/.claude.json's top-level
23
+ // `mcpServers`, not in settings.json (confirmed against
24
+ // code.claude.com/docs/en/mcp and live `claude mcp list` repro, construct-ranh).
25
+
26
+ function getClaudeUserConfigPath(overrides = {}) {
27
+ return overrides.claudeUserConfigPath ?? join(getHomeDir(overrides), '.claude.json');
24
28
  }
25
29
 
26
30
  function readEnabledFeatures(overrides = {}) {
@@ -39,11 +43,11 @@ function isFeatureEnabled(id, overrides = {}) {
39
43
  return (data.enabled ?? []).includes(id);
40
44
  }
41
45
 
42
- function readClaudeSettings(overrides = {}) {
43
- const claudeSettings = getClaudeSettingsPath(overrides);
44
- if (!existsSync(claudeSettings)) return null;
46
+ function readClaudeUserConfig(overrides = {}) {
47
+ const claudeUserConfig = getClaudeUserConfigPath(overrides);
48
+ if (!existsSync(claudeUserConfig)) return null;
45
49
  try {
46
- return JSON.parse(readFileSync(claudeSettings, 'utf8'));
50
+ return JSON.parse(readFileSync(claudeUserConfig, 'utf8'));
47
51
  } catch {
48
52
  return null;
49
53
  }
@@ -194,9 +198,9 @@ function getMcpPlatforms(mcp, overrides = {}) {
194
198
  platforms.push('Claude.ai');
195
199
  }
196
200
 
197
- // --- Claude Code: user settings (~/.claude/settings.json) ---
198
- const claudeSettings = readClaudeSettings(overrides);
199
- if (getHostSupport(mcp, 'claude').mode === 'managed' && configHasAny(claudeSettings?.mcpServers, matchIds)) {
201
+ // --- Claude Code: user scope (~/.claude.json's top-level mcpServers) ---
202
+ const claudeUserConfig = readClaudeUserConfig(overrides);
203
+ if (getHostSupport(mcp, 'claude').mode === 'managed' && configHasAny(claudeUserConfig?.mcpServers, matchIds)) {
200
204
  platforms.push('Claude Code');
201
205
  }
202
206
 
@@ -0,0 +1,184 @@
1
+ /**
2
+ * lib/flows/checkpoint.mjs — durable per-step checkpointing and resume for flow runs.
3
+ *
4
+ * Persists a Run after every completed step under the project's machine-scoped
5
+ * state root (ADR-0066) at `runtime/flows/runs/<runId>.json`, atomic write via
6
+ * temp-file-then-rename — the same pattern lib/orchestration/run-store.mjs uses
7
+ * for orchestration runs. A run's Set/Map fields (completed, joinProgress,
8
+ * usage) are serialized to plain arrays and reconstructed on load; the flow
9
+ * definition itself (its run/router functions) is never persisted — a
10
+ * resuming caller supplies the same flow object it would use to start a run,
11
+ * exactly like loadFlow's `handlers` pattern for JSON flows. A checkpoint
12
+ * records which flow id it was written under; resuming against a different
13
+ * flow id throws rather than silently replaying history against the wrong
14
+ * step graph.
15
+ *
16
+ * Idempotent re-entry: runCheckpointed() checkpoints strictly AFTER each
17
+ * advanceRun() call, so a step already reflected in a saved checkpoint's
18
+ * `completed` set is never in that checkpoint's `frontier` and will not
19
+ * re-run on resume — the frontier only ever holds not-yet-completed steps,
20
+ * which is a property of the Run shape itself, not something this module adds.
21
+ * The remaining window — a crash between a step's run() executing and the
22
+ * following checkpoint write landing — is closed by authoring convention, not
23
+ * an engine guarantee: a step's run() must be idempotent under at-least-once
24
+ * execution (pure computation, or a naturally idempotent side effect such as
25
+ * an upsert), the same constraint the engine already implies by recording
26
+ * delegation intent rather than performing it (engine.mjs's workerBackend
27
+ * handling). A step that must perform an exactly-once side effect belongs
28
+ * outside the engine, driven by its recorded delegation, not inside run().
29
+ */
30
+
31
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
32
+ import { join } from 'node:path';
33
+
34
+ import { resolveStateDir } from '../state-root.mjs';
35
+ import { createRun, advanceRun } from './engine.mjs';
36
+ import { RUN_STATUS } from './constants.mjs';
37
+
38
+ export class FlowCheckpointError extends Error {
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = 'FlowCheckpointError';
42
+ this.code = 'FLOW_CHECKPOINT_MISMATCH';
43
+ }
44
+ }
45
+
46
+ let writeCounter = 0;
47
+
48
+ export function runsDir(cwd = process.cwd()) {
49
+ return resolveStateDir(cwd, 'runtime', 'flows', 'runs', { ensureDir: false });
50
+ }
51
+
52
+ function checkpointPath(cwd, runId) {
53
+ return join(runsDir(cwd), `${runId}.json`);
54
+ }
55
+
56
+ function atomicWriteJson(filePath, value) {
57
+ writeCounter = (writeCounter + 1) % 100000;
58
+ const tmp = `${filePath}.${process.pid}.${writeCounter}.tmp`;
59
+ writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`);
60
+ renameSync(tmp, filePath);
61
+ }
62
+
63
+ function serializeRun(run) {
64
+ return {
65
+ state: run.state,
66
+ frontier: [...run.frontier],
67
+ completed: [...run.completed],
68
+ joinProgress: [...run.joinProgress.entries()].map(([step, satisfied]) => [step, [...satisfied]]),
69
+ usage: [...run.usage.entries()],
70
+ history: run.history,
71
+ status: run.status,
72
+ error: run.error,
73
+ };
74
+ }
75
+
76
+ function deserializeRun(flow, data) {
77
+ return {
78
+ flow,
79
+ state: data.state,
80
+ frontier: [...(data.frontier || [])],
81
+ completed: new Set(data.completed || []),
82
+ joinProgress: new Map((data.joinProgress || []).map(([step, satisfied]) => [step, new Set(satisfied)])),
83
+ usage: new Map(data.usage || []),
84
+ history: data.history || [],
85
+ status: data.status,
86
+ error: data.error || null,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Persist a run's current state to its checkpoint file. Overwrites any prior
92
+ * checkpoint for the same runId — a checkpoint is the run's latest snapshot,
93
+ * not a history of every tick (history already lives on the run itself).
94
+ */
95
+ export function checkpointRun(cwd, runId, flow, run) {
96
+ const dir = runsDir(cwd);
97
+ mkdirSync(dir, { recursive: true });
98
+ atomicWriteJson(checkpointPath(cwd, runId), {
99
+ runId,
100
+ flowId: flow.id ?? null,
101
+ updatedAt: new Date().toISOString(),
102
+ run: serializeRun(run),
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Read a checkpoint's raw persisted shape ({runId, flowId, updatedAt, run}),
108
+ * or null when no checkpoint exists or the file is corrupt.
109
+ */
110
+ export function loadCheckpoint(cwd, runId) {
111
+ const file = checkpointPath(cwd, runId);
112
+ if (!existsSync(file)) return null;
113
+ try {
114
+ return JSON.parse(readFileSync(file, 'utf8'));
115
+ } catch {
116
+ return null;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Reconstruct a Run from its checkpoint against the given flow, or null when
122
+ * no checkpoint exists. Throws FlowCheckpointError when the checkpoint was
123
+ * recorded against a different flow id than the one supplied — resuming a
124
+ * run against the wrong step graph would silently reinterpret its history.
125
+ */
126
+ export function resumeRun(cwd, runId, flow) {
127
+ const checkpoint = loadCheckpoint(cwd, runId);
128
+ if (!checkpoint) return null;
129
+ if (flow.id && checkpoint.flowId && flow.id !== checkpoint.flowId) {
130
+ throw new FlowCheckpointError(`checkpoint "${runId}" was recorded against flow "${checkpoint.flowId}", not "${flow.id}"`);
131
+ }
132
+ return deserializeRun(flow, checkpoint.run);
133
+ }
134
+
135
+ /**
136
+ * Start a new run and checkpoint it immediately, so a resume attempted before
137
+ * the first tick still finds a valid checkpoint rather than nothing.
138
+ */
139
+ export function startRun(cwd, runId, flow, initialState = {}) {
140
+ const run = createRun(flow, initialState);
141
+ checkpointRun(cwd, runId, flow, run);
142
+ return run;
143
+ }
144
+
145
+ /**
146
+ * Advance a checkpointed run by exactly one tick: resume from the last
147
+ * checkpoint when one exists, otherwise start fresh from initialState, then
148
+ * run a single advanceRun() and checkpoint the result. Unlike
149
+ * runCheckpointed(), this never loops to completion — each call surfaces
150
+ * exactly the step that tick just completed, which is what a caller driving
151
+ * a run one delegation at a time (e.g. lib/orchestration/delegation-flow.mjs)
152
+ * needs: the current step, not the whole remaining chain. A run already at a
153
+ * terminal status is returned as-is, not re-driven.
154
+ */
155
+ export async function tickCheckpointed(cwd, runId, flow, initialState = {}) {
156
+ let run = resumeRun(cwd, runId, flow) ?? startRun(cwd, runId, flow, initialState);
157
+ if (run.status === RUN_STATUS.RUNNING) {
158
+ run = await advanceRun(run);
159
+ checkpointRun(cwd, runId, flow, run);
160
+ }
161
+ return run;
162
+ }
163
+
164
+ /**
165
+ * Drive a flow to completion one checkpointed tick at a time: resume from the
166
+ * last checkpoint when one exists, otherwise start fresh from initialState.
167
+ * Every tick's result is checkpointed before the loop continues, so a crash
168
+ * at any point resumes from the most recently completed step, never earlier
169
+ * and never mid-step.
170
+ */
171
+ export async function runCheckpointed(cwd, runId, flow, initialState = {}, { maxSteps = 10000 } = {}) {
172
+ let run = resumeRun(cwd, runId, flow) ?? startRun(cwd, runId, flow, initialState);
173
+ let steps = 0;
174
+ while (run.status === RUN_STATUS.RUNNING && steps < maxSteps) {
175
+ run = await advanceRun(run);
176
+ checkpointRun(cwd, runId, flow, run);
177
+ steps += 1;
178
+ }
179
+ if (run.status === RUN_STATUS.RUNNING && steps >= maxSteps) {
180
+ run = { ...run, status: RUN_STATUS.ERROR, error: { code: 'MAX_STEPS_EXCEEDED', message: `flow exceeded ${maxSteps} steps without reaching a terminal state` } };
181
+ checkpointRun(cwd, runId, flow, run);
182
+ }
183
+ return run;
184
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * lib/flows/constants.mjs — shared sentinels and status enums for the flow engine.
3
+ *
4
+ * TERMINAL is a plain string sentinel (not a Symbol) so a router's return value
5
+ * survives JSON serialization unchanged, matching the future checkpoint/resume
6
+ * requirement that a run's next-step pointer be persistable as-is.
7
+ */
8
+
9
+ export const TERMINAL = '@@flow/terminal';
10
+
11
+ export const WORKER_BACKENDS = Object.freeze(['inline', 'provider', 'host']);
12
+
13
+ export const STEP_STATUS = Object.freeze({
14
+ DONE: 'done',
15
+ ERROR: 'error',
16
+ BUDGET_EXHAUSTED: 'budget-exhausted',
17
+ INVALID_STATE: 'invalid-state',
18
+ });
19
+
20
+ export const RUN_STATUS = Object.freeze({
21
+ RUNNING: 'running',
22
+ COMPLETED: 'completed',
23
+ ERROR: 'error',
24
+ BUDGET_EXHAUSTED: 'budget-exhausted',
25
+ });
26
+
27
+ export const JOIN_MODES = Object.freeze(['all', 'any']);