@geraldmaron/construct 1.1.1 → 1.2.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 (426) hide show
  1. package/README.md +30 -19
  2. package/bin/construct +487 -87
  3. package/bin/construct-postinstall.mjs +30 -2
  4. package/examples/distribution/README.md +42 -0
  5. package/examples/distribution/manifest.json +53 -0
  6. package/examples/distribution/sources/adr.md +84 -0
  7. package/examples/distribution/sources/deck-one-pager.md +65 -0
  8. package/examples/distribution/sources/prd-platform.md +161 -0
  9. package/examples/distribution/sources/research-brief.md +80 -0
  10. package/examples/distribution/sources/rfc-platform.md +82 -0
  11. package/examples/distribution/sources/runbook.md +103 -0
  12. package/examples/distribution/sources/strategy.md +88 -0
  13. package/lib/adapters-sync.mjs +67 -0
  14. package/lib/artifact-gate-notice.mjs +38 -0
  15. package/lib/artifact-manifest.mjs +258 -0
  16. package/lib/artifact-release-gate.mjs +171 -0
  17. package/lib/artifact-reviewers.mjs +68 -0
  18. package/lib/artifact-type-from-path.mjs +79 -0
  19. package/lib/artifact-workflow.mjs +252 -0
  20. package/lib/audit-skills.mjs +4 -0
  21. package/lib/audit-specialists.mjs +285 -0
  22. package/lib/audit-trail.mjs +1 -1
  23. package/lib/auto-docs.mjs +90 -19
  24. package/lib/beads/drift.mjs +27 -6
  25. package/lib/beads-client.mjs +49 -121
  26. package/lib/beads-optimistic.mjs +5 -12
  27. package/lib/brand-fonts.mjs +93 -0
  28. package/lib/brand-prose.mjs +214 -0
  29. package/lib/brand-tokens.mjs +92 -0
  30. package/lib/bridges/copilot-proxy.mjs +13 -26
  31. package/lib/capability-ledger.mjs +156 -0
  32. package/lib/certification/artifact-fixtures.mjs +132 -0
  33. package/lib/certification/artifact-gates.mjs +63 -0
  34. package/lib/certification/artifact-provenance.mjs +97 -0
  35. package/lib/certification/canonical-scenarios.mjs +78 -0
  36. package/lib/certification/cli.mjs +191 -0
  37. package/lib/certification/dashboard-api.mjs +71 -0
  38. package/lib/certification/demo-parity.mjs +116 -0
  39. package/lib/certification/document-io-fixtures.mjs +246 -0
  40. package/lib/certification/document-workflow.mjs +97 -0
  41. package/lib/certification/eval-bridge.mjs +77 -0
  42. package/lib/certification/model-routing.mjs +149 -0
  43. package/lib/certification/prompt-budget.mjs +119 -0
  44. package/lib/certification/rc-gate.mjs +206 -0
  45. package/lib/certification/real-llm-scenarios.mjs +303 -0
  46. package/lib/certification/role-cards.mjs +113 -0
  47. package/lib/certification/role-overlays.mjs +117 -0
  48. package/lib/certification/run.mjs +122 -0
  49. package/lib/certification/runner.mjs +323 -0
  50. package/lib/certification/scenarios.mjs +51 -0
  51. package/lib/certification/skill-inventory.mjs +289 -0
  52. package/lib/certification/skill-scenarios.mjs +147 -0
  53. package/lib/certification/specialist-contracts.mjs +85 -0
  54. package/lib/certification/specialist-scenarios.mjs +175 -0
  55. package/lib/certification/stale-impact.mjs +146 -0
  56. package/lib/certification/status.mjs +252 -0
  57. package/lib/certification/store.mjs +77 -0
  58. package/lib/chat/cli.mjs +333 -0
  59. package/lib/chat/command-suggest.mjs +161 -0
  60. package/lib/chat/commands.mjs +215 -0
  61. package/lib/chat/config.mjs +142 -0
  62. package/lib/chat/context-compactor.mjs +250 -0
  63. package/lib/chat/context-continuation.mjs +253 -0
  64. package/lib/chat/continuation-source.mjs +58 -0
  65. package/lib/chat/demo-guide.mjs +61 -0
  66. package/lib/chat/design-tokens.mjs +91 -0
  67. package/lib/chat/desktop-binary.mjs +79 -0
  68. package/lib/chat/desktop-build.mjs +130 -0
  69. package/lib/chat/desktop-launcher.mjs +133 -0
  70. package/lib/chat/evidence.mjs +145 -0
  71. package/lib/chat/export.mjs +74 -0
  72. package/lib/chat/harness/driver.mjs +91 -0
  73. package/lib/chat/list-picker.mjs +112 -0
  74. package/lib/chat/model-picker.mjs +356 -0
  75. package/lib/chat/openrouter-fallback.mjs +151 -0
  76. package/lib/chat/permission-prompt.mjs +33 -0
  77. package/lib/chat/picker-catalog.mjs +45 -0
  78. package/lib/chat/policy-telemetry.mjs +34 -0
  79. package/lib/chat/present.mjs +246 -0
  80. package/lib/chat/session-context.mjs +39 -0
  81. package/lib/chat/session-persist.mjs +73 -0
  82. package/lib/chat/session-restore.mjs +71 -0
  83. package/lib/chat/session-settings.mjs +53 -0
  84. package/lib/chat/system-prompt.mjs +52 -0
  85. package/lib/chat/transparency.mjs +93 -0
  86. package/lib/chat/tui/color-scheme.mjs +42 -0
  87. package/lib/chat/tui/markdown.mjs +123 -0
  88. package/lib/chat/tui/presentation.mjs +100 -0
  89. package/lib/chat/tui/render.mjs +500 -0
  90. package/lib/chat/tui/turn-block.mjs +284 -0
  91. package/lib/chat/tui/turn-present.mjs +18 -0
  92. package/lib/chat/tui/turn-state.mjs +88 -0
  93. package/lib/chat/tui/usage.mjs +122 -0
  94. package/lib/chat/web-commands.mjs +146 -0
  95. package/lib/chat/web-launcher.mjs +63 -0
  96. package/lib/chat/web-picker-keys.mjs +46 -0
  97. package/lib/chat/web-session.mjs +159 -0
  98. package/lib/cli-commands.mjs +146 -15
  99. package/lib/cli-service-inventory.mjs +42 -0
  100. package/lib/comment-lint.mjs +6 -4
  101. package/lib/config/intake-policy.mjs +209 -0
  102. package/lib/config/project-config.mjs +11 -2
  103. package/lib/config/schema.mjs +86 -6
  104. package/lib/config/source-targets.mjs +311 -0
  105. package/lib/contract-schemas/decision.json +50 -0
  106. package/lib/contract-schemas/implementation.json +51 -0
  107. package/lib/contract-schemas/review-report.json +32 -0
  108. package/lib/contract-schemas/test-report.json +43 -0
  109. package/lib/contracts/construct-handoff.mjs +60 -0
  110. package/lib/contracts/validate.mjs +32 -3
  111. package/lib/contracts/violation-log.mjs +58 -5
  112. package/lib/dashboard-demo.mjs +71 -0
  113. package/lib/dashboard-static.mjs +19 -5
  114. package/lib/decisions/registry.mjs +5 -3
  115. package/lib/deck-export-pptx.mjs +1152 -0
  116. package/lib/demo-recording.mjs +142 -0
  117. package/lib/demo-script.mjs +114 -0
  118. package/lib/demo-surface.mjs +249 -0
  119. package/lib/demo.mjs +598 -140
  120. package/lib/diagram-export.mjs +192 -0
  121. package/lib/diagram.mjs +13 -11
  122. package/lib/docs-verify.mjs +25 -7
  123. package/lib/doctor/index.mjs +3 -1
  124. package/lib/doctor/project-adapters.mjs +44 -0
  125. package/lib/doctor/source-checkout.mjs +16 -0
  126. package/lib/doctor/watchers/cx-budget.mjs +98 -0
  127. package/lib/doctor/watchers/graph-staleness.mjs +52 -0
  128. package/lib/doctor/watchers/process-pressure.mjs +4 -3
  129. package/lib/document-export.mjs +384 -38
  130. package/lib/document-extract.mjs +44 -4
  131. package/lib/document-ingest.mjs +29 -20
  132. package/lib/embed/auto-sources.mjs +44 -0
  133. package/lib/embed/cli.mjs +4 -3
  134. package/lib/embed/daemon.mjs +18 -46
  135. package/lib/embed/demand-fetch.mjs +50 -19
  136. package/lib/embed/inbox.mjs +6 -10
  137. package/lib/embed/worker.mjs +4 -3
  138. package/lib/embedded-contract/index.mjs +11 -0
  139. package/lib/embedded-contract/model-resolve.mjs +10 -9
  140. package/lib/env-config.mjs +26 -0
  141. package/lib/evals/dataset.mjs +137 -0
  142. package/lib/evals/gates.mjs +175 -0
  143. package/lib/export-branding.mjs +32 -0
  144. package/lib/graph/build-co-change.mjs +54 -0
  145. package/lib/graph/build-from-registry.mjs +136 -0
  146. package/lib/graph/build-import-graph.mjs +153 -0
  147. package/lib/graph/cli.mjs +110 -0
  148. package/lib/graph/impact-cli.mjs +89 -0
  149. package/lib/graph/impact.mjs +108 -0
  150. package/lib/graph/staleness.mjs +44 -0
  151. package/lib/graph/store.mjs +172 -0
  152. package/lib/health-check.mjs +90 -76
  153. package/lib/hooks/artifact-release-gate.mjs +43 -0
  154. package/lib/hooks/brand-prose-lint.mjs +38 -0
  155. package/lib/hooks/graph-impact-advisory.mjs +62 -0
  156. package/lib/hooks/session-optimize.mjs +84 -207
  157. package/lib/hooks/session-start.mjs +18 -16
  158. package/lib/host-disposition.mjs +7 -0
  159. package/lib/improvement/cli.mjs +189 -0
  160. package/lib/improvement/controller.mjs +137 -0
  161. package/lib/improvement/proposal.mjs +120 -0
  162. package/lib/improvement/specialist-loop.mjs +192 -0
  163. package/lib/improvement/store.mjs +89 -0
  164. package/lib/improvement/surface.mjs +219 -0
  165. package/lib/ingest-tooling.mjs +97 -0
  166. package/lib/init/doc-lanes.mjs +165 -0
  167. package/lib/init-docs.mjs +31 -178
  168. package/lib/init-unified.mjs +50 -119
  169. package/lib/init-update-guide.mjs +102 -0
  170. package/lib/init-update.mjs +32 -1
  171. package/lib/init.mjs +8 -0
  172. package/lib/install/desktop-binary-download.mjs +85 -0
  173. package/lib/install/legacy-global-cleanup.mjs +189 -0
  174. package/lib/intake/constants.mjs +29 -0
  175. package/lib/intake/daemon.mjs +2 -0
  176. package/lib/intake/git-queue.mjs +9 -8
  177. package/lib/intake/intake-config.mjs +52 -105
  178. package/lib/intake/legacy-paths.mjs +5 -0
  179. package/lib/intake/queue.mjs +2 -1
  180. package/lib/intake/session-prelude.mjs +90 -1
  181. package/lib/libreoffice-export.mjs +97 -0
  182. package/lib/logging/rotate.mjs +9 -0
  183. package/lib/maintenance/docker-reclaim.mjs +206 -0
  184. package/lib/mcp/external-schema-cost.mjs +74 -0
  185. package/lib/mcp/server.mjs +103 -10
  186. package/lib/mcp/stdio-mcp-probe.mjs +188 -0
  187. package/lib/mcp/tool-budget.mjs +55 -4
  188. package/lib/mcp/tools/document.mjs +18 -4
  189. package/lib/mcp/tools/embedded-contract.mjs +14 -0
  190. package/lib/mcp/tools/skills.mjs +32 -3
  191. package/lib/mcp/tools/workflow.mjs +13 -1
  192. package/lib/model-free-selector.mjs +18 -0
  193. package/lib/model-registry.mjs +13 -229
  194. package/lib/model-router.mjs +373 -92
  195. package/lib/models/behavior-matrix.mjs +289 -0
  196. package/lib/models/catalog.mjs +209 -0
  197. package/lib/models/execution-capability-profile.mjs +196 -0
  198. package/lib/models/execution-policy.mjs +307 -0
  199. package/lib/models/provider-poll.mjs +383 -0
  200. package/lib/npm-spawn-env.mjs +17 -0
  201. package/lib/ollama/installed-models.mjs +129 -0
  202. package/lib/oracle/actions.mjs +187 -0
  203. package/lib/oracle/artifact-gate.mjs +99 -0
  204. package/lib/oracle/cli.mjs +204 -0
  205. package/lib/oracle/daemon-entry.mjs +14 -0
  206. package/lib/oracle/dispatch.mjs +81 -0
  207. package/lib/oracle/execute.mjs +143 -0
  208. package/lib/oracle/gaps.mjs +76 -0
  209. package/lib/oracle/index.mjs +84 -0
  210. package/lib/oracle/issues.mjs +164 -0
  211. package/lib/oracle/org-graph.mjs +170 -0
  212. package/lib/oracle/policy.mjs +80 -0
  213. package/lib/oracle/read-model.mjs +398 -0
  214. package/lib/oracle/reconcile.mjs +191 -0
  215. package/lib/oracle/routing.mjs +89 -0
  216. package/lib/oracle/synthesize.mjs +384 -0
  217. package/lib/oracle/verdicts.mjs +51 -0
  218. package/lib/orchestration/worker.mjs +88 -27
  219. package/lib/orchestration-policy.mjs +50 -0
  220. package/lib/parity.mjs +96 -2
  221. package/lib/playwright-demo.mjs +254 -0
  222. package/lib/project-init-shared.mjs +4 -1
  223. package/lib/prompt-composer.js +7 -3
  224. package/lib/prompt-validation-contract.mjs +24 -0
  225. package/lib/provider-capabilities.js +57 -11
  226. package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
  227. package/lib/providers/contract/adapters/git/index.mjs +115 -0
  228. package/lib/providers/contract/adapters/github/index.mjs +166 -0
  229. package/lib/providers/contract/adapters/jira/index.mjs +187 -0
  230. package/lib/providers/contract/adapters/slack/index.mjs +175 -0
  231. package/lib/providers/contract/contract-tests.mjs +57 -0
  232. package/lib/providers/contract/errors.mjs +48 -0
  233. package/lib/providers/contract/interface.mjs +50 -0
  234. package/lib/providers/contract/registry.mjs +102 -0
  235. package/lib/providers/copilot-auth.mjs +297 -0
  236. package/lib/providers/credential-bootstrap.mjs +178 -0
  237. package/lib/providers/credential-catalog.mjs +46 -0
  238. package/lib/providers/credential-sources.mjs +63 -0
  239. package/lib/providers/creds.mjs +5 -2
  240. package/lib/providers/op-run.mjs +59 -0
  241. package/lib/providers/secret-resolver.mjs +159 -0
  242. package/lib/publish-template.mjs +163 -0
  243. package/lib/publish-tooling.mjs +119 -0
  244. package/lib/publish.mjs +305 -0
  245. package/lib/registry/cli.mjs +82 -0
  246. package/lib/registry/consolidation.mjs +147 -0
  247. package/lib/registry/generate-docs.mjs +75 -0
  248. package/lib/registry/skill-verification.mjs +57 -0
  249. package/lib/registry/surface-map.mjs +76 -0
  250. package/lib/registry/validate.mjs +135 -0
  251. package/lib/resources/budget.mjs +82 -0
  252. package/lib/resources/process-budget.mjs +45 -0
  253. package/lib/rules-delivery.mjs +9 -2
  254. package/lib/rules-read.mjs +26 -0
  255. package/lib/runtime-env.mjs +23 -0
  256. package/lib/runtime-pressure.mjs +51 -7
  257. package/lib/schema-infer.mjs +13 -2
  258. package/lib/server/chat-loop.mjs +622 -0
  259. package/lib/server/demo-preview.mjs +63 -0
  260. package/lib/server/index.mjs +259 -9
  261. package/lib/server/rate-limit.mjs +5 -3
  262. package/lib/server/webhook.mjs +3 -2
  263. package/lib/service-manager.mjs +60 -8
  264. package/lib/setup.mjs +70 -7
  265. package/lib/specialists/prompt-schema.mjs +19 -10
  266. package/lib/specialists/roster.mjs +43 -0
  267. package/lib/specialists/scaffold.mjs +56 -0
  268. package/lib/storage/backend.mjs +6 -6
  269. package/lib/storage/hybrid-query.mjs +7 -4
  270. package/lib/storage/state-source.mjs +5 -5
  271. package/lib/storage/sync.mjs +2 -3
  272. package/lib/telemetry/rule-calls.mjs +52 -0
  273. package/lib/template-registry.mjs +2 -2
  274. package/lib/templates/visual-requirements.mjs +27 -51
  275. package/lib/test-corpus-inventory.mjs +313 -0
  276. package/lib/uninstall/uninstall.mjs +19 -7
  277. package/lib/update.mjs +12 -0
  278. package/lib/upgrade.mjs +14 -0
  279. package/lib/wireframe.mjs +20 -14
  280. package/lib/worker/run.mjs +17 -4
  281. package/lib/worker/trace.mjs +11 -3
  282. package/package.json +28 -14
  283. package/personas/construct.md +2 -2
  284. package/platforms/claude/settings.template.json +36 -0
  285. package/rules/common/patterns.md +1 -1
  286. package/rules/common/release-gates.md +4 -3
  287. package/scripts/sync-specialists.mjs +41 -28
  288. package/skills/devops/data-engineering.md +1 -1
  289. package/skills/docs/adr-workflow.md +1 -0
  290. package/skills/docs/backlog-proposal-workflow.md +1 -0
  291. package/skills/docs/codebase-research-workflow.md +40 -0
  292. package/skills/docs/customer-profile-workflow.md +1 -0
  293. package/skills/docs/document-ingest-workflow.md +1 -0
  294. package/skills/docs/evidence-ingest-workflow.md +1 -0
  295. package/skills/docs/init-docs.md +2 -2
  296. package/skills/docs/init-project.md +7 -2
  297. package/skills/docs/prd-workflow.md +24 -1
  298. package/skills/docs/prfaq-workflow.md +1 -0
  299. package/skills/docs/product-intelligence-workflow.md +1 -0
  300. package/skills/docs/product-signal-workflow.md +1 -0
  301. package/skills/docs/research-workflow.md +54 -37
  302. package/skills/docs/runbook-workflow.md +1 -0
  303. package/skills/docs/strategy-workflow.md +1 -0
  304. package/skills/docs/user-research-workflow.md +40 -0
  305. package/skills/operating/orchestration-reference.md +1 -1
  306. package/skills/roles/architect.md +5 -0
  307. package/skills/roles/operator.docs.md +4 -0
  308. package/skills/routing.md +4 -2
  309. package/specialists/artifact-manifest.json +489 -0
  310. package/specialists/artifact-manifest.schema.json +83 -0
  311. package/specialists/audit-enrichments.json +454 -0
  312. package/specialists/contracts.json +37 -25
  313. package/specialists/prompts/_shared/validation-contract.md +26 -0
  314. package/specialists/prompts/cx-accessibility.md +21 -2
  315. package/specialists/prompts/cx-ai-engineer.md +24 -1
  316. package/specialists/prompts/cx-architect.md +4 -1
  317. package/specialists/prompts/cx-business-strategist.md +23 -2
  318. package/specialists/prompts/cx-data-analyst.md +22 -1
  319. package/specialists/prompts/cx-data-engineer.md +23 -2
  320. package/specialists/prompts/cx-debugger.md +21 -2
  321. package/specialists/prompts/cx-designer.md +26 -3
  322. package/specialists/prompts/cx-devil-advocate.md +19 -2
  323. package/specialists/prompts/cx-docs-keeper.md +28 -1
  324. package/specialists/prompts/cx-engineer.md +22 -1
  325. package/specialists/prompts/cx-evaluator.md +18 -1
  326. package/specialists/prompts/cx-explorer.md +21 -2
  327. package/specialists/prompts/cx-legal-compliance.md +21 -2
  328. package/specialists/prompts/cx-operations.md +21 -2
  329. package/specialists/prompts/cx-oracle.md +94 -0
  330. package/specialists/prompts/cx-orchestrator.md +20 -1
  331. package/specialists/prompts/cx-platform-engineer.md +25 -2
  332. package/specialists/prompts/cx-product-manager.md +32 -1
  333. package/specialists/prompts/cx-qa.md +24 -2
  334. package/specialists/prompts/cx-rd-lead.md +23 -2
  335. package/specialists/prompts/cx-release-manager.md +23 -2
  336. package/specialists/prompts/cx-researcher.md +22 -2
  337. package/specialists/prompts/cx-reviewer.md +18 -1
  338. package/specialists/prompts/cx-security.md +22 -2
  339. package/specialists/prompts/cx-sre.md +30 -3
  340. package/specialists/prompts/cx-test-automation.md +7 -1
  341. package/specialists/prompts/cx-trace-reviewer.md +22 -3
  342. package/specialists/prompts/cx-ux-researcher.md +21 -2
  343. package/specialists/registry.json +52 -173
  344. package/specialists/tone-profiles.json +42 -0
  345. package/templates/demos/playwright/demo-recording.config.mjs +47 -0
  346. package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
  347. package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
  348. package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
  349. package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
  350. package/templates/demos/tapes/resource-guard-rails.tape +49 -0
  351. package/templates/demos/vhs/construct-cockpit.json +24 -0
  352. package/templates/distribution/construct-brand.typ +446 -0
  353. package/templates/distribution/construct-decision.typ +38 -0
  354. package/templates/distribution/construct-deck.html +95 -0
  355. package/templates/distribution/construct-pdf.typ +38 -0
  356. package/templates/distribution/construct-prd.typ +38 -0
  357. package/templates/distribution/construct-reference.docx +0 -0
  358. package/templates/distribution/construct-research.typ +38 -0
  359. package/templates/distribution/construct-web.html +92 -0
  360. package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
  361. package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
  362. package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
  363. package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
  364. package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
  365. package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
  366. package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
  367. package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
  368. package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
  369. package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
  370. package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  371. package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
  372. package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
  373. package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
  374. package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
  375. package/templates/distribution/fonts/README.md +36 -0
  376. package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
  377. package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
  378. package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
  379. package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
  380. package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
  381. package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
  382. package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
  383. package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
  384. package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
  385. package/templates/distribution/icons/activity.svg +15 -0
  386. package/templates/distribution/icons/alert-triangle.svg +17 -0
  387. package/templates/distribution/icons/book-open.svg +16 -0
  388. package/templates/distribution/icons/bot.svg +20 -0
  389. package/templates/distribution/icons/brain.svg +22 -0
  390. package/templates/distribution/icons/circle-check.svg +16 -0
  391. package/templates/distribution/icons/clipboard-check.svg +17 -0
  392. package/templates/distribution/icons/cpu.svg +28 -0
  393. package/templates/distribution/icons/database.svg +17 -0
  394. package/templates/distribution/icons/eye.svg +16 -0
  395. package/templates/distribution/icons/file-text.svg +19 -0
  396. package/templates/distribution/icons/gauge.svg +16 -0
  397. package/templates/distribution/icons/git-branch.svg +17 -0
  398. package/templates/distribution/icons/key.svg +17 -0
  399. package/templates/distribution/icons/layers.svg +17 -0
  400. package/templates/distribution/icons/list-checks.svg +19 -0
  401. package/templates/distribution/icons/lock.svg +16 -0
  402. package/templates/distribution/icons/message-square.svg +15 -0
  403. package/templates/distribution/icons/network.svg +19 -0
  404. package/templates/distribution/icons/route.svg +17 -0
  405. package/templates/distribution/icons/scale.svg +19 -0
  406. package/templates/distribution/icons/search.svg +16 -0
  407. package/templates/distribution/icons/send.svg +16 -0
  408. package/templates/distribution/icons/server.svg +18 -0
  409. package/templates/distribution/icons/shield-check.svg +16 -0
  410. package/templates/distribution/icons/users.svg +18 -0
  411. package/templates/distribution/icons/webhook.svg +17 -0
  412. package/templates/distribution/icons/workflow.svg +17 -0
  413. package/templates/distribution/icons/wrench.svg +15 -0
  414. package/templates/distribution/run.mjs +18 -1
  415. package/templates/docs/adr.md +7 -0
  416. package/templates/docs/construct_guide.md +8 -8
  417. package/templates/docs/customer-profile.md +4 -0
  418. package/templates/docs/one-pager.md +4 -0
  419. package/templates/docs/postmortem.md +31 -0
  420. package/templates/docs/prd-platform.md +19 -0
  421. package/templates/docs/prd.md +15 -0
  422. package/templates/docs/prfaq.md +7 -0
  423. package/templates/docs/qa-strategy.md +103 -0
  424. package/templates/docs/research-brief.md +8 -0
  425. package/templates/docs/rfc-platform.md +12 -0
  426. package/templates/docs/test-plan.md +7 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * lib/oracle/actions.mjs — Oracle tick executor with bounded-auto policy.
