@geraldmaron/construct 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (505) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +235 -209
  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 +55 -54
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +90 -24
  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/cli.mjs +12 -2
  44. package/lib/doctor/source-checkout.mjs +3 -4
  45. package/lib/doctor/watchers/consistency.mjs +148 -43
  46. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  47. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  48. package/lib/doctor/watchers/service-health.mjs +4 -32
  49. package/lib/document-export.mjs +73 -9
  50. package/lib/document-extract.mjs +10 -198
  51. package/lib/embed/artifact.mjs +2 -3
  52. package/lib/embed/cli.mjs +1 -1
  53. package/lib/embed/config.mjs +1 -2
  54. package/lib/embed/customer-profiles.mjs +1 -1
  55. package/lib/embed/daemon.mjs +5 -6
  56. package/lib/embed/demand-fetch.mjs +114 -2
  57. package/lib/embed/docs-lifecycle.mjs +9 -5
  58. package/lib/embed/intake-metrics.mjs +2 -2
  59. package/lib/embed/notifications.mjs +2 -3
  60. package/lib/embed/recommendation-store.mjs +25 -22
  61. package/lib/embed/role-framing.mjs +4 -9
  62. package/lib/embedded-contract/capability.mjs +4 -2
  63. package/lib/embedded-contract/contract-version.mjs +1 -1
  64. package/lib/env-config.mjs +2 -2
  65. package/lib/flavors/loader.mjs +21 -19
  66. package/lib/graph/build-from-registry.mjs +31 -7
  67. package/lib/graph/staleness.mjs +1 -1
  68. package/lib/headhunt.mjs +82 -35
  69. package/lib/hooks/agent-tracker.mjs +2 -2
  70. package/lib/hooks/config-protection.mjs +1 -1
  71. package/lib/hooks/registry-sync.mjs +3 -3
  72. package/lib/host-capabilities.mjs +5 -5
  73. package/lib/improvement/controller.mjs +2 -2
  74. package/lib/init-unified.mjs +19 -21
  75. package/lib/intake/classify.mjs +26 -0
  76. package/lib/intake/daemon.mjs +2 -3
  77. package/lib/intake/prepare.mjs +10 -3
  78. package/lib/intake/session-prelude.mjs +1 -1
  79. package/lib/intake/tables/creative.mjs +9 -9
  80. package/lib/intake/tables/operations.mjs +4 -4
  81. package/lib/intake/tables/research.mjs +2 -2
  82. package/lib/integrations/intake-integrations.mjs +9 -10
  83. package/lib/knowledge/research-store.mjs +2 -2
  84. package/lib/mcp/server.mjs +104 -61
  85. package/lib/mcp/tool-budget.mjs +1 -46
  86. package/lib/mcp/tool-recovery.mjs +44 -0
  87. package/lib/mcp/tools/artifact-author.mjs +36 -0
  88. package/lib/mcp/tools/find-tool.mjs +86 -0
  89. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  90. package/lib/mcp/tools/project.mjs +4 -3
  91. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  92. package/lib/mcp/tools/skills.mjs +39 -22
  93. package/lib/mcp/tools/telemetry.mjs +2 -1
  94. package/lib/mcp/tools/workflow.mjs +1 -1
  95. package/lib/mcp-platform-config.mjs +7 -5
  96. package/lib/migrations/index.mjs +4 -2
  97. package/lib/migrations/v2-unified-registry.mjs +35 -0
  98. package/lib/model-router.mjs +203 -36
  99. package/lib/models/catalog.mjs +25 -9
  100. package/lib/models/execution-capability-profile.mjs +3 -3
  101. package/lib/models/execution-policy.mjs +7 -6
  102. package/lib/models/provider-poll.mjs +18 -4
  103. package/lib/opencode-config.mjs +56 -1
  104. package/lib/opencode-runtime-plugin.mjs +13 -10
  105. package/lib/oracle/artifact-gate.mjs +14 -4
  106. package/lib/oracle/cli.mjs +2 -0
  107. package/lib/oracle/dispatch.mjs +18 -1
  108. package/lib/oracle/execute.mjs +39 -7
  109. package/lib/oracle/index.mjs +1 -1
  110. package/lib/oracle/org-graph.mjs +10 -5
  111. package/lib/oracle/policy.mjs +6 -0
  112. package/lib/oracle/read-model.mjs +103 -6
  113. package/lib/oracle/reconcile.mjs +31 -4
  114. package/lib/oracle/remediation-dispatch.mjs +53 -0
  115. package/lib/oracle/routing.mjs +5 -0
  116. package/lib/oracle/synthesize.mjs +80 -3
  117. package/lib/orchestration/routing-tables.mjs +44 -10
  118. package/lib/orchestration/runtime.mjs +2 -0
  119. package/lib/orchestration/worker.mjs +1 -1
  120. package/lib/orchestration-policy.mjs +351 -62
  121. package/lib/overrides/resolver.mjs +1 -12
  122. package/lib/parity.mjs +37 -11
  123. package/lib/policy/engine.mjs +80 -15
  124. package/lib/prompt-composer.js +51 -15
  125. package/lib/prompt-metadata.mjs +3 -2
  126. package/lib/providers/connection-probe.mjs +58 -0
  127. package/lib/providers/copilot-auth.mjs +1 -1
  128. package/lib/providers/credential-bootstrap.mjs +1 -1
  129. package/lib/providers/op-run.mjs +3 -4
  130. package/lib/providers/secret-resolver.mjs +31 -0
  131. package/lib/publish-tooling.mjs +3 -11
  132. package/lib/publish.mjs +12 -29
  133. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  134. package/lib/reflect.mjs +2 -2
  135. package/lib/registry/agent-manifest.mjs +190 -0
  136. package/lib/registry/assemble.mjs +133 -0
  137. package/lib/registry/catalog.mjs +171 -0
  138. package/lib/registry/cli.mjs +590 -6
  139. package/lib/registry/consolidation.mjs +5 -4
  140. package/lib/registry/docs-sync.mjs +67 -0
  141. package/lib/registry/loader.mjs +147 -0
  142. package/lib/registry/org-io.mjs +76 -0
  143. package/lib/registry/retired-paths.mjs +71 -0
  144. package/lib/registry/surface-map.mjs +5 -7
  145. package/lib/registry/validator.mjs +438 -0
  146. package/lib/research-execution-policy.mjs +173 -0
  147. package/lib/roles/catalog.mjs +4 -9
  148. package/lib/roles/fence.mjs +107 -1
  149. package/lib/roles/flavor-bindings.mjs +68 -0
  150. package/lib/roles/gateway.mjs +140 -29
  151. package/lib/roles/manifest.mjs +22 -13
  152. package/lib/roles/router.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 +6 -26
  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 -8
  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 +269 -51
  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/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  433. package/apps/chat/engine/loop-driver.mjs +0 -211
  434. package/apps/chat/engine/models.mjs +0 -122
  435. package/apps/chat/engine/provider-adapters.mjs +0 -171
  436. package/apps/chat/engine/tools/permission.mjs +0 -54
  437. package/apps/chat/engine/tools/primitives.mjs +0 -180
  438. package/apps/chat/engine/tools/registry.mjs +0 -122
  439. package/apps/chat/engine/turn-controls.mjs +0 -70
  440. package/lib/boundary.mjs +0 -127
  441. package/lib/certification/dashboard-api.mjs +0 -71
  442. package/lib/chat/cli.mjs +0 -333
  443. package/lib/chat/command-suggest.mjs +0 -161
  444. package/lib/chat/commands.mjs +0 -215
  445. package/lib/chat/config.mjs +0 -142
  446. package/lib/chat/context-compactor.mjs +0 -250
  447. package/lib/chat/context-continuation.mjs +0 -253
  448. package/lib/chat/continuation-source.mjs +0 -58
  449. package/lib/chat/demo-guide.mjs +0 -61
  450. package/lib/chat/design-tokens.mjs +0 -91
  451. package/lib/chat/desktop-binary.mjs +0 -81
  452. package/lib/chat/desktop-build.mjs +0 -130
  453. package/lib/chat/desktop-launcher.mjs +0 -133
  454. package/lib/chat/evidence.mjs +0 -145
  455. package/lib/chat/export.mjs +0 -74
  456. package/lib/chat/harness/driver.mjs +0 -91
  457. package/lib/chat/list-picker.mjs +0 -112
  458. package/lib/chat/model-picker.mjs +0 -356
  459. package/lib/chat/openrouter-fallback.mjs +0 -151
  460. package/lib/chat/permission-prompt.mjs +0 -33
  461. package/lib/chat/picker-catalog.mjs +0 -45
  462. package/lib/chat/policy-telemetry.mjs +0 -34
  463. package/lib/chat/present.mjs +0 -246
  464. package/lib/chat/session-context.mjs +0 -39
  465. package/lib/chat/session-persist.mjs +0 -73
  466. package/lib/chat/session-restore.mjs +0 -71
  467. package/lib/chat/session-settings.mjs +0 -53
  468. package/lib/chat/system-prompt.mjs +0 -52
  469. package/lib/chat/transparency.mjs +0 -93
  470. package/lib/chat/tui/color-scheme.mjs +0 -42
  471. package/lib/chat/tui/markdown.mjs +0 -123
  472. package/lib/chat/tui/presentation.mjs +0 -100
  473. package/lib/chat/tui/render.mjs +0 -500
  474. package/lib/chat/tui/turn-block.mjs +0 -284
  475. package/lib/chat/tui/turn-present.mjs +0 -18
  476. package/lib/chat/tui/turn-state.mjs +0 -88
  477. package/lib/chat/tui/usage.mjs +0 -122
  478. package/lib/chat/web-commands.mjs +0 -146
  479. package/lib/chat/web-launcher.mjs +0 -63
  480. package/lib/chat/web-picker-keys.mjs +0 -46
  481. package/lib/chat/web-session.mjs +0 -159
  482. package/lib/config/alias.mjs +0 -57
  483. package/lib/dashboard-demo.mjs +0 -71
  484. package/lib/dashboard-static.mjs +0 -175
  485. package/lib/install/desktop-binary-download.mjs +0 -88
  486. package/lib/profiles/loader.mjs +0 -123
  487. package/lib/profiles/rebrand.mjs +0 -46
  488. package/lib/server/auth.mjs +0 -169
  489. package/lib/server/chat-loop.mjs +0 -622
  490. package/lib/server/chat.mjs +0 -336
  491. package/lib/server/cors.mjs +0 -77
  492. package/lib/server/csrf.mjs +0 -103
  493. package/lib/server/demo-preview.mjs +0 -63
  494. package/lib/server/index.mjs +0 -2641
  495. package/lib/server/insights.mjs +0 -780
  496. package/lib/server/langfuse-login.mjs +0 -58
  497. package/lib/server/rate-limit.mjs +0 -93
  498. package/lib/server/telemetry-login.mjs +0 -100
  499. package/lib/server/webhook.mjs +0 -512
  500. package/specialists/contracts.json +0 -1032
  501. package/specialists/contracts.schema.json +0 -83
  502. package/specialists/policy-inventory.json +0 -188
  503. package/specialists/registry.json +0 -1412
  504. package/specialists/role-manifests.json +0 -217
  505. package/specialists/teams.json +0 -94
