@geraldmaron/construct 1.4.2 → 1.5.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 (519) hide show
  1. package/.env.example +36 -0
  2. package/README.md +41 -7
  3. package/bin/construct +1027 -64
  4. package/examples/distribution/sources/deck-one-pager.md +1 -1
  5. package/lib/acp/server.mjs +21 -7
  6. package/lib/adapters-sync.mjs +2 -1
  7. package/lib/audit-trail.mjs +185 -2
  8. package/lib/beads/auto-close.mjs +2 -1
  9. package/lib/beads/drift.mjs +2 -1
  10. package/lib/bridges/copilot-proxy.mjs +20 -5
  11. package/lib/certification/skill-inventory.mjs +10 -1
  12. package/lib/cli/approvals.mjs +147 -0
  13. package/lib/cli-commands.mjs +105 -14
  14. package/lib/comment-lint.mjs +127 -8
  15. package/lib/config/schema.mjs +6 -29
  16. package/lib/config/source-target-registry.mjs +70 -0
  17. package/lib/config/source-targets.mjs +179 -205
  18. package/lib/contracts/coverage.mjs +77 -0
  19. package/lib/contracts/validate.mjs +102 -13
  20. package/lib/contracts/violation-log.mjs +50 -4
  21. package/lib/db/migrate.mjs +69 -0
  22. package/lib/db/migrations/001_orchestration_runs.sql +9 -0
  23. package/lib/db/migrations/002_queue_provider.sql +50 -0
  24. package/lib/db/migrations/003_worker_registry.sql +17 -0
  25. package/lib/db/migrations/004_trace_events.sql +16 -0
  26. package/lib/db/migrations/005_shared_memory.sql +15 -0
  27. package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
  28. package/lib/decisions/enforced-baseline.json +0 -2
  29. package/lib/decisions/registry.mjs +3 -2
  30. package/lib/deployment/parity-contract.mjs +1 -1
  31. package/lib/deployment-mode.mjs +78 -2
  32. package/lib/diagram-export.mjs +10 -1
  33. package/lib/distill.mjs +5 -2
  34. package/lib/docs-verify.mjs +2 -1
  35. package/lib/doctor/cli.mjs +1 -0
  36. package/lib/doctor/command-on-path.mjs +24 -0
  37. package/lib/doctor/diagnosis.mjs +89 -0
  38. package/lib/doctor/embedding-health.mjs +50 -0
  39. package/lib/doctor/engine-health.mjs +93 -0
  40. package/lib/doctor/graph-validate.mjs +47 -0
  41. package/lib/doctor/index.mjs +18 -4
  42. package/lib/doctor/sidecar-providers.mjs +56 -0
  43. package/lib/doctor/watchers/consistency.mjs +93 -1
  44. package/lib/doctor/watchers/cx-budget.mjs +1 -1
  45. package/lib/doctor/watchers/graph-staleness.mjs +1 -1
  46. package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
  47. package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
  48. package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
  49. package/lib/doctor/watchers/provider-breaker.mjs +78 -0
  50. package/lib/document-export.mjs +52 -27
  51. package/lib/document-extract/docling-client.mjs +9 -5
  52. package/lib/document-extract/docling-sidecar.py +30 -0
  53. package/lib/document-extract.mjs +1 -1
  54. package/lib/document-ingest.mjs +9 -0
  55. package/lib/embed/approval-queue.mjs +184 -88
  56. package/lib/embed/authority-guard.mjs +65 -2
  57. package/lib/embed/capability-jobs.mjs +365 -0
  58. package/lib/embed/capability-lifecycle.mjs +219 -0
  59. package/lib/embed/capability-loader.mjs +303 -0
  60. package/lib/embed/capability-runtime.mjs +58 -0
  61. package/lib/embed/cli.mjs +185 -8
  62. package/lib/embed/config.mjs +4 -0
  63. package/lib/embed/daemon.mjs +125 -6
  64. package/lib/embed/demand-fetch.mjs +190 -54
  65. package/lib/embed/inbox.mjs +12 -10
  66. package/lib/embed/presets/ops-triage.mjs +236 -0
  67. package/lib/embed/presets/pm-feedback.mjs +341 -0
  68. package/lib/embed/presets/tpm.mjs +401 -0
  69. package/lib/embed/providers/jira.mjs +72 -1
  70. package/lib/embed/providers/registry.mjs +140 -33
  71. package/lib/embedded-contract/capability.mjs +4 -0
  72. package/lib/embedded-contract/execution.mjs +1 -1
  73. package/lib/embedded-contract/model-resolve.mjs +53 -3
  74. package/lib/embedded-contract/workflow-defs.mjs +48 -73
  75. package/lib/embedded-contract/workflow-invoke.mjs +144 -4
  76. package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
  77. package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
  78. package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
  79. package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
  80. package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
  81. package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
  82. package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
  83. package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
  84. package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
  85. package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
  86. package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
  87. package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
  88. package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
  89. package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
  90. package/lib/env-config.mjs +50 -9
  91. package/lib/extensions/index.mjs +27 -0
  92. package/lib/extensions/loader.mjs +120 -0
  93. package/lib/extensions/manifest-schema.mjs +141 -0
  94. package/lib/extensions/manifests/anthropic.manifest.json +12 -0
  95. package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
  96. package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
  97. package/lib/extensions/manifests/directory.manifest.json +12 -0
  98. package/lib/extensions/manifests/docling.manifest.json +37 -0
  99. package/lib/extensions/manifests/echo.manifest.json +8 -0
  100. package/lib/extensions/manifests/feedback.manifest.json +12 -0
  101. package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
  102. package/lib/extensions/manifests/github.manifest.json +52 -0
  103. package/lib/extensions/manifests/linear.manifest.json +50 -0
  104. package/lib/extensions/manifests/local.manifest.json +12 -0
  105. package/lib/extensions/manifests/ollama.manifest.json +12 -0
  106. package/lib/extensions/manifests/openai.manifest.json +12 -0
  107. package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
  108. package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
  109. package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
  110. package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
  111. package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
  112. package/lib/extensions/manifests/openrouter.manifest.json +12 -0
  113. package/lib/extensions/manifests/postgres.manifest.json +12 -0
  114. package/lib/extensions/manifests/salesforce.manifest.json +12 -0
  115. package/lib/extensions/manifests/slack.manifest.json +58 -0
  116. package/lib/extensions/manifests/whisper.manifest.json +36 -0
  117. package/lib/extensions/validate.mjs +175 -0
  118. package/lib/features.mjs +13 -9
  119. package/lib/flows/checkpoint.mjs +184 -0
  120. package/lib/flows/constants.mjs +27 -0
  121. package/lib/flows/define.mjs +146 -0
  122. package/lib/flows/engine.mjs +249 -0
  123. package/lib/flows/errors.mjs +19 -0
  124. package/lib/flows/index.mjs +27 -0
  125. package/lib/flows/joins.mjs +18 -0
  126. package/lib/flows/schema.mjs +90 -0
  127. package/lib/flows/state.mjs +31 -0
  128. package/lib/frameworks/loader.mjs +99 -0
  129. package/lib/frameworks/schema.mjs +157 -0
  130. package/lib/graph/build-from-corpus.mjs +67 -0
  131. package/lib/graph/build-from-embed.mjs +82 -0
  132. package/lib/graph/build-from-registry.mjs +164 -3
  133. package/lib/graph/cli.mjs +456 -12
  134. package/lib/graph/gap-queries.mjs +156 -0
  135. package/lib/graph/gaps.mjs +41 -0
  136. package/lib/graph/impacted.mjs +129 -0
  137. package/lib/graph/runtime-evidence.mjs +177 -0
  138. package/lib/graph/security-coverage.mjs +113 -0
  139. package/lib/graph/staleness.mjs +241 -10
  140. package/lib/graph/store.mjs +24 -7
  141. package/lib/graph/validate.mjs +161 -0
  142. package/lib/headhunt.mjs +9 -8
  143. package/lib/health-check.mjs +15 -7
  144. package/lib/hook-health.mjs +1 -1
  145. package/lib/hooks/agent-tracker.mjs +10 -4
  146. package/lib/hooks/edit-guard.mjs +3 -3
  147. package/lib/hooks/graph-impact-advisory.mjs +2 -2
  148. package/lib/hooks/guard-bash.mjs +41 -15
  149. package/lib/hooks/mcp-health-check.mjs +11 -4
  150. package/lib/hooks/model-fallback.mjs +50 -6
  151. package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
  152. package/lib/hooks/pre-compact.mjs +181 -173
  153. package/lib/hooks/rule-verifier.mjs +2 -1
  154. package/lib/hooks/session-reflect.mjs +2 -1
  155. package/lib/hooks/session-start.mjs +3 -3
  156. package/lib/host-capabilities.mjs +13 -2
  157. package/lib/host-disposition.mjs +19 -7
  158. package/lib/identity.mjs +92 -0
  159. package/lib/ingest/degraded-extract.mjs +96 -0
  160. package/lib/ingest/docling-remote.mjs +2 -1
  161. package/lib/ingest/provider-extract.mjs +7 -19
  162. package/lib/ingest/sidecar-providers.mjs +196 -0
  163. package/lib/ingest-tooling.mjs +11 -5
  164. package/lib/init-unified.mjs +57 -45
  165. package/lib/init-update.mjs +2 -1
  166. package/lib/install/legacy-global-cleanup.mjs +25 -0
  167. package/lib/intake/daemon.mjs +99 -8
  168. package/lib/intake/git-queue.mjs +110 -38
  169. package/lib/intake/queue-registry.mjs +50 -0
  170. package/lib/intake/queue.mjs +133 -17
  171. package/lib/intake/session-prelude.mjs +57 -8
  172. package/lib/integrations/intake-integrations.mjs +61 -111
  173. package/lib/intent-classifier.mjs +43 -5
  174. package/lib/libreoffice-export.mjs +8 -8
  175. package/lib/logging/rotate.mjs +5 -4
  176. package/lib/mcp/broker.mjs +332 -17
  177. package/lib/mcp/denied-store.mjs +95 -0
  178. package/lib/mcp/destructive-gate.mjs +30 -0
  179. package/lib/mcp/dispatch-envelope.mjs +199 -0
  180. package/lib/mcp/memory-bridge.mjs +2 -1
  181. package/lib/mcp/server.mjs +154 -1411
  182. package/lib/mcp/tool-definitions-memory.mjs +376 -0
  183. package/lib/mcp/tool-definitions-project.mjs +271 -0
  184. package/lib/mcp/tool-definitions-skills.mjs +311 -0
  185. package/lib/mcp/tool-definitions-workflow.mjs +398 -0
  186. package/lib/mcp/tool-definitions.mjs +25 -0
  187. package/lib/mcp/tool-registry.mjs +107 -0
  188. package/lib/mcp/tool-safety.mjs +1 -0
  189. package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
  190. package/lib/mcp/tools/orchestration-run.mjs +165 -25
  191. package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
  192. package/lib/mcp/tools/provider-write.mjs +187 -0
  193. package/lib/mcp/tools/scope.mjs +0 -3
  194. package/lib/mcp/tools/skills.mjs +1 -1
  195. package/lib/mcp/tools/storage.mjs +0 -8
  196. package/lib/mcp/transport/auth.mjs +238 -0
  197. package/lib/mcp/transport/http.mjs +136 -0
  198. package/lib/mcp/transport/mode.mjs +31 -0
  199. package/lib/mcp/transport/stdio.mjs +22 -0
  200. package/lib/mcp-catalog.json +4 -4
  201. package/lib/mcp-manager.mjs +34 -23
  202. package/lib/mcp-platform-config.mjs +31 -7
  203. package/lib/mode-capabilities.mjs +109 -0
  204. package/lib/model-router.mjs +42 -9
  205. package/lib/models/catalog.mjs +40 -1
  206. package/lib/net-guard.mjs +247 -0
  207. package/lib/observation-store.mjs +6 -2
  208. package/lib/ollama/provision-context.mjs +2 -1
  209. package/lib/opencode-config.mjs +22 -3
  210. package/lib/opencode-runtime-plugin.mjs +120 -2
  211. package/lib/oracle/actions.mjs +204 -10
  212. package/lib/oracle/cli.mjs +24 -3
  213. package/lib/oracle/dispatch.mjs +2 -1
  214. package/lib/oracle/execute.mjs +2 -2
  215. package/lib/oracle/gaps.mjs +3 -3
  216. package/lib/oracle/heartbeat.mjs +53 -0
  217. package/lib/oracle/read-model.mjs +69 -13
  218. package/lib/oracle/reconcile.mjs +3 -46
  219. package/lib/oracle/routing.mjs +37 -31
  220. package/lib/oracle/synthesize.mjs +1 -1
  221. package/lib/orchestration/classification.mjs +434 -0
  222. package/lib/orchestration/delegation-flow.mjs +132 -0
  223. package/lib/orchestration/flow-selection.mjs +418 -0
  224. package/lib/orchestration/gates.mjs +244 -0
  225. package/lib/orchestration/host-sampling.mjs +121 -0
  226. package/lib/orchestration/policy-constants.mjs +48 -0
  227. package/lib/orchestration/provider-outcome.mjs +197 -0
  228. package/lib/orchestration/readiness.mjs +114 -13
  229. package/lib/orchestration/run-store-postgres.mjs +32 -26
  230. package/lib/orchestration/run-store-sqlite.mjs +8 -14
  231. package/lib/orchestration/run-store.mjs +25 -12
  232. package/lib/orchestration/runtime.mjs +446 -39
  233. package/lib/orchestration/store.mjs +87 -12
  234. package/lib/orchestration/trace-store.mjs +125 -0
  235. package/lib/orchestration/web-capability.mjs +3 -2
  236. package/lib/orchestration/worker-runtime.mjs +162 -0
  237. package/lib/orchestration/worker.mjs +528 -89
  238. package/lib/orchestration-policy.mjs +67 -1111
  239. package/lib/packs/cli.mjs +144 -0
  240. package/lib/packs/core-pack.mjs +112 -0
  241. package/lib/packs/enablement.mjs +187 -0
  242. package/lib/packs/index.mjs +23 -0
  243. package/lib/packs/loader.mjs +176 -0
  244. package/lib/packs/manifest-schema.mjs +58 -0
  245. package/lib/packs/prompts.mjs +120 -0
  246. package/lib/packs/validate.mjs +208 -0
  247. package/lib/plugin-registry.mjs +4 -5
  248. package/lib/policy/audit-gate.mjs +42 -0
  249. package/lib/policy/consumption-budget.mjs +149 -0
  250. package/lib/policy/engine.mjs +81 -6
  251. package/lib/policy/role-authority.mjs +89 -0
  252. package/lib/prompt-composer.js +19 -3
  253. package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
  254. package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
  255. package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
  256. package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
  257. package/lib/providers/contract/adapters/github/index.mjs +38 -3
  258. package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
  259. package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
  260. package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
  261. package/lib/providers/contract/adapters/jira/manifest.json +17 -0
  262. package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
  263. package/lib/providers/contract.mjs +207 -0
  264. package/lib/providers/credential-bootstrap.mjs +7 -4
  265. package/lib/providers/credential-sources.mjs +14 -2
  266. package/lib/providers/directory/index.mjs +273 -0
  267. package/lib/providers/feedback/index.mjs +392 -0
  268. package/lib/providers/filter-audit.mjs +68 -0
  269. package/lib/providers/filter-schema.mjs +54 -0
  270. package/lib/providers/github/index.mjs +31 -0
  271. package/lib/providers/instance-config.mjs +215 -0
  272. package/lib/providers/op-locate.mjs +96 -0
  273. package/lib/providers/op-run.mjs +64 -9
  274. package/lib/providers/registry.mjs +26 -3
  275. package/lib/providers/secret-audit-wiring.mjs +6 -0
  276. package/lib/providers/secret-resolver.mjs +240 -23
  277. package/lib/publish-template.mjs +6 -3
  278. package/lib/publish-tooling.mjs +4 -0
  279. package/lib/publish.mjs +32 -4
  280. package/lib/queue/pg-queue.mjs +395 -0
  281. package/lib/reflect.mjs +2 -1
  282. package/lib/registry/assemble.mjs +28 -2
  283. package/lib/registry/consolidation.mjs +8 -1
  284. package/lib/registry/custom-scaffold.mjs +200 -0
  285. package/lib/registry/custom-schema.mjs +137 -0
  286. package/lib/registry/loader.mjs +8 -3
  287. package/lib/registry/manifests/format-engines.default.json +15 -0
  288. package/lib/registry/manifests/surface-map.default.json +46 -0
  289. package/lib/registry/retired-paths.mjs +1 -0
  290. package/lib/registry/surface-map.mjs +100 -50
  291. package/lib/render-visual-check.mjs +240 -0
  292. package/lib/resources/budget.mjs +40 -17
  293. package/lib/roles/flavor-bindings.mjs +36 -14
  294. package/lib/roles/gateway.mjs +15 -8
  295. package/lib/roots.mjs +107 -0
  296. package/lib/runtime/uv-bootstrap.mjs +32 -6
  297. package/lib/runtime/whisper-bootstrap.mjs +11 -3
  298. package/lib/runtime-env.mjs +5 -2
  299. package/lib/scheduler/index.mjs +8 -20
  300. package/lib/schema-infer.mjs +31 -2
  301. package/lib/scopes/lifecycle.mjs +29 -25
  302. package/lib/scopes/loader.mjs +49 -12
  303. package/lib/scopes/teams.mjs +1 -1
  304. package/lib/security/ingest-boundary.mjs +80 -0
  305. package/lib/security/recall-wrapper.mjs +99 -0
  306. package/lib/security/trust.mjs +132 -0
  307. package/lib/service-manager.mjs +38 -19
  308. package/lib/setup.mjs +41 -23
  309. package/lib/skills-apply.mjs +4 -2
  310. package/lib/specialists/postconditions.mjs +2 -2
  311. package/lib/state-root.mjs +147 -0
  312. package/lib/status.mjs +597 -6
  313. package/lib/storage/admin.mjs +77 -5
  314. package/lib/storage/backend-registry.mjs +73 -0
  315. package/lib/storage/backend.mjs +63 -9
  316. package/lib/storage/embeddings-openai.mjs +12 -2
  317. package/lib/storage/hybrid-query.mjs +11 -3
  318. package/lib/storage/retrieval-hardening.mjs +384 -0
  319. package/lib/storage/shared-memory.mjs +158 -0
  320. package/lib/storage/vector-client.mjs +69 -2
  321. package/lib/team/health.mjs +72 -0
  322. package/lib/telemetry/backends/local.mjs +9 -3
  323. package/lib/telemetry/client.mjs +3 -7
  324. package/lib/template-registry.mjs +10 -12
  325. package/lib/tenant/context.mjs +82 -0
  326. package/lib/tenant/isolation.mjs +129 -0
  327. package/lib/test-corpus-inventory.mjs +104 -2
  328. package/lib/uninstall/uninstall.mjs +78 -12
  329. package/lib/validator.mjs +4 -6
  330. package/lib/worker/entrypoint.mjs +2 -1
  331. package/lib/worker/run.mjs +2 -2
  332. package/lib/worker/trace.mjs +5 -11
  333. package/lib/workflow-state.mjs +52 -8
  334. package/lib/workflows/liveness.mjs +203 -0
  335. package/lib/workflows/loader.mjs +177 -0
  336. package/lib/workflows/manifest-schema.mjs +71 -0
  337. package/lib/workflows/surface-parity.mjs +118 -0
  338. package/lib/workflows/validate.mjs +127 -0
  339. package/lib/writes/control-plane.mjs +140 -0
  340. package/lib/writes/envelope.mjs +206 -0
  341. package/lib/writes/sent-log.mjs +86 -0
  342. package/lib/writes/write-intent.mjs +109 -0
  343. package/package.json +16 -8
  344. package/personas/construct.md +12 -3
  345. package/registry/capabilities.json +143 -36
  346. package/rules/common/comments.md +1 -0
  347. package/schemas/project-config.schema.json +0 -12
  348. package/schemas/unified-registry.schema.json +2 -4
  349. package/scripts/sync-specialists.mjs +284 -81
  350. package/skills/docs/adr-workflow.md +1 -1
  351. package/skills/docs/codebase-research-workflow.md +3 -3
  352. package/skills/docs/prd-workflow.md +5 -6
  353. package/skills/docs/runbook-workflow.md +2 -2
  354. package/skills/docs/user-research-workflow.md +3 -3
  355. package/skills/operating/fleet-health-routing.md +77 -0
  356. package/skills/roles/ai-engineer.md +1 -1
  357. package/skills/roles/architect.md +0 -1
  358. package/skills/roles/business-strategist.md +1 -1
  359. package/skills/roles/data-analyst.telemetry.md +1 -1
  360. package/skills/roles/data-engineer.md +1 -1
  361. package/skills/roles/data-engineer.pipeline.md +1 -1
  362. package/skills/roles/data-engineer.vector-retrieval.md +1 -2
  363. package/skills/roles/data-engineer.warehouse.md +1 -1
  364. package/skills/roles/designer.accessibility.md +1 -1
  365. package/skills/roles/designer.md +0 -1
  366. package/skills/roles/devil-advocate.md +1 -1
  367. package/skills/roles/docs-keeper.md +1 -1
  368. package/skills/roles/evaluator.md +1 -1
  369. package/skills/roles/explorer.md +1 -1
  370. package/skills/roles/platform-engineer.md +1 -1
  371. package/skills/roles/qa.ai-eval.md +1 -2
  372. package/skills/roles/qa.api-contract.md +0 -1
  373. package/skills/roles/qa.data-pipeline.md +0 -1
  374. package/skills/roles/qa.md +0 -1
  375. package/skills/roles/qa.web-ui.md +0 -1
  376. package/skills/roles/release-manager.md +1 -1
  377. package/skills/roles/researcher.md +0 -2
  378. package/skills/roles/reviewer.md +0 -3
  379. package/skills/roles/security.ai.md +1 -1
  380. package/skills/roles/security.legal-compliance.md +1 -1
  381. package/skills/roles/security.md +0 -1
  382. package/skills/roles/security.privacy.md +0 -1
  383. package/skills/roles/security.supply-chain.md +1 -1
  384. package/skills/roles/sre.md +1 -1
  385. package/skills/roles/test-automation.md +1 -1
  386. package/skills/roles/trace-reviewer.md +1 -1
  387. package/skills/roles/ux-researcher.md +1 -1
  388. package/specialists/artifact-manifest.json +36 -36
  389. package/specialists/org/contracts/accessibility-to-qa.json +11 -3
  390. package/specialists/org/contracts/any-to-business-strategist.json +19 -7
  391. package/specialists/org/contracts/any-to-debugger.json +7 -1
  392. package/specialists/org/contracts/any-to-designer.json +7 -1
  393. package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
  394. package/specialists/org/contracts/any-to-explorer.json +13 -4
  395. package/specialists/org/contracts/any-to-sre-incident.json +6 -2
  396. package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
  397. package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
  398. package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
  399. package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
  400. package/specialists/org/contracts/architect-to-engineer.json +20 -4
  401. package/specialists/org/contracts/architect-to-evaluator.json +15 -5
  402. package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
  403. package/specialists/org/contracts/architect-to-operations.json +17 -4
  404. package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
  405. package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
  406. package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
  407. package/specialists/org/contracts/engineer-to-qa.json +20 -4
  408. package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
  409. package/specialists/org/contracts/explorer-to-engineer.json +11 -3
  410. package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
  411. package/specialists/org/contracts/operations-to-user.json +53 -0
  412. package/specialists/org/contracts/operations-triage-output.json +53 -0
  413. package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
  414. package/specialists/org/contracts/product-manager-to-architect.json +30 -10
  415. package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
  416. package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
  417. package/specialists/org/contracts/qa-to-release-manager.json +11 -3
  418. package/specialists/org/contracts/researcher-to-architect.json +10 -2
  419. package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
  420. package/specialists/org/contracts/reviewer-to-security.json +17 -3
  421. package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
  422. package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
  423. package/specialists/org/contracts/user-to-construct.json +11 -4
  424. package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
  425. package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
  426. package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
  427. package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
  428. package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
  429. package/specialists/org/groups/engineering-group.json +2 -6
  430. package/specialists/org/groups/governance-group.json +2 -3
  431. package/specialists/org/groups/operations-group.json +5 -8
  432. package/specialists/org/groups/product-group.json +4 -8
  433. package/specialists/org/groups/quality-group.json +3 -7
  434. package/specialists/org/groups/strategy-group.json +6 -9
  435. package/specialists/org/models.json.example +8 -0
  436. package/specialists/org/scopes/creative.json +6 -1
  437. package/specialists/org/scopes/operations.json +7 -1
  438. package/specialists/org/scopes/research.json +6 -1
  439. package/specialists/org/scopes/rnd.json +7 -1
  440. package/specialists/org/specialists/cx-architect.json +27 -8
  441. package/specialists/org/specialists/cx-designer.json +22 -8
  442. package/specialists/org/specialists/cx-engineer.json +71 -7
  443. package/specialists/org/specialists/cx-operations.json +89 -15
  444. package/specialists/org/specialists/cx-orchestrator.json +16 -4
  445. package/specialists/org/specialists/cx-product-manager.json +28 -9
  446. package/specialists/org/specialists/cx-qa.json +16 -6
  447. package/specialists/org/specialists/cx-researcher.json +40 -7
  448. package/specialists/org/specialists/cx-reviewer.json +61 -11
  449. package/specialists/org/specialists/cx-security.json +30 -10
  450. package/specialists/org/teams/design-team.json +3 -4
  451. package/specialists/org/teams/engineering-team.json +3 -10
  452. package/specialists/org/teams/governance-team.json +3 -5
  453. package/specialists/org/teams/operations-team.json +6 -12
  454. package/specialists/org/teams/product-management-team.json +2 -3
  455. package/specialists/org/teams/quality-team.json +4 -12
  456. package/specialists/org/teams/research-team.json +3 -3
  457. package/specialists/org/teams/strategy-team.json +7 -14
  458. package/specialists/prompts/cx-architect.md +20 -1
  459. package/specialists/prompts/cx-data-analyst.md +1 -1
  460. package/specialists/prompts/cx-designer.md +12 -4
  461. package/specialists/prompts/cx-engineer.md +15 -1
  462. package/specialists/prompts/cx-operations.md +14 -2
  463. package/specialists/prompts/cx-orchestrator.md +9 -5
  464. package/specialists/prompts/cx-product-manager.md +5 -1
  465. package/specialists/prompts/cx-qa.md +6 -2
  466. package/specialists/prompts/cx-researcher.md +25 -30
  467. package/specialists/prompts/cx-reviewer.md +19 -1
  468. package/specialists/prompts/cx-security.md +5 -1
  469. package/templates/distribution/construct-brand.typ +123 -59
  470. package/templates/distribution/construct-decision.typ +6 -3
  471. package/templates/distribution/construct-pdf.typ +11 -4
  472. package/templates/distribution/construct-prd.typ +6 -3
  473. package/templates/distribution/construct-research.typ +7 -4
  474. package/lib/providers/contract/adapters/git/index.mjs +0 -115
  475. package/lib/providers/contract/adapters/slack/index.mjs +0 -175
  476. package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
  477. package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
  478. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
  479. package/specialists/org/contracts/designer-to-accessibility.json +0 -36
  480. package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
  481. package/specialists/org/contracts/qa-to-test-automation.json +0 -42
  482. package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
  483. package/specialists/org/contracts/sre-to-release-manager.json +0 -36
  484. package/specialists/org/specialists/cx-accessibility.json +0 -69
  485. package/specialists/org/specialists/cx-ai-engineer.json +0 -75
  486. package/specialists/org/specialists/cx-business-strategist.json +0 -72
  487. package/specialists/org/specialists/cx-data-engineer.json +0 -71
  488. package/specialists/org/specialists/cx-devil-advocate.json +0 -71
  489. package/specialists/org/specialists/cx-docs-keeper.json +0 -82
  490. package/specialists/org/specialists/cx-evaluator.json +0 -69
  491. package/specialists/org/specialists/cx-explorer.json +0 -69
  492. package/specialists/org/specialists/cx-legal-compliance.json +0 -74
  493. package/specialists/org/specialists/cx-oracle.json +0 -46
  494. package/specialists/org/specialists/cx-platform-engineer.json +0 -80
  495. package/specialists/org/specialists/cx-rd-lead.json +0 -73
  496. package/specialists/org/specialists/cx-release-manager.json +0 -77
  497. package/specialists/org/specialists/cx-sre.json +0 -94
  498. package/specialists/org/specialists/cx-test-automation.json +0 -69
  499. package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
  500. package/specialists/org/specialists/cx-ux-researcher.json +0 -68
  501. package/specialists/org/teams/accessibility-team.json +0 -39
  502. package/specialists/org/teams/ux-research-team.json +0 -39
  503. package/specialists/prompts/cx-accessibility.md +0 -55
  504. package/specialists/prompts/cx-ai-engineer.md +0 -124
  505. package/specialists/prompts/cx-business-strategist.md +0 -58
  506. package/specialists/prompts/cx-data-engineer.md +0 -55
  507. package/specialists/prompts/cx-devil-advocate.md +0 -59
  508. package/specialists/prompts/cx-docs-keeper.md +0 -164
  509. package/specialists/prompts/cx-evaluator.md +0 -49
  510. package/specialists/prompts/cx-explorer.md +0 -71
  511. package/specialists/prompts/cx-legal-compliance.md +0 -58
  512. package/specialists/prompts/cx-oracle.md +0 -98
  513. package/specialists/prompts/cx-platform-engineer.md +0 -97
  514. package/specialists/prompts/cx-rd-lead.md +0 -59
  515. package/specialists/prompts/cx-release-manager.md +0 -54
  516. package/specialists/prompts/cx-sre.md +0 -111
  517. package/specialists/prompts/cx-test-automation.md +0 -55
  518. package/specialists/prompts/cx-trace-reviewer.md +0 -101
  519. package/specialists/prompts/cx-ux-researcher.md +0 -53
