@geraldmaron/construct 1.2.3 → 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 (504) 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/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/scopes/enrich.mjs +67 -0
  153. package/lib/scopes/hooks.mjs +12 -0
  154. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  155. package/lib/scopes/loader.mjs +104 -0
  156. package/lib/scopes/rebrand.mjs +32 -0
  157. package/lib/scopes/research-profile.mjs +16 -0
  158. package/lib/scopes/teams.mjs +98 -0
  159. package/lib/service-manager.mjs +1 -117
  160. package/lib/setup-prompts.mjs +1 -1
  161. package/lib/setup.mjs +6 -26
  162. package/lib/skills/composition-graph.mjs +98 -0
  163. package/lib/skills/router.mjs +80 -0
  164. package/lib/specialist-contracts.mjs +17 -18
  165. package/lib/specialists/postconditions.mjs +2 -2
  166. package/lib/specialists/prompt-schema.mjs +6 -5
  167. package/lib/specialists/roster.mjs +7 -12
  168. package/lib/specialists/schema.mjs +9 -6
  169. package/lib/status.mjs +25 -90
  170. package/lib/storage/file-lock.mjs +6 -3
  171. package/lib/telemetry/skill-calls.mjs +1 -1
  172. package/lib/templates/doc-presentation.mjs +63 -0
  173. package/lib/templates/visual-requirements.mjs +35 -2
  174. package/lib/term-format.mjs +107 -2
  175. package/lib/test-env-setup.mjs +21 -0
  176. package/lib/ui/components.mjs +42 -0
  177. package/lib/ui/glyphs.mjs +58 -0
  178. package/lib/ui/links.mjs +115 -0
  179. package/lib/ui/theme.mjs +108 -0
  180. package/lib/uninstall/uninstall.mjs +2 -1
  181. package/lib/validator.mjs +45 -8
  182. package/lib/validators/skill-effectiveness.mjs +174 -0
  183. package/lib/validators/skills.mjs +1 -1
  184. package/package.json +12 -8
  185. package/personas/construct.md +12 -3
  186. package/platforms/claude/CLAUDE.md +1 -1
  187. package/rules/common/beads-hygiene.md +52 -0
  188. package/rules/common/neurodivergent-output.md +4 -7
  189. package/rules/common/research.md +2 -0
  190. package/scripts/sync-specialists.mjs +260 -49
  191. package/skills/ai/prompt-optimizer.md +3 -3
  192. package/skills/brand/output-vibe.md +48 -0
  193. package/skills/docs/adr-workflow.md +3 -0
  194. package/skills/docs/backlog-proposal-workflow.md +3 -0
  195. package/skills/docs/codebase-research-workflow.md +5 -2
  196. package/skills/docs/customer-profile-workflow.md +3 -0
  197. package/skills/docs/document-ingest-workflow.md +3 -0
  198. package/skills/docs/evidence-ingest-workflow.md +4 -1
  199. package/skills/docs/init-project.md +1 -1
  200. package/skills/docs/prd-workflow.md +1 -1
  201. package/skills/docs/prfaq-workflow.md +3 -0
  202. package/skills/docs/product-intelligence-workflow.md +4 -1
  203. package/skills/docs/product-signal-workflow.md +3 -0
  204. package/skills/docs/research-workflow.md +13 -6
  205. package/skills/docs/runbook-workflow.md +3 -0
  206. package/skills/docs/strategy-workflow.md +3 -0
  207. package/skills/docs/user-research-workflow.md +6 -3
  208. package/skills/operating/orchestration-reference.md +1 -1
  209. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  210. package/skills/roles/architect.ai-systems.md +1 -1
  211. package/skills/roles/architect.data.md +1 -1
  212. package/skills/roles/architect.enterprise.md +1 -1
  213. package/skills/roles/architect.integration.md +1 -1
  214. package/skills/roles/architect.md +1 -1
  215. package/skills/roles/architect.platform.md +1 -1
  216. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  217. package/skills/roles/data-analyst.experiment.md +1 -1
  218. package/skills/roles/data-analyst.md +1 -1
  219. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  220. package/skills/roles/data-analyst.product.md +1 -1
  221. package/skills/roles/data-analyst.telemetry.md +1 -1
  222. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  223. package/skills/roles/data-engineer.pipeline.md +2 -2
  224. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  225. package/skills/roles/data-engineer.warehouse.md +2 -2
  226. package/skills/roles/debugger.md +1 -1
  227. package/skills/roles/designer.accessibility.md +1 -1
  228. package/skills/roles/designer.md +1 -1
  229. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  230. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  231. package/skills/roles/engineer.md +3 -7
  232. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  233. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  234. package/skills/roles/{operator.md → operations.md} +2 -5
  235. package/skills/roles/orchestrator.md +1 -1
  236. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  237. package/skills/roles/product-manager.ai-product.md +1 -1
  238. package/skills/roles/product-manager.enterprise.md +1 -1
  239. package/skills/roles/product-manager.growth.md +1 -2
  240. package/skills/roles/product-manager.md +1 -2
  241. package/skills/roles/product-manager.platform.md +1 -1
  242. package/skills/roles/product-manager.product.md +1 -1
  243. package/skills/roles/qa.ai-eval.md +1 -1
  244. package/skills/roles/qa.api-contract.md +1 -1
  245. package/skills/roles/qa.data-pipeline.md +1 -1
  246. package/skills/roles/qa.md +1 -1
  247. package/skills/roles/qa.web-ui.md +1 -1
  248. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  249. package/skills/roles/researcher.md +1 -1
  250. package/skills/roles/reviewer.md +1 -1
  251. package/skills/roles/security.ai.md +1 -1
  252. package/skills/roles/security.appsec.md +1 -1
  253. package/skills/roles/security.cloud.md +1 -1
  254. package/skills/roles/security.legal-compliance.md +1 -1
  255. package/skills/roles/security.md +1 -1
  256. package/skills/roles/security.privacy.md +1 -1
  257. package/skills/roles/security.supply-chain.md +1 -1
  258. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  259. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  260. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  261. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  262. package/skills/routing.json +18 -0
  263. package/skills/routing.md +1 -1
  264. package/specialists/artifact-manifest.json +2 -1
  265. package/specialists/audit-enrichments.json +14 -14
  266. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  267. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  268. package/specialists/org/contracts/any-to-debugger.json +38 -0
  269. package/specialists/org/contracts/any-to-designer.json +33 -0
  270. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  271. package/specialists/org/contracts/any-to-explorer.json +39 -0
  272. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  273. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  274. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  275. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  276. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  277. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  278. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  279. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  280. package/specialists/org/contracts/architect-to-operations.json +45 -0
  281. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  282. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  283. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  284. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  285. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  286. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  287. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  288. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  289. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  290. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  291. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  292. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  293. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  294. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  295. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  296. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  297. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  298. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  299. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  300. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  301. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  302. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  303. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  304. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  305. package/specialists/org/contracts/user-to-construct.json +30 -0
  306. package/specialists/org/groups/engineering-group.json +43 -0
  307. package/specialists/org/groups/governance-group.json +38 -0
  308. package/specialists/org/groups/operations-group.json +41 -0
  309. package/specialists/org/groups/product-group.json +46 -0
  310. package/specialists/org/groups/quality-group.json +42 -0
  311. package/specialists/org/groups/strategy-group.json +41 -0
  312. package/specialists/org/policies/action-approval.json +13 -0
  313. package/specialists/org/policies/agents-routing.json +13 -0
  314. package/specialists/org/policies/architecture.json +20 -0
  315. package/specialists/org/policies/bash-safety.json +13 -0
  316. package/specialists/org/policies/beads-hygiene.json +13 -0
  317. package/specialists/org/policies/bootstrap-state.json +13 -0
  318. package/specialists/org/policies/code-review.json +13 -0
  319. package/specialists/org/policies/coding-style.json +13 -0
  320. package/specialists/org/policies/comments.json +13 -0
  321. package/specialists/org/policies/commit-approval.json +13 -0
  322. package/specialists/org/policies/contract-preconditions.json +13 -0
  323. package/specialists/org/policies/deployment.json +20 -0
  324. package/specialists/org/policies/description.json +14 -0
  325. package/specialists/org/policies/design-approval.json +19 -0
  326. package/specialists/org/policies/doc-ownership.json +13 -0
  327. package/specialists/org/policies/file-path-fence.json +13 -0
  328. package/specialists/org/policies/framing.json +13 -0
  329. package/specialists/org/policies/git-workflow.json +13 -0
  330. package/specialists/org/policies/incident-response.json +15 -0
  331. package/specialists/org/policies/intake-triage.json +15 -0
  332. package/specialists/org/policies/neurodivergent-output.json +13 -0
  333. package/specialists/org/policies/no-fabrication.json +13 -0
  334. package/specialists/org/policies/patterns.json +13 -0
  335. package/specialists/org/policies/quality-gate-approval.json +17 -0
  336. package/specialists/org/policies/release-gates.json +13 -0
  337. package/specialists/org/policies/research-evidence.json +13 -0
  338. package/specialists/org/policies/review-before-change.json +13 -0
  339. package/specialists/org/policies/rollback.json +15 -0
  340. package/specialists/org/policies/scope-change.json +20 -0
  341. package/specialists/org/policies/secret-scan.json +13 -0
  342. package/specialists/org/policies/security-approval.json +17 -0
  343. package/specialists/org/policies/security.json +13 -0
  344. package/specialists/org/policies/session-efficiency.json +13 -0
  345. package/specialists/org/policies/skill-routing.json +13 -0
  346. package/specialists/org/policies/strategic-prioritization.json +17 -0
  347. package/specialists/org/policies/testing.json +13 -0
  348. package/specialists/org/policies/tool-invisibility.json +14 -0
  349. package/specialists/org/scopes/creative.json +54 -0
  350. package/specialists/org/scopes/operations.json +51 -0
  351. package/specialists/org/scopes/research.json +60 -0
  352. package/specialists/org/scopes/rnd.json +81 -0
  353. package/specialists/org/specialists/cx-accessibility.json +69 -0
  354. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  355. package/specialists/org/specialists/cx-architect.json +89 -0
  356. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  357. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  358. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  359. package/specialists/org/specialists/cx-debugger.json +75 -0
  360. package/specialists/org/specialists/cx-designer.json +85 -0
  361. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  362. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  363. package/specialists/org/specialists/cx-engineer.json +106 -0
  364. package/specialists/org/specialists/cx-evaluator.json +69 -0
  365. package/specialists/org/specialists/cx-explorer.json +69 -0
  366. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  367. package/specialists/org/specialists/cx-operations.json +72 -0
  368. package/specialists/org/specialists/cx-oracle.json +46 -0
  369. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  370. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  371. package/specialists/org/specialists/cx-product-manager.json +102 -0
  372. package/specialists/org/specialists/cx-qa.json +79 -0
  373. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  374. package/specialists/org/specialists/cx-release-manager.json +77 -0
  375. package/specialists/org/specialists/cx-researcher.json +82 -0
  376. package/specialists/org/specialists/cx-reviewer.json +71 -0
  377. package/specialists/org/specialists/cx-security.json +91 -0
  378. package/specialists/org/specialists/cx-sre.json +94 -0
  379. package/specialists/org/specialists/cx-test-automation.json +69 -0
  380. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  381. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  382. package/specialists/org/teams/accessibility-team.json +39 -0
  383. package/specialists/org/teams/design-team.json +40 -0
  384. package/specialists/org/teams/engineering-team.json +50 -0
  385. package/specialists/org/teams/governance-team.json +41 -0
  386. package/specialists/org/teams/operations-team.json +46 -0
  387. package/specialists/org/teams/product-management-team.json +45 -0
  388. package/specialists/org/teams/quality-team.json +49 -0
  389. package/specialists/org/teams/research-team.json +39 -0
  390. package/specialists/org/teams/strategy-team.json +48 -0
  391. package/specialists/org/teams/ux-research-team.json +39 -0
  392. package/specialists/prompts/_shared/validation-contract.md +1 -1
  393. package/specialists/prompts/_team-template.md +10 -0
  394. package/specialists/prompts/cx-accessibility.md +1 -0
  395. package/specialists/prompts/cx-ai-engineer.md +1 -1
  396. package/specialists/prompts/cx-business-strategist.md +1 -1
  397. package/specialists/prompts/cx-data-engineer.md +1 -1
  398. package/specialists/prompts/cx-designer.md +1 -0
  399. package/specialists/prompts/cx-devil-advocate.md +1 -1
  400. package/specialists/prompts/cx-docs-keeper.md +1 -1
  401. package/specialists/prompts/cx-evaluator.md +1 -1
  402. package/specialists/prompts/cx-explorer.md +1 -1
  403. package/specialists/prompts/cx-operations.md +1 -1
  404. package/specialists/prompts/cx-oracle.md +7 -3
  405. package/specialists/prompts/cx-orchestrator.md +17 -1
  406. package/specialists/prompts/cx-platform-engineer.md +1 -1
  407. package/specialists/prompts/cx-product-manager.md +2 -0
  408. package/specialists/prompts/cx-release-manager.md +1 -1
  409. package/specialists/prompts/cx-researcher.md +7 -4
  410. package/specialists/prompts/cx-sre.md +1 -1
  411. package/specialists/prompts/cx-test-automation.md +2 -2
  412. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  413. package/specialists/prompts/cx-ux-researcher.md +2 -1
  414. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  415. package/templates/demos/scripts/capability-contract.json +25 -0
  416. package/templates/demos/scripts/intake-triage.json +25 -0
  417. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  418. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  419. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  420. package/templates/demos/tapes/capability-contract.tape +39 -0
  421. package/templates/demos/tapes/intake-triage.tape +39 -0
  422. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  423. package/templates/distribution/construct-brand.typ +23 -18
  424. package/templates/distribution/construct-decision.typ +1 -1
  425. package/templates/distribution/construct-pdf.typ +1 -1
  426. package/templates/distribution/construct-prd.typ +1 -1
  427. package/templates/distribution/construct-research.typ +1 -1
  428. package/templates/distribution/mermaid-puppeteer.json +8 -0
  429. package/templates/docs/persona-artifact.md +1 -1
  430. package/templates/docs/prd.md +6 -0
  431. package/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  432. package/apps/chat/engine/loop-driver.mjs +0 -211
  433. package/apps/chat/engine/models.mjs +0 -122
  434. package/apps/chat/engine/provider-adapters.mjs +0 -171
  435. package/apps/chat/engine/tools/permission.mjs +0 -54
  436. package/apps/chat/engine/tools/primitives.mjs +0 -180
  437. package/apps/chat/engine/tools/registry.mjs +0 -122
  438. package/apps/chat/engine/turn-controls.mjs +0 -70
  439. package/lib/boundary.mjs +0 -127
  440. package/lib/certification/dashboard-api.mjs +0 -71
  441. package/lib/chat/cli.mjs +0 -333
  442. package/lib/chat/command-suggest.mjs +0 -161
  443. package/lib/chat/commands.mjs +0 -215
  444. package/lib/chat/config.mjs +0 -142
  445. package/lib/chat/context-compactor.mjs +0 -250
  446. package/lib/chat/context-continuation.mjs +0 -253
  447. package/lib/chat/continuation-source.mjs +0 -58
  448. package/lib/chat/demo-guide.mjs +0 -61
  449. package/lib/chat/design-tokens.mjs +0 -91
  450. package/lib/chat/desktop-binary.mjs +0 -81
  451. package/lib/chat/desktop-build.mjs +0 -130
  452. package/lib/chat/desktop-launcher.mjs +0 -133
  453. package/lib/chat/evidence.mjs +0 -145
  454. package/lib/chat/export.mjs +0 -74
  455. package/lib/chat/harness/driver.mjs +0 -91
  456. package/lib/chat/list-picker.mjs +0 -112
  457. package/lib/chat/model-picker.mjs +0 -356
  458. package/lib/chat/openrouter-fallback.mjs +0 -151
  459. package/lib/chat/permission-prompt.mjs +0 -33
  460. package/lib/chat/picker-catalog.mjs +0 -45
  461. package/lib/chat/policy-telemetry.mjs +0 -34
  462. package/lib/chat/present.mjs +0 -246
  463. package/lib/chat/session-context.mjs +0 -39
  464. package/lib/chat/session-persist.mjs +0 -73
  465. package/lib/chat/session-restore.mjs +0 -71
  466. package/lib/chat/session-settings.mjs +0 -53
  467. package/lib/chat/system-prompt.mjs +0 -52
  468. package/lib/chat/transparency.mjs +0 -93
  469. package/lib/chat/tui/color-scheme.mjs +0 -42
  470. package/lib/chat/tui/markdown.mjs +0 -123
  471. package/lib/chat/tui/presentation.mjs +0 -100
  472. package/lib/chat/tui/render.mjs +0 -500
  473. package/lib/chat/tui/turn-block.mjs +0 -284
  474. package/lib/chat/tui/turn-present.mjs +0 -18
  475. package/lib/chat/tui/turn-state.mjs +0 -88
  476. package/lib/chat/tui/usage.mjs +0 -122
  477. package/lib/chat/web-commands.mjs +0 -146
  478. package/lib/chat/web-launcher.mjs +0 -63
  479. package/lib/chat/web-picker-keys.mjs +0 -46
  480. package/lib/chat/web-session.mjs +0 -159
  481. package/lib/config/alias.mjs +0 -57
  482. package/lib/dashboard-demo.mjs +0 -71
  483. package/lib/dashboard-static.mjs +0 -175
  484. package/lib/install/desktop-binary-download.mjs +0 -88
  485. package/lib/profiles/loader.mjs +0 -123
  486. package/lib/profiles/rebrand.mjs +0 -46
  487. package/lib/server/auth.mjs +0 -169
  488. package/lib/server/chat-loop.mjs +0 -622
  489. package/lib/server/chat.mjs +0 -336
  490. package/lib/server/cors.mjs +0 -77
  491. package/lib/server/csrf.mjs +0 -103
  492. package/lib/server/demo-preview.mjs +0 -63
  493. package/lib/server/index.mjs +0 -2641
  494. package/lib/server/insights.mjs +0 -780
  495. package/lib/server/langfuse-login.mjs +0 -58
  496. package/lib/server/rate-limit.mjs +0 -93
  497. package/lib/server/telemetry-login.mjs +0 -100
  498. package/lib/server/webhook.mjs +0 -512
  499. package/specialists/contracts.json +0 -1032
  500. package/specialists/contracts.schema.json +0 -83
  501. package/specialists/policy-inventory.json +0 -188
  502. package/specialists/registry.json +0 -1412
  503. package/specialists/role-manifests.json +0 -217
  504. 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.
@@ -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
+ }