@geraldmaron/construct 1.2.2 → 1.3.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 (497) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +286 -220
  3. package/bin/construct-postinstall.mjs +0 -21
  4. package/commands/work/optimize-prompts.md +1 -1
  5. package/examples/distribution/sources/adr.md +2 -2
  6. package/examples/seed-observations/decisions.md +1 -1
  7. package/lib/artifact-loop-core.mjs +412 -0
  8. package/lib/artifact-manifest.mjs +6 -0
  9. package/lib/artifact-release-gate.mjs +115 -49
  10. package/lib/audit-rules.mjs +2 -4
  11. package/lib/audit-skills.mjs +32 -20
  12. package/lib/audit-specialists.mjs +44 -26
  13. package/lib/auto-docs.mjs +6 -5
  14. package/lib/brand-prose.mjs +3 -3
  15. package/lib/brand-tokens.mjs +2 -2
  16. package/lib/certification/artifact-fixtures.mjs +4 -1
  17. package/lib/certification/demo-parity.mjs +6 -32
  18. package/lib/certification/real-llm-scenarios.mjs +58 -13
  19. package/lib/certification/role-cards.mjs +7 -6
  20. package/lib/certification/role-overlays.mjs +4 -4
  21. package/lib/certification/runner.mjs +9 -2
  22. package/lib/certification/skill-inventory.mjs +34 -22
  23. package/lib/certification/skill-scenarios.mjs +21 -8
  24. package/lib/certification/specialist-contracts.mjs +4 -3
  25. package/lib/certification/specialist-scenarios.mjs +7 -6
  26. package/lib/certification/status.mjs +5 -4
  27. package/lib/cli-commands.mjs +56 -57
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +125 -0
  31. package/lib/config/schema.mjs +5 -5
  32. package/lib/config/source-targets.mjs +80 -0
  33. package/lib/contracts/validate.mjs +119 -51
  34. package/lib/decisions/golden.mjs +3 -2
  35. package/lib/decisions/registry.mjs +11 -6
  36. package/lib/demo-project.mjs +188 -0
  37. package/lib/demo-recording.mjs +9 -34
  38. package/lib/demo-script.mjs +2 -2
  39. package/lib/demo-surface.mjs +13 -131
  40. package/lib/demo-tour-renderer.mjs +92 -0
  41. package/lib/demo.mjs +69 -54
  42. package/lib/diagram-export.mjs +63 -14
  43. package/lib/doctor/source-checkout.mjs +3 -4
  44. package/lib/doctor/watchers/consistency.mjs +90 -22
  45. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  46. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  47. package/lib/doctor/watchers/service-health.mjs +4 -32
  48. package/lib/document-export.mjs +73 -9
  49. package/lib/document-extract.mjs +10 -198
  50. package/lib/embed/artifact.mjs +2 -3
  51. package/lib/embed/cli.mjs +1 -1
  52. package/lib/embed/config.mjs +1 -2
  53. package/lib/embed/customer-profiles.mjs +1 -1
  54. package/lib/embed/daemon.mjs +5 -6
  55. package/lib/embed/demand-fetch.mjs +114 -2
  56. package/lib/embed/docs-lifecycle.mjs +9 -5
  57. package/lib/embed/intake-metrics.mjs +2 -2
  58. package/lib/embed/notifications.mjs +2 -3
  59. package/lib/embed/recommendation-store.mjs +25 -22
  60. package/lib/embed/role-framing.mjs +4 -9
  61. package/lib/embedded-contract/capability.mjs +4 -2
  62. package/lib/embedded-contract/contract-version.mjs +1 -1
  63. package/lib/env-config.mjs +2 -2
  64. package/lib/flavors/loader.mjs +21 -19
  65. package/lib/graph/build-from-registry.mjs +31 -7
  66. package/lib/graph/staleness.mjs +1 -1
  67. package/lib/headhunt.mjs +82 -35
  68. package/lib/hooks/agent-tracker.mjs +2 -2
  69. package/lib/hooks/config-protection.mjs +1 -1
  70. package/lib/hooks/registry-sync.mjs +3 -3
  71. package/lib/host-capabilities.mjs +5 -5
  72. package/lib/improvement/controller.mjs +2 -2
  73. package/lib/init-unified.mjs +19 -21
  74. package/lib/intake/classify.mjs +26 -0
  75. package/lib/intake/daemon.mjs +2 -3
  76. package/lib/intake/prepare.mjs +10 -3
  77. package/lib/intake/session-prelude.mjs +1 -1
  78. package/lib/intake/tables/creative.mjs +9 -9
  79. package/lib/intake/tables/operations.mjs +4 -4
  80. package/lib/intake/tables/research.mjs +2 -2
  81. package/lib/integrations/intake-integrations.mjs +9 -10
  82. package/lib/knowledge/research-store.mjs +2 -2
  83. package/lib/mcp/server.mjs +104 -61
  84. package/lib/mcp/tool-budget.mjs +1 -46
  85. package/lib/mcp/tool-recovery.mjs +44 -0
  86. package/lib/mcp/tools/artifact-author.mjs +36 -0
  87. package/lib/mcp/tools/find-tool.mjs +86 -0
  88. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  89. package/lib/mcp/tools/project.mjs +4 -3
  90. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  91. package/lib/mcp/tools/skills.mjs +39 -22
  92. package/lib/mcp/tools/telemetry.mjs +2 -1
  93. package/lib/mcp/tools/workflow.mjs +1 -1
  94. package/lib/mcp-platform-config.mjs +7 -5
  95. package/lib/migrations/index.mjs +4 -2
  96. package/lib/migrations/v2-unified-registry.mjs +35 -0
  97. package/lib/model-router.mjs +203 -36
  98. package/lib/models/catalog.mjs +25 -9
  99. package/lib/models/execution-capability-profile.mjs +3 -3
  100. package/lib/models/execution-policy.mjs +7 -6
  101. package/lib/models/provider-poll.mjs +18 -4
  102. package/lib/opencode-config.mjs +56 -1
  103. package/lib/opencode-runtime-plugin.mjs +13 -10
  104. package/lib/oracle/artifact-gate.mjs +14 -4
  105. package/lib/oracle/cli.mjs +2 -0
  106. package/lib/oracle/dispatch.mjs +18 -1
  107. package/lib/oracle/execute.mjs +39 -7
  108. package/lib/oracle/index.mjs +1 -1
  109. package/lib/oracle/org-graph.mjs +10 -5
  110. package/lib/oracle/policy.mjs +6 -0
  111. package/lib/oracle/read-model.mjs +103 -6
  112. package/lib/oracle/reconcile.mjs +31 -4
  113. package/lib/oracle/remediation-dispatch.mjs +53 -0
  114. package/lib/oracle/routing.mjs +5 -0
  115. package/lib/oracle/synthesize.mjs +80 -3
  116. package/lib/orchestration/routing-tables.mjs +44 -10
  117. package/lib/orchestration/runtime.mjs +2 -0
  118. package/lib/orchestration/worker.mjs +1 -1
  119. package/lib/orchestration-policy.mjs +351 -62
  120. package/lib/overrides/resolver.mjs +1 -12
  121. package/lib/parity.mjs +37 -11
  122. package/lib/policy/engine.mjs +80 -15
  123. package/lib/prompt-composer.js +51 -15
  124. package/lib/prompt-metadata.mjs +3 -2
  125. package/lib/providers/connection-probe.mjs +58 -0
  126. package/lib/providers/copilot-auth.mjs +1 -1
  127. package/lib/providers/credential-bootstrap.mjs +1 -1
  128. package/lib/providers/op-run.mjs +3 -4
  129. package/lib/providers/secret-resolver.mjs +31 -0
  130. package/lib/publish-tooling.mjs +3 -11
  131. package/lib/publish.mjs +12 -29
  132. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  133. package/lib/reflect.mjs +2 -2
  134. package/lib/registry/agent-manifest.mjs +190 -0
  135. package/lib/registry/assemble.mjs +133 -0
  136. package/lib/registry/catalog.mjs +171 -0
  137. package/lib/registry/cli.mjs +590 -6
  138. package/lib/registry/consolidation.mjs +5 -4
  139. package/lib/registry/docs-sync.mjs +67 -0
  140. package/lib/registry/loader.mjs +147 -0
  141. package/lib/registry/org-io.mjs +76 -0
  142. package/lib/registry/retired-paths.mjs +71 -0
  143. package/lib/registry/surface-map.mjs +5 -7
  144. package/lib/registry/validator.mjs +438 -0
  145. package/lib/research-execution-policy.mjs +173 -0
  146. package/lib/roles/catalog.mjs +4 -9
  147. package/lib/roles/fence.mjs +107 -1
  148. package/lib/roles/flavor-bindings.mjs +68 -0
  149. package/lib/roles/gateway.mjs +140 -29
  150. package/lib/roles/manifest.mjs +22 -13
  151. package/lib/roles/router.mjs +1 -1
  152. package/lib/runtime-env.mjs +1 -1
  153. package/lib/scopes/enrich.mjs +67 -0
  154. package/lib/scopes/hooks.mjs +12 -0
  155. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  156. package/lib/scopes/loader.mjs +104 -0
  157. package/lib/scopes/rebrand.mjs +32 -0
  158. package/lib/scopes/research-profile.mjs +16 -0
  159. package/lib/scopes/teams.mjs +98 -0
  160. package/lib/service-manager.mjs +1 -117
  161. package/lib/setup-prompts.mjs +1 -1
  162. package/lib/setup.mjs +17 -28
  163. package/lib/skills/composition-graph.mjs +98 -0
  164. package/lib/skills/router.mjs +80 -0
  165. package/lib/specialist-contracts.mjs +17 -18
  166. package/lib/specialists/postconditions.mjs +2 -2
  167. package/lib/specialists/prompt-schema.mjs +6 -5
  168. package/lib/specialists/roster.mjs +7 -12
  169. package/lib/specialists/schema.mjs +9 -6
  170. package/lib/status.mjs +25 -90
  171. package/lib/storage/file-lock.mjs +6 -3
  172. package/lib/telemetry/skill-calls.mjs +1 -1
  173. package/lib/templates/doc-presentation.mjs +63 -0
  174. package/lib/templates/visual-requirements.mjs +35 -2
  175. package/lib/term-format.mjs +107 -2
  176. package/lib/test-env-setup.mjs +21 -0
  177. package/lib/ui/components.mjs +42 -0
  178. package/lib/ui/glyphs.mjs +58 -0
  179. package/lib/ui/links.mjs +115 -0
  180. package/lib/ui/theme.mjs +108 -0
  181. package/lib/uninstall/uninstall.mjs +2 -1
  182. package/lib/validator.mjs +45 -8
  183. package/lib/validators/skill-effectiveness.mjs +174 -0
  184. package/lib/validators/skills.mjs +1 -1
  185. package/package.json +12 -7
  186. package/personas/construct.md +12 -3
  187. package/platforms/claude/CLAUDE.md +1 -1
  188. package/rules/common/beads-hygiene.md +52 -0
  189. package/rules/common/neurodivergent-output.md +4 -7
  190. package/rules/common/research.md +2 -0
  191. package/scripts/sync-specialists.mjs +260 -49
  192. package/skills/ai/prompt-optimizer.md +3 -3
  193. package/skills/brand/output-vibe.md +48 -0
  194. package/skills/docs/adr-workflow.md +3 -0
  195. package/skills/docs/backlog-proposal-workflow.md +3 -0
  196. package/skills/docs/codebase-research-workflow.md +5 -2
  197. package/skills/docs/customer-profile-workflow.md +3 -0
  198. package/skills/docs/document-ingest-workflow.md +3 -0
  199. package/skills/docs/evidence-ingest-workflow.md +4 -1
  200. package/skills/docs/init-project.md +1 -1
  201. package/skills/docs/prd-workflow.md +1 -1
  202. package/skills/docs/prfaq-workflow.md +3 -0
  203. package/skills/docs/product-intelligence-workflow.md +4 -1
  204. package/skills/docs/product-signal-workflow.md +3 -0
  205. package/skills/docs/research-workflow.md +13 -6
  206. package/skills/docs/runbook-workflow.md +3 -0
  207. package/skills/docs/strategy-workflow.md +3 -0
  208. package/skills/docs/user-research-workflow.md +6 -3
  209. package/skills/operating/orchestration-reference.md +1 -1
  210. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  211. package/skills/roles/architect.ai-systems.md +1 -1
  212. package/skills/roles/architect.data.md +1 -1
  213. package/skills/roles/architect.enterprise.md +1 -1
  214. package/skills/roles/architect.integration.md +1 -1
  215. package/skills/roles/architect.md +1 -1
  216. package/skills/roles/architect.platform.md +1 -1
  217. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  218. package/skills/roles/data-analyst.experiment.md +1 -1
  219. package/skills/roles/data-analyst.md +1 -1
  220. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  221. package/skills/roles/data-analyst.product.md +1 -1
  222. package/skills/roles/data-analyst.telemetry.md +1 -1
  223. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  224. package/skills/roles/data-engineer.pipeline.md +2 -2
  225. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  226. package/skills/roles/data-engineer.warehouse.md +2 -2
  227. package/skills/roles/debugger.md +1 -1
  228. package/skills/roles/designer.accessibility.md +1 -1
  229. package/skills/roles/designer.md +1 -1
  230. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  231. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  232. package/skills/roles/engineer.md +3 -7
  233. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  234. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  235. package/skills/roles/{operator.md → operations.md} +2 -5
  236. package/skills/roles/orchestrator.md +1 -1
  237. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  238. package/skills/roles/product-manager.ai-product.md +1 -1
  239. package/skills/roles/product-manager.enterprise.md +1 -1
  240. package/skills/roles/product-manager.growth.md +1 -2
  241. package/skills/roles/product-manager.md +1 -2
  242. package/skills/roles/product-manager.platform.md +1 -1
  243. package/skills/roles/product-manager.product.md +1 -1
  244. package/skills/roles/qa.ai-eval.md +1 -1
  245. package/skills/roles/qa.api-contract.md +1 -1
  246. package/skills/roles/qa.data-pipeline.md +1 -1
  247. package/skills/roles/qa.md +1 -1
  248. package/skills/roles/qa.web-ui.md +1 -1
  249. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  250. package/skills/roles/researcher.md +1 -1
  251. package/skills/roles/reviewer.md +1 -1
  252. package/skills/roles/security.ai.md +1 -1
  253. package/skills/roles/security.appsec.md +1 -1
  254. package/skills/roles/security.cloud.md +1 -1
  255. package/skills/roles/security.legal-compliance.md +1 -1
  256. package/skills/roles/security.md +1 -1
  257. package/skills/roles/security.privacy.md +1 -1
  258. package/skills/roles/security.supply-chain.md +1 -1
  259. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  260. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  261. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  262. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  263. package/skills/routing.json +18 -0
  264. package/skills/routing.md +1 -1
  265. package/specialists/artifact-manifest.json +2 -1
  266. package/specialists/audit-enrichments.json +14 -14
  267. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  268. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  269. package/specialists/org/contracts/any-to-debugger.json +38 -0
  270. package/specialists/org/contracts/any-to-designer.json +33 -0
  271. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  272. package/specialists/org/contracts/any-to-explorer.json +39 -0
  273. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  274. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  275. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  276. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  277. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  278. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  279. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  280. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  281. package/specialists/org/contracts/architect-to-operations.json +45 -0
  282. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  283. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  284. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  285. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  286. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  287. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  288. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  289. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  290. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  291. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  292. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  293. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  294. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  295. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  296. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  297. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  298. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  299. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  300. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  301. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  302. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  303. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  304. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  305. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  306. package/specialists/org/contracts/user-to-construct.json +30 -0
  307. package/specialists/org/groups/engineering-group.json +43 -0
  308. package/specialists/org/groups/governance-group.json +38 -0
  309. package/specialists/org/groups/operations-group.json +41 -0
  310. package/specialists/org/groups/product-group.json +46 -0
  311. package/specialists/org/groups/quality-group.json +42 -0
  312. package/specialists/org/groups/strategy-group.json +41 -0
  313. package/specialists/org/policies/action-approval.json +13 -0
  314. package/specialists/org/policies/agents-routing.json +13 -0
  315. package/specialists/org/policies/architecture.json +20 -0
  316. package/specialists/org/policies/bash-safety.json +13 -0
  317. package/specialists/org/policies/beads-hygiene.json +13 -0
  318. package/specialists/org/policies/bootstrap-state.json +13 -0
  319. package/specialists/org/policies/code-review.json +13 -0
  320. package/specialists/org/policies/coding-style.json +13 -0
  321. package/specialists/org/policies/comments.json +13 -0
  322. package/specialists/org/policies/commit-approval.json +13 -0
  323. package/specialists/org/policies/contract-preconditions.json +13 -0
  324. package/specialists/org/policies/deployment.json +20 -0
  325. package/specialists/org/policies/description.json +14 -0
  326. package/specialists/org/policies/design-approval.json +19 -0
  327. package/specialists/org/policies/doc-ownership.json +13 -0
  328. package/specialists/org/policies/file-path-fence.json +13 -0
  329. package/specialists/org/policies/framing.json +13 -0
  330. package/specialists/org/policies/git-workflow.json +13 -0
  331. package/specialists/org/policies/incident-response.json +15 -0
  332. package/specialists/org/policies/intake-triage.json +15 -0
  333. package/specialists/org/policies/neurodivergent-output.json +13 -0
  334. package/specialists/org/policies/no-fabrication.json +13 -0
  335. package/specialists/org/policies/patterns.json +13 -0
  336. package/specialists/org/policies/quality-gate-approval.json +17 -0
  337. package/specialists/org/policies/release-gates.json +13 -0
  338. package/specialists/org/policies/research-evidence.json +13 -0
  339. package/specialists/org/policies/review-before-change.json +13 -0
  340. package/specialists/org/policies/rollback.json +15 -0
  341. package/specialists/org/policies/scope-change.json +20 -0
  342. package/specialists/org/policies/secret-scan.json +13 -0
  343. package/specialists/org/policies/security-approval.json +17 -0
  344. package/specialists/org/policies/security.json +13 -0
  345. package/specialists/org/policies/session-efficiency.json +13 -0
  346. package/specialists/org/policies/skill-routing.json +13 -0
  347. package/specialists/org/policies/strategic-prioritization.json +17 -0
  348. package/specialists/org/policies/testing.json +13 -0
  349. package/specialists/org/policies/tool-invisibility.json +14 -0
  350. package/specialists/org/scopes/creative.json +54 -0
  351. package/specialists/org/scopes/operations.json +51 -0
  352. package/specialists/org/scopes/research.json +60 -0
  353. package/specialists/org/scopes/rnd.json +81 -0
  354. package/specialists/org/specialists/cx-accessibility.json +69 -0
  355. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  356. package/specialists/org/specialists/cx-architect.json +89 -0
  357. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  358. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  359. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  360. package/specialists/org/specialists/cx-debugger.json +75 -0
  361. package/specialists/org/specialists/cx-designer.json +85 -0
  362. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  363. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  364. package/specialists/org/specialists/cx-engineer.json +106 -0
  365. package/specialists/org/specialists/cx-evaluator.json +69 -0
  366. package/specialists/org/specialists/cx-explorer.json +69 -0
  367. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  368. package/specialists/org/specialists/cx-operations.json +72 -0
  369. package/specialists/org/specialists/cx-oracle.json +46 -0
  370. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  371. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  372. package/specialists/org/specialists/cx-product-manager.json +102 -0
  373. package/specialists/org/specialists/cx-qa.json +79 -0
  374. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  375. package/specialists/org/specialists/cx-release-manager.json +77 -0
  376. package/specialists/org/specialists/cx-researcher.json +82 -0
  377. package/specialists/org/specialists/cx-reviewer.json +71 -0
  378. package/specialists/org/specialists/cx-security.json +91 -0
  379. package/specialists/org/specialists/cx-sre.json +94 -0
  380. package/specialists/org/specialists/cx-test-automation.json +69 -0
  381. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  382. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  383. package/specialists/org/teams/accessibility-team.json +39 -0
  384. package/specialists/org/teams/design-team.json +40 -0
  385. package/specialists/org/teams/engineering-team.json +50 -0
  386. package/specialists/org/teams/governance-team.json +41 -0
  387. package/specialists/org/teams/operations-team.json +46 -0
  388. package/specialists/org/teams/product-management-team.json +45 -0
  389. package/specialists/org/teams/quality-team.json +49 -0
  390. package/specialists/org/teams/research-team.json +39 -0
  391. package/specialists/org/teams/strategy-team.json +48 -0
  392. package/specialists/org/teams/ux-research-team.json +39 -0
  393. package/specialists/prompts/_shared/validation-contract.md +1 -1
  394. package/specialists/prompts/_team-template.md +10 -0
  395. package/specialists/prompts/cx-accessibility.md +1 -0
  396. package/specialists/prompts/cx-ai-engineer.md +1 -1
  397. package/specialists/prompts/cx-business-strategist.md +1 -1
  398. package/specialists/prompts/cx-data-engineer.md +1 -1
  399. package/specialists/prompts/cx-designer.md +1 -0
  400. package/specialists/prompts/cx-devil-advocate.md +1 -1
  401. package/specialists/prompts/cx-docs-keeper.md +1 -1
  402. package/specialists/prompts/cx-evaluator.md +1 -1
  403. package/specialists/prompts/cx-explorer.md +1 -1
  404. package/specialists/prompts/cx-operations.md +1 -1
  405. package/specialists/prompts/cx-oracle.md +7 -3
  406. package/specialists/prompts/cx-orchestrator.md +17 -1
  407. package/specialists/prompts/cx-platform-engineer.md +1 -1
  408. package/specialists/prompts/cx-product-manager.md +2 -0
  409. package/specialists/prompts/cx-release-manager.md +1 -1
  410. package/specialists/prompts/cx-researcher.md +7 -4
  411. package/specialists/prompts/cx-sre.md +1 -1
  412. package/specialists/prompts/cx-test-automation.md +2 -2
  413. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  414. package/specialists/prompts/cx-ux-researcher.md +2 -1
  415. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  416. package/templates/demos/scripts/capability-contract.json +25 -0
  417. package/templates/demos/scripts/intake-triage.json +25 -0
  418. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  419. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  420. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  421. package/templates/demos/tapes/capability-contract.tape +39 -0
  422. package/templates/demos/tapes/intake-triage.tape +39 -0
  423. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  424. package/templates/distribution/construct-brand.typ +23 -18
  425. package/templates/distribution/construct-decision.typ +1 -1
  426. package/templates/distribution/construct-pdf.typ +1 -1
  427. package/templates/distribution/construct-prd.typ +1 -1
  428. package/templates/distribution/construct-research.typ +1 -1
  429. package/templates/distribution/mermaid-puppeteer.json +8 -0
  430. package/templates/docs/persona-artifact.md +1 -1
  431. package/templates/docs/prd.md +6 -0
  432. package/lib/boundary.mjs +0 -127
  433. package/lib/certification/dashboard-api.mjs +0 -71
  434. package/lib/chat/cli.mjs +0 -333
  435. package/lib/chat/command-suggest.mjs +0 -161
  436. package/lib/chat/commands.mjs +0 -215
  437. package/lib/chat/config.mjs +0 -142
  438. package/lib/chat/context-compactor.mjs +0 -250
  439. package/lib/chat/context-continuation.mjs +0 -253
  440. package/lib/chat/continuation-source.mjs +0 -58
  441. package/lib/chat/demo-guide.mjs +0 -61
  442. package/lib/chat/design-tokens.mjs +0 -91
  443. package/lib/chat/desktop-binary.mjs +0 -81
  444. package/lib/chat/desktop-build.mjs +0 -130
  445. package/lib/chat/desktop-launcher.mjs +0 -133
  446. package/lib/chat/evidence.mjs +0 -145
  447. package/lib/chat/export.mjs +0 -74
  448. package/lib/chat/harness/driver.mjs +0 -91
  449. package/lib/chat/list-picker.mjs +0 -112
  450. package/lib/chat/model-picker.mjs +0 -356
  451. package/lib/chat/openrouter-fallback.mjs +0 -151
  452. package/lib/chat/permission-prompt.mjs +0 -33
  453. package/lib/chat/picker-catalog.mjs +0 -45
  454. package/lib/chat/policy-telemetry.mjs +0 -34
  455. package/lib/chat/present.mjs +0 -246
  456. package/lib/chat/session-context.mjs +0 -39
  457. package/lib/chat/session-persist.mjs +0 -73
  458. package/lib/chat/session-restore.mjs +0 -71
  459. package/lib/chat/session-settings.mjs +0 -53
  460. package/lib/chat/system-prompt.mjs +0 -52
  461. package/lib/chat/transparency.mjs +0 -93
  462. package/lib/chat/tui/color-scheme.mjs +0 -42
  463. package/lib/chat/tui/markdown.mjs +0 -123
  464. package/lib/chat/tui/presentation.mjs +0 -100
  465. package/lib/chat/tui/render.mjs +0 -500
  466. package/lib/chat/tui/turn-block.mjs +0 -284
  467. package/lib/chat/tui/turn-present.mjs +0 -18
  468. package/lib/chat/tui/turn-state.mjs +0 -88
  469. package/lib/chat/tui/usage.mjs +0 -122
  470. package/lib/chat/web-commands.mjs +0 -146
  471. package/lib/chat/web-launcher.mjs +0 -63
  472. package/lib/chat/web-picker-keys.mjs +0 -46
  473. package/lib/chat/web-session.mjs +0 -159
  474. package/lib/config/alias.mjs +0 -57
  475. package/lib/dashboard-demo.mjs +0 -71
  476. package/lib/dashboard-static.mjs +0 -175
  477. package/lib/install/desktop-binary-download.mjs +0 -88
  478. package/lib/profiles/loader.mjs +0 -123
  479. package/lib/profiles/rebrand.mjs +0 -46
  480. package/lib/server/auth.mjs +0 -169
  481. package/lib/server/chat-loop.mjs +0 -622
  482. package/lib/server/chat.mjs +0 -336
  483. package/lib/server/cors.mjs +0 -77
  484. package/lib/server/csrf.mjs +0 -103
  485. package/lib/server/demo-preview.mjs +0 -63
  486. package/lib/server/index.mjs +0 -2641
  487. package/lib/server/insights.mjs +0 -780
  488. package/lib/server/langfuse-login.mjs +0 -58
  489. package/lib/server/rate-limit.mjs +0 -93
  490. package/lib/server/telemetry-login.mjs +0 -100
  491. package/lib/server/webhook.mjs +0 -512
  492. package/specialists/contracts.json +0 -1032
  493. package/specialists/contracts.schema.json +0 -83
  494. package/specialists/policy-inventory.json +0 -188
  495. package/specialists/registry.json +0 -1412
  496. package/specialists/role-manifests.json +0 -217
  497. package/specialists/teams.json +0 -94
