@geraldmaron/construct 1.4.2 → 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 (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 +55 -38
  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 +261 -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 +103 -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,401 @@
1
+ /**
2
+ * lib/embed/presets/tpm.mjs — deterministic TPM analysis for the `operations`
3
+ * embed preset (see the embed-capability manifest schema decision record).
4
+ *
5
+ * No reasoning engine ships in this repo yet, so this module is a pure,
6
+ * deterministic `reasoningExecutor(plan, ctx)` that the embed capability tick
7
+ * accepts unchanged: it reads the operations specialist's bound provider
8
+ * snapshot slice (Jira + Confluence + Slack sections, already binding-scoped
9
+ * and filter-narrowed by the caller) and composes the ops dependency-
10
+ * sequencing framework (cx-ops-dependency-sequencing) over it. It emits an
11
+ * output packet that
12
+ * validates against the `operations-tpm-briefing` output contract plus the
13
+ * writeIntents (draft Jira tickets) the caller enqueues for approval.
14
+ *
15
+ * Every load-bearing claim carries provenance — a source id the reader can
16
+ * re-verify (a Confluence page id, a Jira issue key, a Slack message id). A
17
+ * requirement with no covering issue is reported as a finding that cites the
18
+ * requirement id and the empty coverage, never a fabricated issue key.
19
+ * Nothing here writes to a provider: proposals are data, the approval queue is
20
+ * the only path out.
21
+ *
22
+ * Analysis moves (mapped to the five ops-framework steps):
23
+ * 1. Parse PRD docs (Confluence) into requirement items.
24
+ * 2. Map Jira epics/issues to requirements → coverage matrix.
25
+ * 3. Uncovered requirement → missing-work finding + a proposed draft issue.
26
+ * 4. Due-date / dependency-chain analysis → timeline-risk findings w/ evidence.
27
+ * 5. Slack signals cross-referenced for contradiction / blocker mentions.
28
+ */
29
+
30
+ const JIRA_PROVIDER = 'atlassian-jira';
31
+ const CONFLUENCE_PROVIDER = 'atlassian-confluence';
32
+ const SLACK_PROVIDER = 'slack';
33
+
34
+ const JIRA_WRITE_KIND = 'createIssue';
35
+
36
+ // A requirement line in a PRD body: an id token (REQ-12, R-3, FR-4…) followed
37
+ // by a colon or dash and the requirement text. Matching is deterministic so a
38
+ // seeded fixture produces the same requirement ids on every run.
39
+ const REQUIREMENT_LINE_RE = /^\s*(?:[-*]\s*)?((?:REQ|FR|NFR|R)-\d+)\s*[:.\-–)]\s*(.+?)\s*$/i;
40
+
41
+ // Slack signal keywords that flag a blocker or a contradiction against the
42
+ // plan. Presence alone is not a claim — the finding always cites the message id.
43
+ const BLOCKER_TERMS = ['blocked', 'blocker', 'stuck', 'cannot proceed', 'waiting on', 'slipping', 'at risk', 'wont make', "won't make", 'behind schedule'];
44
+ const CONTRADICTION_TERMS = ['descoped', 'de-scoped', 'cut from', 'dropped', 'no longer', 'reversed', 'changed our mind', 'not doing'];
45
+
46
+ function sectionItems(sections, providerId) {
47
+ const section = (sections || []).find((s) => s.provider === providerId);
48
+ return Array.isArray(section?.items) ? section.items : [];
49
+ }
50
+
51
+ function docText(doc) {
52
+ return String(doc?.body ?? doc?.text ?? doc?.content ?? '');
53
+ }
54
+
55
+ function docId(doc) {
56
+ return doc?.id ?? doc?.pageId ?? doc?.key ?? 'unknown';
57
+ }
58
+
59
+ /**
60
+ * Parse every PRD doc's body into requirement items. Each item keeps a
61
+ * re-verifiable provenance pair (the page id + the requirement id) so a
62
+ * downstream finding never has to invent where a requirement came from.
63
+ */
64
+ export function parseRequirements(docs) {
65
+ const requirements = [];
66
+ for (const doc of docs || []) {
67
+ const sourceDocId = docId(doc);
68
+ const sourceTitle = doc?.title ?? '[untitled]';
69
+ for (const line of docText(doc).split(/\r?\n/)) {
70
+ const match = REQUIREMENT_LINE_RE.exec(line);
71
+ if (!match) continue;
72
+ const reqId = match[1].toUpperCase();
73
+ requirements.push({
74
+ reqId,
75
+ text: match[2].trim(),
76
+ sourceDocId,
77
+ sourceTitle,
78
+ provenance: `${sourceDocId}#${reqId}`,
79
+ });
80
+ }
81
+ }
82
+ return requirements;
83
+ }
84
+
85
+ function issueKey(issue) {
86
+ return issue?.key ?? issue?.id ?? 'unknown';
87
+ }
88
+
89
+ // An issue covers a requirement when it names the requirement id in its
90
+ // summary, description, or an explicit `requirements` field. Substring match on
91
+ // a word-bounded id keeps coverage deterministic and auditable.
92
+ function issueMentionsRequirement(issue, reqId) {
93
+ const declared = Array.isArray(issue?.requirements) ? issue.requirements.map((r) => String(r).toUpperCase()) : [];
94
+ if (declared.includes(reqId)) return true;
95
+ const haystack = `${issue?.summary ?? ''} ${issue?.description ?? ''}`.toUpperCase();
96
+ const bounded = new RegExp(`(^|[^A-Z0-9])${reqId.replace(/[-]/g, '\\-')}([^A-Z0-9]|$)`);
97
+ return bounded.test(haystack);
98
+ }
99
+
100
+ /**
101
+ * Map Jira issues to requirements. Returns a coverage matrix: one row per
102
+ * requirement listing the covering issue keys (possibly empty). Every row
103
+ * carries the requirement's provenance so the briefing can cite it whether the
104
+ * requirement is covered or not.
105
+ */
106
+ export function buildCoverageMatrix(requirements, issues) {
107
+ return requirements.map((req) => {
108
+ const covering = (issues || [])
109
+ .filter((issue) => issueMentionsRequirement(issue, req.reqId))
110
+ .map((issue) => issueKey(issue));
111
+ return {
112
+ reqId: req.reqId,
113
+ text: req.text,
114
+ provenance: req.provenance,
115
+ coveredBy: covering,
116
+ covered: covering.length > 0,
117
+ };
118
+ });
119
+ }
120
+
121
+ function draftTicketProposal(row, req) {
122
+ return {
123
+ providerId: JIRA_PROVIDER,
124
+ writeKind: JIRA_WRITE_KIND,
125
+ reqId: row.reqId,
126
+ payload: {
127
+ type: 'issue',
128
+ issuetype: 'Task',
129
+ summary: `Cover ${row.reqId}: ${req?.text ?? row.text}`,
130
+ description: `Auto-drafted by the operations TPM preset because ${row.reqId} (source ${row.provenance}) has no covering Jira issue. Requirement text: ${req?.text ?? row.text}. Verify and edit before approving; nothing is created without approval.`,
131
+ requirements: [row.reqId],
132
+ },
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Uncovered requirement → one missing-work finding + one proposed draft issue.
138
+ * The finding cites the requirement id and the empty coverage; the proposal is
139
+ * data only — the caller runs it through AuthorityGuard and the approval queue.
140
+ */
141
+ export function findMissingWork(coverageMatrix, requirements) {
142
+ const findings = [];
143
+ const proposals = [];
144
+ const byId = new Map((requirements || []).map((r) => [r.reqId, r]));
145
+ for (const row of coverageMatrix) {
146
+ if (row.covered) continue;
147
+ const req = byId.get(row.reqId);
148
+ findings.push({
149
+ reqId: row.reqId,
150
+ text: row.text,
151
+ evidence: { requirement: row.provenance, coverage: 'none' },
152
+ statement: `${row.reqId} ("${row.text}") has no covering Jira issue (source ${row.provenance}).`,
153
+ });
154
+ proposals.push(draftTicketProposal(row, req));
155
+ }
156
+ return { findings, proposals };
157
+ }
158
+
159
+ function parseDate(value) {
160
+ if (!value) return null;
161
+ const t = Date.parse(value);
162
+ return Number.isNaN(t) ? null : t;
163
+ }
164
+
165
+ function issueDeps(issue) {
166
+ const raw = issue?.blockedBy ?? issue?.dependsOn ?? issue?.blocks ?? [];
167
+ return Array.isArray(raw) ? raw.map((d) => String(d)) : [];
168
+ }
169
+
170
+ /**
171
+ * Due-date / dependency-chain analysis. Walks each issue's blocking chain and
172
+ * registers a timeline-risk finding when a blocker is due no earlier than the
173
+ * dependent it gates (the dependent cannot finish on time), or when an issue is
174
+ * explicitly flagged blocked. Every finding cites its evidence — the issue
175
+ * keys, the due dates, and the chain — so no risk claim is unverifiable.
176
+ *
177
+ * `now` is injectable so a fixture is deterministic; it defaults to the current
178
+ * time only when a caller omits it.
179
+ */
180
+ export function findTimelineRisks(issues, { now = Date.now() } = {}) {
181
+ const byKey = new Map((issues || []).map((i) => [issueKey(i), i]));
182
+ const findings = [];
183
+
184
+ for (const issue of issues || []) {
185
+ const key = issueKey(issue);
186
+ const due = parseDate(issue?.dueDate);
187
+ const status = String(issue?.status ?? '').toLowerCase();
188
+
189
+ if (status.includes('blocked')) {
190
+ findings.push({
191
+ issueKey: key,
192
+ kind: 'blocked-status',
193
+ evidence: { issue: key, status: issue?.status ?? 'unknown' },
194
+ statement: `${key} is marked '${issue?.status ?? 'unknown'}' — flagged as an active blocker (source ${key}).`,
195
+ });
196
+ }
197
+
198
+ for (const depKey of issueDeps(issue)) {
199
+ const blocker = byKey.get(depKey);
200
+ if (!blocker) {
201
+ findings.push({
202
+ issueKey: key,
203
+ kind: 'unknown-blocker',
204
+ evidence: { issue: key, dependsOn: depKey, blockerDueDate: '[unverified]' },
205
+ statement: `${key} depends on ${depKey}, which is not in the bound snapshot — its schedule is [unverified] (source ${key}).`,
206
+ });
207
+ continue;
208
+ }
209
+ const blockerDue = parseDate(blocker?.dueDate);
210
+ if (due != null && blockerDue != null && blockerDue >= due) {
211
+ findings.push({
212
+ issueKey: key,
213
+ kind: 'slipping-chain',
214
+ evidence: {
215
+ issue: key,
216
+ issueDueDate: issue.dueDate,
217
+ blocker: depKey,
218
+ blockerDueDate: blocker.dueDate,
219
+ },
220
+ statement: `${key} (due ${issue.dueDate}) is blocked by ${depKey} (due ${blocker.dueDate}); the blocker is due no earlier than the dependent, so ${key} cannot finish on time (sources ${key}, ${depKey}).`,
221
+ });
222
+ }
223
+ }
224
+ }
225
+ return findings;
226
+ }
227
+
228
+ function messageId(msg) {
229
+ return msg?.id ?? msg?.ts ?? msg?.permalink ?? 'unknown';
230
+ }
231
+
232
+ function matchTerms(text, terms) {
233
+ const lower = String(text ?? '').toLowerCase();
234
+ return terms.filter((term) => lower.includes(term));
235
+ }
236
+
237
+ /**
238
+ * Cross-reference Slack signals for blocker or contradiction mentions. Each
239
+ * finding is a citation, not an interpretation: it names the message id, the
240
+ * channel, and the exact terms that matched, and — when a requirement id or
241
+ * issue key appears in the message — links the signal to that artifact.
242
+ */
243
+ export function findSlackSignals(messages, { requirements = [], issues = [] } = {}) {
244
+ const reqIds = requirements.map((r) => r.reqId);
245
+ const issueKeys = (issues || []).map((i) => issueKey(i));
246
+ const findings = [];
247
+
248
+ for (const msg of messages || []) {
249
+ const text = String(msg?.text ?? '');
250
+ const blockers = matchTerms(text, BLOCKER_TERMS);
251
+ const contradictions = matchTerms(text, CONTRADICTION_TERMS);
252
+ if (blockers.length === 0 && contradictions.length === 0) continue;
253
+
254
+ const upper = text.toUpperCase();
255
+ const refReqs = reqIds.filter((id) => upper.includes(id));
256
+ const refIssues = issueKeys.filter((k) => k !== 'unknown' && upper.includes(String(k).toUpperCase()));
257
+
258
+ findings.push({
259
+ messageId: messageId(msg),
260
+ channel: msg?.channel ?? 'unknown',
261
+ kind: contradictions.length > 0 ? 'contradiction' : 'blocker',
262
+ terms: [...blockers, ...contradictions],
263
+ references: { requirements: refReqs, issues: refIssues },
264
+ evidence: { message: messageId(msg), channel: msg?.channel ?? 'unknown' },
265
+ statement: `Slack message ${messageId(msg)} in ${msg?.channel ?? 'unknown'} mentions ${[...blockers, ...contradictions].map((t) => `'${t}'`).join(', ')}${refReqs.length || refIssues.length ? ` referencing ${[...refReqs, ...refIssues].join(', ')}` : ''} (source ${messageId(msg)}).`,
266
+ });
267
+ }
268
+ return findings;
269
+ }
270
+
271
+ function renderList(items, render) {
272
+ if (!items.length) return ['- _(none)_'];
273
+ return items.map(render);
274
+ }
275
+
276
+ /**
277
+ * Render the TPM briefing as markdown. Every load-bearing line ends with a
278
+ * parenthetical source id; a "Provenance index" section enumerates every source
279
+ * the briefing draws on so the reader can re-verify each claim.
280
+ */
281
+ export function renderBriefing(analysis) {
282
+ const { coverageMatrix, missingWork, timelineRisks, slackSignals, provenance, generatedAt } = analysis;
283
+
284
+ const lines = [];
285
+ lines.push('# TPM Briefing');
286
+ lines.push('');
287
+ lines.push(`Generated ${generatedAt} by the operations specialist (embed preset: operations).`);
288
+ lines.push('Every load-bearing claim below cites a re-verifiable source id. Proposals are held for approval and never auto-created.');
289
+ lines.push('');
290
+
291
+ lines.push('## Requirement coverage');
292
+ lines.push('');
293
+ lines.push(...renderList(coverageMatrix, (row) => `- ${row.reqId}: ${row.covered ? `covered by ${row.coveredBy.join(', ')}` : 'NOT COVERED'} (source ${row.provenance})`));
294
+ lines.push('');
295
+
296
+ lines.push('## Missing work (uncovered requirements)');
297
+ lines.push('');
298
+ lines.push(...renderList(missingWork.findings, (f) => `- ${f.statement}`));
299
+ lines.push('');
300
+
301
+ lines.push('## Timeline risk');
302
+ lines.push('');
303
+ lines.push(...renderList(timelineRisks, (f) => `- [${f.kind}] ${f.statement}`));
304
+ lines.push('');
305
+
306
+ lines.push('## Misalignment signals (Slack)');
307
+ lines.push('');
308
+ lines.push(...renderList(slackSignals, (f) => `- [${f.kind}] ${f.statement}`));
309
+ lines.push('');
310
+
311
+ lines.push('## Proposed tickets (awaiting approval)');
312
+ lines.push('');
313
+ lines.push(...renderList(missingWork.proposals, (p) => `- ${p.providerId}.${p.writeKind} — ${p.payload.summary} (for ${p.reqId})`));
314
+ lines.push('');
315
+
316
+ lines.push('## Provenance index');
317
+ lines.push('');
318
+ lines.push(...renderList(provenance, (src) => `- ${src}`));
319
+ lines.push('');
320
+
321
+ return lines.join('\n');
322
+ }
323
+
324
+ function collectProvenance(requirements, issues, messages) {
325
+ const sources = new Set();
326
+ for (const r of requirements) sources.add(r.provenance);
327
+ for (const i of issues) sources.add(issueKey(i));
328
+ for (const m of messages) sources.add(messageId(m));
329
+ return [...sources];
330
+ }
331
+
332
+ /**
333
+ * Run the deterministic TPM analysis over a bound snapshot slice. Pure — no
334
+ * provider calls, no queue writes. Returns the structured analysis, the output
335
+ * packet (contract shape), the write proposals, and the rendered briefing.
336
+ *
337
+ * @param {Array<{provider:string, items:object[]}>} sections
338
+ * @param {{ now?: number, generatedAt?: string }} [opts]
339
+ */
340
+ export function analyzeTpm(sections, { now = Date.now(), generatedAt } = {}) {
341
+ const docs = sectionItems(sections, CONFLUENCE_PROVIDER);
342
+ const issues = sectionItems(sections, JIRA_PROVIDER);
343
+ const messages = sectionItems(sections, SLACK_PROVIDER);
344
+
345
+ const requirements = parseRequirements(docs);
346
+ const coverageMatrix = buildCoverageMatrix(requirements, issues);
347
+ const missingWork = findMissingWork(coverageMatrix, requirements);
348
+ const timelineRisks = findTimelineRisks(issues, { now });
349
+ const slackSignals = findSlackSignals(messages, { requirements, issues });
350
+ const provenance = collectProvenance(requirements, issues, messages);
351
+
352
+ const analysis = {
353
+ generatedAt: generatedAt ?? new Date(now).toISOString(),
354
+ requirements,
355
+ coverageMatrix,
356
+ missingWork,
357
+ timelineRisks,
358
+ slackSignals,
359
+ provenance,
360
+ };
361
+
362
+ const briefing = renderBriefing(analysis);
363
+
364
+ // Each contract field is a section wrapper ({ count, findings/rows/sources })
365
+ // rather than a bare array so a legitimately empty section still counts as
366
+ // present under the output contract — an absent section signals a broken
367
+ // producer, an empty one signals "checked, nothing found".
368
+ const outputPacket = {
369
+ coverageMatrix: { count: coverageMatrix.length, rows: coverageMatrix },
370
+ missingWork: { count: missingWork.findings.length, findings: missingWork.findings },
371
+ timelineRisks: { count: timelineRisks.length, findings: timelineRisks },
372
+ misalignment: { count: slackSignals.length, findings: slackSignals },
373
+ proposals: {
374
+ count: missingWork.proposals.length,
375
+ items: missingWork.proposals.map((p) => ({ providerId: p.providerId, writeKind: p.writeKind, reqId: p.reqId, summary: p.payload.summary })),
376
+ },
377
+ provenance: { count: provenance.length, sources: provenance },
378
+ briefing,
379
+ };
380
+
381
+ return { analysis, outputPacket, writeProposals: missingWork.proposals, briefing };
382
+ }
383
+
384
+ /**
385
+ * The F5 reasoningExecutor. `runCapabilityTick` calls this with
386
+ * `(plan, { manifest, sections, sliceErrors, specialistId })` and consumes the
387
+ * returned `{ outputPacket, writeProposals }`. The plan is not needed for the
388
+ * deterministic composition — the ops framework is already encoded in the
389
+ * analysis moves — but it is accepted to match the F5 contract exactly.
390
+ *
391
+ * @param {{ now?: number, generatedAt?: string }} [config] injected for deterministic tests
392
+ * @returns {(plan: object, ctx: object) => Promise<{outputPacket: object, writeProposals: object[], briefing: string}>}
393
+ */
394
+ export function createTpmReasoningExecutor(config = {}) {
395
+ return async function tpmReasoningExecutor(_plan, ctx = {}) {
396
+ const { outputPacket, writeProposals, briefing } = analyzeTpm(ctx.sections ?? [], config);
397
+ return { outputPacket, writeProposals, briefing };
398
+ };
399
+ }
400
+
401
+ export default createTpmReasoningExecutor;
@@ -18,6 +18,14 @@
18
18
  * refs: [issues]
19
19
  * limit: 50 # max issues (default: 50)
20
20
  * fields: [summary,status,assignee,priority] # optional field subset
21
+ * filter: { scope, predicates, nativeQuery } # ADR-0060 filter block (LMCP-B11)
22
+ *
23
+ * When `filter` is present, `buildJqlFromFilter` compiles `scope.projects` +
24
+ * `predicates.*` + `nativeQuery` into JQL (server-side pushdown). Per
25
+ * ADR-0060 §3 pushdown is preferred but never sufficient on its own — the
26
+ * plane-side `matchesFilter` re-check in the poll loop (lib/embed/daemon.mjs)
27
+ * is the mandatory backstop and the enforcement of the predicates-AND-
28
+ * nativeQuery conjunction.
21
29
  */
22
30
 
23
31
  export class JiraProvider {
@@ -64,7 +72,7 @@ export class JiraProvider {
64
72
 
65
73
  async #listIssues(opts) {
66
74
  const limit = Number(opts.limit ?? 50);
67
- const jql = opts.jql ?? buildDefaultJql(opts);
75
+ const jql = opts.jql ?? (opts.filter ? buildJqlFromFilter(opts, opts.filter) : buildDefaultJql(opts));
68
76
  const fields = opts.fields ?? ['summary', 'status', 'assignee', 'priority', 'issuetype', 'labels', 'updated', 'created'];
69
77
 
70
78
  const data = await this.#post('/rest/api/3/search/jql', {
@@ -190,3 +198,66 @@ function buildDefaultJql(opts) {
190
198
  : '';
191
199
  return `${projectClause}statusCategory != Done ORDER BY updated DESC`;
192
200
  }
201
+
202
+ const JIRA_STATUS_CATEGORY = Object.freeze({
203
+ 'to-do': 'To Do',
204
+ 'in-progress': 'In Progress',
205
+ done: 'Done',
206
+ });
207
+
208
+ function jqlQuote(value) {
209
+ return `"${String(value).replace(/"/g, '\\"')}"`;
210
+ }
211
+
212
+ /**
213
+ * Compile an ADR-0060 filter block into JQL. `scope.projects` and
214
+ * `predicates.*` fold into AND-joined clauses; `opts.project`/`opts.projects`
215
+ * still apply when `scope.projects` is absent. `nativeQuery` is wrapped in
216
+ * parens and AND-joined with the compiled clauses — the conjunction the ADR
217
+ * requires, applied server-side here and re-verified plane-side afterward.
218
+ */
219
+ export function buildJqlFromFilter(opts, filter) {
220
+ const clauses = [];
221
+
222
+ const scopeProjects = filter?.scope?.projects?.length ? filter.scope.projects : resolveProjects(opts);
223
+ if (scopeProjects.length) {
224
+ clauses.push(`project in (${scopeProjects.map(jqlQuote).join(', ')})`);
225
+ }
226
+
227
+ const predicates = filter?.predicates ?? {};
228
+ if (predicates.assignee?.length) {
229
+ clauses.push(`assignee in (${predicates.assignee.map(jqlQuote).join(', ')})`);
230
+ }
231
+ if (predicates.statusCategory?.length) {
232
+ const jiraCategories = predicates.statusCategory.map((c) => JIRA_STATUS_CATEGORY[c]).filter(Boolean);
233
+ if (jiraCategories.length) clauses.push(`statusCategory in (${jiraCategories.map(jqlQuote).join(', ')})`);
234
+ }
235
+ if (predicates.priority?.length) {
236
+ clauses.push(`priority in (${predicates.priority.map(jqlQuote).join(', ')})`);
237
+ }
238
+ if (predicates.label?.length) {
239
+ clauses.push(`labels in (${predicates.label.map(jqlQuote).join(', ')})`);
240
+ }
241
+ if (predicates.updatedSince) {
242
+ clauses.push(`updated >= ${jqlUpdatedSince(predicates.updatedSince)}`);
243
+ }
244
+
245
+ if (!clauses.length) clauses.push('statusCategory != Done');
246
+
247
+ let jql = clauses.join(' AND ');
248
+ if (filter?.nativeQuery) jql = `(${jql}) AND (${filter.nativeQuery})`;
249
+ return `${jql} ORDER BY updated DESC`;
250
+ }
251
+
252
+ function jqlUpdatedSince(value) {
253
+ const isoDuration = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?)?$/.exec(value);
254
+ if (isoDuration) {
255
+ const days = Number(isoDuration[1] ?? 0);
256
+ if (days > 0) return `"-${days}d"`;
257
+ const hours = Number(isoDuration[2] ?? 0);
258
+ const minutes = Number(isoDuration[3] ?? 0);
259
+ if (hours > 0) return `"-${hours}h"`;
260
+ if (minutes > 0) return `"-${minutes}m"`;
261
+ }
262
+ return jqlQuote(value);
263
+ }