seer-mcp 0.1.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 (371) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +176 -0
  3. package/README.md +272 -0
  4. package/README_dev.md +199 -0
  5. package/dist/bundle/ci.d.ts +47 -0
  6. package/dist/bundle/ci.d.ts.map +1 -0
  7. package/dist/bundle/ci.js +113 -0
  8. package/dist/bundle/ci.js.map +1 -0
  9. package/dist/bundle/contract.d.ts +111 -0
  10. package/dist/bundle/contract.d.ts.map +1 -0
  11. package/dist/bundle/contract.js +352 -0
  12. package/dist/bundle/contract.js.map +1 -0
  13. package/dist/bundle/export.d.ts +36 -0
  14. package/dist/bundle/export.d.ts.map +1 -0
  15. package/dist/bundle/export.js +152 -0
  16. package/dist/bundle/export.js.map +1 -0
  17. package/dist/bundle/external.d.ts +66 -0
  18. package/dist/bundle/external.d.ts.map +1 -0
  19. package/dist/bundle/external.js +238 -0
  20. package/dist/bundle/external.js.map +1 -0
  21. package/dist/bundle/format.d.ts +94 -0
  22. package/dist/bundle/format.d.ts.map +1 -0
  23. package/dist/bundle/format.js +42 -0
  24. package/dist/bundle/format.js.map +1 -0
  25. package/dist/bundle/import.d.ts +49 -0
  26. package/dist/bundle/import.d.ts.map +1 -0
  27. package/dist/bundle/import.js +116 -0
  28. package/dist/bundle/import.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +1402 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/init.d.ts +48 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +284 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/db/schema.d.ts +3 -0
  38. package/dist/db/schema.d.ts.map +1 -0
  39. package/dist/db/schema.js +616 -0
  40. package/dist/db/schema.js.map +1 -0
  41. package/dist/db/store.d.ts +1011 -0
  42. package/dist/db/store.d.ts.map +1 -0
  43. package/dist/db/store.js +3888 -0
  44. package/dist/db/store.js.map +1 -0
  45. package/dist/graph/pagerank.d.ts +9 -0
  46. package/dist/graph/pagerank.d.ts.map +1 -0
  47. package/dist/graph/pagerank.js +47 -0
  48. package/dist/graph/pagerank.js.map +1 -0
  49. package/dist/indexer/architecture.d.ts +72 -0
  50. package/dist/indexer/architecture.d.ts.map +1 -0
  51. package/dist/indexer/architecture.js +112 -0
  52. package/dist/indexer/architecture.js.map +1 -0
  53. package/dist/indexer/behavior.d.ts +75 -0
  54. package/dist/indexer/behavior.d.ts.map +1 -0
  55. package/dist/indexer/behavior.js +395 -0
  56. package/dist/indexer/behavior.js.map +1 -0
  57. package/dist/indexer/boundaries.d.ts +60 -0
  58. package/dist/indexer/boundaries.d.ts.map +1 -0
  59. package/dist/indexer/boundaries.js +366 -0
  60. package/dist/indexer/boundaries.js.map +1 -0
  61. package/dist/indexer/churn.d.ts +15 -0
  62. package/dist/indexer/churn.d.ts.map +1 -0
  63. package/dist/indexer/churn.js +49 -0
  64. package/dist/indexer/churn.js.map +1 -0
  65. package/dist/indexer/classify.d.ts +9 -0
  66. package/dist/indexer/classify.d.ts.map +1 -0
  67. package/dist/indexer/classify.js +90 -0
  68. package/dist/indexer/classify.js.map +1 -0
  69. package/dist/indexer/context.d.ts +176 -0
  70. package/dist/indexer/context.d.ts.map +1 -0
  71. package/dist/indexer/context.js +193 -0
  72. package/dist/indexer/context.js.map +1 -0
  73. package/dist/indexer/continuity.d.ts +67 -0
  74. package/dist/indexer/continuity.d.ts.map +1 -0
  75. package/dist/indexer/continuity.js +288 -0
  76. package/dist/indexer/continuity.js.map +1 -0
  77. package/dist/indexer/detectchanges.d.ts +32 -0
  78. package/dist/indexer/detectchanges.d.ts.map +1 -0
  79. package/dist/indexer/detectchanges.js +74 -0
  80. package/dist/indexer/detectchanges.js.map +1 -0
  81. package/dist/indexer/discovery.d.ts +37 -0
  82. package/dist/indexer/discovery.d.ts.map +1 -0
  83. package/dist/indexer/discovery.js +136 -0
  84. package/dist/indexer/discovery.js.map +1 -0
  85. package/dist/indexer/externaldeps.d.ts +18 -0
  86. package/dist/indexer/externaldeps.d.ts.map +1 -0
  87. package/dist/indexer/externaldeps.js +288 -0
  88. package/dist/indexer/externaldeps.js.map +1 -0
  89. package/dist/indexer/freshness.d.ts +48 -0
  90. package/dist/indexer/freshness.d.ts.map +1 -0
  91. package/dist/indexer/freshness.js +128 -0
  92. package/dist/indexer/freshness.js.map +1 -0
  93. package/dist/indexer/git.d.ts +144 -0
  94. package/dist/indexer/git.d.ts.map +1 -0
  95. package/dist/indexer/git.js +444 -0
  96. package/dist/indexer/git.js.map +1 -0
  97. package/dist/indexer/index.d.ts +145 -0
  98. package/dist/indexer/index.d.ts.map +1 -0
  99. package/dist/indexer/index.js +930 -0
  100. package/dist/indexer/index.js.map +1 -0
  101. package/dist/indexer/modules.d.ts +62 -0
  102. package/dist/indexer/modules.d.ts.map +1 -0
  103. package/dist/indexer/modules.js +293 -0
  104. package/dist/indexer/modules.js.map +1 -0
  105. package/dist/indexer/preflight.d.ts +154 -0
  106. package/dist/indexer/preflight.d.ts.map +1 -0
  107. package/dist/indexer/preflight.js +399 -0
  108. package/dist/indexer/preflight.js.map +1 -0
  109. package/dist/indexer/protoScanner.d.ts +34 -0
  110. package/dist/indexer/protoScanner.d.ts.map +1 -0
  111. package/dist/indexer/protoScanner.js +133 -0
  112. package/dist/indexer/protoScanner.js.map +1 -0
  113. package/dist/indexer/risk.d.ts +115 -0
  114. package/dist/indexer/risk.d.ts.map +1 -0
  115. package/dist/indexer/risk.js +194 -0
  116. package/dist/indexer/risk.js.map +1 -0
  117. package/dist/indexer/serviceHostScanner.d.ts +25 -0
  118. package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
  119. package/dist/indexer/serviceHostScanner.js +95 -0
  120. package/dist/indexer/serviceHostScanner.js.map +1 -0
  121. package/dist/indexer/serviceLinks.d.ts +105 -0
  122. package/dist/indexer/serviceLinks.d.ts.map +1 -0
  123. package/dist/indexer/serviceLinks.js +509 -0
  124. package/dist/indexer/serviceLinks.js.map +1 -0
  125. package/dist/indexer/shapehash.d.ts +98 -0
  126. package/dist/indexer/shapehash.d.ts.map +1 -0
  127. package/dist/indexer/shapehash.js +354 -0
  128. package/dist/indexer/shapehash.js.map +1 -0
  129. package/dist/indexer/skeleton.d.ts +15 -0
  130. package/dist/indexer/skeleton.d.ts.map +1 -0
  131. package/dist/indexer/skeleton.js +136 -0
  132. package/dist/indexer/skeleton.js.map +1 -0
  133. package/dist/indexer/symbolhistory.d.ts +41 -0
  134. package/dist/indexer/symbolhistory.d.ts.map +1 -0
  135. package/dist/indexer/symbolhistory.js +124 -0
  136. package/dist/indexer/symbolhistory.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +68 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +179 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/server.d.ts +80 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +1610 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/parser/index.d.ts +8 -0
  146. package/dist/parser/index.d.ts.map +1 -0
  147. package/dist/parser/index.js +33 -0
  148. package/dist/parser/index.js.map +1 -0
  149. package/dist/parser/languages/cpp.d.ts +3 -0
  150. package/dist/parser/languages/cpp.d.ts.map +1 -0
  151. package/dist/parser/languages/cpp.js +350 -0
  152. package/dist/parser/languages/cpp.js.map +1 -0
  153. package/dist/parser/languages/csharp.d.ts +3 -0
  154. package/dist/parser/languages/csharp.d.ts.map +1 -0
  155. package/dist/parser/languages/csharp.js +239 -0
  156. package/dist/parser/languages/csharp.js.map +1 -0
  157. package/dist/parser/languages/go.d.ts +3 -0
  158. package/dist/parser/languages/go.d.ts.map +1 -0
  159. package/dist/parser/languages/go.js +259 -0
  160. package/dist/parser/languages/go.js.map +1 -0
  161. package/dist/parser/languages/java.d.ts +3 -0
  162. package/dist/parser/languages/java.d.ts.map +1 -0
  163. package/dist/parser/languages/java.js +391 -0
  164. package/dist/parser/languages/java.js.map +1 -0
  165. package/dist/parser/languages/python.d.ts +3 -0
  166. package/dist/parser/languages/python.d.ts.map +1 -0
  167. package/dist/parser/languages/python.js +396 -0
  168. package/dist/parser/languages/python.js.map +1 -0
  169. package/dist/parser/languages/rust.d.ts +3 -0
  170. package/dist/parser/languages/rust.d.ts.map +1 -0
  171. package/dist/parser/languages/rust.js +159 -0
  172. package/dist/parser/languages/rust.js.map +1 -0
  173. package/dist/parser/languages/typescript.d.ts +3 -0
  174. package/dist/parser/languages/typescript.d.ts.map +1 -0
  175. package/dist/parser/languages/typescript.js +1442 -0
  176. package/dist/parser/languages/typescript.js.map +1 -0
  177. package/dist/parser/parserContext.d.ts +77 -0
  178. package/dist/parser/parserContext.d.ts.map +1 -0
  179. package/dist/parser/parserContext.js +354 -0
  180. package/dist/parser/parserContext.js.map +1 -0
  181. package/dist/parser/walker.d.ts +81 -0
  182. package/dist/parser/walker.d.ts.map +1 -0
  183. package/dist/parser/walker.js +217 -0
  184. package/dist/parser/walker.js.map +1 -0
  185. package/dist/parser/worker.d.ts +66 -0
  186. package/dist/parser/worker.d.ts.map +1 -0
  187. package/dist/parser/worker.js +129 -0
  188. package/dist/parser/worker.js.map +1 -0
  189. package/dist/parser/workerpool.d.ts +107 -0
  190. package/dist/parser/workerpool.d.ts.map +1 -0
  191. package/dist/parser/workerpool.js +383 -0
  192. package/dist/parser/workerpool.js.map +1 -0
  193. package/dist/scip/format.d.ts +87 -0
  194. package/dist/scip/format.d.ts.map +1 -0
  195. package/dist/scip/format.js +31 -0
  196. package/dist/scip/format.js.map +1 -0
  197. package/dist/scip/import.d.ts +37 -0
  198. package/dist/scip/import.d.ts.map +1 -0
  199. package/dist/scip/import.js +180 -0
  200. package/dist/scip/import.js.map +1 -0
  201. package/dist/types.d.ts +392 -0
  202. package/dist/types.d.ts.map +1 -0
  203. package/dist/types.js +4 -0
  204. package/dist/types.js.map +1 -0
  205. package/docs/architecture.md +105 -0
  206. package/docs/benchmarks/methodology.md +134 -0
  207. package/docs/benchmarks/raw-results.md +71 -0
  208. package/docs/benchmarks.md +74 -0
  209. package/docs/cli.md +148 -0
  210. package/docs/examples/behavior-tests.md +70 -0
  211. package/docs/examples/change-history.md +85 -0
  212. package/docs/examples/pre-edit-context.md +81 -0
  213. package/docs/examples/service-links.md +88 -0
  214. package/docs/examples.md +80 -0
  215. package/docs/faq.md +70 -0
  216. package/docs/internals.md +104 -0
  217. package/docs/languages.md +70 -0
  218. package/docs/limits.md +52 -0
  219. package/docs/mcp.md +199 -0
  220. package/docs/quickstart.md +119 -0
  221. package/docs/testing.md +123 -0
  222. package/docs/tools.md +115 -0
  223. package/package.json +52 -0
  224. package/research-codebase.md +578 -0
  225. package/seer-cli-docs.md +326 -0
  226. package/seer-master-guide.md +246 -0
  227. package/src/bundle/ci.ts +141 -0
  228. package/src/bundle/contract.ts +387 -0
  229. package/src/bundle/export.ts +175 -0
  230. package/src/bundle/external.ts +285 -0
  231. package/src/bundle/format.ts +92 -0
  232. package/src/bundle/import.ts +157 -0
  233. package/src/cli/index.ts +1249 -0
  234. package/src/cli/init.ts +389 -0
  235. package/src/db/schema.ts +614 -0
  236. package/src/db/store.ts +4306 -0
  237. package/src/graph/pagerank.ts +53 -0
  238. package/src/indexer/architecture.ts +148 -0
  239. package/src/indexer/behavior.ts +466 -0
  240. package/src/indexer/boundaries.ts +374 -0
  241. package/src/indexer/churn.ts +58 -0
  242. package/src/indexer/classify.ts +96 -0
  243. package/src/indexer/context.ts +340 -0
  244. package/src/indexer/continuity.ts +322 -0
  245. package/src/indexer/detectchanges.ts +94 -0
  246. package/src/indexer/discovery.ts +176 -0
  247. package/src/indexer/externaldeps.ts +243 -0
  248. package/src/indexer/freshness.ts +166 -0
  249. package/src/indexer/git.ts +453 -0
  250. package/src/indexer/index.ts +1092 -0
  251. package/src/indexer/modules.ts +358 -0
  252. package/src/indexer/preflight.ts +548 -0
  253. package/src/indexer/protoScanner.ts +147 -0
  254. package/src/indexer/risk.ts +304 -0
  255. package/src/indexer/serviceHostScanner.ts +92 -0
  256. package/src/indexer/serviceLinks.ts +543 -0
  257. package/src/indexer/shapehash.ts +370 -0
  258. package/src/indexer/skeleton.ts +169 -0
  259. package/src/indexer/symbolhistory.ts +172 -0
  260. package/src/indexer/watcher.ts +206 -0
  261. package/src/mcp/server.ts +1659 -0
  262. package/src/parser/index.ts +37 -0
  263. package/src/parser/languages/cpp.ts +361 -0
  264. package/src/parser/languages/csharp.ts +235 -0
  265. package/src/parser/languages/go.ts +259 -0
  266. package/src/parser/languages/java.ts +382 -0
  267. package/src/parser/languages/python.ts +370 -0
  268. package/src/parser/languages/rust.ts +164 -0
  269. package/src/parser/languages/typescript.ts +1435 -0
  270. package/src/parser/parserContext.ts +392 -0
  271. package/src/parser/walker.ts +306 -0
  272. package/src/parser/worker.ts +181 -0
  273. package/src/parser/workerpool.ts +448 -0
  274. package/src/scip/format.ts +83 -0
  275. package/src/scip/import.ts +216 -0
  276. package/src/types.ts +457 -0
  277. package/tests/benchmark-service-links.ts +244 -0
  278. package/tests/bug-regressions.ts +626 -0
  279. package/tests/filters.ts +264 -0
  280. package/tests/fixtures/Counter.tsx +38 -0
  281. package/tests/fixtures/caller.ts +7 -0
  282. package/tests/fixtures/collisions.ts +23 -0
  283. package/tests/fixtures/local_helper.ts +5 -0
  284. package/tests/fixtures/overloads.java +17 -0
  285. package/tests/fixtures/remote_helper.ts +4 -0
  286. package/tests/fixtures/sample.c +15 -0
  287. package/tests/fixtures/sample.cpp +47 -0
  288. package/tests/fixtures/sample.cs +62 -0
  289. package/tests/fixtures/sample.go +68 -0
  290. package/tests/fixtures/sample.h +30 -0
  291. package/tests/fixtures/sample.java +85 -0
  292. package/tests/fixtures/sample.py +46 -0
  293. package/tests/fixtures/sample.rs +78 -0
  294. package/tests/fixtures/sample.ts +76 -0
  295. package/tests/fixtures-service/HttpClients.cs +30 -0
  296. package/tests/fixtures-service/HttpClients.java +24 -0
  297. package/tests/fixtures-service/billing.ts +15 -0
  298. package/tests/fixtures-service/docker-compose.yml +15 -0
  299. package/tests/fixtures-service/gateway.ts +10 -0
  300. package/tests/fixtures-service/get_user.ts +11 -0
  301. package/tests/fixtures-service/graphql_client.ts +63 -0
  302. package/tests/fixtures-service/graphql_server.ts +30 -0
  303. package/tests/fixtures-service/grpc_client.go +30 -0
  304. package/tests/fixtures-service/http_clients.go +23 -0
  305. package/tests/fixtures-service/http_clients.py +38 -0
  306. package/tests/fixtures-service/http_clients.ts +49 -0
  307. package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
  308. package/tests/fixtures-service/k8s_calls.ts +20 -0
  309. package/tests/fixtures-service/messaging.ts +87 -0
  310. package/tests/fixtures-service/trpc_client.ts +39 -0
  311. package/tests/fixtures-service/trpc_server.ts +39 -0
  312. package/tests/fixtures-service/user_service.proto +33 -0
  313. package/tests/fixtures-trackcd/Cargo.toml +11 -0
  314. package/tests/fixtures-trackcd/SpringController.java +36 -0
  315. package/tests/fixtures-trackcd/auth_service.ts +19 -0
  316. package/tests/fixtures-trackcd/complex_module.py +50 -0
  317. package/tests/fixtures-trackcd/express_app.js +30 -0
  318. package/tests/fixtures-trackcd/fastapi_app.py +49 -0
  319. package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
  320. package/tests/fixtures-trackcd/go.mod +8 -0
  321. package/tests/fixtures-trackcd/package.json +15 -0
  322. package/tests/fixtures-trackcd/requirements.txt +4 -0
  323. package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
  324. package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
  325. package/tests/fixtures-tracke/auth/crypto.ts +7 -0
  326. package/tests/fixtures-tracke/billing/Billing.ts +20 -0
  327. package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
  328. package/tests/fixtures-tracke/billing/server.ts +17 -0
  329. package/tests/fixtures-tracke/package.json +7 -0
  330. package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
  331. package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
  332. package/tests/fixtures-trackf/package.json +5 -0
  333. package/tests/fixtures-trackf/src/auth.ts +26 -0
  334. package/tests/fixtures-trackf/src/handlers.ts +35 -0
  335. package/tests/fixtures-tracki/billing/routes.ts +12 -0
  336. package/tests/fixtures-tracki/gateway/client.ts +13 -0
  337. package/tests/git-features.ts +267 -0
  338. package/tests/init.ts +141 -0
  339. package/tests/mcp-jit.ts +130 -0
  340. package/tests/mcp-smoke.ts +191 -0
  341. package/tests/mcp-trackcd.ts +169 -0
  342. package/tests/mcp-tracke.ts +229 -0
  343. package/tests/mcp-trackf.ts +330 -0
  344. package/tests/mcp-trackg.ts +219 -0
  345. package/tests/mcp-tracki.ts +174 -0
  346. package/tests/mcp-watcher.ts +126 -0
  347. package/tests/optspec.ts +194 -0
  348. package/tests/parallel-index.ts +333 -0
  349. package/tests/parallel-read.ts +125 -0
  350. package/tests/parallel-recovery.ts +241 -0
  351. package/tests/perf-callers.ts +145 -0
  352. package/tests/query-parity.ts +184 -0
  353. package/tests/query-perf.ts +55 -0
  354. package/tests/scale-parallel-parity.ts +225 -0
  355. package/tests/scale-test.ts +523 -0
  356. package/tests/smoke.ts +396 -0
  357. package/tests/trackcd.ts +325 -0
  358. package/tests/tracke-collisions.ts +255 -0
  359. package/tests/tracke.ts +314 -0
  360. package/tests/trackf-bugs.ts +406 -0
  361. package/tests/trackf.ts +390 -0
  362. package/tests/trackg.ts +1372 -0
  363. package/tests/tracki-boundaries.ts +202 -0
  364. package/tests/tracki-continuity.ts +253 -0
  365. package/tests/tracki-contract-diff.ts +249 -0
  366. package/tests/tracki-external-bundles.ts +341 -0
  367. package/tests/tracki-preflight.ts +251 -0
  368. package/tests/verify-roles.ts +51 -0
  369. package/tests/worker-parity.ts +286 -0
  370. package/tests/worker-pool.ts +262 -0
  371. package/tsconfig.json +20 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Track F bug regression tests — pins fixes for the five issues caught in
