@geraldmaron/construct 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (556) hide show
  1. package/.env.example +36 -0
  2. package/README.md +41 -7
  3. package/bin/construct +1029 -65
  4. package/bin/construct-postinstall.mjs +27 -2
  5. package/config/tag-vocabulary.json +264 -0
  6. package/examples/distribution/sources/deck-one-pager.md +1 -1
  7. package/lib/acp/server.mjs +21 -7
  8. package/lib/adapters-sync.mjs +2 -1
  9. package/lib/audit-trail.mjs +185 -2
  10. package/lib/beads/auto-close.mjs +2 -1
  11. package/lib/beads/drift.mjs +2 -1
  12. package/lib/bridges/copilot-proxy.mjs +20 -5
  13. package/lib/certification/skill-inventory.mjs +10 -1
  14. package/lib/cli/approvals.mjs +147 -0
  15. package/lib/cli-commands.mjs +105 -14
  16. package/lib/comment-lint.mjs +127 -8
  17. package/lib/config/schema.mjs +7 -30
  18. package/lib/config/source-target-registry.mjs +70 -0
  19. package/lib/config/source-targets.mjs +179 -205
  20. package/lib/contracts/coverage.mjs +77 -0
  21. package/lib/contracts/validate.mjs +102 -13
  22. package/lib/contracts/violation-log.mjs +50 -4
  23. package/lib/db/migrate.mjs +69 -0
  24. package/lib/db/migrations/001_orchestration_runs.sql +9 -0
  25. package/lib/db/migrations/002_queue_provider.sql +50 -0
  26. package/lib/db/migrations/003_worker_registry.sql +17 -0
  27. package/lib/db/migrations/004_trace_events.sql +16 -0
  28. package/lib/db/migrations/005_shared_memory.sql +15 -0
  29. package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
  30. package/lib/decisions/enforced-baseline.json +0 -2
  31. package/lib/decisions/registry.mjs +3 -2
  32. package/lib/deployment/parity-contract.mjs +1 -1
  33. package/lib/deployment-mode.mjs +78 -2
  34. package/lib/diagram-export.mjs +10 -1
  35. package/lib/distill.mjs +5 -2
  36. package/lib/docs-verify.mjs +2 -1
  37. package/lib/doctor/cli.mjs +1 -0
  38. package/lib/doctor/command-on-path.mjs +24 -0
  39. package/lib/doctor/diagnosis.mjs +109 -0
  40. package/lib/doctor/embedding-health.mjs +50 -0
  41. package/lib/doctor/engine-health.mjs +93 -0
  42. package/lib/doctor/graph-validate.mjs +47 -0
  43. package/lib/doctor/index.mjs +23 -4
  44. package/lib/doctor/sidecar-providers.mjs +56 -0
  45. package/lib/doctor/watchers/consistency.mjs +93 -1
  46. package/lib/doctor/watchers/cx-budget.mjs +1 -1
  47. package/lib/doctor/watchers/graph-staleness.mjs +1 -1
  48. package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
  49. package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
  50. package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
  51. package/lib/doctor/watchers/provider-breaker.mjs +78 -0
  52. package/lib/document-export.mjs +52 -27
  53. package/lib/document-extract/docling-client.mjs +9 -5
  54. package/lib/document-extract/docling-sidecar.py +30 -0
  55. package/lib/document-extract.mjs +1 -1
  56. package/lib/document-ingest.mjs +9 -0
  57. package/lib/embed/approval-queue.mjs +184 -88
  58. package/lib/embed/authority-guard.mjs +65 -2
  59. package/lib/embed/capability-jobs.mjs +365 -0
  60. package/lib/embed/capability-lifecycle.mjs +219 -0
  61. package/lib/embed/capability-loader.mjs +303 -0
  62. package/lib/embed/capability-runtime.mjs +58 -0
  63. package/lib/embed/cli.mjs +185 -8
  64. package/lib/embed/config.mjs +4 -0
  65. package/lib/embed/daemon.mjs +125 -6
  66. package/lib/embed/demand-fetch.mjs +190 -54
  67. package/lib/embed/inbox.mjs +12 -10
  68. package/lib/embed/presets/ops-triage.mjs +236 -0
  69. package/lib/embed/presets/pm-feedback.mjs +341 -0
  70. package/lib/embed/presets/tpm.mjs +401 -0
  71. package/lib/embed/providers/jira.mjs +72 -1
  72. package/lib/embed/providers/registry.mjs +140 -33
  73. package/lib/embed/worker.mjs +5 -0
  74. package/lib/embedded-contract/capability.mjs +4 -0
  75. package/lib/embedded-contract/execution.mjs +1 -1
  76. package/lib/embedded-contract/model-resolve.mjs +53 -3
  77. package/lib/embedded-contract/workflow-defs.mjs +48 -73
  78. package/lib/embedded-contract/workflow-invoke.mjs +144 -4
  79. package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
  80. package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
  81. package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
  82. package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
  83. package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
  84. package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
  85. package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
  86. package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
  87. package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
  88. package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
  89. package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
  90. package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
  91. package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
  92. package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
  93. package/lib/env-config.mjs +60 -11
  94. package/lib/export-validate.mjs +34 -2
  95. package/lib/extensions/index.mjs +27 -0
  96. package/lib/extensions/loader.mjs +120 -0
  97. package/lib/extensions/manifest-schema.mjs +141 -0
  98. package/lib/extensions/manifests/anthropic.manifest.json +12 -0
  99. package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
  100. package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
  101. package/lib/extensions/manifests/directory.manifest.json +12 -0
  102. package/lib/extensions/manifests/docling.manifest.json +37 -0
  103. package/lib/extensions/manifests/echo.manifest.json +8 -0
  104. package/lib/extensions/manifests/feedback.manifest.json +12 -0
  105. package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
  106. package/lib/extensions/manifests/github.manifest.json +52 -0
  107. package/lib/extensions/manifests/linear.manifest.json +50 -0
  108. package/lib/extensions/manifests/local.manifest.json +12 -0
  109. package/lib/extensions/manifests/ollama.manifest.json +12 -0
  110. package/lib/extensions/manifests/openai.manifest.json +12 -0
  111. package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
  112. package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
  113. package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
  114. package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
  115. package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
  116. package/lib/extensions/manifests/openrouter.manifest.json +12 -0
  117. package/lib/extensions/manifests/postgres.manifest.json +12 -0
  118. package/lib/extensions/manifests/salesforce.manifest.json +12 -0
  119. package/lib/extensions/manifests/slack.manifest.json +58 -0
  120. package/lib/extensions/manifests/whisper.manifest.json +36 -0
  121. package/lib/extensions/validate.mjs +175 -0
  122. package/lib/features.mjs +13 -9
  123. package/lib/flows/checkpoint.mjs +184 -0
  124. package/lib/flows/constants.mjs +27 -0
  125. package/lib/flows/define.mjs +146 -0
  126. package/lib/flows/engine.mjs +249 -0
  127. package/lib/flows/errors.mjs +19 -0
  128. package/lib/flows/index.mjs +27 -0
  129. package/lib/flows/joins.mjs +18 -0
  130. package/lib/flows/schema.mjs +90 -0
  131. package/lib/flows/state.mjs +31 -0
  132. package/lib/frameworks/loader.mjs +99 -0
  133. package/lib/frameworks/schema.mjs +157 -0
  134. package/lib/graph/build-from-corpus.mjs +67 -0
  135. package/lib/graph/build-from-embed.mjs +82 -0
  136. package/lib/graph/build-from-registry.mjs +164 -3
  137. package/lib/graph/cli.mjs +456 -12
  138. package/lib/graph/gap-queries.mjs +156 -0
  139. package/lib/graph/gaps.mjs +41 -0
  140. package/lib/graph/impacted.mjs +129 -0
  141. package/lib/graph/runtime-evidence.mjs +177 -0
  142. package/lib/graph/security-coverage.mjs +113 -0
  143. package/lib/graph/staleness.mjs +241 -10
  144. package/lib/graph/store.mjs +24 -7
  145. package/lib/graph/validate.mjs +161 -0
  146. package/lib/headhunt.mjs +9 -8
  147. package/lib/health-check.mjs +15 -7
  148. package/lib/hook-health.mjs +1 -1
  149. package/lib/hooks/agent-tracker.mjs +10 -4
  150. package/lib/hooks/edit-guard.mjs +3 -3
  151. package/lib/hooks/graph-impact-advisory.mjs +2 -2
  152. package/lib/hooks/guard-bash.mjs +41 -15
  153. package/lib/hooks/mcp-health-check.mjs +11 -4
  154. package/lib/hooks/model-fallback.mjs +50 -6
  155. package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
  156. package/lib/hooks/pre-compact.mjs +181 -173
  157. package/lib/hooks/rule-verifier.mjs +2 -1
  158. package/lib/hooks/session-reflect.mjs +2 -1
  159. package/lib/hooks/session-start.mjs +3 -3
  160. package/lib/host/readiness.mjs +109 -0
  161. package/lib/host-capabilities.mjs +13 -2
  162. package/lib/host-disposition.mjs +29 -8
  163. package/lib/identity.mjs +92 -0
  164. package/lib/ingest/degraded-extract.mjs +96 -0
  165. package/lib/ingest/docling-remote.mjs +2 -1
  166. package/lib/ingest/provider-extract.mjs +7 -19
  167. package/lib/ingest/sidecar-providers.mjs +196 -0
  168. package/lib/ingest-tooling.mjs +11 -5
  169. package/lib/init-unified.mjs +55 -38
  170. package/lib/init-update.mjs +2 -1
  171. package/lib/install/legacy-global-cleanup.mjs +25 -0
  172. package/lib/install/stage-project.mjs +41 -4
  173. package/lib/intake/daemon.mjs +99 -8
  174. package/lib/intake/git-queue.mjs +110 -38
  175. package/lib/intake/prepare.mjs +2 -0
  176. package/lib/intake/queue-registry.mjs +50 -0
  177. package/lib/intake/queue.mjs +133 -17
  178. package/lib/intake/session-prelude.mjs +57 -8
  179. package/lib/integrations/intake-integrations.mjs +61 -111
  180. package/lib/intent-classifier.mjs +43 -5
  181. package/lib/libreoffice-export.mjs +8 -8
  182. package/lib/logging/rotate.mjs +5 -4
  183. package/lib/mcp/broker.mjs +332 -17
  184. package/lib/mcp/denied-store.mjs +95 -0
  185. package/lib/mcp/destructive-approval.mjs +57 -0
  186. package/lib/mcp/destructive-gate.mjs +30 -0
  187. package/lib/mcp/dispatch-envelope.mjs +199 -0
  188. package/lib/mcp/memory-bridge.mjs +2 -1
  189. package/lib/mcp/server.mjs +185 -1268
  190. package/lib/mcp/tool-definitions-memory.mjs +376 -0
  191. package/lib/mcp/tool-definitions-project.mjs +271 -0
  192. package/lib/mcp/tool-definitions-skills.mjs +311 -0
  193. package/lib/mcp/tool-definitions-workflow.mjs +398 -0
  194. package/lib/mcp/tool-definitions.mjs +25 -0
  195. package/lib/mcp/tool-rate-limit.mjs +47 -0
  196. package/lib/mcp/tool-registry.mjs +107 -0
  197. package/lib/mcp/tool-safety.mjs +95 -0
  198. package/lib/mcp/tool-surface-parity.mjs +60 -0
  199. package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
  200. package/lib/mcp/tools/orchestration-run.mjs +197 -19
  201. package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
  202. package/lib/mcp/tools/project.mjs +25 -8
  203. package/lib/mcp/tools/provider-write.mjs +187 -0
  204. package/lib/mcp/tools/scope.mjs +0 -3
  205. package/lib/mcp/tools/skills.mjs +1 -1
  206. package/lib/mcp/tools/storage.mjs +2 -2
  207. package/lib/mcp/tools/web-search-governance.mjs +96 -0
  208. package/lib/mcp/tools/web-search.mjs +5 -76
  209. package/lib/mcp/transport/auth.mjs +238 -0
  210. package/lib/mcp/transport/http.mjs +136 -0
  211. package/lib/mcp/transport/mode.mjs +31 -0
  212. package/lib/mcp/transport/stdio.mjs +22 -0
  213. package/lib/mcp-catalog.json +4 -4
  214. package/lib/mcp-manager.mjs +34 -23
  215. package/lib/mcp-platform-config.mjs +53 -20
  216. package/lib/mode-capabilities.mjs +109 -0
  217. package/lib/model-router.mjs +42 -9
  218. package/lib/models/catalog.mjs +40 -1
  219. package/lib/net-guard.mjs +247 -0
  220. package/lib/observation-store.mjs +6 -2
  221. package/lib/ollama/provision-context.mjs +2 -1
  222. package/lib/opencode-config.mjs +22 -3
  223. package/lib/opencode-runtime-plugin.mjs +120 -2
  224. package/lib/oracle/actions.mjs +204 -10
  225. package/lib/oracle/cli.mjs +24 -3
  226. package/lib/oracle/daemon-entry.mjs +6 -0
  227. package/lib/oracle/dispatch.mjs +2 -1
  228. package/lib/oracle/execute.mjs +2 -2
  229. package/lib/oracle/gaps.mjs +3 -3
  230. package/lib/oracle/heartbeat.mjs +53 -0
  231. package/lib/oracle/read-model.mjs +69 -13
  232. package/lib/oracle/reconcile.mjs +3 -46
  233. package/lib/oracle/routing.mjs +37 -31
  234. package/lib/oracle/synthesize.mjs +1 -1
  235. package/lib/orchestration/classification.mjs +434 -0
  236. package/lib/orchestration/delegation-flow.mjs +132 -0
  237. package/lib/orchestration/flow-selection.mjs +418 -0
  238. package/lib/orchestration/gates.mjs +244 -0
  239. package/lib/orchestration/host-sampling.mjs +121 -0
  240. package/lib/orchestration/policy-constants.mjs +48 -0
  241. package/lib/orchestration/provider-outcome.mjs +197 -0
  242. package/lib/orchestration/readiness.mjs +114 -13
  243. package/lib/orchestration/run-store-postgres.mjs +32 -26
  244. package/lib/orchestration/run-store-sqlite.mjs +8 -14
  245. package/lib/orchestration/run-store.mjs +25 -12
  246. package/lib/orchestration/runtime.mjs +508 -62
  247. package/lib/orchestration/store.mjs +87 -12
  248. package/lib/orchestration/trace-store.mjs +125 -0
  249. package/lib/orchestration/web-capability.mjs +60 -0
  250. package/lib/orchestration/worker-runtime.mjs +162 -0
  251. package/lib/orchestration/worker.mjs +763 -80
  252. package/lib/orchestration-policy.mjs +67 -1111
  253. package/lib/output-quality.mjs +61 -2
  254. package/lib/packs/cli.mjs +144 -0
  255. package/lib/packs/core-pack.mjs +112 -0
  256. package/lib/packs/enablement.mjs +187 -0
  257. package/lib/packs/index.mjs +23 -0
  258. package/lib/packs/loader.mjs +176 -0
  259. package/lib/packs/manifest-schema.mjs +58 -0
  260. package/lib/packs/prompts.mjs +120 -0
  261. package/lib/packs/validate.mjs +208 -0
  262. package/lib/path-policy.mjs +56 -0
  263. package/lib/plugin-registry.mjs +4 -5
  264. package/lib/policy/audit-gate.mjs +42 -0
  265. package/lib/policy/consumption-budget.mjs +149 -0
  266. package/lib/policy/engine.mjs +81 -6
  267. package/lib/policy/role-authority.mjs +89 -0
  268. package/lib/prompt-composer.js +19 -3
  269. package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
  270. package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
  271. package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
  272. package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
  273. package/lib/providers/contract/adapters/github/index.mjs +38 -3
  274. package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
  275. package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
  276. package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
  277. package/lib/providers/contract/adapters/jira/manifest.json +17 -0
  278. package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
  279. package/lib/providers/contract.mjs +207 -0
  280. package/lib/providers/credential-bootstrap.mjs +7 -4
  281. package/lib/providers/credential-sources.mjs +14 -2
  282. package/lib/providers/directory/index.mjs +261 -0
  283. package/lib/providers/feedback/index.mjs +392 -0
  284. package/lib/providers/filter-audit.mjs +68 -0
  285. package/lib/providers/filter-schema.mjs +54 -0
  286. package/lib/providers/github/index.mjs +31 -0
  287. package/lib/providers/instance-config.mjs +215 -0
  288. package/lib/providers/op-locate.mjs +96 -0
  289. package/lib/providers/op-run.mjs +64 -9
  290. package/lib/providers/registry.mjs +26 -3
  291. package/lib/providers/secret-audit-wiring.mjs +41 -18
  292. package/lib/providers/secret-resolver.mjs +263 -31
  293. package/lib/publish-template.mjs +6 -3
  294. package/lib/publish-tooling.mjs +4 -0
  295. package/lib/publish.mjs +32 -4
  296. package/lib/queue/pg-queue.mjs +395 -0
  297. package/lib/reflect.mjs +2 -1
  298. package/lib/registry/assemble.mjs +28 -2
  299. package/lib/registry/catalog.mjs +6 -0
  300. package/lib/registry/consolidation.mjs +8 -1
  301. package/lib/registry/custom-scaffold.mjs +200 -0
  302. package/lib/registry/custom-schema.mjs +137 -0
  303. package/lib/registry/loader.mjs +15 -4
  304. package/lib/registry/manifests/format-engines.default.json +15 -0
  305. package/lib/registry/manifests/surface-map.default.json +46 -0
  306. package/lib/registry/retired-paths.mjs +1 -0
  307. package/lib/registry/surface-map.mjs +100 -50
  308. package/lib/registry/validate.mjs +3 -3
  309. package/lib/render-visual-check.mjs +240 -0
  310. package/lib/resources/budget.mjs +40 -17
  311. package/lib/roles/flavor-bindings.mjs +36 -14
  312. package/lib/roles/gateway.mjs +15 -8
  313. package/lib/roots.mjs +107 -0
  314. package/lib/runtime/uv-bootstrap.mjs +32 -6
  315. package/lib/runtime/whisper-bootstrap.mjs +11 -3
  316. package/lib/runtime-env.mjs +5 -2
  317. package/lib/sandbox.mjs +1 -1
  318. package/lib/scheduler/index.mjs +8 -20
  319. package/lib/schema-infer.mjs +31 -2
  320. package/lib/scopes/lifecycle.mjs +29 -25
  321. package/lib/scopes/loader.mjs +49 -12
  322. package/lib/scopes/teams.mjs +1 -1
  323. package/lib/security/ingest-boundary.mjs +80 -0
  324. package/lib/security/recall-wrapper.mjs +99 -0
  325. package/lib/security/trust.mjs +132 -0
  326. package/lib/service-manager.mjs +93 -24
  327. package/lib/setup.mjs +41 -23
  328. package/lib/skills-apply.mjs +4 -2
  329. package/lib/specialists/postconditions.mjs +2 -2
  330. package/lib/state-root.mjs +147 -0
  331. package/lib/status.mjs +597 -6
  332. package/lib/storage/admin.mjs +84 -7
  333. package/lib/storage/backend-registry.mjs +73 -0
  334. package/lib/storage/backend.mjs +63 -9
  335. package/lib/storage/embeddings-openai.mjs +12 -2
  336. package/lib/storage/hybrid-query.mjs +11 -3
  337. package/lib/storage/retrieval-hardening.mjs +384 -0
  338. package/lib/storage/shared-memory.mjs +158 -0
  339. package/lib/storage/vector-client.mjs +69 -2
  340. package/lib/team/health.mjs +72 -0
  341. package/lib/telemetry/backends/local.mjs +9 -3
  342. package/lib/telemetry/client.mjs +3 -7
  343. package/lib/template-registry.mjs +10 -12
  344. package/lib/tenant/context.mjs +82 -0
  345. package/lib/tenant/isolation.mjs +129 -0
  346. package/lib/test-corpus-inventory.mjs +103 -2
  347. package/lib/uninstall/uninstall.mjs +78 -12
  348. package/lib/validator.mjs +4 -6
  349. package/lib/worker/entrypoint.mjs +2 -1
  350. package/lib/worker/run.mjs +2 -2
  351. package/lib/worker/trace.mjs +5 -11
  352. package/lib/workflow-state.mjs +52 -8
  353. package/lib/workflows/liveness.mjs +203 -0
  354. package/lib/workflows/loader.mjs +177 -0
  355. package/lib/workflows/manifest-schema.mjs +71 -0
  356. package/lib/workflows/surface-parity.mjs +118 -0
  357. package/lib/workflows/validate.mjs +127 -0
  358. package/lib/writes/control-plane.mjs +140 -0
  359. package/lib/writes/envelope.mjs +206 -0
  360. package/lib/writes/sent-log.mjs +86 -0
  361. package/lib/writes/write-intent.mjs +109 -0
  362. package/package.json +20 -7
  363. package/personas/construct.md +12 -3
  364. package/registry/agent-manifest.json +117 -0
  365. package/registry/capabilities.json +1987 -0
  366. package/rules/common/comments.md +1 -0
  367. package/schemas/brand-voice.schema.json +24 -0
  368. package/schemas/capability-registry.schema.json +72 -0
  369. package/schemas/certification-run.schema.json +130 -0
  370. package/schemas/demo-recording.schema.json +46 -0
  371. package/schemas/eval-dataset.schema.json +79 -0
  372. package/schemas/execution-capability-profile.schema.json +46 -0
  373. package/schemas/execution-policy.schema.json +114 -0
  374. package/schemas/improvement-proposal.schema.json +65 -0
  375. package/schemas/mcp-tool-output.schema.json +61 -0
  376. package/schemas/platform-capabilities.schema.json +83 -0
  377. package/schemas/project-config.schema.json +215 -0
  378. package/schemas/project-demo.schema.json +60 -0
  379. package/schemas/provider-behavior-matrix.schema.json +91 -0
  380. package/schemas/scope.schema.json +197 -0
  381. package/schemas/specialist-trace.schema.json +107 -0
  382. package/schemas/team.schema.json +99 -0
  383. package/schemas/unified-registry.schema.json +546 -0
  384. package/scripts/sync-specialists.mjs +336 -106
  385. package/skills/docs/adr-workflow.md +1 -1
  386. package/skills/docs/codebase-research-workflow.md +3 -3
  387. package/skills/docs/prd-workflow.md +5 -6
  388. package/skills/docs/runbook-workflow.md +2 -2
  389. package/skills/docs/user-research-workflow.md +3 -3
  390. package/skills/operating/fleet-health-routing.md +77 -0
  391. package/skills/roles/ai-engineer.md +1 -1
  392. package/skills/roles/architect.md +0 -1
  393. package/skills/roles/business-strategist.md +1 -1
  394. package/skills/roles/data-analyst.telemetry.md +1 -1
  395. package/skills/roles/data-engineer.md +1 -1
  396. package/skills/roles/data-engineer.pipeline.md +1 -1
  397. package/skills/roles/data-engineer.vector-retrieval.md +1 -2
  398. package/skills/roles/data-engineer.warehouse.md +1 -1
  399. package/skills/roles/designer.accessibility.md +1 -1
  400. package/skills/roles/designer.md +0 -1
  401. package/skills/roles/devil-advocate.md +1 -1
  402. package/skills/roles/docs-keeper.md +1 -1
  403. package/skills/roles/evaluator.md +1 -1
  404. package/skills/roles/explorer.md +1 -1
  405. package/skills/roles/platform-engineer.md +1 -1
  406. package/skills/roles/qa.ai-eval.md +1 -2
  407. package/skills/roles/qa.api-contract.md +0 -1
  408. package/skills/roles/qa.data-pipeline.md +0 -1
  409. package/skills/roles/qa.md +0 -1
  410. package/skills/roles/qa.web-ui.md +0 -1
  411. package/skills/roles/release-manager.md +1 -1
  412. package/skills/roles/researcher.md +0 -2
  413. package/skills/roles/reviewer.md +0 -3
  414. package/skills/roles/security.ai.md +1 -1
  415. package/skills/roles/security.legal-compliance.md +1 -1
  416. package/skills/roles/security.md +0 -1
  417. package/skills/roles/security.privacy.md +0 -1
  418. package/skills/roles/security.supply-chain.md +1 -1
  419. package/skills/roles/sre.md +1 -1
  420. package/skills/roles/test-automation.md +1 -1
  421. package/skills/roles/trace-reviewer.md +1 -1
  422. package/skills/roles/ux-researcher.md +1 -1
  423. package/specialists/artifact-manifest.json +36 -36
  424. package/specialists/org/contracts/accessibility-to-qa.json +11 -3
  425. package/specialists/org/contracts/any-to-business-strategist.json +19 -7
  426. package/specialists/org/contracts/any-to-debugger.json +7 -1
  427. package/specialists/org/contracts/any-to-designer.json +7 -1
  428. package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
  429. package/specialists/org/contracts/any-to-explorer.json +13 -4
  430. package/specialists/org/contracts/any-to-sre-incident.json +6 -2
  431. package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
  432. package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
  433. package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
  434. package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
  435. package/specialists/org/contracts/architect-to-engineer.json +20 -4
  436. package/specialists/org/contracts/architect-to-evaluator.json +15 -5
  437. package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
  438. package/specialists/org/contracts/architect-to-operations.json +17 -4
  439. package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
  440. package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
  441. package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
  442. package/specialists/org/contracts/engineer-to-qa.json +20 -4
  443. package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
  444. package/specialists/org/contracts/explorer-to-engineer.json +11 -3
  445. package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
  446. package/specialists/org/contracts/operations-to-user.json +53 -0
  447. package/specialists/org/contracts/operations-triage-output.json +53 -0
  448. package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
  449. package/specialists/org/contracts/product-manager-to-architect.json +30 -10
  450. package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
  451. package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
  452. package/specialists/org/contracts/qa-to-release-manager.json +11 -3
  453. package/specialists/org/contracts/researcher-to-architect.json +10 -2
  454. package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
  455. package/specialists/org/contracts/reviewer-to-security.json +17 -3
  456. package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
  457. package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
  458. package/specialists/org/contracts/user-to-construct.json +11 -4
  459. package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
  460. package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
  461. package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
  462. package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
  463. package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
  464. package/specialists/org/groups/engineering-group.json +2 -6
  465. package/specialists/org/groups/governance-group.json +2 -3
  466. package/specialists/org/groups/operations-group.json +5 -8
  467. package/specialists/org/groups/product-group.json +4 -8
  468. package/specialists/org/groups/quality-group.json +3 -7
  469. package/specialists/org/groups/strategy-group.json +6 -9
  470. package/specialists/org/models.json.example +8 -0
  471. package/specialists/org/scopes/creative.json +6 -1
  472. package/specialists/org/scopes/operations.json +7 -1
  473. package/specialists/org/scopes/research.json +6 -1
  474. package/specialists/org/scopes/rnd.json +7 -1
  475. package/specialists/org/specialists/cx-architect.json +27 -8
  476. package/specialists/org/specialists/cx-designer.json +22 -8
  477. package/specialists/org/specialists/cx-engineer.json +71 -7
  478. package/specialists/org/specialists/cx-operations.json +89 -15
  479. package/specialists/org/specialists/cx-orchestrator.json +16 -4
  480. package/specialists/org/specialists/cx-product-manager.json +28 -9
  481. package/specialists/org/specialists/cx-qa.json +16 -6
  482. package/specialists/org/specialists/cx-researcher.json +41 -7
  483. package/specialists/org/specialists/cx-reviewer.json +61 -11
  484. package/specialists/org/specialists/cx-security.json +30 -10
  485. package/specialists/org/teams/design-team.json +3 -4
  486. package/specialists/org/teams/engineering-team.json +3 -10
  487. package/specialists/org/teams/governance-team.json +3 -5
  488. package/specialists/org/teams/operations-team.json +6 -12
  489. package/specialists/org/teams/product-management-team.json +2 -3
  490. package/specialists/org/teams/quality-team.json +4 -12
  491. package/specialists/org/teams/research-team.json +3 -3
  492. package/specialists/org/teams/strategy-team.json +7 -14
  493. package/specialists/prompts/cx-architect.md +20 -1
  494. package/specialists/prompts/cx-data-analyst.md +1 -1
  495. package/specialists/prompts/cx-designer.md +12 -4
  496. package/specialists/prompts/cx-engineer.md +15 -1
  497. package/specialists/prompts/cx-operations.md +14 -2
  498. package/specialists/prompts/cx-orchestrator.md +9 -5
  499. package/specialists/prompts/cx-product-manager.md +5 -1
  500. package/specialists/prompts/cx-qa.md +6 -2
  501. package/specialists/prompts/cx-researcher.md +27 -31
  502. package/specialists/prompts/cx-reviewer.md +19 -1
  503. package/specialists/prompts/cx-security.md +5 -1
  504. package/templates/distribution/construct-brand.typ +123 -59
  505. package/templates/distribution/construct-decision.typ +6 -3
  506. package/templates/distribution/construct-pdf.typ +11 -4
  507. package/templates/distribution/construct-prd.typ +6 -3
  508. package/templates/distribution/construct-research.typ +7 -4
  509. package/vendor/pandoc-ext/README.md +3 -0
  510. package/vendor/pandoc-ext/diagram.lua +687 -0
  511. package/lib/providers/contract/adapters/git/index.mjs +0 -115
  512. package/lib/providers/contract/adapters/slack/index.mjs +0 -175
  513. package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
  514. package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
  515. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
  516. package/specialists/org/contracts/designer-to-accessibility.json +0 -36
  517. package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
  518. package/specialists/org/contracts/qa-to-test-automation.json +0 -42
  519. package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
  520. package/specialists/org/contracts/sre-to-release-manager.json +0 -36
  521. package/specialists/org/specialists/cx-accessibility.json +0 -69
  522. package/specialists/org/specialists/cx-ai-engineer.json +0 -75
  523. package/specialists/org/specialists/cx-business-strategist.json +0 -72
  524. package/specialists/org/specialists/cx-data-engineer.json +0 -71
  525. package/specialists/org/specialists/cx-devil-advocate.json +0 -71
  526. package/specialists/org/specialists/cx-docs-keeper.json +0 -82
  527. package/specialists/org/specialists/cx-evaluator.json +0 -69
  528. package/specialists/org/specialists/cx-explorer.json +0 -69
  529. package/specialists/org/specialists/cx-legal-compliance.json +0 -74
  530. package/specialists/org/specialists/cx-oracle.json +0 -46
  531. package/specialists/org/specialists/cx-platform-engineer.json +0 -80
  532. package/specialists/org/specialists/cx-rd-lead.json +0 -73
  533. package/specialists/org/specialists/cx-release-manager.json +0 -77
  534. package/specialists/org/specialists/cx-sre.json +0 -94
  535. package/specialists/org/specialists/cx-test-automation.json +0 -69
  536. package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
  537. package/specialists/org/specialists/cx-ux-researcher.json +0 -68
  538. package/specialists/org/teams/accessibility-team.json +0 -39
  539. package/specialists/org/teams/ux-research-team.json +0 -39
  540. package/specialists/prompts/cx-accessibility.md +0 -55
  541. package/specialists/prompts/cx-ai-engineer.md +0 -124
  542. package/specialists/prompts/cx-business-strategist.md +0 -58
  543. package/specialists/prompts/cx-data-engineer.md +0 -55
  544. package/specialists/prompts/cx-devil-advocate.md +0 -59
  545. package/specialists/prompts/cx-docs-keeper.md +0 -164
  546. package/specialists/prompts/cx-evaluator.md +0 -49
  547. package/specialists/prompts/cx-explorer.md +0 -71
  548. package/specialists/prompts/cx-legal-compliance.md +0 -58
  549. package/specialists/prompts/cx-oracle.md +0 -98
  550. package/specialists/prompts/cx-platform-engineer.md +0 -97
  551. package/specialists/prompts/cx-rd-lead.md +0 -59
  552. package/specialists/prompts/cx-release-manager.md +0 -54
  553. package/specialists/prompts/cx-sre.md +0 -111
  554. package/specialists/prompts/cx-test-automation.md +0 -55
  555. package/specialists/prompts/cx-trace-reviewer.md +0 -101
  556. package/specialists/prompts/cx-ux-researcher.md +0 -53
