limn-review 0.1.123

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 (474) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +133 -0
  3. package/dist/ai/runtime.js +535 -0
  4. package/dist/api/plugins/auth.js +169 -0
  5. package/dist/api/plugins/error-handler.js +114 -0
  6. package/dist/api/plugins/rate-limit.js +855 -0
  7. package/dist/api/plugins/security.js +321 -0
  8. package/dist/api/routes/activity.js +103 -0
  9. package/dist/api/routes/ai-runtime.js +67 -0
  10. package/dist/api/routes/auth.js +194 -0
  11. package/dist/api/routes/billing.js +209 -0
  12. package/dist/api/routes/bot-triage.js +1289 -0
  13. package/dist/api/routes/conflicts.js +730 -0
  14. package/dist/api/routes/contact.js +318 -0
  15. package/dist/api/routes/daily-brief.js +58 -0
  16. package/dist/api/routes/flow.js +71 -0
  17. package/dist/api/routes/health.js +7 -0
  18. package/dist/api/routes/insights.js +200 -0
  19. package/dist/api/routes/me.js +458 -0
  20. package/dist/api/routes/mentions.js +22 -0
  21. package/dist/api/routes/mergers.js +8 -0
  22. package/dist/api/routes/ml-labels.js +109 -0
  23. package/dist/api/routes/open-prs.js +35 -0
  24. package/dist/api/routes/prs.js +1633 -0
  25. package/dist/api/routes/reactions.js +217 -0
  26. package/dist/api/routes/repos.js +563 -0
  27. package/dist/api/routes/search.js +41 -0
  28. package/dist/api/routes/threads.js +118 -0
  29. package/dist/api/routes/timeline.js +150 -0
  30. package/dist/api/routes/users.js +67 -0
  31. package/dist/api/routes/webhooks.js +181 -0
  32. package/dist/api/routes/workspaces.js +322 -0
  33. package/dist/app.js +274 -0
  34. package/dist/ascii.js +19 -0
  35. package/dist/auth/account.js +482 -0
  36. package/dist/auth/crypto.js +46 -0
  37. package/dist/bot-triage/resolve.js +59 -0
  38. package/dist/cli.js +388 -0
  39. package/dist/coding/activity.js +80 -0
  40. package/dist/coding/agent.js +288 -0
  41. package/dist/coding/ai-fix/auto-fix.js +212 -0
  42. package/dist/coding/ai-fix/manager.js +370 -0
  43. package/dist/coding/ai-fix/persist.js +213 -0
  44. package/dist/coding/ai-fix/pr-context.js +58 -0
  45. package/dist/coding/ai-fix/prompts.js +138 -0
  46. package/dist/coding/ai-fix/review-seed.js +357 -0
  47. package/dist/coding/ai-fix/routes.js +425 -0
  48. package/dist/coding/git-ops.js +321 -0
  49. package/dist/coding/git.js +184 -0
  50. package/dist/coding/merge.js +262 -0
  51. package/dist/coding/safe-path.js +75 -0
  52. package/dist/coding/schema.js +43 -0
  53. package/dist/config.js +648 -0
  54. package/dist/conflict/diff.js +304 -0
  55. package/dist/conflict/git.js +118 -0
  56. package/dist/conflict/hash.js +73 -0
  57. package/dist/conflict/janitor.js +218 -0
  58. package/dist/conflict/land.js +675 -0
  59. package/dist/conflict/model-types.js +2 -0
  60. package/dist/conflict/model.js +1081 -0
  61. package/dist/conflict/parse.js +142 -0
  62. package/dist/conflict/seam.js +136 -0
  63. package/dist/conflict/session.js +532 -0
  64. package/dist/conflict/suggestion.js +398 -0
  65. package/dist/conflict/tokens.js +72 -0
  66. package/dist/data-dir.js +112 -0
  67. package/dist/db/actor-lanes.js +213 -0
  68. package/dist/db/automation-ids.js +97 -0
  69. package/dist/db/automation-output.js +349 -0
  70. package/dist/db/blast-radius-query.js +36 -0
  71. package/dist/db/blast-radius.js +226 -0
  72. package/dist/db/bot-dormancy.js +115 -0
  73. package/dist/db/bot-overlap.js +579 -0
  74. package/dist/db/bot-volume.js +722 -0
  75. package/dist/db/bot-window.js +35 -0
  76. package/dist/db/branch-queries.js +310 -0
  77. package/dist/db/change-shape.js +243 -0
  78. package/dist/db/changepoint.js +75 -0
  79. package/dist/db/cleanup.js +36 -0
  80. package/dist/db/client.js +102 -0
  81. package/dist/db/code-loc.js +388 -0
  82. package/dist/db/credits.js +79 -0
  83. package/dist/db/daily-brief.js +406 -0
  84. package/dist/db/dependency-cards.js +145 -0
  85. package/dist/db/erase-account.js +255 -0
  86. package/dist/db/export-account.js +189 -0
  87. package/dist/db/failing-checks.js +124 -0
  88. package/dist/db/file-coupling.js +197 -0
  89. package/dist/db/flow-detail.js +661 -0
  90. package/dist/db/flow-pointers.js +197 -0
  91. package/dist/db/flow-settings.js +67 -0
  92. package/dist/db/forecast.js +114 -0
  93. package/dist/db/line-overlap.js +81 -0
  94. package/dist/db/merge-queue.js +189 -0
  95. package/dist/db/migrate.js +17 -0
  96. package/dist/db/migrations/0000_purple_ben_grimm.sql +155 -0
  97. package/dist/db/migrations/0001_colorful_ozymandias.sql +31 -0
  98. package/dist/db/migrations/0002_famous_sersi.sql +9 -0
  99. package/dist/db/migrations/0003_clever_shinobi_shaw.sql +3 -0
  100. package/dist/db/migrations/0004_pale_scalphunter.sql +1 -0
  101. package/dist/db/migrations/0005_daffy_guardian.sql +2 -0
  102. package/dist/db/migrations/0006_cute_violations.sql +45 -0
  103. package/dist/db/migrations/0007_dark_anthem.sql +2 -0
  104. package/dist/db/migrations/0008_multitenant_accounts.sql +44 -0
  105. package/dist/db/migrations/0009_lean_bodies.sql +13 -0
  106. package/dist/db/migrations/0010_lean_bodies_nullable.sql +56 -0
  107. package/dist/db/migrations/0011_pr_diff_size.sql +10 -0
  108. package/dist/db/migrations/0012_findings_included_default.sql +7 -0
  109. package/dist/db/migrations/0013_review_routing.sql +9 -0
  110. package/dist/db/migrations/0014_account_last_active.sql +6 -0
  111. package/dist/db/migrations/0015_repos_inbox_watch.sql +9 -0
  112. package/dist/db/migrations/0016_repo_viewer_permission.sql +7 -0
  113. package/dist/db/migrations/0017_account_display_name.sql +7 -0
  114. package/dist/db/migrations/0018_finding_comment_kind.sql +7 -0
  115. package/dist/db/migrations/0019_finding_file_in_diff.sql +7 -0
  116. package/dist/db/migrations/0020_claude_review_cost_telemetry.sql +11 -0
  117. package/dist/db/migrations/0021_account_feed_last_seen.sql +7 -0
  118. package/dist/db/migrations/0022_ci_status_events.sql +21 -0
  119. package/dist/db/migrations/0023_ai_usage.sql +21 -0
  120. package/dist/db/migrations/0024_pr_head_ref_name.sql +6 -0
  121. package/dist/db/migrations/0025_account_billing.sql +7 -0
  122. package/dist/db/migrations/0026_ai_credit_allowance.sql +5 -0
  123. package/dist/db/migrations/0027_user_github_type.sql +6 -0
  124. package/dist/db/migrations/0028_bot_review_classification.sql +21 -0
  125. package/dist/db/migrations/0029_bot_mute_rules.sql +19 -0
  126. package/dist/db/migrations/0030_teams.sql +32 -0
  127. package/dist/db/migrations/0031_events_pr_idx.sql +7 -0
  128. package/dist/db/migrations/0032_benchmark_contributions.sql +29 -0
  129. package/dist/db/migrations/0033_thread_addressed_confidence.sql +9 -0
  130. package/dist/db/migrations/0034_thread_resolved_at.sql +6 -0
  131. package/dist/db/migrations/0035_pr_first_review_requested_at.sql +5 -0
  132. package/dist/db/migrations/0036_search_index.sql +25 -0
  133. package/dist/db/migrations/0037_author_indexes.sql +15 -0
  134. package/dist/db/migrations/0038_auto_merge_requests.sql +37 -0
  135. package/dist/db/migrations/0039_branch_status.sql +44 -0
  136. package/dist/db/migrations/0040_pr_review_decision.sql +10 -0
  137. package/dist/db/migrations/0041_branch_commit_detail.sql +38 -0
  138. package/dist/db/migrations/0042_repo_reviewers.sql +176 -0
  139. package/dist/db/migrations/0043_account_reviewers.sql +131 -0
  140. package/dist/db/migrations/0044_workspaces.sql +138 -0
  141. package/dist/db/migrations/0045_workspace_reviewers.sql +180 -0
  142. package/dist/db/migrations/0046_drop_inbox_watch.sql +25 -0
  143. package/dist/db/migrations/0047_ml_comment_labels.sql +40 -0
  144. package/dist/db/migrations/0048_ml_vendor_severity.sql +31 -0
  145. package/dist/db/migrations/0049_seat_pricing_repo_description.sql +21 -0
  146. package/dist/db/migrations/0050_users_app_slug.sql +12 -0
  147. package/dist/db/migrations/0051_auto_merge_queue.sql +17 -0
  148. package/dist/db/migrations/0052_trunk_ci_status_events.sql +43 -0
  149. package/dist/db/migrations/0053_automation_roles.sql +79 -0
  150. package/dist/db/migrations/0054_automation_vendor_kinds.sql +514 -0
  151. package/dist/db/migrations/0055_auto_merge_phase.sql +12 -0
  152. package/dist/db/migrations/0056_pr_mentions.sql +22 -0
  153. package/dist/db/migrations/0057_large_pr_threshold.sql +14 -0
  154. package/dist/db/migrations/0058_pending_mute.sql +53 -0
  155. package/dist/db/migrations/0059_merge_queue_state.sql +33 -0
  156. package/dist/db/migrations/0060_drop_my_turn_dismissals.sql +33 -0
  157. package/dist/db/migrations/0061_auto_merge_durable_anchors.sql +27 -0
  158. package/dist/db/migrations/0062_blast_radius_config.sql +21 -0
  159. package/dist/db/migrations/0063_repo_file_coupling.sql +33 -0
  160. package/dist/db/migrations/0064_pr_content_kind.sql +21 -0
  161. package/dist/db/migrations/0065_workspace_flow_settings.sql +13 -0
  162. package/dist/db/migrations/0066_review_request_events.sql +31 -0
  163. package/dist/db/migrations/0067_pr_security_signals.sql +86 -0
  164. package/dist/db/migrations/0068_my_turn_settings.sql +18 -0
  165. package/dist/db/migrations/0069_my_turn_dismissals.sql +41 -0
  166. package/dist/db/migrations/0070_claude_review_follow_up_ticket.sql +22 -0
  167. package/dist/db/migrations/0071_claude_review_trigger.sql +11 -0
  168. package/dist/db/migrations/0072_commit_message_headline.sql +13 -0
  169. package/dist/db/migrations/0073_claude_review_chat.sql +31 -0
  170. package/dist/db/migrations/0074_adopt_agentic_tables.sql +98 -0
  171. package/dist/db/migrations/0075_review_lens_drop_learnings.sql +15 -0
  172. package/dist/db/migrations/0076_claude_review_threads.sql +17 -0
  173. package/dist/db/migrations/0077_claude_review_ci_failures.sql +10 -0
  174. package/dist/db/migrations/0078_ai_fix_change_report.sql +17 -0
  175. package/dist/db/migrations/0079_claude_finding_story.sql +16 -0
  176. package/dist/db/migrations/meta/0000_snapshot.json +1116 -0
  177. package/dist/db/migrations/meta/0001_snapshot.json +1321 -0
  178. package/dist/db/migrations/meta/0002_snapshot.json +1375 -0
  179. package/dist/db/migrations/meta/0003_snapshot.json +1396 -0
  180. package/dist/db/migrations/meta/0004_snapshot.json +1416 -0
  181. package/dist/db/migrations/meta/0005_snapshot.json +1430 -0
  182. package/dist/db/migrations/meta/0006_snapshot.json +1745 -0
  183. package/dist/db/migrations/meta/0007_snapshot.json +1759 -0
  184. package/dist/db/migrations/meta/_journal.json +566 -0
  185. package/dist/db/migrations-pg/0000_tired_alex_power.sql +268 -0
  186. package/dist/db/migrations-pg/0001_dizzy_mockingbird.sql +3 -0
  187. package/dist/db/migrations-pg/0002_glorious_ikaris.sql +4 -0
  188. package/dist/db/migrations-pg/0003_melted_secret_warriors.sql +2 -0
  189. package/dist/db/migrations-pg/0004_numerous_triton.sql +1 -0
  190. package/dist/db/migrations-pg/0005_thick_blockbuster.sql +2 -0
  191. package/dist/db/migrations-pg/0006_majestic_dracula.sql +1 -0
  192. package/dist/db/migrations-pg/0007_same_human_torch.sql +2 -0
  193. package/dist/db/migrations-pg/0008_slimy_toxin.sql +1 -0
  194. package/dist/db/migrations-pg/0009_fuzzy_jubilee.sql +4 -0
  195. package/dist/db/migrations-pg/0010_late_hammerhead.sql +1 -0
  196. package/dist/db/migrations-pg/0011_happy_leech.sql +16 -0
  197. package/dist/db/migrations-pg/0012_woozy_captain_midlands.sql +16 -0
  198. package/dist/db/migrations-pg/0013_yielding_nicolaos.sql +1 -0
  199. package/dist/db/migrations-pg/0014_wakeful_bill_hollister.sql +2 -0
  200. package/dist/db/migrations-pg/0015_aspiring_black_tom.sql +1 -0
  201. package/dist/db/migrations-pg/0016_cute_ghost_rider.sql +30 -0
  202. package/dist/db/migrations-pg/0017_far_changeling.sql +24 -0
  203. package/dist/db/migrations-pg/0018_fancy_miss_america.sql +1 -0
  204. package/dist/db/migrations-pg/0019_benchmark_contributions.sql +22 -0
  205. package/dist/db/migrations-pg/0020_majestic_edwin_jarvis.sql +3 -0
  206. package/dist/db/migrations-pg/0021_green_salo.sql +1 -0
  207. package/dist/db/migrations-pg/0022_wise_baron_strucker.sql +1 -0
  208. package/dist/db/migrations-pg/0023_search_index.sql +19 -0
  209. package/dist/db/migrations-pg/0024_author_indexes.sql +9 -0
  210. package/dist/db/migrations-pg/0025_auto_merge_requests.sql +26 -0
  211. package/dist/db/migrations-pg/0026_branch_status.sql +32 -0
  212. package/dist/db/migrations-pg/0027_pr_review_decision.sql +5 -0
  213. package/dist/db/migrations-pg/0028_branch_commit_detail.sql +9 -0
  214. package/dist/db/migrations-pg/0029_repo_reviewers.sql +127 -0
  215. package/dist/db/migrations-pg/0030_account_reviewers.sql +77 -0
  216. package/dist/db/migrations-pg/0031_workspaces.sql +131 -0
  217. package/dist/db/migrations-pg/0032_workspace_reviewers.sql +165 -0
  218. package/dist/db/migrations-pg/0033_drop_inbox_watch.sql +14 -0
  219. package/dist/db/migrations-pg/0034_ml_comment_labels.sql +33 -0
  220. package/dist/db/migrations-pg/0035_ml_vendor_severity.sql +16 -0
  221. package/dist/db/migrations-pg/0036_seat_pricing_repo_description.sql +12 -0
  222. package/dist/db/migrations-pg/0037_users_app_slug.sql +9 -0
  223. package/dist/db/migrations-pg/0038_auto_merge_queue.sql +10 -0
  224. package/dist/db/migrations-pg/0039_trunk_ci_status_events.sql +27 -0
  225. package/dist/db/migrations-pg/0040_automation_roles.sql +63 -0
  226. package/dist/db/migrations-pg/0041_automation_vendor_kinds.sql +488 -0
  227. package/dist/db/migrations-pg/0042_auto_merge_phase.sql +8 -0
  228. package/dist/db/migrations-pg/0043_pr_mentions.sql +19 -0
  229. package/dist/db/migrations-pg/0044_large_pr_threshold.sql +6 -0
  230. package/dist/db/migrations-pg/0045_pending_mute.sql +26 -0
  231. package/dist/db/migrations-pg/0046_merge_queue_state.sql +18 -0
  232. package/dist/db/migrations-pg/0047_drop_my_turn_dismissals.sql +21 -0
  233. package/dist/db/migrations-pg/0048_auto_merge_durable_anchors.sql +27 -0
  234. package/dist/db/migrations-pg/0049_blast_radius_config.sql +8 -0
  235. package/dist/db/migrations-pg/0050_repo_file_coupling.sql +13 -0
  236. package/dist/db/migrations-pg/0051_pr_content_kind.sql +6 -0
  237. package/dist/db/migrations-pg/0052_workspace_flow_settings.sql +4 -0
  238. package/dist/db/migrations-pg/0053_review_request_events.sql +18 -0
  239. package/dist/db/migrations-pg/0054_pr_security_signals.sql +59 -0
  240. package/dist/db/migrations-pg/0055_my_turn_settings.sql +6 -0
  241. package/dist/db/migrations-pg/0056_my_turn_dismissals.sql +21 -0
  242. package/dist/db/migrations-pg/0057_claude_review_follow_up_ticket.sql +8 -0
  243. package/dist/db/migrations-pg/0058_claude_review_trigger.sql +4 -0
  244. package/dist/db/migrations-pg/0059_commit_message_headline.sql +3 -0
  245. package/dist/db/migrations-pg/0060_claude_review_chat.sql +24 -0
  246. package/dist/db/migrations-pg/0061_adopt_agentic_tables.sql +79 -0
  247. package/dist/db/migrations-pg/0062_review_lens_drop_learnings.sql +5 -0
  248. package/dist/db/migrations-pg/0063_claude_review_threads.sql +7 -0
  249. package/dist/db/migrations-pg/0064_claude_review_ci_failures.sql +4 -0
  250. package/dist/db/migrations-pg/0065_ai_fix_change_report.sql +9 -0
  251. package/dist/db/migrations-pg/0066_claude_finding_story.sql +7 -0
  252. package/dist/db/migrations-pg/meta/0000_snapshot.json +2137 -0
  253. package/dist/db/migrations-pg/meta/0001_snapshot.json +2143 -0
  254. package/dist/db/migrations-pg/meta/0002_snapshot.json +2170 -0
  255. package/dist/db/migrations-pg/meta/0003_snapshot.json +2182 -0
  256. package/dist/db/migrations-pg/meta/0004_snapshot.json +2188 -0
  257. package/dist/db/migrations-pg/meta/0005_snapshot.json +2201 -0
  258. package/dist/db/migrations-pg/meta/0006_snapshot.json +2207 -0
  259. package/dist/db/migrations-pg/meta/0007_snapshot.json +2219 -0
  260. package/dist/db/migrations-pg/meta/0008_snapshot.json +2226 -0
  261. package/dist/db/migrations-pg/meta/0009_snapshot.json +2250 -0
  262. package/dist/db/migrations-pg/meta/0010_snapshot.json +2256 -0
  263. package/dist/db/migrations-pg/meta/0011_snapshot.json +2412 -0
  264. package/dist/db/migrations-pg/meta/0012_snapshot.json +2528 -0
  265. package/dist/db/migrations-pg/meta/0013_snapshot.json +2534 -0
  266. package/dist/db/migrations-pg/meta/0014_snapshot.json +2547 -0
  267. package/dist/db/migrations-pg/meta/0015_snapshot.json +2553 -0
  268. package/dist/db/migrations-pg/meta/0016_snapshot.json +2774 -0
  269. package/dist/db/migrations-pg/meta/0017_snapshot.json +2999 -0
  270. package/dist/db/migrations-pg/meta/0018_snapshot.json +3020 -0
  271. package/dist/db/migrations-pg/meta/0020_snapshot.json +3228 -0
  272. package/dist/db/migrations-pg/meta/0021_snapshot.json +3234 -0
  273. package/dist/db/migrations-pg/meta/0022_snapshot.json +3240 -0
  274. package/dist/db/migrations-pg/meta/_journal.json +475 -0
  275. package/dist/db/ml-labels.js +1666 -0
  276. package/dist/db/my-turn-dismissals.js +343 -0
  277. package/dist/db/my-turn-settings.js +34 -0
  278. package/dist/db/pending-classify.js +46 -0
  279. package/dist/db/pending-mute.js +171 -0
  280. package/dist/db/pending-tabs.js +238 -0
  281. package/dist/db/period-metrics.js +820 -0
  282. package/dist/db/person-period.js +763 -0
  283. package/dist/db/pr-intervals.js +930 -0
  284. package/dist/db/pr-liveness.js +240 -0
  285. package/dist/db/pr-mentions.js +279 -0
  286. package/dist/db/pr-merge-queue-stamp.js +91 -0
  287. package/dist/db/queries.js +14631 -0
  288. package/dist/db/reaction-targets.js +94 -0
  289. package/dist/db/repo-activity.js +171 -0
  290. package/dist/db/retention.js +138 -0
  291. package/dist/db/review-threads-for-review.js +240 -0
  292. package/dist/db/run-migrations.js +24 -0
  293. package/dist/db/schema.pg.js +1220 -0
  294. package/dist/db/schema.sqlite.js +1801 -0
  295. package/dist/db/search.js +317 -0
  296. package/dist/db/security-alerts.js +218 -0
  297. package/dist/db/synthesis-input.js +565 -0
  298. package/dist/db/triage.js +320 -0
  299. package/dist/db/usage.js +63 -0
  300. package/dist/db/work-plan.js +1086 -0
  301. package/dist/db/working-hours.js +161 -0
  302. package/dist/feed/my-turn.js +122 -0
  303. package/dist/github/actions-logs.js +397 -0
  304. package/dist/github/auth.js +86 -0
  305. package/dist/github/branch-queries.js +229 -0
  306. package/dist/github/client.js +454 -0
  307. package/dist/github/codeowners.js +221 -0
  308. package/dist/github/commit-checks.js +105 -0
  309. package/dist/github/compare.js +110 -0
  310. package/dist/github/diff-anchor.js +227 -0
  311. package/dist/github/mutations.js +706 -0
  312. package/dist/github/pr-file-content.js +254 -0
  313. package/dist/github/pr-liveness.js +161 -0
  314. package/dist/github/queries.js +875 -0
  315. package/dist/github/rate-budget.js +129 -0
  316. package/dist/github/reactions.js +161 -0
  317. package/dist/github/reviewer-suggest.js +104 -0
  318. package/dist/github/team-reviewers.js +96 -0
  319. package/dist/index.js +184 -0
  320. package/dist/merge/auto-merge-runner.js +960 -0
  321. package/dist/ml/severity-client.js +218 -0
  322. package/dist/pr/detail-enricher.js +26 -0
  323. package/dist/pro/bind.js +302 -0
  324. package/dist/pro/contract.js +37 -0
  325. package/dist/pro/migrate.js +64 -0
  326. package/dist/review/agent-context.js +127 -0
  327. package/dist/review/agent.js +385 -0
  328. package/dist/review/agentic.js +59 -0
  329. package/dist/review/auth.js +94 -0
  330. package/dist/review/chat-agent.js +179 -0
  331. package/dist/review/claude-review/ai-ready.js +16 -0
  332. package/dist/review/claude-review/auto-settings.js +139 -0
  333. package/dist/review/claude-review/auto.js +244 -0
  334. package/dist/review/claude-review/chat.js +489 -0
  335. package/dist/review/claude-review/ci-failures.js +350 -0
  336. package/dist/review/claude-review/follow-up.js +310 -0
  337. package/dist/review/claude-review/manager.js +686 -0
  338. package/dist/review/claude-review/persist.js +996 -0
  339. package/dist/review/claude-review/prompts.js +357 -0
  340. package/dist/review/claude-review/routes.js +518 -0
  341. package/dist/review/claude-review/routing.js +61 -0
  342. package/dist/review/claude-review/specialists.js +190 -0
  343. package/dist/review/claude-review/threads.js +265 -0
  344. package/dist/review/claude-review/ticket.js +282 -0
  345. package/dist/review/clone-hygiene.js +380 -0
  346. package/dist/review/clone-manager.js +367 -0
  347. package/dist/review/llm.js +113 -0
  348. package/dist/review/model-options.js +48 -0
  349. package/dist/review/plugin-providers.js +12 -0
  350. package/dist/review/post-review.js +216 -0
  351. package/dist/review/post-seam.js +161 -0
  352. package/dist/review/prepare.js +168 -0
  353. package/dist/review/pricing.js +17 -0
  354. package/dist/review/schema.js +156 -0
  355. package/dist/review/submit-map.js +41 -0
  356. package/dist/review/usage.js +90 -0
  357. package/dist/shared/claude-review.js +335 -0
  358. package/dist/shared/conflict-fold.js +151 -0
  359. package/dist/shared/conflicts.js +30 -0
  360. package/dist/shared/flow-settings.js +191 -0
  361. package/dist/shared/index.js +8 -0
  362. package/dist/shared/my-turn-settings.js +238 -0
  363. package/dist/shared/pending-rules.js +281 -0
  364. package/dist/shared/product.js +54 -0
  365. package/dist/shared/types.js +896 -0
  366. package/dist/status.js +386 -0
  367. package/dist/sync/adaptive.js +185 -0
  368. package/dist/sync/app-attribution.js +66 -0
  369. package/dist/sync/auth-notices.js +36 -0
  370. package/dist/sync/backfill-ci-history.js +315 -0
  371. package/dist/sync/backfill-commit-headlines.js +136 -0
  372. package/dist/sync/backfill-pr-security.js +175 -0
  373. package/dist/sync/backfill-review-requests.js +97 -0
  374. package/dist/sync/benchmark-rollup.js +50 -0
  375. package/dist/sync/bot-detection.js +309 -0
  376. package/dist/sync/bot-resolution-markers.js +30 -0
  377. package/dist/sync/branch-status.js +634 -0
  378. package/dist/sync/classify-change-shape.js +150 -0
  379. package/dist/sync/commit-files.js +74 -0
  380. package/dist/sync/derive-thread-state.js +99 -0
  381. package/dist/sync/drain-review-threads.js +212 -0
  382. package/dist/sync/hydrate-detail.js +482 -0
  383. package/dist/sync/mention-scan.js +89 -0
  384. package/dist/sync/ml-enrichment.js +478 -0
  385. package/dist/sync/pr-change-signal.js +96 -0
  386. package/dist/sync/pr-liveness-sweep.js +139 -0
  387. package/dist/sync/pr-settle.js +381 -0
  388. package/dist/sync/refresh-pr.js +163 -0
  389. package/dist/sync/reparse-vendor-badges.js +495 -0
  390. package/dist/sync/resync-after-write.js +280 -0
  391. package/dist/sync/review-fingerprint.js +117 -0
  392. package/dist/sync/reviewer-behavior.js +103 -0
  393. package/dist/sync/reviewer-classify.js +503 -0
  394. package/dist/sync/routing-files.js +97 -0
  395. package/dist/sync/scheduled-jobs.js +12 -0
  396. package/dist/sync/scheduler.js +159 -0
  397. package/dist/sync/security-detect.js +521 -0
  398. package/dist/sync/sync-manager.js +750 -0
  399. package/dist/sync/sync-one-pr.js +205 -0
  400. package/dist/sync/sync-repo.js +400 -0
  401. package/dist/sync/unsettled-prs.js +506 -0
  402. package/dist/sync/upsert.js +1190 -0
  403. package/package.json +56 -0
  404. package/public/apple-touch-icon.png +0 -0
  405. package/public/assets/AiFixTab-CQP6PT5u.js +2 -0
  406. package/public/assets/ClaudeReviewTab-DQJ-58ZA.js +8 -0
  407. package/public/assets/PrBotBehaviourTab-CCnFrWbC.js +1 -0
  408. package/public/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
  409. package/public/assets/highlight-BEHUn5zE.css +10 -0
  410. package/public/assets/highlight-CIIZLa6Z.js +12 -0
  411. package/public/assets/index-BDnYs6zg.js +73 -0
  412. package/public/assets/index-mCuI--RC.css +1 -0
  413. package/public/assets/markdown-I_JLLVAZ.js +32 -0
  414. package/public/assets/perfProbe-Da13EhTE.js +1 -0
  415. package/public/assets/tanstack-5qsxxaLe.js +17 -0
  416. package/public/assets/vis-C3BCkP5U.js +1280 -0
  417. package/public/assets/vis-ziCVehMy.css +1 -0
  418. package/public/favicon-16.png +0 -0
  419. package/public/favicon-32.png +0 -0
  420. package/public/favicon-48.png +0 -0
  421. package/public/favicon.svg +5 -0
  422. package/public/index.html +41 -0
  423. package/public-landing/apple-touch-icon.png +0 -0
  424. package/public-landing/assets/archivo-latin-500-BWmvx2Mg.woff2 +0 -0
  425. package/public-landing/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
  426. package/public-landing/assets/index-CA3eEGXO.js +40 -0
  427. package/public-landing/assets/index-naQrQ6Fe.css +1 -0
  428. package/public-landing/assets/jetbrains-mono-latin-400-RruCX7RW.woff2 +0 -0
  429. package/public-landing/assets/newsreader-latin-400-DExrRRrN.woff2 +0 -0
  430. package/public-landing/assets/newsreader-latin-400-italic-b0I-AwbV.woff2 +0 -0
  431. package/public-landing/bots/index.html +156 -0
  432. package/public-landing/contact/index.html +156 -0
  433. package/public-landing/cookies/index.html +156 -0
  434. package/public-landing/demo/limn-walkthrough-poster.jpg +0 -0
  435. package/public-landing/demo/limn-walkthrough.cues.json +44 -0
  436. package/public-landing/demo/limn-walkthrough.mp4 +0 -0
  437. package/public-landing/favicon-16.png +0 -0
  438. package/public-landing/favicon-32.png +0 -0
  439. package/public-landing/favicon-48.png +0 -0
  440. package/public-landing/favicon.svg +5 -0
  441. package/public-landing/features/index.html +156 -0
  442. package/public-landing/for-developers/index.html +156 -0
  443. package/public-landing/for-managers/index.html +156 -0
  444. package/public-landing/how-it-works/index.html +156 -0
  445. package/public-landing/how-we-measure/index.html +156 -0
  446. package/public-landing/icon-192.png +0 -0
  447. package/public-landing/icon-512.png +0 -0
  448. package/public-landing/index.html +156 -0
  449. package/public-landing/insights/index.html +156 -0
  450. package/public-landing/og-image.png +0 -0
  451. package/public-landing/pricing/index.html +156 -0
  452. package/public-landing/privacy/index.html +156 -0
  453. package/public-landing/pro/index.html +156 -0
  454. package/public-landing/reviews/index.html +156 -0
  455. package/public-landing/robots.txt +8 -0
  456. package/public-landing/shots/benchmark.png +0 -0
  457. package/public-landing/shots/bot-roi.png +0 -0
  458. package/public-landing/shots/bot-settings.png +0 -0
  459. package/public-landing/shots/chronology.png +0 -0
  460. package/public-landing/shots/feed.png +0 -0
  461. package/public-landing/shots/flow-metrics.png +0 -0
  462. package/public-landing/shots/free-reports.png +0 -0
  463. package/public-landing/shots/pending-board.png +0 -0
  464. package/public-landing/shots/pending-card.png +0 -0
  465. package/public-landing/shots/period-report.png +0 -0
  466. package/public-landing/shots/pr-detail.png +0 -0
  467. package/public-landing/shots/pr-threads.png +0 -0
  468. package/public-landing/shots/reach.png +0 -0
  469. package/public-landing/shots/repo-rows.png +0 -0
  470. package/public-landing/shots/severity-findings.png +0 -0
  471. package/public-landing/shots/severity-strip.png +0 -0
  472. package/public-landing/site.webmanifest +14 -0
  473. package/public-landing/sitemap.xml +51 -0
  474. package/public-landing/terms/index.html +156 -0