@@ -1,9 +1,24 @@
1
1
  /**
2
2
  * lib/storage/admin.mjs — storage administration and maintenance.
3
+ *
4
+ * purgeExpiredData() is the TTL/size eviction path for the machine-scoped
5
+ * vector-indexed observations store (ADR-0066: <stateRoot>/lancedb,
6
+ * construct-rf26.17). Checking or pruning never provisions the LanceDB
7
+ * index as a side effect — a project that never ran `construct ingest`
8
+ * has no lancedb/ dir yet, so the call is a no-op rather than a trigger to
9
+ * materialize one. Caps resolve env var > construct.config.json
10
+ * (resources.disk.observationsMaxDays / observationsMaxRows) > default,
11
+ * matching the precedence documented in lib/config/project-config.mjs.
3
12
  */
4
13
  import fs from 'node:fs';
5
14
  import path from 'node:path';
6
15
  import { resolveWithinRoot } from '../path-policy.mjs';
16
+ import { resolveStateDir } from '../state-root.mjs';
17
+ import { loadProjectConfig, resolveSetting } from '../config/project-config.mjs';
18
+ import { VectorClient } from './vector-client.mjs';
19
+
20
+ const OBSERVATIONS_MAX_DAYS_DEFAULT = 90;
21
+ const OBSERVATIONS_MAX_ROWS_DEFAULT = 5000;
7
22
 
