openpitstop 1.0.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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +512 -0
  3. package/demo-repo/README.md +24 -0
  4. package/demo-repo/package-lock.json +4483 -0
  5. package/demo-repo/package.json +17 -0
  6. package/demo-repo/src/config.js +5 -0
  7. package/demo-repo/src/db.js +31 -0
  8. package/demo-repo/src/server.js +13 -0
  9. package/demo-repo/src/userController.js +22 -0
  10. package/demo-repo/src/userRepo.js +18 -0
  11. package/demo-repo/src/userService.js +59 -0
  12. package/demo-repo/test/user.test.js +12 -0
  13. package/demo-repo-fintech/README.md +27 -0
  14. package/demo-repo-fintech/package-lock.json +1023 -0
  15. package/demo-repo-fintech/package.json +14 -0
  16. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
  17. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
  18. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
  19. package/demo-repo-fintech/src/gateway.js +26 -0
  20. package/demo-repo-fintech/src/server.js +61 -0
  21. package/demo-repo-generators/package-lock.json +1508 -0
  22. package/demo-repo-generators/package.json +17 -0
  23. package/demo-repo-generators/public/index.html +10 -0
  24. package/demo-repo-generators/scripts/build.js +17 -0
  25. package/demo-repo-generators/src/counter.js +16 -0
  26. package/demo-repo-integrity/README.md +49 -0
  27. package/demo-repo-integrity/package.json +12 -0
  28. package/demo-repo-integrity/src/money.js +19 -0
  29. package/demo-repo-integrity/test/money.test.js +17 -0
  30. package/dist/analyzers/accessibility.d.ts +18 -0
  31. package/dist/analyzers/accessibility.js +248 -0
  32. package/dist/analyzers/accessibility.js.map +1 -0
  33. package/dist/analyzers/dependencyGraph.d.ts +14 -0
  34. package/dist/analyzers/dependencyGraph.js +206 -0
  35. package/dist/analyzers/dependencyGraph.js.map +1 -0
  36. package/dist/analyzers/devex.d.ts +19 -0
  37. package/dist/analyzers/devex.js +301 -0
  38. package/dist/analyzers/devex.js.map +1 -0
  39. package/dist/analyzers/duplication.d.ts +2 -0
  40. package/dist/analyzers/duplication.js +47 -0
  41. package/dist/analyzers/duplication.js.map +1 -0
  42. package/dist/analyzers/index.d.ts +12 -0
  43. package/dist/analyzers/index.js +38 -0
  44. package/dist/analyzers/index.js.map +1 -0
  45. package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
  46. package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
  47. package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
  48. package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
  49. package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
  50. package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
  51. package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
  52. package/dist/analyzers/integrity/exitCheat.js +31 -0
  53. package/dist/analyzers/integrity/exitCheat.js.map +1 -0
  54. package/dist/analyzers/integrity/git.d.ts +10 -0
  55. package/dist/analyzers/integrity/git.js +181 -0
  56. package/dist/analyzers/integrity/git.js.map +1 -0
  57. package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
  58. package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
  59. package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
  60. package/dist/analyzers/integrity/helpers.d.ts +21 -0
  61. package/dist/analyzers/integrity/helpers.js +66 -0
  62. package/dist/analyzers/integrity/helpers.js.map +1 -0
  63. package/dist/analyzers/integrity/index.d.ts +10 -0
  64. package/dist/analyzers/integrity/index.js +27 -0
  65. package/dist/analyzers/integrity/index.js.map +1 -0
  66. package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
  67. package/dist/analyzers/integrity/mockOverreach.js +72 -0
  68. package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
  69. package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
  70. package/dist/analyzers/integrity/suppressionCreep.js +27 -0
  71. package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
  72. package/dist/analyzers/integrity/testTamper.d.ts +2 -0
  73. package/dist/analyzers/integrity/testTamper.js +153 -0
  74. package/dist/analyzers/integrity/testTamper.js.map +1 -0
  75. package/dist/analyzers/integrity/types.d.ts +63 -0
  76. package/dist/analyzers/integrity/types.js +13 -0
  77. package/dist/analyzers/integrity/types.js.map +1 -0
  78. package/dist/analyzers/ledger/attacks.d.ts +17 -0
  79. package/dist/analyzers/ledger/attacks.js +135 -0
  80. package/dist/analyzers/ledger/attacks.js.map +1 -0
  81. package/dist/analyzers/ledger/discover.d.ts +15 -0
  82. package/dist/analyzers/ledger/discover.js +122 -0
  83. package/dist/analyzers/ledger/discover.js.map +1 -0
  84. package/dist/analyzers/ledger/evidence.d.ts +22 -0
  85. package/dist/analyzers/ledger/evidence.js +127 -0
  86. package/dist/analyzers/ledger/evidence.js.map +1 -0
  87. package/dist/analyzers/ledger/harness.d.ts +4 -0
  88. package/dist/analyzers/ledger/harness.js +270 -0
  89. package/dist/analyzers/ledger/harness.js.map +1 -0
  90. package/dist/analyzers/ledger/index.d.ts +14 -0
  91. package/dist/analyzers/ledger/index.js +116 -0
  92. package/dist/analyzers/ledger/index.js.map +1 -0
  93. package/dist/analyzers/ledger/types.d.ts +31 -0
  94. package/dist/analyzers/ledger/types.js +2 -0
  95. package/dist/analyzers/ledger/types.js.map +1 -0
  96. package/dist/analyzers/perf.d.ts +2 -0
  97. package/dist/analyzers/perf.js +106 -0
  98. package/dist/analyzers/perf.js.map +1 -0
  99. package/dist/analyzers/reliability.d.ts +22 -0
  100. package/dist/analyzers/reliability.js +339 -0
  101. package/dist/analyzers/reliability.js.map +1 -0
  102. package/dist/analyzers/routes.d.ts +17 -0
  103. package/dist/analyzers/routes.js +115 -0
  104. package/dist/analyzers/routes.js.map +1 -0
  105. package/dist/analyzers/security.d.ts +2 -0
  106. package/dist/analyzers/security.js +302 -0
  107. package/dist/analyzers/security.js.map +1 -0
  108. package/dist/analyzers/suiteRunner.d.ts +64 -0
  109. package/dist/analyzers/suiteRunner.js +381 -0
  110. package/dist/analyzers/suiteRunner.js.map +1 -0
  111. package/dist/analyzers/tests.d.ts +2 -0
  112. package/dist/analyzers/tests.js +215 -0
  113. package/dist/analyzers/tests.js.map +1 -0
  114. package/dist/analyzers/types.d.ts +203 -0
  115. package/dist/analyzers/types.js +2 -0
  116. package/dist/analyzers/types.js.map +1 -0
  117. package/dist/analyzers/util.d.ts +35 -0
  118. package/dist/analyzers/util.js +209 -0
  119. package/dist/analyzers/util.js.map +1 -0
  120. package/dist/ciCheckA.d.ts +2 -0
  121. package/dist/ciCheckA.js +5 -0
  122. package/dist/ciCheckA.js.map +1 -0
  123. package/dist/ciCheckB.d.ts +2 -0
  124. package/dist/ciCheckB.js +6 -0
  125. package/dist/ciCheckB.js.map +1 -0
  126. package/dist/cli.d.ts +2 -0
  127. package/dist/cli.js +65 -0
  128. package/dist/cli.js.map +1 -0
  129. package/dist/commands/budget.d.ts +33 -0
  130. package/dist/commands/budget.js +108 -0
  131. package/dist/commands/budget.js.map +1 -0
  132. package/dist/commands/ci.d.ts +14 -0
  133. package/dist/commands/ci.js +157 -0
  134. package/dist/commands/ci.js.map +1 -0
  135. package/dist/commands/demo.d.ts +2 -0
  136. package/dist/commands/demo.js +163 -0
  137. package/dist/commands/demo.js.map +1 -0
  138. package/dist/commands/digest.d.ts +8 -0
  139. package/dist/commands/digest.js +189 -0
  140. package/dist/commands/digest.js.map +1 -0
  141. package/dist/commands/doctor.d.ts +2 -0
  142. package/dist/commands/doctor.js +98 -0
  143. package/dist/commands/doctor.js.map +1 -0
  144. package/dist/commands/drive.d.ts +3 -0
  145. package/dist/commands/drive.js +170 -0
  146. package/dist/commands/drive.js.map +1 -0
  147. package/dist/commands/gate.d.ts +2 -0
  148. package/dist/commands/gate.js +136 -0
  149. package/dist/commands/gate.js.map +1 -0
  150. package/dist/commands/honesty.d.ts +2 -0
  151. package/dist/commands/honesty.js +182 -0
  152. package/dist/commands/honesty.js.map +1 -0
  153. package/dist/commands/inspect.d.ts +2 -0
  154. package/dist/commands/inspect.js +244 -0
  155. package/dist/commands/inspect.js.map +1 -0
  156. package/dist/commands/install.d.ts +2 -0
  157. package/dist/commands/install.js +81 -0
  158. package/dist/commands/install.js.map +1 -0
  159. package/dist/commands/integrity.d.ts +2 -0
  160. package/dist/commands/integrity.js +74 -0
  161. package/dist/commands/integrity.js.map +1 -0
  162. package/dist/commands/memory.d.ts +6 -0
  163. package/dist/commands/memory.js +90 -0
  164. package/dist/commands/memory.js.map +1 -0
  165. package/dist/commands/pen.d.ts +17 -0
  166. package/dist/commands/pen.js +158 -0
  167. package/dist/commands/pen.js.map +1 -0
  168. package/dist/commands/prompt.d.ts +2 -0
  169. package/dist/commands/prompt.js +37 -0
  170. package/dist/commands/prompt.js.map +1 -0
  171. package/dist/commands/readyCheck.d.ts +21 -0
  172. package/dist/commands/readyCheck.js +68 -0
  173. package/dist/commands/readyCheck.js.map +1 -0
  174. package/dist/commands/report.d.ts +2 -0
  175. package/dist/commands/report.js +48 -0
  176. package/dist/commands/report.js.map +1 -0
  177. package/dist/commands/repro.d.ts +3 -0
  178. package/dist/commands/repro.js +61 -0
  179. package/dist/commands/repro.js.map +1 -0
  180. package/dist/commands/scan.d.ts +27 -0
  181. package/dist/commands/scan.js +348 -0
  182. package/dist/commands/scan.js.map +1 -0
  183. package/dist/commands/share.d.ts +2 -0
  184. package/dist/commands/share.js +163 -0
  185. package/dist/commands/share.js.map +1 -0
  186. package/dist/commands/trends.d.ts +2 -0
  187. package/dist/commands/trends.js +103 -0
  188. package/dist/commands/trends.js.map +1 -0
  189. package/dist/commands/try.d.ts +12 -0
  190. package/dist/commands/try.js +208 -0
  191. package/dist/commands/try.js.map +1 -0
  192. package/dist/commands/verify.d.ts +44 -0
  193. package/dist/commands/verify.js +381 -0
  194. package/dist/commands/verify.js.map +1 -0
  195. package/dist/commands/watch.d.ts +10 -0
  196. package/dist/commands/watch.js +70 -0
  197. package/dist/commands/watch.js.map +1 -0
  198. package/dist/evidence.d.ts +37 -0
  199. package/dist/evidence.js +61 -0
  200. package/dist/evidence.js.map +1 -0
  201. package/dist/graph/correlate.d.ts +7 -0
  202. package/dist/graph/correlate.js +246 -0
  203. package/dist/graph/correlate.js.map +1 -0
  204. package/dist/graph/integrity.d.ts +22 -0
  205. package/dist/graph/integrity.js +39 -0
  206. package/dist/graph/integrity.js.map +1 -0
  207. package/dist/installer/targets.d.ts +23 -0
  208. package/dist/installer/targets.js +162 -0
  209. package/dist/installer/targets.js.map +1 -0
  210. package/dist/memory/store.d.ts +33 -0
  211. package/dist/memory/store.js +131 -0
  212. package/dist/memory/store.js.map +1 -0
  213. package/dist/pen/dynamic.d.ts +36 -0
  214. package/dist/pen/dynamic.js +622 -0
  215. package/dist/pen/dynamic.js.map +1 -0
  216. package/dist/pen/fix.d.ts +35 -0
  217. package/dist/pen/fix.js +239 -0
  218. package/dist/pen/fix.js.map +1 -0
  219. package/dist/pen/proof.d.ts +46 -0
  220. package/dist/pen/proof.js +112 -0
  221. package/dist/pen/proof.js.map +1 -0
  222. package/dist/pen/report.d.ts +11 -0
  223. package/dist/pen/report.js +258 -0
  224. package/dist/pen/report.js.map +1 -0
  225. package/dist/pen/static.d.ts +17 -0
  226. package/dist/pen/static.js +565 -0
  227. package/dist/pen/static.js.map +1 -0
  228. package/dist/pen/store.d.ts +11 -0
  229. package/dist/pen/store.js +36 -0
  230. package/dist/pen/store.js.map +1 -0
  231. package/dist/pen/types.d.ts +112 -0
  232. package/dist/pen/types.js +53 -0
  233. package/dist/pen/types.js.map +1 -0
  234. package/dist/report/format.d.ts +126 -0
  235. package/dist/report/format.js +706 -0
  236. package/dist/report/format.js.map +1 -0
  237. package/dist/report/score.d.ts +47 -0
  238. package/dist/report/score.js +164 -0
  239. package/dist/report/score.js.map +1 -0
  240. package/dist/repro/framework.d.ts +26 -0
  241. package/dist/repro/framework.js +224 -0
  242. package/dist/repro/framework.js.map +1 -0
  243. package/dist/repro/generate.d.ts +14 -0
  244. package/dist/repro/generate.js +669 -0
  245. package/dist/repro/generate.js.map +1 -0
  246. package/dist/repro/ids.d.ts +32 -0
  247. package/dist/repro/ids.js +175 -0
  248. package/dist/repro/ids.js.map +1 -0
  249. package/dist/repro/index.d.ts +24 -0
  250. package/dist/repro/index.js +69 -0
  251. package/dist/repro/index.js.map +1 -0
  252. package/dist/repro/pen.d.ts +26 -0
  253. package/dist/repro/pen.js +321 -0
  254. package/dist/repro/pen.js.map +1 -0
  255. package/dist/repro/run.d.ts +18 -0
  256. package/dist/repro/run.js +36 -0
  257. package/dist/repro/run.js.map +1 -0
  258. package/dist/sandbox/proxy.d.ts +41 -0
  259. package/dist/sandbox/proxy.js +231 -0
  260. package/dist/sandbox/proxy.js.map +1 -0
  261. package/dist/sandbox/startCmd.d.ts +24 -0
  262. package/dist/sandbox/startCmd.js +71 -0
  263. package/dist/sandbox/startCmd.js.map +1 -0
  264. package/dist/ui/spinner.d.ts +18 -0
  265. package/dist/ui/spinner.js +26 -0
  266. package/dist/ui/spinner.js.map +1 -0
  267. package/dist/verify/metrics.d.ts +13 -0
  268. package/dist/verify/metrics.js +72 -0
  269. package/dist/verify/metrics.js.map +1 -0
  270. package/package.json +68 -0
  271. package/scripts/cheat-demo.cjs +102 -0
  272. package/templates/ledger/preload.cjs +331 -0
  273. package/templates/pen/preload.cjs +196 -0
  274. package/templates/pitstop.prompt.md +371 -0
