@geraldmaron/construct 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (505) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +235 -209
  3. package/bin/construct-postinstall.mjs +0 -21
  4. package/commands/work/optimize-prompts.md +1 -1
  5. package/examples/distribution/sources/adr.md +2 -2
  6. package/examples/seed-observations/decisions.md +1 -1
  7. package/lib/artifact-loop-core.mjs +412 -0
  8. package/lib/artifact-manifest.mjs +6 -0
  9. package/lib/artifact-release-gate.mjs +115 -49
  10. package/lib/audit-rules.mjs +2 -4
  11. package/lib/audit-skills.mjs +32 -20
  12. package/lib/audit-specialists.mjs +44 -26
  13. package/lib/auto-docs.mjs +6 -5
  14. package/lib/brand-prose.mjs +3 -3
  15. package/lib/brand-tokens.mjs +2 -2
  16. package/lib/certification/artifact-fixtures.mjs +4 -1
  17. package/lib/certification/demo-parity.mjs +6 -32
  18. package/lib/certification/real-llm-scenarios.mjs +58 -13
  19. package/lib/certification/role-cards.mjs +7 -6
  20. package/lib/certification/role-overlays.mjs +4 -4
  21. package/lib/certification/runner.mjs +9 -2
  22. package/lib/certification/skill-inventory.mjs +34 -22
  23. package/lib/certification/skill-scenarios.mjs +21 -8
  24. package/lib/certification/specialist-contracts.mjs +4 -3
  25. package/lib/certification/specialist-scenarios.mjs +7 -6
  26. package/lib/certification/status.mjs +5 -4
  27. package/lib/cli-commands.mjs +55 -54
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +90 -24
  31. package/lib/config/schema.mjs +5 -5
  32. package/lib/config/source-targets.mjs +80 -0
  33. package/lib/contracts/validate.mjs +119 -51
  34. package/lib/decisions/golden.mjs +3 -2
  35. package/lib/decisions/registry.mjs +11 -6
  36. package/lib/demo-project.mjs +188 -0
  37. package/lib/demo-recording.mjs +9 -34
  38. package/lib/demo-script.mjs +2 -2
  39. package/lib/demo-surface.mjs +13 -131
  40. package/lib/demo-tour-renderer.mjs +92 -0
  41. package/lib/demo.mjs +69 -54
  42. package/lib/diagram-export.mjs +63 -14
  43. package/lib/doctor/cli.mjs +12 -2
  44. package/lib/doctor/source-checkout.mjs +3 -4
  45. package/lib/doctor/watchers/consistency.mjs +148 -43
  46. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  47. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  48. package/lib/doctor/watchers/service-health.mjs +4 -32
  49. package/lib/document-export.mjs +73 -9
  50. package/lib/document-extract.mjs +10 -198
  51. package/lib/embed/artifact.mjs +2 -3
  52. package/lib/embed/cli.mjs +1 -1
  53. package/lib/embed/config.mjs +1 -2
  54. package/lib/embed/customer-profiles.mjs +1 -1
  55. package/lib/embed/daemon.mjs +5 -6
  56. package/lib/embed/demand-fetch.mjs +114 -2
  57. package/lib/embed/docs-lifecycle.mjs +9 -5
  58. package/lib/embed/intake-metrics.mjs +2 -2
  59. package/lib/embed/notifications.mjs +2 -3
  60. package/lib/embed/recommendation-store.mjs +25 -22
  61. package/lib/embed/role-framing.mjs +4 -9
  62. package/lib/embedded-contract/capability.mjs +4 -2
  63. package/lib/embedded-contract/contract-version.mjs +1 -1
  64. package/lib/env-config.mjs +2 -2
  65. package/lib/flavors/loader.mjs +21 -19
  66. package/lib/graph/build-from-registry.mjs +31 -7
  67. package/lib/graph/staleness.mjs +1 -1
  68. package/lib/headhunt.mjs +82 -35
  69. package/lib/hooks/agent-tracker.mjs +2 -2
  70. package/lib/hooks/config-protection.mjs +1 -1
  71. package/lib/hooks/registry-sync.mjs +3 -3
  72. package/lib/host-capabilities.mjs +5 -5
  73. package/lib/improvement/controller.mjs +2 -2
  74. package/lib/init-unified.mjs +19 -21
  75. package/lib/intake/classify.mjs +26 -0
  76. package/lib/intake/daemon.mjs +2 -3
  77. package/lib/intake/prepare.mjs +10 -3
  78. package/lib/intake/session-prelude.mjs +1 -1
  79. package/lib/intake/tables/creative.mjs +9 -9
  80. package/lib/intake/tables/operations.mjs +4 -4
  81. package/lib/intake/tables/research.mjs +2 -2
  82. package/lib/integrations/intake-integrations.mjs +9 -10
  83. package/lib/knowledge/research-store.mjs +2 -2
  84. package/lib/mcp/server.mjs +104 -61
  85. package/lib/mcp/tool-budget.mjs +1 -46
  86. package/lib/mcp/tool-recovery.mjs +44 -0
  87. package/lib/mcp/tools/artifact-author.mjs +36 -0
  88. package/lib/mcp/tools/find-tool.mjs +86 -0
  89. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  90. package/lib/mcp/tools/project.mjs +4 -3
  91. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  92. package/lib/mcp/tools/skills.mjs +39 -22
  93. package/lib/mcp/tools/telemetry.mjs +2 -1
  94. package/lib/mcp/tools/workflow.mjs +1 -1
  95. package/lib/mcp-platform-config.mjs +7 -5
  96. package/lib/migrations/index.mjs +4 -2
  97. package/lib/migrations/v2-unified-registry.mjs +35 -0
  98. package/lib/model-router.mjs +203 -36
  99. package/lib/models/catalog.mjs +25 -9
  100. package/lib/models/execution-capability-profile.mjs +3 -3
  101. package/lib/models/execution-policy.mjs +7 -6
  102. package/lib/models/provider-poll.mjs +18 -4
  103. package/lib/opencode-config.mjs +56 -1
  104. package/lib/opencode-runtime-plugin.mjs +13 -10
  105. package/lib/oracle/artifact-gate.mjs +14 -4
  106. package/lib/oracle/cli.mjs +2 -0
  107. package/lib/oracle/dispatch.mjs +18 -1
  108. package/lib/oracle/execute.mjs +39 -7
  109. package/lib/oracle/index.mjs +1 -1
  110. package/lib/oracle/org-graph.mjs +10 -5
  111. package/lib/oracle/policy.mjs +6 -0
  112. package/lib/oracle/read-model.mjs +103 -6
  113. package/lib/oracle/reconcile.mjs +31 -4
  114. package/lib/oracle/remediation-dispatch.mjs +53 -0
  115. package/lib/oracle/routing.mjs +5 -0
  116. package/lib/oracle/synthesize.mjs +80 -3
  117. package/lib/orchestration/routing-tables.mjs +44 -10
  118. package/lib/orchestration/runtime.mjs +2 -0
  119. package/lib/orchestration/worker.mjs +1 -1
  120. package/lib/orchestration-policy.mjs +351 -62
  121. package/lib/overrides/resolver.mjs +1 -12
  122. package/lib/parity.mjs +37 -11
  123. package/lib/policy/engine.mjs +80 -15
  124. package/lib/prompt-composer.js +51 -15
  125. package/lib/prompt-metadata.mjs +3 -2
  126. package/lib/providers/connection-probe.mjs +58 -0
  127. package/lib/providers/copilot-auth.mjs +1 -1
  128. package/lib/providers/credential-bootstrap.mjs +1 -1
  129. package/lib/providers/op-run.mjs +3 -4
  130. package/lib/providers/secret-resolver.mjs +31 -0
  131. package/lib/publish-tooling.mjs +3 -11
  132. package/lib/publish.mjs +12 -29
  133. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  134. package/lib/reflect.mjs +2 -2
  135. package/lib/registry/agent-manifest.mjs +190 -0
  136. package/lib/registry/assemble.mjs +133 -0
  137. package/lib/registry/catalog.mjs +171 -0
  138. package/lib/registry/cli.mjs +590 -6
  139. package/lib/registry/consolidation.mjs +5 -4
  140. package/lib/registry/docs-sync.mjs +67 -0
  141. package/lib/registry/loader.mjs +147 -0
  142. package/lib/registry/org-io.mjs +76 -0
  143. package/lib/registry/retired-paths.mjs +71 -0
  144. package/lib/registry/surface-map.mjs +5 -7
  145. package/lib/registry/validator.mjs +438 -0
  146. package/lib/research-execution-policy.mjs +173 -0
  147. package/lib/roles/catalog.mjs +4 -9
  148. package/lib/roles/fence.mjs +107 -1
  149. package/lib/roles/flavor-bindings.mjs +68 -0
  150. package/lib/roles/gateway.mjs +140 -29
  151. package/lib/roles/manifest.mjs +22 -13
  152. package/lib/roles/router.mjs +1 -1
  153. package/lib/scopes/enrich.mjs +67 -0
  154. package/lib/scopes/hooks.mjs +12 -0
  155. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  156. package/lib/scopes/loader.mjs +104 -0
  157. package/lib/scopes/rebrand.mjs +32 -0
  158. package/lib/scopes/research-profile.mjs +16 -0
  159. package/lib/scopes/teams.mjs +98 -0
  160. package/lib/service-manager.mjs +1 -117
  161. package/lib/setup-prompts.mjs +1 -1
  162. package/lib/setup.mjs +6 -26
  163. package/lib/skills/composition-graph.mjs +98 -0
  164. package/lib/skills/router.mjs +80 -0
  165. package/lib/specialist-contracts.mjs +17 -18
  166. package/lib/specialists/postconditions.mjs +2 -2
  167. package/lib/specialists/prompt-schema.mjs +6 -5
  168. package/lib/specialists/roster.mjs +7 -12
  169. package/lib/specialists/schema.mjs +9 -6
  170. package/lib/status.mjs +25 -90
  171. package/lib/storage/file-lock.mjs +6 -3
  172. package/lib/telemetry/skill-calls.mjs +1 -1
  173. package/lib/templates/doc-presentation.mjs +63 -0
  174. package/lib/templates/visual-requirements.mjs +35 -2
  175. package/lib/term-format.mjs +107 -2
  176. package/lib/test-env-setup.mjs +21 -0
  177. package/lib/ui/components.mjs +42 -0
  178. package/lib/ui/glyphs.mjs +58 -0
  179. package/lib/ui/links.mjs +115 -0
  180. package/lib/ui/theme.mjs +108 -0
  181. package/lib/uninstall/uninstall.mjs +2 -1
  182. package/lib/validator.mjs +45 -8
  183. package/lib/validators/skill-effectiveness.mjs +174 -0
  184. package/lib/validators/skills.mjs +1 -1
  185. package/package.json +12 -8
  186. package/personas/construct.md +12 -3
  187. package/platforms/claude/CLAUDE.md +1 -1
  188. package/rules/common/beads-hygiene.md +52 -0
  189. package/rules/common/neurodivergent-output.md +4 -7
  190. package/rules/common/research.md +2 -0
  191. package/scripts/sync-specialists.mjs +269 -51
  192. package/skills/ai/prompt-optimizer.md +3 -3
  193. package/skills/brand/output-vibe.md +48 -0
  194. package/skills/docs/adr-workflow.md +3 -0
  195. package/skills/docs/backlog-proposal-workflow.md +3 -0
  196. package/skills/docs/codebase-research-workflow.md +5 -2
  197. package/skills/docs/customer-profile-workflow.md +3 -0
  198. package/skills/docs/document-ingest-workflow.md +3 -0
  199. package/skills/docs/evidence-ingest-workflow.md +4 -1
  200. package/skills/docs/init-project.md +1 -1
  201. package/skills/docs/prd-workflow.md +1 -1
  202. package/skills/docs/prfaq-workflow.md +3 -0
  203. package/skills/docs/product-intelligence-workflow.md +4 -1
  204. package/skills/docs/product-signal-workflow.md +3 -0
  205. package/skills/docs/research-workflow.md +13 -6
  206. package/skills/docs/runbook-workflow.md +3 -0
  207. package/skills/docs/strategy-workflow.md +3 -0
  208. package/skills/docs/user-research-workflow.md +6 -3
  209. package/skills/operating/orchestration-reference.md +1 -1
  210. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  211. package/skills/roles/architect.ai-systems.md +1 -1
  212. package/skills/roles/architect.data.md +1 -1
  213. package/skills/roles/architect.enterprise.md +1 -1
  214. package/skills/roles/architect.integration.md +1 -1
  215. package/skills/roles/architect.md +1 -1
  216. package/skills/roles/architect.platform.md +1 -1
  217. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  218. package/skills/roles/data-analyst.experiment.md +1 -1
  219. package/skills/roles/data-analyst.md +1 -1
  220. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  221. package/skills/roles/data-analyst.product.md +1 -1
  222. package/skills/roles/data-analyst.telemetry.md +1 -1
  223. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  224. package/skills/roles/data-engineer.pipeline.md +2 -2
  225. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  226. package/skills/roles/data-engineer.warehouse.md +2 -2
  227. package/skills/roles/debugger.md +1 -1
  228. package/skills/roles/designer.accessibility.md +1 -1
  229. package/skills/roles/designer.md +1 -1
  230. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  231. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  232. package/skills/roles/engineer.md +3 -7
  233. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  234. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  235. package/skills/roles/{operator.md → operations.md} +2 -5
  236. package/skills/roles/orchestrator.md +1 -1
  237. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  238. package/skills/roles/product-manager.ai-product.md +1 -1
  239. package/skills/roles/product-manager.enterprise.md +1 -1
  240. package/skills/roles/product-manager.growth.md +1 -2
  241. package/skills/roles/product-manager.md +1 -2
  242. package/skills/roles/product-manager.platform.md +1 -1
  243. package/skills/roles/product-manager.product.md +1 -1
  244. package/skills/roles/qa.ai-eval.md +1 -1
  245. package/skills/roles/qa.api-contract.md +1 -1
  246. package/skills/roles/qa.data-pipeline.md +1 -1
  247. package/skills/roles/qa.md +1 -1
  248. package/skills/roles/qa.web-ui.md +1 -1
  249. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  250. package/skills/roles/researcher.md +1 -1
  251. package/skills/roles/reviewer.md +1 -1
  252. package/skills/roles/security.ai.md +1 -1
  253. package/skills/roles/security.appsec.md +1 -1
  254. package/skills/roles/security.cloud.md +1 -1
  255. package/skills/roles/security.legal-compliance.md +1 -1
  256. package/skills/roles/security.md +1 -1
  257. package/skills/roles/security.privacy.md +1 -1
  258. package/skills/roles/security.supply-chain.md +1 -1
  259. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  260. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  261. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  262. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  263. package/skills/routing.json +18 -0
  264. package/skills/routing.md +1 -1
  265. package/specialists/artifact-manifest.json +2 -1
  266. package/specialists/audit-enrichments.json +14 -14
  267. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  268. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  269. package/specialists/org/contracts/any-to-debugger.json +38 -0
  270. package/specialists/org/contracts/any-to-designer.json +33 -0
  271. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  272. package/specialists/org/contracts/any-to-explorer.json +39 -0
  273. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  274. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  275. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  276. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  277. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  278. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  279. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  280. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  281. package/specialists/org/contracts/architect-to-operations.json +45 -0
  282. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  283. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  284. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  285. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  286. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  287. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  288. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  289. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  290. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  291. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  292. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  293. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  294. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  295. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  296. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  297. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  298. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  299. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  300. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  301. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  302. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  303. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  304. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  305. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  306. package/specialists/org/contracts/user-to-construct.json +30 -0
  307. package/specialists/org/groups/engineering-group.json +43 -0
  308. package/specialists/org/groups/governance-group.json +38 -0
  309. package/specialists/org/groups/operations-group.json +41 -0
  310. package/specialists/org/groups/product-group.json +46 -0
  311. package/specialists/org/groups/quality-group.json +42 -0
  312. package/specialists/org/groups/strategy-group.json +41 -0
  313. package/specialists/org/policies/action-approval.json +13 -0
  314. package/specialists/org/policies/agents-routing.json +13 -0
  315. package/specialists/org/policies/architecture.json +20 -0
  316. package/specialists/org/policies/bash-safety.json +13 -0
  317. package/specialists/org/policies/beads-hygiene.json +13 -0
  318. package/specialists/org/policies/bootstrap-state.json +13 -0
  319. package/specialists/org/policies/code-review.json +13 -0
  320. package/specialists/org/policies/coding-style.json +13 -0
  321. package/specialists/org/policies/comments.json +13 -0
  322. package/specialists/org/policies/commit-approval.json +13 -0
  323. package/specialists/org/policies/contract-preconditions.json +13 -0
  324. package/specialists/org/policies/deployment.json +20 -0
  325. package/specialists/org/policies/description.json +14 -0
  326. package/specialists/org/policies/design-approval.json +19 -0
  327. package/specialists/org/policies/doc-ownership.json +13 -0
  328. package/specialists/org/policies/file-path-fence.json +13 -0
  329. package/specialists/org/policies/framing.json +13 -0
  330. package/specialists/org/policies/git-workflow.json +13 -0
  331. package/specialists/org/policies/incident-response.json +15 -0
  332. package/specialists/org/policies/intake-triage.json +15 -0
  333. package/specialists/org/policies/neurodivergent-output.json +13 -0
  334. package/specialists/org/policies/no-fabrication.json +13 -0
  335. package/specialists/org/policies/patterns.json +13 -0
  336. package/specialists/org/policies/quality-gate-approval.json +17 -0
  337. package/specialists/org/policies/release-gates.json +13 -0
  338. package/specialists/org/policies/research-evidence.json +13 -0
  339. package/specialists/org/policies/review-before-change.json +13 -0
  340. package/specialists/org/policies/rollback.json +15 -0
  341. package/specialists/org/policies/scope-change.json +20 -0
  342. package/specialists/org/policies/secret-scan.json +13 -0
  343. package/specialists/org/policies/security-approval.json +17 -0
  344. package/specialists/org/policies/security.json +13 -0
  345. package/specialists/org/policies/session-efficiency.json +13 -0
  346. package/specialists/org/policies/skill-routing.json +13 -0
  347. package/specialists/org/policies/strategic-prioritization.json +17 -0
  348. package/specialists/org/policies/testing.json +13 -0
  349. package/specialists/org/policies/tool-invisibility.json +14 -0
  350. package/specialists/org/scopes/creative.json +54 -0
  351. package/specialists/org/scopes/operations.json +51 -0
  352. package/specialists/org/scopes/research.json +60 -0
  353. package/specialists/org/scopes/rnd.json +81 -0
  354. package/specialists/org/specialists/cx-accessibility.json +69 -0
  355. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  356. package/specialists/org/specialists/cx-architect.json +89 -0
  357. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  358. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  359. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  360. package/specialists/org/specialists/cx-debugger.json +75 -0
  361. package/specialists/org/specialists/cx-designer.json +85 -0
  362. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  363. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  364. package/specialists/org/specialists/cx-engineer.json +106 -0
  365. package/specialists/org/specialists/cx-evaluator.json +69 -0
  366. package/specialists/org/specialists/cx-explorer.json +69 -0
  367. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  368. package/specialists/org/specialists/cx-operations.json +72 -0
  369. package/specialists/org/specialists/cx-oracle.json +46 -0
  370. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  371. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  372. package/specialists/org/specialists/cx-product-manager.json +102 -0
  373. package/specialists/org/specialists/cx-qa.json +79 -0
  374. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  375. package/specialists/org/specialists/cx-release-manager.json +77 -0
  376. package/specialists/org/specialists/cx-researcher.json +82 -0
  377. package/specialists/org/specialists/cx-reviewer.json +71 -0
  378. package/specialists/org/specialists/cx-security.json +91 -0
  379. package/specialists/org/specialists/cx-sre.json +94 -0
  380. package/specialists/org/specialists/cx-test-automation.json +69 -0
  381. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  382. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  383. package/specialists/org/teams/accessibility-team.json +39 -0
  384. package/specialists/org/teams/design-team.json +40 -0
  385. package/specialists/org/teams/engineering-team.json +50 -0
  386. package/specialists/org/teams/governance-team.json +41 -0
  387. package/specialists/org/teams/operations-team.json +46 -0
  388. package/specialists/org/teams/product-management-team.json +45 -0
  389. package/specialists/org/teams/quality-team.json +49 -0
  390. package/specialists/org/teams/research-team.json +39 -0
  391. package/specialists/org/teams/strategy-team.json +48 -0
  392. package/specialists/org/teams/ux-research-team.json +39 -0
  393. package/specialists/prompts/_shared/validation-contract.md +1 -1
  394. package/specialists/prompts/_team-template.md +10 -0
  395. package/specialists/prompts/cx-accessibility.md +1 -0
  396. package/specialists/prompts/cx-ai-engineer.md +1 -1
  397. package/specialists/prompts/cx-business-strategist.md +1 -1
  398. package/specialists/prompts/cx-data-engineer.md +1 -1
  399. package/specialists/prompts/cx-designer.md +1 -0
  400. package/specialists/prompts/cx-devil-advocate.md +1 -1
  401. package/specialists/prompts/cx-docs-keeper.md +1 -1
  402. package/specialists/prompts/cx-evaluator.md +1 -1
  403. package/specialists/prompts/cx-explorer.md +1 -1
  404. package/specialists/prompts/cx-operations.md +1 -1
  405. package/specialists/prompts/cx-oracle.md +7 -3
  406. package/specialists/prompts/cx-orchestrator.md +17 -1
  407. package/specialists/prompts/cx-platform-engineer.md +1 -1
  408. package/specialists/prompts/cx-product-manager.md +2 -0
  409. package/specialists/prompts/cx-release-manager.md +1 -1
  410. package/specialists/prompts/cx-researcher.md +7 -4
  411. package/specialists/prompts/cx-sre.md +1 -1
  412. package/specialists/prompts/cx-test-automation.md +2 -2
  413. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  414. package/specialists/prompts/cx-ux-researcher.md +2 -1
  415. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  416. package/templates/demos/scripts/capability-contract.json +25 -0
  417. package/templates/demos/scripts/intake-triage.json +25 -0
  418. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  419. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  420. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  421. package/templates/demos/tapes/capability-contract.tape +39 -0
  422. package/templates/demos/tapes/intake-triage.tape +39 -0
  423. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  424. package/templates/distribution/construct-brand.typ +23 -18
  425. package/templates/distribution/construct-decision.typ +1 -1
  426. package/templates/distribution/construct-pdf.typ +1 -1
  427. package/templates/distribution/construct-prd.typ +1 -1
  428. package/templates/distribution/construct-research.typ +1 -1
  429. package/templates/distribution/mermaid-puppeteer.json +8 -0
  430. package/templates/docs/persona-artifact.md +1 -1
  431. package/templates/docs/prd.md +6 -0
  432. package/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  433. package/apps/chat/engine/loop-driver.mjs +0 -211
  434. package/apps/chat/engine/models.mjs +0 -122
  435. package/apps/chat/engine/provider-adapters.mjs +0 -171
  436. package/apps/chat/engine/tools/permission.mjs +0 -54
  437. package/apps/chat/engine/tools/primitives.mjs +0 -180
  438. package/apps/chat/engine/tools/registry.mjs +0 -122
  439. package/apps/chat/engine/turn-controls.mjs +0 -70
  440. package/lib/boundary.mjs +0 -127
  441. package/lib/certification/dashboard-api.mjs +0 -71
  442. package/lib/chat/cli.mjs +0 -333
  443. package/lib/chat/command-suggest.mjs +0 -161
  444. package/lib/chat/commands.mjs +0 -215
  445. package/lib/chat/config.mjs +0 -142
  446. package/lib/chat/context-compactor.mjs +0 -250
  447. package/lib/chat/context-continuation.mjs +0 -253
  448. package/lib/chat/continuation-source.mjs +0 -58
  449. package/lib/chat/demo-guide.mjs +0 -61
  450. package/lib/chat/design-tokens.mjs +0 -91
  451. package/lib/chat/desktop-binary.mjs +0 -81
  452. package/lib/chat/desktop-build.mjs +0 -130
  453. package/lib/chat/desktop-launcher.mjs +0 -133
  454. package/lib/chat/evidence.mjs +0 -145
  455. package/lib/chat/export.mjs +0 -74
  456. package/lib/chat/harness/driver.mjs +0 -91
  457. package/lib/chat/list-picker.mjs +0 -112
  458. package/lib/chat/model-picker.mjs +0 -356
  459. package/lib/chat/openrouter-fallback.mjs +0 -151
  460. package/lib/chat/permission-prompt.mjs +0 -33
  461. package/lib/chat/picker-catalog.mjs +0 -45
  462. package/lib/chat/policy-telemetry.mjs +0 -34
  463. package/lib/chat/present.mjs +0 -246
  464. package/lib/chat/session-context.mjs +0 -39
  465. package/lib/chat/session-persist.mjs +0 -73
  466. package/lib/chat/session-restore.mjs +0 -71
  467. package/lib/chat/session-settings.mjs +0 -53
  468. package/lib/chat/system-prompt.mjs +0 -52
  469. package/lib/chat/transparency.mjs +0 -93
  470. package/lib/chat/tui/color-scheme.mjs +0 -42
  471. package/lib/chat/tui/markdown.mjs +0 -123
  472. package/lib/chat/tui/presentation.mjs +0 -100
  473. package/lib/chat/tui/render.mjs +0 -500
  474. package/lib/chat/tui/turn-block.mjs +0 -284
  475. package/lib/chat/tui/turn-present.mjs +0 -18
  476. package/lib/chat/tui/turn-state.mjs +0 -88
  477. package/lib/chat/tui/usage.mjs +0 -122
  478. package/lib/chat/web-commands.mjs +0 -146
  479. package/lib/chat/web-launcher.mjs +0 -63
  480. package/lib/chat/web-picker-keys.mjs +0 -46
  481. package/lib/chat/web-session.mjs +0 -159
  482. package/lib/config/alias.mjs +0 -57
  483. package/lib/dashboard-demo.mjs +0 -71
  484. package/lib/dashboard-static.mjs +0 -175
  485. package/lib/install/desktop-binary-download.mjs +0 -88
  486. package/lib/profiles/loader.mjs +0 -123
  487. package/lib/profiles/rebrand.mjs +0 -46
  488. package/lib/server/auth.mjs +0 -169
  489. package/lib/server/chat-loop.mjs +0 -622
  490. package/lib/server/chat.mjs +0 -336
  491. package/lib/server/cors.mjs +0 -77
  492. package/lib/server/csrf.mjs +0 -103
  493. package/lib/server/demo-preview.mjs +0 -63
  494. package/lib/server/index.mjs +0 -2641
  495. package/lib/server/insights.mjs +0 -780
  496. package/lib/server/langfuse-login.mjs +0 -58
  497. package/lib/server/rate-limit.mjs +0 -93
  498. package/lib/server/telemetry-login.mjs +0 -100
  499. package/lib/server/webhook.mjs +0 -512
  500. package/specialists/contracts.json +0 -1032
  501. package/specialists/contracts.schema.json +0 -83
  502. package/specialists/policy-inventory.json +0 -188
  503. package/specialists/registry.json +0 -1412
  504. package/specialists/role-manifests.json +0 -217
  505. package/specialists/teams.json +0 -94
@@ -1,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
+ }