@quickchatai/pi-quickreview 0.0.0-stage → 1.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 (689) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +75 -2
  3. package/assets/agents/pass-abstraction.md +64 -0
  4. package/assets/agents/pass-ask-human.md +134 -0
  5. package/assets/agents/pass-backcompat.md +67 -0
  6. package/assets/agents/pass-baseline.md +61 -0
  7. package/assets/agents/pass-behaviour-delta.md +64 -0
  8. package/assets/agents/pass-bounds.md +36 -0
  9. package/assets/agents/pass-claims.md +31 -0
  10. package/assets/agents/pass-concurrency.md +168 -0
  11. package/assets/agents/pass-consistency.md +57 -0
  12. package/assets/agents/pass-contracts.md +30 -0
  13. package/assets/agents/pass-data-safety.md +54 -0
  14. package/assets/agents/pass-deferred.md +35 -0
  15. package/assets/agents/pass-delivery.md +37 -0
  16. package/assets/agents/pass-duplication.md +60 -0
  17. package/assets/agents/pass-intent.md +63 -0
  18. package/assets/agents/pass-lint-conformance.md +46 -0
  19. package/assets/agents/pass-maintainability.md +67 -0
  20. package/assets/agents/pass-placement.md +68 -0
  21. package/assets/agents/pass-prod-risk.md +24 -0
  22. package/assets/agents/pass-rollout.md +32 -0
  23. package/assets/agents/pass-roundtrip.md +46 -0
  24. package/assets/agents/pass-security.md +31 -0
  25. package/assets/agents/pass-simplicity.md +68 -0
  26. package/assets/agents/pass-statelessness.md +27 -0
  27. package/assets/agents/pass-style.md +37 -0
  28. package/assets/agents/pass-sweep.md +57 -0
  29. package/assets/agents/pass-tests.md +58 -0
  30. package/assets/agents/pass-ui.md +23 -0
  31. package/assets/agents/pass-validate.md +50 -0
  32. package/assets/agents/pass-widened.md +46 -0
  33. package/assets/agents/scan/pass-scan.md +86 -0
  34. package/assets/config.example.toml +1067 -0
  35. package/assets/deploy/reviewer/AGENTS.override.md +12 -0
  36. package/assets/profiles/default/README.md +21 -0
  37. package/assets/profiles/default/addenda/.gitkeep +0 -0
  38. package/assets/profiles/default/profile.toml +17 -0
  39. package/assets/profiles/example-django/README.md +21 -0
  40. package/assets/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  41. package/assets/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  42. package/assets/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  43. package/assets/profiles/example-django/addenda/40-orm-usage.md +19 -0
  44. package/assets/profiles/example-django/addenda/50-declared-shape.md +10 -0
  45. package/assets/profiles/example-django/addenda/60-config-placement.md +14 -0
  46. package/assets/profiles/example-django/heuristics.toml +24 -0
  47. package/assets/profiles/example-django/profile.toml +9 -0
  48. package/assets/profiles/fast/README.md +24 -0
  49. package/assets/profiles/fast/profile.toml +59 -0
  50. package/assets/profiles/minimal/README.md +17 -0
  51. package/assets/profiles/minimal/addenda/.gitkeep +0 -0
  52. package/assets/profiles/minimal/profile.toml +8 -0
  53. package/assets/profiles/snappy/README.md +24 -0
  54. package/assets/profiles/snappy/addenda/10-race-as-question.md +6 -0
  55. package/assets/profiles/snappy/profile.toml +46 -0
  56. package/assets/prompts/companion-day-open.md +46 -0
  57. package/assets/prompts/companion-first-run.md +104 -0
  58. package/assets/prompts/companion.md +821 -0
  59. package/assets/prompts/conversation-system.md +117 -0
  60. package/assets/prompts/eod-sweep.md +120 -0
  61. package/assets/prompts/onboarding.md +74 -0
  62. package/assets/prompts/pass-common.md +52 -0
  63. package/assets/prompts/pass-quality-common.md +22 -0
  64. package/assets/prompts/round-first.md +874 -0
  65. package/assets/prompts/round-follow-up.md +991 -0
  66. package/assets/prompts/round-re-review.md +889 -0
  67. package/assets/prompts/round-recheck.md +196 -0
  68. package/assets/prompts/round-staleness.md +856 -0
  69. package/assets/schemas/review-result.v1.json +391 -0
  70. package/assets/schemas/scan-result.v1.json +421 -0
  71. package/assets/schemas/version.v1.json +57 -0
  72. package/assets/seed-manifest.json +19 -0
  73. package/assets/seeds/agents/pass-baseline.md +61 -0
  74. package/assets/seeds/agents/pass-contracts.md +30 -0
  75. package/assets/seeds/agents/pass-data-safety.md +54 -0
  76. package/assets/seeds/agents/pass-delivery.md +37 -0
  77. package/assets/seeds/agents/pass-prod-risk.md +24 -0
  78. package/assets/seeds/agents/pass-security.md +31 -0
  79. package/assets/seeds/agents/pass-statelessness.md +27 -0
  80. package/assets/seeds/agents/pass-ui.md +23 -0
  81. package/assets/seeds/agents/pass-validate.md +50 -0
  82. package/assets/seeds/prompts/conversation-system.md +117 -0
  83. package/assets/seeds/prompts/eod-sweep.md +120 -0
  84. package/assets/seeds/prompts/pass-common.md +52 -0
  85. package/dist/extensions/bash-guard.d.ts +467 -0
  86. package/dist/extensions/bash-guard.d.ts.map +1 -0
  87. package/dist/extensions/bash-guard.js +3058 -0
  88. package/dist/extensions/bash-guard.js.map +1 -0
  89. package/dist/extensions/finding-clusters.d.ts +33 -0
  90. package/dist/extensions/finding-clusters.d.ts.map +1 -0
  91. package/dist/extensions/finding-clusters.js +218 -0
  92. package/dist/extensions/finding-clusters.js.map +1 -0
  93. package/dist/extensions/ledger-pin.d.ts +76 -0
  94. package/dist/extensions/ledger-pin.d.ts.map +1 -0
  95. package/dist/extensions/ledger-pin.js +331 -0
  96. package/dist/extensions/ledger-pin.js.map +1 -0
  97. package/dist/extensions/lens.d.ts +43 -0
  98. package/dist/extensions/lens.d.ts.map +1 -0
  99. package/dist/extensions/lens.js +150 -0
  100. package/dist/extensions/lens.js.map +1 -0
  101. package/dist/extensions/pi-tool.d.ts +22 -0
  102. package/dist/extensions/pi-tool.d.ts.map +1 -0
  103. package/dist/extensions/pi-tool.js +20 -0
  104. package/dist/extensions/pi-tool.js.map +1 -0
  105. package/dist/extensions/quickreview.d.ts +21 -0
  106. package/dist/extensions/quickreview.d.ts.map +1 -0
  107. package/dist/extensions/quickreview.js +132 -0
  108. package/dist/extensions/quickreview.js.map +1 -0
  109. package/dist/src/anchors.d.ts +54 -0
  110. package/dist/src/anchors.d.ts.map +1 -0
  111. package/dist/src/anchors.js +127 -0
  112. package/dist/src/anchors.js.map +1 -0
  113. package/dist/src/assetroot.d.ts +16 -0
  114. package/dist/src/assetroot.d.ts.map +1 -0
  115. package/dist/src/assetroot.js +77 -0
  116. package/dist/src/assetroot.js.map +1 -0
  117. package/dist/src/authwatch.d.ts +61 -0
  118. package/dist/src/authwatch.d.ts.map +1 -0
  119. package/dist/src/authwatch.js +210 -0
  120. package/dist/src/authwatch.js.map +1 -0
  121. package/dist/src/budget.d.ts +15 -0
  122. package/dist/src/budget.d.ts.map +1 -0
  123. package/dist/src/budget.js +32 -0
  124. package/dist/src/budget.js.map +1 -0
  125. package/dist/src/casebudget.d.ts +3 -0
  126. package/dist/src/casebudget.d.ts.map +1 -0
  127. package/dist/src/casebudget.js +3 -0
  128. package/dist/src/casebudget.js.map +1 -0
  129. package/dist/src/cigate.d.ts +116 -0
  130. package/dist/src/cigate.d.ts.map +1 -0
  131. package/dist/src/cigate.js +176 -0
  132. package/dist/src/cigate.js.map +1 -0
  133. package/dist/src/codespan.d.ts +46 -0
  134. package/dist/src/codespan.d.ts.map +1 -0
  135. package/dist/src/codespan.js +75 -0
  136. package/dist/src/codespan.js.map +1 -0
  137. package/dist/src/contextpack.d.ts +56 -0
  138. package/dist/src/contextpack.d.ts.map +1 -0
  139. package/dist/src/contextpack.js +195 -0
  140. package/dist/src/contextpack.js.map +1 -0
  141. package/dist/src/contract.d.ts +8 -0
  142. package/dist/src/contract.d.ts.map +1 -0
  143. package/dist/src/contract.js +21 -0
  144. package/dist/src/contract.js.map +1 -0
  145. package/dist/src/coverage.d.ts +32 -0
  146. package/dist/src/coverage.d.ts.map +1 -0
  147. package/dist/src/coverage.js +85 -0
  148. package/dist/src/coverage.js.map +1 -0
  149. package/dist/src/datafence.d.ts +19 -0
  150. package/dist/src/datafence.d.ts.map +1 -0
  151. package/dist/src/datafence.js +60 -0
  152. package/dist/src/datafence.js.map +1 -0
  153. package/dist/src/dedup.d.ts +65 -0
  154. package/dist/src/dedup.d.ts.map +1 -0
  155. package/dist/src/dedup.js +459 -0
  156. package/dist/src/dedup.js.map +1 -0
  157. package/dist/src/depsources.d.ts +304 -0
  158. package/dist/src/depsources.d.ts.map +1 -0
  159. package/dist/src/depsources.js +3367 -0
  160. package/dist/src/depsources.js.map +1 -0
  161. package/dist/src/diffinput.d.ts +10 -0
  162. package/dist/src/diffinput.d.ts.map +1 -0
  163. package/dist/src/diffinput.js +172 -0
  164. package/dist/src/diffinput.js.map +1 -0
  165. package/dist/src/doubtwords.d.ts +10 -0
  166. package/dist/src/doubtwords.d.ts.map +1 -0
  167. package/dist/src/doubtwords.js +13 -0
  168. package/dist/src/doubtwords.js.map +1 -0
  169. package/dist/src/ecosystem.d.ts +148 -0
  170. package/dist/src/ecosystem.d.ts.map +1 -0
  171. package/dist/src/ecosystem.js +338 -0
  172. package/dist/src/ecosystem.js.map +1 -0
  173. package/dist/src/engine/agentdir.d.ts +5 -0
  174. package/dist/src/engine/agentdir.d.ts.map +1 -0
  175. package/dist/src/engine/agentdir.js +21 -0
  176. package/dist/src/engine/agentdir.js.map +1 -0
  177. package/dist/src/engine/auth.d.ts +214 -0
  178. package/dist/src/engine/auth.d.ts.map +1 -0
  179. package/dist/src/engine/auth.js +374 -0
  180. package/dist/src/engine/auth.js.map +1 -0
  181. package/dist/src/engine/budget.d.ts +60 -0
  182. package/dist/src/engine/budget.d.ts.map +1 -0
  183. package/dist/src/engine/budget.js +113 -0
  184. package/dist/src/engine/budget.js.map +1 -0
  185. package/dist/src/engine/capfd.d.ts +6 -0
  186. package/dist/src/engine/capfd.d.ts.map +1 -0
  187. package/dist/src/engine/capfd.js +21 -0
  188. package/dist/src/engine/capfd.js.map +1 -0
  189. package/dist/src/engine/caseout.d.ts +66 -0
  190. package/dist/src/engine/caseout.d.ts.map +1 -0
  191. package/dist/src/engine/caseout.js +107 -0
  192. package/dist/src/engine/caseout.js.map +1 -0
  193. package/dist/src/engine/context.d.ts +57 -0
  194. package/dist/src/engine/context.d.ts.map +1 -0
  195. package/dist/src/engine/context.js +212 -0
  196. package/dist/src/engine/context.js.map +1 -0
  197. package/dist/src/engine/envmap.d.ts +13 -0
  198. package/dist/src/engine/envmap.d.ts.map +1 -0
  199. package/dist/src/engine/envmap.js +372 -0
  200. package/dist/src/engine/envmap.js.map +1 -0
  201. package/dist/src/engine/extfiles.d.ts +7 -0
  202. package/dist/src/engine/extfiles.d.ts.map +1 -0
  203. package/dist/src/engine/extfiles.js +23 -0
  204. package/dist/src/engine/extfiles.js.map +1 -0
  205. package/dist/src/engine/keyroute.d.ts +21 -0
  206. package/dist/src/engine/keyroute.d.ts.map +1 -0
  207. package/dist/src/engine/keyroute.js +58 -0
  208. package/dist/src/engine/keyroute.js.map +1 -0
  209. package/dist/src/engine/lens/cache-group.d.ts +20 -0
  210. package/dist/src/engine/lens/cache-group.d.ts.map +1 -0
  211. package/dist/src/engine/lens/cache-group.js +31 -0
  212. package/dist/src/engine/lens/cache-group.js.map +1 -0
  213. package/dist/src/engine/lens/cache-run.d.ts +65 -0
  214. package/dist/src/engine/lens/cache-run.d.ts.map +1 -0
  215. package/dist/src/engine/lens/cache-run.js +159 -0
  216. package/dist/src/engine/lens/cache-run.js.map +1 -0
  217. package/dist/src/engine/lens/deadline.d.ts +54 -0
  218. package/dist/src/engine/lens/deadline.d.ts.map +1 -0
  219. package/dist/src/engine/lens/deadline.js +111 -0
  220. package/dist/src/engine/lens/deadline.js.map +1 -0
  221. package/dist/src/engine/lens/engine-tools.d.ts +13 -0
  222. package/dist/src/engine/lens/engine-tools.d.ts.map +1 -0
  223. package/dist/src/engine/lens/engine-tools.js +37 -0
  224. package/dist/src/engine/lens/engine-tools.js.map +1 -0
  225. package/dist/src/engine/lens/env.d.ts +23 -0
  226. package/dist/src/engine/lens/env.d.ts.map +1 -0
  227. package/dist/src/engine/lens/env.js +42 -0
  228. package/dist/src/engine/lens/env.js.map +1 -0
  229. package/dist/src/engine/lens/launch-plan.d.ts +63 -0
  230. package/dist/src/engine/lens/launch-plan.d.ts.map +1 -0
  231. package/dist/src/engine/lens/launch-plan.js +203 -0
  232. package/dist/src/engine/lens/launch-plan.js.map +1 -0
  233. package/dist/src/engine/lens/leaf-cache.d.ts +22 -0
  234. package/dist/src/engine/lens/leaf-cache.d.ts.map +1 -0
  235. package/dist/src/engine/lens/leaf-cache.js +115 -0
  236. package/dist/src/engine/lens/leaf-cache.js.map +1 -0
  237. package/dist/src/engine/lens/permits.d.ts +14 -0
  238. package/dist/src/engine/lens/permits.d.ts.map +1 -0
  239. package/dist/src/engine/lens/permits.js +18 -0
  240. package/dist/src/engine/lens/permits.js.map +1 -0
  241. package/dist/src/engine/lens/registry.d.ts +55 -0
  242. package/dist/src/engine/lens/registry.d.ts.map +1 -0
  243. package/dist/src/engine/lens/registry.js +140 -0
  244. package/dist/src/engine/lens/registry.js.map +1 -0
  245. package/dist/src/engine/lens/round-lens.d.ts +16 -0
  246. package/dist/src/engine/lens/round-lens.d.ts.map +1 -0
  247. package/dist/src/engine/lens/round-lens.js +46 -0
  248. package/dist/src/engine/lens/round-lens.js.map +1 -0
  249. package/dist/src/engine/lens/runlens.d.ts +103 -0
  250. package/dist/src/engine/lens/runlens.d.ts.map +1 -0
  251. package/dist/src/engine/lens/runlens.js +538 -0
  252. package/dist/src/engine/lens/runlens.js.map +1 -0
  253. package/dist/src/engine/lens/section.d.ts +19 -0
  254. package/dist/src/engine/lens/section.d.ts.map +1 -0
  255. package/dist/src/engine/lens/section.js +40 -0
  256. package/dist/src/engine/lens/section.js.map +1 -0
  257. package/dist/src/engine/lens/semaphore.d.ts +13 -0
  258. package/dist/src/engine/lens/semaphore.d.ts.map +1 -0
  259. package/dist/src/engine/lens/semaphore.js +44 -0
  260. package/dist/src/engine/lens/semaphore.js.map +1 -0
  261. package/dist/src/engine/lens/tool.d.ts +34 -0
  262. package/dist/src/engine/lens/tool.d.ts.map +1 -0
  263. package/dist/src/engine/lens/tool.js +345 -0
  264. package/dist/src/engine/lens/tool.js.map +1 -0
  265. package/dist/src/engine/lens/wave.d.ts +63 -0
  266. package/dist/src/engine/lens/wave.d.ts.map +1 -0
  267. package/dist/src/engine/lens/wave.js +95 -0
  268. package/dist/src/engine/lens/wave.js.map +1 -0
  269. package/dist/src/engine/livesessions.d.ts +21 -0
  270. package/dist/src/engine/livesessions.d.ts.map +1 -0
  271. package/dist/src/engine/livesessions.js +57 -0
  272. package/dist/src/engine/livesessions.js.map +1 -0
  273. package/dist/src/engine/livewire.d.ts +18 -0
  274. package/dist/src/engine/livewire.d.ts.map +1 -0
  275. package/dist/src/engine/livewire.js +31 -0
  276. package/dist/src/engine/livewire.js.map +1 -0
  277. package/dist/src/engine/openable.d.ts +37 -0
  278. package/dist/src/engine/openable.d.ts.map +1 -0
  279. package/dist/src/engine/openable.js +120 -0
  280. package/dist/src/engine/openable.js.map +1 -0
  281. package/dist/src/engine/pisdk.d.ts +226 -0
  282. package/dist/src/engine/pisdk.d.ts.map +1 -0
  283. package/dist/src/engine/pisdk.js +552 -0
  284. package/dist/src/engine/pisdk.js.map +1 -0
  285. package/dist/src/engine/port.d.ts +144 -0
  286. package/dist/src/engine/port.d.ts.map +1 -0
  287. package/dist/src/engine/port.js +16 -0
  288. package/dist/src/engine/port.js.map +1 -0
  289. package/dist/src/engine/prompts.d.ts +159 -0
  290. package/dist/src/engine/prompts.d.ts.map +1 -0
  291. package/dist/src/engine/prompts.js +339 -0
  292. package/dist/src/engine/prompts.js.map +1 -0
  293. package/dist/src/engine/render.d.ts +10 -0
  294. package/dist/src/engine/render.d.ts.map +1 -0
  295. package/dist/src/engine/render.js +23 -0
  296. package/dist/src/engine/render.js.map +1 -0
  297. package/dist/src/engine/reviewruntime.d.ts +13 -0
  298. package/dist/src/engine/reviewruntime.d.ts.map +1 -0
  299. package/dist/src/engine/reviewruntime.js +35 -0
  300. package/dist/src/engine/reviewruntime.js.map +1 -0
  301. package/dist/src/engine/reviewsdk.d.ts +75 -0
  302. package/dist/src/engine/reviewsdk.d.ts.map +1 -0
  303. package/dist/src/engine/reviewsdk.js +180 -0
  304. package/dist/src/engine/reviewsdk.js.map +1 -0
  305. package/dist/src/engine/round.d.ts +166 -0
  306. package/dist/src/engine/round.d.ts.map +1 -0
  307. package/dist/src/engine/round.js +543 -0
  308. package/dist/src/engine/round.js.map +1 -0
  309. package/dist/src/engine/roundagents.d.ts +17 -0
  310. package/dist/src/engine/roundagents.d.ts.map +1 -0
  311. package/dist/src/engine/roundagents.js +49 -0
  312. package/dist/src/engine/roundagents.js.map +1 -0
  313. package/dist/src/engine/roundevents.d.ts +110 -0
  314. package/dist/src/engine/roundevents.d.ts.map +1 -0
  315. package/dist/src/engine/roundevents.js +276 -0
  316. package/dist/src/engine/roundevents.js.map +1 -0
  317. package/dist/src/engine/roundsdk.d.ts +33 -0
  318. package/dist/src/engine/roundsdk.d.ts.map +1 -0
  319. package/dist/src/engine/roundsdk.js +76 -0
  320. package/dist/src/engine/roundsdk.js.map +1 -0
  321. package/dist/src/engine/sdkruntime.d.ts +23 -0
  322. package/dist/src/engine/sdkruntime.d.ts.map +1 -0
  323. package/dist/src/engine/sdkruntime.js +340 -0
  324. package/dist/src/engine/sdkruntime.js.map +1 -0
  325. package/dist/src/engine/select.d.ts +16 -0
  326. package/dist/src/engine/select.d.ts.map +1 -0
  327. package/dist/src/engine/select.js +32 -0
  328. package/dist/src/engine/select.js.map +1 -0
  329. package/dist/src/engine/sessionwrap.d.ts +4 -0
  330. package/dist/src/engine/sessionwrap.d.ts.map +1 -0
  331. package/dist/src/engine/sessionwrap.js +31 -0
  332. package/dist/src/engine/sessionwrap.js.map +1 -0
  333. package/dist/src/engine/toolfence.d.ts +90 -0
  334. package/dist/src/engine/toolfence.d.ts.map +1 -0
  335. package/dist/src/engine/toolfence.js +378 -0
  336. package/dist/src/engine/toolfence.js.map +1 -0
  337. package/dist/src/engine/tooluid.d.ts +128 -0
  338. package/dist/src/engine/tooluid.d.ts.map +1 -0
  339. package/dist/src/engine/tooluid.js +401 -0
  340. package/dist/src/engine/tooluid.js.map +1 -0
  341. package/dist/src/envstrip.d.ts +10 -0
  342. package/dist/src/envstrip.d.ts.map +1 -0
  343. package/dist/src/envstrip.js +15 -0
  344. package/dist/src/envstrip.js.map +1 -0
  345. package/dist/src/escalationprompt.d.ts +22 -0
  346. package/dist/src/escalationprompt.d.ts.map +1 -0
  347. package/dist/src/escalationprompt.js +54 -0
  348. package/dist/src/escalationprompt.js.map +1 -0
  349. package/dist/src/execfn.d.ts +29 -0
  350. package/dist/src/execfn.d.ts.map +1 -0
  351. package/dist/src/execfn.js +55 -0
  352. package/dist/src/execfn.js.map +1 -0
  353. package/dist/src/finding-chain.d.ts +40 -0
  354. package/dist/src/finding-chain.d.ts.map +1 -0
  355. package/dist/src/finding-chain.js +76 -0
  356. package/dist/src/finding-chain.js.map +1 -0
  357. package/dist/src/finding-row.d.ts +24 -0
  358. package/dist/src/finding-row.d.ts.map +1 -0
  359. package/dist/src/finding-row.js +26 -0
  360. package/dist/src/finding-row.js.map +1 -0
  361. package/dist/src/findingkey.d.ts +99 -0
  362. package/dist/src/findingkey.d.ts.map +1 -0
  363. package/dist/src/findingkey.js +210 -0
  364. package/dist/src/findingkey.js.map +1 -0
  365. package/dist/src/findingsrecord.d.ts +175 -0
  366. package/dist/src/findingsrecord.d.ts.map +1 -0
  367. package/dist/src/findingsrecord.js +410 -0
  368. package/dist/src/findingsrecord.js.map +1 -0
  369. package/dist/src/followup-mode.d.ts +4 -0
  370. package/dist/src/followup-mode.d.ts.map +1 -0
  371. package/dist/src/followup-mode.js +6 -0
  372. package/dist/src/followup-mode.js.map +1 -0
  373. package/dist/src/gate.d.ts +199 -0
  374. package/dist/src/gate.d.ts.map +1 -0
  375. package/dist/src/gate.js +315 -0
  376. package/dist/src/gate.js.map +1 -0
  377. package/dist/src/ghcred.d.ts +58 -0
  378. package/dist/src/ghcred.d.ts.map +1 -0
  379. package/dist/src/ghcred.js +139 -0
  380. package/dist/src/ghcred.js.map +1 -0
  381. package/dist/src/githubAuth.d.ts +32 -0
  382. package/dist/src/githubAuth.d.ts.map +1 -0
  383. package/dist/src/githubAuth.js +116 -0
  384. package/dist/src/githubAuth.js.map +1 -0
  385. package/dist/src/githubcore.d.ts +20 -0
  386. package/dist/src/githubcore.d.ts.map +1 -0
  387. package/dist/src/githubcore.js +20 -0
  388. package/dist/src/githubcore.js.map +1 -0
  389. package/dist/src/glob.d.ts +11 -0
  390. package/dist/src/glob.d.ts.map +1 -0
  391. package/dist/src/glob.js +69 -0
  392. package/dist/src/glob.js.map +1 -0
  393. package/dist/src/guards.d.ts +93 -0
  394. package/dist/src/guards.d.ts.map +1 -0
  395. package/dist/src/guards.js +306 -0
  396. package/dist/src/guards.js.map +1 -0
  397. package/dist/src/heartbeat.d.ts +29 -0
  398. package/dist/src/heartbeat.d.ts.map +1 -0
  399. package/dist/src/heartbeat.js +87 -0
  400. package/dist/src/heartbeat.js.map +1 -0
  401. package/dist/src/heuristics.d.ts +247 -0
  402. package/dist/src/heuristics.d.ts.map +1 -0
  403. package/dist/src/heuristics.js +927 -0
  404. package/dist/src/heuristics.js.map +1 -0
  405. package/dist/src/homes.d.ts +181 -0
  406. package/dist/src/homes.d.ts.map +1 -0
  407. package/dist/src/homes.js +242 -0
  408. package/dist/src/homes.js.map +1 -0
  409. package/dist/src/hunkheader.d.ts +11 -0
  410. package/dist/src/hunkheader.d.ts.map +1 -0
  411. package/dist/src/hunkheader.js +18 -0
  412. package/dist/src/hunkheader.js.map +1 -0
  413. package/dist/src/include.d.ts +435 -0
  414. package/dist/src/include.d.ts.map +1 -0
  415. package/dist/src/include.js +1692 -0
  416. package/dist/src/include.js.map +1 -0
  417. package/dist/src/launchvocab.d.ts +36 -0
  418. package/dist/src/launchvocab.d.ts.map +1 -0
  419. package/dist/src/launchvocab.js +59 -0
  420. package/dist/src/launchvocab.js.map +1 -0
  421. package/dist/src/ledgercarry.d.ts +24 -0
  422. package/dist/src/ledgercarry.d.ts.map +1 -0
  423. package/dist/src/ledgercarry.js +109 -0
  424. package/dist/src/ledgercarry.js.map +1 -0
  425. package/dist/src/lensids.d.ts +7 -0
  426. package/dist/src/lensids.d.ts.map +1 -0
  427. package/dist/src/lensids.js +53 -0
  428. package/dist/src/lensids.js.map +1 -0
  429. package/dist/src/lensplanning.d.ts +8 -0
  430. package/dist/src/lensplanning.d.ts.map +1 -0
  431. package/dist/src/lensplanning.js +126 -0
  432. package/dist/src/lensplanning.js.map +1 -0
  433. package/dist/src/lensresult.d.ts +94 -0
  434. package/dist/src/lensresult.d.ts.map +1 -0
  435. package/dist/src/lensresult.js +162 -0
  436. package/dist/src/lensresult.js.map +1 -0
  437. package/dist/src/lenstuning.d.ts +97 -0
  438. package/dist/src/lenstuning.d.ts.map +1 -0
  439. package/dist/src/lenstuning.js +326 -0
  440. package/dist/src/lenstuning.js.map +1 -0
  441. package/dist/src/lintconfig.d.ts +31 -0
  442. package/dist/src/lintconfig.d.ts.map +1 -0
  443. package/dist/src/lintconfig.js +543 -0
  444. package/dist/src/lintconfig.js.map +1 -0
  445. package/dist/src/live/events.d.ts +188 -0
  446. package/dist/src/live/events.d.ts.map +1 -0
  447. package/dist/src/live/events.js +136 -0
  448. package/dist/src/live/events.js.map +1 -0
  449. package/dist/src/live/writer.d.ts +54 -0
  450. package/dist/src/live/writer.d.ts.map +1 -0
  451. package/dist/src/live/writer.js +516 -0
  452. package/dist/src/live/writer.js.map +1 -0
  453. package/dist/src/localreview.d.ts +128 -0
  454. package/dist/src/localreview.d.ts.map +1 -0
  455. package/dist/src/localreview.js +426 -0
  456. package/dist/src/localreview.js.map +1 -0
  457. package/dist/src/loosenedtests.d.ts +13 -0
  458. package/dist/src/loosenedtests.d.ts.map +1 -0
  459. package/dist/src/loosenedtests.js +188 -0
  460. package/dist/src/loosenedtests.js.map +1 -0
  461. package/dist/src/memoryport.d.ts +264 -0
  462. package/dist/src/memoryport.d.ts.map +1 -0
  463. package/dist/src/memoryport.js +445 -0
  464. package/dist/src/memoryport.js.map +1 -0
  465. package/dist/src/migrations.d.ts +106 -0
  466. package/dist/src/migrations.d.ts.map +1 -0
  467. package/dist/src/migrations.js +373 -0
  468. package/dist/src/migrations.js.map +1 -0
  469. package/dist/src/oneshot.d.ts +12 -0
  470. package/dist/src/oneshot.d.ts.map +1 -0
  471. package/dist/src/oneshot.js +8 -0
  472. package/dist/src/oneshot.js.map +1 -0
  473. package/dist/src/packing.d.ts +11 -0
  474. package/dist/src/packing.d.ts.map +1 -0
  475. package/dist/src/packing.js +31 -0
  476. package/dist/src/packing.js.map +1 -0
  477. package/dist/src/passes.d.ts +16 -0
  478. package/dist/src/passes.d.ts.map +1 -0
  479. package/dist/src/passes.js +104 -0
  480. package/dist/src/passes.js.map +1 -0
  481. package/dist/src/passtasks.d.ts +68 -0
  482. package/dist/src/passtasks.d.ts.map +1 -0
  483. package/dist/src/passtasks.js +360 -0
  484. package/dist/src/passtasks.js.map +1 -0
  485. package/dist/src/pathinside.d.ts +5 -0
  486. package/dist/src/pathinside.d.ts.map +1 -0
  487. package/dist/src/pathinside.js +9 -0
  488. package/dist/src/pathinside.js.map +1 -0
  489. package/dist/src/pibuiltins.d.ts +36 -0
  490. package/dist/src/pibuiltins.d.ts.map +1 -0
  491. package/dist/src/pibuiltins.js +83 -0
  492. package/dist/src/pibuiltins.js.map +1 -0
  493. package/dist/src/plankey.d.ts +4 -0
  494. package/dist/src/plankey.d.ts.map +1 -0
  495. package/dist/src/plankey.js +10 -0
  496. package/dist/src/plankey.js.map +1 -0
  497. package/dist/src/planner.d.ts +375 -0
  498. package/dist/src/planner.d.ts.map +1 -0
  499. package/dist/src/planner.js +722 -0
  500. package/dist/src/planner.js.map +1 -0
  501. package/dist/src/plugin/args.d.ts +19 -0
  502. package/dist/src/plugin/args.d.ts.map +1 -0
  503. package/dist/src/plugin/args.js +107 -0
  504. package/dist/src/plugin/args.js.map +1 -0
  505. package/dist/src/plugin/index.d.ts +3 -0
  506. package/dist/src/plugin/index.d.ts.map +1 -0
  507. package/dist/src/plugin/index.js +14 -0
  508. package/dist/src/plugin/index.js.map +1 -0
  509. package/dist/src/plugin/login.d.ts +39 -0
  510. package/dist/src/plugin/login.d.ts.map +1 -0
  511. package/dist/src/plugin/login.js +73 -0
  512. package/dist/src/plugin/login.js.map +1 -0
  513. package/dist/src/plugin/review.d.ts +27 -0
  514. package/dist/src/plugin/review.d.ts.map +1 -0
  515. package/dist/src/plugin/review.js +298 -0
  516. package/dist/src/plugin/review.js.map +1 -0
  517. package/dist/src/plugin/runner.d.ts +6 -0
  518. package/dist/src/plugin/runner.d.ts.map +1 -0
  519. package/dist/src/plugin/runner.js +65 -0
  520. package/dist/src/plugin/runner.js.map +1 -0
  521. package/dist/src/plugin/scan.d.ts +79 -0
  522. package/dist/src/plugin/scan.d.ts.map +1 -0
  523. package/dist/src/plugin/scan.js +348 -0
  524. package/dist/src/plugin/scan.js.map +1 -0
  525. package/dist/src/plugin/session.d.ts +56 -0
  526. package/dist/src/plugin/session.d.ts.map +1 -0
  527. package/dist/src/plugin/session.js +2 -0
  528. package/dist/src/plugin/session.js.map +1 -0
  529. package/dist/src/plugin/setup.d.ts +19 -0
  530. package/dist/src/plugin/setup.d.ts.map +1 -0
  531. package/dist/src/plugin/setup.js +142 -0
  532. package/dist/src/plugin/setup.js.map +1 -0
  533. package/dist/src/plugin/versiongate.d.ts +26 -0
  534. package/dist/src/plugin/versiongate.d.ts.map +1 -0
  535. package/dist/src/plugin/versiongate.js +83 -0
  536. package/dist/src/plugin/versiongate.js.map +1 -0
  537. package/dist/src/postingkey.d.ts +8 -0
  538. package/dist/src/postingkey.d.ts.map +1 -0
  539. package/dist/src/postingkey.js +25 -0
  540. package/dist/src/postingkey.js.map +1 -0
  541. package/dist/src/postingpolicy.d.ts +108 -0
  542. package/dist/src/postingpolicy.d.ts.map +1 -0
  543. package/dist/src/postingpolicy.js +341 -0
  544. package/dist/src/postingpolicy.js.map +1 -0
  545. package/dist/src/pr-identity.d.ts +3 -0
  546. package/dist/src/pr-identity.d.ts.map +1 -0
  547. package/dist/src/pr-identity.js +9 -0
  548. package/dist/src/pr-identity.js.map +1 -0
  549. package/dist/src/prdelta.d.ts +181 -0
  550. package/dist/src/prdelta.d.ts.map +1 -0
  551. package/dist/src/prdelta.js +501 -0
  552. package/dist/src/prdelta.js.map +1 -0
  553. package/dist/src/pricing.d.ts +94 -0
  554. package/dist/src/pricing.d.ts.map +1 -0
  555. package/dist/src/pricing.js +209 -0
  556. package/dist/src/pricing.js.map +1 -0
  557. package/dist/src/priorfindings.d.ts +167 -0
  558. package/dist/src/priorfindings.d.ts.map +1 -0
  559. package/dist/src/priorfindings.js +445 -0
  560. package/dist/src/priorfindings.js.map +1 -0
  561. package/dist/src/priortasks.d.ts +31 -0
  562. package/dist/src/priortasks.d.ts.map +1 -0
  563. package/dist/src/priortasks.js +119 -0
  564. package/dist/src/priortasks.js.map +1 -0
  565. package/dist/src/profilelookup.d.ts +59 -0
  566. package/dist/src/profilelookup.d.ts.map +1 -0
  567. package/dist/src/profilelookup.js +98 -0
  568. package/dist/src/profilelookup.js.map +1 -0
  569. package/dist/src/profileoverrides.d.ts +91 -0
  570. package/dist/src/profileoverrides.d.ts.map +1 -0
  571. package/dist/src/profileoverrides.js +313 -0
  572. package/dist/src/profileoverrides.js.map +1 -0
  573. package/dist/src/profiles.d.ts +144 -0
  574. package/dist/src/profiles.d.ts.map +1 -0
  575. package/dist/src/profiles.js +604 -0
  576. package/dist/src/profiles.js.map +1 -0
  577. package/dist/src/promptdoc.d.ts +3 -0
  578. package/dist/src/promptdoc.d.ts.map +1 -0
  579. package/dist/src/promptdoc.js +18 -0
  580. package/dist/src/promptdoc.js.map +1 -0
  581. package/dist/src/prtarget.d.ts +70 -0
  582. package/dist/src/prtarget.d.ts.map +1 -0
  583. package/dist/src/prtarget.js +181 -0
  584. package/dist/src/prtarget.js.map +1 -0
  585. package/dist/src/recheckorder.d.ts +32 -0
  586. package/dist/src/recheckorder.d.ts.map +1 -0
  587. package/dist/src/recheckorder.js +5 -0
  588. package/dist/src/recheckorder.js.map +1 -0
  589. package/dist/src/reviewapi.d.ts +207 -0
  590. package/dist/src/reviewapi.d.ts.map +1 -0
  591. package/dist/src/reviewapi.js +939 -0
  592. package/dist/src/reviewapi.js.map +1 -0
  593. package/dist/src/reviewbotfindings.d.ts +33 -0
  594. package/dist/src/reviewbotfindings.d.ts.map +1 -0
  595. package/dist/src/reviewbotfindings.js +80 -0
  596. package/dist/src/reviewbotfindings.js.map +1 -0
  597. package/dist/src/reviewconfig.d.ts +741 -0
  598. package/dist/src/reviewconfig.d.ts.map +1 -0
  599. package/dist/src/reviewconfig.js +797 -0
  600. package/dist/src/reviewconfig.js.map +1 -0
  601. package/dist/src/reviewdelta.d.ts +51 -0
  602. package/dist/src/reviewdelta.d.ts.map +1 -0
  603. package/dist/src/reviewdelta.js +119 -0
  604. package/dist/src/reviewdelta.js.map +1 -0
  605. package/dist/src/reviewdest.d.ts +86 -0
  606. package/dist/src/reviewdest.d.ts.map +1 -0
  607. package/dist/src/reviewdest.js +142 -0
  608. package/dist/src/reviewdest.js.map +1 -0
  609. package/dist/src/reviewerstub.d.ts +12 -0
  610. package/dist/src/reviewerstub.d.ts.map +1 -0
  611. package/dist/src/reviewerstub.js +28 -0
  612. package/dist/src/reviewerstub.js.map +1 -0
  613. package/dist/src/reviewhost.d.ts +58 -0
  614. package/dist/src/reviewhost.d.ts.map +1 -0
  615. package/dist/src/reviewhost.js +135 -0
  616. package/dist/src/reviewhost.js.map +1 -0
  617. package/dist/src/reviewpost.d.ts +79 -0
  618. package/dist/src/reviewpost.d.ts.map +1 -0
  619. package/dist/src/reviewpost.js +233 -0
  620. package/dist/src/reviewpost.js.map +1 -0
  621. package/dist/src/reviewrender.d.ts +154 -0
  622. package/dist/src/reviewrender.d.ts.map +1 -0
  623. package/dist/src/reviewrender.js +480 -0
  624. package/dist/src/reviewrender.js.map +1 -0
  625. package/dist/src/reviewstate.d.ts +62 -0
  626. package/dist/src/reviewstate.d.ts.map +1 -0
  627. package/dist/src/reviewstate.js +167 -0
  628. package/dist/src/reviewstate.js.map +1 -0
  629. package/dist/src/reviewstore.d.ts +17 -0
  630. package/dist/src/reviewstore.d.ts.map +1 -0
  631. package/dist/src/reviewstore.js +105 -0
  632. package/dist/src/reviewstore.js.map +1 -0
  633. package/dist/src/round-progress.d.ts +120 -0
  634. package/dist/src/round-progress.d.ts.map +1 -0
  635. package/dist/src/round-progress.js +172 -0
  636. package/dist/src/round-progress.js.map +1 -0
  637. package/dist/src/round-stage-deadlines.d.ts +12 -0
  638. package/dist/src/round-stage-deadlines.d.ts.map +1 -0
  639. package/dist/src/round-stage-deadlines.js +10 -0
  640. package/dist/src/round-stage-deadlines.js.map +1 -0
  641. package/dist/src/session-command.d.ts +19 -0
  642. package/dist/src/session-command.d.ts.map +1 -0
  643. package/dist/src/session-command.js +20 -0
  644. package/dist/src/session-command.js.map +1 -0
  645. package/dist/src/settledread.d.ts +61 -0
  646. package/dist/src/settledread.d.ts.map +1 -0
  647. package/dist/src/settledread.js +135 -0
  648. package/dist/src/settledread.js.map +1 -0
  649. package/dist/src/severity.d.ts +167 -0
  650. package/dist/src/severity.d.ts.map +1 -0
  651. package/dist/src/severity.js +364 -0
  652. package/dist/src/severity.js.map +1 -0
  653. package/dist/src/severityrank.d.ts +8 -0
  654. package/dist/src/severityrank.d.ts.map +1 -0
  655. package/dist/src/severityrank.js +16 -0
  656. package/dist/src/severityrank.js.map +1 -0
  657. package/dist/src/standing-review.d.ts +45 -0
  658. package/dist/src/standing-review.d.ts.map +1 -0
  659. package/dist/src/standing-review.js +43 -0
  660. package/dist/src/standing-review.js.map +1 -0
  661. package/dist/src/storerows.d.ts +225 -0
  662. package/dist/src/storerows.d.ts.map +1 -0
  663. package/dist/src/storerows.js +7 -0
  664. package/dist/src/storerows.js.map +1 -0
  665. package/dist/src/styleguide.d.ts +25 -0
  666. package/dist/src/styleguide.d.ts.map +1 -0
  667. package/dist/src/styleguide.js +272 -0
  668. package/dist/src/styleguide.js.map +1 -0
  669. package/dist/src/sweepstatus.d.ts +42 -0
  670. package/dist/src/sweepstatus.d.ts.map +1 -0
  671. package/dist/src/sweepstatus.js +97 -0
  672. package/dist/src/sweepstatus.js.map +1 -0
  673. package/dist/src/templates.d.ts +243 -0
  674. package/dist/src/templates.d.ts.map +1 -0
  675. package/dist/src/templates.js +847 -0
  676. package/dist/src/templates.js.map +1 -0
  677. package/dist/src/types.d.ts +639 -0
  678. package/dist/src/types.d.ts.map +1 -0
  679. package/dist/src/types.js +90 -0
  680. package/dist/src/types.js.map +1 -0
  681. package/dist/src/unifieddiff.d.ts +22 -0
  682. package/dist/src/unifieddiff.d.ts.map +1 -0
  683. package/dist/src/unifieddiff.js +176 -0
  684. package/dist/src/unifieddiff.js.map +1 -0
  685. package/dist/src/yieldloop.d.ts +2 -0
  686. package/dist/src/yieldloop.d.ts.map +1 -0
  687. package/dist/src/yieldloop.js +14 -0
  688. package/dist/src/yieldloop.js.map +1 -0
  689. package/package.json +60 -4