3
+ *
4
+ * Auto actions: alignment census spawn, registry validate import, adapters
5
+ * sync (Construct tool repo only). Approve actions queue to
6
+ * <project>/.cx/oracle/pending.jsonl. High-severity gaps auto-raise beads
7
+ * when enabled. Denied actions are skipped.
8
+ */
9
+
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { spawnSync } from 'node:child_process';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ import { collectReadModel, enrichReadModel } from './read-model.mjs';
16
+ import { synthesizeVerdict } from './synthesize.mjs';
17
+ import { classifyAction, autoRaiseEnabled, autoRaiseEnabledForGap } from './policy.mjs';
18
+ import { isConstructPackageRepo } from '../host-disposition.mjs';
19
+ import { syncProjectAdapters } from '../adapters-sync.mjs';
20
+ import { writeVerdict } from './verdicts.mjs';
21
+ import { raiseIssuesForGaps } from './issues.mjs';
22
+ import { maybeWriteHighSeverityRouting } from './dispatch.mjs';
23
+ import { executeApprovedAction } from './execute.mjs';
24
+ import { signOffMetadata } from './routing.mjs';
25
+
26
+ function pendingPath(projectDir) {
27
+ return path.join(projectDir, '.cx', 'oracle', 'pending.jsonl');
28
+ }
29
+
30
+ function ensureOracleDir(projectDir) {
31
+ const dir = path.join(projectDir, '.cx', 'oracle');
32
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
33
+ return dir;
34
+ }
35
+
36
+ function appendPending(projectDir, record) {
37
+ ensureOracleDir(projectDir);
38
+ const line = JSON.stringify({ ...record, queuedAt: new Date().toISOString() }) + '\n';
39
+ fs.appendFileSync(pendingPath(projectDir), line, 'utf8');
40
+ }
41
+
42
+ export function listPending(projectDir) {
43
+ const file = pendingPath(projectDir);
44
+ if (!fs.existsSync(file)) return [];
45
+ return fs.readFileSync(file, 'utf8')
46
+ .split('\n')
47
+ .filter(Boolean)
48
+ .map((line) => { try { return JSON.parse(line); } catch { return null; } })
49
+ .filter(Boolean);
50
+ }
51
+
52
+ function rewritePending(projectDir, pending) {
53
+ const file = pendingPath(projectDir);
54
+ fs.writeFileSync(file, pending.map((p) => JSON.stringify(p)).join('\n') + (pending.length ? '\n' : ''), 'utf8');
55
+ }
56
+
57
+ export async function approvePending(projectDir, id, { execute = true, rootDir, homeDir, dryRun = false } = {}) {
58
+ const pending = listPending(projectDir);
59
+ const match = pending.find((p) => p.id === id);
60
+ if (!match) return { ok: false, reason: 'not-found' };
61
+ if (match.status === 'approved' && match.executedAt) {
62
+ return { ok: true, already: true, action: match };
63
+ }
64
+ match.status = 'approved';
65
+ match.approvedAt = new Date().toISOString();
66
+ match.signOff = match.signOff ?? signOffMetadata({ id: match.kind }, projectDir);
67
+
68
+ let executionResult = null;
69
+ if (execute) {
70
+ try {
71
+ executionResult = await executeApprovedAction(match, {
72
+ rootDir: rootDir ?? defaultRootDir(),
73
+ projectDir,
74
+ homeDir,
75
+ dryRun,
76
+ });
77
+ match.executedAt = new Date().toISOString();
78
+ match.executionResult = executionResult;
79
+ if (executionResult?.gateway?.bdIssueId) match.beadId = executionResult.gateway.bdIssueId;
80
+ } catch (err) {
81
+ match.executionResult = { ok: false, error: err?.message || String(err) };
82
+ }
83
+ }
84
+
85
+ rewritePending(projectDir, pending);
86
+ return { ok: true, action: match, executionResult };
87
+ }
88
+
89
+ async function executeAutoAction(kind, { rootDir, projectDir, dryRun }) {
90
+ if (dryRun) return { kind, dryRun: true, ok: true };
91
+
92
+ switch (kind) {
93
+ case 'census-run': {
94
+ const script = path.join(rootDir, 'scripts', 'alignment', 'census.mjs');
95
+ if (!fs.existsSync(script)) return { kind, ok: false, error: 'census script missing' };
96
+ const result = spawnSync(process.execPath, [script], { cwd: rootDir, encoding: 'utf8' });
97
+ return {
98
+ kind,
99
+ ok: result.status === 0,
100
+ exitCode: result.status,
101
+ stderr: (result.stderr || '').slice(0, 500),
102
+ };
103
+ }
104
+ case 'registry-validate': {
105
+ const { validateCapabilityRegistry } = await import('../registry/validate.mjs');
106
+ const report = validateCapabilityRegistry({ rootDir });
107
+ return { kind, ok: report.valid, errors: report.errors?.length ?? 0, warnings: report.warnings?.length ?? 0 };
108
+ }
109
+ case 'adapters-sync': {
110
+ if (!isConstructPackageRepo(projectDir)) {
111
+ return { kind, ok: true, skipped: true, reason: 'not-tool-repo' };
112
+ }
113
+ const result = syncProjectAdapters({ projectRoot: projectDir, packageRoot: rootDir, log: () => {} });
114
+ return { kind, ok: !!result.synced, hosts: result.hosts ?? [] };
115
+ }
116
+ default:
117
+ return { kind, ok: false, error: 'unknown-auto-action' };
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Run one Oracle tick: collect signals, synthesize verdict, execute auto
123
+ * actions, queue approve actions, raise beads for high gaps.
124
+ */
125
+ export async function runOracleTick({ rootDir, projectDir, homeDir, dryRun = false } = {}) {
126
+ let readModel = collectReadModel({ rootDir, projectDir, homeDir });
127
+ readModel = await enrichReadModel(readModel);
128
+ const synthesis = synthesizeVerdict(readModel);
129
+
130
+ const executed = [];
131
+ const queued = [];
132
+ const skipped = [];
133
+ let beadsRaised = [];
134
+
135
+ if (autoRaiseEnabled() && synthesis.gaps.some((g) => autoRaiseEnabledForGap(g))) {
136
+ beadsRaised = await raiseIssuesForGaps({ projectDir, gaps: synthesis.gaps, dryRun });
137
+ }
138
+
139
+ for (const rec of synthesis.recommendedActions) {
140
+ const classification = classifyAction(rec.kind);
141
+ if (classification === 'deny') {
142
+ skipped.push({ ...rec, classification });
143
+ continue;
144
+ }
145
+ if (classification === 'auto') {
146
+ const result = await executeAutoAction(rec.kind, { rootDir, projectDir, dryRun });
147
+ executed.push({ ...rec, classification, result });
148
+ continue;
149
+ }
150
+ const id = `oracle-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
151
+ const pending = {
152
+ id,
153
+ kind: rec.kind,
154
+ summary: rec.summary,
155
+ classification,
156
+ status: 'pending',
157
+ signOff: rec.signOff ?? signOffMetadata({ id: rec.kind }, projectDir),
158
+ context: { ...rec },
159
+ };
160
+ if (!dryRun) appendPending(projectDir, pending);
161
+ queued.push(pending);
162
+ }
163
+
164
+ const tick = {
165
+ at: new Date().toISOString(),
166
+ dryRun,
167
+ verdict: synthesis.verdict,
168
+ gaps: synthesis.gaps,
169
+ executed,
170
+ queued,
171
+ skipped,
172
+ beadsRaised,
173
+ };
174
+
175
+ if (!dryRun) {
176
+ writeVerdict(projectDir, tick, { beadsRaised, orgGraph: readModel.orgGraph });
177
+ maybeWriteHighSeverityRouting({ projectDir, tick, synthesis, readModel });
178
+ }
179
+
180
+ return { readModel, ...synthesis, tick };
181
+ }
182
+
183
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
184
+
185
+ export function defaultRootDir() {
186
+ return path.resolve(MODULE_DIR, '../..');
187
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * lib/oracle/artifact-gate.mjs — Oracle read-model signals for typed artifact gates.
3
+ *
4
+ * Surfaces manifest audit drift, release-gate bypass markers, and high-risk
5
+ * artifacts missing required reviewers in the agent log.
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+
11
+ import { auditSpecialists } from '../audit-specialists.mjs';
12
+ import { inferArtifactTypeFromPath, isArtifactGatePath } from '../artifact-type-from-path.mjs';
13
+ import {
14
+ missingRequiredReviewers,
15
+ parseReleaseGateFrontmatter,
16
+ } from '../artifact-reviewers.mjs';
17
+ import { isConstructPackageRepo } from '../host-disposition.mjs';
18
+
19
+ function walkMarkdown(dir, out = []) {
20
+ if (!fs.existsSync(dir)) return out;
21
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
22
+ const full = path.join(dir, entry.name);
23
+ if (entry.isDirectory()) walkMarkdown(full, out);
24
+ else if (entry.name.endsWith('.md')) out.push(full);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function collectBypassArtifacts(projectDir) {
30
+ const roots = [
31
+ path.join(projectDir, 'docs'),
32
+ path.join(projectDir, '.cx', 'research'),
33
+ ];
34
+ const bypassed = [];
35
+ for (const root of roots) {
36
+ for (const file of walkMarkdown(root)) {
37
+ const rel = path.relative(projectDir, file).replace(/\\/g, '/');
38
+ if (!isArtifactGatePath(rel)) continue;
39
+ const { bypass, reason } = parseReleaseGateFrontmatter(file);
40
+ if (!bypass) continue;
41
+ bypassed.push({
42
+ path: rel,
43
+ type: inferArtifactTypeFromPath(file, { rootDir: projectDir }),
44
+ reason: reason || null,
45
+ });
46
+ }
47
+ }
48
+ return bypassed;
49
+ }
50
+
51
+ function collectReviewerGaps(projectDir) {
52
+ const roots = [
53
+ path.join(projectDir, 'docs', 'prd'),
54
+ path.join(projectDir, 'docs', 'adr'),
55
+ path.join(projectDir, 'docs', 'rfc'),
56
+ ];
57
+ const gaps = [];
58
+ for (const root of roots) {
59
+ if (!fs.existsSync(root)) continue;
60
+ for (const file of walkMarkdown(root)) {
61
+ const rel = path.relative(projectDir, file).replace(/\\/g, '/');
62
+ const type = inferArtifactTypeFromPath(file, { rootDir: projectDir });
63
+ if (!type) continue;
64
+ const missing = missingRequiredReviewers({ filePath: file, cwd: projectDir });
65
+ if (missing.length > 0) {
66
+ gaps.push({ path: rel, type, missingReviewers: missing });
67
+ }
68
+ }
69
+ }
70
+ return gaps.slice(0, 20);
71
+ }
72
+
73
+ export function collectArtifactGateSignals({ rootDir, projectDir }) {
74
+ const bypassed = collectBypassArtifacts(projectDir);
75
+ const reviewerGaps = collectReviewerGaps(projectDir);
76
+
77
+ let specialistAudit = { present: false, pass: true, crossCheckCount: 0 };
78
+ if (isConstructPackageRepo(projectDir)) {
79
+ try {
80
+ const audit = auditSpecialists({ rootDir, silent: true });
81
+ specialistAudit = {
82
+ present: true,
83
+ pass: audit.pass,
84
+ crossCheckCount: audit.crossCheckIssues?.length ?? 0,
85
+ crossCheckIssues: (audit.crossCheckIssues ?? []).slice(0, 5),
86
+ };
87
+ } catch {
88
+ specialistAudit = { present: true, pass: false, crossCheckCount: -1 };
89
+ }
90
+ }
91
+
92
+ return {
93
+ bypassed,
94
+ bypassCount: bypassed.length,
95
+ reviewerGaps,
96
+ reviewerGapCount: reviewerGaps.length,
97
+ specialistAudit,
98
+ };
99
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * lib/oracle/cli.mjs — CLI handlers for `construct oracle <subcommand>`.
3
+ *
4
+ * Subcommands: start, status, review, pending, approve, gaps, reconcile.
5
+ */
6
+
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import os from 'node:os';
10
+ import { spawn } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ import { readHeartbeat } from '../daemons/contract.mjs';
14
+ import {
15
+ buildOracleDaemon,
16
+ heartbeatPath,
17
+ readLastTick,
18
+ KILLSWITCH_ENV,
19
+ } from './index.mjs';
20
+ import { runOracleTick, listPending, approvePending, defaultRootDir } from './actions.mjs';
21
+ import { collectOracleGaps, formatOracleGapsReport } from './gaps.mjs';
22
+ import { reconcileOracleHygieneBeads } from './reconcile.mjs';
23
+
24
+ const MODULE_FILE = fileURLToPath(import.meta.url);
25
+
26
+ function runtimeDir(homeDir) {
27
+ return path.join(homeDir, '.cx', 'runtime', 'oracle');
28
+ }
29
+
30
+ async function cmdStart(args, { rootDir, projectDir, homeDir }) {
31
+ const hb = heartbeatPath(homeDir);
32
+ const live = readHeartbeat(hb);
33
+ if (live) {
34
+ process.stdout.write(`oracle daemon already running (pid ${live.pid})\n`);
35
+ return 0;
36
+ }
37
+
38
+ if (process.env[KILLSWITCH_ENV] === 'off' || process.env[KILLSWITCH_ENV] === '0') {
39
+ process.stderr.write(`oracle daemon disabled (${KILLSWITCH_ENV}=off)\n`);
40
+ return 1;
41
+ }
42
+
43
+ const foreground = args.includes('--foreground') || args.includes('-f');
44
+ if (foreground) {
45
+ const daemon = buildOracleDaemon({ rootDir, projectDir, homeDir });
46
+ const result = await daemon.run();
47
+ process.stdout.write(`oracle daemon stopped: ${result.reason}\n`);
48
+ return 0;
49
+ }
50
+
51
+ const entry = path.join(path.dirname(MODULE_FILE), 'daemon-entry.mjs');
52
+ const logDir = runtimeDir(homeDir);
53
+ fs.mkdirSync(logDir, { recursive: true });
54
+ const logFile = path.join(logDir, 'oracle-daemon.log');
55
+ const fd = fs.openSync(logFile, 'a');
56
+
57
+ const child = spawn(process.execPath, [entry], {
58
+ detached: true,
59
+ stdio: ['ignore', fd, fd],
60
+ env: {
61
+ ...process.env,
62
+ CONSTRUCT_ORACLE_ROOT: rootDir,
63
+ CONSTRUCT_ORACLE_PROJECT: projectDir,
64
+ },
65
+ cwd: projectDir,
66
+ });
67
+ child.unref();
68
+ process.stdout.write(`oracle daemon started (pid ${child.pid})\n`);
69
+ return 0;
70
+ }
71
+
72
+ function cmdStatus(_args, { homeDir }) {
73
+ const hb = readHeartbeat(heartbeatPath(homeDir));
74
+ const last = readLastTick(homeDir);
75
+ const payload = {
76
+ running: !!hb,
77
+ heartbeat: hb,
78
+ lastTick: last,
79
+ killswitch: process.env[KILLSWITCH_ENV] === 'off' || process.env[KILLSWITCH_ENV] === '0',
80
+ };
81
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
82
+ return 0;
83
+ }
84
+
85
+ async function cmdReview(args, { rootDir, projectDir, homeDir }) {
86
+ const dryRun = args.includes('--dry-run');
87
+ const json = args.includes('--json');
88
+ const result = await runOracleTick({ rootDir, projectDir, homeDir, dryRun });
89
+ if (json) {
90
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
91
+ return 0;
92
+ }
93
+ process.stdout.write(`Oracle verdict: ${result.verdict}\n`);
94
+ if (result.gaps.length) {
95
+ process.stdout.write(`Gaps (${result.gaps.length}):\n`);
96
+ for (const g of result.gaps) {
97
+ process.stdout.write(` [${g.severity}] ${g.id}: ${g.detail}\n`);
98
+ }
99
+ }
100
+ if (result.tick.executed.length) {
101
+ process.stdout.write(`Auto-executed (${result.tick.executed.length}):\n`);
102
+ for (const e of result.tick.executed) {
103
+ process.stdout.write(` ${e.kind}: ${e.result?.ok ? 'ok' : 'failed'}\n`);
104
+ }
105
+ }
106
+ if (result.tick.queued.length) {
107
+ process.stdout.write(`Queued for approval (${result.tick.queued.length}):\n`);
108
+ for (const q of result.tick.queued) {
109
+ process.stdout.write(` ${q.id} ${q.kind}: ${q.summary}\n`);
110
+ }
111
+ }
112
+ return 0;
113
+ }
114
+
115
+ function cmdPending(_args, { projectDir }) {
116
+ const pending = listPending(projectDir).filter((p) => p.status !== 'approved');
117
+ process.stdout.write(JSON.stringify(pending, null, 2) + '\n');
118
+ return 0;
119
+ }
120
+
121
+ function cmdApprove(args, { projectDir, rootDir, homeDir }) {
122
+ const id = args.find((a) => !a.startsWith('-'));
123
+ if (!id) {
124
+ process.stderr.write('Usage: construct oracle approve <id> [--no-execute]\n');
125
+ return 1;
126
+ }
127
+ const noExecute = args.includes('--no-execute');
128
+ return approvePending(projectDir, id, {
129
+ execute: !noExecute,
130
+ rootDir,
131
+ homeDir,
132
+ }).then((result) => {
133
+ if (!result.ok) {
134
+ process.stderr.write(`approve failed: ${result.reason}\n`);
135
+ return 1;
136
+ }
137
+ process.stdout.write(JSON.stringify(result.action, null, 2) + '\n');
138
+ return 0;
139
+ });
140
+ }
141
+
142
+ function cmdGaps(args, { projectDir }) {
143
+ const json = args.includes('--json');
144
+ const data = collectOracleGaps(projectDir);
145
+ if (json) {
146
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
147
+ return 0;
148
+ }
149
+ process.stdout.write(formatOracleGapsReport(data));
150
+ return 0;
151
+ }
152
+
153
+ async function cmdReconcile(args, { projectDir }) {
154
+ const dryRun = args.includes('--dry-run');
155
+ const json = args.includes('--json');
156
+ const result = await reconcileOracleHygieneBeads({ projectDir, dryRun });
157
+ if (json) {
158
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
159
+ return 0;
160
+ }
161
+ process.stdout.write(`Oracle reconcile${dryRun ? ' (dry-run)' : ''}: found ${result.found} hygiene beads\n`);
162
+ if (Object.keys(result.kept).length) {
163
+ process.stdout.write(`Keeping newest per gap: ${JSON.stringify(result.kept)}\n`);
164
+ }
165
+ process.stdout.write(`Planned close: ${result.plannedClose.length}\n`);
166
+ if (!dryRun) process.stdout.write(`Closed: ${result.closed.length}\n`);
167
+ if (result.raisedPruned) process.stdout.write(`Pruned raised-issues rows: ${result.raisedPruned}\n`);
168
+ if (result.contractViolations?.superseded) {
169
+ process.stdout.write(`Superseded ${result.contractViolations.recentCount} bare-goal contract violation(s)\n`);
170
+ } else if (result.contractViolations?.wouldSupersede) {
171
+ process.stdout.write(`Would supersede ${result.contractViolations.recentCount} bare-goal contract violation(s)\n`);
172
+ }
173
+ return 0;
174
+ }
175
+
176
+ /**
177
+ * Entry point for bin/construct.
178
+ */
179
+ export async function runOracleCli(args, opts = {}) {
180
+ const sub = args[0];
181
+ const subArgs = args.slice(1);
182
+ const homeDir = opts.homeDir ?? os.homedir();
183
+ const rootDir = opts.rootDir ?? defaultRootDir();
184
+ const projectDir = opts.projectDir ?? process.cwd();
185
+
186
+ switch (sub) {
187
+ case 'start': return cmdStart(subArgs, { rootDir, projectDir, homeDir });
188
+ case 'status': return cmdStatus(subArgs, { homeDir });
189
+ case 'review': return cmdReview(subArgs, { rootDir, projectDir, homeDir });
190
+ case 'pending': return cmdPending(subArgs, { projectDir });
191
+ case 'approve': return cmdApprove(subArgs, { projectDir, rootDir, homeDir });
192
+ case 'gaps': return cmdGaps(subArgs, { projectDir });
193
+ case 'reconcile': return cmdReconcile(subArgs, { projectDir });
194
+ default:
195
+ process.stderr.write(
196
+ 'Usage: construct oracle <start|status|review|pending|approve|gaps|reconcile> [--json] [--dry-run] [--foreground] [--no-execute]\n',
197
+ );
198
+ if (sub && sub !== '--help' && sub !== '-h') {
199
+ process.stderr.write(`Unknown subcommand: ${sub}\n`);
200
+ return 1;
201
+ }
202
+ return sub === '--help' || sub === '-h' ? 0 : 1;
203
+ }
204
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * lib/oracle/daemon-entry.mjs — detached entrypoint for `construct oracle start`.
3
+ *
4
+ * Spawns the Oracle daemon in the background. Reads root/project overrides
5
+ * from CONSTRUCT_ORACLE_ROOT and CONSTRUCT_ORACLE_PROJECT.
6
+ */
7
+
8
+ import { runOracleDaemon } from './index.mjs';
9
+ import { defaultRootDir } from './actions.mjs';
10
+
11
+ const rootDir = process.env.CONSTRUCT_ORACLE_ROOT || defaultRootDir();
12
+ const projectDir = process.env.CONSTRUCT_ORACLE_PROJECT || process.cwd();
13
+
14
+ await runOracleDaemon({ rootDir, projectDir });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * lib/oracle/dispatch.mjs — cx-oracle routing artifacts (deterministic, LLM-free).
3
+ *
4
+ * Writes audit markdown under .cx/oracle/routing/ for approve-gated dispatch.
5
+ * Optional high-severity auto-artifact on tick when gaps are load-bearing.
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+
11
+ import { routeGap, routeAction } from './routing.mjs';
12
+
13
+ export function routingDir(projectDir) {
14
+ return path.join(projectDir, '.cx', 'oracle', 'routing');
15
+ }
16
+
17
+ /**
18
+ * Build deterministic routing markdown from synthesis packet.
19
+ */
20
+ export function buildRoutingArtifact({ synthesis, readModel, tickId, route }) {
21
+ const lines = [
22
+ `# ORACLE ROUTING — ${tickId}`,
23
+ '',
24
+ `VERDICT: ${synthesis.verdict ?? 'unknown'}`,
25
+ '',
26
+ 'GAPS:',
27
+ ];
28
+
29
+ for (const gap of synthesis.gaps ?? []) {
30
+ const r = routeGap(gap);
31
+ lines.push(` [${gap.severity}] ${gap.id} — ${gap.detail}`);
32
+ lines.push(` signal: ${gap.signal ?? 'unknown'}`);
33
+ lines.push(` route: ${r.primary}${r.secondary ? ` (secondary: ${r.secondary})` : ''}`);
34
+ if (r.gateType) lines.push(` gate: ${r.gateType}`);
35
+ }
36
+
37
+ if (!synthesis.gaps?.length) lines.push(' (none)');
38
+
39
+ lines.push('', 'RECOMMENDED ACTIONS:');
40
+ for (const action of synthesis.recommendedActions ?? []) {
41
+ const r = routeAction(action.kind);
42
+ lines.push(` ${action.kind}: ${action.summary}`);
43
+ lines.push(` route: ${r.primary} · gate: ${r.gateType ?? 'none'}`);
44
+ }
45
+ if (!synthesis.recommendedActions?.length) lines.push(' (none)');
46
+
47
+ if (readModel?.collectedAt) {
48
+ lines.push('', `Read model collected: ${readModel.collectedAt}`);
49
+ }
50
+ if (route?.primary) {
51
+ lines.push('', `DISPATCH TARGET: ${route.primary}`);
52
+ }
53
+
54
+ lines.push('', 'BLOCKED:', ' none (deterministic routing artifact — LLM dispatch is approve-gated)');
55
+ return lines.join('\n');
56
+ }
57
+
58
+ /**
59
+ * @returns {string} artifact path relative to project root
60
+ */
61
+ export function writeRoutingArtifact({ projectDir, tickId, synthesis, readModel, route }) {
62
+ const dir = routingDir(projectDir);
63
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
64
+ const fileName = `${tickId.replace(/[^a-zA-Z0-9._-]/g, '_')}.md`;
65
+ const file = path.join(dir, fileName);
66
+ const content = buildRoutingArtifact({ synthesis, readModel, tickId, route });
67
+ fs.writeFileSync(file, content, 'utf8');
68
+ return path.relative(projectDir, file);
69
+ }
70
+
71
+ /**
72
+ * Write routing artifact for high-severity tick when any HIGH gap exists.
73
+ */
74
+ export function maybeWriteHighSeverityRouting({ projectDir, tick, synthesis, readModel }) {
75
+ const hasHigh = (synthesis.gaps ?? []).some((g) => g.severity === 'high');
76
+ if (!hasHigh) return null;
77
+ const tickId = tick.at ?? new Date().toISOString();
78
+ const primaryGap = synthesis.gaps.find((g) => g.severity === 'high');
79
+ const route = routeGap(primaryGap);
80
+ return writeRoutingArtifact({ projectDir, tickId, synthesis, readModel, route });
81
+ }