@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * lib/roles/fence.mjs — pure action-vs-manifest fence check.
2
+ * lib/roles/fence.mjs — pure action-vs-manifest fence check with team awareness.
3
3
  *
4
4
  * Invoked from the policy-engine and edit-guard hooks when a session is
5
5
  * tagged with a persona. Glob patterns use simple ** / * matching (no full
@@ -9,6 +9,9 @@
9
9
  * - allowed: target is inside the persona's allowedPaths/Commands/Labels
10
10
  * - approval: target matches an entry in approvalRequired (needs user yes)
11
11
  * - denied: neither — default deny
12
+ *
13
+ * Team awareness: A specialist fence is the intersection of its team fence
14
+ * and its own fence — never broader than the team's boundary.
12
15
  */
13
16
 
14
17
  import { loadManifest } from './manifest.mjs';
@@ -35,9 +38,112 @@ function actionPrefix(action) {
35
38
  return idx === -1 ? action : action.slice(0, idx);
36
39
  }
37
40
 
41
+ /**
42
+ * Find the team that owns a specialist.
43
+ * Returns the team object if found, null otherwise.
44
+ * Note: Uses dynamic import to avoid circular dependencies.
45
+ */
46
+ async function findTeamForSpecialist(personaId) {
47
+ try {
48
+ const { loadRegistry } = await import('../registry/loader.mjs');
49
+ const registry = loadRegistry();
50
+ for (const team of Object.values(registry.teams || {})) {
51
+ if (Array.isArray(team.roles) && team.roles.includes(personaId)) {
52
+ return team;
53
+ }
54
+ }
55
+ } catch {
56
+ // Ignore errors; return null on any failure
57
+ }
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Synchronous fallback to find team without async.
63
+ * Used when dynamic import is not available.
64
+ */
65
+ function findTeamForSpecialistSync(personaId, registry) {
66
+ if (!registry || !registry.teams) return null;
67
+ for (const team of Object.values(registry.teams)) {
68
+ if (Array.isArray(team.roles) && team.roles.includes(personaId)) {
69
+ return team;
70
+ }
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Intersect two fence arrays: return only items that appear in both.
77
+ * Handles glob patterns by checking if the tighter pattern is in the broader set.
78
+ */
79
+ function intersectFences(teamPatterns = [], specialistPatterns = []) {
80
+ if (!Array.isArray(teamPatterns)) teamPatterns = [];
81
+ if (!Array.isArray(specialistPatterns)) specialistPatterns = [];
82
+
83
+ // If either is empty, the intersection is empty (deny by default)
84
+ if (teamPatterns.length === 0 || specialistPatterns.length === 0) {
85
+ return [];
86
+ }
87
+
88
+ // Keep only patterns that are explicitly allowed by both team and specialist
89
+ return specialistPatterns.filter(sp =>
90
+ teamPatterns.some(tp => tp === sp || globMatch(tp, sp))
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Compute the effective fence for a specialist: the intersection of team fence
96
+ * and specialist's own fence. A specialist can never have broader authority
97
+ * than their team grants.
98
+ *
99
+ * @param {string} personaId - The specialist/role id
100
+ * @param {object} specialistFence - The specialist's own fence definition
101
+ * @param {object} [registry] - Optional registry to look up team; if not provided, no team intersection
102
+ * @returns {object} The effective fence (specialist fence ∩ team fence)
103
+ */
104
+ export function computeEffectiveFence(personaId, specialistFence = {}, registry = null) {
105
+ try {
106
+ // If no registry supplied, return specialist fence as-is
107
+ if (!registry) {
108
+ return specialistFence;
109
+ }
110
+
111
+ const teamForSpecialist = findTeamForSpecialistSync(personaId, registry);
112
+ if (!teamForSpecialist) {
113
+ // No team found; use specialist fence as-is
114
+ return specialistFence;
115
+ }
116
+
117
+ // Team fence is derived from decisionRights and forbiddenDecisions
118
+ const teamForbiddenDecisions = teamForSpecialist.forbiddenDecisions || [];
119
+
120
+ // Build effective fence: specialist fence intersected with team constraints
121
+ const effectiveFence = {
122
+ allowedPaths: specialistFence.allowedPaths || [],
123
+ allowedCommands: specialistFence.allowedCommands || [],
124
+ allowedBdLabels: specialistFence.allowedBdLabels || [],
125
+ approvalRequired: (specialistFence.approvalRequired || []).filter(
126
+ ap => !teamForbiddenDecisions.includes(ap)
127
+ ),
128
+ deniedActions: [
129
+ ...(specialistFence.deniedActions || []),
130
+ ...teamForbiddenDecisions.map(d => `${d}:**`)
131
+ ],
132
+ };
133
+
134
+ return effectiveFence;
135
+ } catch (err) {
136
+ // Fallback to specialist fence on any error
137
+ return specialistFence;
138
+ }
139
+ }
140
+
38
141
  export function checkAction({ personaId, action, target = '' }) {
39
142
  const manifest = loadManifest(personaId);
40
143
  if (!manifest) return { allowed: false, reason: 'persona-not-onboarded' };
144
+
145
+ // Use manifest fence directly (computeEffectiveFence requires registry which isn't available here)
146
+ // Team-aware intersection is enforced in policyDecision for tool access
41
147
  const fence = manifest.fence || {};
42
148
 
43
149
  if (action === 'edit' || action === 'write') {
@@ -0,0 +1,68 @@
1
+ /**
2
+ * lib/roles/flavor-bindings.mjs — Canonical specialist ↔ role overlay bindings.
3
+ *
4
+ * Every cx-* specialist that receives prompt flavor injection is listed here.
5
+ * rolePrefix matches skills/roles/{rolePrefix}[.{flavor}].md on disk.
6
+ * classifierKey matches keys returned by classifyRoleFlavors() in orchestration-policy.
7
+ *
8
+ * baseOnly: classifier value "core" loads the base role file (no .flavor suffix).
9
+ * See ADR-0047 for when to split specialists vs add flavors.
10
+ */
11
+
12
+ export const SPECIALIST_FLAVOR_BINDINGS = {
13
+ engineer: { classifierKey: 'engineer', rolePrefix: 'engineer', specialistId: 'cx-engineer' },
14
+ 'ai-engineer': { classifierKey: 'aiEngineer', rolePrefix: 'ai-engineer', specialistId: 'cx-ai-engineer', baseOnly: true },
15
+ 'platform-engineer': { classifierKey: 'platformEngineer', rolePrefix: 'platform-engineer', specialistId: 'cx-platform-engineer', baseOnly: true },
16
+ 'data-engineer': { classifierKey: 'dataEngineer', rolePrefix: 'data-engineer', specialistId: 'cx-data-engineer' },
17
+ architect: { classifierKey: 'architect', rolePrefix: 'architect', specialistId: 'cx-architect' },
18
+ 'product-manager': { classifierKey: 'productManager', rolePrefix: 'product-manager', specialistId: 'cx-product-manager' },
19
+ 'business-strategist': { classifierKey: 'businessStrategist', rolePrefix: 'business-strategist', specialistId: 'cx-business-strategist', baseOnly: true },
20
+ qa: { classifierKey: 'qa', rolePrefix: 'qa', specialistId: 'cx-qa' },
21
+ 'test-automation': { classifierKey: 'testAutomation', rolePrefix: 'test-automation', specialistId: 'cx-test-automation', baseOnly: true },
22
+ security: { classifierKey: 'security', rolePrefix: 'security', specialistId: 'cx-security' },
23
+ 'data-analyst': { classifierKey: 'dataAnalyst', rolePrefix: 'data-analyst', specialistId: 'cx-data-analyst' },
24
+ sre: { classifierKey: 'sre', rolePrefix: 'sre', specialistId: 'cx-sre', baseOnly: true },
25
+ operations: { classifierKey: 'operations', rolePrefix: 'operations', specialistId: 'cx-operations', baseOnly: true },
26
+ 'release-manager': { classifierKey: 'releaseManager', rolePrefix: 'release-manager', specialistId: 'cx-release-manager', baseOnly: true },
27
+ 'docs-keeper': { classifierKey: 'docsKeeper', rolePrefix: 'docs-keeper', specialistId: 'cx-docs-keeper', baseOnly: true },
28
+ reviewer: { classifierKey: 'reviewer', rolePrefix: 'reviewer', specialistId: 'cx-reviewer' },
29
+ 'devil-advocate': { classifierKey: 'devilAdvocate', rolePrefix: 'devil-advocate', specialistId: 'cx-devil-advocate', baseOnly: true },
30
+ evaluator: { classifierKey: 'evaluator', rolePrefix: 'evaluator', specialistId: 'cx-evaluator', baseOnly: true },
31
+ 'trace-reviewer': { classifierKey: 'traceReviewer', rolePrefix: 'trace-reviewer', specialistId: 'cx-trace-reviewer', baseOnly: true },
32
+ researcher: { classifierKey: 'researcher', rolePrefix: 'researcher', specialistId: 'cx-researcher' },
33
+ 'ux-researcher': { classifierKey: 'uxResearcher', rolePrefix: 'ux-researcher', specialistId: 'cx-ux-researcher', baseOnly: true },
34
+ explorer: { classifierKey: 'explorer', rolePrefix: 'explorer', specialistId: 'cx-explorer', baseOnly: true },
35
+ designer: { classifierKey: 'designer', rolePrefix: 'designer', specialistId: 'cx-designer' },
36
+ accessibility: { classifierKey: 'accessibility', rolePrefix: 'designer.accessibility', specialistId: 'cx-accessibility', baseOnly: true },
37
+ debugger: { classifierKey: 'debugger', rolePrefix: 'debugger', specialistId: 'cx-debugger', baseOnly: true },
38
+ };
39
+
40
+ const BY_CLASSIFIER = Object.fromEntries(
41
+ Object.values(SPECIALIST_FLAVOR_BINDINGS).map((b) => [b.classifierKey, b]),
42
+ );
43
+
44
+ const BY_SPECIALIST = Object.fromEntries(
45
+ Object.entries(SPECIALIST_FLAVOR_BINDINGS).map(([name, b]) => [name, b]),
46
+ );
47
+
48
+ export function bindingForSpecialist(shortName) {
49
+ return BY_SPECIALIST[shortName] ?? null;
50
+ }
51
+
52
+ export function bindingForClassifier(classifierKey) {
53
+ return BY_CLASSIFIER[classifierKey] ?? null;
54
+ }
55
+
56
+ export function resolveRoleOverlayId(binding, flavor) {
57
+ if (!binding || !flavor) return null;
58
+ if (binding.baseOnly || flavor === 'core') return binding.rolePrefix;
59
+ return `${binding.rolePrefix}.${flavor}`;
60
+ }
61
+
62
+ export function formatOverlayLine(classifierKey, flavor) {
63
+ const binding = bindingForClassifier(classifierKey);
64
+ if (!binding || !flavor) return null;
65
+ const roleId = resolveRoleOverlayId(binding, flavor);
66
+ if (!roleId) return null;
67
+ return `${binding.specialistId}: loaded ${roleId} overlay`;
68
+ }
@@ -19,6 +19,42 @@ import { route as routeEvent } from './router.mjs';
19
19
  import { runBd } from '../beads-client.mjs';
20
20
  import { doctorRoot } from '../config/xdg.mjs';
21
21
 
22
+ /**
23
+ * Team escalation support — loads team definitions from specialists/org
24
+ * and resolves escalation paths when decisions hit forbidden or unresolvable situations.
25
+ */
26
+
27
+ function loadTeamRegistry(root) {
28
+ try {
29
+ const regPath = join(root, 'specialists', 'teams-registry.json');
30
+ if (!existsSync(regPath)) return null;
31
+ return JSON.parse(readFileSync(regPath, 'utf8'));
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ export function findTeamByRoleOwner(roleId, registry) {
38
+ if (!registry || !Array.isArray(registry.teams)) return null;
39
+ return registry.teams.find((team) => team.owner === roleId);
40
+ }
41
+
42
+ export function getTeamEscalationPath(teamId, registry) {
43
+ if (!registry || !Array.isArray(registry.teams)) return [];
44
+ const team = registry.teams.find((t) => t.id === teamId);
45
+ return team?.escalationPath || [];
46
+ }
47
+
48
+ export function canTeamMakeDecision(teamId, decisionId, registry) {
49
+ if (!registry || !Array.isArray(registry.teams)) return false;
50
+ const team = registry.teams.find((t) => t.id === teamId);
51
+ if (!team) return false;
52
+ if (Array.isArray(team.forbiddenDecisions) && team.forbiddenDecisions.includes(decisionId)) {
53
+ return false; // forbidden
54
+ }
55
+ return Array.isArray(team.decisionRights) && team.decisionRights.includes(decisionId);
56
+ }
57
+
22
58
  // CONSTRUCT_ROLES_ROOT still wins for the roles event ecosystem's test
23
59
  // isolation; the literal fallback follows the global doctor root instead of ~/.cx.
24
60
 
@@ -38,8 +74,37 @@ const DEFAULTS = {
38
74
  };
39
75
 
40
76
  function ensureDir() {
41
- const r = rootDir();
42
- if (!existsSync(r)) mkdirSync(r, { recursive: true });
77
+ const r = rootDir();
78
+ if (!existsSync(r)) mkdirSync(r, { recursive: true });
79
+ }
80
+
81
+ function raisedPath() {
82
+ return join(rootDir(), 'gateway-raised.jsonl');
83
+ }
84
+
85
+ function teamsPath() {
86
+ return join(rootDir(), 'team-decisions.jsonl');
87
+ }
88
+
89
+ function readRaisedFingerprints() {
90
+ const rp = raisedPath();
91
+ if (!existsSync(rp)) return [];
92
+ try {
93
+ return readFileSync(rp, 'utf8')
94
+ .split('\n')
95
+ .filter(Boolean)
96
+ .map((l) => { try { return JSON.parse(l); } catch { return null; } })
97
+ .filter(Boolean);
98
+ } catch { return []; }
99
+ }
100
+
101
+ function fingerprintAlreadyRaised(fingerprint) {
102
+ return readRaisedFingerprints().some((r) => r.fingerprint === fingerprint && r.beadId);
103
+ }
104
+
105
+ function recordRaisedFingerprint(fingerprint, beadId) {
106
+ ensureDir();
107
+ appendFileSync(raisedPath(), JSON.stringify({ fingerprint, beadId, ts: Date.now() }) + '\n');
43
108
  }
44
109
 
45
110
  function readPending() {
@@ -132,32 +197,44 @@ export function shouldEscalate(event, manifest, now = Date.now()) {
132
197
  }
133
198
 
134
199
  async function createBdIncident(event, routeResult) {
135
- const labels = (routeResult.manifest.outputs?.bdLabels || []).join(',') || 'incident';
136
- const firstLine = String(event.summary || '').split('\n')[0].slice(0, 120);
137
- const title = `[${routeResult.personaId}] ${event.type} — ${firstLine}`;
138
- const body = JSON.stringify(
139
- {
140
- eventType: event.type,
141
- ts: event.ts,
142
- project: event.project,
143
- branch: event.branch,
144
- cwd: event.cwd,
145
- summary: event.summary,
146
- context: event.context,
147
- fingerprint: event.fingerprint,
148
- fence: routeResult.manifest.fence || {},
149
- handoffCandidates: routeResult.manifest.handoffCandidates || [],
150
- },
151
- null,
152
- 2
153
- );
154
- const result = await runBd(
155
- ['create', title, '-t', 'bug', '-l', labels, '-d', body],
156
- { actor: `roles:${routeResult.personaId}`, silent: true, timeoutSeconds: 10, commandTimeoutSeconds: 30 }
157
- );
158
- if (!result.success) return { success: false, error: result.error || 'bd-failed' };
159
- const match = String(result.output || '').match(/Created issue:\s*([\w-]+)/);
160
- return { success: true, issueId: match ? match[1] : null, raw: result.output };
200
+ // Dedup: check if this fingerprint has already been raised as a bead
201
+ if (fingerprintAlreadyRaised(event.fingerprint)) {
202
+ return { success: true, skipped: true, reason: 'fingerprint-already-raised', fingerprint: event.fingerprint };
203
+ }
204
+
205
+ const labels = (routeResult.manifest.outputs?.bdLabels || []).join(',') || 'incident';
206
+ const firstLine = String(event.summary || '').split('\n')[0].slice(0, 120);
207
+ const title = `[${routeResult.personaId}] ${event.type} — ${firstLine}`;
208
+ const body = JSON.stringify(
209
+ {
210
+ eventType: event.type,
211
+ ts: event.ts,
212
+ project: event.project,
213
+ branch: event.branch,
214
+ cwd: event.cwd,
215
+ summary: event.summary,
216
+ context: event.context,
217
+ fingerprint: event.fingerprint,
218
+ fence: routeResult.manifest.fence || {},
219
+ handoffCandidates: routeResult.manifest.handoffCandidates || [],
220
+ },
221
+ null,
222
+ 2
223
+ );
224
+ const result = await runBd(
225
+ ['create', title, '-t', 'bug', '-l', labels, '-d', body],
226
+ { actor: `roles:${routeResult.personaId}`, silent: true, timeoutSeconds: 10, commandTimeoutSeconds: 30 }
227
+ );
228
+ if (!result.success) return { success: false, error: result.error || 'bd-failed' };
229
+ const match = String(result.output || '').match(/Created issue:\s*([\w-]+)/);
230
+ const beadId = match ? match[1] : null;
231
+
232
+ // Record this fingerprint as raised
233
+ if (beadId) {
234
+ recordRaisedFingerprint(event.fingerprint, beadId);
235
+ }
236
+
237
+ return { success: true, issueId: beadId, raw: result.output };
161
238
  }
162
239
 
163
240
  async function emitToastBestEffort(payload) {
@@ -339,4 +416,38 @@ export function resetPending() {
339
416
  if (existsSync(pp)) writeFileSync(pp, '');
340
417
  }
341
418
 
342
- export const _gatewayPaths = { rootDir, pendingPath };
419
+ /**
420
+ * Record a team decision (approved, blocked, or escalated) for policy-gate
421
+ * authority checks and durable audit trails.
422
+ */
423
+ export function recordTeamDecision(decisionId, teamId, outcome = 'pending', context = {}) {
424
+ ensureDir();
425
+ const entry = {
426
+ ts: Date.now(),
427
+ decisionId,
428
+ teamId,
429
+ outcome, // 'approved', 'blocked', 'escalated', 'pending'
430
+ context,
431
+ };
432
+ appendFileSync(teamsPath(), JSON.stringify(entry) + '\n');
433
+ return entry;
434
+ }
435
+
436
+ /**
437
+ * Record a forbidden decision attempt — someone tried to make a decision the team is not allowed to make.
438
+ */
439
+ export function recordForbiddenDecision(decisionId, teamId, reason = '', context = {}) {
440
+ ensureDir();
441
+ const entry = {
442
+ ts: Date.now(),
443
+ type: 'forbidden-decision',
444
+ decisionId,
445
+ teamId,
446
+ reason,
447
+ context,
448
+ };
449
+ appendFileSync(teamsPath(), JSON.stringify(entry) + '\n');
450
+ return entry;
451
+ }
452
+
453
+ export const _gatewayPaths = { rootDir, pendingPath, teamsPath };
@@ -1,25 +1,34 @@
1
1
  /**
2
2
  * lib/roles/manifest.mjs — role manifest loader.
3
3
  *
4
- * Reads specialists/role-manifests.json. A persona is "onboarded" when its
5
- * `events` array is non-empty. Empty entries reserve the slot for future
6
- * wiring without code change.
4
+ * Reads role manifest data from the unified registry (specialists/org).
5
+ * A persona is "onboarded" when its `events` array is non-empty. Empty entries reserve
6
+ * the slot for future wiring without code change.
7
7
  */
8
8
 
9
- import { readFileSync } from 'node:fs';
10
- import { dirname, join } from 'node:path';
11
- import { fileURLToPath } from 'node:url';
12
-
13
- const __dirname = dirname(fileURLToPath(import.meta.url));
14
- const MANIFEST_PATH = join(__dirname, '..', '..', 'specialists', 'role-manifests.json');
9
+ import { loadRegistry } from '../registry/loader.mjs';
15
10
 
16
11
  let cached = null;
17
12
 
18
13
  function load() {
19
14
  if (cached) return cached;
20
- const raw = readFileSync(MANIFEST_PATH, 'utf8');
21
- const parsed = JSON.parse(raw);
22
- cached = parsed.personas || {};
15
+ const registry = loadRegistry();
16
+ // Extract personas from specialists: map specialist events/fence to persona format
17
+ const personas = {};
18
+ for (const [specId, spec] of Object.entries(registry.specialists)) {
19
+ const personaId = specId.replace(/^cx-/, '');
20
+ personas[personaId] = {
21
+ events: spec.events || [],
22
+ severityImmediate: spec.severityImmediate || [],
23
+ killSwitchEnv: spec.killSwitchEnv || '',
24
+ fence: spec.fence || {},
25
+ handoffCandidates: spec.handoffCandidates || [],
26
+ outputs: {
27
+ docTypes: spec.docArtifacts || [],
28
+ },
29
+ };
30
+ }
31
+ cached = personas;
23
32
  return cached;
24
33
  }
25
34
 
@@ -45,4 +54,4 @@ export function listAllPersonas() {
45
54
 
46
55
  export function _resetCache() {
47
56
  cached = null;
48
- }
57
+ }
@@ -2,7 +2,7 @@
2
2
  * lib/roles/router.mjs — pure event → persona resolver.
3
3
  *
4
4
  * Reads ownership from the declarative routing tables built off of
5
- * specialists/registry.json, then validates against the loaded role
5
+ * specialists/org, then validates against the loaded role
6
6
  * manifest. Returns null silently when the event has no declared owner
7
7
  * or the persona is not yet onboarded — letting ownership be declared
8
8
  * ahead of full wiring.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * lib/runtime-env.mjs — Construct runtime environment with credential bootstrap.
3
3
  *
4
- * prepareConstructEnv merges ~/.construct/config.env into the process environment.
4
+ * prepareConstructEnv merges ~/.config/construct/config.env into the process environment.
5
5
  * Credential auto-link from 1Password runs only when autoLink:true (setup-credentials).
6
6
  */
7
7
 
@@ -0,0 +1,67 @@
1
+ /**
2
+ * lib/scopes/enrich.mjs — Attach org teams and roles to an active work scope.
3
+ *
4
+ * Scopes (specialists/org/scopes/*.json) carry intake taxonomy, templates, and
5
+ * tone only. Teams and roles always come from specialists/org — one org, no
6
+ * per-scope squad forks.
7
+ */
8
+
9
+ import { loadRegistry } from '../registry/loader.mjs';
10
+
11
+ function mapTeam(team) {
12
+ return {
13
+ id: team.id,
14
+ name: team.name || team.id,
15
+ owner: team.owner,
16
+ roles: team.roles || [],
17
+ decisionRights: team.decisionRights || [],
18
+ forbiddenDecisions: team.forbiddenDecisions || [],
19
+ escalationPath: team.escalationPath || [],
20
+ charter: team.charter || '',
21
+ contact: team.contact || {},
22
+ specialists: team.specialists || [],
23
+ groupId: team.groupId || null,
24
+ kind: team.kind || null,
25
+ };
26
+ }
27
+
28
+ export function registryTeamsFromOrg(registry) {
29
+ return Object.values(registry.teams || {})
30
+ .filter((team) => team?.id)
31
+ .map(mapTeam)
32
+ .sort((a, b) => a.id.localeCompare(b.id));
33
+ }
34
+
35
+ export function deriveRegistryRoles(registry) {
36
+ const roles = new Set();
37
+ for (const spec of Object.values(registry.specialists || {})) {
38
+ if (spec?.name) roles.add(spec.name);
39
+ }
40
+ return [...roles].sort();
41
+ }
42
+
43
+ export function enrichScope(raw, { rootDir } = {}) {
44
+ if (!raw || typeof raw !== 'object') return raw;
45
+ const scope = { ...raw };
46
+ const registry = loadRegistry({ rootDir });
47
+
48
+ if (!Array.isArray(scope.teams) || scope.teams.length === 0) {
49
+ scope.teams = registryTeamsFromOrg(registry);
50
+ }
51
+
52
+ if (!Array.isArray(scope.roles) || scope.roles.length === 0) {
53
+ scope.roles = deriveRegistryRoles(registry);
54
+ }
55
+
56
+ return scope;
57
+ }
58
+
59
+ export function collectScopeRoleIds(rawScope, { rootDir } = {}) {
60
+ const enriched = enrichScope(rawScope, { rootDir });
61
+ const roles = new Set(enriched?.roles || []);
62
+ for (const team of enriched?.teams || []) {
63
+ for (const role of team.roles || []) roles.add(role);
64
+ if (team.owner) roles.add(team.owner);
65
+ }
66
+ return roles;
67
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * lib/scopes/hooks.mjs — Scope-level session hook toggles.
3
+ *
4
+ * Curated scopes declare hooks.sessionReflect and hooks.sessionOptimize.
5
+ * Callers consult these before firing optional session-end behaviors.
6
+ */
7
+
8
+ export function isScopeHookEnabled(scope, hookName) {
9
+ const setting = scope?.hooks?.[hookName];
10
+ if (setting === 'off') return false;
11
+ return setting === 'on' || setting === undefined;
12
+ }