@@ -1,7 +1,7 @@
1
1
  /**
2
- * lib/profiles/lifecycle.mjs — Profile lifecycle operations.
2
+ * lib/scopes/lifecycle.mjs — Scope lifecycle operations.
3
3
  *
4
- * A profile is a curated description of an org's work loop, intake taxonomy,
4
+ * A scope is a curated description of an org's work loop, intake taxonomy,
5
5
  * role set, and rebrand language. Creating one is a research task, not a
6
6
  * code task. The module enforces that by producing a draft + a requirements
7
7
  * brief that names which existing Construct specialists are expected to
@@ -9,44 +9,44 @@
9
9
  * answers, then promote the draft.
10
10
  *
11
11
  * Lifecycle stages:
12
- * draft - in `.cx/profiles/draft-<id>/` with requirements.md + profile.json
13
- * active - in `profiles/<id>.json` (curated) or `.cx/profile.json` (custom)
14
- * archived - in `archive/profiles/<id>/` with the final state and an
12
+ * draft - in `.cx/scopes/draft-<id>/` with requirements.md + scope.json
13
+ * active - in `specialists/org/scopes/<id>.json` (curated) or `.cx/scope.json` (custom)
14
+ * archived - in `archive/scopes/<id>/` with the final state and an
15
15
  * archive-note.md explaining why it was retired
16
16
  *
17
- * Health: per-profile observation and outcome counts pulled from the existing
18
- * stores, so profile health travels alongside the rest of the learning loops.
17
+ * Health: per-scope observation and outcome counts pulled from the existing
18
+ * stores, so scope health travels alongside the rest of the learning loops.
19
19
  *
20
- * Lifecycle events: createDraftProfile and archiveProfile each fire a
21
- * profile.updated role event so subscribers can react to taxonomy changes
20
+ * Lifecycle events: createDraftScope and archiveScope each fire a
21
+ * scope.updated role event so subscribers can react to taxonomy changes
22
22
  * without polling the filesystem.
23
23
  */
