mergen-server 1.4.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 (308) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +96 -0
  3. package/dist/__stubs__/calibration.js +471 -0
  4. package/dist/__stubs__/causal.js +601 -0
  5. package/dist/__stubs__/closed-source.js +150 -0
  6. package/dist/__stubs__/license.js +35 -0
  7. package/dist/__stubs__/plans.js +197 -0
  8. package/dist/app.js +467 -0
  9. package/dist/cli.js +250 -0
  10. package/dist/commands/agent-identity.js +79 -0
  11. package/dist/commands/gate.js +107 -0
  12. package/dist/commands/github.js +632 -0
  13. package/dist/commands/incident.js +644 -0
  14. package/dist/commands/policy.js +710 -0
  15. package/dist/commands/scan.js +243 -0
  16. package/dist/commands/setup.js +1218 -0
  17. package/dist/commands/shared.js +109 -0
  18. package/dist/commands/team.js +809 -0
  19. package/dist/datadog/blame-attribution.js +228 -0
  20. package/dist/datadog/client.js +159 -0
  21. package/dist/datadog/compactor.js +169 -0
  22. package/dist/datadog/fingerprinter.js +34 -0
  23. package/dist/datadog/incident-state.js +21 -0
  24. package/dist/datadog/line-matcher.js +56 -0
  25. package/dist/datadog/memory-store.js +399 -0
  26. package/dist/datadog/otel-trace.js +67 -0
  27. package/dist/index.js +505 -0
  28. package/dist/intelligence/action-risk.js +132 -0
  29. package/dist/intelligence/activity-feed.js +40 -0
  30. package/dist/intelligence/agent-identity.js +115 -0
  31. package/dist/intelligence/agent-pipeline.js +329 -0
  32. package/dist/intelligence/agent-profiles.js +81 -0
  33. package/dist/intelligence/ai-commit.js +16 -0
  34. package/dist/intelligence/approval-analytics.js +42 -0
  35. package/dist/intelligence/approval-events.js +5 -0
  36. package/dist/intelligence/arch-boundaries.js +94 -0
  37. package/dist/intelligence/arch-graph.js +117 -0
  38. package/dist/intelligence/autonomy.js +397 -0
  39. package/dist/intelligence/azure-broker.js +57 -0
  40. package/dist/intelligence/azure-proxy-sessions.js +49 -0
  41. package/dist/intelligence/baseline.js +1 -0
  42. package/dist/intelligence/behavior-baseline.js +134 -0
  43. package/dist/intelligence/billing.js +1 -0
  44. package/dist/intelligence/blast-radius.js +224 -0
  45. package/dist/intelligence/bypass.js +157 -0
  46. package/dist/intelligence/calibration-classifier.js +77 -0
  47. package/dist/intelligence/calibration-git-sync.js +74 -0
  48. package/dist/intelligence/calibration.js +1 -0
  49. package/dist/intelligence/cascade-detector.js +58 -0
  50. package/dist/intelligence/case-study-generator.js +126 -0
  51. package/dist/intelligence/causal-graph.js +126 -0
  52. package/dist/intelligence/causal.js +1 -0
  53. package/dist/intelligence/change-risk.js +102 -0
  54. package/dist/intelligence/compliance-report.js +134 -0
  55. package/dist/intelligence/confidence-report.js +85 -0
  56. package/dist/intelligence/corpus-to-policy.js +140 -0
  57. package/dist/intelligence/credential-broker.js +112 -0
  58. package/dist/intelligence/credential-misuse.js +39 -0
  59. package/dist/intelligence/debug-sessions.js +108 -0
  60. package/dist/intelligence/decision-escalation.js +36 -0
  61. package/dist/intelligence/decision-memory.js +47 -0
  62. package/dist/intelligence/decision-similarity.js +85 -0
  63. package/dist/intelligence/default-safety-tests.js +53 -0
  64. package/dist/intelligence/degradation-watcher.js +108 -0
  65. package/dist/intelligence/detector-plugins.js +70 -0
  66. package/dist/intelligence/detectors.js +1 -0
  67. package/dist/intelligence/device-auth.js +138 -0
  68. package/dist/intelligence/diff-size.js +42 -0
  69. package/dist/intelligence/ebpf-verify.js +45 -0
  70. package/dist/intelligence/enterprise-policy-engine.js +1122 -0
  71. package/dist/intelligence/error-fingerprint.js +1 -0
  72. package/dist/intelligence/execution-gate.js +100 -0
  73. package/dist/intelligence/execution-mode.js +22 -0
  74. package/dist/intelligence/gate-analytics.js +256 -0
  75. package/dist/intelligence/gate-decision.js +64 -0
  76. package/dist/intelligence/gcp-broker.js +50 -0
  77. package/dist/intelligence/git-adr-sync.js +207 -0
  78. package/dist/intelligence/hitl-hold.js +460 -0
  79. package/dist/intelligence/hypothesis-history.js +93 -0
  80. package/dist/intelligence/idp-identity.js +83 -0
  81. package/dist/intelligence/impl-critic.js +216 -0
  82. package/dist/intelligence/incident-autopilot.js +622 -0
  83. package/dist/intelligence/incident-replay.js +162 -0
  84. package/dist/intelligence/incident-result-cache.js +33 -0
  85. package/dist/intelligence/incident-similarity.js +58 -0
  86. package/dist/intelligence/infra-detectors.js +312 -0
  87. package/dist/intelligence/license.js +185 -0
  88. package/dist/intelligence/llm-spokesperson.js +62 -0
  89. package/dist/intelligence/mcp-prompts.js +320 -0
  90. package/dist/intelligence/mcp-resources.js +171 -0
  91. package/dist/intelligence/normalize.js +108 -0
  92. package/dist/intelligence/notifications.js +83 -0
  93. package/dist/intelligence/oidc-issuer.js +87 -0
  94. package/dist/intelligence/override-corpus.js +597 -0
  95. package/dist/intelligence/planning-gate.js +84 -0
  96. package/dist/intelligence/plans.js +223 -0
  97. package/dist/intelligence/platt-scaling.js +164 -0
  98. package/dist/intelligence/policy-proposals.js +84 -0
  99. package/dist/intelligence/policy-suggester.js +161 -0
  100. package/dist/intelligence/policy-sync.js +103 -0
  101. package/dist/intelligence/policy-test-runner.js +35 -0
  102. package/dist/intelligence/postmortem-parser.js +181 -0
  103. package/dist/intelligence/postmortem-retrieval.js +224 -0
  104. package/dist/intelligence/postmortem-store.js +596 -0
  105. package/dist/intelligence/pr-commenter.js +81 -0
  106. package/dist/intelligence/pr-policy-gate.js +38 -0
  107. package/dist/intelligence/pr-shadow-analyzer.js +229 -0
  108. package/dist/intelligence/process-tree.js +108 -0
  109. package/dist/intelligence/prompts.js +1 -0
  110. package/dist/intelligence/replay.js +38 -0
  111. package/dist/intelligence/repro-steps.js +1 -0
  112. package/dist/intelligence/resource-extractors.js +0 -0
  113. package/dist/intelligence/resource-file-context.js +138 -0
  114. package/dist/intelligence/risk-score.js +22 -0
  115. package/dist/intelligence/rollback.js +137 -0
  116. package/dist/intelligence/runbook-updater.js +143 -0
  117. package/dist/intelligence/sandbox.js +194 -0
  118. package/dist/intelligence/script-trust.js +104 -0
  119. package/dist/intelligence/session-metrics.js +67 -0
  120. package/dist/intelligence/session-threat-tracker.js +231 -0
  121. package/dist/intelligence/shadow-digest-cron.js +81 -0
  122. package/dist/intelligence/shadow-log.js +284 -0
  123. package/dist/intelligence/shell-ast.js +145 -0
  124. package/dist/intelligence/siem-forward.js +104 -0
  125. package/dist/intelligence/slack-digest.js +151 -0
  126. package/dist/intelligence/slack-override-loop.js +217 -0
  127. package/dist/intelligence/slack-routing.js +67 -0
  128. package/dist/intelligence/slack.js +900 -0
  129. package/dist/intelligence/sql-ast.js +31 -0
  130. package/dist/intelligence/team.js +1 -0
  131. package/dist/intelligence/telemetry.js +76 -0
  132. package/dist/intelligence/threshold-optimizer.js +75 -0
  133. package/dist/intelligence/token-budget.js +66 -0
  134. package/dist/intelligence/tool-guard.js +708 -0
  135. package/dist/intelligence/tool-manifest 2.js +74 -0
  136. package/dist/intelligence/tool-manifest.js +125 -0
  137. package/dist/intelligence/tools-analysis.js +857 -0
  138. package/dist/intelligence/tools-arch.js +152 -0
  139. package/dist/intelligence/tools-autonomy.js +495 -0
  140. package/dist/intelligence/tools-blast-radius.js +140 -0
  141. package/dist/intelligence/tools-browser.js +431 -0
  142. package/dist/intelligence/tools-change-timeline.js +154 -0
  143. package/dist/intelligence/tools-credentials.js +117 -0
  144. package/dist/intelligence/tools-datadog.js +233 -0
  145. package/dist/intelligence/tools-debug-sessions.js +209 -0
  146. package/dist/intelligence/tools-discovery.js +284 -0
  147. package/dist/intelligence/tools-infra.js +612 -0
  148. package/dist/intelligence/tools-intent.js +128 -0
  149. package/dist/intelligence/tools-memory.js +514 -0
  150. package/dist/intelligence/tools-runbook.js +951 -0
  151. package/dist/intelligence/tools-sessions.js +107 -0
  152. package/dist/intelligence/tools-state.js +232 -0
  153. package/dist/intelligence/tools-utility.js +430 -0
  154. package/dist/intelligence/tools-validate.js +215 -0
  155. package/dist/intelligence/tools.js +1 -0
  156. package/dist/intelligence/trace-context.js +11 -0
  157. package/dist/intelligence/unclassified-clusters.js +87 -0
  158. package/dist/intelligence/usage.js +195 -0
  159. package/dist/routes/active-authors.js +63 -0
  160. package/dist/routes/activity-feed.js +21 -0
  161. package/dist/routes/adr.js +117 -0
  162. package/dist/routes/agent-activity.js +184 -0
  163. package/dist/routes/agent-blunders.js +163 -0
  164. package/dist/routes/agents.js +175 -0
  165. package/dist/routes/api-keys.js +65 -0
  166. package/dist/routes/arch.js +75 -0
  167. package/dist/routes/audit-export.js +93 -0
  168. package/dist/routes/billing-dashboard.js +158 -0
  169. package/dist/routes/billing-outcome.js +77 -0
  170. package/dist/routes/calibration.js +305 -0
  171. package/dist/routes/ci-gate-history.js +20 -0
  172. package/dist/routes/ci-gate.js +232 -0
  173. package/dist/routes/ci.js +293 -0
  174. package/dist/routes/compliance-report.js +24 -0
  175. package/dist/routes/confidence.js +30 -0
  176. package/dist/routes/credentials.js +119 -0
  177. package/dist/routes/dashboard.js +1407 -0
  178. package/dist/routes/decision-memory.js +42 -0
  179. package/dist/routes/demo.js +844 -0
  180. package/dist/routes/device-auth-stub.js +173 -0
  181. package/dist/routes/explain-why.js +39 -0
  182. package/dist/routes/gate-analytics.js +213 -0
  183. package/dist/routes/gate.js +28 -0
  184. package/dist/routes/github-webhook.js +364 -0
  185. package/dist/routes/habituation.js +30 -0
  186. package/dist/routes/health-integrations.js +243 -0
  187. package/dist/routes/heartbeats.js +45 -0
  188. package/dist/routes/hitl.js +364 -0
  189. package/dist/routes/impact-report.js +645 -0
  190. package/dist/routes/incident-webhook.js +63 -0
  191. package/dist/routes/incidents.js +366 -0
  192. package/dist/routes/layers.js +62 -0
  193. package/dist/routes/license.js +187 -0
  194. package/dist/routes/oidc-issuer.js +30 -0
  195. package/dist/routes/onboarding.js +170 -0
  196. package/dist/routes/otel.js +67 -0
  197. package/dist/routes/otlp-receiver.js +179 -0
  198. package/dist/routes/overrides.js +241 -0
  199. package/dist/routes/pagerduty.js +258 -0
  200. package/dist/routes/policies.js +809 -0
  201. package/dist/routes/policy-nl.js +169 -0
  202. package/dist/routes/postmortem.js +102 -0
  203. package/dist/routes/pr-shadow.js +47 -0
  204. package/dist/routes/rbac.js +61 -0
  205. package/dist/routes/replay.js +17 -0
  206. package/dist/routes/risk-report.js +164 -0
  207. package/dist/routes/runbooks.js +125 -0
  208. package/dist/routes/safety-test.js +174 -0
  209. package/dist/routes/sdk.js +222 -0
  210. package/dist/routes/sensor.js +819 -0
  211. package/dist/routes/sentry.js +140 -0
  212. package/dist/routes/sessions.js +43 -0
  213. package/dist/routes/setup-ui.js +594 -0
  214. package/dist/routes/shadow-report.js +107 -0
  215. package/dist/routes/slack-routing.js +171 -0
  216. package/dist/routes/team-usage.js +71 -0
  217. package/dist/routes/telemetry.js +28 -0
  218. package/dist/routes/tenants.js +199 -0
  219. package/dist/routes/tickets.js +167 -0
  220. package/dist/routes/validate.js +13 -0
  221. package/dist/routes/war-room.js +113 -0
  222. package/dist/scripts/check-arch.js +22 -0
  223. package/dist/seeds/community-corpus.js +176 -0
  224. package/dist/seeds/corpus.js +1224 -0
  225. package/dist/sensor/action-ledger.js +327 -0
  226. package/dist/sensor/adr-store.js +140 -0
  227. package/dist/sensor/agent-blunder-store.js +370 -0
  228. package/dist/sensor/agent-context-store.js +225 -0
  229. package/dist/sensor/agent-memory-store.js +231 -0
  230. package/dist/sensor/audit-fetch.js +23 -0
  231. package/dist/sensor/audit-log.js +273 -0
  232. package/dist/sensor/buffer-schemas.js +225 -0
  233. package/dist/sensor/buffer.js +599 -0
  234. package/dist/sensor/bypass-tracker.js +93 -0
  235. package/dist/sensor/ci-gate-history.js +70 -0
  236. package/dist/sensor/cloud-auth.js +183 -0
  237. package/dist/sensor/commit-context-store.js +235 -0
  238. package/dist/sensor/docker-log-stream.js +164 -0
  239. package/dist/sensor/docker-monitor.js +122 -0
  240. package/dist/sensor/extended-buffer.js +46 -0
  241. package/dist/sensor/feedback-token.js +41 -0
  242. package/dist/sensor/file-lock.js +6 -0
  243. package/dist/sensor/fs-watcher.js +106 -0
  244. package/dist/sensor/gate-heartbeat.js +122 -0
  245. package/dist/sensor/git-suspect.js +136 -0
  246. package/dist/sensor/habituation-store.js +77 -0
  247. package/dist/sensor/heartbeat-monitor.js +133 -0
  248. package/dist/sensor/incident-store.js +340 -0
  249. package/dist/sensor/infra-normalizer.js +513 -0
  250. package/dist/sensor/ingest.js +339 -0
  251. package/dist/sensor/jest-reporter.js +52 -0
  252. package/dist/sensor/k8s-events.js +132 -0
  253. package/dist/sensor/layer2-store.js +111 -0
  254. package/dist/sensor/layer3-store.js +230 -0
  255. package/dist/sensor/layer4-store.js +147 -0
  256. package/dist/sensor/logger.js +13 -0
  257. package/dist/sensor/otel-exporter.js +161 -0
  258. package/dist/sensor/paths.js +71 -0
  259. package/dist/sensor/policy-history.js +147 -0
  260. package/dist/sensor/pr-shadow-store.js +83 -0
  261. package/dist/sensor/process-watcher.js +162 -0
  262. package/dist/sensor/rbac.js +92 -0
  263. package/dist/sensor/redact.js +114 -0
  264. package/dist/sensor/redis-store.js +191 -0
  265. package/dist/sensor/route-reachability.js +64 -0
  266. package/dist/sensor/security-utils.js +18 -0
  267. package/dist/sensor/service-graph.js +170 -0
  268. package/dist/sensor/service-topology.js +227 -0
  269. package/dist/sensor/session-history.js +74 -0
  270. package/dist/sensor/session-persist.js +38 -0
  271. package/dist/sensor/shadow-promote.js +122 -0
  272. package/dist/sensor/sourcemap.js +281 -0
  273. package/dist/sensor/sqlite-store.js +205 -0
  274. package/dist/sensor/sso.js +164 -0
  275. package/dist/sensor/vitest-reporter.js +65 -0
  276. package/dist/sensor/watcher.js +48 -0
  277. package/dist/storage/interfaces.js +0 -0
  278. package/dist/storage/pg/pg-action-ledger.js +138 -0
  279. package/dist/storage/pg/pg-approval-store.js +107 -0
  280. package/dist/storage/pg/pg-blunder-store.js +193 -0
  281. package/dist/storage/pg/pg-ci-gate-history.js +83 -0
  282. package/dist/storage/pg/pg-client.js +24 -0
  283. package/dist/storage/pg/pg-event-store.js +63 -0
  284. package/dist/storage/pg/pg-incident-store.js +190 -0
  285. package/dist/storage/pg/pg-migrations.js +26 -0
  286. package/dist/storage/pg/pg-override-corpus.js +447 -0
  287. package/dist/storage/pg/pg-shadow-log.js +129 -0
  288. package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
  289. package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
  290. package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
  291. package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
  292. package/dist/storage/sqlite/sqlite-event-store.js +29 -0
  293. package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
  294. package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
  295. package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
  296. package/dist/storage/store-factory.js +55 -0
  297. package/dist/storage/store-registry.js +31 -0
  298. package/dist/update-checker.js +107 -0
  299. package/dist/workers/autopilot-worker.js +28 -0
  300. package/dist/workers/notification-worker.js +28 -0
  301. package/dist/workers/queues.js +58 -0
  302. package/dist/workers/validation-worker.js +24 -0
  303. package/dist/workers/worker-registry.js +18 -0
  304. package/package.json +123 -0
  305. package/sdk/mergen-inject.js +260 -0
  306. package/sdk/node.js +313 -0
  307. package/sdk/vite-plugin.ts +57 -0
  308. package/sdk/webpack-plugin.js +106 -0
