@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
@@ -6,13 +6,14 @@
6
6
  * spawns whatever is available and returns a result per service.
7
7
  */
8
8
  import { spawn, spawnSync } from 'node:child_process';
9
+ import { randomBytes } from 'node:crypto';
9
10
  import fs from 'node:fs';
10
11
  import net from 'node:net';
11
12
  import os from 'node:os';
12
13
  import path from 'node:path';
13
14
  import { fileURLToPath } from 'node:url';
14
15
 
15
- import { findAvailablePort } from './host-capabilities.mjs';
16
+ import { findAvailablePort, detectActiveSessions } from './host-capabilities.mjs';
16
17
  import { wrapWithOpRun } from './providers/op-run.mjs';
17
18
  import { getUserEnvPath, loadConstructEnv, parseEnvFile, writeEnvValues } from './env-config.mjs';
18
19
  import { runPressureRelease } from './runtime-pressure.mjs';
@@ -22,6 +23,7 @@ import { readHeartbeat } from './daemons/contract.mjs';
22
23
  import { heartbeatPath } from './oracle/index.mjs';
23
24
  import { stateDir } from './config/xdg.mjs';
24
25
  import { doctorRoot } from './config/xdg.mjs';
26
+ import { resolveStateDir } from './state-root.mjs';
25
27
 
26
28
  const INSTALL_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
27
29
 
@@ -163,13 +165,19 @@ async function isCopilotBridgeRunning(port) {
163
165
  return probeRuntimePort(port);
164
166
  }
165
167
 
166
- // Opt-in 1Password resolution (ADR-0049 Design A): each long-lived service launches
167
- // under `op run --env-file -- <cmd>` so op:// references resolve once at this stable
168
- // parent and every child inherits resolved provider keys. wrapWithOpRun returns the
169
- // command unchanged when CONSTRUCT_OP_ENV_FILE is unset, the file is missing, or `op`
170
- // is absent, so the un-opted path is byte-for-byte the prior behavior. The env that
171
- // carries CONSTRUCT_OP_ENV_FILE is options.env when a site sets one (doctor, oracle)
172
- // and process.env otherwise (cm, opencode, copilot bridge).
168
+ // Opt-in 1Password resolution (ADR-0049 Design A): under `construct dev` the process
169
+ // re-execs once under a single `op run --env-file` (maybeReExecUnderOpRun), so op://
170
+ // references resolve one time at that stable parent and every daemon here inherits the
171
+ // resolved keys through liveEnv the CONSTRUCT_OP_RUN_ACTIVE sentinel then makes
172
+ // wrapWithOpRun a no-op so no daemon nests a second op run. wrapWithOpRun stays the
173
+ // fallback for daemons started outside that parent (an opencode/copilot-bridge restart
174
+ // from a hook), wrapping them individually. It returns the command unchanged when
175
+ // CONSTRUCT_OP_ENV_FILE is unset, the file is missing, `op` is absent, or the sentinel
176
+ // is set, so the un-opted path is byte-for-byte the prior behavior. doctor, oracle, and
177
+ // cm consume no provider API keys (construct-0wmj) and are spawned unwrapped; every
178
+ // service is still spawned with the merged liveEnv, so config.env-only vars (including
179
+ // CONSTRUCT_OP_ENV_FILE and an OP_SERVICE_ACCOUNT_TOKEN) reach the child rather than
180
+ // only the wrapped op-run subset.
173
181
 