@@ -8,7 +8,8 @@ import { readFileSync, readdirSync, existsSync } from 'node:fs';
8
8
  import { loadRegistry } from '../../registry/loader.mjs';
9
9
  import { join, resolve } from 'node:path';
10
10
  import { homedir } from 'node:os';
11
- import { execSync } from 'node:child_process';
11
+ import { execFileSync } from 'node:child_process';
12
+ import { resolveWithinRoot } from '../../path-policy.mjs';
12
13
  import { loadConstructEnv } from '../../env-config.mjs';
13
14
  import { inspectContextState } from '../../context-state.mjs';
14
15
  import { loadWorkflow, summarizeWorkflow, inspectWorkflowHealth } from '../../workflow-state.mjs';
@@ -16,8 +17,11 @@ import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory }
16
17
  import { buildPublicHealthSurface } from '../../status.mjs';
17
18
  import { doctorRoot } from '../../config/xdg.mjs';
18
19
 
19
- export function exec(cmd, cwd) {
20
- return execSync(cmd, { stdio: 'pipe', timeout: 10000, cwd }).toString().trim();
20
+ // Model-controlled refs reach these git calls. Run git through argv with no shell so a
21
+ // base_ref like `$(...)` is handed to git as one literal argument, never evaluated.
22
+
23
+ function runGit(argv, cwd) {
24
+ return execFileSync('git', argv, { stdio: 'pipe', timeout: 10000, cwd }).toString().trim();
21
25
  }
22
26
 
23
27
  export function readJSON(filePath) {
@@ -140,10 +144,16 @@ export function summarizeDiff(args) {
140
144
  const baseRef = args.base_ref ?? 'HEAD~1';
141
145
  const cwd = args.cwd ? resolve(args.cwd) : process.cwd();
142
146
 
147
+ // A ref that starts with `-` would be read by git as an option, not a revision;
148
+ // control characters never belong in a ref. Reject both before argv handoff.
149
+ if (typeof baseRef !== 'string' || baseRef.startsWith('-') || /[\0\n\r]/.test(baseRef)) {
150
+ return { error: 'Invalid base_ref' };
151
+ }
152
+
143
153
  let stat, nameStatus;
144
154
  try {
145
- stat = exec(`git diff --stat ${baseRef}`, cwd);
146
- nameStatus = exec(`git diff --name-status ${baseRef}`, cwd);
155
+ stat = runGit(['diff', '--stat', baseRef], cwd);
156
+ nameStatus = runGit(['diff', '--name-status', baseRef], cwd);
147
157
  } catch {
148
158
  return { error: 'Not a git repository or git not available' };
149
159
  }
@@ -184,7 +194,14 @@ export function summarizeDiff(args) {
184
194
  }
185
195
 
186
196
  export function scanFile(args) {
187
- const filePath = resolve(args.file_path);
197
+ const root = args.cwd ? resolve(args.cwd) : process.cwd();
198
+
199
+ let filePath;
200
+ try {
201
+ filePath = resolveWithinRoot(root, args.file_path);
202
+ } catch (err) {
203
+ return { error: err.message };
204
+ }
188
205
 
189
206
  let rawBuffer;
190
207
  try {
@@ -302,14 +319,14 @@ export function projectContext(args, { ROOT_DIR }) {
302
319
  let changed_files = [];
303
320
 
304
321
  try {
305
- const log = exec('git log --oneline -10', cwd);
322
+ const log = runGit(['log', '--oneline', '-10'], cwd);
306
323
  recent_commits = log.split('\n').filter(Boolean);
307
324
  } catch {
308
325
  // not a git repo or no commits
309
326
  }
310
327
 
311
328
  try {
312
- const status = exec('git status --short', cwd);
329
+ const status = runGit(['status', '--short'], cwd);
313
330
  const statusLines = status.split('\n').filter(Boolean);
314
331
  changed_files = statusLines.map((l) => l.trim());
315
332
  working_tree_status = statusLines.length > 0 ? 'dirty' : 'clean';
@@ -0,0 +1,187 @@
1
+ /**
2
+ * lib/mcp/tools/provider-write.mjs — provider_write MCP tool (LMCP-I7).
3
+ *
4
+ * The only MCP-reachable path from a host agent (Claude Code, OpenCode) to an
5
+ * external write. Classified destructive in lib/mcp/tool-safety.mjs, so
6
+ * server.mjs's dispatch-time checkDestructiveGate() call already refuses the
7
+ * execute path without a valid out-of-band approval_token before this module
8
+ * ever runs — this file does not re-implement that check, it is downstream
9
+ * of it.
10
+ *
11
+ * Two modes, selected by `dry_run` (default true):
12
+ * - dry_run=true: resolves the governed-write adapter for `provider` and
13
+ * asks it to render the payload it would submit (adapter.renderDryRun),
14
+ * which runs field/shape validation only. Never calls
15
+ * lib/writes/envelope.mjs and never reaches adapter.write() — no network
16
+ * call, no side effect, regardless of token presence.
17
+ * - dry_run=false (execute): the destructive gate has already run in
18
+ * server.mjs by the time dispatchToolByName reaches this module, so
19
+ * arriving here at all means a token was consumed. Dispatches through
20
+ * writeWithEnvelope() (LMCP-J2) — idempotency key, sent-log dedup,
21
+ * policy/approval gate, retry, audit — which is the only caller of
22
+ * adapter.write(). This module never calls adapter.write() directly.
23
+ *
24
+ * E4 respect: when args.specialist_id is present (an embedded-specialist
25
+ * caller), the proposed provider+writeKind is checked against that
26
+ * specialist's embedBindings grant via AuthorityGuard before either mode
27
+ * proceeds. A specialist outside its grant is denied here, before the
28
+ * adapter is even resolved — independent of and in addition to the
29
+ * destructive-gate token check server.mjs already performed.
30
+ */
31
+
32
+ import { writeWithEnvelope } from '../../writes/envelope.mjs';
33
+ import { WriteSentLog } from '../../writes/sent-log.mjs';
34
+ import { AuthorityGuard } from '../../embed/authority-guard.mjs';
35
+ import { mergedEmbedBindings } from '../../embed/capability-jobs.mjs';
36
+ import { createGovernedJiraProvider } from '../../providers/contract/adapters/jira/governed-write.mjs';
37
+ import { createJiraTransport } from '../../providers/contract/adapters/jira/transport.mjs';
38
+ import { createGovernedConfluenceProvider } from '../../providers/contract/adapters/confluence/governed-write.mjs';
39
+ import { createConfluenceTransport } from '../../providers/contract/adapters/confluence/transport.mjs';
40
+ import { createGovernedGithubProvider } from '../../providers/contract/adapters/github/governed-write.mjs';
41
+ import githubAdapter from '../../providers/contract/adapters/github/index.mjs';
42
+
43
+ // Real transports read credentials from process.env at construction time
44
+ // (JIRA_URL/JIRA_EMAIL/JIRA_TOKEN, CONFLUENCE_URL/CONFLUENCE_EMAIL/
45
+ // CONFLUENCE_TOKEN); github's transport is the gh CLI, already
46
+ // credential-free at construction. Building the transport lazily per call
47
+ // (rather than once at module load) keeps a missing-credential AuthError
48
+ // scoped to the one call that needed it.
49
+
50
+ const ADAPTER_FACTORIES = {
51
+ jira: () => createGovernedJiraProvider({ jiraTransport: createJiraTransport() }),
52
+ confluence: () => createGovernedConfluenceProvider({ confluenceTransport: createConfluenceTransport() }),
53
+ github: () => createGovernedGithubProvider({ ghAdapter: githubAdapter }),
54
+ };
55
+
56
+ /**
57
+ * Resolve the governed-write adapter for a provider name. Isolated as its
58
+ * own function so tests can override resolution via the `deps.resolveAdapter`
59
+ * injection point without touching this module's real-transport wiring.
60
+ *
61
+ * @param {string} provider
62
+ * @returns {{ meta: object, write: Function, search?: Function, renderDryRun?: Function }}
63
+ */
64
+ function resolveAdapter(provider) {
65
+ const factory = ADAPTER_FACTORIES[provider];
66
+ if (!factory) {
67
+ const known = Object.keys(ADAPTER_FACTORIES).join(', ');
68
+ throw new Error(`provider_write: unknown provider "${provider}" (known: ${known})`);
69
+ }
70
+ return factory();
71
+ }
72
+
73
+ /**
74
+ * Check an embedded-specialist caller's proposal against its embedBindings
75
+ * grant. Returns null (no opinion) when args carries no specialist_id, so
76
+ * non-embed callers are unaffected — additive enforcement layered in front
77
+ * of the destructive gate, mirroring lib/embed/capability-jobs.mjs's
78
+ * checkProposalAuthority for the daemon path.
79
+ *
80
+ * @param {object} args
81
+ * @param {{ rootDir?: string, env?: object, embedBindings?: object }} opts
82
+ * @returns {Promise<{ allowed: boolean, reason?: string }|null>}
83
+ */
84
+ async function checkE4Binding(args, { rootDir, env = process.env, embedBindings } = {}) {
85
+ if (!args.specialist_id) return null;
86
+
87
+ const bindings = embedBindings ?? mergedEmbedBindings({ rootDir, env });
88
+ const guard = new AuthorityGuard({ authority: {} }, null, bindings);
89
+ const result = await guard.check('externalPost', {
90
+ proposal: {
91
+ specialistId: args.specialist_id,
92
+ providerId: args.provider,
93
+ writeKind: args.item?.type,
94
+ },
95
+ });
96
+
97
+ // Only a binding-scoped denial (mode: 'denied' with no queueId) short-circuits
98
+ // provider_write; a queued/autonomous authority-level outcome is not this
99
+ // tool's concern — the destructive gate + J2 envelope already own approval.
100
+ if (result.mode === 'denied' && !result.allowed) {
101
+ return { allowed: false, reason: result.reason };
102
+ }
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * provider_write tool implementation.
108
+ *
109
+ * @param {object} args
110
+ * @param {string} args.provider - 'jira' | 'confluence' | 'github'
111
+ * @param {object} args.item - write payload, shape depends on provider (see governed-write.mjs per adapter)
112
+ * @param {boolean} [args.dry_run=true] - render-only when true; never touches adapter.write()
113
+ * @param {string} [args.specialist_id] - embedded-specialist caller id (LMCP-E4 binding check)
114
+ * @param {string} [args.idempotency_key] - explicit idempotency key forwarded to the J2 envelope
115
+ * @param {object} [deps] - injection points for tests
116
+ * @param {Function} [deps.resolveAdapter] - override adapter resolution
117
+ * @param {WriteSentLog} [deps.sentLog] - override sent-log instance
118
+ * @param {object} [deps.embedBindings] - override merged embedBindings map
119
+ * @param {string} [deps.rootDir]
120
+ * @param {object} [deps.env]
121
+ * @returns {Promise<object>}
122
+ */
123
+ export async function providerWrite(args = {}, deps = {}) {
124
+ const provider = args.provider;
125
+ const item = args.item;
126
+ const dryRun = args.dry_run !== false;
127
+
128
+ if (!provider) return { error: 'provider_write: args.provider is required' };
129
+ if (!item || typeof item !== 'object') return { error: 'provider_write: args.item is required' };
130
+
131
+ const bindingDenial = await checkE4Binding(args, {
132
+ rootDir: deps.rootDir,
133
+ env: deps.env,
134
+ embedBindings: deps.embedBindings,
135
+ });
136
+ if (bindingDenial) {
137
+ return { status: 'denied', provider, dryRun, reason: bindingDenial.reason };
138
+ }
139
+
140
+ const resolve = deps.resolveAdapter ?? resolveAdapter;
141
+ let adapter;
142
+ try {
143
+ adapter = resolve(provider);
144
+ } catch (err) {
145
+ return { error: err.message ?? String(err) };
146
+ }
147
+
148
+ if (dryRun) {
149
+ if (typeof adapter.renderDryRun !== 'function') {
150
+ return {
151
+ status: 'dry-run',
152
+ provider,
153
+ dryRun: true,
154
+ diff: null,
155
+ note: `provider "${provider}" adapter has no renderDryRun; payload echoed verbatim, no validation performed.`,
156
+ payload: item,
157
+ };
158
+ }
159
+ try {
160
+ const diff = adapter.renderDryRun(item);
161
+ return { status: 'dry-run', provider, dryRun: true, diff };
162
+ } catch (err) {
163
+ return { status: 'dry-run-invalid', provider, dryRun: true, error: err.message ?? String(err) };
164
+ }
165
+ }
166
+
167
+ const sentLog = deps.sentLog ?? new WriteSentLog({ persistPath: WriteSentLog.resolvePersistPath(deps.rootDir ?? process.cwd()) });
168
+
169
+ const envelopeResult = await writeWithEnvelope({
170
+ provider: adapter,
171
+ config: {},
172
+ payload: item,
173
+ dryRun: false,
174
+ sentLog,
175
+ idempotencyKey: args.idempotency_key,
176
+ requestedBy: args.specialist_id ? { role: `cx-${args.specialist_id}` } : {},
177
+ });
178
+
179
+ return {
180
+ status: envelopeResult.status,
181
+ provider,
182
+ dryRun: false,
183
+ envelope: envelopeResult.envelope,
184
+ };
185
+ }
186
+
187
+ export default providerWrite;
@@ -201,9 +201,6 @@ export function scopeCreate(args = {}) {
201
201
  // Mutating, gated, destructive: archive a curated scope.
202
202
 
203
203
  export function scopeArchive(args = {}) {
204
- if (args.confirm !== true) {
205
- return { error: 'scope_archive requires confirm=true (destructive: moves files into archive/scopes/)' };
206
- }
207
204
  if (!args.id || !args.reason || String(args.reason).trim().length < 8) {
208
205
  return { error: 'scope_archive requires id:string and reason:string (>=8 chars)' };
209
206
  }
@@ -253,7 +253,7 @@ export async function orchestrationPolicy(args) {
253
253
  const { emitTraceEvent, newTraceId } = await import('../../worker/trace.mjs');
254
254
  const cwd = process.cwd();
255
255
  const queue = createIntakeQueue(cwd, process.env);
256
- const intake = queue.read(intakeId);
256
+ const intake = await queue.read(intakeId);
257
257
  if (!intake) {
258
258
  intakeError = `intake packet not found: ${intakeId}`;
259
259
  } else if (!intake.triage) {
@@ -2,12 +2,12 @@
2
2
  * lib/mcp/tools/storage.mjs — Storage MCP tools: status, sync, reset, and artifact deletion.
3
3
  *
4
4
  * All functions are async. Wraps lib/storage/admin.mjs and lib/storage/sync.mjs.
5
- * Requires confirm=true guards on destructive operations (reset, delete).
5
+ * Destructive operations (reset, delete) require confirm=true plus a one-time out-of-band
6
+ * approval token, since these tools are reachable only through the model's argument channel.
6
7
  */
7
8
  import { resolve } from 'node:path';
8
9
  import { deleteIngestedArtifacts, getStorageStatus, inferProjectName, purgeExpiredData, resetStorage } from '../../storage/admin.mjs';
9
10
  import { syncFileStateToSql } from '../../storage/sync.mjs';
10
-
11
11
  export async function storageStatus(args) {
12
12
  const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
13
13
  const project = args.project ? String(args.project) : inferProjectName(cwd);
@@ -0,0 +1,96 @@
1
+ /**
2
+ * lib/mcp/tools/web-search-governance.mjs — the single F08 grader for web evidence.
3
+ *
4
+ * Every web result any specialist or tool ever sees passes through governWebResults here,
5
+ * so the F08 governance invariants (ADR-0017) are produced in exactly one place: a
6
+ * claim-relative class, an Admiralty grade defaulting to C3 with a derived confidence,
7
+ * a normalized date with a 12-month staleness flag, most-recent-first ordering, and the
8
+ * OWASP-LLM01 `trust: 'untrusted'` stamp on externally-sourced text. Results without a
9
+ * verifiable http(s) URL are dropped, never guessed. web-search.mjs (the governed
10
+ * provider tool), the orchestration worker's provider-native web path, and the remote
11
+ * orchestration ingress guard all re-use this grader so a citation can never reach a
12
+ * specialist ungoverned or mislabeled as trusted.
13
+ */
14
+
15
+ const COMMUNITY_HOSTS = ['reddit.com', 'stackoverflow.com', 'news.ycombinator.com', 'discord.com', 'github.com'];
16
+ const HIGH_CONFIDENCE_GRADES = new Set(['A1', 'A2', 'B1']);
17
+ const STALE_AFTER_DAYS = 365;
18
+
19
+ export function normalizeDate(it) {
20
+ const raw = it.date || it.publishedAt || it.published || it.published_at || it.datePublished || null;
21
+ if (!raw) return null;
22
+ const t = Date.parse(raw);
23
+ return Number.isNaN(t) ? null : new Date(t).toISOString().slice(0, 10);
24
+ }
25
+
26
+ // Claim-relative class (ADR-0017): community content is admissible primary evidence for
27
+ // sentiment/demand claims under a checklist the tool cannot verify deterministically, so
28
+ // community hosts default to `tertiary` (conservative for factual claims) and everything
29
+ // else to `secondary`, leaving the researcher to promote a community source when the claim
30
+ // is about experience.
31
+
32
+ export function classifyResult(url) {
33
+ try {
34
+ const host = new URL(url).hostname.replace(/^www\./, '');
35
+ return COMMUNITY_HOSTS.some((h) => host === h || host.endsWith('.' + h)) ? 'tertiary' : 'secondary';
36
+ } catch {
37
+ return 'tertiary';
38
+ }
39
+ }
40
+
41
+ // Confidence maps from the Admiralty grade; `high` is reserved for A1/A2/B1 (ADR-0017). A
42
+ // grade with reliability A/B or credibility 1/2 is `medium`; anything weaker is `low`.
43
+
44
+ export function confidenceFromGrade(grade) {
45
+ if (HIGH_CONFIDENCE_GRADES.has(grade)) return 'high';
46
+ const reliability = grade?.[0];
47
+ const credibility = grade?.[1];
48
+ if (reliability === 'A' || reliability === 'B' || credibility === '1' || credibility === '2') return 'medium';
49
+ return 'low';
50
+ }
51
+
52
+ // Grade one raw item into a governed web result, or null when it carries no verifiable
53
+ // http(s) URL. A raw item may come from a governed provider (may carry its own admiralty)
54
+ // or a provider-native citation (never does — defaults to C3 + needsGrading).
55
+
56
+ export function governWebResult(it, { now = Date.now() } = {}) {
57
+ if (!it || typeof it.url !== 'string' || !/^https?:\/\//.test(it.url)) return null;
58
+ const graded = typeof it.admiralty === 'string' && /^[A-F][1-6]$/.test(it.admiralty);
59
+ const admiralty = graded ? it.admiralty : 'C3';
60
+ const date = normalizeDate(it);
61
+ const stale = date != null && (now - Date.parse(date)) > STALE_AFTER_DAYS * 86_400_000;
62
+ return {
63
+ source: 'web',
64
+ url: it.url,
65
+ title: typeof it.title === 'string' && it.title ? it.title : it.url,
66
+ snippet: typeof it.snippet === 'string' ? it.snippet : '',
67
+ class: classifyResult(it.url),
68
+ admiralty,
69
+ confidence: confidenceFromGrade(admiralty),
70
+ date,
71
+ stale,
72
+ needsGrading: !graded,
73
+ // OWASP LLM01: web snippets are externally-sourced data — must not be treated as instructions
74
+ trust: 'untrusted',
75
+ };
76
+ }
77
+
78
+ // Order most-recent-first: fresh dated results, then undated (input order kept by the
79
+ // stable sort), then stale ones; newest first within a tier.
80
+
81
+ function recencyTier(r) {
82
+ return r.date && !r.stale ? 0 : r.date ? 2 : 1;
83
+ }
84
+
85
+ export function governWebResults(items, { now = Date.now() } = {}) {
86
+ const results = (Array.isArray(items) ? items : [])
87
+ .map((it) => governWebResult(it, { now }))
88
+ .filter(Boolean);
89
+ results.sort((a, b) => {
90
+ const t = recencyTier(a) - recencyTier(b);
91
+ if (t !== 0) return t;
92
+ if (a.date && b.date) return Date.parse(b.date) - Date.parse(a.date);
93
+ return 0;
94
+ });
95
+ return results;
96
+ }
@@ -8,29 +8,12 @@
8
8
  * Admiralty grade with a derived confidence (ADR-0017) — results without a URL are dropped, not
9
9
  * guessed; (2) every result is labeled `source: 'web'` and the tool never falls back to repo/source
10
10
  * search; (3) when no governed provider is configured or the provider is unreachable, the tool
11
- * returns a typed degradation and zero results rather than claiming it searched the web.
11
+ * returns a typed degradation and zero results rather than claiming it searched the web. The grading
12
+ * itself lives in web-search-governance.mjs, the single chokepoint shared with the orchestration
13
+ * worker's provider-native web path (ADR-0050).
12
14
  */
13
15
 
14
- const COMMUNITY_HOSTS = ['reddit.com', 'stackoverflow.com', 'news.ycombinator.com', 'discord.com', 'github.com'];
15
- const HIGH_CONFIDENCE_GRADES = new Set(['A1', 'A2', 'B1']);
16
-
17
- // Recency enforcement (rules/common/research.md §1, research-execution-policy
18
- // recencyRule). The researcher is told to search most-recent-first and treat
19
- // sources older than 12 months as stale, but the provider returns relevance- and
20
- // authority-ranked results with the date dropped, so older established sources
21
- // surface over newer ones. Normalize a date from whatever field the provider
22
- // supplies, flag staleness against the 12-month window, and reorder
23
- // most-recent-first. Admiralty-derived `confidence` is left intact (ADR-0017);
24
- // recency rides alongside it as `date` + `stale` rather than folded into it.
25
-
26
- const STALE_AFTER_DAYS = 365;
27
-
28
- function normalizeDate(it) {
29
- const raw = it.date || it.publishedAt || it.published || it.published_at || it.datePublished || null;
30
- if (!raw) return null;
31
- const t = Date.parse(raw);
32
- return Number.isNaN(t) ? null : new Date(t).toISOString().slice(0, 10);
33
- }
16
+ import { governWebResults } from './web-search-governance.mjs';
34
17
 
35
18
  function providerConfig(env) {
36
19
  const url = env.WEB_SEARCH_URL;
@@ -38,31 +21,6 @@ function providerConfig(env) {
38
21
  return { url, key: env.WEB_SEARCH_KEY || null };
39
22
  }
40
23
 
41
- // Claim-relative class (ADR-0017): community content is admissible primary evidence for sentiment/
42
- // demand claims under a checklist the tool cannot verify deterministically, so it defaults community
43
- // hosts to `tertiary` (conservative for factual claims) and everything else to `secondary`, leaving
44
- // the researcher to promote a community source to primary when the claim is about experience.
45
-
46
- function classifyResult(url) {
47
- try {
48
- const host = new URL(url).hostname.replace(/^www\./, '');
49
- return COMMUNITY_HOSTS.some((h) => host === h || host.endsWith('.' + h)) ? 'tertiary' : 'secondary';
50
- } catch {
51
- return 'tertiary';
52
- }
53
- }
54
-
55
- // Confidence maps from the Admiralty grade; `high` is reserved for A1/A2/B1 (ADR-0017). A grade with
56
- // reliability A/B or credibility 1/2 is `medium`; anything weaker is `low`.
57
-
58
- function confidenceFromGrade(grade) {
59
- if (HIGH_CONFIDENCE_GRADES.has(grade)) return 'high';
60
- const reliability = grade?.[0];
61
- const credibility = grade?.[1];
62
- if (reliability === 'A' || reliability === 'B' || credibility === '1' || credibility === '2') return 'medium';
63
- return 'low';
64
- }
65
-
66
24
  function degraded(reason, note) {
67
25
  return { source: 'web', degraded: true, degradationReason: reason, results: [], note };
68
26
  }
@@ -92,36 +50,7 @@ export async function webSearch(args = {}, { env = process.env, fetchImpl = glob
92
50
  }
93
51
 
94
52
  const items = Array.isArray(payload?.results) ? payload.results : [];
95
- const results = items
96
- .filter((it) => it && typeof it.url === 'string' && /^https?:\/\//.test(it.url))
97
- .map((it) => {
98
- const admiralty = typeof it.admiralty === 'string' && /^[A-F][1-6]$/.test(it.admiralty) ? it.admiralty : 'C3';
99
- const date = normalizeDate(it);
100
- const stale = date != null && (now - Date.parse(date)) > STALE_AFTER_DAYS * 86_400_000;
101
- return {
102
- source: 'web',
103
- url: it.url,
104
- title: typeof it.title === 'string' && it.title ? it.title : it.url,
105
- snippet: typeof it.snippet === 'string' ? it.snippet : '',
106
- class: classifyResult(it.url),
107
- admiralty,
108
- confidence: confidenceFromGrade(admiralty),
109
- date,
110
- stale,
111
- needsGrading: !(typeof it.admiralty === 'string' && /^[A-F][1-6]$/.test(it.admiralty)),
112
- };
113
- });
114
-
115
- // Order most-recent-first: fresh dated results, then undated (provider order
116
- // kept by the stable sort), then stale ones; newest first within a tier.
117
-
118
- const tier = (r) => (r.date && !r.stale ? 0 : r.date ? 2 : 1);
119
- results.sort((a, b) => {
120
- const t = tier(a) - tier(b);
121
- if (t !== 0) return t;
122
- if (a.date && b.date) return Date.parse(b.date) - Date.parse(a.date);
123
- return 0;
124
- });
53
+ const results = governWebResults(items, { now });
125
54
 
126
55
  return { source: 'web', degraded: false, query, claim, results, dropped: items.length - results.length };
127
56
  }