24
24
  import fs from 'node:fs';
25
25
  import path from 'node:path';
26
26
  import { fileURLToPath } from 'node:url';
27
27
 
28
- import { listProfiles, loadProfile } from './loader.mjs';
28
+ import { listScopes, loadScope } from './loader.mjs';
29
29
  import { emitBestEffort as emitRoleEvent } from '../roles/event-bus.mjs';
30
30
 
31
31
  const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
32
32
  const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
33
33
 
34
34
  function draftDir(cwd, id) {
35
- return path.join(cwd, '.cx', 'profiles', `draft-${id}`);
35
+ return path.join(cwd, '.cx', 'scopes', `draft-${id}`);
36
36
  }
37
37
 
38
38
  function archiveDir(cwd, id) {
39
- return path.join(cwd, 'archive', 'profiles', id);
39
+ return path.join(cwd, 'archive', 'scopes', id);
40
40
  }
41
41
 
42
- const REQUIREMENTS_BRIEF = (id, displayName) => `# Profile requirements: ${displayName} (${id})
42
+ const REQUIREMENTS_BRIEF = (id, displayName) => `# Scope requirements: ${displayName} (${id})
43
43
 
44
- This brief is a discovery and design contract for a new Construct profile. Methodology lives in \`docs/guides/concepts/persona-research.md\` and lifecycle in \`docs/guides/concepts/profile-lifecycle.md\`. Treat each section as a task to dispatch to the named specialist. A draft profile is not ready to promote until every section has evidence backing it. The aim is research-grounded, not opinion-grounded.
44
+ This brief is a discovery and design contract for a new Construct scope. Methodology lives in \`docs/guides/concepts/persona-research.md\` and lifecycle in \`docs/guides/concepts/scope-lifecycle.md\`. Treat each section as a task to dispatch to the named specialist. A draft scope is not ready to promote until every section has evidence backing it. The aim is research-grounded, not opinion-grounded.
45
45
 
46
46
  Specialists used here are the existing Construct cx-* personas (already in the registry). They are dispatched the standard way; this doc just names which questions each one owns.
47
47
 
48
48
  This draft scaffolding produces:
49
- - \`profile.json\` — the structural definition (departments, roles, intake)
49
+ - \`scope.json\` — the structural definition (departments, roles, intake)
50
50
  - \`requirements.md\` — this brief
51
51
  - \`personas/<role>.md\` — one persona artifact per role (filled during the discover phase)
52
52
  - \`departments/<dept>.md\` — one charter doc per department (filled during the frame phase)
@@ -77,17 +77,17 @@ Goal: turn discovery into an intake taxonomy and a stage sequence.
77
77
 
78
78
  Goal: define the role set and how it interlocks with the curated cx-* registry.
79
79
 
80
- - List role ids the profile will reference (max 80, grouped into max 12 departments with max 20 roles per department). For each, indicate one of:
80
+ - List role ids the scope will reference (max 80, grouped into max 12 departments with max 20 roles per department). For each, indicate one of:
81
81
  reuse-existing (name the cx-* agent), create-new (specify scope), or compose-overlay (which base role + which flavor).
82
82
  - Identify cross-role handoffs. Where can ambiguity stall the loop? Name the orchestrator role.
83
- - Validate against the per-role cap (max 6 flavors per role per profile).
84
- - Acceptance: every declared role either exists in specialists/registry.json or has a written scope statement.
83
+ - Validate against the per-role cap (max 6 flavors per role per scope).
84
+ - Acceptance: every declared role either exists in specialists/org or has a written scope statement.
85
85
 
86
86
  ## 4. Validation (cx-evaluator)
87
87
 
88
88
  Goal: prove the draft works on real signals before promotion.
89
89
 
90
- - Run \`construct profile classify --draft=${id} --fixture=<path>\` against at least 5 representative signals.
90
+ - Run \`construct scope classify --draft=${id} --fixture=<path>\` against at least 5 representative signals.
91
91
  - Score: precision (right intake type), recall (no false unknowns), routing-confidence (median).
92
92
  - Acceptance: precision >= 0.7, recall >= 0.7, no \`unknown\` for the canonical signals.
93
93
 
@@ -95,29 +95,29 @@ Goal: prove the draft works on real signals before promotion.
95
95
 
96
96
  Goal: move the draft into the active catalog.
97
97
 
98
- - Curated path: hand-edit \`profiles/${id}.json\` from this draft, open a PR, run \`npm run lint:profiles\`.
99
- - Custom path: copy the draft profile.json to \`<project>/.cx/profile.json\` with \`"custom": true\`; \`construct profile set\` for switching between curated.
98
+ - Curated path: hand-edit \`specialists/org/scopes/${id}.json\` from this draft, open a PR, run \`npm run lint:scopes\`.
99
+ - Custom path: copy the draft scope.json to \`<project>/.cx/scope.json\` with \`"custom": true\`; \`construct scope set\` for switching between curated.
100
100
  - Either path requires the validation acceptance criteria above to be met.
101
101
 
102
102
  ## 6. Health monitoring (cx-evaluator + cx-trace-reviewer)
103
103
 
104
- Goal: keep the profile honest after it ships.
104
+ Goal: keep the scope honest after it ships.
105
105
 
106
- - \`construct profile health ${id}\` reports observation counts, per-role outcome rates, classification confidence over a window.
106
+ - \`construct scope health ${id}\` reports observation counts, per-role outcome rates, classification confidence over a window.
107
107
  - Any role with success-rate < 0.5 across 10+ runs triggers a review: is the role wrong, the prompt wrong, or the routing wrong?
108
- - Health data is the input for future profile revisions; never edit a profile without a health report first.
108
+ - Health data is the input for future scope revisions; never edit a scope without a health report first.
109
109
 
110
110
  ## 7. Archive (cx-docs-keeper + operator)
111
111
 
112
- Goal: retire a profile cleanly without losing the learning.
112
+ Goal: retire a scope cleanly without losing the learning.
113
113
 
114
- - \`construct profile archive ${id}\` moves the profile JSON and the intake table reference into \`archive/profiles/${id}/\`, including the final health report as evidence.
115
- - Observations and outcomes already recorded under this profile stay in place; they are durable.
114
+ - \`construct scope archive ${id}\` moves the scope JSON and the intake table reference into \`archive/scopes/${id}/\`, including the final health report as evidence.
115
+ - Observations and outcomes already recorded under this scope stay in place; they are durable.
116
116
  - An archive-note.md explains why it was retired (superseded by, deprecated for, merged into).
117
117
  `;