@@ -0,0 +1,167 @@
1
+ import { Router } from "express";
2
+ import https from "https";
3
+ import { store } from "../sensor/buffer.js";
4
+ import { hypothesisHistory } from "../intelligence/hypothesis-history.js";
5
+ import { generateReproSteps } from "../intelligence/repro-steps.js";
6
+ import { findCodeOwners } from "../sensor/git-suspect.js";
7
+ import logger from "../sensor/logger.js";
8
+ function createTicketsRouter() {
9
+ const router = Router();
10
+ router.post("/tickets/linear", async (req, res) => {
11
+ const { pid, team_id } = req.body ?? {};
12
+ const apiKey = process.env.LINEAR_API_KEY ?? "";
13
+ const teamId = team_id ?? process.env.LINEAR_TEAM_ID ?? "";
14
+ if (!apiKey) {
15
+ res.status(400).json({ error: "LINEAR_API_KEY not configured", fix: "export LINEAR_API_KEY=lin_api_...", docs: "https://linear.app/settings/api" });
16
+ return;
17
+ }
18
+ if (!teamId) {
19
+ res.status(400).json({ error: "LINEAR_TEAM_ID not configured", fix: "export LINEAR_TEAM_ID=<team-id> # or pass team_id in the request body", docs: "https://linear.app/settings/api" });
20
+ return;
21
+ }
22
+ const { title, description } = await buildTicketContent(pid);
23
+ const mutation = `
24
+ mutation CreateIssue($input: IssueCreateInput!) {
25
+ issueCreate(input: $input) {
26
+ success
27
+ issue { id identifier url title }
28
+ }
29
+ }
30
+ `;
31
+ try {
32
+ const result = await httpPost(
33
+ "https://api.linear.app/graphql",
34
+ JSON.stringify({ query: mutation, variables: { input: { teamId, title, description, priority: 2 } } }),
35
+ { "Authorization": apiKey, "Content-Type": "application/json" }
36
+ );
37
+ const parsed = JSON.parse(result);
38
+ if (parsed.errors) {
39
+ res.status(400).json({ error: "Linear API error", details: parsed.errors });
40
+ return;
41
+ }
42
+ const issue = parsed.data?.issueCreate?.issue;
43
+ logger.info({ id: issue?.identifier }, "linear: ticket created");
44
+ res.json({ ok: true, url: issue?.url, id: issue?.identifier });
45
+ } catch (err) {
46
+ logger.warn({ err }, "linear: ticket creation failed");
47
+ res.status(500).json({ error: err instanceof Error ? err.message : "Linear API failed" });
48
+ }
49
+ });
50
+ router.post("/tickets/jira", async (req, res) => {
51
+ const { pid, project_key } = req.body ?? {};
52
+ const baseUrl = process.env.JIRA_BASE_URL ?? "";
53
+ const email = process.env.JIRA_EMAIL ?? "";
54
+ const apiToken = process.env.JIRA_API_TOKEN ?? "";
55
+ const projectKey = project_key ?? process.env.JIRA_PROJECT_KEY ?? "";
56
+ if (!baseUrl) {
57
+ res.status(400).json({ error: "JIRA_BASE_URL not configured", fix: "export JIRA_BASE_URL=https://yourco.atlassian.net", docs: "https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/" });
58
+ return;
59
+ }
60
+ if (!email) {
61
+ res.status(400).json({ error: "JIRA_EMAIL not configured", fix: "export JIRA_EMAIL=you@company.com", docs: "https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/" });
62
+ return;
63
+ }
64
+ if (!apiToken) {
65
+ res.status(400).json({ error: "JIRA_API_TOKEN not configured", fix: "export JIRA_API_TOKEN=<token> # create at https://id.atlassian.com/manage-profile/security/api-tokens", docs: "https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/" });
66
+ return;
67
+ }
68
+ if (!projectKey) {
69
+ res.status(400).json({ error: "JIRA_PROJECT_KEY not configured", fix: "export JIRA_PROJECT_KEY=ENG # or pass project_key in the request body", docs: "https://support.atlassian.com/jira-software-cloud/docs/what-is-a-jira-project/" });
70
+ return;
71
+ }
72
+ const { title, description } = await buildTicketContent(pid);
73
+ const jiraPayload = {
74
+ fields: {
75
+ project: { key: projectKey },
76
+ summary: title,
77
+ description: {
78
+ type: "doc",
79
+ version: 1,
80
+ content: [{ type: "paragraph", content: [{ type: "text", text: description }] }]
81
+ },
82
+ issuetype: { name: "Bug" }
83
+ }
84
+ };
85
+ const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
86
+ const jiraUrl = baseUrl.replace(/\/$/, "") + "/rest/api/3/issue";
87
+ try {
88
+ const result = await httpPost(
89
+ jiraUrl,
90
+ JSON.stringify(jiraPayload),
91
+ { "Authorization": `Basic ${auth}`, "Content-Type": "application/json", "Accept": "application/json" }
92
+ );
93
+ const parsed = JSON.parse(result);
94
+ if (parsed.errors) {
95
+ res.status(400).json({ error: "Jira API error", details: parsed.errors });
96
+ return;
97
+ }
98
+ const issueUrl = `${baseUrl.replace(/\/$/, "")}/browse/${parsed.key}`;
99
+ logger.info({ key: parsed.key }, "jira: ticket created");
100
+ res.json({ ok: true, url: issueUrl, key: parsed.key });
101
+ } catch (err) {
102
+ logger.warn({ err }, "jira: ticket creation failed");
103
+ res.status(500).json({ error: err instanceof Error ? err.message : "Jira API failed" });
104
+ }
105
+ });
106
+ return router;
107
+ }
108
+ async function buildTicketContent(pid) {
109
+ const latest = hypothesisHistory.latest();
110
+ const hyp = pid ? latest?.chain?.hypotheses?.find((h) => h.pid === pid) ?? latest?.topHypothesis : latest?.topHypothesis;
111
+ const logs = store.getLogs(200);
112
+ const network = store.getNetwork(200);
113
+ const contexts = store.getContext(20);
114
+ const deploys = store.getDeployments(1);
115
+ const deploy = deploys[0] ?? null;
116
+ const repro = generateReproSteps(logs, network, contexts);
117
+ let ownersLine = "";
118
+ if (latest?.chain?.errors?.[0]?.primaryFrame?.file) {
119
+ const owners = findCodeOwners(latest.chain.errors[0].primaryFrame.file, process.cwd());
120
+ if (owners?.owners?.length) ownersLine = `
121
+ Owners: ${owners.owners.join(", ")}`;
122
+ }
123
+ const title = hyp?.summary ?? "Bug report from Mergen";
124
+ const parts = [];
125
+ if (hyp?.summary) parts.push(`**Issue:** ${hyp.summary}`);
126
+ if (hyp?.fixHint) parts.push(`**Suggested fix:** ${hyp.fixHint}`);
127
+ if (deploy) parts.push(`**Affected SHA:** ${deploy.shortSha ?? deploy.sha.slice(0, 7)} (${deploy.environment})`);
128
+ if (ownersLine) parts.push(ownersLine);
129
+ if (repro.steps.length > 0) {
130
+ parts.push("\n**Steps to reproduce:**");
131
+ repro.steps.forEach((s, i) => parts.push(`${i + 1}. ${s}`));
132
+ }
133
+ if (hyp?.evidence?.length) {
134
+ parts.push("\n**Evidence:**");
135
+ hyp.evidence.slice(0, 5).forEach((e) => parts.push(`- ${e}`));
136
+ }
137
+ const dashUrl = process.env.MERGEN_DASHBOARD_URL;
138
+ if (dashUrl) parts.push(`
139
+ [View in Mergen](${dashUrl}/dashboard)`);
140
+ parts.push("\n_Generated automatically by Mergen_");
141
+ return { title, description: parts.join("\n") };
142
+ }
143
+ function httpPost(url, body, headers) {
144
+ return new Promise((resolve, reject) => {
145
+ const parsed = new URL(url);
146
+ const options = {
147
+ hostname: parsed.hostname,
148
+ port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
149
+ path: parsed.pathname + parsed.search,
150
+ method: "POST",
151
+ headers: { ...headers, "Content-Length": Buffer.byteLength(body) }
152
+ };
153
+ const req = https.request(options, (res) => {
154
+ let data = "";
155
+ res.on("data", (c) => {
156
+ data += c.toString();
157
+ });
158
+ res.on("end", () => resolve(data));
159
+ });
160
+ req.on("error", reject);
161
+ req.write(body);
162
+ req.end();
163
+ });
164
+ }
165
+ export {
166
+ createTicketsRouter
167
+ };
@@ -0,0 +1,13 @@
1
+ import { Router } from "express";
2
+ import { getWatchState } from "../sensor/fs-watcher.js";
3
+ function createValidateRouter() {
4
+ const router = Router();
5
+ router.get("/validate/state", (_req, res) => {
6
+ const state = getWatchState();
7
+ res.json({ watching: state !== null, ...state ?? {} });
8
+ });
9
+ return router;
10
+ }
11
+ export {
12
+ createValidateRouter
13
+ };
@@ -0,0 +1,113 @@
1
+ import { Router } from "express";
2
+ import { memoryStore, formatMttr } from "../datadog/memory-store.js";
3
+ import { getActiveIncident } from "../datadog/incident-state.js";
4
+ import { store } from "../sensor/buffer.js";
5
+ import { verifyFeedbackToken } from "../sensor/feedback-token.js";
6
+ import logger from "../sensor/logger.js";
7
+ function createWarRoomRouter() {
8
+ const router = Router();
9
+ router.get("/api/war-room", (_req, res) => {
10
+ const active = getActiveIncident();
11
+ const openRecs = memoryStore.listOpen();
12
+ const blastRadius = store.getBlastRadius({ since: active?.firedAt });
13
+ const allRecs = memoryStore.listAll(100);
14
+ const resolved = allRecs.filter((r) => r.mttrMs !== null).sort((a, b) => b.firedAt - a.firedAt).slice(0, 20).map((r) => ({
15
+ id: r.id,
16
+ service: r.service,
17
+ alertTitle: r.pdAlertTitle,
18
+ firedAt: r.firedAt,
19
+ mttrMs: r.mttrMs,
20
+ mttrFmt: r.mttrMs ? formatMttr(r.mttrMs) : null,
21
+ resolutionType: r.resolutionType,
22
+ fingerprint: r.fingerprint,
23
+ attributionConfidence: r.attributionConfidence,
24
+ attributionSha: r.attributionSha ? r.attributionSha.slice(0, 8) : null,
25
+ attributionValidated: r.attributionValidated,
26
+ fixPrUrl: r.fixPrUrl
27
+ }));
28
+ const validated = allRecs.filter(
29
+ (r) => r.attributionConfidence !== null && r.attributionValidated !== null
30
+ );
31
+ const bands = {
32
+ high: validated.filter((r) => r.attributionConfidence >= 0.8),
33
+ medium: validated.filter((r) => r.attributionConfidence >= 0.6 && r.attributionConfidence < 0.8),
34
+ low: validated.filter((r) => r.attributionConfidence < 0.6)
35
+ };
36
+ const accuracy = Object.fromEntries(
37
+ Object.entries(bands).map(([band, recs]) => [
38
+ band,
39
+ {
40
+ correct: recs.filter((r) => r.attributionValidated === 1).length,
41
+ total: recs.length,
42
+ pct: recs.length > 0 ? Math.round(recs.filter((r) => r.attributionValidated === 1).length / recs.length * 100) : null
43
+ }
44
+ ])
45
+ );
46
+ const openEnriched = openRecs.map((r) => ({
47
+ id: r.id,
48
+ service: r.service,
49
+ alertTitle: r.pdAlertTitle,
50
+ alertUrl: r.pdAlertUrl,
51
+ firedAt: r.firedAt,
52
+ implicatedFile: r.implicatedFile,
53
+ implicatedLine: r.implicatedLine,
54
+ attributionConfidence: r.attributionConfidence,
55
+ attributionSha: r.attributionSha ? r.attributionSha.slice(0, 8) : null,
56
+ traceId: r.traceId
57
+ }));
58
+ res.json({
59
+ ok: true,
60
+ activeIncident: active ? {
61
+ service: active.service,
62
+ alertTitle: active.alertTitle,
63
+ firedAt: active.firedAt,
64
+ blameConfidence: active.blameAttribution?.confidence ?? null,
65
+ blameLabel: active.blameAttribution?.confidenceLabel ?? null,
66
+ blameSha: active.blameAttribution?.topCandidate?.sha.slice(0, 8) ?? null,
67
+ blameExplanation: active.blameAttribution?.explanation ?? null
68
+ } : null,
69
+ openIncidents: openEnriched,
70
+ blastRadius: {
71
+ affectedSessions: blastRadius.affectedSessions,
72
+ affectedUsers: blastRadius.affectedUsers,
73
+ errorCount: blastRadius.errorCount,
74
+ firstSeenAt: blastRadius.firstSeenAt,
75
+ durationMs: blastRadius.durationMs,
76
+ browserSegments: blastRadius.browserSegments,
77
+ topErrors: blastRadius.topErrors.slice(0, 3)
78
+ },
79
+ mttrHistory: resolved,
80
+ attributionAccuracy: accuracy
81
+ });
82
+ });
83
+ router.get("/attribution-feedback", (req, res) => {
84
+ const id = parseInt(String(req.query.id ?? ""), 10);
85
+ const correct = req.query.correct === "1";
86
+ const token = String(req.query.token ?? "");
87
+ if (isNaN(id)) {
88
+ res.status(400).send("Invalid id");
89
+ return;
90
+ }
91
+ if (!verifyFeedbackToken(id, correct ? 1 : 0, token)) {
92
+ logger.warn({ id, correct }, "attribution-feedback: invalid or missing token \u2014 rejected");
93
+ res.status(403).send("Invalid or expired feedback link");
94
+ return;
95
+ }
96
+ memoryStore.recordAttributionFeedback(id, correct ? 1 : 0);
97
+ logger.info({ id, correct }, "attribution feedback via link");
98
+ res.send(`<!DOCTYPE html>
99
+ <html lang="en">
100
+ <head><meta charset="UTF-8"><title>Mergen \u2014 Feedback</title>
101
+ <style>body{font-family:system-ui;text-align:center;padding:60px;background:#0f1117;color:#e2e8f0}</style>
102
+ </head>
103
+ <body>
104
+ <h2 style="color:${correct ? "#22c55e" : "#ef4444"}">${correct ? "\u2705 Attribution marked correct" : "\u274C Attribution marked incorrect"}</h2>
105
+ <p style="color:#64748b;margin-top:12px">Feedback stored. Mergen's accuracy improves with every validated incident.</p>
106
+ <p style="margin-top:24px"><a href="/dashboard" style="color:#3b82f6">Back to war room</a></p>
107
+ </body></html>`);
108
+ });
109
+ return router;
110
+ }
111
+ export {
112
+ createWarRoomRouter
113
+ };
@@ -0,0 +1,22 @@
1
+ import path from "path";
2
+ import { checkBoundaries, formatBoundaryReport } from "../intelligence/arch-boundaries.js";
3
+ const args = process.argv.slice(2);
4
+ const srcDirFlag = args.indexOf("--srcDir");
5
+ const srcDir = srcDirFlag !== -1 && args[srcDirFlag + 1] ? args[srcDirFlag + 1] : path.resolve(process.cwd(), "src");
6
+ console.log(`
7
+ Checking architectural boundaries in: ${srcDir}
8
+ `);
9
+ const result = checkBoundaries({ srcDir });
10
+ const report = formatBoundaryReport(result, srcDir);
11
+ console.log(report);
12
+ if (result.violations.length > 0) {
13
+ console.error(`
14
+ \u2716 ${result.violations.length} violation(s) found. Fix before committing.
15
+ `);
16
+ process.exit(1);
17
+ } else {
18
+ console.log(`
19
+ \u2714 ${result.filesChecked} files checked. No violations.
20
+ `);
21
+ process.exit(0);
22
+ }
@@ -0,0 +1,176 @@
1
+ import { importOverrides } from "../intelligence/override-corpus.js";
2
+ import logger from "../sensor/logger.js";
3
+ const COMMUNITY_ENTRIES = [
4
+ // ── Friday settlement window ──────────────────────────────────────────────
5
+ {
6
+ incidentTag: "infra_db_connection_pool",
7
+ proposedCommand: "ALTER SYSTEM SET max_connections = 300",
8
+ overrideReason: "compliance-hold",
9
+ rationale: "Friday settlement window \u2014 pool resize requires DBA sign-off and is unsafe during batch job window (Fri 14:00\u201324:00 UTC)",
10
+ service: "postgres",
11
+ environment: "production",
12
+ dayOfWeek: 5,
13
+ // Friday
14
+ hourOfDay: 14,
15
+ actor: "community"
16
+ },
17
+ {
18
+ incidentTag: "infra_db_connection_pool",
19
+ proposedCommand: "ALTER SYSTEM SET max_connections = 300",
20
+ overrideReason: "compliance-hold",
21
+ rationale: "Friday settlement window late \u2014 do not resize DB connections after 18:00 UTC Friday",
22
+ service: "postgres",
23
+ environment: "production",
24
+ dayOfWeek: 5,
25
+ hourOfDay: 18,
26
+ actor: "community"
27
+ },
28
+ // ── Schema mutations during peak traffic ──────────────────────────────────
29
+ {
30
+ incidentTag: "infra_slow_query",
31
+ proposedCommand: "CREATE INDEX CONCURRENTLY idx_users_email ON users(email)",
32
+ overrideReason: "batch-window",
33
+ rationale: "Index builds compete with peak traffic queries Mon\u2013Fri 08:00\u201318:00 UTC \u2014 run during off-hours or maintenance window",
34
+ service: "postgres",
35
+ environment: "production",
36
+ dayOfWeek: 1,
37
+ // Monday
38
+ hourOfDay: 9,
39
+ actor: "community"
40
+ },
41
+ {
42
+ incidentTag: "infra_slow_query",
43
+ proposedCommand: "CREATE INDEX CONCURRENTLY idx_users_email ON users(email)",
44
+ overrideReason: "batch-window",
45
+ rationale: "Index builds during mid-day peak \u2014 defer to overnight maintenance window",
46
+ service: "postgres",
47
+ environment: "production",
48
+ dayOfWeek: 3,
49
+ // Wednesday
50
+ hourOfDay: 14,
51
+ actor: "community"
52
+ },
53
+ // ── Redis cache flush during active sessions ───────────────────────────────
54
+ {
55
+ incidentTag: "infra_service_unavailable",
56
+ proposedCommand: "redis-cli FLUSHDB",
57
+ overrideReason: "on-call-discretion",
58
+ rationale: "Flushing Redis during business hours logs out all active users \u2014 coordinate with support team and post change notice first",
59
+ service: "redis",
60
+ environment: "production",
61
+ dayOfWeek: 2,
62
+ // Tuesday
63
+ hourOfDay: 10,
64
+ actor: "community"
65
+ },
66
+ // ── Weekend batch window auto-scaling block ───────────────────────────────
67
+ {
68
+ incidentTag: "infra_queue_backlog",
69
+ proposedCommand: "kubectl scale deployment/worker --replicas=20",
70
+ overrideReason: "batch-window",
71
+ rationale: "Weekend batch window \u2014 worker auto-scaling competes with scheduled ETL jobs Sat\u2013Sun; coordinate with data team before scaling",
72
+ service: "worker",
73
+ environment: "production",
74
+ dayOfWeek: 6,
75
+ // Saturday
76
+ hourOfDay: 2,
77
+ actor: "community"
78
+ },
79
+ {
80
+ incidentTag: "infra_queue_backlog",
81
+ proposedCommand: "kubectl scale deployment/worker --replicas=20",
82
+ overrideReason: "batch-window",
83
+ rationale: "Sunday batch window \u2014 do not auto-scale workers during Sunday ETL window (00:00\u201306:00 UTC)",
84
+ service: "worker",
85
+ environment: "production",
86
+ dayOfWeek: 0,
87
+ // Sunday
88
+ hourOfDay: 3,
89
+ actor: "community"
90
+ },
91
+ // ── Database credential rotation ──────────────────────────────────────────
92
+ {
93
+ incidentTag: "infra_service_unavailable",
94
+ proposedCommand: "aws secretsmanager rotate-secret --secret-id prod/db/password",
95
+ overrideReason: "compliance-hold",
96
+ rationale: "DB credential rotation requires CAB approval and a coordinated rolling restart \u2014 cannot be done autonomously without change ticket",
97
+ service: "secrets-manager",
98
+ environment: "production",
99
+ dayOfWeek: 4,
100
+ // Thursday
101
+ hourOfDay: 15,
102
+ actor: "community"
103
+ },
104
+ // ── Direct production migration block ────────────────────────────────────
105
+ {
106
+ incidentTag: "infra_slow_query",
107
+ proposedCommand: "prisma migrate deploy",
108
+ overrideReason: "compliance-hold",
109
+ rationale: "Production migrations must run via CI pipeline with rollback plan \u2014 direct deploy bypasses peer review and breaks audit trail",
110
+ service: "api",
111
+ environment: "production",
112
+ dayOfWeek: 1,
113
+ hourOfDay: 11,
114
+ actor: "community"
115
+ },
116
+ // ── OOM restart block during payment processing ──────────────────────────
117
+ {
118
+ incidentTag: "infra_oom_kill",
119
+ proposedCommand: "kubectl rollout restart deployment/payments",
120
+ overrideReason: "on-call-discretion",
121
+ rationale: "Payments service restart during checkout window drops in-flight transactions \u2014 drain first, then restart after traffic drops below 20 req/s",
122
+ service: "payments",
123
+ environment: "production",
124
+ dayOfWeek: 5,
125
+ // Friday
126
+ hourOfDay: 17,
127
+ actor: "community"
128
+ },
129
+ // ── Rate limit cascade: do not retry immediately ─────────────────────────
130
+ {
131
+ incidentTag: "infra_rate_limit_cascade",
132
+ proposedCommand: "kubectl scale deployment/api --replicas=10",
133
+ overrideReason: "cost-constraint",
134
+ rationale: "Scaling API pods does not fix upstream rate limit cascades \u2014 it amplifies the retry storm; fix the backoff logic instead",
135
+ service: "api",
136
+ environment: "production",
137
+ dayOfWeek: 2,
138
+ hourOfDay: 8,
139
+ actor: "community"
140
+ },
141
+ // ── Disk pressure: do not delete pg_wal without DBA approval ─────────────
142
+ {
143
+ incidentTag: "infra_disk_pressure",
144
+ proposedCommand: "find /var/lib/postgresql/data/pg_wal -mtime +7 -delete",
145
+ overrideReason: "compliance-hold",
146
+ rationale: "Deleting WAL segments without verifying replication lag can break standby replicas \u2014 always check pg_stat_replication first, then get DBA approval",
147
+ service: "postgres",
148
+ environment: "production",
149
+ dayOfWeek: 3,
150
+ hourOfDay: 20,
151
+ actor: "community"
152
+ }
153
+ ];
154
+ function loadCommunityCorpus() {
155
+ try {
156
+ const { imported, skipped } = importOverrides(
157
+ COMMUNITY_ENTRIES.map(({ actor: _actor, ...entry }) => ({
158
+ ...entry,
159
+ manualAction: "Deferred \u2014 see rationale"
160
+ })),
161
+ { source: "community", actor: "community" }
162
+ );
163
+ if (imported > 0) {
164
+ logger.info({ loaded: imported, skipped }, "community-corpus: seeded");
165
+ }
166
+ return { loaded: imported, skipped };
167
+ } catch (err) {
168
+ logger.warn({ err }, "community-corpus: failed to seed");
169
+ return { loaded: 0, skipped: COMMUNITY_ENTRIES.length };
170
+ }
171
+ }
172
+ const COMMUNITY_CORPUS_COUNT = COMMUNITY_ENTRIES.length;
173
+ export {
174
+ COMMUNITY_CORPUS_COUNT,
175
+ loadCommunityCorpus
176
+ };