@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
@@ -0,0 +1,41 @@
1
+ /**
2
+ * lib/graph/gaps.mjs — coverage-gap queries over the living dependency graph.
3
+ *
4
+ * findMissingTestCapabilities() answers "which capabilities/workflows have
5
+ * zero validating tests" by reading the persisted graph's inbound validates
6
+ * edges — the C1 store already dedups test --validates--> capability edges
7
+ * from both registry (verification.functional/hostEmulation) and
8
+ * corpus-annotation (@capability tags) sources, so a zero-count here is a
9
+ * real gap, not a source-coverage artifact. A workflow is flagged only when
10
+ * every capability that embeds it also has zero validating tests, since a
11
+ * partially-tested workflow is a weaker signal than an untested capability.
12
+ */
13
+
14
+ import { loadGraph, nodesByType, dependentsOf, dependenciesOf } from './store.mjs';
15
+
16
+ /**
17
+ * @param {string} rootDir
18
+ * @returns {{ graphPresent: boolean, capabilities: string[], workflows: string[] }}
19
+ */
20
+ export function findMissingTestCapabilities(rootDir) {
21
+ const graph = loadGraph(rootDir);
22
+ if (!graph.exists) return { graphPresent: false, capabilities: [], workflows: [] };
23
+
24
+ const untestedCapIds = new Set();
25
+ for (const cap of nodesByType(graph, 'capability')) {
26
+ if (dependentsOf(graph, cap.id, 'validates').length === 0) untestedCapIds.add(cap.id);
27
+ }
28
+
29
+ const untestedWorkflows = [];
30
+ for (const wf of nodesByType(graph, 'workflow')) {
31
+ const embeddingCaps = dependentsOf(graph, wf.id, 'embeds');
32
+ if (embeddingCaps.length === 0) continue;
33
+ if (embeddingCaps.every((capId) => untestedCapIds.has(capId))) untestedWorkflows.push(wf.id);
34
+ }
35
+
36
+ return {
37
+ graphPresent: true,
38
+ capabilities: [...untestedCapIds].sort(),
39
+ workflows: untestedWorkflows.sort(),
40
+ };
41
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * lib/graph/impacted.mjs — change-impact traversal for `construct graph impacted`.
3
+ *
4
+ * Given a set of changed repo-relative file paths, walks the living
5
+ * dependency graph forward from each changed-file node through `imports`
6
+ * (reverse — files/tests that import the changed file), `realizes`
7
+ * (changed file → capability), `embeds` (capability → workflow), `validates`
8
+ * (test → capability, reverse to find tests), and `documents` (doc → workflow
9
+ * or provider, reverse to find docs) edges to answer: which workflows, tests,
10
+ * and docs does this changeset touch. Scopes which checks a PR must run for a
11
+ * CI drift gate. A changed path absent from the graph is reported in
12
+ * `unknown` rather than raising — an unrecognized file (renamed, deleted,
13
+ * generated, or simply not yet built into the graph) is common and must not
14
+ * crash the traversal.
15
+ */
16
+
17
+ import { loadGraph, dependenciesOf, dependentsOf } from './store.mjs';
18
+
19
+ function toFileNodeId(rel) {
20
+ return rel.endsWith('.test.mjs') || rel.endsWith('.test.js') ? `test:${rel}` : `file:${rel}`;
21
+ }
22
+
23
+ function normalizeChangedPaths(changedFiles) {
24
+ return [...new Set((changedFiles || []).map((f) => f.split('\\').join('/').replace(/^\.\//, '')))].filter(Boolean);
25
+ }
26
+
27
+ // Reverse-import closure: every file/test/module that transitively imports
28
+ // the changed node, so a leaf-file edit surfaces every consumer up the chain.
29
+
30
+ function reverseImportClosure(graph, startId) {
31
+ const reached = new Set();
32
+ const stack = [startId];
33
+ while (stack.length) {
34
+ const id = stack.pop();
35
+ for (const importer of dependentsOf(graph, id, 'imports')) {
36
+ if (reached.has(importer)) continue;
37
+ reached.add(importer);
38
+ stack.push(importer);
39
+ }
40
+ }
41
+ return reached;
42
+ }
43
+
44
+ function stripPrefix(id) {
45
+ const colon = id.indexOf(':');
46
+ return colon === -1 ? id : id.slice(colon + 1);
47
+ }
48
+
49
+ /**
50
+ * @param {object} opts
51
+ * @param {string} opts.rootDir — project root holding .cx/graph/.
52
+ * @param {string[]} opts.changedFiles — repo-relative paths.
53
+ * @returns {{
54
+ * graphPresent: boolean,
55
+ * changed: string[],
56
+ * unknown: string[],
57
+ * impactedWorkflows: string[],
58
+ * impactedTests: string[],
59
+ * impactedDocs: string[],
60
+ * impactedCapabilities: string[],
61
+ * }}
62
+ */
63
+ export function computeImpacted({ rootDir, changedFiles }) {
64
+ const changed = normalizeChangedPaths(changedFiles);
65
+ const graph = loadGraph(rootDir);
66
+
67
+ if (!graph.exists) {
68
+ return {
69
+ graphPresent: false,
70
+ changed,
71
+ unknown: changed,
72
+ impactedWorkflows: [],
73
+ impactedTests: [],
74
+ impactedDocs: [],
75
+ impactedCapabilities: [],
76
+ };
77
+ }
78
+
79
+ const unknown = [];
80
+ const capabilities = new Set();
81
+ const tests = new Set();
82
+ const workflows = new Set();
83
+ const docs = new Set();
84
+
85
+ for (const rel of changed) {
86
+ const id = toFileNodeId(rel);
87
+ if (!graph.nodes.has(id)) { unknown.push(rel); continue; }
88
+
89
+ if (id.startsWith('test:')) tests.add(id);
90
+
91
+ for (const consumer of reverseImportClosure(graph, id)) {
92
+ if (consumer.startsWith('test:')) tests.add(consumer);
93
+ }
94
+
95
+ for (const cap of dependenciesOf(graph, id, 'realizes')) capabilities.add(cap);
96
+ }
97
+
98
+ // A capability reached via realizes pulls in its validating tests and its
99
+ // embedded workflow; a test reached via the import closure pulls in every
100
+ // capability it validates, closing the loop in either direction.
101
+
102
+ for (const cap of [...capabilities]) {
103
+ for (const t of dependentsOf(graph, cap, 'validates')) tests.add(t);
104
+ for (const wf of dependenciesOf(graph, cap, 'embeds')) workflows.add(wf);
105
+ }
106
+ for (const t of tests) {
107
+ for (const cap of dependenciesOf(graph, t, 'validates')) {
108
+ capabilities.add(cap);
109
+ for (const wf of dependenciesOf(graph, cap, 'embeds')) workflows.add(wf);
110
+ }
111
+ }
112
+
113
+ // Docs point at what they document (doc --documents--> workflow|provider),
114
+ // so a doc is impacted when it documents a workflow already in scope.
115
+
116
+ for (const wf of workflows) {
117
+ for (const doc of dependentsOf(graph, wf, 'documents')) docs.add(doc);
118
+ }
119
+
120
+ return {
121
+ graphPresent: true,
122
+ changed,
123
+ unknown: unknown.sort(),
124
+ impactedWorkflows: [...workflows].map(stripPrefix).sort(),
125
+ impactedTests: [...tests].map(stripPrefix).sort(),
126
+ impactedDocs: [...docs].map(stripPrefix).sort(),
127
+ impactedCapabilities: [...capabilities].map(stripPrefix).sort(),
128
+ };
129
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * lib/graph/runtime-evidence.mjs — runtime-evidence edges: traces linked to workflow nodes (LMCP-C9).
3
+ *
4
+ * The living graph has static edges only (registry, import-graph, co-change,
5
+ * corpus-annotation) — a workflow can be green in every one of those and still
6
+ * never have executed successfully. This module closes that gap by reading
7
+ * persisted orchestration run records (`<stateRoot>/runtime/orchestration/runs/*.json`,
8
+ * ADR-0066's machine-scoped location for the Mode-A filesystem run store
9
+ * lib/orchestration/run-store.mjs writes to) read-only and mapping each run
10
+ * that resolves to a workflow type onto a
11
+ * `runtime-evidence:<runId> --evidenced_by--> workflow:<type>` edge, carrying
12
+ * `{ timestamp, outcome }` on the runtime-evidence node's attrs. One node per
13
+ * run (not one node per workflow) so repeated runs of the same workflow each
14
+ * leave their own evidence rather than collapsing into a single edge whose
15
+ * weight-summing (store.mjs normalizeEdges) would blur distinct timestamps.
16
+ *
17
+ * A run's workflow type is `run.request.workflowType`, falling back to
18
+ * `run.plan.suggestedWorkflowType` (the same fallback lib/orchestration/
19
+ * run-store.mjs's listRuns already exposes) — a run with neither is not
20
+ * attributable to a workflow node and is skipped, not fabricated onto one.
21
+ * Outcome favors the F4-aggregated `run.executionState` (executed |
22
+ * degraded-executed | prepared | failed) when present since it already
23
+ * reconciles every task's signal; a legacy run with no executionState falls
24
+ * back to a coarser mapping off `run.status`.
25
+ *
26
+ * `latestEvidenceByWorkflow` reduces the per-run nodes to one summary per
27
+ * workflow type (most recent timestamp wins) — the shape `graph explain` and
28
+ * the C6 execution-staleness dimension (staleness.mjs) both consume, so a
29
+ * workflow's "last execution" is defined in exactly one place.
30
+ */
31
+
32
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
33
+ import path from 'node:path';
34
+
35
+ import { nodeId } from './store.mjs';
36
+ import { listWorkflowDefs } from '../embedded-contract/workflow-defs.mjs';
37
+ import { runtimeDir } from '../orchestration/run-store.mjs';
38
+
39
+ function runsDir(rootDir) {
40
+ return path.join(runtimeDir(rootDir), 'runs');
41
+ }
42
+
43
+ /**
44
+ * Read every persisted run record. Read-only — this module never writes to
45
+ * the orchestration run store. A corrupt run file is skipped, not fatal.
46
+ *
47
+ * @param {string} rootDir
48
+ * @returns {object[]}
49
+ */
50
+ export function readRunRecords(rootDir) {
51
+ const dir = runsDir(rootDir);
52
+ if (!existsSync(dir)) return [];
53
+ const out = [];
54
+ for (const name of readdirSync(dir)) {
55
+ if (!name.endsWith('.json')) continue;
56
+ try {
57
+ out.push(JSON.parse(readFileSync(path.join(dir, name), 'utf8')));
58
+ } catch {
59
+ /* a corrupt run file is skipped, not fatal to evidence collection */
60
+ }
61
+ }
62
+ return out;
63
+ }
64
+
65
+ function resolveWorkflowType(run) {
66
+ return run?.request?.workflowType || run?.plan?.suggestedWorkflowType || null;
67
+ }
68
+
69
+ // run.executionState (LMCP-F4) already reconciles every task's signal into
70
+ // one of executed | degraded-executed | prepared | failed; only a legacy run
71
+ // with no executionState falls back to the coarser run.status mapping.
72
+
73
+ const STATUS_TO_OUTCOME = {
74
+ 'completed': 'executed',
75
+ 'degraded': 'degraded-executed',
76
+ 'completed-prepare-only': 'prepared',
77
+ 'completed-with-failures': 'failed',
78
+ 'cancelled': 'failed',
79
+ 'error': 'failed',
80
+ };
81
+
82
+ function resolveOutcome(run) {
83
+ if (run.executionState) return run.executionState;
84
+ return STATUS_TO_OUTCOME[run.status] || null;
85
+ }
86
+
87
+ function resolveTimestamp(run) {
88
+ return run.updatedAt || run.createdAt || null;
89
+ }
90
+
91
+ /**
92
+ * Map every persisted run onto a runtime-evidence node + evidenced_by edge to
93
+ * its workflow node, when the run resolves to a known workflow type and has
94
+ * reached a terminal (non-planned, non-running) state.
95
+ *
96
+ * @param {object} opts
97
+ * @param {string} opts.rootDir
98
+ * @returns {{ nodes: object[], edges: object[], skippedRunIds: string[] }}
99
+ */
100
+ export function buildRuntimeEvidence({ rootDir }) {
101
+ const runs = readRunRecords(rootDir);
102
+ const nodes = [];
103
+ const edges = [];
104
+ const skipped = [];
105
+
106
+ for (const run of runs) {
107
+ if (!run?.runId) continue;
108
+ if (run.status === 'planned' || run.status === 'running') { skipped.push(run.runId); continue; }
109
+ const workflowType = resolveWorkflowType(run);
110
+ if (!workflowType) { skipped.push(run.runId); continue; }
111
+
112
+ const timestamp = resolveTimestamp(run);
113
+ const outcome = resolveOutcome(run);
114
+ const evidenceId = nodeId('runtime-evidence', run.runId);
115
+ const workflowNodeId = nodeId('workflow', workflowType);
116
+
117
+ nodes.push({
118
+ id: evidenceId,
119
+ type: 'runtime-evidence',
120
+ name: run.runId,
121
+ attrs: {
122
+ runId: run.runId,
123
+ workflowType,
124
+ timestamp,
125
+ outcome,
126
+ status: run.status,
127
+ taskCount: Array.isArray(run.tasks) ? run.tasks.length : 0,
128
+ },
129
+ });
130
+ edges.push({
131
+ from: evidenceId,
132
+ to: workflowNodeId,
133
+ rel: 'evidenced_by',
134
+ source: 'runtime-evidence',
135
+ });
136
+ }
137
+
138
+ return { nodes, edges, skippedRunIds: skipped };
139
+ }
140
+
141
+ /**
142
+ * Reduce per-run evidence nodes to one summary per workflow type: the most
143
+ * recent timestamp wins. Workflow types with zero evidence are present with
144
+ * `lastExecution: null` so a caller can distinguish never-executed from
145
+ * merely-stale without a second pass over the graph.
146
+ *
147
+ * @param {{ nodes: object[] }} evidence — buildRuntimeEvidence() output.
148
+ * @returns {Record<string, { timestamp: string, outcome: string, runId: string }|null>}
149
+ */
150
+ export function latestEvidenceByWorkflow(evidence) {
151
+ const byWorkflow = {};
152
+ for (const wf of listWorkflowDefs()) {
153
+ byWorkflow[wf.type] = null;
154
+ }
155
+ for (const n of evidence.nodes) {
156
+ if (n.type !== 'runtime-evidence') continue;
157
+ const { workflowType, timestamp, outcome, runId } = n.attrs || {};
158
+ if (!workflowType || !timestamp) continue;
159
+ const prev = byWorkflow[workflowType];
160
+ if (!prev || String(timestamp) > String(prev.timestamp)) {
161
+ byWorkflow[workflowType] = { timestamp, outcome, runId };
162
+ }
163
+ }
164
+ return byWorkflow;
165
+ }
166
+
167
+ /**
168
+ * Convenience: read runs directly off disk and reduce to per-workflow last
169
+ * execution in one call, for callers (CLI, staleness) that don't need the
170
+ * raw node/edge lists.
171
+ *
172
+ * @param {string} rootDir
173
+ * @returns {Record<string, { timestamp: string, outcome: string, runId: string }|null>}
174
+ */
175
+ export function computeLastExecutionByWorkflow(rootDir) {
176
+ return latestEvidenceByWorkflow(buildRuntimeEvidence({ rootDir }));
177
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * lib/graph/security-coverage.mjs — wire the adversarial/security test corpus
3
+ * into the living graph and measure OWASP GenAI coverage (LMCP-N8).
4
+ *
5
+ * A security test disconnected from the workflows it protects leaves coverage
6
+ * unmeasurable. This module makes the linkage a first-class graph fact: a test
7
+ * annotated `@owasp LLM01` (and optionally
8
+ * `@secures <workflow-id>`) becomes a test node carrying its OWASP categories,
9
+ * plus a `test --secures--> workflow` edge for each protected workflow. The
10
+ * coverage queries then read that structure back out of the graph — the matrix
11
+ * and gap list are generated from the graph, never a hand-maintained sidecar.
12
+ */
13
+
14
+ import { nodeId, loadGraph, nodesByType, dependentsOf } from './store.mjs';
15
+ import { extractSecurityTestEdges } from '../test-corpus-inventory.mjs';
16
+ import { loadEmbedCapabilities } from '../embed/capability-loader.mjs';
17
+
18
+ /** OWASP GenAI (LLM) Top 10, 2025 edition — the coverage rubric. */
19
+ export const OWASP_GENAI_TOP10 = Object.freeze([
20
+ { id: 'LLM01', name: 'Prompt Injection' },
21
+ { id: 'LLM02', name: 'Sensitive Information Disclosure' },
22
+ { id: 'LLM03', name: 'Supply Chain' },
23
+ { id: 'LLM04', name: 'Data and Model Poisoning' },
24
+ { id: 'LLM05', name: 'Improper Output Handling' },
25
+ { id: 'LLM06', name: 'Excessive Agency' },
26
+ { id: 'LLM07', name: 'System Prompt Leakage' },
27
+ { id: 'LLM08', name: 'Vector and Embedding Weaknesses' },
28
+ { id: 'LLM09', name: 'Misinformation' },
29
+ { id: 'LLM10', name: 'Unbounded Consumption' },
30
+ ]);
31
+
32
+ /**
33
+ * Seed security-test nodes + `secures` edges from `@owasp`/`@secures` tags.
34
+ *
35
+ * @param {{ rootDir: string }} opts
36
+ * @returns {{ nodes: object[], edges: object[] }}
37
+ */
38
+ export function buildFromSecurity({ rootDir }) {
39
+ const nodes = [];
40
+ const edges = [];
41
+
42
+ // A `@secures <id>` may name an executable workflow or an embed preset; the
43
+ // edge must point at whichever node actually exists, so resolve embed ids
44
+ // (which live as `embed:<id>`, not `workflow:<id>`) up front.
45
+ const embedIds = new Set(loadEmbedCapabilities({ rootDir }).capabilities.map((m) => m.id).filter(Boolean));
46
+
47
+ for (const { testPath, owasp, secures } of extractSecurityTestEdges({ rootDir })) {
48
+ const testId = nodeId('test', testPath);
49
+ nodes.push({ id: testId, type: 'test', name: testPath, attrs: { path: testPath, exists: true, owasp } });
50
+ for (const securedId of secures) {
51
+ const to = embedIds.has(securedId) ? nodeId('embed', securedId) : nodeId('workflow', securedId);
52
+ edges.push({ from: testId, to, rel: 'secures', source: 'corpus-annotation' });
53
+ }
54
+ }
55
+
56
+ return { nodes, edges };
57
+ }
58
+
59
+ /** Every test node carrying at least one OWASP category, from the live graph. */
60
+ function securityTestNodes(graph) {
61
+ return nodesByType(graph, 'test').filter((n) => Array.isArray(n.attrs?.owasp) && n.attrs.owasp.length > 0);
62
+ }
63
+
64
+ /**
65
+ * OWASP coverage matrix generated from the graph: all 10 categories, each with
66
+ * the count and paths of the tests tagged for it (0 for uncovered categories).
67
+ *
68
+ * @param {string} rootDir
69
+ * @returns {{ graphPresent: boolean, categories: Array<{ id, name, testCount, tests: string[] }>, uncovered: string[] }}
70
+ */
71
+ export function buildOwaspMatrix(rootDir) {
72
+ const graph = loadGraph(rootDir);
73
+ if (!graph.exists) return { graphPresent: false, categories: [], uncovered: [] };
74
+
75
+ const byCategory = new Map(OWASP_GENAI_TOP10.map((c) => [c.id, []]));
76
+ for (const test of securityTestNodes(graph)) {
77
+ for (const cat of test.attrs.owasp) {
78
+ if (byCategory.has(cat)) byCategory.get(cat).push(test.attrs.path ?? test.name);
79
+ }
80
+ }
81
+
82
+ const categories = OWASP_GENAI_TOP10.map(({ id, name }) => {
83
+ const tests = [...byCategory.get(id)].sort();
84
+ return { id, name, testCount: tests.length, tests };
85
+ });
86
+
87
+ return {
88
+ graphPresent: true,
89
+ categories,
90
+ uncovered: categories.filter((c) => c.testCount === 0).map((c) => c.id),
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Executable workflows and embed presets with zero inbound `secures` edges —
96
+ * the security-coverage gap list. Embed presets are included because they are
97
+ * the highest-agency units (external reads + write proposals).
98
+ *
99
+ * @param {string} rootDir
100
+ * @returns {{ graphPresent: boolean, workflows: string[], covered: string[] }}
101
+ */
102
+ export function findWorkflowsMissingSecurity(rootDir) {
103
+ const graph = loadGraph(rootDir);
104
+ if (!graph.exists) return { graphPresent: false, workflows: [], covered: [] };
105
+
106
+ const missing = [];
107
+ const covered = [];
108
+ for (const node of [...nodesByType(graph, 'workflow'), ...nodesByType(graph, 'embed')]) {
109
+ if (dependentsOf(graph, node.id, 'secures').length === 0) missing.push(node.id);
110
+ else covered.push(node.id);
111
+ }
112
+ return { graphPresent: true, workflows: missing.sort(), covered: covered.sort() };
113
+ }