3
+ * the post-Track-F audit:
4
+ *
5
+ * 1. (HIGH) SCIP imports were not layer-additive. `clearScipProvenance(path)`
6
+ * globally wiped every SCIP row regardless of path, so importing two
7
+ * different SCIP files left only the last one's rows behind even though
8
+ * the scip_imports table listed both. Fix: scope the wipe by
9
+ * `scip_import_id` (v7.1 column on symbols + edges).
10
+ *
11
+ * 2. (MEDIUM) Cached/migrated DBs skipped the shape-hash backfill. The
12
+ * indexer ran `buildShapeHashes` only when `graphChanged` was true, but
13
+ * a v6→v7 migration leaves every file's content unchanged so the cached
14
+ * branch fired and shape hashes stayed NULL forever. Fix: also run when
15
+ * the store reports `hasMissingShapeHashes()`.
16
+ *
17
+ * 3. (MEDIUM) Bundle import did not enforce schema compatibility. Fix:
18
+ * reject manifests whose schemaVersion exceeds CURRENT_SCHEMA_VERSION.
19
+ *
20
+ * 4. (MEDIUM) MCP bundle import ignored the server's custom --db, always
21
+ * landing the bundle at <workspace>/.seer/graph.db. (Verified by code
22
+ * reading; pinned by a unit-style test below that exercises the same
23
+ * code path the MCP tool uses.)
24
+ *
25
+ * 5. (LOW) Bundle bytes were not deterministic — `builtAt = Date.now()`.
26
+ * Fix: allow callers to pin builtAt via options.builtAt.
27
+ *
28
+ * Run: npx tsx tests/trackf-bugs.ts
29
+ */
30
+
31
+ import path from 'path';
32
+ import fs from 'fs';
33
+ import os from 'os';
34
+ import crypto from 'crypto';
35
+ import { Indexer } from '../src/indexer/index';
36
+ import { Store } from '../src/db/store';
37
+ import { CURRENT_SCHEMA_VERSION } from '../src/db/schema';
38
+ import { importScip } from '../src/scip/import';
39
+ import { exportBundle } from '../src/bundle/export';
40
+ import { importBundle, readBundleManifest } from '../src/bundle/import';
41
+
42
+ const FIX = path.join(__dirname, 'fixtures-trackf');
43
+ const TMP_DIR = path.join(os.tmpdir(), `seer-trackf-bugs-${Date.now()}`);
44
+
45
+ let passed = 0;
46
+ let failed = 0;
47
+ function safeStr(v: unknown): string {
48
+ if (typeof v === 'bigint') return `0x${v.toString(16)}`;
49
+ try { return JSON.stringify(v); }
50
+ catch { return String(v); }
51
+ }
52
+ function assert(cond: boolean, msg: string): void {
53
+ if (cond) { console.log(` ✓ ${msg}`); passed++; }
54
+ else { console.error(` ✗ ${msg}`); failed++; }
55
+ }
56
+ function assertEq<T>(actual: T, expected: T, msg: string): void {
57
+ assert(actual === expected, `${msg} (got ${safeStr(actual)}, expected ${safeStr(expected)})`);
58
+ }
59
+
60
+ function cleanup(): void {
61
+ try { fs.rmSync(TMP_DIR, { recursive: true, force: true }); } catch { /* */ }
62
+ }
63
+
64
+ async function freshlyIndexed(dbPath: string): Promise<void> {
65
+ const s = new Store(dbPath);
66
+ await new Indexer(s).indexDirectory(FIX, { quiet: true });
67
+ s.close();
68
+ }
69
+
70
+ async function main(): Promise<void> {
71
+ console.log('\nSeer Track F — Bug Regression Tests');
72
+ console.log('=====================================\n');
73
+ fs.mkdirSync(TMP_DIR, { recursive: true });
74
+
75
+ // ── Bug 1 (HIGH): per-layer SCIP wipe ─────────────────────────────────
76
+ console.log('── Bug 1: SCIP layer additivity ──');
77
+ {
78
+ const dbPath = path.join(TMP_DIR, 'bug1.db');
79
+ await freshlyIndexed(dbPath);
80
+ const store = new Store(dbPath);
81
+ try {
82
+ // Two SCIP layers, each contributing one SCIP-pure symbol in different
83
+ // (non-overlapping) line ranges so neither merges with tree-sitter
84
+ // rows. Both target the same file because that's the only file we
85
+ // index for the fixture — what matters for the test is that the two
86
+ // imports have different paths so they're tracked as separate layers.
87
+ const layerA = {
88
+ tool: 'layer-a/1.0',
89
+ documents: [{
90
+ relativePath: 'src/auth.ts',
91
+ symbols: [{
92
+ symbolId: 'a#alphaOnly', displayName: 'alphaOnly', kind: 'function',
93
+ relativePath: 'src/auth.ts',
94
+ range: { startLine: 50, startCharacter: 0, endLine: 52, endCharacter: 1 },
95
+ }],
96
+ occurrences: [],
97
+ }],
98
+ };
99
+ const layerB = {
100
+ tool: 'layer-b/1.0',
101
+ documents: [{
102
+ relativePath: 'src/auth.ts',
103
+ symbols: [{
104
+ symbolId: 'b#betaOnly', displayName: 'betaOnly', kind: 'function',
105
+ relativePath: 'src/auth.ts',
106
+ range: { startLine: 60, startCharacter: 0, endLine: 62, endCharacter: 1 },
107
+ }],
108
+ occurrences: [],
109
+ }],
110
+ };
111
+ const aPath = path.join(TMP_DIR, 'a.scip.json');
112
+ const bPath = path.join(TMP_DIR, 'b.scip.json');
113
+ fs.writeFileSync(aPath, JSON.stringify(layerA));
114
+ fs.writeFileSync(bPath, JSON.stringify(layerB));
115
+
116
+ // Import layer A.
117
+ await importScip(aPath, store, { repoRoot: FIX });
118
+ const afterA = store.getProvenanceCounts();
119
+ assertEq(afterA.symbols.scip, 1, 'after A: 1 SCIP symbol');
120
+ assertEq(store.listScipImports().length, 1, 'after A: 1 scip_imports row');
121
+
122
+ // Import layer B — should be ADDITIVE.
123
+ await importScip(bPath, store, { repoRoot: FIX });
124
+ const afterB = store.getProvenanceCounts();
125
+ assertEq(afterB.symbols.scip, 2,
126
+ 'after B: 2 SCIP symbols (layer A row survived layer B import)');
127
+ assertEq(store.listScipImports().length, 2, 'after B: 2 scip_imports rows');
128
+
129
+ // Each SCIP row should be linked to the correct scip_imports.id.
130
+ const scipRows = store.rawDb().prepare(`
131
+ SELECT s.name, s.scip_import_id, si.tool
132
+ FROM symbols s
133
+ JOIN scip_imports si ON si.id = s.scip_import_id
134
+ WHERE s.provenance = 'scip'
135
+ ORDER BY s.name
136
+ `).all() as Array<{ name: string; tool: string }>;
137
+ assertEq(scipRows.length, 2, 'both SCIP-pure rows linked to their importer');
138
+ assert(scipRows.some(r => r.name === 'alphaOnly' && r.tool === 'layer-a/1.0'),
139
+ 'alphaOnly linked to layer-a');
140
+ assert(scipRows.some(r => r.name === 'betaOnly' && r.tool === 'layer-b/1.0'),
141
+ 'betaOnly linked to layer-b');
142
+
143
+ // Clearing layer A by path should leave layer B intact.
144
+ const cleared = store.clearScipProvenance(aPath);
145
+ assert(cleared >= 1, 'clearScipProvenance(aPath) removed at least one row');
146
+ const afterClearA = store.getProvenanceCounts();
147
+ assertEq(afterClearA.symbols.scip, 1,
148
+ 'after clear(a): exactly layer B remains');
149
+ const remaining = store.listScipImports();
150
+ assertEq(remaining.length, 1, 'after clear(a): 1 scip_imports row left');
151
+ assertEq(remaining[0].tool, 'layer-b/1.0',
152
+ 'after clear(a): layer-b is the survivor');
153
+
154
+ // Re-import A — additive again.
155
+ await importScip(aPath, store, { repoRoot: FIX });
156
+ assertEq(store.getProvenanceCounts().symbols.scip, 2,
157
+ 'after re-import(a): both layers back');
158
+
159
+ // Global wipe (no path) clears everything.
160
+ store.clearScipProvenance();
161
+ const afterGlobal = store.getProvenanceCounts();
162
+ assertEq(afterGlobal.symbols.scip, 0, 'global clear: zero SCIP-pure symbols');
163
+ assertEq(store.listScipImports().length, 0, 'global clear: scip_imports empty');
164
+ } finally { store.close(); }
165
+ }
166
+
167
+ // ── Bug 1 follow-up: per-layer wipe preserves scip-merge from sibling ─
168
+ console.log('\n── Bug 1: scip-merge survives sibling layer wipe ──');
169
+ {
170
+ const dbPath = path.join(TMP_DIR, 'bug1-merge.db');
171
+ await freshlyIndexed(dbPath);
172
+ const store = new Store(dbPath);
173
+ try {
174
+ // Layer X merges with a tree-sitter row; layer Y adds a fresh SCIP row.
175
+ // Clearing Y must NOT demote X's scip-merge.
176
+ const layerX = {
177
+ tool: 'x', documents: [{
178
+ relativePath: 'src/auth.ts',
179
+ symbols: [{
180
+ symbolId: 'x#login', displayName: 'login', qualifiedName: 'AuthService.login',
181
+ kind: 'method', relativePath: 'src/auth.ts',
182
+ range: { startLine: 3, startCharacter: 0, endLine: 6, endCharacter: 1 },
183
+ }],
184
+ occurrences: [],
185
+ }],
186
+ };
187
+ const layerY = {
188
+ tool: 'y', documents: [{
189
+ relativePath: 'src/auth.ts',
190
+ symbols: [{
191
+ symbolId: 'y#fresh', displayName: 'freshHelper', kind: 'function',
192
+ relativePath: 'src/auth.ts',
193
+ range: { startLine: 70, startCharacter: 0, endLine: 72, endCharacter: 1 },
194
+ }],
195
+ occurrences: [],
196
+ }],
197
+ };
198
+ const xPath = path.join(TMP_DIR, 'x.scip.json');
199
+ const yPath = path.join(TMP_DIR, 'y.scip.json');
200
+ fs.writeFileSync(xPath, JSON.stringify(layerX));
201
+ fs.writeFileSync(yPath, JSON.stringify(layerY));
202
+ await importScip(xPath, store, { repoRoot: FIX });
203
+ await importScip(yPath, store, { repoRoot: FIX });
204
+ assertEq(store.getProvenanceCounts().symbols['scip-merge'], 1,
205
+ 'pre-clear: 1 scip-merge row');
206
+ assertEq(store.getProvenanceCounts().symbols.scip, 1,
207
+ 'pre-clear: 1 SCIP-pure row');
208
+
209
+ store.clearScipProvenance(yPath);
210
+ const after = store.getProvenanceCounts();
211
+ assertEq(after.symbols['scip-merge'], 1,
212
+ 'after clear(y): layer X scip-merge survives');
213
+ assertEq(after.symbols.scip, 0,
214
+ 'after clear(y): layer Y SCIP-pure rows gone');
215
+ } finally { store.close(); }
216
+ }
217
+
218
+ // ── Bug 2: shape-hash backfill on cached/migrated re-index ───────────
219
+ console.log('\n── Bug 2: shape-hash backfill on cached re-index ──');
220
+ {
221
+ const dbPath = path.join(TMP_DIR, 'bug2.db');
222
+ await freshlyIndexed(dbPath);
223
+ // Simulate the post-migration state: every shape_hash is NULL but the
224
+ // file content hashes still match the indexer's view, so every file
225
+ // would land in the "cached" path on re-index.
226
+ const s1 = new Store(dbPath);
227
+ s1.rawDb().exec('UPDATE symbols SET shape_hash = NULL');
228
+ const preWipe = s1.getStats();
229
+ assertEq(preWipe.shapeHashed ?? -1, 0, 'manually cleared: shapeHashed=0');
230
+ s1.close();
231
+
232
+ // Re-run the indexer — every file should be cached, no new files indexed.
233
+ const s2 = new Store(dbPath);
234
+ const r = await new Indexer(s2).indexDirectory(FIX, { quiet: true });
235
+ assertEq(r.filesIndexed, 0,
236
+ 'all files cached (graphChanged=false: filesIndexed=0)');
237
+ assert((r.shapeHashesAdded ?? 0) >= 3,
238
+ `backfill ran even on cached re-index (shapeHashesAdded=${r.shapeHashesAdded})`);
239
+ assert((s2.getStats().shapeHashed ?? 0) >= 3,
240
+ `shape_hashed count restored (got ${s2.getStats().shapeHashed})`);
241
+
242
+ // Now a second re-run with hashes already in place skips the backfill
243
+ // (still graphChanged=false, but hasMissingShapeHashes()=false).
244
+ const r2 = await new Indexer(s2).indexDirectory(FIX, { quiet: true });
245
+ assertEq(r2.shapeHashesAdded ?? -1, 0,
246
+ 'second re-run skips backfill (already complete)');
247
+ s2.close();
248
+ }
249
+
250
+ // ── Bug 3: bundle import enforces schema compatibility ───────────────
251
+ console.log('\n── Bug 3: bundle schema compat check ──');
252
+ {
253
+ const dbPath = path.join(TMP_DIR, 'bug3.db');
254
+ await freshlyIndexed(dbPath);
255
+ const out = path.join(TMP_DIR, 'bug3.bundle');
256
+ await exportBundle(dbPath, FIX, { out });
257
+
258
+ // Tamper the manifest to claim schemaVersion=999. Format-version is
259
+ // intentionally left at 1 so the format check (which we DID have) still
260
+ // passes — only the schema-version gate stops this import.
261
+ const buf = fs.readFileSync(out);
262
+ const mLen = buf.readUInt32BE(8);
263
+ const manifest = JSON.parse(buf.slice(12, 12 + mLen).toString('utf-8'));
264
+ manifest.schemaVersion = 999;
265
+ // Re-hash the DB so dbSha256 stays correct; that's not what we're testing.
266
+ const newManifest = Buffer.from(JSON.stringify(manifest, null, 2), 'utf-8');
267
+ const tampered = Buffer.concat([
268
+ buf.slice(0, 8),
269
+ Buffer.alloc(4),
270
+ newManifest,
271
+ buf.slice(12 + mLen),
272
+ ]);
273
+ tampered.writeUInt32BE(newManifest.length, 8);
274
+ const tamperPath = path.join(TMP_DIR, 'bug3-tampered.bundle');
275
+ fs.writeFileSync(tamperPath, tampered);
276
+
277
+ let threw = false;
278
+ let msg = '';
279
+ try {
280
+ await importBundle(tamperPath, {
281
+ repoRoot: FIX, dbOut: path.join(TMP_DIR, 'bug3-out.db'),
282
+ });
283
+ } catch (err) {
284
+ msg = (err as Error).message;
285
+ threw = msg.includes('schemaVersion') && msg.includes('newer');
286
+ }
287
+ assert(threw, `import refuses bundle with schemaVersion > CURRENT (msg: ${msg})`);
288
+
289
+ // Bundles with valid (≤ CURRENT) schemaVersion still import.
290
+ const validPath = path.join(TMP_DIR, 'bug3-valid.bundle');
291
+ fs.copyFileSync(out, validPath);
292
+ const ok = await importBundle(validPath, {
293
+ repoRoot: FIX, dbOut: path.join(TMP_DIR, 'bug3-ok.db'),
294
+ });
295
+ assertEq(ok.manifest.schemaVersion, CURRENT_SCHEMA_VERSION,
296
+ 'untampered bundle imports cleanly');
297
+
298
+ // Negative schemaVersion is rejected.
299
+ const manifest2 = { ...manifest, schemaVersion: 0 };
300
+ const newManifest2 = Buffer.from(JSON.stringify(manifest2, null, 2), 'utf-8');
301
+ const tampered2 = Buffer.concat([
302
+ buf.slice(0, 8),
303
+ Buffer.alloc(4),
304
+ newManifest2,
305
+ buf.slice(12 + mLen),
306
+ ]);
307
+ tampered2.writeUInt32BE(newManifest2.length, 8);
308
+ const zeroPath = path.join(TMP_DIR, 'bug3-zero.bundle');
309
+ fs.writeFileSync(zeroPath, tampered2);
310
+ let zeroThrew = false;
311
+ try {
312
+ await importBundle(zeroPath, {
313
+ repoRoot: FIX, dbOut: path.join(TMP_DIR, 'bug3-zero-out.db'),
314
+ });
315
+ } catch (err) {
316
+ zeroThrew = (err as Error).message.includes('invalid schemaVersion');
317
+ }
318
+ assert(zeroThrew, 'bundle with schemaVersion=0 is rejected');
319
+
320
+ // skipSchemaCheck lets the user opt out.
321
+ const opted = await importBundle(tamperPath, {
322
+ repoRoot: FIX, dbOut: path.join(TMP_DIR, 'bug3-opt.db'),
323
+ skipSchemaCheck: true,
324
+ });
325
+ assertEq(opted.manifest.schemaVersion, 999,
326
+ 'skipSchemaCheck=true lets the bogus version through (for forensics)');
327
+ }
328
+
329
+ // ── Bug 4: bundle import lands at the requested dbOut ────────────────
330
+ // This pins the unit-level contract the MCP fix relies on: importBundle()
331
+ // must write to options.dbOut when supplied, never to a default. The MCP
332
+ // server now always supplies this.dbPath, which is what's broken pre-fix.
333
+ console.log('\n── Bug 4: bundle import respects custom dbOut ──');
334
+ {
335
+ const dbPath = path.join(TMP_DIR, 'bug4.db');
336
+ await freshlyIndexed(dbPath);
337
+ const bundlePath = path.join(TMP_DIR, 'bug4.bundle');
338
+ await exportBundle(dbPath, FIX, { out: bundlePath });
339
+
340
+ // Land the bundle at a non-default path inside a fresh "workspace".
341
+ const fakeWorkspace = path.join(TMP_DIR, 'fake-ws');
342
+ fs.mkdirSync(fakeWorkspace, { recursive: true });
343
+ const customDb = path.join(TMP_DIR, 'custom-elsewhere.db');
344
+ const r = await importBundle(bundlePath, {
345
+ repoRoot: fakeWorkspace, dbOut: customDb,
346
+ });
347
+ assertEq(r.dbPath, customDb, 'importBundle writes to options.dbOut');
348
+ assert(fs.existsSync(customDb), 'custom dbOut file exists');
349
+ // The default location must NOT have been touched.
350
+ const defaultPath = path.join(fakeWorkspace, '.seer', 'graph.db');
351
+ assert(!fs.existsSync(defaultPath),
352
+ 'default <repoRoot>/.seer/graph.db was NOT created when dbOut overrode it');
353
+ }
354
+
355
+ // ── Bug 5: deterministic bundle bytes with pinned builtAt ────────────
356
+ console.log('\n── Bug 5: deterministic bundle bytes when builtAt is pinned ──');
357
+ {
358
+ const dbPath = path.join(TMP_DIR, 'bug5.db');
359
+ await freshlyIndexed(dbPath);
360
+ const fixedAt = 1_700_000_000_000;
361
+ const out1 = path.join(TMP_DIR, 'bug5-a.bundle');
362
+ const out2 = path.join(TMP_DIR, 'bug5-b.bundle');
363
+ // Pin everything that can vary across runs.
364
+ await exportBundle(dbPath, FIX, {
365
+ out: out1, gitHead: 'fixedsha', gitBranch: 'main', builtAt: fixedAt,
366
+ });
367
+ // A small delay between exports — without builtAt override, this alone
368
+ // produced different bytes pre-fix.
369
+ await new Promise(r => setTimeout(r, 25));
370
+ await exportBundle(dbPath, FIX, {
371
+ out: out2, gitHead: 'fixedsha', gitBranch: 'main', builtAt: fixedAt,
372
+ });
373
+ const sha1 = crypto.createHash('sha256').update(fs.readFileSync(out1)).digest('hex');
374
+ const sha2 = crypto.createHash('sha256').update(fs.readFileSync(out2)).digest('hex');
375
+ assertEq(sha1, sha2,
376
+ 'two exports with pinned (gitHead, gitBranch, builtAt) produce identical bytes');
377
+
378
+ // Sanity: omitting builtAt keeps the manifest mutable across calls (so
379
+ // we don't accidentally make this property mandatory for normal use).
380
+ const out3 = path.join(TMP_DIR, 'bug5-c.bundle');
381
+ const out4 = path.join(TMP_DIR, 'bug5-d.bundle');
382
+ await exportBundle(dbPath, FIX, { out: out3, gitHead: 'h', gitBranch: 'b' });
383
+ await new Promise(r => setTimeout(r, 25));
384
+ await exportBundle(dbPath, FIX, { out: out4, gitHead: 'h', gitBranch: 'b' });
385
+ const m3 = readBundleManifest(out3);
386
+ const m4 = readBundleManifest(out4);
387
+ assert(m3.builtAt !== m4.builtAt,
388
+ 'without pin: builtAt differs across calls (Date.now() default preserved)');
389
+ }
390
+
391
+ cleanup();
392
+
393
+ console.log(`\n══════════════════════════════════════════════════════════════`);
394
+ console.log(` Bug regressions: ${passed} passed, ${failed} failed`);
395
+ if (failed > 0) {
396
+ console.error('\n TRACK F BUG TESTS FAILED\n');
397
+ process.exit(1);
398
+ }
399
+ console.log('\n All Track F audit fixes pinned. ✓\n');
400
+ }
401
+
402
+ main().catch(err => {
403
+ console.error('trackf-bugs crashed:', err);
404
+ try { cleanup(); } catch { /* */ }
405
+ process.exit(1);
406
+ });