@@ -0,0 +1,36 @@
1
+ import { and, eq } from 'drizzle-orm';
2
+ import { db, schema } from './client.js';
3
+ import { blastSignalsFor } from './blast-radius.js';
4
+ import { hubReadingFor, loadRepoCoupling } from './file-coupling.js';
5
+ // The one-PR blast-radius read, for the Pro `impact` annotation's grounding (ctx.queries
6
+ // .getBlastSignals). Deliberately NOT in queries.ts: that file contains literal NUL bytes around
7
+ // offset 132k, so `rg`/`grep` silently under-report matches in it, and a seam the plugin depends
8
+ // on should be greppable.
9
+ //
10
+ // ⚠ OWNERSHIP-SCOPED, and the scope is on the PR row itself (`pullRequests.accountId`), not a
11
+ // join to repos — an id from a request body reaching a foreign PR here would let a caller spend
12
+ // their own credits describing someone else's diff. A foreign or unknown id resolves to null and
13
+ // the caller 404s, the same rule every id-addressed getter in this app follows.
14
+ export async function getBlastSignalsForPr(accountId, prId) {
15
+ const { pullRequests } = schema;
16
+ const rows = await db
17
+ .select({
18
+ repoId: pullRequests.repoId,
19
+ files: pullRequests.files,
20
+ additions: pullRequests.additions,
21
+ deletions: pullRequests.deletions,
22
+ changedFiles: pullRequests.changedFiles,
23
+ })
24
+ .from(pullRequests)
25
+ .where(and(eq(pullRequests.accountId, accountId), eq(pullRequests.id, prId)))
26
+ .limit(1)
27
+ .execute();
28
+ const pr = rows[0];
29
+ if (!pr)
30
+ return null;
31
+ const coupling = await loadRepoCoupling(accountId, [pr.repoId]);
32
+ // ⚠ null here is the SAME "we never measured this pull request" the chip renders nothing for.
33
+ // The caller must skip rather than describe a diff nobody measured.
34
+ return blastSignalsFor(pr, hubReadingFor(pr.files, coupling.get(pr.repoId)));
35
+ }
36
+ //# sourceMappingURL=blast-radius-query.js.map
@@ -0,0 +1,226 @@
1
+ import { codeLocFor, isNonCodeFile, PR_FILES_PAGE_CAP } from './code-loc.js';
2
+ // ============================================================================
3
+ // BLAST RADIUS — how far a pull request can REACH, folded from its stored files
4
+ // ============================================================================
5
+ //
6
+ // The second, ORTHOGONAL reading beside the large-PR flag. Size is a weak proxy for risk: a
7
+ // 2,000-line lockfile bump is trivial and a four-line change to a database migration is not.
8
+ // This module produces the EVIDENCE VECTOR; it never decides a level. The level is decided by
9
+ // exactly one function, `blastRadius()` in the SPA's `lib/ui.ts`, so that changing the
10
+ // sensitivity dial in Settings repaints every surface with no cache invalidation anywhere.
11
+ //
12
+ // ---- WHY THIS IS NOT THE CLAUDE REVIEW ROUTER ------------------------------
13
+ //
14
+ // `decideReviewMode` (packages/pro/src/claude-review/routing.ts) asks a genuinely similar
15
+ // question — "can this be reviewed from the diff alone?" — and its SHAPE is the ancestor of the
16
+ // arms below (file / line / directory / subsystem ceilings, a contract-touch signal,
17
+ // `allFilesNew`). It cannot be CALLED here, for three structural reasons:
18
+ //
19
+ // 1. It runs on the DIFF BODY — `analyzeDiff` over the noise-stripped unified diff that
20
+ // `review/prepare.ts` fetches through the `gh` CLI. That is a GitHub call per pull request,
21
+ // and NOTHING ON THE PENDING BOARD MAY FETCH ON MOUNT (fifty cards would be ~150 calls to
22
+ // paint a screen). This module reads columns that are already on the row.
23
+ // 2. Claude Review is LOCAL-ONLY and force-disabled in cloud. Blast radius works in both modes.
24
+ // 3. Its gate is deliberately OVER-conservative — any ambiguity routes to `worktree` — because
25
+ // over-routing there only costs money. Over-routing HERE marks everything `high` and the
26
+ // feature stops meaning anything.
27
+ //
28
+ // ⚠ FOR THE SAME REASONS IT MUST NOT IMPORT `NOISE_GLOBS` OR `API_PATH_PATTERNS`. Both live in
29
+ // `review/prepare.ts` and both are tuned for a paid agent's diff budget, where over-matching is
30
+ // SAFE. Over-matching here is a false claim on screen. Editing either to suit this feature would
31
+ // silently change what Claude Review reviews, with no test to catch it.
32
+ //
33
+ // ---- THE FOUR PATH CLASSIFIERS, AND WHY NONE MAY BE FOLDED INTO ANOTHER -----
34
+ //
35
+ // `db/code-loc.ts` documents the first three. This is the fourth, and the same rule applies:
36
+ // they answer four different questions and are NOT meant to agree.
37
+ //
38
+ // 1. `NOISE_GLOBS` (review/prepare.ts) — "strip this from the diff the paid AGENT reads?"
39
+ // 2. `isLockFile` (frontend lib/diff) — "start this file's diff COLLAPSED?"
40
+ // 3. `isNonCodeFile` (db/code-loc.ts) — "does this churn count as CODE a human must read?"
41
+ // 4. `BLAST_SURFACES` + `isTestFile` (HERE) — "do this file's CONSUMERS LIVE OUTSIDE THE DIFF?"
42
+ //
43
+ // Number 4 is the only one asking about REACH rather than volume or presentation, which is why
44
+ // it is a list of contract shapes rather than a list of things to ignore. It sits ON TOP of
45
+ // number 3: `isNonCodeFile` is reused verbatim to split code from prose/config, and this file
46
+ // adds only the two judgements code-loc has no opinion about — is it a test, and is it a
47
+ // contract.
48
+ //
49
+ // ---- CALIBRATION -----------------------------------------------------------
50
+ //
51
+ // Measured over 1,405 open pull requests with a usable measurement, across 22 real repositories:
52
+ // low 34.3% / medium 35.1% / high 29.7% / unknown 0.8%. Arm fire counts: dirs 230, hub 174,
53
+ // files 165, lines 160, subsystems 57 — none redundant. Surface frequency: deps 222, schema 55,
54
+ // ci 43, dts 2, migration 2, sql 2. That last row is why `deps` and `ci` are carried but are NOT
55
+ // high arms (see `BlastSurface` in packages/shared for each one's argument), and why migrations
56
+ // and IDL are rare-but-decisive rather than common.
57
+ /** Path shapes whose consumers live OUTSIDE the diff. Ordered most-consequential first; the
58
+ * emitted `surfaces[]` preserves this order so a rendered list needs no client-side sort.
59
+ *
60
+ * ⚠ Matched CASE-INSENSITIVELY on the normalised path, like `isNonCodeFile` and unlike the
61
+ * agent's `NOISE_GLOBS` — a `Migrations/` directory is still a migrations directory. */
62
+ const BLAST_SURFACES = [
63
+ // A migrations directory. The one change on this list that is genuinely hard to undo.
64
+ ['db_migration', /(^|\/)migrations?\//],
65
+ // A schema or models tree, or a `schema.*` module. ⚠ THE KNOWN FALSE-POSITIVE SHAPE: in a repo
66
+ // whose PRODUCT is an ORM, its own `src/**/schema/**` source tree trips a rule that means
67
+ // "this pull request changes the database". Measured: 55 hits, almost all one such repo. That
68
+ // is what `surfacesOff` in the account's config exists to switch off, and it is why the chip
69
+ // always NAMES the surface rather than asserting a bare level.
70
+ ['db_schema', /(^|\/)(schema|schemas|models)\/|(^|\/)schemas?\.[a-z0-9]+$/],
71
+ ['sql', /\.sql$/],
72
+ ['public_types', /\.d\.ts$/],
73
+ ['idl', /\.(proto|thrift|graphql|gql)$/],
74
+ ['openapi', /(^|\/)(openapi|swagger)[^/]*\.(ya?ml|json)$/],
75
+ ['infra', /\.(tf|tfvars)$|(^|\/)(helm|charts|k8s|kubernetes)\//],
76
+ ['auth', /(^|\/)(auth|authz|authn|security|permissions?|acl|crypto)\//],
77
+ // MEDIUM, never high — see the BlastSurface doc.
78
+ ['ci', /(^|\/)\.github\/workflows\//],
79
+ // Narration only, never an arm — see the BlastSurface doc.
80
+ [
81
+ 'deps',
82
+ /(^|\/)(package\.json|requirements\.txt|go\.mod|go\.sum|cargo\.toml|gemfile|pyproject\.toml|build\.gradle|pom\.xml|pnpm-lock\.yaml|package-lock\.json|yarn\.lock|poetry\.lock|composer\.json)$/,
83
+ ],
84
+ ];
85
+ function normalize(path) {
86
+ let p = path.replace(/\\/g, '/');
87
+ if (p.startsWith('./'))
88
+ p = p.slice(2);
89
+ if (p.startsWith('/'))
90
+ p = p.slice(1);
91
+ return p.toLowerCase();
92
+ }
93
+ /**
94
+ * Is this path a TEST?
95
+ *
96
+ * Tests are code — `isNonCodeFile` correctly counts them, because a reviewer does read them —
97
+ * but they are code whose blast radius is bounded by construction: a broken test fails CI rather
98
+ * than production. So they are counted APART, and "every code file here is a test" is one of the
99
+ * two ways a pull request earns `low`.
100
+ *
101
+ * ⚠ Deliberately does NOT match a bare `test`/`tests` BASENAME, only a directory segment or a
102
+ * conventional suffix. `scripts/test` is an executable and `test.go` in a package root is
103
+ * ambiguous enough to leave counted — the same "directory segments only" trap `isNonCodeFile`
104
+ * documents, where treating a basename as a directory silently uncounts a hand-written file.
105
+ */
106
+ export function isTestFile(path) {
107
+ const p = normalize(path);
108
+ if (/(^|\/)(tests?|__tests__|specs?|e2e|testing)\//.test(p))
109
+ return true;
110
+ if (/\.(test|spec)\.[a-z0-9]+$/.test(p))
111
+ return true;
112
+ if (/_test\.[a-z0-9]+$/.test(p))
113
+ return true;
114
+ if (/(^|\/)test_[^/]+\.py$/.test(p))
115
+ return true;
116
+ return false;
117
+ }
118
+ /** The contract surfaces this path matches, if any. Exported for the fixture tests. */
119
+ export function surfacesForPath(path) {
120
+ const p = normalize(path);
121
+ const out = [];
122
+ for (const [surface, re] of BLAST_SURFACES)
123
+ if (re.test(p))
124
+ out.push(surface);
125
+ return out;
126
+ }
127
+ function dirOf(path) {
128
+ const i = path.lastIndexOf('/');
129
+ return i === -1 ? '.' : path.slice(0, i);
130
+ }
131
+ function subsystemOf(path) {
132
+ const i = path.indexOf('/');
133
+ return i === -1 ? path : path.slice(0, i);
134
+ }
135
+ /**
136
+ * The change shape, but only when it still describes the CURRENT head.
137
+ *
138
+ * ⚠ RETURNS null ON ANY MISMATCH OR ABSENCE, and null never demotes anything. Three separate
139
+ * things land here and all three are the same answer: never classified, classified at a head
140
+ * that has since moved, or classified with no sha recorded at all.
141
+ */
142
+ function contentKindFor(pr) {
143
+ if (pr.contentKind == null)
144
+ return null;
145
+ if (pr.contentKindSha == null || pr.headSha == null)
146
+ return null;
147
+ return pr.contentKindSha === pr.headSha ? pr.contentKind : null;
148
+ }
149
+ /**
150
+ * Fold one stored pull-request row into its blast-radius signal vector.
151
+ *
152
+ * Returns `null` for every UNKNOWN — and it does not re-derive what "unknown" means, it calls
153
+ * `codeLocFor` and inherits all four of its data traps (`files IS NULL` at 18.5% of the corpus,
154
+ * a never-observed size at 18.8%, the overloaded `files = []` sentinel, and the page cap). One
155
+ * definition of "we did not measure this pull request", used by both features.
156
+ *
157
+ * ⚠ `null` IS NOT "low". Roughly a tenth of open pull requests land here and every surface must
158
+ * render nothing for them — a reader who can tell an unmeasured pull request from a small one is
159
+ * reading a claim we did not make.
160
+ */
161
+ export function blastSignalsFor(pr, hub) {
162
+ const { codeLoc, codeLocIsLowerBound } = codeLocFor(pr);
163
+ // Unknown size or no per-file breakdown → nothing honest to say about reach either.
164
+ if (codeLoc == null)
165
+ return null;
166
+ const files = pr.files ?? [];
167
+ let testFiles = 0;
168
+ let nonCodeFiles = 0;
169
+ let allNew = true;
170
+ const codePaths = [];
171
+ const surfaces = new Set();
172
+ for (const f of files) {
173
+ if (typeof f?.path !== 'string')
174
+ continue;
175
+ // ⚠ SURFACES ARE COLLECTED OVER EVERY FILE, code or not. An OpenAPI document and a Terraform
176
+ // plan are `isNonCodeFile` (structured config, correctly excluded from the LINE count) and
177
+ // are exactly the contracts this feature exists to catch. Restricting the scan to code files
178
+ // would make `openapi` and half of `infra` unreachable.
179
+ for (const s of surfacesForPath(f.path))
180
+ surfaces.add(s);
181
+ if (isNonCodeFile(f.path)) {
182
+ nonCodeFiles += 1;
183
+ continue;
184
+ }
185
+ if (isTestFile(f.path)) {
186
+ testFiles += 1;
187
+ continue;
188
+ }
189
+ codePaths.push(normalize(f.path));
190
+ // A file with no additions and no deletions is a pure rename/mode change; it cannot be the
191
+ // reason a PR is "all new", so it does not vote either way.
192
+ if (f.deletions > 0)
193
+ allNew = false;
194
+ }
195
+ // ⚠ `allNew` is only meaningful when there IS code. An empty code list would otherwise report
196
+ // `true` and claim "nothing existing can break" about a pull request with nothing in it.
197
+ if (codePaths.length === 0)
198
+ allNew = false;
199
+ return {
200
+ codeFiles: codePaths.length,
201
+ testFiles,
202
+ nonCodeFiles,
203
+ dirs: new Set(codePaths.map(dirOf)).size,
204
+ subsystems: new Set(codePaths.map(subsystemOf)).size,
205
+ // Emitted in BLAST_SURFACES order (most-consequential first) rather than insertion order, so
206
+ // the chip's sentence leads with the migration and not with whichever file GitHub listed first.
207
+ surfaces: BLAST_SURFACES.map(([s]) => s).filter((s) => surfaces.has(s)),
208
+ allNew,
209
+ hubDegree: hub?.degree ?? null,
210
+ hubBar: hub?.bar ?? null,
211
+ hubPath: hub?.path ?? null,
212
+ // The same fact `codeLocIsLowerBound` reports, re-stated for the reach vector because it
213
+ // governs a DIFFERENT rule here: over there it forbids asserting "small", here it forbids
214
+ // asserting "low". Both come from the one page-cap test in `codeLocFor`.
215
+ truncated: codeLocIsLowerBound,
216
+ // ⚠ THE STALENESS TEST LIVES HERE, ONCE. A diff read is only true of the commit it read, so a
217
+ // verdict stored against an older head describes code that may no longer be there — and a
218
+ // stale "comments only" would keep a level capped after a force-push added a schema change.
219
+ // Every surface reads the fold, so no surface has to remember this comparison.
220
+ contentKind: contentKindFor(pr),
221
+ };
222
+ }
223
+ /** Re-exported so a caller folding both features reads one import. The page cap is the single
224
+ * fact behind `codeLocIsLowerBound` and `BlastSignals.truncated` alike. */
225
+ export { PR_FILES_PAGE_CAP };
226
+ //# sourceMappingURL=blast-radius.js.map
@@ -0,0 +1,115 @@
1
+ // ── "Is this bot dormant?" — ONE predicate, ONE window, CORE ─────────────────────────────────
2
+ //
3
+ // The Bots → ROI table has always been able to say a reviewer is DORMANT: it is still classified,
4
+ // still (perhaps) paid for, and it did nothing inside the window being read. Two OTHER surfaces —
5
+ // the Bots → Benchmark tab and the Bot Tuning Advisor — used to keep rendering those reviewers, so
6
+ // one screen said "dormant" while the next two placed the same bot against a peer cohort and
7
+ // offered tuning advice for it. This module is the one fact both of those surfaces now hide on.
8
+ //
9
+ // ⚠ THE WINDOW IS FIXED AT 30 DAYS AND MUST STAY FIXED. The three surfaces disagreed about time
10
+ // before this existed: ROI defaults to a trailing 14 days that the USER FLIPS with a chip, the
11
+ // Advisor is pinned to `rolling_30`, and the benchmark fold has no time window at all. Adopting the
12
+ // ROI chip's value would make a workspace's peer placement appear and disappear because somebody
13
+ // flipped a selector on a different tab — a fact about a bot must not be a function of another
14
+ // screen's view state. 30 days is the Advisor's existing pin, so no surface changed its own window
15
+ // to gain this gate.
16
+ //
17
+ // ⚠ CONSEQUENCE, STATED RATHER THAN HIDDEN: a bot with nothing in the last 14 days but something in
18
+ // the last 30 reads "dormant" on the ROI table (relative to the view) and still appears on the
19
+ // Benchmark and the Advisor (it is not dormant as a fact). That is deliberate. The ROI table's
20
+ // window chip and its "widen the window to see them counted" affordance are about the CURRENT VIEW;
21
+ // this predicate is about the BOT.
22
+ //
23
+ // ⚠ THE GRAIN IS THE WORKSPACE, NEVER A REPO. The Advisor cannot narrow by repository at all (the
24
+ // SPA sends only `?workspace=`), and a `?repoIds=`-narrowed benchmark request deliberately builds no
25
+ // rollup — so a repo-grained gate has nowhere to land on two of the three surfaces. The repository
26
+ // set below is therefore the workspace's MEMBERSHIP and never a caller's `repoIds` narrowing, the
27
+ // same rule `workspaceHumanSeatCount` follows for the same reason: the answer must not shrink
28
+ // because a reader filtered a chart.
29
+ //
30
+ // ⚠ THE EVIDENCE IS EXACTLY THE ROI TABLE'S, DELIBERATELY. `getBotAnalytics` calls a reviewer
31
+ // dormant when it has NO window activity, where activity is: a review THREAD it opened, a review
32
+ // COMMENT it left, or a submitted REVIEW (body-only "nothing to flag" passes included — that is a
33
+ // bot doing its job and saying so). Three tables, unioned. The Advisor's own emission gate is
34
+ // narrower (threads-or-ML-labels), so a bot can already be absent from the Advisor while ROI calls
35
+ // it live; widening THIS predicate to match the Advisor instead would hide bots the ROI table shows
36
+ // as working, which is the opposite of what a reader asked for. The union is the safe direction:
37
+ // any observed work at all keeps a reviewer visible.
38
+ //
39
+ // The predicate is CORE because both consumers must not be able to disagree: the Advisor filter
40
+ // lives in `getAdvisorFindings` (`db/queries.ts`), and the benchmark reaches this through the
41
+ // OPTIONAL `ProHostQueries.dormantBotUserIds` seam (apiVersion stays 21 — the `getWorkPlan` /
42
+ // `workspaceHumanSeatCount` precedent; an older host simply hides nothing).
43
+ //
44
+ // ⚠ IT DELIBERATELY IMPORTS NOTHING FROM `queries.ts`. That file imports this one (the Advisor
45
+ // filter lives inside `getAdvisorFindings`), and a cycle between the two would be a hazard nobody
46
+ // wants in the app's largest module. The membership read below is the two lines of
47
+ // `getWorkspaceRepoIds` this needs, minus the render ordering a set does not care about; the
48
+ // candidate ids are the CALLER's, which is also what stops this module owning a second opinion
49
+ // about who is a bot.
50
+ import { and, eq, gte, inArray, ne } from 'drizzle-orm';
51
+ import { db, schema } from './client.js';
52
+ /** The ONE dormancy window, in days. See the header: fixed on purpose, and equal to the Advisor's
53
+ * existing `rolling_30` pin so no surface moved its own window to gain this gate. */
54
+ export const DORMANCY_WINDOW_DAYS = 30;
55
+ const { pullRequests, reviewComments, reviewThreads, reviews, workspaceRepos } = schema;
56
+ /**
57
+ * The automated reviewers that produced NO observable work in this workspace over the fixed
58
+ * dormancy window.
59
+ *
60
+ * `candidateUserIds` IS THE CALLER'S OWN LINEUP and is required: the Advisor passes the automated
61
+ * set it already resolved, the benchmark passes the review vendors it is about. Nothing here
62
+ * re-decides who is a bot — a second classifier is how a benchmark ends up disagreeing with the
63
+ * Timeline about a login. The result is always a SUBSET of the candidates, so a caller can treat it
64
+ * as a plain "hide these" set.
65
+ *
66
+ * ⚠ An empty workspace (no repos) yields NO dormant ids rather than "all of them". Dormancy is a
67
+ * claim about observed silence, and a workspace whose data we hold none of has observed nothing —
68
+ * hiding every bot there would be an absence rendered as a judgement.
69
+ */
70
+ export async function dormantBotUserIds(accountId, workspaceId, candidateUserIds) {
71
+ const candidates = [...new Set(candidateUserIds)];
72
+ if (candidates.length === 0)
73
+ return [];
74
+ // ⚠ MEMBERSHIP, NOT A NARROWING — see the header. Account-scoped on both columns, so a foreign or
75
+ // unknown workspace id yields no repos and therefore no dormant ids (never another tenant's).
76
+ const memberRows = await db
77
+ .select({ repoId: workspaceRepos.repoId })
78
+ .from(workspaceRepos)
79
+ .where(and(eq(workspaceRepos.accountId, accountId), eq(workspaceRepos.workspaceId, workspaceId)))
80
+ .execute();
81
+ const repoIds = memberRows.map((r) => r.repoId);
82
+ if (repoIds.length === 0)
83
+ return [];
84
+ const since = new Date(Date.now() - DORMANCY_WINDOW_DAYS * 86_400_000);
85
+ const repoScope = inArray(pullRequests.repoId, repoIds);
86
+ const [threadRows, commentRows, reviewRows] = await Promise.all([
87
+ db
88
+ .selectDistinct({ userId: reviewThreads.originalCommenterId })
89
+ .from(reviewThreads)
90
+ .innerJoin(pullRequests, eq(pullRequests.id, reviewThreads.prId))
91
+ .where(and(eq(pullRequests.accountId, accountId), inArray(reviewThreads.originalCommenterId, candidates), gte(reviewThreads.createdAt, since), repoScope))
92
+ .execute(),
93
+ db
94
+ .selectDistinct({ userId: reviewComments.authorId })
95
+ .from(reviewComments)
96
+ .innerJoin(pullRequests, eq(pullRequests.id, reviewComments.prId))
97
+ .where(and(eq(pullRequests.accountId, accountId), inArray(reviewComments.authorId, candidates), gte(reviewComments.createdAt, since), repoScope))
98
+ .execute(),
99
+ // A body-only review IS activity — the ROI table counts it for exactly this decision, and a
100
+ // reviewer that reads a PR and says "nothing to flag" is working.
101
+ db
102
+ .selectDistinct({ userId: reviews.authorId })
103
+ .from(reviews)
104
+ .innerJoin(pullRequests, eq(pullRequests.id, reviews.prId))
105
+ .where(and(eq(pullRequests.accountId, accountId), inArray(reviews.authorId, candidates), ne(reviews.state, 'pending'), gte(reviews.submittedAt, since), repoScope))
106
+ .execute(),
107
+ ]);
108
+ const active = new Set();
109
+ for (const rows of [threadRows, commentRows, reviewRows])
110
+ for (const r of rows)
111
+ if (r.userId != null)
112
+ active.add(r.userId);
113
+ return candidates.filter((id) => !active.has(id));
114
+ }
115
+ //# sourceMappingURL=bot-dormancy.js.map