package/lib/headhunt.mjs CHANGED
@@ -13,15 +13,19 @@ import os from 'node:os';
13
13
  import path from 'node:path';
14
14
  import { createInterface } from 'node:readline';
15
15
  import { selectOption } from './tty-prompts.mjs';
16
+ import { resolveActiveScope } from './scopes/loader.mjs';
17
+ import { classifyObjectiveForScope } from './scopes/teams.mjs';
18
+ import { loadRegistry } from './registry/loader.mjs';
16
19
 
17
20
  const OVERLAY_DIRNAME = 'domain-overlays';
18
21
  const PROMOTION_DIRNAME = 'promotion-requests';
19
- const TEAMS_FILE = 'specialists/teams.json';
20
22
 
21
23
  function loadTeamTemplates(root) {
22
24
  try {
23
- const teamsPath = path.join(root, TEAMS_FILE);
24
- return JSON.parse(fs.readFileSync(teamsPath, 'utf8'));
25
+ const registry = loadRegistry({ rootDir: root });
26
+ return {
27
+ templates: registry.teams || {},
28
+ };
25
29
  } catch {
26
30
  return null;
27
31
  }
@@ -63,37 +67,70 @@ function promptQuestion(rl, question) {
63
67
  return new Promise((resolve) => rl.question(question, resolve));
64
68
  }
65
69
 
66
- function classifyOverlay(objective = '', explicitTeam = '') {
70
+ export function classifyOverlay(objective = '', explicitTeam = '', { cwd = process.cwd() } = {}) {
67
71
  if (explicitTeam) {
68
72
  return {
69
73
  focus: 'custom',
70
74
  attachTo: explicitTeam.split(',').map((value) => value.trim()).filter(Boolean),
75
+ teamFocus: 'custom-team',
71
76
  };
72
77
  }
73
78
 
79
+ const profile = resolveActiveScope(cwd);
80
+ const profileMatch = classifyObjectiveForScope(objective, profile);
81
+ if (profileMatch) return profileMatch;
82
+
74
83
  const text = objective.toLowerCase();
84
+
85
+ // NEW: Team-first classification returns both team id and specialist roles
75
86
  if (/security|policy|compliance|secrets|iam|threat|audit/.test(text)) {
76
- return { focus: 'security', attachTo: ['cx-security', 'cx-architect'] };
87
+ return {
88
+ focus: 'security',
89
+ attachTo: ['cx-security', 'cx-architect'],
90
+ teamFocus: 'governance-group',
91
+ recommendedTeam: 'governance-group',
92
+ };
77
93
  }
78
94
  if (/implement|build|module|deploy|pipeline|ci\/cd|apply|code/.test(text)) {
79
- return { focus: 'implementation', attachTo: ['cx-engineer', 'cx-architect'] };
95
+ return {
96
+ focus: 'implementation',
97
+ attachTo: ['cx-engineer', 'cx-architect'],
98
+ teamFocus: 'engineering-group',
99
+ recommendedTeam: 'engineering-group',
100
+ };
80
101
  }
81
102
  if (/runbook|docs|documentation|guide|onboard/.test(text)) {
82
- return { focus: 'documentation', attachTo: ['cx-docs-keeper', 'cx-researcher'] };
103
+ return {
104
+ focus: 'documentation',
105
+ attachTo: ['cx-docs-keeper', 'cx-researcher'],
106
+ teamFocus: 'operations-group',
107
+ recommendedTeam: 'operations-group',
108
+ };
83
109
  }
84
110
  if (/research|compare|recent|latest|best practices|upgrade|migration/.test(text)) {
85
- return { focus: 'research', attachTo: ['cx-researcher', 'cx-architect'] };
111
+ return {
112
+ focus: 'research',
113
+ attachTo: ['cx-researcher', 'cx-architect'],
114
+ teamFocus: 'strategy-group',
115
+ recommendedTeam: 'strategy-group',
116
+ };
86
117
  }
87
- return { focus: 'architecture', attachTo: ['cx-architect', 'cx-researcher'] };
118
+ return {
119
+ focus: 'architecture',
120
+ attachTo: ['cx-architect', 'cx-researcher'],
121
+ teamFocus: 'engineering-group',
122
+ recommendedTeam: 'engineering-group',
123
+ };
88
124
  }
89
125
 
90
- function buildOverlayPrompt({ domain, objective, scope, permanence, focus, freshness }) {
126
+ function buildOverlayPrompt({ domain, objective, scope, permanence, focus, freshness, teamFocus = '' }) {
91
127
  return [
92
128
  `Domain overlay: ${domain}`,
93
129
  `Objective: ${objective}`,
94
130
  scope ? `Scope: ${scope}` : null,
95
131
  `Permanence: ${permanence}`,
96
132
  `Primary focus: ${focus}`,
133
+ teamFocus ? `Team focus: ${teamFocus}` : null,
97
134
  `Freshness requirement: ${freshness}`,
98
135
  'Rules:',
99
136
  '- Treat this as an internal Construct overlay, not a permanent public role.',
@@ -102,6 +139,7 @@ function buildOverlayPrompt({ domain, objective, scope, permanence, focus, fresh
102
139
  '- Separate confirmed facts from inference.',
103
140
  '- Respect existing Construct workflow, security, and validation boundaries.',
104
141
  '- Do not broaden tool permissions just because the domain is specialized.',
142
+ '- NEW: Team-first approach — work within the team structure and escalation paths defined in your organization.',
105
143
  ].filter(Boolean).join('\n');
106
144
  }
107
145
 
@@ -342,7 +380,7 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
342
380
  if (!templateName) { listTeamTemplates(cwd); return; }
343
381
  const root = findConstructRoot(cwd);
344
382
  const teams = loadTeamTemplates(root);
345
- if (!teams) throw new Error('specialists/teams.json not found. Run from the Construct root.');
383
+ if (!teams) throw new Error('specialists/org not found. Run from the Construct root.');
346
384
  const tpl = teams.templates[templateName];
347
385
  if (!tpl) throw new Error(`Unknown template: ${templateName}. Run 'construct headhunt template' to list available templates.`);
348
386
  const objective = typeof flags.for === 'string' ? flags.for.trim() : rest.slice(1).join(' ').trim();
@@ -354,29 +392,30 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
354
392
  const overlayDir = getOverlayDir(root);
355
393
  const expiresAt = permanence === 'temporary' ? new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString() : null;
356
394
 
357
- const overlay = {
358
- id: overlayId,
359
- type: 'domain-overlay',
360
- domain: templateName,
361
- objective,
362
- scope: typeof flags.scope === 'string' ? flags.scope.trim() : null,
363
- permanence,
364
- focus: tpl.focus,
365
- attachTo: tpl.members,
366
- freshness,
367
- teamTemplate: templateName,
368
- teamId: overlayId,
369
- createdAt: now.toISOString(),
370
- expiresAt,
371
- project: path.basename(root),
372
- workflowId: null,
373
- taskKey: null,
374
- status: 'active',
375
- prompt: buildOverlayPrompt({ domain: templateName, objective, scope: typeof flags.scope === 'string' ? flags.scope : '', permanence, focus: tpl.focus, freshness }),
376
- skills: tpl.skills ?? [],
377
- promotionGates: tpl.promotionGates ?? buildPromotionGates(),
378
- challenge: null,
379
- };
395
+ const overlay = {
396
+ id: overlayId,
397
+ type: 'domain-overlay',
398
+ domain: templateName,
399
+ objective,
400
+ scope: typeof flags.scope === 'string' ? flags.scope.trim() : null,
401
+ permanence,
402
+ focus: tpl.focus,
403
+ attachTo: tpl.members,
404
+ freshness,
405
+ teamTemplate: templateName,
406
+ teamId: overlayId,
407
+ teamFocus: tpl.focus,
408
+ createdAt: now.toISOString(),
409
+ expiresAt,
410
+ project: path.basename(root),
411
+ workflowId: null,
412
+ taskKey: null,
413
+ status: 'active',
414
+ prompt: buildOverlayPrompt({ domain: templateName, objective, scope: typeof flags.scope === 'string' ? flags.scope : '', permanence, focus: tpl.focus, freshness, teamFocus: tpl.focus }),
415
+ skills: tpl.skills ?? [],
416
+ promotionGates: tpl.promotionGates ?? buildPromotionGates(),
417
+ challenge: null,
418
+ };
380
419
 
381
420
  const overlayJsonPath = path.join(overlayDir, `${overlayId}.json`);
382
421
  const overlayMdPath = path.join(overlayDir, `${overlayId}.md`);
@@ -448,7 +487,11 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
448
487
  }
449
488
 
450
489
  const freshness = typeof flags.freshness === 'string' ? flags.freshness : 'current';
451
- const classification = classifyOverlay(objective, typeof flags.team === 'string' ? flags.team : '');
490
+ const classification = classifyOverlay(
491
+ objective,
492
+ typeof flags.team === 'string' ? flags.team : '',
493
+ { cwd },
494
+ );
452
495
  const now = new Date();
453
496
  const root = findConstructRoot(cwd);
454
497
  const overlayId = `${slugify(domain)}-${now.toISOString().replace(/[:.]/g, '-')}`;
@@ -473,6 +516,9 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
473
516
  taskKey: null,
474
517
  status: permanence === 'temporary' ? 'active' : 'promotion_requested',
475
518
  teamId: overlayId,
519
+ teamFocus: classification.teamFocus || classification.focus,
520
+ recommendedTeam: classification.recommendedTeam || null,
521
+ scopeTeamSource: classification.scopeTeamSource || null,
476
522
  sourceStrategy: [
477
523
  'official docs',
478
524
  'release notes/changelogs',
@@ -488,6 +534,7 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
488
534
  permanence,
489
535
  focus: classification.focus,
490
536
  freshness,
537
+ teamFocus: classification.teamFocus,
491
538
  }),
492
539
  promotionGates: buildPromotionGates(),
493
540
  challenge: permanence === 'temporary' ? null : {
@@ -155,10 +155,10 @@ try {
155
155
  if (success !== null) {
156
156
  try {
157
157
  const { recordOutcome } = await import('../outcomes/record.mjs');
158
- const { resolveActiveProfile } = await import('../profiles/loader.mjs');
158
+ const { resolveActiveScope } = await import('../scopes/loader.mjs');
159
159
  const roleId = agentName.replace(/^cx-/, '');
160
160
  const projectCwd = input?.cwd || process.cwd();
161
- const activeProfile = resolveActiveProfile(projectCwd);
161
+ const activeProfile = resolveActiveScope(projectCwd);
162
162
  recordOutcome(projectCwd, {
163
163
  role: roleId,
164
164
  success,
@@ -52,7 +52,7 @@ const PROTECTED = [
52
52
  ];
53
53
 
54
54
  const META_FILES = [
55
- /(?:^|\/)specialists\/registry\.json$/,
55
+ /(?:^|\/)specialists\/org(?:\/|$)/,
56
56
  /(?:^|\/)lib\/setup\.mjs$/,
57
57
  /(?:^|\/)bin\/construct-postinstall\.mjs$/,
58
58
  /(?:^|\/)claude\/settings\.template\.json$/,
@@ -3,11 +3,11 @@
3
3
  * lib/hooks/registry-sync.mjs — Registry-change reminder hook.
4
4
  *
5
5
  * PostToolUse on Write|Edit. Fires only when the edited file is
6
- * specialists/registry.json (the construct repo) or an installed
6
+ * specialists/org/ (the construct repo) or an installed
7
7
  * agents/registry.json mirror. Emits a one-line reminder on stderr telling
8
8
  * the developer to run `construct sync` when they're ready. Does NOT
9
9
  * auto-execute sync — auto-execution during tests (which legitimately
10
- * mutate registry.json) races test cleanup and regenerates platform state
10
+ * mutate the registry) races test cleanup and regenerates platform state
11
11
  * mid-suite. The dev decides when to sync, the same way ordinary code
12
12
  * changes don't auto-build.
13
13
  *
@@ -30,7 +30,7 @@ const input = readHookInput();
30
30
  const filePath = input?.tool_input?.file_path || process.env.TOOL_INPUT_FILE_PATH || '';
31
31
  const matchesRegistry =
32
32
  filePath.endsWith(path.join('agents', 'registry.json')) ||
33
- filePath.endsWith('/specialists/registry.json');
33
+ filePath.includes(`${path.sep}specialists${path.sep}org`);
34
34
  if (!matchesRegistry) process.exit(0);
35
35
 
36
36
  const isNonInteractive =
@@ -153,7 +153,7 @@ export function detectActiveSessions() {
153
153
  // dispatch or config-driven agents): Claude Code, OpenCode.
154
154
  // mcp-orchestrated — no native subagent primitive, but the host calls the
155
155
  // `orchestration_run` MCP tool to reach the same orchestrated
156
- // outcome via the daemon: Codex, VS Code, Cursor, Copilot.
156
+ // outcome in-process: Codex, VS Code, Cursor, Copilot.
157
157
  // prompt-only — neither native dispatch nor MCP; instruction files only.
158
158
  // No supported 2026 host is prompt-only.
159
159
 
@@ -211,7 +211,7 @@ export function detectHostCapabilities() {
211
211
  sharedTaskRuntime: "tracker-plus-plan",
212
212
  lifecycleHooks: [],
213
213
  notes: [
214
- "Run a full multi-specialist chain via the `orchestration_run` MCP tool (start `construct dashboard`).",
214
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool (in-process; no daemon needed).",
215
215
  "Use Construct profiles, MCP project/memory tools, and the active session for single-pass work.",
216
216
  ],
217
217
  },
@@ -225,7 +225,7 @@ export function detectHostCapabilities() {
225
225
  sharedTaskRuntime: "editor-session",
226
226
  lifecycleHooks: [],
227
227
  notes: [
228
- "Run a full multi-specialist chain via the `orchestration_run` MCP tool in Copilot agent mode (start `construct dashboard`).",
228
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool in Copilot agent mode (in-process; no daemon needed).",
229
229
  "Construct's MCP servers load from `.vscode/mcp.json` (project) and the user-profile `mcp.json` (global, only when it already exists).",
230
230
  ],
231
231
  },
@@ -239,7 +239,7 @@ export function detectHostCapabilities() {
239
239
  sharedTaskRuntime: "editor-session",
240
240
  lifecycleHooks: [],
241
241
  notes: [
242
- "Run a full multi-specialist chain via the `orchestration_run` MCP tool (start `construct dashboard`).",
242
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool (in-process; no daemon needed).",
243
243
  "Construct manages Cursor MCP registrations in `.cursor/mcp.json` (project) and `~/.cursor/mcp.json` (global, when it already exists).",
244
244
  ],
245
245
  },
@@ -253,7 +253,7 @@ export function detectHostCapabilities() {
253
253
  sharedTaskRuntime: "editor-session",
254
254
  lifecycleHooks: [],
255
255
  notes: [
256
- "Copilot agent mode runs Construct's MCP tools — use `orchestration_run` for a real multi-specialist chain (start `construct dashboard`).",
256
+ "Copilot agent mode runs Construct's MCP tools — use `orchestration_run` for a real multi-specialist chain (in-process; no daemon needed).",
257
257
  "Construct also writes reusable prompt profiles under `.github/prompts/` for single-pass role work.",
258
258
  ],
259
259
  },
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * The controller is the mutation boundary for the governed loop. It admits only
6
6
  * versioned artifacts — proposal objects, dataset items, and evaluation reports,
7
- * never raw chat history — and refuses any candidate that lacks provenance,
7
+ * never raw unversioned session history — and refuses any candidate that lacks provenance,
8
8
  * lacks held-out evaluation evidence, failed a deterministic gate, names an unknown
9
9
  * approver, or has an unresolved dependency. Promotability is re-derived from the
10
10
  * report's own gate results rather than a precomputed flag, so a tampered decision
@@ -22,7 +22,7 @@ function isVersioned(artifact) {
22
22
 
23
23
  /**
24
24
  * Admit the input artifacts. Only versioned proposal/dataset/evaluation objects are
25
- * accepted; an unversioned blob (such as raw chat history) is refused
25
+ * accepted; an unversioned blob (such as raw session history) is refused
26
26
  * (construct-6zga.1.5 AC1).
27
27
  */
28
28
  export function admitArtifacts({ proposal = null, dataset = null, evaluationReport = null } = {}) {
@@ -69,11 +69,11 @@ const commitBootstrap = args.includes("--commit-bootstrap");
69
69
  const withHostFlags = new Set(HOST_KEYS.filter((k) => args.includes(`--with-${k}`)));
70
70
  const allHosts = args.includes("--all-hosts");
71
71
 
72
- // Active profile selector. `--profile=<id>` writes the field into the
73
- // project's construct.config.json so resolveActiveProfile picks it up
72
+ // Active scope selector. `--scope=<id>` writes the field into the
73
+ // project's construct.config.json so resolveActiveScope picks it up
74
74
  // on first run. Unknown ids are rejected with the available catalog.
75
- const profileArg = args.find((arg) => arg.startsWith("--profile="));
76
- const profileId = profileArg ? profileArg.split("=")[1] : null;
75
+ const scopeArg = args.find((arg) => arg.startsWith("--scope="));
76
+ const scopeId = scopeArg ? scopeArg.split("=")[1] : null;
77
77
 
78
78
  const created = [];
79
79
  const skipped = [];
@@ -745,10 +745,10 @@ async function main() {
745
745
  const { findProjectConfigPath, loadProjectConfig, writeProjectConfig, PROJECT_CONFIG_FILENAME } = await import('./config/project-config.mjs');
746
746
  const { DEFAULT_PROJECT_CONFIG } = await import('./config/schema.mjs');
747
747
 
748
- if (profileId) {
749
- const { loadProfile, listProfiles } = await import('./profiles/loader.mjs');
750
- if (!loadProfile(profileId)) {
751
- console.error(`Unknown profile: ${profileId}. Available: ${listProfiles().join(', ')}`);
748
+ if (scopeId) {
749
+ const { loadScope, listScopes } = await import('./scopes/loader.mjs');
750
+ if (!loadScope(scopeId)) {
751
+ console.error(`Unknown scope: ${scopeId}. Available: ${listScopes().join(', ')}`);
752
752
  process.exit(1);
753
753
  }
754
754
  }
@@ -763,29 +763,29 @@ async function main() {
763
763
  const cfg = {
764
764
  $schema: "./node_modules/@geraldmaron/construct/schemas/project-config.schema.json",
765
765
  ...DEFAULT_PROJECT_CONFIG,
766
- profile: profileId || 'rnd',
766
+ scope: scopeId || 'rnd',
767
767
  };
768
768
  // Explicitly write the full default config object so all options are
769
769
  // visible and discoverable to the user in the file. The $schema property
770
770
  // provides inline documentation and validation in supported editors.
771
771
  writeProjectConfig(cfgPath, cfg, { validate: true, silent: true });
772
772
  created.push(PROJECT_CONFIG_FILENAME);
773
- if (!quiet && profileId) console.log(`Profile set to ${profileId}.`);
774
- } else if (profileId) {
773
+ if (!quiet && scopeId) console.log(`Scope set to ${scopeId}.`);
774
+ } else if (scopeId) {
775
775
  const loaded = loadProjectConfig(target);
776
776
  const cfg = loaded?.raw ? { ...loaded.raw } : { version: 1 };
777
- cfg.profile = profileId;
777
+ cfg.scope = scopeId;
778
778
  writeProjectConfig(cfgPath, cfg, { validate: true, silent: true });
779
- if (!quiet) console.log(`Profile set to ${profileId}.`);
779
+ if (!quiet) console.log(`Scope set to ${scopeId}.`);
780
780
  }
781
781
  }
782
782
 
783
- // Resolve active profile to drive capability-gated scaffolding (intake
784
- // archetype, attribution stamping). Falls back to rnd when no profile is
785
- // configured — matches resolveActiveProfile semantics elsewhere.
783
+ // Resolve active scope to drive capability-gated scaffolding (intake
784
+ // archetype, attribution stamping). Falls back to rnd when no scope is
785
+ // configured — matches resolveActiveScope semantics elsewhere.
786
786
 
787
- const { resolveActiveProfile } = await import('./profiles/loader.mjs');
788
- const activeProfile = resolveActiveProfile(target, profileId) ?? null;
787
+ const { resolveActiveScope } = await import('./scopes/loader.mjs');
788
+ const activeProfile = resolveActiveScope(target, scopeId) ?? null;
789
789
  const intakeCap = activeProfile?.capabilities?.intake ?? null;
790
790
  const { gatherAttribution } = await import('./intake/attribution.mjs');
791
791
  const attribution = intakeCap?.attribution ? gatherAttribution() : null;
@@ -1228,12 +1228,10 @@ async function main() {
1228
1228
 
1229
1229
  if (!quiet) {
1230
1230
  console.log('\nServices:');
1231
- let dashboardUrl = null;
1232
1231
  for (const svc of results) {
1233
1232
  if (svc.status === 'started' || svc.status === 'reused') {
1234
1233
  const status = svc.status === 'reused' ? ' (running)' : '';
1235
1234
  console.log(` ${svc.name}${status}${svc.url ? ` ${svc.url}` : ''}`);
1236
- if (svc.name === 'Dashboard' && svc.url) dashboardUrl = svc.url;
1237
1235
  } else if (svc.status === 'failed') {
1238
1236
  console.log(` ${svc.name} — failed${svc.note ? ` (${svc.note})` : ''}`);
1239
1237
  }
@@ -1241,7 +1239,7 @@ async function main() {
1241
1239
 
1242
1240
  const telemetrySvc = results.find(r => r.name === 'Telemetry' && r.url?.includes('localhost'));
1243
1241
  if (telemetrySvc) console.log(`\nTelemetry: ${telemetrySvc.url}`);
1244
- if (dashboardUrl) console.log(`\nDashboard: ${dashboardUrl} · stop services with \`construct stop\``);
1242
+ console.log('\nStop services with `construct stop`');
1245
1243
  }
1246
1244
  } catch (error) {
1247
1245
  console.error(`Services could not be started: ${error.message}`);
@@ -34,6 +34,11 @@
34
34
  */
35
35
 
36
36
  import path from 'node:path';
37
+ import { createRequire } from 'node:module';
38
+ import { fileURLToPath } from 'node:url';
39
+
40
+ const require = createRequire(import.meta.url);
41
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
37
42
 
38
43
  // Backward-compatible exports. RND defaults preserve the symbol surface
39
44
  // referenced by intake-config, prepare, and existing tests.
@@ -72,6 +77,22 @@ const TABLES = {
72
77
  research: researchTable,
73
78
  };
74
79
 
80
+ const dynamicTableCache = new Map();
81
+
82
+ function loadClassificationTable(tableRef) {
83
+ if (!tableRef || typeof tableRef !== 'string') return null;
84
+ if (dynamicTableCache.has(tableRef)) return dynamicTableCache.get(tableRef);
85
+ const abs = path.isAbsolute(tableRef) ? tableRef : path.join(REPO_ROOT, tableRef);
86
+ try {
87
+ const mod = require(abs);
88
+ const table = mod.default ?? mod;
89
+ dynamicTableCache.set(tableRef, table);
90
+ return table;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
75
96
  // Filename patterns are explicit author-intent signals. The boost remains
76
97
  // at +0.4 so a clear keyword majority can still override (a postmortem
77
98
  // genuinely about a CVE escalation routes to security, not incident).
@@ -167,6 +188,11 @@ function matcherFor(entry) {
167
188
  function resolveTable(profile) {
168
189
  if (!profile) return rndTable;
169
190
  const id = typeof profile === 'string' ? profile : profile.id;
191
+ const tableRef = typeof profile === 'object' ? profile?.intake?.classificationTable : null;
192
+ if (typeof tableRef === 'string') {
193
+ const fromPath = loadClassificationTable(tableRef);
194
+ if (fromPath) return fromPath;
195
+ }
170
196
  return TABLES[id] || rndTable;
171
197
  }
172
198
 
@@ -19,14 +19,13 @@ import { join, basename } from 'node:path';
19
19
 
20
20
  import { createDaemon, classifyPacket } from '../daemons/contract.mjs';
21
21
  import { memoryCapMbFor } from '../resources/process-budget.mjs';
22
- import { stateDir } from '../config/xdg.mjs';
23
22
 
24
23
  const KILLSWITCH_ENV = 'CONSTRUCT_INTAKE_DAEMON';
25
24
 
26
25
  function inboxDir(cwd) { return join(cwd, 'inbox'); }
27
26
  function pendingDir(cwd) { return join(cwd, '.cx', 'intake', 'pending'); }
28
27
  function deadLetterDir(cwd) { return join(cwd, '.cx', 'intake', 'dead-letter'); }
29
- function heartbeatPath() { return join(stateDir(), 'intake-daemon.heartbeat'); }
28
+ function heartbeatPath(cwd) { return join(cwd, '.cx', 'runtime', 'intake-daemon.heartbeat'); }
30
29
 
31
30
  function ensureDir(dir) {
32
31
  try { mkdirSync(dir, { recursive: true }); } catch { /* ignore */ }
@@ -128,7 +127,7 @@ export function buildIntakeDaemon({ cwd = process.cwd(), intervalMs = 60_000, cl
128
127
  name: 'intake',
129
128
  intervalMs,
130
129
  killswitchEnv: KILLSWITCH_ENV,
131
- heartbeatPath: heartbeatPath(),
130
+ heartbeatPath: heartbeatPath(cwd),
132
131
  maxRuntimeMs: 24 * 60 * 60 * 1000,
133
132
  maxIdleTicks: 6,
134
133
  memoryCapMb: memoryCapMbFor('intake', cwd),
@@ -34,7 +34,8 @@ import { suggestDocsLaneForFile } from '../docs-routing.mjs';
34
34
  import { createIntakeQueue } from './queue.mjs';
35
35
  import { classifyRdIntake, suggestTags } from './classify.mjs';
36
36
  import { detectCustomerMentions, linkSignalToCustomer, updateCustomerProfile } from '../embed/customer-profiles.mjs';
37
- import { resolveActiveProfile } from '../profiles/loader.mjs';
37
+ import { resolveActiveScope } from '../scopes/loader.mjs';
38
+ import { resolveResearchProfile } from '../scopes/research-profile.mjs';
38
39
  import { emitBestEffort as emitRoleEvent } from '../roles/event-bus.mjs';
39
40
  import { gatherAttribution, stampAttribution } from './attribution.mjs';
40
41
  import { MANIFEST_REL_PATH } from './manifest.mjs';
@@ -91,13 +92,19 @@ export async function prepareIntakeForIngestedFile({
91
92
  // Resolve the active profile so non-RND projects route to their own table.
92
93
  // Falls back to rnd for any project without explicit configuration, which
93
94
  // preserves prior behavior for every existing user.
94
- const activeProfile = resolveActiveProfile(rootDir);
95
- const triage = classifyFn({
95
+ const activeProfile = resolveActiveScope(rootDir);
96
+ const classified = classifyFn({
96
97
  sourcePath: ingestedFile.sourcePath,
97
98
  extractedText: extracted,
98
99
  related,
99
100
  profile: activeProfile?.id,
100
101
  });
102
+ const researchProfile = resolveResearchProfile({
103
+ intakeType: classified?.intakeType,
104
+ artifactType: lane,
105
+ scope: activeProfile,
106
+ });
107
+ const triage = researchProfile ? { ...classified, researchProfile } : classified;
101
108
 
102
109
  // Detect customer mentions and update profiles
103
110
  const customerMentions = detectCustomerMentions(extracted.slice(0, 2000));
@@ -13,7 +13,7 @@
13
13
  import { createIntakeQueue } from './queue.mjs';
14
14
  import { formatTriageLine } from './classify.mjs';
15
15
  import { isBrokered } from '../mcp/broker.mjs';
16
- import { getRebrand } from '../profiles/rebrand.mjs';
16
+ import { getRebrand } from '../scopes/rebrand.mjs';
17
17
  import { homedir as osHomedir } from 'node:os';
18
18
  import { readLastTick } from '../oracle/index.mjs';
19
19
  import { listPending } from '../oracle/actions.mjs';
@@ -11,8 +11,8 @@ export const STAGES = ['brief', 'research', 'draft', 'review', 'publish', 'measu
11
11
  export const UNKNOWN_TRIAGE = {
12
12
  intakeType: 'unknown',
13
13
  rdStage: 'unknown',
14
- primaryOwner: 'operator',
15
- recommendedChain: ['operator'],
14
+ primaryOwner: 'operations',
15
+ recommendedChain: ['operations'],
16
16
  recommendedAction: 'summarize',
17
17
  risk: 'low',
18
18
  requiresApproval: false,
@@ -33,8 +33,8 @@ export const CLASSIFICATION_TABLE = [
33
33
  intakeType: 'brief',
34
34
  keywords: ['brief', 'campaign', 'launch plan', 'go-to-market', 'gtm', 'positioning', 'narrative', 'audience'],
35
35
  rdStage: 'brief',
36
- primaryOwner: 'product-lead',
37
- recommendedChain: ['product-lead', 'researcher', 'content-writer'],
36
+ primaryOwner: 'product-manager',
37
+ recommendedChain: ['product-manager', 'researcher', 'docs-keeper'],
38
38
  recommendedAction: 'draft-prd',
39
39
  risk: 'medium',
40
40
  requiresApproval: false,
@@ -43,8 +43,8 @@ export const CLASSIFICATION_TABLE = [
43
43
  intakeType: 'content-request',
44
44
  keywords: ['blog', 'article', 'landing page', 'social post', 'newsletter', 'email copy', 'video script', 'headline', 'post', 'content'],
45
45
  rdStage: 'draft',
46
- primaryOwner: 'content-writer',
47
- recommendedChain: ['content-writer', 'reviewer'],
46
+ primaryOwner: 'docs-keeper',
47
+ recommendedChain: ['docs-keeper', 'reviewer'],
48
48
  recommendedAction: 'draft-rfc',
49
49
  risk: 'low',
50
50
  requiresApproval: false,
@@ -74,7 +74,7 @@ export const CLASSIFICATION_TABLE = [
74
74
  keywords: ['report', 'dashboard', 'attribution', 'ctr', 'cpa', 'roas', 'conversion', 'funnel', 'engagement'],
75
75
  rdStage: 'measure',
76
76
  primaryOwner: 'data-analyst',
77
- recommendedChain: ['data-analyst', 'product-lead'],
77
+ recommendedChain: ['data-analyst', 'product-manager'],
78
78
  recommendedAction: 'evaluate',
79
79
  risk: 'low',
80
80
  requiresApproval: false,
@@ -83,8 +83,8 @@ export const CLASSIFICATION_TABLE = [
83
83
  intakeType: 'ops',
84
84
  keywords: ['process', 'workflow', 'template', 'sla', 'calendar', 'planning', 'editorial calendar'],
85
85
  rdStage: 'review',
86
- primaryOwner: 'operator',
87
- recommendedChain: ['operator', 'product-lead'],
86
+ primaryOwner: 'operations',
87
+ recommendedChain: ['operations', 'product-manager'],
88
88
  recommendedAction: 'create-runbook',
89
89
  risk: 'low',
90
90
  requiresApproval: false,
@@ -12,8 +12,8 @@ export const STAGES = ['request', 'triage', 'resolve', 'document', 'improve'];
12
12
  export const UNKNOWN_TRIAGE = {
13
13
  intakeType: 'unknown',
14
14
  rdStage: 'unknown',
15
- primaryOwner: 'operator',
16
- recommendedChain: ['operator'],
15
+ primaryOwner: 'operations',
16
+ recommendedChain: ['operations'],
17
17
  recommendedAction: 'summarize',
18
18
  risk: 'low',
19
19
  requiresApproval: false,
@@ -74,8 +74,8 @@ export const CLASSIFICATION_TABLE = [
74
74
  intakeType: 'request',
75
75
  keywords: ['request', 'access', 'feature request', 'integration', 'new tool', 'change request', 'how do i', 'can someone'],
76
76
  rdStage: 'triage',
77
- primaryOwner: 'operator',
78
- recommendedChain: ['operator', 'engineer'],
77
+ primaryOwner: 'operations',
78
+ recommendedChain: ['operations', 'engineer'],
79
79
  recommendedAction: 'clarify',
80
80
  risk: 'low',
81
81
  requiresApproval: false,
@@ -74,8 +74,8 @@ export const CLASSIFICATION_TABLE = [
74
74
  intakeType: 'ops',
75
75
  keywords: ['process', 'methodology', 'template', 'protocol', 'consent form', 'data handling'],
76
76
  rdStage: 'gather',
77
- primaryOwner: 'operator',
78
- recommendedChain: ['operator', 'researcher'],
77
+ primaryOwner: 'operations',
78
+ recommendedChain: ['operations', 'researcher'],
79
79
  recommendedAction: 'create-runbook',
80
80
  risk: 'low',
81
81
  requiresApproval: false,