@@ -0,0 +1,306 @@
1
+ // Posting-time guards, all applied at the one moment that matters:
2
+ // immediately before a review reaches GitHub.
3
+ //
4
+ // 1. Never re-block a head a human already approved. A non-author human
5
+ // approval at the head under review downgrades a REQUEST_CHANGES to a
6
+ // COMMENT: the bot still reports everything it found, it just stops
7
+ // holding the gate against a person who has already looked.
8
+ // 2. No-op when there is no new evidence. The mark lives in
9
+ // `reviewed_heads` (state.ts): same head AND no author reply since ⇒ post
10
+ // nothing. The same mark is what the post-approval delta recheck reads.
11
+ // 3. Re-check merged/closed state immediately before posting, so no review
12
+ // lands at or after a merge.
13
+ // 5. The live head must be the head the round actually reviewed, so a push
14
+ // landing between a round's validation and its posting step never gets
15
+ // the stale review posted against it. On the gate route the intent's
16
+ // commit_id is the reviewed head, so a mismatch with the guard-3 live
17
+ // read refuses; a session's post carries QCR_EXPECTED_HEAD
18
+ // (cited by its round prompt) as the intent's commit_id, and a post that
19
+ // names none carries no expectation, so a session whose prompt does not
20
+ // set it is never wedged.
21
+ // 4. No green CI at this head ⇒ APPROVE is unavailable. The review still
22
+ // posts — as a COMMENT with the approval withheld and a plain note saying
23
+ // so — and the PR is re-queued when CI goes green. The classifier lives
24
+ // in cigate.ts.
25
+ //
26
+ // All of them are installed on the one MutationGate (gate.ts `MutationGuard`),
27
+ // not on a call site, so no posting route in the daemon can reach GitHub
28
+ // around them. The sessions post through scripts/post-review.sh,
29
+ // approve-pr.sh and reply-thread.sh, Node-only clients of the daemon's post
30
+ // endpoint (src/ipc/postshim.ts); the daemon builds the intent and hands it to
31
+ // the same gate, so these guards bind them too. The gate's after-step makes
32
+ // the reviewed-head stamp. tests/guards.test.ts drives the real gate over the
33
+ // cases.
34
+ // Guard 2 never gates an APPROVE. Guard 4 softens rather than refuses: an
35
+ // approval over red CI posts as a COMMENT with the approval withheld.
36
+ //
37
+ // Nothing here dismisses, retracts or edits an existing review. A guard's only
38
+ // powers are to refuse a mutation and to soften its event to COMMENT — a
39
+ // REQUEST_CHANGES a human's approval outranks (guard 1), an APPROVE red CI
40
+ // withholds (guard 4).
41
+ import { ciBlocksApproval, classifyCi, withholdApproval, } from "./cigate.js";
42
+ import { standingReview } from "./standing-review.js";
43
+ /** A bot's login: the App's own, or any `[bot]` account. Bot-authored PRs
44
+ * never enter the state machine (spec §3, src/statemachine.ts re-exports
45
+ * this), and a bot's approval is no human's. */
46
+ export function isBotAuthor(author, botLogin) {
47
+ return author === botLogin || author.endsWith("[bot]");
48
+ }
49
+ /** Mutation kinds that put words on a PR — the ones the guards inspect. */
50
+ export const POSTING_KINDS = new Set([
51
+ "create_review",
52
+ "create_review_comment_reply",
53
+ "create_issue_comment",
54
+ ]);
55
+ // ---- guard 1: a human already approved this head --------------------------
56
+ /**
57
+ * The login of a non-author human whose APPROVED review stands at `head_sha`,
58
+ * or null. "Stands" means it is that person's LATEST verdict-bearing review:
59
+ * a later CHANGES_REQUESTED or DISMISSED by the same person retires it.
60
+ * COMMENTED/PENDING reviews carry no verdict and are ignored.
61
+ *
62
+ * Reviews are read in submission order, which is the order the REST endpoint
63
+ * returns them in.
64
+ */
65
+ export function humanApprovalAtHead(reviews, opts) {
66
+ if (!opts.head_sha)
67
+ return null;
68
+ const latest = new Map();
69
+ for (const r of reviews) {
70
+ const state = (r.state ?? "").toUpperCase();
71
+ if (state !== "APPROVED" && state !== "CHANGES_REQUESTED" && state !== "DISMISSED")
72
+ continue;
73
+ if (!r.user)
74
+ continue;
75
+ latest.set(r.user, { state, commit_id: r.commit_id });
76
+ }
77
+ for (const [user, r] of latest) {
78
+ if (r.state !== "APPROVED")
79
+ continue;
80
+ if (r.commit_id !== opts.head_sha)
81
+ continue;
82
+ if (user === opts.author)
83
+ continue; // GitHub forbids it, but never trust that
84
+ if (isBotAuthor(user, opts.botLogin))
85
+ continue;
86
+ return user;
87
+ }
88
+ return null;
89
+ }
90
+ /** The note the downgraded review carries, so the softer event is never a
91
+ * silent change of mind. */
92
+ export function downgradeNote(approver, headSha) {
93
+ return (`_Not blocking: **@${approver}** has already approved this head (\`${headSha.slice(0, 7)}\`). ` +
94
+ "The findings above are reported for information only — this review does not request changes._");
95
+ }
96
+ /** REQUEST_CHANGES → COMMENT, findings untouched, with the note appended. */
97
+ export function downgradeForHumanApproval(body, approver, headSha) {
98
+ return {
99
+ ...body,
100
+ event: "COMMENT",
101
+ body: `${body.body ?? ""}\n\n${downgradeNote(approver, headSha)}`,
102
+ };
103
+ }
104
+ // ---- guard 2: no new evidence ---------------------------------------------
105
+ /**
106
+ * Is there anything new to review at `head_sha`? True unless the mark sits on
107
+ * exactly this head and no author reply has landed since it was stamped.
108
+ * An unknown head (empty string) is always "new": guessing would suppress a
109
+ * real review.
110
+ */
111
+ export function hasNewEvidence(mark, headSha) {
112
+ if (!mark || !headSha)
113
+ return true;
114
+ // A NEEDS_HUMAN/ESCALATE mark (legacy vocabulary) is an undecided review:
115
+ // it must never suppress a re-run or a posting — a re-triggered PR with only
116
+ // an undecided mark deserves a full round. Read through the one
117
+ // standing-review interpretation, so this guard and dispatch's skip can
118
+ // never disagree about what the mark says.
119
+ if (standingReview(mark).kind === "undecided")
120
+ return true;
121
+ if (mark.head_sha !== headSha)
122
+ return true;
123
+ // `>=`, not `>`: a reply stamped in the same millisecond as the review is
124
+ // ambiguous, and the safe reading of an ambiguous reply is "new evidence".
125
+ // Re-stamping the mark clears the reply, so this cannot loop.
126
+ if (mark.last_author_reply_at && mark.last_author_reply_at >= mark.reviewed_at)
127
+ return true;
128
+ return false;
129
+ }
130
+ /**
131
+ * Read the checks at a head for guard 4. Null means "cannot see CI" — no
132
+ * adapter method, no head, or a failed read — and a guard never blocks on what
133
+ * it cannot see: failing closed here would withhold every approval during a
134
+ * GitHub outage, turning an outage into a pile of parked PRs. What it CAN see
135
+ * it enforces without exception, infra failures included.
136
+ */
137
+ async function readCi(deps, pr, head, log) {
138
+ if (!deps.adapter.listChecks || !head)
139
+ return null;
140
+ try {
141
+ return classifyCi(await deps.adapter.listChecks(head));
142
+ }
143
+ catch (err) {
144
+ log(`guard: checks read failed on #${pr}`, err);
145
+ deps.store.logEvent("approval_ci_unreadable", pr, { head_sha: head, error: String(err) });
146
+ return null;
147
+ }
148
+ }
149
+ export function makePostingGuard(deps) {
150
+ const log = deps.log ?? (() => { });
151
+ const refuse = (intent, pr, reason, extra = {}) => {
152
+ log(`guard: refused ${intent.kind} on #${pr} — ${reason}`);
153
+ deps.store.logEvent("posting_guard_blocked", pr, { kind: intent.kind, reason, ...extra });
154
+ return { block: reason };
155
+ };
156
+ return {
157
+ async before(intent) {
158
+ if (!POSTING_KINDS.has(intent.kind))
159
+ return {};
160
+ const pr = intent.pr;
161
+ if (!pr)
162
+ return {}; // no PR to check against — nothing this guard can say
163
+ const body = (intent.body ?? {});
164
+ // --- guard 3: is the PR still open, right now? ---
165
+ let ref;
166
+ if (deps.guards.recheck_pr_state_before_post) {
167
+ try {
168
+ ref = await deps.adapter.getPr(pr);
169
+ }
170
+ catch (err) {
171
+ // Fail closed: an unverifiable PR state is exactly when a post-merge
172
+ // review could land. The session retries; reconcile re-derives the
173
+ // truth.
174
+ return refuse(intent, pr, "pr-unreadable", { error: String(err) });
175
+ }
176
+ if (ref.merged)
177
+ return refuse(intent, pr, "pr-merged");
178
+ if (ref.state === "closed")
179
+ return refuse(intent, pr, "pr-closed");
180
+ }
181
+ if (intent.kind !== "create_review")
182
+ return {};
183
+ // The head the review is ABOUT: the payload's commit_id when it has one,
184
+ // otherwise whatever the PR points at now.
185
+ const head = body.commit_id || ref?.head_sha || "";
186
+ // --- guard 5: the head the review is about must still BE the head ---
187
+ // A push landing between a round's validation and its posting step
188
+ // would otherwise land the stale review against the new head. Fires
189
+ // only where the data exists —
190
+ // an intent without a commit_id carries no expectation, and without the
191
+ // guard-3 read there is no live head to compare against. Shas compare
192
+ // case-insensitively: git shas are hex, and an LLM-written
193
+ // commit_id with drifted case must never fake a "moved" head.
194
+ // A deferred session review the daemon delivers
195
+ // (`intent.postingRetry`) posts at its ORIGINAL commit_id even when the
196
+ // PR has moved on; admission pins the key to
197
+ // that commit_id and marker.
198
+ if (deps.guards.refuse_on_head_drift &&
199
+ intent.postingRetry === undefined &&
200
+ body.commit_id &&
201
+ ref &&
202
+ ref.head_sha.toLowerCase() !== body.commit_id.toLowerCase()) {
203
+ return refuse(intent, pr, "head-drift", {
204
+ expected_head: body.commit_id,
205
+ live_head: ref.head_sha,
206
+ });
207
+ }
208
+ // --- guard 2: has anything happened since the last review? ---
209
+ //
210
+ // The escalation note is exempt, and only it. Guard 2 exists to stop the
211
+ // bot re-reviewing a head it already reviewed. An escalation note is not
212
+ // a review of the head: it carries no verdict, claims no head (see
213
+ // `after` below), and by design lands right after the round's own review
214
+ // — i.e. always at a head the mark has just been stamped on, so guard 2
215
+ // would suppress every one of them. Its duplicate-suppression is strictly
216
+ // narrower than guard 2's: the `escalations` ledger refuses the same
217
+ // person + the same thing on the same PR forever, and `[escalation]
218
+ // max_per_pr` caps the rest. Every other guard still binds it —
219
+ // merged/closed (3) and head drift (5) both ran above, unconditionally.
220
+ //
221
+ // An APPROVE is exempt too, as a matter of design. Every approval reaching the gate is a
222
+ // session's approve-pr.sh POST the daemon delivers, and the one that
223
+ // meets an already-stamped head is the CI-green retry of a withheld
224
+ // approval: its COMMENT stamped the head, and refusing the APPROVE here
225
+ // would strand the approval that withholding promised.
226
+ const mechanical = intent.reviewPurpose === "escalation";
227
+ const approval = (body.event ?? "").toUpperCase() === "APPROVE";
228
+ if (deps.guards.skip_unchanged_head &&
229
+ !mechanical &&
230
+ !approval &&
231
+ !hasNewEvidence(deps.store.reviewedHead(pr), head)) {
232
+ return refuse(intent, pr, "no-new-evidence", { head_sha: head });
233
+ }
234
+ // --- guard 1: a human approved this head; do not hold the gate ---
235
+ if (deps.guards.downgrade_on_human_approval &&
236
+ (body.event ?? "").toUpperCase() === "REQUEST_CHANGES") {
237
+ let approver = null;
238
+ try {
239
+ approver = humanApprovalAtHead(await deps.adapter.listReviews(pr), {
240
+ head_sha: head,
241
+ author: ref?.author ?? "",
242
+ botLogin: deps.botLogin,
243
+ });
244
+ }
245
+ catch (err) {
246
+ // A failed read must not block a review — the worst case here is an
247
+ // unsoftened review, not a post-merge or duplicate one.
248
+ log(`guard: review listing failed on #${pr}`, err);
249
+ }
250
+ if (approver) {
251
+ log(`guard: #${pr} REQUEST_CHANGES → COMMENT (@${approver} approved ${head.slice(0, 7)})`);
252
+ deps.store.logEvent("posting_guard_downgraded", pr, { approver, head_sha: head });
253
+ return {
254
+ intent: {
255
+ ...intent,
256
+ body: downgradeForHumanApproval(body, approver, head),
257
+ note: `post COMMENT review on #${pr} (downgraded: @${approver} approved this head)`,
258
+ },
259
+ };
260
+ }
261
+ }
262
+ // --- guard 4: no green CI at this head ⇒ no approval ---
263
+ if (deps.guards.block_approve_on_red_ci && (body.event ?? "").toUpperCase() === "APPROVE") {
264
+ const ci = await readCi(deps, pr, head, log);
265
+ if (ci && ciBlocksApproval(ci, { noChecksIsRed: deps.guards.no_checks_is_red })) {
266
+ log(`guard: #${pr} APPROVE → COMMENT (CI ${ci} at ${head.slice(0, 7)})`);
267
+ deps.store.logEvent("approval_withheld_red_ci", pr, { head_sha: head, ci_state: ci });
268
+ // The promise this note makes to the author — "waiting on CI alone" —
269
+ // is kept by the mark: the `ci_completed` trigger reads it and
270
+ // re-queues the PR the moment CI goes green (cigate.ts).
271
+ deps.store.recordApprovalWithheld(pr, { head_sha: head, ci_state: ci, reason: "red-ci" });
272
+ return {
273
+ intent: {
274
+ ...intent,
275
+ body: withholdApproval(body, ci, head),
276
+ note: `post COMMENT review on #${pr} (approval withheld: CI ${ci} at this head)`,
277
+ },
278
+ };
279
+ }
280
+ // Green (or invisible) CI: no approval is being withheld here any more.
281
+ if (ci === "green")
282
+ deps.store.clearApprovalWithheld(pr);
283
+ }
284
+ return {};
285
+ },
286
+ after(intent, result) {
287
+ // The mark is stamped only for a substantive review that really reached
288
+ // GitHub. A mechanically-authored COMMENT — the migration pre-check,
289
+ // the escalation note — is deterministic mechanics,
290
+ // not the round's verdict; letting one claim the head makes guard 2
291
+ // suppress the substantive review that follows. Keyed on the PRESENCE of
292
+ // a `reviewPurpose` rather than on each spelling, so a future purpose
293
+ // cannot claim a head by being forgotten here.
294
+ if (intent.kind !== "create_review" || intent.reviewPurpose || !result.executed || !intent.pr)
295
+ return;
296
+ const body = (intent.body ?? {});
297
+ if (!body.commit_id)
298
+ return;
299
+ deps.store.recordReviewedHead(intent.pr, {
300
+ head_sha: body.commit_id,
301
+ posted_event: (body.event ?? "").toUpperCase() || null,
302
+ });
303
+ },
304
+ };
305
+ }
306
+ //# sourceMappingURL=guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/guards.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export declare const HEARTBEAT_FILE = "heartbeat.json";
2
+ export interface Beat {
3
+ /** Counts up with every write. */
4
+ seq: number;
5
+ /** When it was written (ms since the epoch). */
6
+ at: number;
7
+ pid: number;
8
+ /** The latest deadline of a turn in flight; null while none runs. */
9
+ deadline_at: number | null;
10
+ /** Process groups the driver had running when it wrote this. */
11
+ pgids: number[];
12
+ }
13
+ /** A turn in flight ends at `at`: the beat carries it until the returned
14
+ * release is called. Cheap and inert when no heartbeat is running. The beat is
15
+ * rewritten at once: a driver that freezes in the turn's first moments (a
16
+ * synchronous loop on the SDK runtime stops the timer too) would otherwise leave
17
+ * a last beat without the deadline, and the watchdog would never act. */
18
+ export declare function trackDeadline(at: number): () => void;
19
+ /** Read a beat; null when the file is missing or is not one. */
20
+ export declare function readBeat(path: string): Beat | null;
21
+ /** Write a beat now and every `intervalMs` after; returns the stop. The timer
22
+ * never holds the process open. A write that fails is skipped: a heartbeat that
23
+ * cannot be written reads as a stopped one, which is what it is. */
24
+ export declare function startHeartbeat(path: string, o?: {
25
+ intervalMs?: number;
26
+ now?: () => number;
27
+ pgids?: () => readonly number[];
28
+ }): () => void;
29
+ //# sourceMappingURL=heartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,MAAM,WAAW,IAAI;IACnB,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gEAAgE;IAChE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD;;;;yEAIyE;AACzE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,IAAI,CAQpD;AAQD,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAclD;AAED;;oEAEoE;AACpE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,CAAC,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,MAAM,IAAI,CA2BZ"}
@@ -0,0 +1,87 @@
1
+ // The heartbeat of a detached review run: a small file its driver rewrites on
2
+ // a timer, which an OUTSIDE process (the watchdog in reviewdetach.ts) reads.
3
+ //
4
+ // An in-process deadline cannot cover a frozen event loop: a synchronous loop
5
+ // stops every timer, the budget's included, and the signal handlers with them.
6
+ // What the driver cannot say for itself, a file that stops advancing says for
7
+ // it. The beat also names the latest deadline of the turns in flight and the
8
+ // process groups of the pi children, so the watchdog can reach the children
9
+ // the driver's own group does not hold.
10
+ import { renameSync, readFileSync, writeFileSync } from "node:fs";
11
+ export const HEARTBEAT_FILE = "heartbeat.json";
12
+ const deadlines = new Set();
13
+ /** Writes a beat now: set while a heartbeat runs. */
14
+ let beatNow = null;
15
+ /** A turn in flight ends at `at`: the beat carries it until the returned
16
+ * release is called. Cheap and inert when no heartbeat is running. The beat is
17
+ * rewritten at once: a driver that freezes in the turn's first moments (a
18
+ * synchronous loop on the SDK runtime stops the timer too) would otherwise leave
19
+ * a last beat without the deadline, and the watchdog would never act. */
20
+ export function trackDeadline(at) {
21
+ const entry = { at };
22
+ deadlines.add(entry);
23
+ beatNow?.();
24
+ return () => {
25
+ deadlines.delete(entry);
26
+ beatNow?.();
27
+ };
28
+ }
29
+ function latestDeadline() {
30
+ let max = null;
31
+ for (const d of deadlines)
32
+ if (max === null || d.at > max)
33
+ max = d.at;
34
+ return max;
35
+ }
36
+ /** Read a beat; null when the file is missing or is not one. */
37
+ export function readBeat(path) {
38
+ try {
39
+ const b = JSON.parse(readFileSync(path, "utf8"));
40
+ if (typeof b.at !== "number" || typeof b.seq !== "number")
41
+ return null;
42
+ return {
43
+ seq: b.seq,
44
+ at: b.at,
45
+ pid: typeof b.pid === "number" ? b.pid : 0,
46
+ deadline_at: typeof b.deadline_at === "number" ? b.deadline_at : null,
47
+ pgids: Array.isArray(b.pgids) ? b.pgids.filter((p) => Number.isInteger(p)) : [],
48
+ };
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ /** Write a beat now and every `intervalMs` after; returns the stop. The timer
55
+ * never holds the process open. A write that fails is skipped: a heartbeat that
56
+ * cannot be written reads as a stopped one, which is what it is. */
57
+ export function startHeartbeat(path, o = {}) {
58
+ const now = o.now ?? Date.now;
59
+ let seq = 0;
60
+ const beat = () => {
61
+ const b = {
62
+ seq: ++seq,
63
+ at: now(),
64
+ pid: process.pid,
65
+ deadline_at: latestDeadline(),
66
+ pgids: [...(o.pgids?.() ?? [])],
67
+ };
68
+ try {
69
+ // renamed into place: the watchdog never reads half a beat
70
+ writeFileSync(`${path}.tmp`, JSON.stringify(b));
71
+ renameSync(`${path}.tmp`, path);
72
+ }
73
+ catch {
74
+ // see above
75
+ }
76
+ };
77
+ beat();
78
+ beatNow = beat;
79
+ const timer = setInterval(beat, o.intervalMs ?? 2_000);
80
+ timer.unref();
81
+ return () => {
82
+ clearInterval(timer);
83
+ if (beatNow === beat)
84
+ beatNow = null;
85
+ };
86
+ }
87
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"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"}