174
182
  function wrapServiceSpawn(command, args, homeDir, options = {}) {
175
183
  const env = options.env || process.env;
@@ -225,9 +233,9 @@ export function startDoctor({ rootDir, homeDir = os.homedir() } = {}) {
225
233
  if (existing) return { started: false, reused: true, pid: existing.pid };
226
234
  const indexPath = path.join(INSTALL_ROOT, 'lib', 'doctor', 'index.mjs');
227
235
  if (!fs.existsSync(indexPath)) return { started: false, reason: 'missing-binary' };
236
+ // doctor consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
228
237
  const doctorEnv = { ...process.env };
229
- const launch = wrapServiceSpawn('node', [indexPath], homeDir, { env: doctorEnv });
230
- const { logPath } = spawnDetached(launch.command, launch.args, homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
238
+ const { logPath } = spawnDetached('node', [indexPath], homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
231
239
  return { started: true, logPath };
232
240
  }
233
241
 
@@ -244,8 +252,8 @@ export function startOracle({ rootDir, homeDir = os.homedir(), projectDir = root
244
252
  CONSTRUCT_ORACLE_ROOT: rootDir,
245
253
  CONSTRUCT_ORACLE_PROJECT: projectDir,
246
254
  };
247
- const launch = wrapServiceSpawn('node', [entry], homeDir, { env: oracleEnv });
248
- const { logPath } = spawnDetached(launch.command, launch.args, homeDir, 'oracle-daemon.log', {
255
+ // oracle consumes no provider API keys (construct-0wmj) no op-run wrap needed.
256
+ const { logPath } = spawnDetached('node', [entry], homeDir, 'oracle-daemon.log', {
249
257
  cwd: projectDir,
250
258
  env: oracleEnv,
251
259
  });
@@ -338,6 +346,17 @@ export async function startServices({
338
346
  const operatorKeepAlive = resolveOllamaKeepAlive(liveEnv);
339
347
  if (operatorKeepAlive) liveEnv.OLLAMA_KEEP_ALIVE = operatorKeepAlive;
340
348
 
349
+ // The Copilot bridge fronts the user's Copilot entitlement over loopback HTTP, so it
350
+ // requires a bearer token. Generate a stable token once, persist it to the 0600
351
+ // config.env, and set it on liveEnv before OpenCode (the only caller) spawns so both
352
+ // the bridge and OpenCode inherit it; writeOpenCodeConfig wires it as an {env:} auth
353
+ // header. Reusing the persisted value keeps a restarted bridge and OpenCode in sync.
354
+ if (wants('copilot-bridge') && support.gh && detectActiveSessions().includes('github-copilot') && !liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN) {
355
+ const copilotBridgeToken = randomBytes(24).toString('hex');
356
+ liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN = copilotBridgeToken;
357
+ writeEnvValues(envPath, { CONSTRUCT_COPILOT_BRIDGE_TOKEN: copilotBridgeToken });
358
+ }
359
+
341
360
  const pressureReport = runPressureReleaseFn({ env: { ...liveEnv, HOME: homeDir } });
342
361
  if (pressureReport?.killed?.length) {
343
362
  results.push({
@@ -354,14 +373,14 @@ export async function startServices({
354
373
  if (traceBackend === 'local') {
355
374
  results.push({
356
375
  name: 'Telemetry',
357
- url: path.join(rootDir, '.cx', 'traces'),
376
+ url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
358
377
  status: 'configured',
359
378
  note: 'local JSONL traces; remote export not configured',
360
379
  });
361
380
  } else if (traceBackend === 'none') {
362
381
  results.push({
363
382
  name: 'Telemetry',
364
- url: path.join(rootDir, '.cx', 'traces'),
383
+ url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
365
384
  status: 'configured',
366
385
  note: 'remote export disabled; local JSONL traces preserved',
367
386
  });
@@ -390,12 +409,12 @@ export async function startServices({
390
409
  if (await memoryProbeFn(ports.memory)) {
391
410
  results.push({ name: 'Memory (cm)', url: `http://127.0.0.1:${ports.memory}`, status: 'reused' });
392
411
  } else {
393
- const cm = wrapServiceSpawn('cm', ['serve', '--port', String(ports.memory)], homeDir, { env: liveEnv });
394
- const cmSpawn = spawnDetachedFn(cm.command, cm.args, homeDir, 'cm.log');
412
+ // cm consumes no provider API keys (construct-0wmj) no op-run wrap needed.
413
+ const cmSpawn = spawnDetachedFn('cm', ['serve', '--port', String(ports.memory)], homeDir, 'cm.log', { env: liveEnv });
395
414
  try {
396
415
  if (cmSpawn?.child?.pid) {
397
416
  writeJson(portOwnershipPath(ports.memory, homeDir), {
398
- pid: cmSpawn.child.pid, command: `${cm.command} ${cm.args.join(' ')}`,
417
+ pid: cmSpawn.child.pid, command: `cm serve --port ${ports.memory}`,
399
418
  cwd: process.cwd(), marker: 'construct', constructManaged: true,
400
419
  startedAt: new Date().toISOString(),
401
420
  });
@@ -413,7 +432,7 @@ export async function startServices({
413
432
  results.push({ name: 'OpenCode', url: `http://127.0.0.1:${ports.bridge}`, status: 'reused' });
414
433
  } else {
415
434
  const opencode = wrapServiceSpawn('opencode', ['serve', '--port', String(ports.bridge)], homeDir, { env: liveEnv });
416
- const ocSpawn = spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log');
435
+ const ocSpawn = spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log', { env: liveEnv });
417
436
  try {
418
437
  if (ocSpawn?.child?.pid) {
419
438
  writeJson(portOwnershipPath(ports.bridge, homeDir), {
@@ -438,7 +457,7 @@ export async function startServices({
438
457
  const proxyPath = path.join(INSTALL_ROOT, 'lib', 'bridges', 'copilot-proxy.mjs');
439
458
  if (fs.existsSync(proxyPath)) {
440
459
  const copilot = wrapServiceSpawn('node', [proxyPath, '--port', String(ports.copilotBridge)], homeDir, { env: liveEnv });
441
- const copilotSpawn = spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log');
460
+ const copilotSpawn = spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log', { env: liveEnv });
442
461
  try {
443
462
  if (copilotSpawn?.child?.pid) {
444
463
  writeJson(portOwnershipPath(ports.copilotBridge, homeDir), {
package/lib/setup.mjs CHANGED
@@ -2,10 +2,14 @@
2
2
  /**
3
3
  * lib/setup.mjs — Machine-scoped first-run setup wizard for Construct.
4
4
  *
5
- * Installs cm/cass, configures managed defaults, pre-warms the embedding model,
6
- * wires MCP integrations, and writes the XDG user config (~/.config/construct/config.env).
7
- * Invoked by `construct install` and by lib/install/first-invocation.mjs
8
- * when a TTY user is missing resources.
5
+ * Installs cm/cass, configures managed defaults, wires MCP integrations, and
6
+ * writes the XDG user config (~/.config/construct/config.env). Invoked by
7
+ * `construct install` and by lib/install/first-invocation.mjs when a TTY
8
+ * user is missing resources.
9
+ *
10
+ * The embedding model and LanceDB vector store provision lazily by default
11
+ * (construct-rf26.17) — pass --with-embeddings to warm both up during
12
+ * install instead of at first semantic-search use, mirroring --with-docling.
9
13
  *
10
14
  * Project-scoped scaffolding (`.cx/`, AGENTS.md, plan.md, adapters) lives
11
15
  * in lib/init-unified.mjs — invoked by `construct init`. These two flows
@@ -35,6 +39,7 @@ import { getEmbeddingModelInfo, warmupEmbeddingModel } from './storage/embedding
35
39
  import { buildPressureGuardValues, installPressureGuardLaunchAgent, loadPressureGuardLaunchAgent } from './runtime-pressure.mjs';
36
40
  import { consentToInstall } from './setup-prompts.mjs';
37
41
  import { cleanupLegacyGlobalConstruct } from './install/legacy-global-cleanup.mjs';
42
+ import { isMainModule } from './roots.mjs';
38
43
 
39
44
  const ROOT_DIR = path.resolve(import.meta.dirname, '..');
40
45
  const HOME = os.homedir();
@@ -59,6 +64,9 @@ Flags:
59
64
  --with-docling eagerly provision the docling document-extraction venv now
60
65
  (heavy, ~10 min via uv; otherwise provisioned lazily on
61
66
  first document ingest)
67
+ --with-embeddings warm the embedding model + LanceDB vector store dir now
68
+ (loads from local cache only, offline-safe; otherwise
69
+ both provision lazily on first semantic-search use)
62
70
  --cleanup-legacy-global
63
71
  remove legacy user-global Construct adapters/MCP/history
64
72
  before setup; does not uninstall a global construct CLI
@@ -330,7 +338,7 @@ function runLegacyGlobalCleanup({ homeDir, dryRun }) {
330
338
  // network call, or service registration that the real run would perform — and
331
339
  // returns before touching disk so the preview is provably side-effect-free.
332
340
 
333
- function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupLegacyGlobal }) {
341
+ function printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent, cleanupLegacyGlobal }) {
334
342
  console.log('Construct install — dry-run (no changes written)');
335
343
  console.log('────────────────────────────────────────────────');
336
344
  console.log(`Scope: ${scope}`);
@@ -350,11 +358,11 @@ function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupL
350
358
  console.log(` · ${getCanonicalOpenCodeConfigPath(homeDir)} (OpenCode config)`);
351
359
  console.log(` · ${path.join(configDir(homeDir), 'lib')} → ${path.join(ROOT_DIR, 'lib')} (hook lib symlink)`);
352
360
  console.log(` · ${path.join(stateDir(homeDir), 'workspace')} (workspace docs scaffold)`);
353
- console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
361
+ if (withEmbeddings) console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
354
362
  console.log('\nWould check / install runtime tooling:');
355
363
  console.log(' · cm (memory CLI) and cass (session search) when available on PATH');
356
- console.log('\nWould reach the network / external services:');
357
- console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
364
+ if (withEmbeddings || withDocling) console.log('\nWould reach the network / external services:');
365
+ if (withEmbeddings) console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
358
366
  if (withDocling) console.log(' · docling Python venv provisioning via uv (~10 min)');
359
367
  console.log(' · construct sync --quiet, construct sync --global, construct doctor');
360
368
  if (process.platform === 'darwin' && !noLaunchAgent) {
@@ -375,9 +383,10 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
375
383
  return;
376
384
  }
377
385
 
378
- const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--cleanup-legacy-global', '--help', '-h']);
386
+ const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--with-embeddings', '--cleanup-legacy-global', '--help', '-h']);
379
387
  const dryRun = argSet.has('--dry-run');
380
388
  const withDocling = argSet.has('--with-docling');
389
+ const withEmbeddings = argSet.has('--with-embeddings');
381
390
  const unknownFlags = args.filter((a) => {
382
391
  if (!a.startsWith('-')) return false;
383
392
  if (a.startsWith('--scope=') || a === '--scope') return false;
@@ -402,7 +411,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
402
411
  printProjectScopeGuidance();
403
412
  return;
404
413
  }
405
- printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
414
+ printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
406
415
  return;
407
416
  }
408
417
 
@@ -470,20 +479,29 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
470
479
 
471
480
  const telemetryResult = process.env.CONSTRUCT_TELEMETRY_URL
472
481
  ? { status: 'configured', note: `remote export configured (${process.env.CONSTRUCT_TELEMETRY_URL})` }
473
- : { status: 'local', note: 'local JSONL traces in .cx/traces; remote export optional' };
482
+ : { status: 'local', note: 'local JSONL traces in the machine-scoped state root; remote export optional' };
474
483
  console.log(`Telemetry: ${telemetryResult.note}`);
475
484
 
476
- fs.mkdirSync(path.dirname(defaultVectorIndexPath(homeDir)), { recursive: true });
477
-
478
- try {
479
- const warm = await warmupEmbeddingModel({ env: process.env });
480
- if (warm.degraded) {
481
- console.log(`Embeddings: degraded fallback (${warm.fallbackReason || 'model unavailable'}) semantic search will use the hashing backend`);
482
- } else {
483
- console.log(`Embeddings: ${warm.model} ready (${warm.dimensions}d, warmed in ${warm.durationMs}ms)`);
485
+ // The embedding model and LanceDB vector store dir are provisioned lazily
486
+ // by default (construct-rf26.17): a project that never runs semantic
487
+ // search never pays for the ONNX model cache or an index directory. Both
488
+ // provision on first real use (`construct ingest`, first observation
489
+ // store/search) exactly like docling below. --with-embeddings opts into
490
+ // warming both up now instead of at first query.
491
+ if (withEmbeddings) {
492
+ fs.mkdirSync(path.dirname(defaultVectorIndexPath(homeDir)), { recursive: true });
493
+ try {
494
+ const warm = await warmupEmbeddingModel({ env: process.env });
495
+ if (warm.degraded) {
496
+ console.log(`Embeddings: degraded fallback (${warm.fallbackReason || 'model unavailable'}) — semantic search will use the hashing backend`);
497
+ } else {
498
+ console.log(`Embeddings: ${warm.model} ready (${warm.dimensions}d, warmed in ${warm.durationMs}ms)`);
499
+ }
500
+ } catch (err) {
501
+ console.log(`Embeddings: warmup skipped (${err?.message || 'unknown error'}) — model will load on first use`);
484
502
  }
485
- } catch (err) {
486
- console.log(`Embeddings: warmup skipped (${err?.message || 'unknown error'}) model will load on first use`);
503
+ } else {
504
+ console.log('Embeddings: will provision on first semantic-search use (pass --with-embeddings to warm up now)');
487
505
  }
488
506
 
489
507
  // Docling (document/PDF extraction) provisions a pinned Python venv via uv,
@@ -592,7 +610,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
592
610
  console.log('Setup complete.');
593
611
 
594
612
  console.log('\nLocal services:');
595
- console.log(' Traces: local JSONL (.cx/traces)');
613
+ console.log(' Traces: local JSONL (machine-scoped state root)');
596
614
  console.log(` Vector: LanceDB (embedded)`);
597
615
  console.log(` ${managedValues.CONSTRUCT_LANCEDB_PATH}`);
598
616
  console.log(` Credentials are saved to ${getUserEnvPath(homeDir)} for later reference.`);
@@ -623,6 +641,6 @@ export function ensureWorkspace(homeDir = HOME) {
623
641
  return wsPath;
624
642
  }
625
643
 
626
- if (import.meta.url === `file://${process.argv[1]}`) {
644
+ if (isMainModule(import.meta.url)) {
627
645
  runSetup({ args: process.argv.slice(2) });
628
646
  }
@@ -87,8 +87,10 @@ export function writeConstructManifest(cwd, profile, classification) {
87
87
  * that records the recommended per-project scoping for Claude Code.
88
88
  *
89
89
  * Why a sidecar and not `settings.json`? Claude Code today only reads
90
- * `permissions`, `hooks`, and `mcpServers` from settings.json. A
91
- * `disabledPlugins` key would be ignored silently. Writing a sidecar is
90
+ * `permissions`, `hooks`, and policy keys (`allowedMcpServers`,
91
+ * `deniedMcpServers`, etc.) from settings.json MCP server definitions
92
+ * live in `~/.claude.json` or `.mcp.json` instead (construct-ranh). A
93
+ * `disabledPlugins` key would be ignored silently either way. Writing a sidecar is
92
94
  * honest — it documents the recommendation without pretending the harness
93
95
  * enforces it. The construct-mcp session-start path can read this sidecar
94
96
  * and inject a "skills out of scope for this project" note.
@@ -18,7 +18,7 @@
18
18
  * - cx-reviewer prevents silent rubber-stamp reviews
19
19
  * - cx-security prevents post-hoc threat models
20
20
  * - cx-debugger prevents symptom-only fixes
21
- * - cx-docs-keeper prevents stale-doc PRs
21
+ * - cx-operations prevents stale-doc PRs (docs-keeper folded in, construct-rf26.11)
22
22
  * - cx-designer prevents post-hoc accessibility
23
23
  */
24
24
 
@@ -75,7 +75,7 @@ export const POSTCONDITIONS = {
75
75
  reason: `rootCauseConfirmedVia must be one of: ${[...ROOT_CAUSE_SOURCES].join(', ')}.`,
76
76
  },
77
77
  ],
78
- 'cx-docs-keeper': [
78
+ 'cx-operations': [
79
79
  {
80
80
  id: 'docs-keeper.cross-doc-coherence-check-ran',
81
81
  description: 'Docs-keeper must run the cross-doc coherence check and attach a named diff.',
@@ -0,0 +1,147 @@
1
+ /**
2
+ * lib/state-root.mjs — machine-scoped state roots for heavy runtime state (ADR-0066).
3
+ *
4
+ * Two distinct scopes live under `constructDir()` (`~/.construct/` or
5
+ * `$CX_TOOLKIT_DIR`), and this module is the only place either is resolved:
6
+ *
7
+ * - Per-project state (`resolveStateRoot`/`resolveStateDir`/`resolveStatePath`):
8
+ * `~/.construct/projects/<key>/` — traces, observations, the vector index,
9
+ * task graphs. Keyed by `deriveProjectKey`, a derivation stable across
10
+ * clones of the same git remote so two checkouts of one repository share
11
+ * accumulated state. A project with no remote (a fresh local-only repo)
12
+ * falls back to a hash of its resolved absolute path.
13
+ *
14
+ * - Machine-shared state (`resolveSharedRuntimeDir`): `~/.construct/runtime/`
15
+ * — not keyed per project at all. Reserved for state whose cost scales with
16
+ * the binary itself, not with any one project (the docling venv,
17
+ * construct-rf26.16): provisioning it per-project multiplies a genuinely
18
+ * large cost for content that isn't project-specific.
19
+ *
20
+ * `.beads/` is a deliberate exception (ADR-0026): issue history travels with
21
+ * the repository and is never resolved through this module.
22
+ */
23
+
24
+ import fs from 'node:fs';
25
+ import path from 'node:path';
26
+ import { createHash } from 'node:crypto';
27
+ import { execFileSync } from 'node:child_process';
28
+
29
+ import { constructDir } from './paths.mjs';
30
+
31
+ function normalizeRemote(url) {
32
+ let s = url.trim();
33
+
34
+ // `git@host:owner/repo.git` (scp-like shorthand) has no `scheme://` prefix,
35
+ // so it needs its own split before the generic scheme/credential strip.
36
+ const scp = s.match(/^[^@/]+@([^:]+):(.+)$/);
37
+ if (scp && !/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(s)) {
38
+ s = `${scp[1]}/${scp[2]}`;
39
+ } else {
40
+ s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, '');
41
+ s = s.replace(/^[^@/]+@/, '');
42
+ }
43
+
44
+ return s.replace(/\.git$/, '').replace(/\/+$/, '').toLowerCase();
45
+ }
46
+
47
+ function readOriginRemote(projectRoot) {
48
+ try {
49
+ const out = execFileSync('git', ['remote', 'get-url', 'origin'], {
50
+ cwd: projectRoot,
51
+ stdio: ['ignore', 'pipe', 'ignore'],
52
+ timeout: 5000,
53
+ }).toString().trim();
54
+ return out || null;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ // The path-hash fallback (no remote) must be symlink-invariant: a caller that
61
+ // reaches the same directory through a symlinked path (e.g. macOS's /var/folders
62
+ // tmp symlink, or a home-directory alias) must still resolve to the same key.
63
+ // realpath is best-effort — a not-yet-created path falls back to path.resolve.
64
+
65
+ function canonicalPath(projectRoot) {
66
+ const resolved = path.resolve(projectRoot);
67
+ try {
68
+ return fs.realpathSync(resolved);
69
+ } catch {
70
+ return resolved;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Derive a stable key for a project: the normalized git origin remote when
76
+ * one exists (so every clone of the same repository resolves to the same
77
+ * key), otherwise a hash of the canonical (symlink-resolved) absolute project
78
+ * path.
79
+ */
80
+ export function deriveProjectKey(projectRoot) {
81
+ const resolved = path.resolve(projectRoot);
82
+ const remote = readOriginRemote(resolved);
83
+ const material = remote ? `remote:${normalizeRemote(remote)}` : `path:${canonicalPath(projectRoot)}`;
84
+ return createHash('sha256').update(material).digest('hex').slice(0, 24);
85
+ }
86
+
87
+ // A trailing plain-object argument is an options bag ({ ensureDir }), never a
88
+ // path segment — path segments are always strings.
89
+
90
+ function popOptions(segments) {
91
+ const last = segments[segments.length - 1];
92
+ if (last !== null && typeof last === 'object' && !Array.isArray(last)) {
93
+ return [segments.slice(0, -1), last];
94
+ }
95
+ return [segments, {}];
96
+ }
97
+
98
+ /**
99
+ * Resolve the machine-scoped state root for a project. Creates the directory
100
+ * (mkdir -p) on this access by default rather than up front — pass
101
+ * `{ ensureDir: false }` for a caller that only wants the path (e.g. a probe
102
+ * that may never actually write). Built on constructDir() so the existing
103
+ * CX_TOOLKIT_DIR override relocates per-project state along with the rest of
104
+ * `~/.construct/`.
105
+ */
106
+ export function resolveStateRoot(projectRoot, { ensureDir = true } = {}) {
107
+ const dir = path.join(constructDir(), 'projects', deriveProjectKey(projectRoot));
108
+ if (ensureDir) fs.mkdirSync(dir, { recursive: true });
109
+ return dir;
110
+ }
111
+
112
+ /**
113
+ * Resolve a subdirectory under a project's state root, creating it by
114
+ * default. Pass a trailing `{ ensureDir: false }` to skip creation.
115
+ */
116
+ export function resolveStateDir(projectRoot, ...segments) {
117
+ const [parts, { ensureDir = true }] = popOptions(segments);
118
+ const dir = path.join(resolveStateRoot(projectRoot, { ensureDir: false }), ...parts);
119
+ if (ensureDir) fs.mkdirSync(dir, { recursive: true });
120
+ return dir;
121
+ }
122
+
123
+ /**
124
+ * Resolve a file path under a project's state root, creating the parent
125
+ * directory (not the file itself) by default. Pass a trailing
126
+ * `{ ensureDir: false }` to skip creation.
127
+ */
128
+ export function resolveStatePath(projectRoot, ...segments) {
129
+ const [parts, { ensureDir = true }] = popOptions(segments);
130
+ const filePath = path.join(resolveStateRoot(projectRoot, { ensureDir: false }), ...parts);
131
+ if (ensureDir) fs.mkdirSync(path.dirname(filePath), { recursive: true });
132
+ return filePath;
133
+ }
134
+
135
+ /**
136
+ * Resolve a machine-shared runtime directory: not project-keyed, unlike
137
+ * resolveStateRoot/resolveStateDir. Every project on this machine — and every
138
+ * clone or worktree of every project — resolves the same segments to the
139
+ * identical directory. Use for state whose provisioning cost is per-machine,
140
+ * per-binary (a shared uv venv), never per-project.
141
+ */
142
+ export function resolveSharedRuntimeDir(...segments) {
143
+ const [parts, { ensureDir = true }] = popOptions(segments);
144
+ const dir = path.join(constructDir(), 'runtime', ...parts);
145
+ if (ensureDir) fs.mkdirSync(dir, { recursive: true });
146
+ return dir;
147
+ }