8
23
  export function inferProjectName(cwd) {
9
24
  return path.basename(cwd) || 'construct';
@@ -19,14 +34,24 @@ function countFiles(dir) {
19
34
  return count;
20
35
  }
21
36
 
22
- export async function getStorageStatus(rootDir, { env = process.env, project = 'construct' } = {}) {
37
+ export async function getStorageStatus(rootDir, { env = process.env, project = 'construct', fsExistsSync = fs.existsSync } = {}) {
23
38
  const internalDir = path.join(rootDir, '.cx', 'knowledge', 'internal');
24
39
  const count = countFiles(internalDir);
25
40
 
41
+ // Health mirrors probeStorageHealth (lib/status.mjs), derived from filesystem state
42
+ // rather than hardcoded: no .cx/ is unavailable, no lancedb store yet is degraded
43
+ // (no vector index), both present is healthy. The lancedb store itself resolves
44
+ // through the same machine-scoped state root (ADR-0066) resetStorage below already
45
+ // uses, not a project-relative `.cx/lancedb` — an override wins when set.
46
+
47
+ const cxExists = fsExistsSync(path.join(rootDir, '.cx'));
48
+ const lancedbExists = fsExistsSync(env.CONSTRUCT_LANCEDB_PATH || resolveStateDir(rootDir, 'lancedb', { ensureDir: false }));
49
+ const status = !cxExists ? 'unavailable' : lancedbExists ? 'healthy' : 'degraded';
50
+
26
51
  return {
27
52
  backend: 'lancedb',
28
53
  project,
29
- status: 'healthy',
54
+ status,
30
55
  ingested: {
31
56
  count,
32
57
  label: 'Ingested documents'
@@ -36,7 +61,7 @@ export async function getStorageStatus(rootDir, { env = process.env, project = '
36
61
 
37
62
  export async function resetStorage(rootDir, { env = process.env, project = 'construct', resetVector = true, resetIngested = false } = {}) {
38
63
  if (resetVector) {
39
- const dbPath = env.CONSTRUCT_LANCEDB_PATH || path.join(rootDir, '.cx', 'lancedb');
64
+ const dbPath = env.CONSTRUCT_LANCEDB_PATH || resolveStateDir(rootDir, 'lancedb', { ensureDir: false });
40
65
  if (fs.existsSync(dbPath)) {
41
66
  fs.rmSync(dbPath, { recursive: true, force: true });
42
67
  }
@@ -78,6 +103,53 @@ export async function deleteIngestedArtifacts(rootDir, { files = [], confirm = f
78
103
  return { status: 'deleted', deletedCount };
79
104
  }
80
105
 
81
- export async function purgeExpiredData() {
82
- return { status: 'ok', purged: 0 };
106
+ // Recursive byte count for the "current size" figure doctor/prune report —
107
+ // LanceDB stores each table as a directory of Lance fragment files, so a
108
+ // single stat() on the db path itself would always read as 0.
109
+
110
+ function dirSizeBytes(dir) {
111
+ if (!fs.existsSync(dir)) return 0;
112
+ let bytes = 0;
113
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
114
+ const full = path.join(dir, entry.name);
115
+ bytes += entry.isDirectory() ? dirSizeBytes(full) : fs.statSync(full).size;
116
+ }
117
+ return bytes;
118
+ }
119
+
120
+ /**
121
+ * Evict expired/overflow rows from the machine-scoped observations_v1 table.
122
+ * A no-op when the vector index was never provisioned — nothing to evict,
123
+ * and provisioning one just to check would defeat the lazy-index contract.
124
+ * Returns { status: 'skipped', reason } in that case, or
125
+ * { status: 'ok', evictedCount, remainingCount, oldestRetainedAt, sizeBytes,
126
+ * maxAgeDays, maxRows } once a purge ran.
127
+ */
128
+ export async function purgeExpiredData(rootDir, { env = process.env, project = 'construct', maxAgeDays, maxRows } = {}) {
129
+ const dbPath = env.CONSTRUCT_LANCEDB_PATH || resolveStateDir(rootDir, 'lancedb', { ensureDir: false });
130
+ if (!fs.existsSync(dbPath)) {
131
+ return { status: 'skipped', reason: 'no vector index provisioned yet' };
132
+ }
133
+
134
+ const { config } = loadProjectConfig(rootDir, env);
135
+ const resolvedMaxAgeDays = maxAgeDays ?? Number(resolveSetting({
136
+ config, env, jsonPath: 'resources.disk.observationsMaxDays',
137
+ envKey: 'CONSTRUCT_OBSERVATIONS_MAX_DAYS', defaultValue: OBSERVATIONS_MAX_DAYS_DEFAULT,
138
+ }).value);
139
+ const resolvedMaxRows = maxRows ?? Number(resolveSetting({
140
+ config, env, jsonPath: 'resources.disk.observationsMaxRows',
141
+ envKey: 'CONSTRUCT_OBSERVATIONS_MAX_ROWS', defaultValue: OBSERVATIONS_MAX_ROWS_DEFAULT,
142
+ }).value);
143
+
144
+ const vectorClient = new VectorClient({ env: { ...env, CONSTRUCT_LANCEDB_PATH: dbPath } });
145
+ const result = await vectorClient.pruneObservations({ maxAgeDays: resolvedMaxAgeDays, maxRows: resolvedMaxRows });
146
+
147
+ return {
148
+ status: 'ok',
149
+ project,
150
+ ...result,
151
+ sizeBytes: dirSizeBytes(dbPath),
152
+ maxAgeDays: resolvedMaxAgeDays,
153
+ maxRows: resolvedMaxRows,
154
+ };
83
155
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * lib/storage/backend-registry.mjs — kind:'storage' provider resolution.
3
+ *
4
+ * Run-store selection uses the same extension-manifest substrate as queue
5
+ * selection: builtin backends are always available, while project/user storage
6
+ * manifests can name an implementation without central dispatch edits.
7
+ */
8
+
9
+ import { loadManifestsFromDir, mergeManifests, resolveManifestDirs } from '../extensions/loader.mjs';
10
+
11
+ export const BUILTIN_STORAGE_BACKENDS = ['filesystem', 'sqlite', 'postgres'];
12
+ export const SUPPORTED_RUN_STORE_IMPLEMENTATIONS = ['filesystem', 'sqlite', 'postgres'];
13
+
14
+ export class UnregisteredStorageProviderError extends Error {
15
+ constructor(backend) {
16
+ super(
17
+ `Storage backend '${backend}' has no registered kind:'storage' provider. ` +
18
+ `Builtin providers: ${BUILTIN_STORAGE_BACKENDS.join(', ')}.`,
19
+ );
20
+ this.name = 'UnregisteredStorageProviderError';
21
+ this.backend = backend;
22
+ }
23
+ }
24
+
25
+ export class UnsupportedStorageProviderError extends Error {
26
+ constructor(backend, implementation) {
27
+ super(
28
+ `Storage backend '${backend}' resolves to unsupported run-store implementation ` +
29
+ `'${implementation || 'unknown'}'. Supported implementations: ` +
30
+ `${SUPPORTED_RUN_STORE_IMPLEMENTATIONS.join(', ')}.`,
31
+ );
32
+ this.name = 'UnsupportedStorageProviderError';
33
+ this.backend = backend;
34
+ this.implementation = implementation || null;
35
+ }
36
+ }
37
+
38
+ export function listStorageProviders({ rootDir = process.cwd(), env = process.env } = {}) {
39
+ const homeDir = env?.HOME || env?.USERPROFILE || undefined;
40
+ const dirs = resolveManifestDirs({ rootDir, homeDir });
41
+ const builtin = loadManifestsFromDir(dirs.builtin).manifests;
42
+ const user = loadManifestsFromDir(dirs.user).manifests;
43
+ const project = loadManifestsFromDir(dirs.project).manifests;
44
+ return mergeManifests(builtin, user, project).filter((m) => m.kind === 'storage');
45
+ }
46
+
47
+ export function findStorageProvider(backend, { rootDir = process.cwd(), env = process.env } = {}) {
48
+ if (!backend) return null;
49
+ return listStorageProviders({ rootDir, env }).find((m) => m.id === backend) || null;
50
+ }
51
+
52
+ export function runStoreImplementationFor(provider) {
53
+ const impl = provider?.operations?.runStore || provider?.runStore || provider?.implementation;
54
+ return typeof impl === 'string' && impl.trim() ? impl.trim().toLowerCase() : null;
55
+ }
56
+
57
+ export function resolveStorageBackend(backend, { rootDir = process.cwd(), env = process.env } = {}) {
58
+ const requested = String(backend || '').trim().toLowerCase();
59
+ if (!requested) return null;
60
+ if (BUILTIN_STORAGE_BACKENDS.includes(requested)) {
61
+ return { backend: requested, implementation: requested, provider: null };
62
+ }
63
+
64
+ const provider = findStorageProvider(requested, { rootDir, env });
65
+ if (!provider) throw new UnregisteredStorageProviderError(requested);
66
+
67
+ const implementation = runStoreImplementationFor(provider);
68
+ if (!SUPPORTED_RUN_STORE_IMPLEMENTATIONS.includes(implementation)) {
69
+ throw new UnsupportedStorageProviderError(requested, implementation);
70
+ }
71
+
72
+ return { backend: requested, implementation, provider };
73
+ }
@@ -1,22 +1,76 @@
1
1
  /**
2
2
  * lib/storage/backend.mjs — shared storage backend helpers.
3
3
  *
4
- * Construct uses embedded LanceDB and Git-backed state. createSqlClient and
5
- * related helpers are stubs that keep existing imports working without
6
- * orchestrating local Postgres.
4
+ * Construct uses embedded LanceDB and Git-backed state by default. When a
5
+ * Postgres URL is configured, createSqlClient returns the optional Postgres.js
6
+ * tagged-template client for shared run-store and migration paths.
7
7
  */
8
8
 
9
- export function createSqlClient(env = process.env) {
10
- // Stub: local-first operations skip Postgres, so external DATABASE_URL has
11
- // no effect here.
9
+ import { createRequire } from 'node:module';
10
+ import { resolveDatabaseUrl } from '../env-config.mjs';
12
11
 
13
- return null;
12
+ const require = createRequire(import.meta.url);
13
+
14
+ function clean(value) {
15
+ return String(value ?? '').trim();
16
+ }
17
+
18
+ function positiveInt(value, fallback) {
19
+ const parsed = Number(value);
20
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
21
+ }
22
+
23
+ function resolvePostgresUrl(env) {
24
+ return clean(env?.DATABASE_URL || env?.CONSTRUCT_DATABASE_URL || resolveDatabaseUrl(env));
25
+ }
26
+
27
+ function resolveSsl(value) {
28
+ const raw = clean(value).toLowerCase();
29
+ if (!raw || raw === 'false' || raw === '0' || raw === 'disable') return false;
30
+ if (raw === 'true' || raw === '1') return true;
31
+ if (['require', 'prefer', 'verify-full'].includes(raw)) return raw;
32
+ return false;
33
+ }
34
+
35
+ function loadPostgresFactory(postgresFactory) {
36
+ if (postgresFactory) return postgresFactory;
37
+ try {
38
+ const mod = require('postgres');
39
+ return mod.default || mod;
40
+ } catch (err) {
41
+ if (err?.code === 'MODULE_NOT_FOUND' || err?.code === 'ERR_MODULE_NOT_FOUND') return null;
42
+ throw err;
43
+ }
44
+ }
45
+
46
+ export function createSqlClient(env = process.env, { postgresFactory } = {}) {
47
+ const url = resolvePostgresUrl(env);
48
+ if (!url) return null;
49
+
50
+ const postgres = loadPostgresFactory(postgresFactory);
51
+ if (!postgres) return null;
52
+
53
+ return postgres(url, {
54
+ max: positiveInt(env?.CONSTRUCT_DB_MAX_CONNECTIONS, 5),
55
+ idle_timeout: positiveInt(env?.CONSTRUCT_DB_IDLE_TIMEOUT_SECONDS, 20),
56
+ connect_timeout: positiveInt(env?.CONSTRUCT_DB_CONNECT_TIMEOUT_SECONDS, 10),
57
+ ssl: resolveSsl(env?.CONSTRUCT_DB_SSL || env?.PGSSLMODE),
58
+ prepare: true,
59
+ onnotice: false,
60
+ connection: { application_name: clean(env?.CONSTRUCT_DB_APPLICATION_NAME) || 'construct' },
61
+ });
14
62
  }
15
63
 
16
64
  export async function probeSqlClient(client) {
17
- return { status: 'unavailable', message: 'SQL backend is no longer used for local operations' };
65
+ if (!client) return { status: 'unavailable', message: 'SQL client is not configured. Set DATABASE_URL or CONSTRUCT_DATABASE_URL.' };
66
+ try {
67
+ await client`SELECT 1 AS ok`;
68
+ return { status: 'available', message: 'Postgres connection succeeded' };
69
+ } catch (err) {
70
+ return { status: 'unavailable', message: err?.message || String(err) };
71
+ }
18
72
  }
19
73
 
20
74
  export async function closeSqlClient(client) {
21
- return Promise.resolve();
75
+ if (client?.end) await client.end({ timeout: 5 });
22
76
  }
@@ -5,10 +5,20 @@
5
5
  * Supports native batching for up to 2048 inputs per request.
6
6
  */
7
7
 
8
+ import { resolveSecret } from '../providers/secret-resolver.mjs';
9
+
8
10
  const MODEL_ID = 'text-embedding-3-small';
9
11
  const DIMENSIONS = 1536;
10
12
  const MAX_BATCH_SIZE = 2048;
11
13
 
14
+ function resolveOpenAiKey(env) {
15
+ try {
16
+ return resolveSecret('OPENAI_API_KEY', { env, allowAmbient: env === process.env });
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
12
22
  async function openaiEmbed(key, input) {
13
23
  const res = await fetch('https://api.openai.com/v1/embeddings', {
14
24
  method: 'POST',
@@ -34,7 +44,7 @@ async function openaiEmbed(key, input) {
34
44
  * @returns {Promise<{ embedding: Float32Array, model: string, dimensions: number }>}
35
45
  */
36
46
  export async function embed(text, { env = process.env } = {}) {
37
- const key = env.OPENAI_API_KEY;
47
+ const key = resolveOpenAiKey(env);
38
48
  if (!key) throw new Error('OPENAI_API_KEY required for OpenAI embeddings');
39
49
 
40
50
  const data = await openaiEmbed(key, text);
@@ -52,7 +62,7 @@ export async function embed(text, { env = process.env } = {}) {
52
62
  * @returns {Promise<{ embedding: Float32Array, model: string, dimensions: number }[]>}
53
63
  */
54
64
  export async function embedBatch(texts, { env = process.env } = {}) {
55
- const key = env.OPENAI_API_KEY;
65
+ const key = resolveOpenAiKey(env);
56
66
  if (!key) throw new Error('OPENAI_API_KEY required for OpenAI embeddings');
57
67
 
58
68
  const results = [];
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * lib/storage/hybrid-query.mjs — unified search across knowledge and facts.
3
3
  *
4
- * Stub for the LanceDB-backed search.
4
+ * Stub for the LanceDB-backed search. Results pass through the N4
5
+ * retrieval-hardening pipeline (lib/storage/retrieval-hardening.mjs) before
6
+ * being returned, so vector-store poisoning defenses (trust-weighted
7
+ * ranking, per-source recall cap, similarity sanity threshold, duplicate
8
+ * collapse) apply to every hybrid-query caller uniformly.
5
9
  */
6
10
  import { VectorClient } from './vector-client.mjs';
11
+ import { hardenRetrieval } from './retrieval-hardening.mjs';
7
12
 
8
13
  export async function buildHybridSearchResultsAsync(rootDir, query, { limit = 10, env = process.env } = {}) {
9
14
  // Embed the query
@@ -17,8 +22,11 @@ export async function buildHybridSearchResultsAsync(rootDir, query, { limit = 10
17
22
  const docs = await client.searchDocuments({ project: 'construct', queryEmbedding: embedding, limit });
18
23
  const obs = await client.searchObservations({ project: 'construct', queryEmbedding: embedding, limit });
19
24
 
20
- // Merge both result sets and rank by similarity
25
+ // Merge both result sets, then rank by similarity, provenance, and recall
26
+ // caps rather than similarity alone — an adversarial embedding tuned for
27
+ // top cosine similarity must not dominate assembled context.
21
28
 
22
29
  const all = [...docs.map(d => ({ ...d, type: 'document' })), ...obs.map(o => ({ ...o, type: 'observation' }))];
23
- return all.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
30
+ const ranked = all.sort((a, b) => b.similarity - a.similarity);
31
+ return hardenRetrieval(ranked, { limit });
24
32
  }