118
118
 
119
119
  const DRAFT_PROFILE_TEMPLATE = (id, displayName) => ({
120
- $schema: '../schemas/profile.schema.json',
120
+ $schema: '../schemas/scope.schema.json',
121
121
  id,
122
122
  displayName: displayName || id,
123
123
  tagline: 'Draft. Fill in via the requirements brief.',
@@ -200,11 +200,11 @@ function departmentTemplate(id, displayName) {
200
200
  }
201
201
 
202
202
  /**
203
- * Scaffold a draft profile under .cx/profiles/draft-<id>/. Returns the paths
204
- * to the requirements brief and the draft profile JSON.
203
+ * Scaffold a draft scope under .cx/scopes/draft-<id>/. Returns the paths
204
+ * to the requirements brief and the draft scope JSON.
205
205
  */
206
206
  /**
207
- * Scaffold a draft profile + its persona and department research artifacts.
207
+ * Scaffold a draft scope + its persona and department research artifacts.
208
208
  *
209
209
  * @param {object} args
210
210
  * @param {string} args.cwd
@@ -213,24 +213,24 @@ function departmentTemplate(id, displayName) {
213
213
  * @param {string[]} [args.seedRoles] - role ids to scaffold persona files for
214
214
  * @param {Array<{id:string,displayName:string}>} [args.seedDepartments] - departments to scaffold charters for
215
215
  */
216
- export function createDraftProfile({ cwd, id, displayName, seedRoles = [], seedDepartments = [] }) {
217
- if (!cwd || !id) throw new Error('createDraftProfile: cwd and id are required');
216
+ export function createDraftScope({ cwd, id, displayName, seedRoles = [], seedDepartments = [] }) {
217
+ if (!cwd || !id) throw new Error('createDraftScope: cwd and id are required');
218
218
  if (!/^[a-z][a-z0-9-]{1,30}$/.test(id)) {
219
- throw new Error('createDraftProfile: id must match ^[a-z][a-z0-9-]{1,30}$');
219
+ throw new Error('createDraftScope: id must match ^[a-z][a-z0-9-]{1,30}$');
220
220
  }
221
- if (listProfiles().includes(id)) {
222
- throw new Error(`createDraftProfile: ${id} already exists in the curated catalog`);
221
+ if (listScopes().includes(id)) {
222
+ throw new Error(`createDraftScope: ${id} already exists in the curated catalog`);
223
223
  }
224
224
  const dir = draftDir(cwd, id);
225
225
  if (fs.existsSync(dir)) {
226
- throw new Error(`createDraftProfile: draft already exists at ${path.relative(cwd, dir)}; archive or delete it before recreating`);
226
+ throw new Error(`createDraftScope: draft already exists at ${path.relative(cwd, dir)}; archive or delete it before recreating`);
227
227
  }
228
228
  fs.mkdirSync(dir, { recursive: true });
229
229
  fs.mkdirSync(path.join(dir, 'personas'), { recursive: true });
230
230
  fs.mkdirSync(path.join(dir, 'departments'), { recursive: true });
231
231
 
232
232
  const briefPath = path.join(dir, 'requirements.md');
233
- const draftPath = path.join(dir, 'profile.json');
233
+ const draftPath = path.join(dir, 'scope.json');
234
234
  fs.writeFileSync(briefPath, REQUIREMENTS_BRIEF(id, displayName || id));
235
235
 
236
236
  const draft = DRAFT_PROFILE_TEMPLATE(id, displayName);
@@ -261,26 +261,26 @@ export function createDraftProfile({ cwd, id, displayName, seedRoles = [], seedD
261
261
  departmentPaths.push(p);
262
262
  }
263
263
 
264
- emitProfileUpdated({ id, stage: 'draft', dir });
264
+ emitScopeUpdated({ id, stage: 'draft', dir });
265
265
  return { dir, briefPath, draftPath, personaPaths, departmentPaths };
266
266
  }
267
267
 
268
- // Lifecycle bridge: emits profile.updated whenever the curated catalog gains
268
+ // Lifecycle bridge: emits scope.updated whenever the curated catalog gains
269
269
  // or loses an entry, so subscribers (none by default; project overlays can
270
270
  // bind) can react to taxonomy changes.
271
271
 
272
- function emitProfileUpdated(context) {
273
- emitRoleEvent('profile.updated', {
274
- summary: `profile ${context.stage}: ${context.id}`,
272
+ function emitScopeUpdated(context) {
273
+ emitRoleEvent('scope.updated', {
274
+ summary: `scope ${context.stage}: ${context.id}`,
275
275
  context,
276
276
  });
277
277
  }
278
278
 
279
279
  /**
280
- * List drafts under .cx/profiles/. Returns [{ id, dir, hasProfile, hasBrief }].
280
+ * List drafts under .cx/scopes/. Returns [{ id, dir, hasScope, hasBrief }].
281
281
  */
282
282
  export function listDrafts(cwd) {
283
- const root = path.join(cwd, '.cx', 'profiles');
283
+ const root = path.join(cwd, '.cx', 'scopes');
284
284
  if (!fs.existsSync(root)) return [];
285
285
  const out = [];
286
286
  for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
@@ -291,7 +291,7 @@ export function listDrafts(cwd) {
291
291
  out.push({
292
292
  id,
293
293
  dir,
294
- hasProfile: fs.existsSync(path.join(dir, 'profile.json')),
294
+ hasScope: fs.existsSync(path.join(dir, 'scope.json')),
295
295
  hasBrief: fs.existsSync(path.join(dir, 'requirements.md')),
296
296
  });
297
297
  }
@@ -299,27 +299,27 @@ export function listDrafts(cwd) {
299
299
  }
300
300
 
301
301
  /**
302
- * Archive a curated profile by moving it (and any artifacts it owns in the
303
- * repo) into archive/profiles/<id>/. Observations and outcomes are not
302
+ * Archive a curated scope by moving it (and any artifacts it owns in the
303
+ * repo) into archive/scopes/<id>/. Observations and outcomes are not
304
304
  * touched; they remain as historical record. Requires a non-empty `reason`.
305
305
  */
306
- export function archiveProfile({ id, reason }) {
307
- if (!id) throw new Error('archiveProfile: id is required');
306
+ export function archiveScope({ id, reason }) {
307
+ if (!id) throw new Error('archiveScope: id is required');
308
308
  if (!reason || reason.trim().length < 8) {
309
- throw new Error('archiveProfile: a substantive reason (>= 8 chars) is required');
309
+ throw new Error('archiveScope: a substantive reason (>= 8 chars) is required');
310
310
  }
311
- const srcProfile = path.join(REPO_ROOT, 'profiles', `${id}.json`);
312
- if (!fs.existsSync(srcProfile)) {
313
- throw new Error(`archiveProfile: ${id} not found in profiles/`);
311
+ const srcScope = path.join(REPO_ROOT, 'specialists', 'org', 'scopes', `${id}.json`);
312
+ if (!fs.existsSync(srcScope)) {
313
+ throw new Error(`archiveScope: ${id} not found in specialists/org/scopes/`);
314
314
  }
315
- const profileJson = JSON.parse(fs.readFileSync(srcProfile, 'utf8'));
316
- const tablePath = typeof profileJson?.intake?.classificationTable === 'string'
317
- ? path.join(REPO_ROOT, profileJson.intake.classificationTable)
315
+ const scopeJson = JSON.parse(fs.readFileSync(srcScope, 'utf8'));
316
+ const tablePath = typeof scopeJson?.intake?.classificationTable === 'string'
317
+ ? path.join(REPO_ROOT, scopeJson.intake.classificationTable)
318
318
  : null;
319
319
 
320
320
  const dstDir = archiveDir(REPO_ROOT, id);
321
321
  fs.mkdirSync(dstDir, { recursive: true });
322
- fs.renameSync(srcProfile, path.join(dstDir, `${id}.json`));
322
+ fs.renameSync(srcScope, path.join(dstDir, `${id}.json`));
323
323
  if (tablePath && fs.existsSync(tablePath)) {
324
324
  fs.renameSync(tablePath, path.join(dstDir, path.basename(tablePath)));
325
325
  }
@@ -334,23 +334,23 @@ export function archiveProfile({ id, reason }) {
334
334
  '',
335
335
  '## What stayed',
336
336
  '',
337
- 'Observations and outcomes recorded under this profile remain in `.cx/observations/` and `.cx/outcomes/` and continue to be searchable. The intake table and profile JSON were moved into this directory. To restore: move the files back to their original paths and re-run `npm run lint:profiles`.',
337
+ 'Observations and outcomes recorded under this scope remain in `.cx/observations/` and `.cx/outcomes/` and continue to be searchable. The intake table and scope JSON were moved into this directory. To restore: move the files back to their original paths and re-run `npm run lint:scopes`.',
338
338
  '',
339
339
  ].join('\n'));
340
- emitProfileUpdated({ id, stage: 'archived', dir: dstDir, reason: reason.trim() });
340
+ emitScopeUpdated({ id, stage: 'archived', dir: dstDir, reason: reason.trim() });
341
341
  return { archived: dstDir };
342
342
  }
343
343
 
344
344
  /**
345
- * Per-profile health rollup. Counts observations and outcomes tagged with
346
- * the profile id, plus the median classification confidence over a window.
345
+ * Per-scope health rollup. Counts observations and outcomes tagged with
346
+ * the scope id, plus the median classification confidence over a window.
347
347
  *
348
348
  * @param {string} cwd - project root
349
- * @param {string} profileId
349
+ * @param {string} scopeId
350
350
  * @param {object} [opts]
351
351
  * @param {number} [opts.windowDays=30]
352
352
  */
353
- export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
353
+ export function scopeHealth(cwd, scopeId, { windowDays = 30 } = {}) {
354
354
  const cutoff = Date.now() - windowDays * 24 * 60 * 60 * 1000;
355
355
  const obsDir = path.join(cwd, '.cx', 'observations');
356
356
  let observationCount = 0;
@@ -361,10 +361,10 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
361
361
  const idx = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
362
362
  for (const entry of idx) {
363
363
  if (Date.parse(entry.createdAt) < cutoff) continue;
364
- // Observations stamp project, not profile. Fall back to project ==
365
- // profile id for the common case where the operator named them the
364
+ // Observations stamp project, not scope. Fall back to project ==
365
+ // scope id for the common case where the operator named them the
366
366
  // same; otherwise count under "all" so we never silently report 0.
367
- observationCount += entry.project === profileId ? 1 : 0;
367
+ observationCount += entry.project === scopeId ? 1 : 0;
368
368
  }
369
369
  } catch { /* unreadable index — health stays 0 */ }
370
370
  }
@@ -380,7 +380,7 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
380
380
  for (const line of lines) {
381
381
  let entry;
382
382
  try { entry = JSON.parse(line); } catch { continue; }
383
- if (entry.profile !== profileId) continue;
383
+ if (entry.profile !== scopeId) continue;
384
384
  if (Date.parse(entry.ts) < cutoff) continue;
385
385
  const r = roleHealth[role] || (roleHealth[role] = { count: 0, success: 0 });
386
386
  r.count += 1;
@@ -396,8 +396,8 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
396
396
  );
397
397
 
398
398
  return {
399
- profile: profileId,
400
- profileExists: !!loadProfile(profileId),
399
+ scope: scopeId,
400
+ scopeExists: !!loadScope(scopeId),
401
401
  windowDays,
402
402
  observationCount,
403
403
  roles: roleSummary,
@@ -0,0 +1,104 @@
1
+ /**
2
+ * lib/scopes/loader.mjs — Work-scope loader (intake lens + org enrichment).
3
+ *
4
+ * Curated scopes live in specialists/org/scopes/<id>.json. Custom overrides
5
+ * use <cwd>/.cx/scope.json with custom: true. construct.config.json scope
6
+ * field selects the active scope id (default rnd).
7
+ *
8
+ * Teams and roles are not authored in scope files — enrich.mjs attaches them
9
+ * from specialists/org at load time.
10
+ */
11
+
12
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ import { enrichScope } from './enrich.mjs';
17
+
18
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
19
+ const REPO_ROOT = resolve(MODULE_DIR, '..', '..');
20
+ const SCOPES_DIR = join(REPO_ROOT, 'specialists', 'org', 'scopes');
21
+
22
+ export const DEFAULT_SCOPE_ID = 'rnd';
23
+
24
+ export function loadScope(id, opts = {}) {
25
+ if (!id || typeof id !== 'string') return null;
26
+ const path = join(SCOPES_DIR, `${id}.json`);
27
+ if (!existsSync(path)) return null;
28
+ try {
29
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
30
+ return enrichScope(raw, opts);
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ export function listScopes() {
37
+ if (!existsSync(SCOPES_DIR)) return [];
38
+ return readdirSync(SCOPES_DIR)
39
+ .filter((f) => f.endsWith('.json'))
40
+ .map((f) => f.replace(/\.json$/, ''))
41
+ .filter((id) => {
42
+ try {
43
+ const raw = JSON.parse(readFileSync(join(SCOPES_DIR, `${id}.json`), 'utf8'));
44
+ return raw?.id === id && raw?.intake?.types && raw?.intake?.stages;
45
+ } catch {
46
+ return false;
47
+ }
48
+ })
49
+ .sort();
50
+ }
51
+
52
+ export function loadCustomScope(cwd) {
53
+ if (!cwd) return null;
54
+ const path = join(cwd, '.cx', 'scope.json');
55
+ if (!existsSync(path)) return null;
56
+ try {
57
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
58
+ if (raw && raw.custom === true) return raw;
59
+ return null;
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ export function resolveActiveScope(cwd, configScopeId = null) {
66
+ const rootDir = REPO_ROOT;
67
+ const enrichOpts = { rootDir };
68
+ if (configScopeId) {
69
+ const s = loadScope(configScopeId, enrichOpts);
70
+ if (s) return s;
71
+ }
72
+ const custom = loadCustomScope(cwd);
73
+ if (custom) return enrichScope(custom, enrichOpts);
74
+ const fromConfig = readScopeFromProjectConfig(cwd);
75
+ if (fromConfig) {
76
+ const s = loadScope(fromConfig, enrichOpts);
77
+ if (s) return s;
78
+ }
79
+ return loadScope(DEFAULT_SCOPE_ID, enrichOpts) ?? minimalRndFallback();
80
+ }
81
+
82
+ function readScopeFromProjectConfig(cwd) {
83
+ if (!cwd) return null;
84
+ const p = join(cwd, 'construct.config.json');
85
+ if (!existsSync(p)) return null;
86
+ try {
87
+ const raw = JSON.parse(readFileSync(p, 'utf8'));
88
+ return typeof raw?.scope === 'string' ? raw.scope : null;
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+
94
+ function minimalRndFallback() {
95
+ return {
96
+ id: 'rnd',
97
+ displayName: 'Software R&D',
98
+ roles: [],
99
+ intake: { types: [], stages: [] },
100
+ docTemplates: [],
101
+ hooks: { sessionReflect: 'on', sessionOptimize: 'on' },
102
+ rebrand: { intakeQueueLabel: 'R&D intake queue', signalNoun: 'signal' },
103
+ };
104
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * lib/scopes/rebrand.mjs — Scope-aware language helper.
3
+ *
4
+ * Each curated scope carries a `rebrand` block: how the active scope
5
+ * wants the intake queue and individual signal items named in user-facing
6
+ * surfaces (CLI output, daemon logs, session-start prelude).
7
+ */
8
+
9
+ import { resolveActiveScope } from './loader.mjs';
10
+
11
+ export const DEFAULT_REBRAND = Object.freeze({
12
+ intakeQueueLabel: 'Intake queue',
13
+ signalNoun: 'signal',
14
+ });
15
+
16
+ export function getRebrand(rootDir) {
17
+ if (!rootDir || typeof rootDir !== 'string') return { ...DEFAULT_REBRAND };
18
+ try {
19
+ const scope = resolveActiveScope(rootDir);
20
+ const rb = scope?.rebrand;
21
+ if (!rb || typeof rb !== 'object') return { ...DEFAULT_REBRAND };
22
+ const intakeQueueLabel = typeof rb.intakeQueueLabel === 'string' && rb.intakeQueueLabel.trim()
23
+ ? rb.intakeQueueLabel.trim()
24
+ : DEFAULT_REBRAND.intakeQueueLabel;
25
+ const signalNoun = typeof rb.signalNoun === 'string' && rb.signalNoun.trim()
26
+ ? rb.signalNoun.trim()
27
+ : DEFAULT_REBRAND.signalNoun;
28
+ return { intakeQueueLabel, signalNoun };
29
+ } catch {
30
+ return { ...DEFAULT_REBRAND };
31
+ }
32
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * lib/scopes/research-profile.mjs — Map intake types to research depth profiles.
3
+ *
4
+ * Scope JSON may declare researchProfiles (user, codebase, external, market).
5
+ * Artifact manifest entries carry a default researchProfile; scope overrides win.
6
+ */
7
+
8
+ export function resolveResearchProfile({ intakeType, artifactType, scope } = {}) {
9
+ if (scope?.researchProfiles && intakeType && scope.researchProfiles[intakeType]) {
10
+ return scope.researchProfiles[intakeType];
11
+ }
12
+ if (scope?.researchProfiles && artifactType && scope.researchProfiles[artifactType]) {
13
+ return scope.researchProfiles[artifactType];
14
+ }
15
+ return null;
16
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * lib/scopes/teams.mjs — Scope-aware routing hints over the single org chart.
3
+ *
4
+ * Scopes change intake taxonomy and terminology, not org membership. Operations-
5
+ * scope projects map objectives to existing rnd squads (operations-team,
6
+ * engineering-team, product-management-team).
7
+ */
8
+
9
+ const OPERATIONS_OBJECTIVE_RULES = [
10
+ {
11
+ teamId: 'operations-team',
12
+ focus: 'reliability',
13
+ attachTo: ['cx-sre', 'cx-docs-keeper'],
14
+ pattern: /\b(incident|outage|deploy|deployment|rollback|reliability|runbook|postmortem|sre|on-?call)\b/i,
15
+ },
16
+ {
17
+ teamId: 'product-management-team',
18
+ focus: 'triage',
19
+ attachTo: ['cx-operations', 'cx-product-manager'],
20
+ pattern: /\b(triage|route|routing|priority|queue|intake|request|escalat)/i,
21
+ },
22
+ {
23
+ teamId: 'engineering-team',
24
+ focus: 'implementation',
25
+ attachTo: ['cx-engineer', 'cx-qa'],
26
+ pattern: /\b(implement|build|fix|code|test|review|deliver|patch)\b/i,
27
+ },
28
+ ];
29
+
30
+ export const SCOPE_INTENT_TO_TEAM = Object.freeze({
31
+ operations: Object.freeze({
32
+ research: 'product-management-team',
33
+ implementation: 'engineering-team',
34
+ investigation: 'operations-team',
35
+ evaluation: 'engineering-team',
36
+ fix: 'engineering-team',
37
+ }),
38
+ });
39
+
40
+ export function scopeTeamsById(scope) {
41
+ if (!Array.isArray(scope?.teams) || scope.teams.length === 0) return null;
42
+ const byId = {};
43
+ for (const team of scope.teams) {
44
+ if (team?.id) byId[team.id] = team;
45
+ }
46
+ return byId;
47
+ }
48
+
49
+ export function resolveIntentTeamForScope(intent, scope) {
50
+ if (!intent || !scope?.id) return null;
51
+ const map = SCOPE_INTENT_TO_TEAM[scope.id];
52
+ return map?.[intent] ?? null;
53
+ }
54
+
55
+ export function classifyObjectiveForScope(objective = '', scope) {
56
+ const teams = scopeTeamsById(scope);
57
+ if (!teams) return null;
58
+
59
+ if (scope.id === 'operations') {
60
+ const text = String(objective);
61
+ for (const rule of OPERATIONS_OBJECTIVE_RULES) {
62
+ if (rule.pattern.test(text)) {
63
+ return {
64
+ focus: rule.focus,
65
+ attachTo: rule.attachTo,
66
+ teamFocus: rule.teamId,
67
+ recommendedTeam: rule.teamId,
68
+ scopeTeamSource: scope.id,
69
+ };
70
+ }
71
+ }
72
+ return {
73
+ focus: 'triage',
74
+ attachTo: ['cx-operations', 'cx-product-manager'],
75
+ teamFocus: 'product-management-team',
76
+ recommendedTeam: 'product-management-team',
77
+ scopeTeamSource: scope.id,
78
+ };
79
+ }
80
+
81
+ const firstTeam = scope.teams[0];
82
+ if (!firstTeam?.id) return null;
83
+ const attachTo = Array.isArray(firstTeam.specialists) && firstTeam.specialists.length > 0
84
+ ? firstTeam.specialists
85
+ : (firstTeam.roles ?? []).map((role) => (role.startsWith('cx-') ? role : `cx-${role}`));
86
+ return {
87
+ focus: firstTeam.id,
88
+ attachTo,
89
+ teamFocus: firstTeam.id,
90
+ recommendedTeam: firstTeam.id,
91
+ scopeTeamSource: scope.id,
92
+ };
93
+ }
94
+
95
+ export function resolveScopeTeamMeta(teamId, scope) {
96
+ const teams = scopeTeamsById(scope);
97
+ return teams?.[teamId] ?? null;
98
+ }