@@ -0,0 +1,246 @@
1
+ import path from "node:path";
2
+ import { buildEdges } from "../analyzers/dependencyGraph.js";
3
+ import { findingIdFor, ledgerFindingId } from "../repro/ids.js";
4
+ /**
5
+ * HEURISTIC (not magic):
6
+ * We treat every Phase-1 output that points at one or more files as a flat
7
+ * "finding". Findings are correlated when they touch the same file, or files
8
+ * that are within 1-2 hops of each other in the dependency graph (so e.g. a
9
+ * buggy leaf and its only caller group together). Within a correlated group we
10
+ * guess the root cause as the highest-scoring finding, where score combines
11
+ * severity with graph centrality: a file many other files depend on is more
12
+ * likely to be the root cause than a leaf file. This is a best-effort guess and
13
+ * is clearly labeled as such in the popup.
14
+ */
15
+ const SEVERITY_RANK = {
16
+ critical: 5,
17
+ high: 4,
18
+ severe: 4,
19
+ error: 4,
20
+ medium: 3,
21
+ moderate: 3,
22
+ warning: 2,
23
+ low: 2,
24
+ info: 1,
25
+ unknown: 2,
26
+ };
27
+ function severityRank(s) {
28
+ return SEVERITY_RANK[s?.toLowerCase()] ?? 2;
29
+ }
30
+ /** Flatten Phase-1 results into a list of file-based findings. */
31
+ export function collectFindings(repo, r) {
32
+ const findings = [];
33
+ for (const i of r.security.issues) {
34
+ findings.push({
35
+ id: findingIdFor("security", i.type, i.file, i.description),
36
+ source: "security",
37
+ severity: i.severity,
38
+ type: i.type,
39
+ description: i.description,
40
+ files: i.file ? [path.relative(repo, i.file)] : [],
41
+ });
42
+ }
43
+ for (const c of r.duplication.clones) {
44
+ findings.push({
45
+ id: findingIdFor("duplication", "duplication", c.files[0], `${c.lines}`),
46
+ source: "duplication",
47
+ severity: "low",
48
+ type: "duplication",
49
+ description: `duplicate code (${c.lines} lines)`,
50
+ files: c.files.map((f) => path.relative(repo, f)),
51
+ });
52
+ }
53
+ for (const cyc of r.dependencyGraph.circular) {
54
+ const files = cyc.map((f) => path.relative(repo, f));
55
+ findings.push({
56
+ id: findingIdFor("graph", "circular", cyc[0], files.join(" → ")),
57
+ source: "graph",
58
+ severity: "medium",
59
+ type: "circular",
60
+ description: `circular dependency: ${files.join(" → ")}`,
61
+ files,
62
+ });
63
+ }
64
+ for (const i of r.accessibility.issues) {
65
+ findings.push({
66
+ id: findingIdFor("a11y", i.type, i.file, i.description),
67
+ source: "a11y",
68
+ severity: i.severity,
69
+ type: "a11y",
70
+ description: i.description,
71
+ files: i.file ? [path.relative(repo, i.file)] : [],
72
+ });
73
+ }
74
+ for (const f of r.reliability.flakyTests) {
75
+ findings.push({
76
+ id: findingIdFor("reliability", "flaky-test", f.file, f.name),
77
+ source: "reliability",
78
+ severity: "warning",
79
+ type: "flaky-test",
80
+ description: `flaky test (outcome changed across ${r.reliability.runs} runs): ${f.name}`,
81
+ files: f.file ? [path.relative(repo, f.file)] : [],
82
+ });
83
+ }
84
+ for (const i of r.reliability.raceSmells) {
85
+ findings.push({
86
+ id: findingIdFor("reliability", "race-condition", i.file, i.description),
87
+ source: "reliability",
88
+ severity: i.severity,
89
+ type: "race-condition",
90
+ description: i.description,
91
+ files: i.file ? [path.relative(repo, i.file)] : [],
92
+ });
93
+ }
94
+ for (const i of r.devex.unusedExports) {
95
+ findings.push({
96
+ id: findingIdFor("devex", "unused-export", i.file, i.description),
97
+ source: "devex",
98
+ severity: i.severity,
99
+ type: "unused-export",
100
+ description: i.description,
101
+ files: i.file ? [path.relative(repo, i.file)] : [],
102
+ });
103
+ }
104
+ for (const d of r.devex.duplicateFunctions) {
105
+ findings.push({
106
+ id: findingIdFor("devex", "duplicate-function", d.files[0]?.file, `${d.name}@${d.files[0]?.file}`),
107
+ source: "devex",
108
+ severity: "medium",
109
+ type: "duplicate-function",
110
+ description: `near-identical function bodies (${(d.similarity * 100).toFixed(0)}% similar, ${d.lines} lines): ${d.name} in ${d.files
111
+ .map((f) => path.basename(f.file))
112
+ .join(" vs ")}`,
113
+ files: d.files.map((f) => path.relative(repo, f.file)),
114
+ });
115
+ }
116
+ return findings;
117
+ }
118
+ export function correlate(repo, r) {
119
+ const findings = collectFindings(repo, r);
120
+ const built = buildEdges(repo);
121
+ const edges = built?.edges ?? new Map();
122
+ // Centrality = in-degree in the dependency graph.
123
+ const inDegree = new Map();
124
+ for (const deps of edges.values()) {
125
+ for (const d of deps)
126
+ inDegree.set(d, (inDegree.get(d) ?? 0) + 1);
127
+ }
128
+ // Undirected adjacency for hop-distance checks.
129
+ const adj = new Map();
130
+ const link = (a, b) => {
131
+ if (!adj.has(a))
132
+ adj.set(a, new Set());
133
+ adj.get(a).add(b);
134
+ };
135
+ for (const [u, deps] of edges) {
136
+ link(u, u);
137
+ for (const v of deps) {
138
+ link(u, v);
139
+ link(v, u);
140
+ }
141
+ }
142
+ const resolve = (f) => path.resolve(repo, f);
143
+ const distance = (a, b) => {
144
+ if (!adj.has(a) || !adj.has(b))
145
+ return Infinity;
146
+ if (a === b)
147
+ return 0;
148
+ const q = [[a, 0]];
149
+ const seen = new Set([a]);
150
+ while (q.length) {
151
+ const [n, d] = q.shift();
152
+ for (const nb of adj.get(n) ?? []) {
153
+ if (nb === b)
154
+ return d + 1;
155
+ if (!seen.has(nb)) {
156
+ seen.add(nb);
157
+ q.push([nb, d + 1]);
158
+ }
159
+ }
160
+ }
161
+ return Infinity;
162
+ };
163
+ const centrality = (f) => Math.max(0, ...f.files.map((fl) => inDegree.get(resolve(fl)) ?? 0));
164
+ const score = (f) => severityRank(f.severity) * 10 + centrality(f);
165
+ const correlated = (a, b) => {
166
+ if (a.files.some((f) => b.files.includes(f)))
167
+ return true;
168
+ for (const fa of a.files) {
169
+ for (const fb of b.files) {
170
+ if (distance(resolve(fa), resolve(fb)) <= 2)
171
+ return true;
172
+ }
173
+ }
174
+ return false;
175
+ };
176
+ // Union-find clustering over the findings.
177
+ const parent = findings.map((_, i) => i);
178
+ const find = (x) => {
179
+ while (parent[x] !== x) {
180
+ parent[x] = parent[parent[x]];
181
+ x = parent[x];
182
+ }
183
+ return x;
184
+ };
185
+ const union = (a, b) => {
186
+ parent[find(a)] = find(b);
187
+ };
188
+ for (let i = 0; i < findings.length; i++) {
189
+ for (let j = i + 1; j < findings.length; j++) {
190
+ if (correlated(findings[i], findings[j]))
191
+ union(i, j);
192
+ }
193
+ }
194
+ const groups = new Map();
195
+ for (let i = 0; i < findings.length; i++) {
196
+ const root = find(i);
197
+ if (!groups.has(root))
198
+ groups.set(root, []);
199
+ groups.get(root).push(i);
200
+ }
201
+ const clusters = [];
202
+ for (const idxs of groups.values()) {
203
+ if (idxs.length < 2)
204
+ continue;
205
+ const group = idxs.map((i) => findings[i]);
206
+ // Root cause = highest score (severity + centrality). Ties broken by order.
207
+ let root = group[0];
208
+ for (const f of group)
209
+ if (score(f) > score(root))
210
+ root = f;
211
+ const symptoms = group.filter((f) => f !== root);
212
+ const sharedFiles = [...new Set(group.flatMap((f) => f.files))];
213
+ clusters.push({ rootCause: root, symptoms, sharedFiles, size: group.length });
214
+ }
215
+ clusters.sort((a, b) => b.size - a.size);
216
+ // A PROVEN double-charge from ledger mode is always the top-ranked cluster.
217
+ // Ledger findings are never merged into the heuristic clusters: they are
218
+ // observed facts (the mocked gateway was actually hit twice), not guesses, so
219
+ // they lead the report unconditionally.
220
+ const ledgerClusters = buildLedgerClusters(repo, r);
221
+ if (ledgerClusters.length > 0)
222
+ clusters.unshift(...ledgerClusters);
223
+ return { clusters, findings };
224
+ }
225
+ function buildLedgerClusters(repo, r) {
226
+ if (!r.ledger || r.ledger.evidence.length === 0)
227
+ return [];
228
+ return r.ledger.evidence.map((ev) => {
229
+ const files = ev.endpointFile ? [ev.endpointFile] : [];
230
+ const rootCause = {
231
+ id: ledgerFindingId(ev),
232
+ source: "ledger",
233
+ severity: "critical",
234
+ type: "proven-double-charge",
235
+ description: ev.summary,
236
+ files,
237
+ };
238
+ return {
239
+ rootCause,
240
+ symptoms: [],
241
+ sharedFiles: files,
242
+ size: 1,
243
+ };
244
+ });
245
+ }
246
+ //# sourceMappingURL=correlate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlate.js","sourceRoot":"","sources":["../../src/graph/correlate.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE;;;;;;;;;;GAUG;AAEH,MAAM,aAAa,GAA2B;IAC5C,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,QAAQ,EAAE,CAAC;IACX,OAAO,EAAE,CAAC;IACV,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,CAAa;IACzD,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;YAC3D,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;YACxE,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE,mBAAmB,CAAC,CAAC,KAAK,SAAS;YAChD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;SAClD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,wBAAwB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACxD,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;YACvD,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,MAAM;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;YAC7D,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,sCAAsC,CAAC,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,CAAC,IAAI,EAAE;YACxF,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,aAAa,EAAE,gBAAgB,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;YACxE,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,gBAAgB;YACtB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;YACjE,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,eAAe;YACrB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,CACd,OAAO,EACP,oBAAoB,EACpB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAChB,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAChC;YACD,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EAAE,mCAAmC,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK;iBACjI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;iBACjC,IAAI,CAAC,MAAM,CAAC,EAAE;YACjB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;SACvD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,CAAa;IAEb,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,IAAI,GAAG,EAAuB,CAAC;IAE7D,kDAAkD;IAClD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,gDAAgD;IAChD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;QACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACvC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACX,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACX,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE;QAChD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO,QAAQ,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACtB,MAAM,CAAC,GAAuB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAsB,CAAC;YAC7C,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBAClC,IAAI,EAAE,KAAK,CAAC;oBAAE,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAClB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACb,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,CAAiB,EAAU,EAAE,CAC/C,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEtE,MAAM,KAAK,GAAG,CAAC,CAAiB,EAAU,EAAE,CAC1C,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAEhD,MAAM,UAAU,GAAG,CAAC,CAAiB,EAAE,CAAiB,EAAW,EAAE;QACnE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1D,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACzB,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACzB,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YAC3D,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,2CAA2C;IAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE;QACjC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,4EAA4E;QAC5E,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAEzC,4EAA4E;IAC5E,yEAAyE;IACzE,8EAA8E;IAC9E,wCAAwC;IACxC,MAAM,cAAc,GAAG,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;QAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;IAEnE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY,EAAE,CAAa;IACtD,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAmB;YAChC,EAAE,EAAE,eAAe,CAAC,EAAE,CAAC;YACvB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,UAAU;YACpB,IAAI,EAAE,sBAAsB;YAC5B,WAAW,EAAE,EAAE,CAAC,OAAO;YACvB,KAAK;SACN,CAAC;QACF,OAAO;YACL,SAAS;YACT,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,KAAK;YAClB,IAAI,EAAE,CAAC;SACR,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { FileChange, IntegrityFinding, IntegrityReport, Verdict } from "../analyzers/integrity/types.js";
2
+ import { runDetectors } from "../analyzers/integrity/index.js";
3
+ /**
4
+ * Combine all detector output into a single integrity report with an overall
5
+ * verdict.
6
+ *
7
+ * CALIBRATION (the contract that keeps this feature trustworthy):
8
+ * - CLEAN : no detector fired.
9
+ * - SUSPICIOUS : only "suspicious" findings — human judgment required,
10
+ * NEVER auto-blocked.
11
+ * - CONFIRMED_CHEAT : at least one "confirmed" finding — auto-blocked, because
12
+ * the pattern is unambiguous (whole test file deleted, a
13
+ * whole test-case block removed, or process.exit(0) added
14
+ * inside application code).
15
+ *
16
+ * Everything that is merely suggestive stays "suspicious" so a legitimate
17
+ * refactor that updates a test (because the spec changed) surfaces with full
18
+ * evidence for a human to judge — it is never silently reverted.
19
+ */
20
+ export declare function buildIntegrityReport(repo: string, from: string, to: string, changes: FileChange[]): IntegrityReport;
21
+ export { runDetectors };
22
+ export type { IntegrityReport, Verdict, IntegrityFinding };
@@ -0,0 +1,39 @@
1
+ import { runDetectors } from "../analyzers/integrity/index.js";
2
+ /**
3
+ * Combine all detector output into a single integrity report with an overall
4
+ * verdict.
5
+ *
6
+ * CALIBRATION (the contract that keeps this feature trustworthy):
7
+ * - CLEAN : no detector fired.
8
+ * - SUSPICIOUS : only "suspicious" findings — human judgment required,
9
+ * NEVER auto-blocked.
10
+ * - CONFIRMED_CHEAT : at least one "confirmed" finding — auto-blocked, because
11
+ * the pattern is unambiguous (whole test file deleted, a
12
+ * whole test-case block removed, or process.exit(0) added
13
+ * inside application code).
14
+ *
15
+ * Everything that is merely suggestive stays "suspicious" so a legitimate
16
+ * refactor that updates a test (because the spec changed) surfaces with full
17
+ * evidence for a human to judge — it is never silently reverted.
18
+ */
19
+ export function buildIntegrityReport(repo, from, to, changes) {
20
+ const findings = runDetectors(changes);
21
+ const confirmed = findings.filter((f) => f.confidence === "confirmed").length;
22
+ const suspicious = findings.filter((f) => f.confidence === "suspicious").length;
23
+ let verdict = "CLEAN";
24
+ if (confirmed > 0)
25
+ verdict = "CONFIRMED_CHEAT";
26
+ else if (suspicious > 0)
27
+ verdict = "SUSPICIOUS";
28
+ return {
29
+ timestamp: new Date().toISOString(),
30
+ repo,
31
+ from,
32
+ to,
33
+ verdict,
34
+ findings,
35
+ summary: { confirmed, suspicious, total: findings.length },
36
+ };
37
+ }
38
+ export { runDetectors };
39
+ //# sourceMappingURL=integrity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/graph/integrity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,IAAY,EACZ,EAAU,EACV,OAAqB;IAErB,MAAM,QAAQ,GAAuB,YAAY,CAAC,OAAO,CAAC,CAAC;IAE3D,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IAC9E,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC;IAEhF,IAAI,OAAO,GAAY,OAAO,CAAC;IAC/B,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,GAAG,iBAAiB,CAAC;SAC1C,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,GAAG,YAAY,CAAC;IAEhD,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI;QACJ,IAAI;QACJ,EAAE;QACF,OAAO;QACP,QAAQ;QACR,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE;KAC3D,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface InstallTarget {
2
+ path: string;
3
+ tool: string;
4
+ level: "project" | "home";
5
+ transform?: "skill" | "workflow" | "gemini";
6
+ note?: string;
7
+ }
8
+ /** Absolute project-level targets, relative to the given repo/cwd. */
9
+ export declare function projectTargets(cwd: string): InstallTarget[];
10
+ /** Absolute user-level (home) targets. */
11
+ export declare function homeTargets(): InstallTarget[];
12
+ export declare function getTargets(cwd: string): InstallTarget[];
13
+ /** Locate templates/pitstop.prompt.md relative to this module or cwd. */
14
+ export declare function resolveTemplatePath(): string;
15
+ /**
16
+ * Render the file content for a target. The Claude Code skill variant rewrites
17
+ * the frontmatter to { name, description } as skills require. The Antigravity
18
+ * workflow variant normalizes the frontmatter to `description` only (their
19
+ * required field) and keeps the title + numbered steps body as-is. The Gemini
20
+ * CLI variant converts to TOML ({ description = "...", prompt = """ body """ }),
21
+ * the format .gemini/commands/*.toml files must use.
22
+ */
23
+ export declare function renderContent(target: InstallTarget, templateText: string): string;
@@ -0,0 +1,162 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ /** Absolute project-level targets, relative to the given repo/cwd. */
6
+ export function projectTargets(cwd) {
7
+ const p = (...parts) => path.resolve(cwd, ...parts);
8
+ return [
9
+ { path: p(".claude", "commands", "pitstop.md"), tool: "Claude Code (project)", level: "project" },
10
+ {
11
+ path: p(".claude", "skills", "pitstop", "SKILL.md"),
12
+ tool: "Claude Code Skill",
13
+ level: "project",
14
+ transform: "skill",
15
+ },
16
+ { path: p(".cursor", "commands", "pitstop.md"), tool: "Cursor (project)", level: "project" },
17
+ { path: p(".opencode", "commands", "pitstop.md"), tool: "OpenCode (project)", level: "project" },
18
+ {
19
+ path: p(".opencode", "command", "pitstop.md"),
20
+ tool: "OpenCode (project, legacy)",
21
+ level: "project",
22
+ },
23
+ // Antigravity workflows: markdown files in .agent/workflows/ (singular,
24
+ // current docs) with YAML frontmatter `description` + title + steps,
25
+ // invoked as /pitstop. Older versions/3rd-party guides use the plural
26
+ // .agents/workflows/ — write both, one is harmless dead weight.
27
+ {
28
+ path: p(".agent", "workflows", "pitstop.md"),
29
+ tool: "Antigravity (project)",
30
+ level: "project",
31
+ transform: "workflow",
32
+ },
33
+ {
34
+ path: p(".agents", "workflows", "pitstop.md"),
35
+ tool: "Antigravity (project, plural legacy)",
36
+ level: "project",
37
+ transform: "workflow",
38
+ },
39
+ // Kilo Code current docs: slash commands live in .kilo/commands/ (project)
40
+ // and ~/.config/kilo/commands/ (global). .kilocode/workflows/ is the LEGACY
41
+ // location the new extension auto-migrates on startup — kept for old builds.
42
+ { path: p(".kilo", "commands", "pitstop.md"), tool: "Kilo Code (project)", level: "project" },
43
+ {
44
+ path: p(".kilocode", "workflows", "pitstop.md"),
45
+ tool: "Kilo Code (project, legacy — auto-migrated)",
46
+ level: "project",
47
+ },
48
+ {
49
+ path: p(".gemini", "commands", "pitstop.toml"),
50
+ tool: "Gemini CLI (project)",
51
+ level: "project",
52
+ transform: "gemini",
53
+ },
54
+ ];
55
+ }
56
+ /** Absolute user-level (home) targets. */
57
+ export function homeTargets() {
58
+ const h = (...parts) => path.join(os.homedir(), ...parts);
59
+ return [
60
+ {
61
+ path: h(".codex", "prompts", "pitstop.md"),
62
+ tool: "Codex CLI (user)",
63
+ level: "home",
64
+ note: "Codex supports prompts only at user level",
65
+ },
66
+ { path: h(".claude", "commands", "pitstop.md"), tool: "Claude Code (user)", level: "home" },
67
+ { path: h(".cursor", "commands", "pitstop.md"), tool: "Cursor (user)", level: "home" },
68
+ // OpenCode current docs: global commands in ~/.config/opencode/commands/;
69
+ // ~/.opencode/commands/ is the older location — keep both.
70
+ {
71
+ path: h(".config", "opencode", "commands", "pitstop.md"),
72
+ tool: "OpenCode (user)",
73
+ level: "home",
74
+ },
75
+ {
76
+ path: h(".opencode", "commands", "pitstop.md"),
77
+ tool: "OpenCode (user, legacy)",
78
+ level: "home",
79
+ },
80
+ // Antigravity global workflows (home) mirror the project layout.
81
+ {
82
+ path: h(".agent", "workflows", "pitstop.md"),
83
+ tool: "Antigravity (user)",
84
+ level: "home",
85
+ transform: "workflow",
86
+ },
87
+ {
88
+ path: h(".agents", "workflows", "pitstop.md"),
89
+ tool: "Antigravity (user, plural legacy)",
90
+ level: "home",
91
+ transform: "workflow",
92
+ },
93
+ { path: h(".config", "kilo", "commands", "pitstop.md"), tool: "Kilo Code (user)", level: "home" },
94
+ {
95
+ path: h(".kilocode", "workflows", "pitstop.md"),
96
+ tool: "Kilo Code (user, legacy — auto-migrated)",
97
+ level: "home",
98
+ },
99
+ {
100
+ path: h(".gemini", "commands", "pitstop.toml"),
101
+ tool: "Gemini CLI (user)",
102
+ level: "home",
103
+ transform: "gemini",
104
+ },
105
+ ];
106
+ }
107
+ export function getTargets(cwd) {
108
+ return [...projectTargets(cwd), ...homeTargets()];
109
+ }
110
+ /** Locate templates/pitstop.prompt.md relative to this module or cwd. */
111
+ export function resolveTemplatePath() {
112
+ const fromModule = fileURLToPath(new URL("../../templates/pitstop.prompt.md", import.meta.url));
113
+ if (fs.existsSync(fromModule))
114
+ return fromModule;
115
+ const fromCwd = path.resolve(process.cwd(), "templates", "pitstop.prompt.md");
116
+ if (fs.existsSync(fromCwd))
117
+ return fromCwd;
118
+ throw new Error("could not locate templates/pitstop.prompt.md");
119
+ }
120
+ /**
121
+ * Render the file content for a target. The Claude Code skill variant rewrites
122
+ * the frontmatter to { name, description } as skills require. The Antigravity
123
+ * workflow variant normalizes the frontmatter to `description` only (their
124
+ * required field) and keeps the title + numbered steps body as-is. The Gemini
125
+ * CLI variant converts to TOML ({ description = "...", prompt = """ body """ }),
126
+ * the format .gemini/commands/*.toml files must use.
127
+ */
128
+ export function renderContent(target, templateText) {
129
+ if (target.transform === "skill") {
130
+ const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
131
+ if (!m)
132
+ return templateText;
133
+ const fm = m[1];
134
+ const body = m[2];
135
+ const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
136
+ const description = descMatch ? descMatch[1].trim() : "";
137
+ return `---\nname: pitstop\ndescription: ${description}\n---\n\n${body}`;
138
+ }
139
+ if (target.transform === "workflow") {
140
+ const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
141
+ if (!m)
142
+ return templateText;
143
+ const fm = m[1];
144
+ const body = m[2];
145
+ const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
146
+ const description = descMatch ? descMatch[1].trim() : "";
147
+ return `---\ndescription: ${description}\n---\n\n${body}`;
148
+ }
149
+ if (target.transform === "gemini") {
150
+ const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
151
+ if (!m)
152
+ return templateText;
153
+ const fm = m[1];
154
+ const body = m[2];
155
+ const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
156
+ let description = descMatch ? descMatch[1].trim() : "";
157
+ description = description.replace(/^"|"$/g, "");
158
+ return `description = "${description}"\nprompt = """\n${body.trim()}\n"""\n`;
159
+ }
160
+ return templateText;
161
+ }
162
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../../src/installer/targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ export type MemoryType = "decision" | "fix" | "rejection";
2
+ export interface MemoryEntry {
3
+ id: string;
4
+ timestamp: string;
5
+ type: MemoryType;
6
+ summary: string;
7
+ context: string;
8
+ relatedFiles: string[];
9
+ }
10
+ /**
11
+ * Identify "this repo" by hashing the git remote URL (fallback: absolute path).
12
+ * The hash determines the memory file name so memory is stable across checkouts.
13
+ */
14
+ export declare function identifyRepo(repo: string): string;
15
+ export declare function globalMemoryPath(repo: string): string;
16
+ export declare function localMemoryPath(repo: string): string;
17
+ export declare function loadEntries(repo: string): MemoryEntry[];
18
+ export interface AddInput {
19
+ type: MemoryType;
20
+ summary: string;
21
+ context?: string;
22
+ relatedFiles?: string[];
23
+ mirror?: boolean;
24
+ }
25
+ export declare function addEntry(repo: string, input: AddInput): MemoryEntry;
26
+ /**
27
+ * 1–2 hop undirected neighborhood of the given files in the dependency graph,
28
+ * as a set of absolute paths. Used to decide whether a memory entry is
29
+ * "relevant" to a file or cluster.
30
+ */
31
+ export declare function buildNeighborhood(repo: string, files: string[]): Set<string>;
32
+ export declare function relevantEntriesForFiles(repo: string, files: string[]): MemoryEntry[];
33
+ export declare function relevantEntries(repo: string, filePath: string): MemoryEntry[];