@quickchatai/quickreview 0.0.0-stage → 1.2.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 (1479) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +401 -2
  3. package/THIRD-PARTY-NOTICES.md +134 -0
  4. package/agents/pass-abstraction.md +64 -0
  5. package/agents/pass-ask-human.md +134 -0
  6. package/agents/pass-backcompat.md +67 -0
  7. package/agents/pass-baseline.md +61 -0
  8. package/agents/pass-behaviour-delta.md +64 -0
  9. package/agents/pass-bounds.md +36 -0
  10. package/agents/pass-claims.md +31 -0
  11. package/agents/pass-concurrency.md +168 -0
  12. package/agents/pass-consistency.md +57 -0
  13. package/agents/pass-contracts.md +30 -0
  14. package/agents/pass-data-safety.md +54 -0
  15. package/agents/pass-deferred.md +35 -0
  16. package/agents/pass-delivery.md +37 -0
  17. package/agents/pass-duplication.md +60 -0
  18. package/agents/pass-intent.md +63 -0
  19. package/agents/pass-lint-conformance.md +46 -0
  20. package/agents/pass-maintainability.md +67 -0
  21. package/agents/pass-placement.md +68 -0
  22. package/agents/pass-prod-risk.md +24 -0
  23. package/agents/pass-rollout.md +32 -0
  24. package/agents/pass-roundtrip.md +46 -0
  25. package/agents/pass-security.md +31 -0
  26. package/agents/pass-simplicity.md +68 -0
  27. package/agents/pass-statelessness.md +27 -0
  28. package/agents/pass-style.md +37 -0
  29. package/agents/pass-sweep.md +57 -0
  30. package/agents/pass-tests.md +58 -0
  31. package/agents/pass-ui.md +23 -0
  32. package/agents/pass-validate.md +50 -0
  33. package/agents/pass-widened.md +46 -0
  34. package/agents/scan/pass-scan.md +86 -0
  35. package/bin/qcr.js +11 -0
  36. package/config.example.toml +1099 -0
  37. package/deploy/ansible/README.md +356 -0
  38. package/deploy/ansible/group_vars/all.yml +212 -0
  39. package/deploy/ansible/inventory.example.ini +25 -0
  40. package/deploy/ansible/playbook.yml +360 -0
  41. package/deploy/collect-secrets.sh +289 -0
  42. package/deploy/collect-slack-secrets.mjs +1190 -0
  43. package/deploy/github-app-manifest.json +31 -0
  44. package/deploy/manifest.schema.json +75 -0
  45. package/deploy/qcr-update.sh +803 -0
  46. package/deploy/reviewer/AGENTS.override.md +12 -0
  47. package/dist/extensions/cli/bash-guard-ambient.js +42 -0
  48. package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
  49. package/dist/extensions/cli/bash-guard.js +5 -0
  50. package/dist/extensions/cli/bash-guard.js.map +1 -0
  51. package/dist/extensions/cli/companion-guard.js +5 -0
  52. package/dist/extensions/cli/companion-guard.js.map +1 -0
  53. package/dist/extensions/cli/debug-round.js +8 -0
  54. package/dist/extensions/cli/debug-round.js.map +1 -0
  55. package/dist/extensions/cli/inject-bridge.js +5 -0
  56. package/dist/extensions/cli/inject-bridge.js.map +1 -0
  57. package/dist/extensions/cli/ledger-pin.js +5 -0
  58. package/dist/extensions/cli/ledger-pin.js.map +1 -0
  59. package/dist/extensions/cli/lens.js +13 -0
  60. package/dist/extensions/cli/lens.js.map +1 -0
  61. package/dist/extensions/cli/login-route.js +5 -0
  62. package/dist/extensions/cli/login-route.js.map +1 -0
  63. package/dist/extensions/cli/muted-theme.js +5 -0
  64. package/dist/extensions/cli/muted-theme.js.map +1 -0
  65. package/dist/extensions/cli/onboarding.js +5 -0
  66. package/dist/extensions/cli/onboarding.js.map +1 -0
  67. package/dist/extensions/cli/setup.js +5 -0
  68. package/dist/extensions/cli/setup.js.map +1 -0
  69. package/dist/extensions/cli/tui-view.js +5 -0
  70. package/dist/extensions/cli/tui-view.js.map +1 -0
  71. package/dist/extensions/companion-guard.js +361 -0
  72. package/dist/extensions/companion-guard.js.map +1 -0
  73. package/dist/extensions/debug-round.js +204 -0
  74. package/dist/extensions/debug-round.js.map +1 -0
  75. package/dist/extensions/inject-bridge.js +603 -0
  76. package/dist/extensions/inject-bridge.js.map +1 -0
  77. package/dist/extensions/login-route.js +180 -0
  78. package/dist/extensions/login-route.js.map +1 -0
  79. package/dist/extensions/muted-theme.js +12 -0
  80. package/dist/extensions/muted-theme.js.map +1 -0
  81. package/dist/extensions/onboarding.js +198 -0
  82. package/dist/extensions/onboarding.js.map +1 -0
  83. package/dist/extensions/setup-replay.js +269 -0
  84. package/dist/extensions/setup-replay.js.map +1 -0
  85. package/dist/extensions/setup-scan.js +998 -0
  86. package/dist/extensions/setup-scan.js.map +1 -0
  87. package/dist/extensions/setup-triage.js +175 -0
  88. package/dist/extensions/setup-triage.js.map +1 -0
  89. package/dist/extensions/setup.js +1160 -0
  90. package/dist/extensions/setup.js.map +1 -0
  91. package/dist/extensions/tui-view.js +389 -0
  92. package/dist/extensions/tui-view.js.map +1 -0
  93. package/dist/extensions/vendor/openviking-pi/client.js +376 -0
  94. package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
  95. package/dist/extensions/vendor/openviking-pi/config.js +189 -0
  96. package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
  97. package/dist/extensions/vendor/openviking-pi/index.js +421 -0
  98. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
  99. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
  100. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
  101. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
  102. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
  103. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
  104. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
  105. package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
  106. package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
  107. package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
  108. package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
  109. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
  110. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
  111. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
  112. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
  113. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
  114. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
  115. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
  116. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
  117. package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
  118. package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
  119. package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
  120. package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
  121. package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
  122. package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
  123. package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
  124. package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
  125. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
  126. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
  127. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
  128. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
  129. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
  130. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
  131. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
  132. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
  133. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
  134. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
  135. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
  136. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
  137. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
  138. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
  139. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
  140. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
  141. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
  142. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
  143. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  144. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
  145. package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
  146. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
  147. package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
  148. package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
  149. package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
  150. package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
  151. package/dist/src/anomalies.js +483 -0
  152. package/dist/src/anomalies.js.map +1 -0
  153. package/dist/src/answerworker.js +180 -0
  154. package/dist/src/answerworker.js.map +1 -0
  155. package/dist/src/appstamp.js +68 -0
  156. package/dist/src/appstamp.js.map +1 -0
  157. package/dist/src/archive/admission.js +3 -0
  158. package/dist/src/archive/admission.js.map +1 -0
  159. package/dist/src/archive/archivedb.js +474 -0
  160. package/dist/src/archive/archivedb.js.map +1 -0
  161. package/dist/src/archive/archiver.js +1770 -0
  162. package/dist/src/archive/archiver.js.map +1 -0
  163. package/dist/src/archive/archivewrite.js +198 -0
  164. package/dist/src/archive/archivewrite.js.map +1 -0
  165. package/dist/src/archive/bundle.js +266 -0
  166. package/dist/src/archive/bundle.js.map +1 -0
  167. package/dist/src/archive/dbmove.js +638 -0
  168. package/dist/src/archive/dbmove.js.map +1 -0
  169. package/dist/src/archive/duplicatecounts.js +80 -0
  170. package/dist/src/archive/duplicatecounts.js.map +1 -0
  171. package/dist/src/archive/eligibility.js +113 -0
  172. package/dist/src/archive/eligibility.js.map +1 -0
  173. package/dist/src/archive/extract.js +288 -0
  174. package/dist/src/archive/extract.js.map +1 -0
  175. package/dist/src/archive/fsutil.js +330 -0
  176. package/dist/src/archive/fsutil.js.map +1 -0
  177. package/dist/src/archive/gzfile.js +277 -0
  178. package/dist/src/archive/gzfile.js.map +1 -0
  179. package/dist/src/archive/kinds.js +22 -0
  180. package/dist/src/archive/kinds.js.map +1 -0
  181. package/dist/src/archive/livecheckpoint.js +178 -0
  182. package/dist/src/archive/livecheckpoint.js.map +1 -0
  183. package/dist/src/archive/liveretention.js +145 -0
  184. package/dist/src/archive/liveretention.js.map +1 -0
  185. package/dist/src/archive/manifest.js +284 -0
  186. package/dist/src/archive/manifest.js.map +1 -0
  187. package/dist/src/archive/overseer.js +283 -0
  188. package/dist/src/archive/overseer.js.map +1 -0
  189. package/dist/src/archive/piglobal.js +129 -0
  190. package/dist/src/archive/piglobal.js.map +1 -0
  191. package/dist/src/archive/plan.js +398 -0
  192. package/dist/src/archive/plan.js.map +1 -0
  193. package/dist/src/archive/prarchive.js +831 -0
  194. package/dist/src/archive/prarchive.js.map +1 -0
  195. package/dist/src/archive/prfiles.js +303 -0
  196. package/dist/src/archive/prfiles.js.map +1 -0
  197. package/dist/src/archive/report.js +169 -0
  198. package/dist/src/archive/report.js.map +1 -0
  199. package/dist/src/archive/restore.js +276 -0
  200. package/dist/src/archive/restore.js.map +1 -0
  201. package/dist/src/archive/rows.js +106 -0
  202. package/dist/src/archive/rows.js.map +1 -0
  203. package/dist/src/archive/seal.js +480 -0
  204. package/dist/src/archive/seal.js.map +1 -0
  205. package/dist/src/archive/status.js +528 -0
  206. package/dist/src/archive/status.js.map +1 -0
  207. package/dist/src/archive/storepass.js +409 -0
  208. package/dist/src/archive/storepass.js.map +1 -0
  209. package/dist/src/backfill.js +217 -0
  210. package/dist/src/backfill.js.map +1 -0
  211. package/dist/src/bench/cost.js +87 -0
  212. package/dist/src/bench/exec.js +76 -0
  213. package/dist/src/bench/values.js +14 -0
  214. package/dist/src/botcontrol.js +127 -0
  215. package/dist/src/botcontrol.js.map +1 -0
  216. package/dist/src/botworker.js +805 -0
  217. package/dist/src/botworker.js.map +1 -0
  218. package/dist/src/capture.js +234 -0
  219. package/dist/src/capture.js.map +1 -0
  220. package/dist/src/cases.js +1491 -0
  221. package/dist/src/cases.js.map +1 -0
  222. package/dist/src/channelfs.js +27 -0
  223. package/dist/src/channelfs.js.map +1 -0
  224. package/dist/src/checklist.js +312 -0
  225. package/dist/src/checklist.js.map +1 -0
  226. package/dist/src/ciretry.js +92 -0
  227. package/dist/src/ciretry.js.map +1 -0
  228. package/dist/src/cli.js +1947 -0
  229. package/dist/src/cli.js.map +1 -0
  230. package/dist/src/cliops.js +334 -0
  231. package/dist/src/cliops.js.map +1 -0
  232. package/dist/src/clonedir.js +15 -0
  233. package/dist/src/clonedir.js.map +1 -0
  234. package/dist/src/commandeffects.js +43 -0
  235. package/dist/src/commandeffects.js.map +1 -0
  236. package/dist/src/commands.js +1335 -0
  237. package/dist/src/commands.js.map +1 -0
  238. package/dist/src/companioninbox.js +203 -0
  239. package/dist/src/companioninbox.js.map +1 -0
  240. package/dist/src/compare.js +673 -0
  241. package/dist/src/compare.js.map +1 -0
  242. package/dist/src/config.js +1041 -0
  243. package/dist/src/config.js.map +1 -0
  244. package/dist/src/configcheck.js +111 -0
  245. package/dist/src/configcheck.js.map +1 -0
  246. package/dist/src/coreext.js +43 -0
  247. package/dist/src/coreext.js.map +1 -0
  248. package/dist/src/daemon.js +945 -0
  249. package/dist/src/daemon.js.map +1 -0
  250. package/dist/src/debuground.js +731 -0
  251. package/dist/src/debuground.js.map +1 -0
  252. package/dist/src/debugroundpi.js +223 -0
  253. package/dist/src/debugroundpi.js.map +1 -0
  254. package/dist/src/diagnostics.js +14 -0
  255. package/dist/src/diagnostics.js.map +1 -0
  256. package/dist/src/digest.js +139 -0
  257. package/dist/src/digest.js.map +1 -0
  258. package/dist/src/dispatch.js +1727 -0
  259. package/dist/src/dispatch.js.map +1 -0
  260. package/dist/src/engine/answerguard.js +18 -0
  261. package/dist/src/engine/answerguard.js.map +1 -0
  262. package/dist/src/engine/botsdk.js +262 -0
  263. package/dist/src/engine/botsdk.js.map +1 -0
  264. package/dist/src/engine/caserunner-sdk.js +332 -0
  265. package/dist/src/engine/caserunner-sdk.js.map +1 -0
  266. package/dist/src/engine/debugpi.js +206 -0
  267. package/dist/src/engine/debugpi.js.map +1 -0
  268. package/dist/src/eod.js +473 -0
  269. package/dist/src/eod.js.map +1 -0
  270. package/dist/src/eodpinwatch.js +391 -0
  271. package/dist/src/eodpinwatch.js.map +1 -0
  272. package/dist/src/escalation.js +1009 -0
  273. package/dist/src/escalation.js.map +1 -0
  274. package/dist/src/eventloop.js +779 -0
  275. package/dist/src/eventloop.js.map +1 -0
  276. package/dist/src/execboundary.js +437 -0
  277. package/dist/src/execboundary.js.map +1 -0
  278. package/dist/src/feedback.js +915 -0
  279. package/dist/src/feedback.js.map +1 -0
  280. package/dist/src/findingscheck.js +200 -0
  281. package/dist/src/findingscheck.js.map +1 -0
  282. package/dist/src/fsdenied.js +26 -0
  283. package/dist/src/fsdenied.js.map +1 -0
  284. package/dist/src/ghread.js +181 -0
  285. package/dist/src/ghread.js.map +1 -0
  286. package/dist/src/github.js +1098 -0
  287. package/dist/src/github.js.map +1 -0
  288. package/dist/src/githubtoken.js +346 -0
  289. package/dist/src/githubtoken.js.map +1 -0
  290. package/dist/src/homes.js +103 -0
  291. package/dist/src/homes.js.map +1 -0
  292. package/dist/src/hostroute.js +24 -0
  293. package/dist/src/hostroute.js.map +1 -0
  294. package/dist/src/improvements.js +625 -0
  295. package/dist/src/improvements.js.map +1 -0
  296. package/dist/src/install.js +946 -0
  297. package/dist/src/install.js.map +1 -0
  298. package/dist/src/installkind.js +40 -0
  299. package/dist/src/installkind.js.map +1 -0
  300. package/dist/src/ipc/capability.js +286 -0
  301. package/dist/src/ipc/capability.js.map +1 -0
  302. package/dist/src/ipc/client.js +226 -0
  303. package/dist/src/ipc/client.js.map +1 -0
  304. package/dist/src/ipc/control.js +70 -0
  305. package/dist/src/ipc/control.js.map +1 -0
  306. package/dist/src/ipc/ghreadshim.js +77 -0
  307. package/dist/src/ipc/ghreadshim.js.map +1 -0
  308. package/dist/src/ipc/launcher.js +65 -0
  309. package/dist/src/ipc/launcher.js.map +1 -0
  310. package/dist/src/ipc/permits.js +293 -0
  311. package/dist/src/ipc/permits.js.map +1 -0
  312. package/dist/src/ipc/postshim.js +137 -0
  313. package/dist/src/ipc/postshim.js.map +1 -0
  314. package/dist/src/ipc/protocol.js +239 -0
  315. package/dist/src/ipc/protocol.js.map +1 -0
  316. package/dist/src/ipc/server.js +312 -0
  317. package/dist/src/ipc/server.js.map +1 -0
  318. package/dist/src/ipc/workerspec.js +148 -0
  319. package/dist/src/ipc/workerspec.js.map +1 -0
  320. package/dist/src/learner/learner.js +1272 -0
  321. package/dist/src/learner/learner.js.map +1 -0
  322. package/dist/src/learner/miner.js +948 -0
  323. package/dist/src/learner/miner.js.map +1 -0
  324. package/dist/src/learner/packet.js +216 -0
  325. package/dist/src/learner/packet.js.map +1 -0
  326. package/dist/src/learner/receipt.js +135 -0
  327. package/dist/src/learner/receipt.js.map +1 -0
  328. package/dist/src/learner/status.js +38 -0
  329. package/dist/src/learner/status.js.map +1 -0
  330. package/dist/src/ledgercheck.js +120 -0
  331. package/dist/src/ledgercheck.js.map +1 -0
  332. package/dist/src/lessonledger.js +451 -0
  333. package/dist/src/lessonledger.js.map +1 -0
  334. package/dist/src/lessonselect.js +639 -0
  335. package/dist/src/lessonselect.js.map +1 -0
  336. package/dist/src/lessonshadow.js +540 -0
  337. package/dist/src/lessonshadow.js.map +1 -0
  338. package/dist/src/levers.js +302 -0
  339. package/dist/src/levers.js.map +1 -0
  340. package/dist/src/live/projection.js +450 -0
  341. package/dist/src/live/projection.js.map +1 -0
  342. package/dist/src/live/publish.js +83 -0
  343. package/dist/src/live/publish.js.map +1 -0
  344. package/dist/src/live/read.js +60 -0
  345. package/dist/src/live/read.js.map +1 -0
  346. package/dist/src/livecli.js +288 -0
  347. package/dist/src/livecli.js.map +1 -0
  348. package/dist/src/loginhint.js +47 -0
  349. package/dist/src/loginhint.js.map +1 -0
  350. package/dist/src/loginrefresh/atomic.js +66 -0
  351. package/dist/src/loginrefresh/atomic.js.map +1 -0
  352. package/dist/src/loginrefresh/lock.js +93 -0
  353. package/dist/src/loginrefresh/lock.js.map +1 -0
  354. package/dist/src/loginrefresh/main.js +58 -0
  355. package/dist/src/loginrefresh/main.js.map +1 -0
  356. package/dist/src/loginrefresh/pirefresh.js +52 -0
  357. package/dist/src/loginrefresh/pirefresh.js.map +1 -0
  358. package/dist/src/loginrefresh/refresher.js +286 -0
  359. package/dist/src/loginrefresh/refresher.js.map +1 -0
  360. package/dist/src/loginrefresh/supervisor.js +253 -0
  361. package/dist/src/loginrefresh/supervisor.js.map +1 -0
  362. package/dist/src/loginrefresh/types.js +4 -0
  363. package/dist/src/loginrefresh/types.js.map +1 -0
  364. package/dist/src/memory/context.js +251 -0
  365. package/dist/src/memory/context.js.map +1 -0
  366. package/dist/src/memory/datafence.js +5 -0
  367. package/dist/src/memory/datafence.js.map +1 -0
  368. package/dist/src/memory/delivery.js +131 -0
  369. package/dist/src/memory/delivery.js.map +1 -0
  370. package/dist/src/memory/eventtags.js +81 -0
  371. package/dist/src/memory/eventtags.js.map +1 -0
  372. package/dist/src/memory/health.js +713 -0
  373. package/dist/src/memory/health.js.map +1 -0
  374. package/dist/src/memory/import.js +504 -0
  375. package/dist/src/memory/import.js.map +1 -0
  376. package/dist/src/memory/layout.js +252 -0
  377. package/dist/src/memory/layout.js.map +1 -0
  378. package/dist/src/memory/legacy-file.js +100 -0
  379. package/dist/src/memory/legacy-file.js.map +1 -0
  380. package/dist/src/memory/lessons.js +370 -0
  381. package/dist/src/memory/lessons.js.map +1 -0
  382. package/dist/src/memory/ovsession.js +449 -0
  383. package/dist/src/memory/ovsession.js.map +1 -0
  384. package/dist/src/memory/relayout.js +259 -0
  385. package/dist/src/memory/relayout.js.map +1 -0
  386. package/dist/src/memory/roundactivity.js +180 -0
  387. package/dist/src/memory/roundactivity.js.map +1 -0
  388. package/dist/src/memory/selection.js +690 -0
  389. package/dist/src/memory/selection.js.map +1 -0
  390. package/dist/src/memory/shadowstatus.js +69 -0
  391. package/dist/src/memory/shadowstatus.js.map +1 -0
  392. package/dist/src/memory/store.js +83 -0
  393. package/dist/src/memory/store.js.map +1 -0
  394. package/dist/src/memory/textidentity.js +8 -0
  395. package/dist/src/memory/textidentity.js.map +1 -0
  396. package/dist/src/metrics.js +773 -0
  397. package/dist/src/metrics.js.map +1 -0
  398. package/dist/src/missingconfig.js +8 -0
  399. package/dist/src/missingconfig.js.map +1 -0
  400. package/dist/src/npmverbs.js +228 -0
  401. package/dist/src/npmverbs.js.map +1 -0
  402. package/dist/src/onboard/choices.js +219 -0
  403. package/dist/src/onboard/choices.js.map +1 -0
  404. package/dist/src/onboard/engine.js +329 -0
  405. package/dist/src/onboard/engine.js.map +1 -0
  406. package/dist/src/onboard/launch.js +75 -0
  407. package/dist/src/onboard/launch.js.map +1 -0
  408. package/dist/src/onboard/pipin.js +22 -0
  409. package/dist/src/onboard/pipin.js.map +1 -0
  410. package/dist/src/onboard/stepforward.js +41 -0
  411. package/dist/src/onboard/stepforward.js.map +1 -0
  412. package/dist/src/onboard/steps/appslug.js +214 -0
  413. package/dist/src/onboard/steps/appslug.js.map +1 -0
  414. package/dist/src/onboard/steps/bringup.js +408 -0
  415. package/dist/src/onboard/steps/bringup.js.map +1 -0
  416. package/dist/src/onboard/steps/compose.js +372 -0
  417. package/dist/src/onboard/steps/compose.js.map +1 -0
  418. package/dist/src/onboard/steps/configdelivery.js +50 -0
  419. package/dist/src/onboard/steps/configdelivery.js.map +1 -0
  420. package/dist/src/onboard/steps/configfill.js +1218 -0
  421. package/dist/src/onboard/steps/configfill.js.map +1 -0
  422. package/dist/src/onboard/steps/deploysh.js +80 -0
  423. package/dist/src/onboard/steps/deploysh.js.map +1 -0
  424. package/dist/src/onboard/steps/embeddings.js +472 -0
  425. package/dist/src/onboard/steps/embeddings.js.map +1 -0
  426. package/dist/src/onboard/steps/escalation.js +571 -0
  427. package/dist/src/onboard/steps/escalation.js.map +1 -0
  428. package/dist/src/onboard/steps/githubaccount.js +595 -0
  429. package/dist/src/onboard/steps/githubaccount.js.map +1 -0
  430. package/dist/src/onboard/steps/githubapp.js +1150 -0
  431. package/dist/src/onboard/steps/githubapp.js.map +1 -0
  432. package/dist/src/onboard/steps/golive.js +508 -0
  433. package/dist/src/onboard/steps/golive.js.map +1 -0
  434. package/dist/src/onboard/steps/index.js +78 -0
  435. package/dist/src/onboard/steps/index.js.map +1 -0
  436. package/dist/src/onboard/steps/ingress.js +817 -0
  437. package/dist/src/onboard/steps/ingress.js.map +1 -0
  438. package/dist/src/onboard/steps/inventory.js +420 -0
  439. package/dist/src/onboard/steps/inventory.js.map +1 -0
  440. package/dist/src/onboard/steps/llmauth.js +966 -0
  441. package/dist/src/onboard/steps/llmauth.js.map +1 -0
  442. package/dist/src/onboard/steps/memory.js +1112 -0
  443. package/dist/src/onboard/steps/memory.js.map +1 -0
  444. package/dist/src/onboard/steps/memorykeys.js +43 -0
  445. package/dist/src/onboard/steps/memorykeys.js.map +1 -0
  446. package/dist/src/onboard/steps/preflight.js +172 -0
  447. package/dist/src/onboard/steps/preflight.js.map +1 -0
  448. package/dist/src/onboard/steps/proof.js +812 -0
  449. package/dist/src/onboard/steps/proof.js.map +1 -0
  450. package/dist/src/onboard/steps/ssh.js +300 -0
  451. package/dist/src/onboard/steps/ssh.js.map +1 -0
  452. package/dist/src/onboard/steps/topology.js +291 -0
  453. package/dist/src/onboard/steps/topology.js.map +1 -0
  454. package/dist/src/onboard/steps/transport.js +519 -0
  455. package/dist/src/onboard/steps/transport.js.map +1 -0
  456. package/dist/src/onboard/steps/tuiroute.js +102 -0
  457. package/dist/src/onboard/steps/tuiroute.js.map +1 -0
  458. package/dist/src/onboard/steps/verifyup.js +378 -0
  459. package/dist/src/onboard/steps/verifyup.js.map +1 -0
  460. package/dist/src/onboard/steps/vmdeploy.js +368 -0
  461. package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
  462. package/dist/src/onboard/toml.js +53 -0
  463. package/dist/src/onboard/toml.js.map +1 -0
  464. package/dist/src/onboard/toolnames.js +30 -0
  465. package/dist/src/onboard/toolnames.js.map +1 -0
  466. package/dist/src/opencli.js +508 -0
  467. package/dist/src/opencli.js.map +1 -0
  468. package/dist/src/openpr.js +457 -0
  469. package/dist/src/openpr.js.map +1 -0
  470. package/dist/src/openviking/client.js +821 -0
  471. package/dist/src/openviking/client.js.map +1 -0
  472. package/dist/src/openviking/context.js +218 -0
  473. package/dist/src/openviking/context.js.map +1 -0
  474. package/dist/src/openviking/embedder.js +457 -0
  475. package/dist/src/openviking/embedder.js.map +1 -0
  476. package/dist/src/openviking/gapsink.js +38 -0
  477. package/dist/src/openviking/gapsink.js.map +1 -0
  478. package/dist/src/openviking/health.js +69 -0
  479. package/dist/src/openviking/health.js.map +1 -0
  480. package/dist/src/openviking/index.js +24 -0
  481. package/dist/src/openviking/index.js.map +1 -0
  482. package/dist/src/openviking/lessonfind.js +54 -0
  483. package/dist/src/openviking/lessonfind.js.map +1 -0
  484. package/dist/src/openviking/probe.js +89 -0
  485. package/dist/src/openviking/probe.js.map +1 -0
  486. package/dist/src/openviking/proc.js +95 -0
  487. package/dist/src/openviking/proc.js.map +1 -0
  488. package/dist/src/openviking/provisionreport.js +144 -0
  489. package/dist/src/openviking/provisionreport.js.map +1 -0
  490. package/dist/src/openviking/reembed.js +317 -0
  491. package/dist/src/openviking/reembed.js.map +1 -0
  492. package/dist/src/openviking/relayout.js +153 -0
  493. package/dist/src/openviking/relayout.js.map +1 -0
  494. package/dist/src/openviking/sidecar-main.js +39 -0
  495. package/dist/src/openviking/sidecar-main.js.map +1 -0
  496. package/dist/src/openviking/sidecar.js +79 -0
  497. package/dist/src/openviking/sidecar.js.map +1 -0
  498. package/dist/src/openviking/store.js +1758 -0
  499. package/dist/src/openviking/store.js.map +1 -0
  500. package/dist/src/openviking/supervisor.js +422 -0
  501. package/dist/src/openviking/supervisor.js.map +1 -0
  502. package/dist/src/openviking/types.js +5 -0
  503. package/dist/src/openviking/types.js.map +1 -0
  504. package/dist/src/operator-recovery.js +172 -0
  505. package/dist/src/operator-recovery.js.map +1 -0
  506. package/dist/src/operatormessage.js +44 -0
  507. package/dist/src/operatormessage.js.map +1 -0
  508. package/dist/src/operatorwrite.js +23 -0
  509. package/dist/src/operatorwrite.js.map +1 -0
  510. package/dist/src/outcomes.js +1364 -0
  511. package/dist/src/outcomes.js.map +1 -0
  512. package/dist/src/piauthcopy.js +5 -0
  513. package/dist/src/piauthcopy.js.map +1 -0
  514. package/dist/src/picli.js +45 -0
  515. package/dist/src/picli.js.map +1 -0
  516. package/dist/src/picwd.js +25 -0
  517. package/dist/src/picwd.js.map +1 -0
  518. package/dist/src/pilogin.js +35 -0
  519. package/dist/src/pilogin.js.map +1 -0
  520. package/dist/src/pingpong.js +1028 -0
  521. package/dist/src/pingpong.js.map +1 -0
  522. package/dist/src/posting-journal.js +442 -0
  523. package/dist/src/posting-journal.js.map +1 -0
  524. package/dist/src/posting-outbox.js +666 -0
  525. package/dist/src/posting-outbox.js.map +1 -0
  526. package/dist/src/posting-receipt.js +216 -0
  527. package/dist/src/posting-receipt.js.map +1 -0
  528. package/dist/src/posting.js +348 -0
  529. package/dist/src/posting.js.map +1 -0
  530. package/dist/src/postops.js +637 -0
  531. package/dist/src/postops.js.map +1 -0
  532. package/dist/src/pr-activity.js +11 -0
  533. package/dist/src/pr-activity.js.map +1 -0
  534. package/dist/src/pr-author.js +5 -0
  535. package/dist/src/pr-author.js.map +1 -0
  536. package/dist/src/pr-conversation.js +355 -0
  537. package/dist/src/pr-conversation.js.map +1 -0
  538. package/dist/src/pr-session-lifecycle.js +542 -0
  539. package/dist/src/pr-session-lifecycle.js.map +1 -0
  540. package/dist/src/preflight.js +341 -0
  541. package/dist/src/preflight.js.map +1 -0
  542. package/dist/src/priorresolve.js +275 -0
  543. package/dist/src/priorresolve.js.map +1 -0
  544. package/dist/src/qcrbin.js +180 -0
  545. package/dist/src/qcrbin.js.map +1 -0
  546. package/dist/src/recheck.js +1399 -0
  547. package/dist/src/recheck.js.map +1 -0
  548. package/dist/src/reconcile.js +840 -0
  549. package/dist/src/reconcile.js.map +1 -0
  550. package/dist/src/recordedfindings.js +15 -0
  551. package/dist/src/recordedfindings.js.map +1 -0
  552. package/dist/src/regression.js +546 -0
  553. package/dist/src/regression.js.map +1 -0
  554. package/dist/src/replay.js +598 -0
  555. package/dist/src/replay.js.map +1 -0
  556. package/dist/src/requiredpasses.js +113 -0
  557. package/dist/src/requiredpasses.js.map +1 -0
  558. package/dist/src/resumecheck.js +168 -0
  559. package/dist/src/resumecheck.js.map +1 -0
  560. package/dist/src/reviewcli.js +671 -0
  561. package/dist/src/reviewcli.js.map +1 -0
  562. package/dist/src/reviewcontext.js +22 -0
  563. package/dist/src/reviewcontext.js.map +1 -0
  564. package/dist/src/reviewdetach.js +574 -0
  565. package/dist/src/reviewdetach.js.map +1 -0
  566. package/dist/src/reviewhistory.js +310 -0
  567. package/dist/src/reviewhistory.js.map +1 -0
  568. package/dist/src/reviewhome.js +26 -0
  569. package/dist/src/reviewhome.js.map +1 -0
  570. package/dist/src/reviewmemory.js +110 -0
  571. package/dist/src/reviewmemory.js.map +1 -0
  572. package/dist/src/reviewnow.js +45 -0
  573. package/dist/src/reviewnow.js.map +1 -0
  574. package/dist/src/reviewrendercli.js +104 -0
  575. package/dist/src/reviewrendercli.js.map +1 -0
  576. package/dist/src/round-work.js +20 -0
  577. package/dist/src/round-work.js.map +1 -0
  578. package/dist/src/runhost.js +72 -0
  579. package/dist/src/runhost.js.map +1 -0
  580. package/dist/src/scanauth.js +25 -0
  581. package/dist/src/scanauth.js.map +1 -0
  582. package/dist/src/scanbox.js +846 -0
  583. package/dist/src/scanbox.js.map +1 -0
  584. package/dist/src/scancli.js +1074 -0
  585. package/dist/src/scancli.js.map +1 -0
  586. package/dist/src/scandriverlog.js +30 -0
  587. package/dist/src/scandriverlog.js.map +1 -0
  588. package/dist/src/session-bridge.js +75 -0
  589. package/dist/src/session-bridge.js.map +1 -0
  590. package/dist/src/session-command-payload.js +29 -0
  591. package/dist/src/session-command-payload.js.map +1 -0
  592. package/dist/src/session-command-supervisor.js +939 -0
  593. package/dist/src/session-command-supervisor.js.map +1 -0
  594. package/dist/src/sessionjsonl.js +85 -0
  595. package/dist/src/sessionjsonl.js.map +1 -0
  596. package/dist/src/sessions.js +541 -0
  597. package/dist/src/sessions.js.map +1 -0
  598. package/dist/src/setup/adapters.js +330 -0
  599. package/dist/src/setup/adapters.js.map +1 -0
  600. package/dist/src/setup/baselines.js +64 -0
  601. package/dist/src/setup/baselines.js.map +1 -0
  602. package/dist/src/setup/caserunner.js +554 -0
  603. package/dist/src/setup/caserunner.js.map +1 -0
  604. package/dist/src/setup/configcommit.js +358 -0
  605. package/dist/src/setup/configcommit.js.map +1 -0
  606. package/dist/src/setup/engine.js +672 -0
  607. package/dist/src/setup/engine.js.map +1 -0
  608. package/dist/src/setup/facts.js +593 -0
  609. package/dist/src/setup/facts.js.map +1 -0
  610. package/dist/src/setup/findings.js +948 -0
  611. package/dist/src/setup/findings.js.map +1 -0
  612. package/dist/src/setup/history.js +338 -0
  613. package/dist/src/setup/history.js.map +1 -0
  614. package/dist/src/setup/interview.js +185 -0
  615. package/dist/src/setup/interview.js.map +1 -0
  616. package/dist/src/setup/ledger.js +207 -0
  617. package/dist/src/setup/ledger.js.map +1 -0
  618. package/dist/src/setup/pins.js +43 -0
  619. package/dist/src/setup/pins.js.map +1 -0
  620. package/dist/src/setup/portrait.js +432 -0
  621. package/dist/src/setup/portrait.js.map +1 -0
  622. package/dist/src/setup/presentation.js +9 -0
  623. package/dist/src/setup/presentation.js.map +1 -0
  624. package/dist/src/setup/profile.js +417 -0
  625. package/dist/src/setup/profile.js.map +1 -0
  626. package/dist/src/setup/replay.js +240 -0
  627. package/dist/src/setup/replay.js.map +1 -0
  628. package/dist/src/setup/replaybuild.js +311 -0
  629. package/dist/src/setup/replaybuild.js.map +1 -0
  630. package/dist/src/setup/replayrun.js +806 -0
  631. package/dist/src/setup/replayrun.js.map +1 -0
  632. package/dist/src/setup/replaysource.js +169 -0
  633. package/dist/src/setup/replaysource.js.map +1 -0
  634. package/dist/src/setup/retune.js +333 -0
  635. package/dist/src/setup/retune.js.map +1 -0
  636. package/dist/src/setup/retunelog.js +51 -0
  637. package/dist/src/setup/retunelog.js.map +1 -0
  638. package/dist/src/setup/scan.js +725 -0
  639. package/dist/src/setup/scan.js.map +1 -0
  640. package/dist/src/setup/scanbuild.js +133 -0
  641. package/dist/src/setup/scanbuild.js.map +1 -0
  642. package/dist/src/setup/scanfacts.js +96 -0
  643. package/dist/src/setup/scanfacts.js.map +1 -0
  644. package/dist/src/setup/scanlease.js +251 -0
  645. package/dist/src/setup/scanlease.js.map +1 -0
  646. package/dist/src/setup/scanreport.js +289 -0
  647. package/dist/src/setup/scanreport.js.map +1 -0
  648. package/dist/src/setup/scanrun.js +1733 -0
  649. package/dist/src/setup/scanrun.js.map +1 -0
  650. package/dist/src/setup/scantree.js +257 -0
  651. package/dist/src/setup/scantree.js.map +1 -0
  652. package/dist/src/setup/stage2.js +617 -0
  653. package/dist/src/setup/stage2.js.map +1 -0
  654. package/dist/src/setup/stage2request.js +259 -0
  655. package/dist/src/setup/stage2request.js.map +1 -0
  656. package/dist/src/setup/survey.js +307 -0
  657. package/dist/src/setup/survey.js.map +1 -0
  658. package/dist/src/setup/surveyrun.js +549 -0
  659. package/dist/src/setup/surveyrun.js.map +1 -0
  660. package/dist/src/setup/textsniff.js +89 -0
  661. package/dist/src/setup/textsniff.js.map +1 -0
  662. package/dist/src/setup/trackersink.js +395 -0
  663. package/dist/src/setup/trackersink.js.map +1 -0
  664. package/dist/src/setup/triage.js +636 -0
  665. package/dist/src/setup/triage.js.map +1 -0
  666. package/dist/src/setup/tuning.js +585 -0
  667. package/dist/src/setup/tuning.js.map +1 -0
  668. package/dist/src/setup/unitcut.js +437 -0
  669. package/dist/src/setup/unitcut.js.map +1 -0
  670. package/dist/src/singleflight.js +72 -0
  671. package/dist/src/singleflight.js.map +1 -0
  672. package/dist/src/slack/announce.js +725 -0
  673. package/dist/src/slack/announce.js.map +1 -0
  674. package/dist/src/slack/api.js +227 -0
  675. package/dist/src/slack/api.js.map +1 -0
  676. package/dist/src/slack/messages.js +158 -0
  677. package/dist/src/slack/messages.js.map +1 -0
  678. package/dist/src/slack/origin.js +550 -0
  679. package/dist/src/slack/origin.js.map +1 -0
  680. package/dist/src/slack/policy.js +225 -0
  681. package/dist/src/slack/policy.js.map +1 -0
  682. package/dist/src/slack/reply.js +153 -0
  683. package/dist/src/slack/reply.js.map +1 -0
  684. package/dist/src/slack/router.js +189 -0
  685. package/dist/src/slack/router.js.map +1 -0
  686. package/dist/src/slack/socket.js +323 -0
  687. package/dist/src/slack/socket.js.map +1 -0
  688. package/dist/src/slack/types.js +11 -0
  689. package/dist/src/slack/types.js.map +1 -0
  690. package/dist/src/sqlitehint.js +30 -0
  691. package/dist/src/sqlitehint.js.map +1 -0
  692. package/dist/src/ssh-observation.js +25 -0
  693. package/dist/src/ssh-observation.js.map +1 -0
  694. package/dist/src/state.js +5869 -0
  695. package/dist/src/state.js.map +1 -0
  696. package/dist/src/statemachine.js +223 -0
  697. package/dist/src/statemachine.js.map +1 -0
  698. package/dist/src/stubs.js +52 -0
  699. package/dist/src/stubs.js.map +1 -0
  700. package/dist/src/takeover.js +506 -0
  701. package/dist/src/takeover.js.map +1 -0
  702. package/dist/src/takeoverpi.js +262 -0
  703. package/dist/src/takeoverpi.js.map +1 -0
  704. package/dist/src/terminal-failure.js +26 -0
  705. package/dist/src/terminal-failure.js.map +1 -0
  706. package/dist/src/thread-resolution-refusal.js +4 -0
  707. package/dist/src/thread-resolution-refusal.js.map +1 -0
  708. package/dist/src/tmux.js +126 -0
  709. package/dist/src/tmux.js.map +1 -0
  710. package/dist/src/trackerid.js +43 -0
  711. package/dist/src/trackerid.js.map +1 -0
  712. package/dist/src/transcriptindex.js +512 -0
  713. package/dist/src/transcriptindex.js.map +1 -0
  714. package/dist/src/tui/actions.js +404 -0
  715. package/dist/src/tui/actions.js.map +1 -0
  716. package/dist/src/tui/alerts.js +523 -0
  717. package/dist/src/tui/alerts.js.map +1 -0
  718. package/dist/src/tui/app.js +1884 -0
  719. package/dist/src/tui/app.js.map +1 -0
  720. package/dist/src/tui/archiveops.js +164 -0
  721. package/dist/src/tui/archiveops.js.map +1 -0
  722. package/dist/src/tui/autofile.js +191 -0
  723. package/dist/src/tui/autofile.js.map +1 -0
  724. package/dist/src/tui/board.js +1219 -0
  725. package/dist/src/tui/board.js.map +1 -0
  726. package/dist/src/tui/candidates.js +699 -0
  727. package/dist/src/tui/candidates.js.map +1 -0
  728. package/dist/src/tui/cellwidth.js +151 -0
  729. package/dist/src/tui/cellwidth.js.map +1 -0
  730. package/dist/src/tui/changelog-store.js +404 -0
  731. package/dist/src/tui/changelog-store.js.map +1 -0
  732. package/dist/src/tui/chord.js +26 -0
  733. package/dist/src/tui/chord.js.map +1 -0
  734. package/dist/src/tui/clipboard.js +81 -0
  735. package/dist/src/tui/clipboard.js.map +1 -0
  736. package/dist/src/tui/cmdbar.js +256 -0
  737. package/dist/src/tui/cmdbar.js.map +1 -0
  738. package/dist/src/tui/companion.js +1339 -0
  739. package/dist/src/tui/companion.js.map +1 -0
  740. package/dist/src/tui/companionplan.js +200 -0
  741. package/dist/src/tui/companionplan.js.map +1 -0
  742. package/dist/src/tui/companiontab.js +61 -0
  743. package/dist/src/tui/companiontab.js.map +1 -0
  744. package/dist/src/tui/confighome.js +105 -0
  745. package/dist/src/tui/confighome.js.map +1 -0
  746. package/dist/src/tui/connect.js +50 -0
  747. package/dist/src/tui/connect.js.map +1 -0
  748. package/dist/src/tui/deploy-ceremony.js +75 -0
  749. package/dist/src/tui/deploy-ceremony.js.map +1 -0
  750. package/dist/src/tui/deploy-drain.js +93 -0
  751. package/dist/src/tui/deploy-drain.js.map +1 -0
  752. package/dist/src/tui/deploy-process.js +234 -0
  753. package/dist/src/tui/deploy-process.js.map +1 -0
  754. package/dist/src/tui/deploytab.js +1263 -0
  755. package/dist/src/tui/deploytab.js.map +1 -0
  756. package/dist/src/tui/design.js +111 -0
  757. package/dist/src/tui/design.js.map +1 -0
  758. package/dist/src/tui/digesttab.js +148 -0
  759. package/dist/src/tui/digesttab.js.map +1 -0
  760. package/dist/src/tui/emptystate.js +32 -0
  761. package/dist/src/tui/emptystate.js.map +1 -0
  762. package/dist/src/tui/evidence.js +790 -0
  763. package/dist/src/tui/evidence.js.map +1 -0
  764. package/dist/src/tui/feed.js +415 -0
  765. package/dist/src/tui/feed.js.map +1 -0
  766. package/dist/src/tui/feedbacktab.js +907 -0
  767. package/dist/src/tui/feedbacktab.js.map +1 -0
  768. package/dist/src/tui/filters.js +75 -0
  769. package/dist/src/tui/filters.js.map +1 -0
  770. package/dist/src/tui/ghread.js +328 -0
  771. package/dist/src/tui/ghread.js.map +1 -0
  772. package/dist/src/tui/historypull.js +61 -0
  773. package/dist/src/tui/historypull.js.map +1 -0
  774. package/dist/src/tui/inboxpull.js +197 -0
  775. package/dist/src/tui/inboxpull.js.map +1 -0
  776. package/dist/src/tui/index.js +1511 -0
  777. package/dist/src/tui/index.js.map +1 -0
  778. package/dist/src/tui/issuesink.js +206 -0
  779. package/dist/src/tui/issuesink.js.map +1 -0
  780. package/dist/src/tui/jsonfile.js +32 -0
  781. package/dist/src/tui/jsonfile.js.map +1 -0
  782. package/dist/src/tui/keys.js +97 -0
  783. package/dist/src/tui/keys.js.map +1 -0
  784. package/dist/src/tui/layout.js +70 -0
  785. package/dist/src/tui/layout.js.map +1 -0
  786. package/dist/src/tui/learning.js +469 -0
  787. package/dist/src/tui/learning.js.map +1 -0
  788. package/dist/src/tui/lessonswitch.js +103 -0
  789. package/dist/src/tui/lessonswitch.js.map +1 -0
  790. package/dist/src/tui/livecontrol.js +167 -0
  791. package/dist/src/tui/livecontrol.js.map +1 -0
  792. package/dist/src/tui/memforward.js +347 -0
  793. package/dist/src/tui/memforward.js.map +1 -0
  794. package/dist/src/tui/memincident.js +287 -0
  795. package/dist/src/tui/memincident.js.map +1 -0
  796. package/dist/src/tui/memory.js +485 -0
  797. package/dist/src/tui/memory.js.map +1 -0
  798. package/dist/src/tui/memtab.js +513 -0
  799. package/dist/src/tui/memtab.js.map +1 -0
  800. package/dist/src/tui/metricstab.js +979 -0
  801. package/dist/src/tui/metricstab.js.map +1 -0
  802. package/dist/src/tui/model.js +129 -0
  803. package/dist/src/tui/model.js.map +1 -0
  804. package/dist/src/tui/mouse.js +83 -0
  805. package/dist/src/tui/mouse.js.map +1 -0
  806. package/dist/src/tui/msg.js +196 -0
  807. package/dist/src/tui/msg.js.map +1 -0
  808. package/dist/src/tui/nativecompanion.js +321 -0
  809. package/dist/src/tui/nativecompanion.js.map +1 -0
  810. package/dist/src/tui/openurl.js +17 -0
  811. package/dist/src/tui/openurl.js.map +1 -0
  812. package/dist/src/tui/paint.js +501 -0
  813. package/dist/src/tui/paint.js.map +1 -0
  814. package/dist/src/tui/panes.js +74 -0
  815. package/dist/src/tui/panes.js.map +1 -0
  816. package/dist/src/tui/pipane.js +166 -0
  817. package/dist/src/tui/pipane.js.map +1 -0
  818. package/dist/src/tui/poll-hooks.js +11 -0
  819. package/dist/src/tui/poll-hooks.js.map +1 -0
  820. package/dist/src/tui/poll.js +113 -0
  821. package/dist/src/tui/poll.js.map +1 -0
  822. package/dist/src/tui/proposals.js +715 -0
  823. package/dist/src/tui/proposals.js.map +1 -0
  824. package/dist/src/tui/prpause.js +139 -0
  825. package/dist/src/tui/prpause.js.map +1 -0
  826. package/dist/src/tui/reload.js +351 -0
  827. package/dist/src/tui/reload.js.map +1 -0
  828. package/dist/src/tui/remote.js +318 -0
  829. package/dist/src/tui/remote.js.map +1 -0
  830. package/dist/src/tui/rollover.js +767 -0
  831. package/dist/src/tui/rollover.js.map +1 -0
  832. package/dist/src/tui/route.js +143 -0
  833. package/dist/src/tui/route.js.map +1 -0
  834. package/dist/src/tui/scantab.js +1053 -0
  835. package/dist/src/tui/scantab.js.map +1 -0
  836. package/dist/src/tui/setupentry.js +81 -0
  837. package/dist/src/tui/setupentry.js.map +1 -0
  838. package/dist/src/tui/setupstate.js +29 -0
  839. package/dist/src/tui/setupstate.js.map +1 -0
  840. package/dist/src/tui/sidebar.js +602 -0
  841. package/dist/src/tui/sidebar.js.map +1 -0
  842. package/dist/src/tui/sshtriage.js +490 -0
  843. package/dist/src/tui/sshtriage.js.map +1 -0
  844. package/dist/src/tui/startup.js +172 -0
  845. package/dist/src/tui/startup.js.map +1 -0
  846. package/dist/src/tui/tabs.js +41 -0
  847. package/dist/src/tui/tabs.js.map +1 -0
  848. package/dist/src/tui/termmirror.js +217 -0
  849. package/dist/src/tui/termmirror.js.map +1 -0
  850. package/dist/src/tui/theme.js +96 -0
  851. package/dist/src/tui/theme.js.map +1 -0
  852. package/dist/src/tui/ticketstab.js +224 -0
  853. package/dist/src/tui/ticketstab.js.map +1 -0
  854. package/dist/src/tui/tmuxsocket.js +101 -0
  855. package/dist/src/tui/tmuxsocket.js.map +1 -0
  856. package/dist/src/tui/trackertickets.js +77 -0
  857. package/dist/src/tui/trackertickets.js.map +1 -0
  858. package/dist/src/tui/unwedge.js +235 -0
  859. package/dist/src/tui/unwedge.js.map +1 -0
  860. package/dist/src/tui/viewer.js +479 -0
  861. package/dist/src/tui/viewer.js.map +1 -0
  862. package/dist/src/tui/viewerinput.js +125 -0
  863. package/dist/src/tui/viewerinput.js.map +1 -0
  864. package/dist/src/updatenotice.js +143 -0
  865. package/dist/src/updatenotice.js.map +1 -0
  866. package/dist/src/usage.js +1007 -0
  867. package/dist/src/usage.js.map +1 -0
  868. package/dist/src/verdicts.js +3860 -0
  869. package/dist/src/verdicts.js.map +1 -0
  870. package/dist/src/version.js +58 -0
  871. package/dist/src/version.js.map +1 -0
  872. package/dist/src/wakes.js +411 -0
  873. package/dist/src/wakes.js.map +1 -0
  874. package/dist/src/webhook.js +736 -0
  875. package/dist/src/webhook.js.map +1 -0
  876. package/dist/src/webhookheal.js +238 -0
  877. package/dist/src/webhookheal.js.map +1 -0
  878. package/dist/src/wire.js +1823 -0
  879. package/dist/src/wire.js.map +1 -0
  880. package/dist/src/worker-session-host.js +531 -0
  881. package/dist/src/worker-session-host.js.map +1 -0
  882. package/dist/src/workerbreaker.js +112 -0
  883. package/dist/src/workerbreaker.js.map +1 -0
  884. package/dist/src/workercontrol.js +49 -0
  885. package/dist/src/workercontrol.js.map +1 -0
  886. package/dist/src/workerlease.js +351 -0
  887. package/dist/src/workerlease.js.map +1 -0
  888. package/dist/src/workerliveness.js +84 -0
  889. package/dist/src/workerliveness.js.map +1 -0
  890. package/dist/src/workerlog.js +65 -0
  891. package/dist/src/workerlog.js.map +1 -0
  892. package/dist/src/workerruntime.js +10 -0
  893. package/dist/src/workerruntime.js.map +1 -0
  894. package/dist/src/workersupervisor.js +1206 -0
  895. package/dist/src/workersupervisor.js.map +1 -0
  896. package/dist/src/workerusage.js +23 -0
  897. package/dist/src/workerusage.js.map +1 -0
  898. package/dist/src/workerworld.js +338 -0
  899. package/dist/src/workerworld.js.map +1 -0
  900. package/dist/src/worktrees.js +984 -0
  901. package/dist/src/worktrees.js.map +1 -0
  902. package/docker/Dockerfile +284 -0
  903. package/docker/Dockerfile.dockerignore +35 -0
  904. package/docker/README.md +221 -0
  905. package/docker/companion/Dockerfile +139 -0
  906. package/docker/companion/agents/evidence.md +34 -0
  907. package/docker/companion/agents/explore.md +26 -0
  908. package/docker/companion/agents/pass-survey.md +49 -0
  909. package/docker/companion/agents/study.md +26 -0
  910. package/docker/companion/entrypoint.sh +362 -0
  911. package/docker/companion/memory-health.mjs +269 -0
  912. package/docker/entrypoint.sh +799 -0
  913. package/docker/memory/Dockerfile +191 -0
  914. package/docker/memory/Dockerfile.dockerignore +20 -0
  915. package/docker/memory/README.md +60 -0
  916. package/docker/memory/entrypoint.sh +163 -0
  917. package/docker/setid/qcr-setid.c +109 -0
  918. package/docker/tunnel/Dockerfile.cloudflared +51 -0
  919. package/docker/tunnel/Dockerfile.ngrok +40 -0
  920. package/docker/tunnel/sidecar.mjs +411 -0
  921. package/docker/vm/tmux.conf +26 -0
  922. package/docker-compose.yml +348 -0
  923. package/extensions/cli/bash-guard-ambient.ts +71 -0
  924. package/extensions/cli/bash-guard.ts +12 -0
  925. package/extensions/cli/companion-guard.ts +8 -0
  926. package/extensions/cli/debug-round.ts +9 -0
  927. package/extensions/cli/inject-bridge.ts +8 -0
  928. package/extensions/cli/ledger-pin.ts +13 -0
  929. package/extensions/cli/lens.ts +22 -0
  930. package/extensions/cli/login-route.ts +10 -0
  931. package/extensions/cli/muted-theme.ts +9 -0
  932. package/extensions/cli/onboarding.ts +8 -0
  933. package/extensions/cli/setup.ts +8 -0
  934. package/extensions/cli/tui-view.ts +8 -0
  935. package/extensions/companion-guard.ts +411 -0
  936. package/extensions/debug-round.ts +281 -0
  937. package/extensions/inject-bridge.ts +630 -0
  938. package/extensions/inline-bash.ts +102 -0
  939. package/extensions/login-route.ts +220 -0
  940. package/extensions/muted-theme.json +68 -0
  941. package/extensions/muted-theme.ts +23 -0
  942. package/extensions/onboarding.ts +262 -0
  943. package/extensions/setup-replay.ts +336 -0
  944. package/extensions/setup-scan.ts +1236 -0
  945. package/extensions/setup-triage.ts +190 -0
  946. package/extensions/setup.ts +1428 -0
  947. package/extensions/tui-view.ts +489 -0
  948. package/extensions/vendor/openviking-pi/LICENSE +201 -0
  949. package/extensions/vendor/openviking-pi/NOTICE +68 -0
  950. package/extensions/vendor/openviking-pi/README.md +550 -0
  951. package/extensions/vendor/openviking-pi/client.ts +588 -0
  952. package/extensions/vendor/openviking-pi/config.json +28 -0
  953. package/extensions/vendor/openviking-pi/config.ts +271 -0
  954. package/extensions/vendor/openviking-pi/index.ts +523 -0
  955. package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
  956. package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
  957. package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
  958. package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
  959. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
  960. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
  961. package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
  962. package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
  963. package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
  964. package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
  965. package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
  966. package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
  967. package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
  968. package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
  969. package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
  970. package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
  971. package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
  972. package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
  973. package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
  974. package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
  975. package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
  976. package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
  977. package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
  978. package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
  979. package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
  980. package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
  981. package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
  982. package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
  983. package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
  984. package/extensions/vendor/openviking-pi/recall.ts +149 -0
  985. package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
  986. package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
  987. package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
  988. package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
  989. package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
  990. package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
  991. package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
  992. package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
  993. package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
  994. package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
  995. package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
  996. package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
  997. package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
  998. package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
  999. package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
  1000. package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
  1001. package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  1002. package/extensions/vendor/openviking-pi/sync.ts +163 -0
  1003. package/extensions/vendor/openviking-pi/takeover.ts +36 -0
  1004. package/extensions/vendor/openviking-pi/tools.ts +479 -0
  1005. package/extensions/vendor/openviking-pi/upstream.json +48 -0
  1006. package/package.json +138 -4
  1007. package/profiles/default/README.md +21 -0
  1008. package/profiles/default/addenda/.gitkeep +0 -0
  1009. package/profiles/default/profile.toml +17 -0
  1010. package/profiles/example-django/README.md +21 -0
  1011. package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  1012. package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  1013. package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  1014. package/profiles/example-django/addenda/40-orm-usage.md +19 -0
  1015. package/profiles/example-django/addenda/50-declared-shape.md +10 -0
  1016. package/profiles/example-django/addenda/60-config-placement.md +14 -0
  1017. package/profiles/example-django/heuristics.toml +24 -0
  1018. package/profiles/example-django/profile.toml +9 -0
  1019. package/profiles/fast/README.md +24 -0
  1020. package/profiles/fast/profile.toml +59 -0
  1021. package/profiles/minimal/README.md +17 -0
  1022. package/profiles/minimal/addenda/.gitkeep +0 -0
  1023. package/profiles/minimal/profile.toml +8 -0
  1024. package/profiles/snappy/README.md +24 -0
  1025. package/profiles/snappy/addenda/10-race-as-question.md +6 -0
  1026. package/profiles/snappy/profile.toml +46 -0
  1027. package/prompts/companion-day-open.md +46 -0
  1028. package/prompts/companion-first-run.md +104 -0
  1029. package/prompts/companion.md +821 -0
  1030. package/prompts/conversation-system.md +117 -0
  1031. package/prompts/eod-sweep.md +120 -0
  1032. package/prompts/onboarding.md +113 -0
  1033. package/prompts/pass-common.md +52 -0
  1034. package/prompts/pass-quality-common.md +22 -0
  1035. package/prompts/round-first.md +874 -0
  1036. package/prompts/round-follow-up.md +991 -0
  1037. package/prompts/round-re-review.md +889 -0
  1038. package/prompts/round-recheck.md +196 -0
  1039. package/prompts/round-staleness.md +856 -0
  1040. package/schemas/review-result.v1.json +391 -0
  1041. package/schemas/scan-result.v1.json +421 -0
  1042. package/schemas/version.v1.json +57 -0
  1043. package/scripts/approve-pr.sh +16 -0
  1044. package/scripts/build-stamp.mjs +67 -0
  1045. package/scripts/commit-verdict.sh +155 -0
  1046. package/scripts/companion-e2e.sh +250 -0
  1047. package/scripts/companion-memory-forward.mjs +1966 -0
  1048. package/scripts/config-fingerprint.sh +28 -0
  1049. package/scripts/deploy-manifest.mjs +141 -0
  1050. package/scripts/deploy.sh +1435 -0
  1051. package/scripts/directory-lock.mjs +193 -0
  1052. package/scripts/heartbeat-fresh.mjs +54 -0
  1053. package/scripts/install-locator.sh +328 -0
  1054. package/scripts/install-wrapper.sh +76 -0
  1055. package/scripts/memory-search-check.mjs +98 -0
  1056. package/scripts/mint-token.sh +233 -0
  1057. package/scripts/openviking-embedder.py +392 -0
  1058. package/scripts/openviking-ov-conf.mjs +161 -0
  1059. package/scripts/openviking-provision.mjs +1122 -0
  1060. package/scripts/openviking-reembed.mjs +1154 -0
  1061. package/scripts/post-review.sh +16 -0
  1062. package/scripts/pr-checks.sh +256 -0
  1063. package/scripts/pr-context.py +174 -0
  1064. package/scripts/pr-context.sh +63 -0
  1065. package/scripts/pr-self.sh +144 -0
  1066. package/scripts/pr-threads.sh +145 -0
  1067. package/scripts/qcr +15 -0
  1068. package/scripts/qcr-host.sh +769 -0
  1069. package/scripts/render-review.sh +24 -0
  1070. package/scripts/reply-thread.sh +16 -0
  1071. package/scripts/required-passes.py +112 -0
  1072. package/scripts/smoke-companion.sh +211 -0
  1073. package/scripts/tui-deploy-runner.mjs +61 -0
  1074. package/scripts/verify-dist.mjs +201 -0
  1075. package/scripts/vm-ghcr-login.sh +188 -0
  1076. package/seeds/agents/pass-baseline.md +61 -0
  1077. package/seeds/agents/pass-contracts.md +30 -0
  1078. package/seeds/agents/pass-data-safety.md +54 -0
  1079. package/seeds/agents/pass-delivery.md +37 -0
  1080. package/seeds/agents/pass-prod-risk.md +24 -0
  1081. package/seeds/agents/pass-security.md +31 -0
  1082. package/seeds/agents/pass-statelessness.md +27 -0
  1083. package/seeds/agents/pass-ui.md +23 -0
  1084. package/seeds/agents/pass-validate.md +50 -0
  1085. package/seeds/prompts/conversation-system.md +117 -0
  1086. package/seeds/prompts/eod-sweep.md +120 -0
  1087. package/seeds/prompts/pass-common.md +52 -0
  1088. package/skills/companion/adjudicate/SKILL.md +247 -0
  1089. package/skills/companion/evidence/SKILL.md +150 -0
  1090. package/skills/companion/land/SKILL.md +200 -0
  1091. package/skills/companion/replay/SKILL.md +138 -0
  1092. package/skills/companion/setup/SKILL.md +167 -0
  1093. package/skills/companion/survey/SKILL.md +160 -0
  1094. package/skills/selfimprove/README.md +34 -0
  1095. package/skills/selfimprove/SKILL.md +127 -0
  1096. package/skills/selfimprove/agents/openai.yaml +2 -0
  1097. package/skills/selfimprove/commands/selfimprove.md +6 -0
  1098. package/skills/selfimprove/references/fix-loop.md +49 -0
  1099. package/skills/selfimprove/references/ledger.md +20 -0
  1100. package/skills/selfimprove/references/setup.md +40 -0
  1101. package/src/anomalies.ts +566 -0
  1102. package/src/answerworker.ts +251 -0
  1103. package/src/appstamp.ts +65 -0
  1104. package/src/archive/admission.ts +3 -0
  1105. package/src/archive/archivedb.ts +548 -0
  1106. package/src/archive/archiver.ts +1963 -0
  1107. package/src/archive/archivewrite.ts +279 -0
  1108. package/src/archive/bundle.ts +333 -0
  1109. package/src/archive/dbmove.ts +911 -0
  1110. package/src/archive/duplicatecounts.ts +98 -0
  1111. package/src/archive/eligibility.ts +181 -0
  1112. package/src/archive/extract.ts +342 -0
  1113. package/src/archive/fsutil.ts +415 -0
  1114. package/src/archive/gzfile.ts +370 -0
  1115. package/src/archive/kinds.ts +21 -0
  1116. package/src/archive/livecheckpoint.ts +217 -0
  1117. package/src/archive/liveretention.ts +180 -0
  1118. package/src/archive/manifest.ts +519 -0
  1119. package/src/archive/overseer.ts +336 -0
  1120. package/src/archive/piglobal.ts +174 -0
  1121. package/src/archive/plan.ts +612 -0
  1122. package/src/archive/prarchive.ts +984 -0
  1123. package/src/archive/prfiles.ts +406 -0
  1124. package/src/archive/report.ts +252 -0
  1125. package/src/archive/restore.ts +330 -0
  1126. package/src/archive/rows.ts +187 -0
  1127. package/src/archive/seal.ts +630 -0
  1128. package/src/archive/status.ts +719 -0
  1129. package/src/archive/storepass.ts +581 -0
  1130. package/src/backfill.ts +362 -0
  1131. package/src/bench/cost.ts +113 -0
  1132. package/src/bench/exec.ts +90 -0
  1133. package/src/bench/values.ts +13 -0
  1134. package/src/botcontrol.ts +176 -0
  1135. package/src/botworker.ts +953 -0
  1136. package/src/capture.ts +321 -0
  1137. package/src/cases.ts +1789 -0
  1138. package/src/channelfs.ts +26 -0
  1139. package/src/checklist.ts +506 -0
  1140. package/src/ciretry.ts +142 -0
  1141. package/src/cli.ts +2306 -0
  1142. package/src/cliops.ts +385 -0
  1143. package/src/clonedir.ts +15 -0
  1144. package/src/commandeffects.ts +57 -0
  1145. package/src/commands.ts +1704 -0
  1146. package/src/companioninbox.ts +239 -0
  1147. package/src/compare.ts +957 -0
  1148. package/src/config.ts +2070 -0
  1149. package/src/configcheck.ts +133 -0
  1150. package/src/coreext.ts +47 -0
  1151. package/src/daemon.ts +1327 -0
  1152. package/src/debuground.ts +927 -0
  1153. package/src/debugroundpi.ts +272 -0
  1154. package/src/diagnostics.ts +14 -0
  1155. package/src/digest.ts +183 -0
  1156. package/src/dispatch.ts +2251 -0
  1157. package/src/engine/answerguard.ts +27 -0
  1158. package/src/engine/botsdk.ts +336 -0
  1159. package/src/engine/caserunner-sdk.ts +423 -0
  1160. package/src/engine/debugpi.ts +247 -0
  1161. package/src/eod.ts +669 -0
  1162. package/src/eodpinwatch.ts +556 -0
  1163. package/src/escalation.ts +1253 -0
  1164. package/src/eventloop.ts +1045 -0
  1165. package/src/execboundary.ts +645 -0
  1166. package/src/feedback.ts +1162 -0
  1167. package/src/findingscheck.ts +298 -0
  1168. package/src/fsdenied.ts +24 -0
  1169. package/src/ghread.ts +242 -0
  1170. package/src/github.ts +1539 -0
  1171. package/src/githubtoken.ts +435 -0
  1172. package/src/homes.ts +175 -0
  1173. package/src/hostroute.ts +53 -0
  1174. package/src/improvements.ts +787 -0
  1175. package/src/install.ts +1103 -0
  1176. package/src/installkind.ts +50 -0
  1177. package/src/ipc/capability.ts +333 -0
  1178. package/src/ipc/client.ts +269 -0
  1179. package/src/ipc/control.ts +90 -0
  1180. package/src/ipc/ghreadshim.ts +94 -0
  1181. package/src/ipc/launcher.ts +65 -0
  1182. package/src/ipc/permits.ts +362 -0
  1183. package/src/ipc/postshim.ts +164 -0
  1184. package/src/ipc/protocol.ts +342 -0
  1185. package/src/ipc/server.ts +371 -0
  1186. package/src/ipc/workerspec.ts +218 -0
  1187. package/src/learner/learner.ts +1655 -0
  1188. package/src/learner/miner.ts +1290 -0
  1189. package/src/learner/packet.ts +340 -0
  1190. package/src/learner/receipt.ts +168 -0
  1191. package/src/learner/status.ts +107 -0
  1192. package/src/ledgercheck.ts +157 -0
  1193. package/src/lessonledger.ts +703 -0
  1194. package/src/lessonselect.ts +888 -0
  1195. package/src/lessonshadow.ts +810 -0
  1196. package/src/levers.ts +393 -0
  1197. package/src/live/projection.ts +491 -0
  1198. package/src/live/publish.ts +92 -0
  1199. package/src/live/read.ts +86 -0
  1200. package/src/livecli.ts +336 -0
  1201. package/src/loginhint.ts +61 -0
  1202. package/src/loginrefresh/atomic.ts +76 -0
  1203. package/src/loginrefresh/lock.ts +104 -0
  1204. package/src/loginrefresh/main.ts +59 -0
  1205. package/src/loginrefresh/pirefresh.ts +60 -0
  1206. package/src/loginrefresh/refresher.ts +326 -0
  1207. package/src/loginrefresh/supervisor.ts +273 -0
  1208. package/src/loginrefresh/types.ts +35 -0
  1209. package/src/memory/context.ts +385 -0
  1210. package/src/memory/datafence.ts +9 -0
  1211. package/src/memory/delivery.ts +171 -0
  1212. package/src/memory/eventtags.ts +80 -0
  1213. package/src/memory/health.ts +953 -0
  1214. package/src/memory/import.ts +664 -0
  1215. package/src/memory/layout.ts +261 -0
  1216. package/src/memory/legacy-file.ts +104 -0
  1217. package/src/memory/lessons.ts +447 -0
  1218. package/src/memory/ovsession.ts +601 -0
  1219. package/src/memory/relayout.ts +335 -0
  1220. package/src/memory/roundactivity.ts +209 -0
  1221. package/src/memory/selection.ts +989 -0
  1222. package/src/memory/shadowstatus.ts +125 -0
  1223. package/src/memory/store.ts +275 -0
  1224. package/src/memory/textidentity.ts +8 -0
  1225. package/src/metrics.ts +1056 -0
  1226. package/src/missingconfig.ts +7 -0
  1227. package/src/npmverbs.ts +266 -0
  1228. package/src/onboard/choices.ts +255 -0
  1229. package/src/onboard/engine.ts +537 -0
  1230. package/src/onboard/launch.ts +103 -0
  1231. package/src/onboard/pipin.ts +31 -0
  1232. package/src/onboard/stepforward.ts +61 -0
  1233. package/src/onboard/steps/appslug.ts +268 -0
  1234. package/src/onboard/steps/bringup.ts +482 -0
  1235. package/src/onboard/steps/compose.ts +490 -0
  1236. package/src/onboard/steps/configdelivery.ts +58 -0
  1237. package/src/onboard/steps/configfill.ts +1440 -0
  1238. package/src/onboard/steps/deploysh.ts +105 -0
  1239. package/src/onboard/steps/embeddings.ts +550 -0
  1240. package/src/onboard/steps/escalation.ts +640 -0
  1241. package/src/onboard/steps/githubaccount.ts +716 -0
  1242. package/src/onboard/steps/githubapp.ts +1498 -0
  1243. package/src/onboard/steps/golive.ts +579 -0
  1244. package/src/onboard/steps/index.ts +82 -0
  1245. package/src/onboard/steps/ingress.ts +974 -0
  1246. package/src/onboard/steps/inventory.ts +526 -0
  1247. package/src/onboard/steps/llmauth.ts +1180 -0
  1248. package/src/onboard/steps/memory.ts +1347 -0
  1249. package/src/onboard/steps/memorykeys.ts +85 -0
  1250. package/src/onboard/steps/preflight.ts +191 -0
  1251. package/src/onboard/steps/proof.ts +1042 -0
  1252. package/src/onboard/steps/ssh.ts +377 -0
  1253. package/src/onboard/steps/topology.ts +338 -0
  1254. package/src/onboard/steps/transport.ts +680 -0
  1255. package/src/onboard/steps/tuiroute.ts +119 -0
  1256. package/src/onboard/steps/verifyup.ts +467 -0
  1257. package/src/onboard/steps/vmdeploy.ts +442 -0
  1258. package/src/onboard/toml.ts +50 -0
  1259. package/src/onboard/toolnames.ts +34 -0
  1260. package/src/opencli.ts +591 -0
  1261. package/src/openpr.ts +568 -0
  1262. package/src/openviking/client.ts +1131 -0
  1263. package/src/openviking/context.ts +263 -0
  1264. package/src/openviking/embedder.ts +516 -0
  1265. package/src/openviking/gapsink.ts +63 -0
  1266. package/src/openviking/health.ts +83 -0
  1267. package/src/openviking/index.ts +119 -0
  1268. package/src/openviking/lessonfind.ts +92 -0
  1269. package/src/openviking/probe.ts +129 -0
  1270. package/src/openviking/proc.ts +110 -0
  1271. package/src/openviking/provisionreport.ts +160 -0
  1272. package/src/openviking/reembed.ts +412 -0
  1273. package/src/openviking/relayout.ts +177 -0
  1274. package/src/openviking/sidecar-main.ts +38 -0
  1275. package/src/openviking/sidecar.ts +106 -0
  1276. package/src/openviking/store.ts +2025 -0
  1277. package/src/openviking/supervisor.ts +474 -0
  1278. package/src/openviking/types.ts +174 -0
  1279. package/src/operator-recovery.ts +237 -0
  1280. package/src/operatormessage.ts +52 -0
  1281. package/src/operatorwrite.ts +25 -0
  1282. package/src/outcomes.ts +1677 -0
  1283. package/src/piauthcopy.ts +12 -0
  1284. package/src/picli.ts +49 -0
  1285. package/src/picwd.ts +25 -0
  1286. package/src/pilogin.ts +37 -0
  1287. package/src/pingpong.ts +1339 -0
  1288. package/src/posting-journal.ts +554 -0
  1289. package/src/posting-outbox.ts +810 -0
  1290. package/src/posting-receipt.ts +341 -0
  1291. package/src/posting.ts +603 -0
  1292. package/src/postops.ts +832 -0
  1293. package/src/pr-activity.ts +20 -0
  1294. package/src/pr-author.ts +6 -0
  1295. package/src/pr-conversation.ts +432 -0
  1296. package/src/pr-session-lifecycle.ts +631 -0
  1297. package/src/preflight.ts +391 -0
  1298. package/src/priorresolve.ts +343 -0
  1299. package/src/qcrbin.ts +186 -0
  1300. package/src/recheck.ts +1776 -0
  1301. package/src/reconcile.ts +1056 -0
  1302. package/src/recordedfindings.ts +27 -0
  1303. package/src/regression.ts +833 -0
  1304. package/src/replay.ts +816 -0
  1305. package/src/requiredpasses.ts +148 -0
  1306. package/src/resumecheck.ts +246 -0
  1307. package/src/reviewcli.ts +787 -0
  1308. package/src/reviewcontext.ts +35 -0
  1309. package/src/reviewdetach.ts +734 -0
  1310. package/src/reviewhistory.ts +441 -0
  1311. package/src/reviewhome.ts +36 -0
  1312. package/src/reviewmemory.ts +133 -0
  1313. package/src/reviewnow.ts +76 -0
  1314. package/src/reviewrendercli.ts +139 -0
  1315. package/src/round-work.ts +42 -0
  1316. package/src/runhost.ts +108 -0
  1317. package/src/scanauth.ts +36 -0
  1318. package/src/scanbox.ts +1081 -0
  1319. package/src/scancli.ts +1296 -0
  1320. package/src/scandriverlog.ts +32 -0
  1321. package/src/session-bridge.ts +139 -0
  1322. package/src/session-command-payload.ts +35 -0
  1323. package/src/session-command-supervisor.ts +1142 -0
  1324. package/src/sessionjsonl.ts +88 -0
  1325. package/src/sessions.ts +659 -0
  1326. package/src/setup/adapters.ts +335 -0
  1327. package/src/setup/baselines.ts +110 -0
  1328. package/src/setup/caserunner.ts +611 -0
  1329. package/src/setup/configcommit.ts +443 -0
  1330. package/src/setup/engine.ts +1001 -0
  1331. package/src/setup/facts.ts +627 -0
  1332. package/src/setup/findings.ts +1264 -0
  1333. package/src/setup/history.ts +443 -0
  1334. package/src/setup/interview.ts +243 -0
  1335. package/src/setup/ledger.ts +324 -0
  1336. package/src/setup/pins.ts +66 -0
  1337. package/src/setup/portrait.ts +635 -0
  1338. package/src/setup/presentation.ts +18 -0
  1339. package/src/setup/profile.ts +508 -0
  1340. package/src/setup/replay.ts +370 -0
  1341. package/src/setup/replaybuild.ts +392 -0
  1342. package/src/setup/replayrun.ts +1096 -0
  1343. package/src/setup/replaysource.ts +198 -0
  1344. package/src/setup/retune.ts +415 -0
  1345. package/src/setup/retunelog.ts +95 -0
  1346. package/src/setup/scan.ts +1033 -0
  1347. package/src/setup/scanbuild.ts +168 -0
  1348. package/src/setup/scanfacts.ts +107 -0
  1349. package/src/setup/scanlease.ts +297 -0
  1350. package/src/setup/scanreport.ts +373 -0
  1351. package/src/setup/scanrun.ts +2268 -0
  1352. package/src/setup/scantree.ts +259 -0
  1353. package/src/setup/stage2.ts +926 -0
  1354. package/src/setup/stage2request.ts +347 -0
  1355. package/src/setup/survey.ts +458 -0
  1356. package/src/setup/surveyrun.ts +758 -0
  1357. package/src/setup/textsniff.ts +95 -0
  1358. package/src/setup/trackersink.ts +433 -0
  1359. package/src/setup/triage.ts +768 -0
  1360. package/src/setup/tuning.ts +766 -0
  1361. package/src/setup/unitcut.ts +523 -0
  1362. package/src/singleflight.ts +86 -0
  1363. package/src/slack/announce.ts +911 -0
  1364. package/src/slack/api.ts +303 -0
  1365. package/src/slack/messages.ts +288 -0
  1366. package/src/slack/origin.ts +599 -0
  1367. package/src/slack/policy.ts +354 -0
  1368. package/src/slack/reply.ts +201 -0
  1369. package/src/slack/router.ts +285 -0
  1370. package/src/slack/socket.ts +384 -0
  1371. package/src/slack/types.ts +108 -0
  1372. package/src/sqlitehint.ts +32 -0
  1373. package/src/ssh-observation.ts +30 -0
  1374. package/src/state.ts +8261 -0
  1375. package/src/statemachine.ts +372 -0
  1376. package/src/stubs.ts +68 -0
  1377. package/src/takeover.ts +634 -0
  1378. package/src/takeoverpi.ts +306 -0
  1379. package/src/terminal-failure.ts +41 -0
  1380. package/src/thread-resolution-refusal.ts +2 -0
  1381. package/src/tmux.ts +153 -0
  1382. package/src/trackerid.ts +41 -0
  1383. package/src/transcriptindex.ts +665 -0
  1384. package/src/tui/actions.ts +484 -0
  1385. package/src/tui/alerts.ts +666 -0
  1386. package/src/tui/app.ts +2026 -0
  1387. package/src/tui/archiveops.ts +192 -0
  1388. package/src/tui/autofile.ts +233 -0
  1389. package/src/tui/board.ts +1529 -0
  1390. package/src/tui/candidates.ts +889 -0
  1391. package/src/tui/cellwidth.ts +148 -0
  1392. package/src/tui/changelog-store.ts +503 -0
  1393. package/src/tui/chord.ts +25 -0
  1394. package/src/tui/clipboard.ts +92 -0
  1395. package/src/tui/cmdbar.ts +299 -0
  1396. package/src/tui/companion.ts +1495 -0
  1397. package/src/tui/companionplan.ts +247 -0
  1398. package/src/tui/companiontab.ts +83 -0
  1399. package/src/tui/confighome.ts +155 -0
  1400. package/src/tui/connect.ts +74 -0
  1401. package/src/tui/deploy-ceremony.ts +104 -0
  1402. package/src/tui/deploy-drain.ts +129 -0
  1403. package/src/tui/deploy-process.ts +265 -0
  1404. package/src/tui/deploytab.ts +1523 -0
  1405. package/src/tui/design.ts +125 -0
  1406. package/src/tui/digesttab.ts +214 -0
  1407. package/src/tui/emptystate.ts +39 -0
  1408. package/src/tui/evidence.ts +999 -0
  1409. package/src/tui/feed.ts +604 -0
  1410. package/src/tui/feedbacktab.ts +1077 -0
  1411. package/src/tui/filters.ts +94 -0
  1412. package/src/tui/ghread.ts +388 -0
  1413. package/src/tui/historypull.ts +81 -0
  1414. package/src/tui/inboxpull.ts +236 -0
  1415. package/src/tui/index.ts +1656 -0
  1416. package/src/tui/issuesink.ts +238 -0
  1417. package/src/tui/jsonfile.ts +42 -0
  1418. package/src/tui/keys.ts +109 -0
  1419. package/src/tui/layout.ts +108 -0
  1420. package/src/tui/learning.ts +627 -0
  1421. package/src/tui/lessonswitch.ts +118 -0
  1422. package/src/tui/livecontrol.ts +228 -0
  1423. package/src/tui/memforward.ts +492 -0
  1424. package/src/tui/memincident.ts +363 -0
  1425. package/src/tui/memory.ts +620 -0
  1426. package/src/tui/memtab.ts +606 -0
  1427. package/src/tui/metricstab.ts +1177 -0
  1428. package/src/tui/model.ts +165 -0
  1429. package/src/tui/mouse.ts +98 -0
  1430. package/src/tui/msg.ts +268 -0
  1431. package/src/tui/nativecompanion.ts +417 -0
  1432. package/src/tui/openurl.ts +21 -0
  1433. package/src/tui/paint.ts +511 -0
  1434. package/src/tui/panes.ts +98 -0
  1435. package/src/tui/pipane.ts +219 -0
  1436. package/src/tui/poll-hooks.ts +12 -0
  1437. package/src/tui/poll.ts +147 -0
  1438. package/src/tui/proposals.ts +838 -0
  1439. package/src/tui/prpause.ts +159 -0
  1440. package/src/tui/reload.ts +436 -0
  1441. package/src/tui/remote.ts +421 -0
  1442. package/src/tui/rollover.ts +969 -0
  1443. package/src/tui/route.ts +173 -0
  1444. package/src/tui/scantab.ts +1290 -0
  1445. package/src/tui/setupentry.ts +93 -0
  1446. package/src/tui/setupstate.ts +52 -0
  1447. package/src/tui/sidebar.ts +749 -0
  1448. package/src/tui/sshtriage.ts +631 -0
  1449. package/src/tui/startup.ts +231 -0
  1450. package/src/tui/tabs.ts +45 -0
  1451. package/src/tui/termmirror.ts +224 -0
  1452. package/src/tui/theme.ts +96 -0
  1453. package/src/tui/ticketstab.ts +310 -0
  1454. package/src/tui/tmuxsocket.ts +129 -0
  1455. package/src/tui/trackertickets.ts +84 -0
  1456. package/src/tui/unwedge.ts +274 -0
  1457. package/src/tui/viewer.ts +575 -0
  1458. package/src/tui/viewerinput.ts +173 -0
  1459. package/src/updatenotice.ts +178 -0
  1460. package/src/usage.ts +1154 -0
  1461. package/src/verdicts.ts +4707 -0
  1462. package/src/version.ts +80 -0
  1463. package/src/wakes.ts +476 -0
  1464. package/src/webhook.ts +951 -0
  1465. package/src/webhookheal.ts +276 -0
  1466. package/src/wire.ts +2182 -0
  1467. package/src/worker-session-host.ts +602 -0
  1468. package/src/workerbreaker.ts +156 -0
  1469. package/src/workercontrol.ts +104 -0
  1470. package/src/workerlease.ts +441 -0
  1471. package/src/workerliveness.ts +103 -0
  1472. package/src/workerlog.ts +79 -0
  1473. package/src/workerruntime.ts +30 -0
  1474. package/src/workersupervisor.ts +1538 -0
  1475. package/src/workerusage.ts +42 -0
  1476. package/src/workerworld.ts +433 -0
  1477. package/src/worktrees.ts +1138 -0
  1478. package/tests/fixtures/regression-cases.json +795 -0
  1479. package/tests/fixtures/seed-manifest.json +19 -0
@@ -0,0 +1,1399 @@
1
+ // Post-approval delta recheck.
2
+ //
3
+ // Once a PR is approved, a later change can still regress it. A staleness
4
+ // check that re-reviews on every commit after the approval spends most of its
5
+ // output concluding nothing changed, so the delta is classified first, and
6
+ // the standing verdict is UPHELD BY DEFAULT.
7
+ //
8
+ // Shape of the mechanism:
9
+ //
10
+ // trigger ──► planRecheck() ──► no-op (uphold, nothing posted, nothing spent)
11
+ // └─► one nudge to the PR's OWN sticky session
12
+ //
13
+ // Four triggers: a push after an approve, CI completing at the head, the base
14
+ // branch advancing, and an author reply. The author reply is deliberately a
15
+ // no-op HERE: `src/pingpong.ts` already owns that path (≤3 exchanges, reply +
16
+ // optional thread resolve) and a second reply handler would duplicate it.
17
+ // With no code delta there is nothing to recheck; the value is the reply,
18
+ // which is already owned.
19
+ //
20
+ // Invariants this module keeps structurally, not by prompting:
21
+ //
22
+ // 1. **Only APPROVE → REQUEST_CHANGES.** A recheck is planned only when the
23
+ // standing verdict is APPROVE, and the decision file accepts exactly two
24
+ // outcomes: `uphold` and `request_changes`. Anything else — including a
25
+ // literal "APPROVE" or "NEEDS_HUMAN" — is coerced to `uphold` and logged.
26
+ // The path can therefore never re-approve and never park.
27
+ // 2. **A trivial or empty delta costs one classification and a no-op.** That
28
+ // is what makes the broad trigger set affordable. A base advance touching
29
+ // no PR-owned file is the loudest instance: every file it brings is a
30
+ // `base-merge` file, so it resolves to `trivial-delta`.
31
+ // 3. **An unchanged head with no new evidence is a no-op** — the same rule
32
+ // the gate's guard applies (`guards.hasNewEvidence`), read here off the
33
+ // same single `reviewed_heads` mark. There is no second copy.
34
+ // 4. **Single shepherd.** The recheck is a nudge to the PR's existing `pr-<n>`
35
+ // session — no new session, no lens fan-out, no round. It goes through
36
+ // `wakeOrHold`, so a nudge arriving mid-round is held and flushed at round
37
+ // end like any other wake.
38
+ // 5. **No GitHub write lives here.** The module reads (compare, files) and
39
+ // writes nothing; the session posts its review through the gated scripts.
40
+ // The MutationGate keeps its monopoly by this module having no mutation.
41
+ import { botAccountLogin } from "@quickchatai/pi-quickreview/src/identity.js";
42
+ import { existsSync, mkdirSync, readdirSync, renameSync, watch } from "node:fs";
43
+ import { join } from "node:path";
44
+ import { readFindingsRecord } from "@quickchatai/pi-quickreview/src/findingsrecord.js";
45
+ import { isMigrationPath } from "@quickchatai/pi-quickreview/src/heuristics.js";
46
+ import { parseHunkHeader } from "@quickchatai/pi-quickreview/src/hunkheader.js";
47
+ import { applyEvent } from "./statemachine.js";
48
+ import { recheckHead12, recheckKeyPrefix } from "@quickchatai/pi-quickreview/src/findingkey.js";
49
+ import { recheckWakeReason } from "./state.js";
50
+ import { normalizeVerdict, recheckPromptMarker, stampPrompt, } from "@quickchatai/pi-quickreview/src/types.js";
51
+ import { standingVerdict } from "@quickchatai/pi-quickreview/src/standing-review.js";
52
+ import { wakeOrHold, withRepoFacts } from "./wakes.js";
53
+ import { prWorktreeRel } from "./worktrees.js";
54
+ import { readSettledJson, resolvePartialWritePolicy, } from "@quickchatai/pi-quickreview/src/settledread.js";
55
+ /** The delta of a range GitHub could not list: no files, and significant by
56
+ * construction — nothing is known about what it holds. */
57
+ export function unreadDelta() {
58
+ return Object.assign([], { unread: true });
59
+ }
60
+ /** Dependency-manifest lockfiles: generated, never reviewed on their content. */
61
+ const LOCKFILES = new Set([
62
+ "package-lock.json",
63
+ "npm-shrinkwrap.json",
64
+ "yarn.lock",
65
+ "pnpm-lock.yaml",
66
+ "poetry.lock",
67
+ "uv.lock",
68
+ "Pipfile.lock",
69
+ "Cargo.lock",
70
+ "Gemfile.lock",
71
+ "composer.lock",
72
+ "go.sum",
73
+ ]);
74
+ /** Manifests whose version field moves on a release bump. */
75
+ const VERSION_MANIFESTS = new Set([
76
+ "package.json",
77
+ "pyproject.toml",
78
+ "Cargo.toml",
79
+ "setup.cfg",
80
+ "VERSION",
81
+ "version.txt",
82
+ ]);
83
+ function baseName(path) {
84
+ return path.split("/").pop() ?? path;
85
+ }
86
+ /**
87
+ * Split a migration filename into its number and the rest: `0042_add_foo.py`
88
+ * → `{ num: "0042", rest: "add_foo.py" }`. Null when the file is not a
89
+ * numbered migration.
90
+ */
91
+ export function migrationName(path) {
92
+ const m = /^(\d+)[_-](.+)$/.exec(baseName(path));
93
+ if (!m || !isMigrationPath(path))
94
+ return null;
95
+ return { num: m[1], rest: m[2] };
96
+ }
97
+ /** A rename that only moves a migration's number: `0042_x.py` → `0043_x.py`. */
98
+ export function isMigrationRenumber(from, to) {
99
+ const a = migrationName(from);
100
+ const b = migrationName(to);
101
+ return a !== null && b !== null && a.rest === b.rest && a.num !== b.num;
102
+ }
103
+ /**
104
+ * The hunks of a unified diff. File headers (`---`/`+++`) are recognised only
105
+ * before the first hunk — inside one, `--- x` is a removed line that reads
106
+ * `-- x`. A patch with no `@@` line is one hunk.
107
+ */
108
+ function parseHunks(patch) {
109
+ const hunks = [];
110
+ let cur = null;
111
+ for (const line of patch.split("\n")) {
112
+ if (line.startsWith("@@")) {
113
+ cur = {
114
+ section: /^@@[^@]*@@ ?(.*)$/.exec(line)?.[1] ?? "",
115
+ oldStart: parseHunkHeader(line)?.old_start ?? null,
116
+ lines: [],
117
+ };
118
+ hunks.push(cur);
119
+ continue;
120
+ }
121
+ if (cur === null && (line.startsWith("+++") || line.startsWith("---")))
122
+ continue;
123
+ const kind = line.charAt(0);
124
+ if (kind !== " " && kind !== "+" && kind !== "-")
125
+ continue;
126
+ if (cur === null) {
127
+ cur = { section: "", oldStart: null, lines: [] };
128
+ hunks.push(cur);
129
+ }
130
+ cur.lines.push({ kind, text: line.slice(1) });
131
+ }
132
+ return hunks;
133
+ }
134
+ function changeBlocks(hunk) {
135
+ const blocks = [];
136
+ let cur = null;
137
+ for (const line of hunk.lines) {
138
+ if (line.kind === " ") {
139
+ cur = null;
140
+ continue;
141
+ }
142
+ if (cur === null) {
143
+ cur = { added: [], removed: [] };
144
+ blocks.push(cur);
145
+ }
146
+ (line.kind === "+" ? cur.added : cur.removed).push(line.text);
147
+ }
148
+ return blocks;
149
+ }
150
+ /** Every line of one kind in a patch, prefix kept — a whole added or removed
151
+ * file's text. */
152
+ function changedLines(patch, kind) {
153
+ return parseHunks(patch).flatMap((h) => h.lines.filter((l) => l.kind === kind).map((l) => `${kind}${l.text}`));
154
+ }
155
+ /** A quoted literal on one line. Whitespace inside one is content, never
156
+ * formatting: `"a b"` → `"ab"` changes the program. */
157
+ const QUOTED = /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)/;
158
+ /** Drop whitespace OUTSIDE quoted literals; the literals stay verbatim. */
159
+ function stripOutsideQuotes(line) {
160
+ return line
161
+ .split(QUOTED)
162
+ .map((part, i) => (i % 2 === 1 ? part : part.replace(/\s+/g, "")))
163
+ .join("");
164
+ }
165
+ /** Languages whose leading whitespace is syntax — a dedent in Python moves a
166
+ * line out of its block; in YAML it moves a key to another mapping; in the
167
+ * indented style-sheet syntaxes (Sass, Stylus) it moves a rule out of the
168
+ * selector it was nested under. */
169
+ function indentSignificant(path) {
170
+ if (!path)
171
+ return false;
172
+ const base = baseName(path);
173
+ return /\.(py|pyi|ya?ml|sass|styl)$/i.test(base) || base === "Makefile" || /\.mk$/i.test(base);
174
+ }
175
+ /** A shell script's basename (a Dockerfile counts). */
176
+ function isShellFile(base) {
177
+ return /\.(sh|bash|zsh|ksh)$/i.test(base) || base === "Dockerfile";
178
+ }
179
+ /** A Ruby file's basename (a Gemfile counts). */
180
+ function isRubyFile(base) {
181
+ return /\.rb$/i.test(base) || base === "Gemfile";
182
+ }
183
+ /** Languages where a line break ends a command or statement — in a shell
184
+ * script `a\n-b` runs two commands where `a -b` runs one; Ruby and Go end a
185
+ * statement at most line breaks. */
186
+ function lineTerminated(path) {
187
+ if (!path)
188
+ return false;
189
+ const base = baseName(path);
190
+ return isShellFile(base) || isRubyFile(base) || /\.go$/i.test(base);
191
+ }
192
+ /**
193
+ * The whitespace-insensitive form of a side of a patch. Whitespace between
194
+ * tokens is dropped (quoted literals kept verbatim) and the lines are
195
+ * concatenated, so a re-indent or a re-flow compares equal. For an
196
+ * indentation-significant file each non-blank line keeps its leading
197
+ * whitespace and stays its own line — a re-flow or re-indent there is not
198
+ * provably meaningless, so it is not formatting. For a file whose line
199
+ * breaks end statements each non-blank line stays its own line, so a re-flow
200
+ * there is not formatting while a re-indent still is.
201
+ */
202
+ function squash(lines, path) {
203
+ if (lineTerminated(path))
204
+ return lines
205
+ .filter((l) => l.trim() !== "")
206
+ .map(stripOutsideQuotes)
207
+ .join("\n");
208
+ if (!indentSignificant(path))
209
+ return lines.map(stripOutsideQuotes).join("");
210
+ return lines
211
+ .filter((l) => l.trim() !== "")
212
+ .map((l) => (/^\s*/.exec(l)?.[0] ?? "") + stripOutsideQuotes(l))
213
+ .join("\n");
214
+ }
215
+ /**
216
+ * The slash-span form of a side of a patch: on each line, the raw text from
217
+ * its first `/` to its last is kept verbatim (delimited), and whitespace
218
+ * elsewhere is dropped. A regex literal never crosses a line, so it always
219
+ * lies inside its line's span; whitespace inside one is content (`/a b/` →
220
+ * `/ab/` changes the program). Telling a regex from a division, a comment or
221
+ * a markup closing tag is not attempted: any whitespace change between the
222
+ * first and last `/` of a line is held significant, so this form can only
223
+ * make a delta less trivial, never more.
224
+ */
225
+ function slashSpans(lines) {
226
+ return lines
227
+ .map((l) => {
228
+ const i = l.indexOf("/");
229
+ const j = l.lastIndexOf("/");
230
+ if (i === j)
231
+ return l.replace(/\s+/g, "");
232
+ return `${l.slice(0, i).replace(/\s+/g, "")}\0${l.slice(i, j + 1)}\0${l.slice(j + 1).replace(/\s+/g, "")}`;
233
+ })
234
+ .join("");
235
+ }
236
+ /** The class of a character for token joining: two characters of one class
237
+ * that whitespace separates would read as ONE token once it is dropped —
238
+ * `const n` → `constn`, `a + ++b` → `a+++b` (the same as `a++ + b`). Null for
239
+ * a character no neighbour joins with (brackets, separators, quotes). */
240
+ function joinClass(ch) {
241
+ if (/[\w$]/.test(ch) || ch.charCodeAt(0) > 127)
242
+ return "word";
243
+ if (/[+\-*/%&|^!~<>=?:.@#]/.test(ch))
244
+ return "op";
245
+ return null;
246
+ }
247
+ /** A word after which a line break ends the statement (JavaScript's
248
+ * restricted productions: `return\n(x)` returns undefined). Matched against
249
+ * the last nine characters, one more than the longest word. */
250
+ const RESTRICTED_WORD_END = /(?:^|[^\w$])(?:return|throw|break|continue|yield|async)$/;
251
+ /**
252
+ * The token-seam form of a side of a patch: whitespace outside quoted
253
+ * literals is dropped EXCEPT where it separates two characters of one join
254
+ * class, where it becomes a single space — or a line break, when the gap
255
+ * holds one. Dropping that whitespace would merge two tokens into one
256
+ * (`a + ++b` and `a++ + b` both squash to `a+++b`), so whether it is there is
257
+ * content. A line break is also kept wherever it can end a statement however
258
+ * the tokens join: after a restricted word (`return\n(x)`) and beside `++` or
259
+ * `--` (`a++\nb` against `a\n++b`). Every line break kept is a `\n` the form
260
+ * holds nowhere else, so compared beside `squash` this form can only make a
261
+ * delta less trivial, never more.
262
+ */
263
+ function tokenSeams(lines) {
264
+ let out = "";
265
+ let gap = false;
266
+ let lineBreak = false;
267
+ // A line comment runs to the end of its line, so the break after a line
268
+ // that holds a comment marker outside a literal is content.
269
+ const LINE_COMMENT = /\/\/|#|--/;
270
+ const push = (text) => {
271
+ if (text === "")
272
+ return;
273
+ const prev = out.slice(-1);
274
+ if (gap && prev !== "") {
275
+ const cls = joinClass(prev);
276
+ const seam = cls !== null && cls === joinClass(text.charAt(0));
277
+ const ends = lineBreak &&
278
+ (seam ||
279
+ RESTRICTED_WORD_END.test(out.slice(-9)) ||
280
+ /(?:\+\+|--)$/.test(out.slice(-2)) ||
281
+ /^(?:\+\+|--)/.test(text));
282
+ // A seam keeps its space and gains the break, so deleting every "\n"
283
+ // gives back the form without breaks exactly — before and after
284
+ // `digitsOnly` masks it — and the break form only ever refines it.
285
+ if (seam)
286
+ out += " ";
287
+ if (ends)
288
+ out += "\n";
289
+ }
290
+ out += text;
291
+ gap = false;
292
+ lineBreak = false;
293
+ };
294
+ for (const line of lines) {
295
+ gap = true;
296
+ lineBreak = true;
297
+ let commented = false;
298
+ line.split(QUOTED).forEach((part, i) => {
299
+ if (i % 2 === 1)
300
+ return push(part);
301
+ if (LINE_COMMENT.test(part))
302
+ commented = true;
303
+ part.split(/(\s+)/).forEach((chunk, k) => {
304
+ if (k % 2 === 1)
305
+ gap = true;
306
+ else
307
+ push(chunk);
308
+ });
309
+ });
310
+ if (commented && out !== "")
311
+ out += "\n";
312
+ }
313
+ return out;
314
+ }
315
+ /** Style sheets: whitespace between two selector tokens is a combinator
316
+ * (`a .child` is a descendant, `a.child` a compound), so no whitespace that
317
+ * separates two tokens there may vanish or appear, whatever their classes. */
318
+ function selectorWhitespace(path) {
319
+ return path !== undefined && /\.(css|scss|sass|less|styl)$/i.test(baseName(path));
320
+ }
321
+ /** Every whitespace run inside a line collapsed to one space and the lines
322
+ * joined by one: a re-indent and a re-flow compare equal, a gap that opens or
323
+ * closes between two tokens does not. */
324
+ function collapsed(lines) {
325
+ return lines
326
+ .map((l) => l.trim().replace(/\s+/g, " "))
327
+ .filter((l) => l !== "")
328
+ .join(" ");
329
+ }
330
+ /** A heredoc opener: `<<EOF`, `<<-'EOF'`, `<< "EOF"` (shell) and `<<~SQL`
331
+ * (Ruby, no space — `a << b` appends). */
332
+ const SHELL_HEREDOC = /<<-?\s*(['"]?)[A-Za-z_]\w*\1/;
333
+ const RUBY_HEREDOC = /<<[-~]?(['"]?)[A-Za-z_]\w*\1/;
334
+ /** A YAML block scalar indicator closing its line (`key: |`, `- >-`). */
335
+ const YAML_BLOCK_SCALAR = /(?:^|:|-)\s*[|>][-+1-9]{0,2}\s*(?:#.*)?$/;
336
+ /**
337
+ * May this hunk's changed lines sit inside a literal that spans lines — a
338
+ * template literal, a triple-quoted string, a heredoc, a YAML block scalar?
339
+ * Whitespace inside one is content, and the per-line `QUOTED` rule cannot see
340
+ * a literal that opens or closes on another line. Judged from what the hunk
341
+ * shows (its section text, its context and its changed lines): an unbalanced
342
+ * backtick, any triple quote, or the language's own opener. A literal whose
343
+ * delimiters both lie outside the hunk is beyond what the patch shows.
344
+ */
345
+ function mayTouchMultilineLiteral(hunk, path) {
346
+ const base = path === undefined ? "" : baseName(path);
347
+ const shell = isShellFile(base);
348
+ const ruby = isRubyFile(base);
349
+ const yaml = /\.ya?ml$/i.test(base);
350
+ return [hunk.section, ...hunk.lines.map((l) => l.text)].some((text) => text.includes('"""') ||
351
+ text.includes("'''") ||
352
+ (text.replace(/\\./g, "").split("`").length - 1) % 2 === 1 ||
353
+ (shell && SHELL_HEREDOC.test(text)) ||
354
+ (ruby && RUBY_HEREDOC.test(text)) ||
355
+ (yaml && YAML_BLOCK_SCALAR.test(text)));
356
+ }
357
+ /**
358
+ * The forms a side of a change block is compared in, every one of which must
359
+ * match for the change to be whitespace-only: `squash` (insignificant
360
+ * whitespace dropped — never inside a quoted literal, never leading
361
+ * indentation of a whitespace-significant language), `slashSpans` (no line's
362
+ * whitespace changed between its first and last `/` — a regex literal's
363
+ * whitespace is content), `tokenSeams` (no whitespace that separates two
364
+ * tokens appeared or vanished) and, in a style sheet, `collapsed`. The one
365
+ * list both `formattingOnly` and `digitsOnly` compare.
366
+ */
367
+ function whitespaceForms(lines, path) {
368
+ const forms = [squash(lines, path), slashSpans(lines), tokenSeams(lines)];
369
+ if (selectorWhitespace(path))
370
+ forms.push(collapsed(lines));
371
+ return forms;
372
+ }
373
+ /** The two sides of a block agree in every `whitespaceForms` form, each
374
+ * compared after `mask` (identity unless a caller masks something out). */
375
+ function sameForms(block, path, mask = (form) => form) {
376
+ const removed = whitespaceForms(block.removed, path);
377
+ return whitespaceForms(block.added, path).every((form, i) => mask(form) === mask(removed[i]));
378
+ }
379
+ /**
380
+ * Whitespace-only change, judged change block by change block (see
381
+ * `changeBlocks`): within a block, concatenation (not a per-line set) is the
382
+ * comparison, so a re-indent or a re-flow passes while a reordering of real
383
+ * lines does not — and a line moved across unchanged code is two blocks that
384
+ * each differ. A hunk that may touch a multi-line literal is never
385
+ * formatting.
386
+ */
387
+ export function formattingOnly(patch, path) {
388
+ const hunks = parseHunks(patch);
389
+ const blocks = hunks.flatMap(changeBlocks);
390
+ if (blocks.length === 0)
391
+ return false;
392
+ if (hunks.some((h) => mayTouchMultilineLiteral(h, path)))
393
+ return false;
394
+ return blocks.every((b) => sameForms(b, path));
395
+ }
396
+ /** A migration number: a digit run that starts a token and is followed by `_`
397
+ * and a letter — the `NNNN_name` shape a migration filename or `dependencies`
398
+ * entry carries. `1_000` (a numeric literal) is not one. */
399
+ const MIGRATION_NUMBER = /(?<![A-Za-z0-9_])\d+(?=_[A-Za-z])/g;
400
+ /** Same text once every MIGRATION-NUMBER run is masked — the renumber's
401
+ * content edit (`dependencies = [("app", "0042_x")]` → `"0043_x"`) and nothing
402
+ * else. Only the migration-number shape is masked (`MIGRATION_NUMBER`): a
403
+ * semantic numeric change inside a renamed migration — a batch size, a
404
+ * timeout, a default, `1_000` → `2_000` — does NOT mask away, so it
405
+ * classifies significant. Judged block by block like `formattingOnly`; in a
406
+ * hunk that may touch a multi-line literal the raw lines are compared, so only
407
+ * the numbers may differ there. */
408
+ export function digitsOnly(patch, path) {
409
+ const masked = (text) => text.replace(MIGRATION_NUMBER, "#");
410
+ return parseHunks(patch).every((hunk) => {
411
+ const literal = mayTouchMultilineLiteral(hunk, path);
412
+ return changeBlocks(hunk).every((block) => literal
413
+ ? masked(block.added.join("\n")) === masked(block.removed.join("\n"))
414
+ : sameForms(block, path, masked));
415
+ });
416
+ }
417
+ /** One release-version line and nothing else on it: `"version": "1.2.3",`
418
+ * (package.json) or `version = "1.2.3"` (pyproject/Cargo). The value must
419
+ * look like a version — a digit first, no spaces — so the npm lifecycle
420
+ * script `"version": "npm run build"` does not pass, and a line that merely
421
+ * MENTIONS version (`serde = { version = "1", features = [...] }`, an inline
422
+ * dependency with a `git` source) is not the key itself. */
423
+ const RELEASE_VERSION_LINE = /^\s*(?:"version"\s*:\s*"v?\d[^"\s]*"\s*,?|version\s*=\s*(?:"v?\d[^"\s]*"|'v?\d[^'\s]*'))\s*$/i;
424
+ /** A TOML table header line, its name with surrounding space dropped:
425
+ * `[package]` → `package`; an array table (`[[bin]]`) keeps its brackets. */
426
+ const TOML_TABLE = /^\s*(\[?\[[^\]]+\]\]?)\s*(?:#.*)?$/;
427
+ /** The TOML tables whose `version` key is the package's own release. */
428
+ const RELEASE_TABLES = new Set(["package", "workspace.package", "project", "tool.poetry"]);
429
+ function tomlTable(text) {
430
+ const m = TOML_TABLE.exec(text);
431
+ if (!m)
432
+ return null;
433
+ const header = m[1];
434
+ return header.startsWith("[[") ? header : header.slice(1, -1).replace(/\s+/g, "");
435
+ }
436
+ /** How far a line of JSON opens (+) or closes (−) objects and arrays, its
437
+ * quoted strings left out. */
438
+ function jsonDepthDelta(text) {
439
+ let delta = 0;
440
+ text.split(QUOTED).forEach((part, i) => {
441
+ if (i % 2 === 1)
442
+ return;
443
+ for (const ch of part) {
444
+ if (ch === "{" || ch === "[")
445
+ delta++;
446
+ else if (ch === "}" || ch === "]")
447
+ delta--;
448
+ }
449
+ });
450
+ return delta;
451
+ }
452
+ /** Every changed line is the release-version key itself — the release-bump
453
+ * shape. The key must also be the package's own, proven from what the hunk
454
+ * shows: in a TOML manifest it sits in a release table (`RELEASE_TABLES`) —
455
+ * `version = "2"` under `[dependencies.serde]` is a dependency requirement —
456
+ * and in a JSON manifest it is a key of the top-level object, which the hunk
457
+ * proves only when it starts at the file's first line (`"version"` under
458
+ * `"config"` is configuration, not the release). A line whose place the patch
459
+ * does not show is not proven to be the release. */
460
+ export function versionOnly(patch, path) {
461
+ if (path !== undefined && ["VERSION", "version.txt"].includes(baseName(path))) {
462
+ const lines = parseHunks(patch).flatMap((h) => h.lines);
463
+ const version = /^v?\d+(?:\.\d+)*(?:[-+][0-9A-Za-z.-]+)?$/;
464
+ return (lines.some((line) => line.kind !== " ") &&
465
+ lines.every((line) => line.text.trim() === "" ? line.kind === " " : version.test(line.text.trim())));
466
+ }
467
+ const toml = path !== undefined && /\.toml$/i.test(path);
468
+ const json = path !== undefined && /\.json$/i.test(path);
469
+ let changed = 0;
470
+ for (const hunk of parseHunks(patch)) {
471
+ let table = tomlTable(hunk.section);
472
+ // The old side's nesting depth before the line; unknown (null) unless
473
+ // the hunk starts at the top of the file.
474
+ let depth = hunk.oldStart === 1 ? 0 : null;
475
+ for (const line of hunk.lines) {
476
+ table = tomlTable(line.text) ?? table;
477
+ if (line.kind !== " ") {
478
+ changed++;
479
+ if (!RELEASE_VERSION_LINE.test(line.text))
480
+ return false;
481
+ if (toml && (table === null || !RELEASE_TABLES.has(table)))
482
+ return false;
483
+ if (json && depth !== 1)
484
+ return false;
485
+ }
486
+ if (depth !== null && line.kind !== "+")
487
+ depth += jsonDepthDelta(line.text);
488
+ }
489
+ }
490
+ return changed > 0;
491
+ }
492
+ /** Does an ownership set hold this file — under its name, or, for a rename,
493
+ * under the name it had? The one ownership rule the classifier and the
494
+ * reviewed-head ownership read share. */
495
+ export function ownedBy(file, owned) {
496
+ return (owned.has(file.filename) ||
497
+ (file.previous_filename !== undefined && owned.has(file.previous_filename)));
498
+ }
499
+ /** The PR's own files as an ownership set — or undefined, "ownership not
500
+ * consulted", when the list was truncated: a PR-owned file past GitHub's cap
501
+ * would otherwise read as a base-merge file, which is trivial. */
502
+ export function ownershipSet(own) {
503
+ if (own.truncated)
504
+ return undefined;
505
+ return new Set(own.flatMap((f) => (f.previous_filename ? [f.filename, f.previous_filename] : [f.filename])));
506
+ }
507
+ /** Classify one file. The rules are deliberately narrow: anything a rule does
508
+ * not positively recognise is significant. */
509
+ export function classifyFile(file, opts = {}) {
510
+ const name = file.filename;
511
+ const trivial = (reason) => ({
512
+ filename: name,
513
+ class: "trivial",
514
+ reason,
515
+ });
516
+ // A hand-resolved merge conflict is never trivial, wherever the file lives.
517
+ if (opts.resolvedFiles?.has(name))
518
+ return { filename: name, class: "significant", reason: "content" };
519
+ if (opts.prFiles && !ownedBy(file, opts.prFiles))
520
+ return trivial("base-merge");
521
+ const patch = file.patch ?? "";
522
+ const churn = (file.additions ?? 0) + (file.deletions ?? 0);
523
+ if (file.previous_filename && isMigrationRenumber(file.previous_filename, name)) {
524
+ // A missing patch with churn (GitHub omits it on large files) proves
525
+ // nothing — the same reading classifyDelta's delete+add pairing takes.
526
+ if (churn === 0 || (patch && (digitsOnly(patch, name) || formattingOnly(patch, name))))
527
+ return trivial("migration-renumber");
528
+ return { filename: name, class: "significant", reason: "content" };
529
+ }
530
+ if (file.previous_filename && churn === 0)
531
+ return trivial("pure-rename");
532
+ if (LOCKFILES.has(baseName(name)))
533
+ return trivial("version-bump");
534
+ if (VERSION_MANIFESTS.has(baseName(name)) && patch && versionOnly(patch, name))
535
+ return trivial("version-bump");
536
+ if (patch && formattingOnly(patch, name))
537
+ return trivial("formatting");
538
+ if (!patch && churn === 0)
539
+ return trivial("no-content-change");
540
+ return { filename: name, class: "significant", reason: "content" };
541
+ }
542
+ /**
543
+ * Classify the whole delta. Run before the per-file pass: GitHub reports a
544
+ * migration renumber as a rename only when it detects one — a large enough
545
+ * content edit comes back as a delete + an add. That pair is a renumber only
546
+ * when its content, too, differs by migration numbers alone (`digitsOnly`).
547
+ */
548
+ export function classifyDelta(files, opts = {}) {
549
+ const removedMigrations = new Map();
550
+ for (const f of files) {
551
+ if (f.status !== "removed")
552
+ continue;
553
+ const m = migrationName(f.filename);
554
+ if (m)
555
+ removedMigrations.set(m.rest, f);
556
+ }
557
+ const renumbered = new Set();
558
+ for (const f of files) {
559
+ if (f.status !== "added")
560
+ continue;
561
+ const m = migrationName(f.filename);
562
+ const partner = m ? removedMigrations.get(m.rest) : undefined;
563
+ if (!m || !partner)
564
+ continue;
565
+ if (migrationName(partner.filename)?.num === m.num)
566
+ continue;
567
+ // The names only say it MIGHT be a renumber; the content must say it is.
568
+ // GitHub reports a rename as delete + add exactly when similarity is low,
569
+ // so the pair is trivial only when the removed file's text and the added
570
+ // file's text differ by migration numbers alone. A missing patch (GitHub
571
+ // omits it on large files) proves nothing, so it is not a renumber.
572
+ if (!f.patch || !partner.patch)
573
+ continue;
574
+ const gone = changedLines(partner.patch, "-");
575
+ const came = changedLines(f.patch, "+");
576
+ if (gone.length === 0 && came.length === 0)
577
+ continue;
578
+ if (!digitsOnly(["@@", ...gone, ...came].join("\n"), f.filename))
579
+ continue;
580
+ renumbered.add(f.filename);
581
+ renumbered.add(partner.filename);
582
+ }
583
+ const classified = files.map((f) => {
584
+ // A hand-resolved conflict beats every trivial rule, renumber included:
585
+ // classifyFile alone decides it.
586
+ if (renumbered.has(f.filename) && !opts.resolvedFiles?.has(f.filename)) {
587
+ // Ownership still wins: a migration that arrived with the base is a
588
+ // base-merge file whatever its number does.
589
+ const owned = !opts.prFiles || opts.prFiles.has(f.filename);
590
+ return {
591
+ filename: f.filename,
592
+ class: "trivial",
593
+ reason: owned ? "migration-renumber" : "base-merge",
594
+ };
595
+ }
596
+ return classifyFile(f, opts);
597
+ });
598
+ const significant = classified.filter((f) => f.class === "significant");
599
+ // A truncated delta is significant by construction: the files GitHub did not
600
+ // return may be exactly the PR-owned edit, and "all visible files trivial"
601
+ // says nothing about them. An unread one, for the same reason.
602
+ if (files.unread)
603
+ return { class: "significant", files: classified, significant, unread: true };
604
+ if (files.truncated)
605
+ return { class: "significant", files: classified, significant, truncated: true };
606
+ const cls = classified.length === 0 ? "empty" : significant.length > 0 ? "significant" : "trivial";
607
+ return { class: cls, files: classified, significant };
608
+ }
609
+ /** CI conclusions that carry no bad news. Anything else at an approved head is
610
+ * new evidence (guard 2's sense): something the standing review did not see. */
611
+ const CI_BENIGN = new Set(["success", "neutral", "skipped", "cancelled", "stale"]);
612
+ export function ciIsAdverse(conclusion) {
613
+ if (!conclusion)
614
+ return false;
615
+ return !CI_BENIGN.has(conclusion.toLowerCase());
616
+ }
617
+ /**
618
+ * Guard 2's rule as the recheck reads it, off the same mark: an unchanged head
619
+ * with nothing new is a no-op. A base advance is exempt because its delta is on
620
+ * the base side, and an adverse CI conclusion IS the new evidence. The runner's
621
+ * free early refusal and the plan both ask this one predicate.
622
+ */
623
+ export function unchangedHeadNoop(trigger, headSha, lastReviewedSha, ci) {
624
+ const adverseCi = trigger === "ci" && ciIsAdverse(ci?.conclusion);
625
+ return trigger !== "base_advance" && headSha === lastReviewedSha && !adverseCi;
626
+ }
627
+ /** Move a withheld-approval mark to a newer head after an uphold. This is NOT
628
+ * a re-approve — the approval was never posted; the CI-gate retry still
629
+ * demands green CI at the new head before anything posts. No mark, or a mark
630
+ * already at this head, is a silent no-op. */
631
+ export function carryWithheldMark(store, pr, newHead, via, log = () => { }) {
632
+ const withheld = store.approvalWithheld(pr);
633
+ if (!withheld || withheld.head_sha === newHead)
634
+ return;
635
+ // Only ever carry TO the PR's current head. Overlapping rechecks finish in
636
+ // any order: an older push's handler finishing after the newer one's would
637
+ // otherwise carry the mark BACK, and the ci_completed retry at the real head
638
+ // then no-ops on "head-moved" with the push backstop already claimed —
639
+ // stranded. Read and write are one synchronous stretch, so nothing can
640
+ // interleave between them.
641
+ const current = store.getPr(pr)?.head_sha;
642
+ if (current && current !== newHead) {
643
+ store.logEvent("approval_withheld_carry_stale", pr, {
644
+ from: withheld.head_sha,
645
+ to: newHead,
646
+ current,
647
+ via,
648
+ });
649
+ log(`recheck: #${pr} withheld approval NOT carried to ${newHead.slice(0, 7)} — the PR is at ${current.slice(0, 7)} (${via})`);
650
+ return;
651
+ }
652
+ // The classifier verdict that caused the withhold rides along with the
653
+ // mark — the carried head has no fresher CI read to offer, and the CI-gate
654
+ // retry re-classifies at release time anyway.
655
+ store.recordApprovalWithheld(pr, {
656
+ head_sha: newHead,
657
+ ci_state: withheld.ci_state ?? null,
658
+ reason: withheld.reason ?? "red-ci",
659
+ });
660
+ store.logEvent("approval_withheld_carried", pr, { from: withheld.head_sha, to: newHead, via });
661
+ log(`recheck: #${pr} withheld approval carried ${withheld.head_sha.slice(0, 7)} → ${newHead.slice(0, 7)} (${via})`);
662
+ }
663
+ /**
664
+ * The whole decision, pure. Order matters: the cheap structural refusals come
665
+ * before anything that would cost a classification, and the classification
666
+ * comes before anything that would cost a session turn.
667
+ */
668
+ export function planRecheck(input) {
669
+ const noop = (reason, classification) => ({
670
+ action: "noop",
671
+ reason,
672
+ class: classification?.class ?? "empty",
673
+ ...(classification ? { classification } : {}),
674
+ });
675
+ // The author-reply trigger is owned end to end by src/pingpong.ts. Nothing
676
+ // to recheck without a code delta, and a second reply handler would
677
+ // duplicate it.
678
+ if (input.trigger === "author_reply")
679
+ return noop("pingpong-owns-reply");
680
+ // Only a standing APPROVE can be moved by this path, and only downwards.
681
+ // Everything else (REQUEST_CHANGES, NEEDS_HUMAN, never reviewed) already has
682
+ // a route: the ordinary trigger → round path.
683
+ if (normalizeVerdict(input.standing) !== "APPROVE")
684
+ return noop("no-standing-approval");
685
+ if (unchangedHeadNoop(input.trigger, input.head_sha, input.last_reviewed_sha, input.ci))
686
+ return noop("unchanged-head");
687
+ const adverseCi = input.trigger === "ci" && ciIsAdverse(input.ci?.conclusion);
688
+ const classification = classifyDelta(input.delta, {
689
+ ...(input.prFiles ? { prFiles: new Set(input.prFiles) } : {}),
690
+ ...(input.resolvedFiles ? { resolvedFiles: new Set(input.resolvedFiles) } : {}),
691
+ });
692
+ if (adverseCi)
693
+ return {
694
+ action: "recheck",
695
+ class: classification.class,
696
+ evidence: classification.unread
697
+ ? `CI concluded ${input.ci?.conclusion}; the delta's files could not be read from GitHub — read the delta in the worktree yourself`
698
+ : `CI concluded ${input.ci?.conclusion}`,
699
+ classification,
700
+ };
701
+ if (classification.class === "empty")
702
+ return noop("empty-delta", classification);
703
+ if (classification.class === "trivial")
704
+ return noop("trivial-delta", classification);
705
+ return {
706
+ action: "recheck",
707
+ class: classification.class,
708
+ evidence: classification.truncated
709
+ ? `GitHub truncated the delta at ${classification.files.length} file(s) — the unseen remainder may hold changes this review has not seen`
710
+ : `${classification.significant.length} changed file(s) this review has not seen`,
711
+ classification,
712
+ };
713
+ }
714
+ // ---- the order handed to the session --------------------------------------
715
+ /** `pr-<n>-recheck-<head12>.json` — the decision file's name under `verdict/`,
716
+ * and the findings record's under `state/findings/`. */
717
+ export function recheckDecisionFile(pr, headSha) {
718
+ return `pr-${pr}-recheck-${recheckHead12(headSha)}.json`;
719
+ }
720
+ export const RECHECK_FILE_RE = /^pr-(\d+)-recheck-([0-9a-f]{7,40})\.json$/i;
721
+ /** The runaway-guard identity of one recheck question: (trigger, head, ci
722
+ * conclusion, base target). Shared by runRecheck's claim and the reconcile
723
+ * backstop's has-this-been-asked check. */
724
+ export function recheckFingerprint(req, headOverride) {
725
+ const head = headOverride ?? req.head_sha ?? "";
726
+ return [req.trigger, head, req.ci?.conclusion ?? "", req.base?.after ?? ""].join(":");
727
+ }
728
+ /** Fallback renderer (stub-wired daemon, unit tests): the order as JSON. */
729
+ export function defaultRecheckPrompt(order) {
730
+ return JSON.stringify(order, null, 2);
731
+ }
732
+ /**
733
+ * The files the PR owns for delta classification: what it owns at the current
734
+ * head PLUS what it owned at the REVIEWED head. A push that
735
+ * restores a reviewed file to its base content drops it from the current diff,
736
+ * yet that is the PR undoing its own reviewed change, not a base merge. The
737
+ * second read happens only when a delta file falls outside today's ownership:
738
+ * `<base_ref>...<reviewed>` is the PR's own diff at the reviewed head. Throws
739
+ * on a read failure — every caller already treats that as "not trivial".
740
+ */
741
+ export async function reviewedOwnership(adapter, pr, reviewedHead, delta, ownedNow) {
742
+ // Unknown ownership (a truncated listing) stays unknown.
743
+ if (!ownedNow)
744
+ return undefined;
745
+ const unowned = delta.some((f) => !ownedBy(f, ownedNow));
746
+ if (!unowned || !adapter.getPr || !adapter.compareFiles)
747
+ return new Set(ownedNow);
748
+ const { base_ref } = await adapter.getPr(pr);
749
+ if (!base_ref)
750
+ return new Set(ownedNow);
751
+ const then = ownershipSet(await adapter.compareFiles(base_ref, reviewedHead));
752
+ return then ? new Set([...ownedNow, ...then]) : undefined;
753
+ }
754
+ /**
755
+ * The files hand-resolved inside the merges of `from..to` — the one reading
756
+ * of the merge check both the recheck and dispatch's system re-trigger
757
+ * classifier make. No check wired ⇒ none. Degraded (git could not tell) keeps
758
+ * the delta-only classification, but is recorded as an event and a durable
759
+ * anomaly row — never silently quieter. `via` names a caller other than the
760
+ * recheck.
761
+ */
762
+ export async function verifyMerges(store, resolve, from, to, pr, via) {
763
+ if (!resolve)
764
+ return [];
765
+ const res = await resolve(from, to, pr);
766
+ if (res.degraded) {
767
+ store.logEvent("recheck_merge_verify_degraded", pr, { from, to, ...(via ? { via } : {}) });
768
+ store.logAnomaly("recheck_merge_verify_degraded", pr, null, `merge-verify degraded ${from.slice(0, 7)}→${to.slice(0, 7)}${via ? ` at ${via}` : ""} — delta-only classification kept`);
769
+ }
770
+ return res.files;
771
+ }
772
+ /**
773
+ * One trigger, start to finish. Returns the plan so callers (and tests) can see
774
+ * exactly why a trigger cost nothing.
775
+ */
776
+ export async function runRecheck(deps, req) {
777
+ const { cfg, store } = deps;
778
+ const log = deps.log ?? (() => { });
779
+ const now = deps.now ?? (() => new Date());
780
+ const done = (plan) => {
781
+ if (plan.action === "noop")
782
+ store.logEvent("recheck_noop", req.pr, {
783
+ trigger: req.trigger,
784
+ reason: plan.reason,
785
+ delta_class: plan.class,
786
+ files: plan.classification?.files.map((f) => `${f.filename} (${f.reason})`) ?? [],
787
+ });
788
+ return plan;
789
+ };
790
+ // Deploy-window drain: generate no recheck nudge at all —
791
+ // prefer not creating work over hold-then-flush, because the session the
792
+ // nudge would wake is about to be killed by the deploy. The reconcile
793
+ // push-backstop re-raises the trigger after the lift.
794
+ if (store.drainState().draining)
795
+ return done({ action: "noop", reason: "draining", class: "empty" });
796
+ if (!cfg.recheck.enabled || !triggerEnabled(cfg, req.trigger))
797
+ return done({ action: "noop", reason: "disabled", class: "empty" });
798
+ // Cheapest refusal first: ping-pong owns the author reply.
799
+ if (req.trigger === "author_reply")
800
+ return done(planRecheck({ ...emptyPlanInput(req), standing: "APPROVE" }));
801
+ const mark = store.reviewedHead(req.pr);
802
+ if (standingVerdict(mark) !== "APPROVE")
803
+ return done({ action: "noop", reason: "no-standing-approval", class: "empty" });
804
+ const row = store.getPr(req.pr);
805
+ if (!row || row.state === "closed")
806
+ return done({ action: "noop", reason: "pr-not-open", class: "empty" });
807
+ // A pause stops this PR's review work, a nudge included — it defers the
808
+ // question, never loses it. A push question is asked again by the reconcile
809
+ // backstop once the pause lifts; a CI or base-advance question has no such
810
+ // sweep, so it goes on to its claim and its nudge is held until the resume
811
+ // (`deferWhilePaused` below).
812
+ if (row.paused && req.trigger === "push")
813
+ return done({ action: "noop", reason: "paused", class: "empty" });
814
+ const head = req.head_sha || row.head_sha;
815
+ if (head.toLowerCase() !== row.head_sha.toLowerCase())
816
+ return done({ action: "noop", reason: "stale-head", class: "empty" });
817
+ const lastReviewed = mark.head_sha;
818
+ // Refuse before spending a single API call on a head we already decided.
819
+ if (unchangedHeadNoop(req.trigger, head, lastReviewed, req.ci))
820
+ return done({ action: "noop", reason: "unchanged-head", class: "empty" });
821
+ // The range the delta is read over — and the one its merges are checked in.
822
+ const range = req.trigger === "base_advance" && req.base
823
+ ? { from: req.base.before, to: req.base.after }
824
+ : { from: lastReviewed, to: head };
825
+ if (!deps.adapter.compareFiles || !deps.adapter.listPrFiles)
826
+ return done({ action: "noop", reason: "no-delta-source", class: "empty" });
827
+ let delta;
828
+ let prFiles;
829
+ try {
830
+ delta = await deps.adapter.compareFiles(range.from, range.to);
831
+ prFiles = ownershipSet(await deps.adapter.listPrFiles(req.pr));
832
+ // Ownership is the union of the reviewed head's and the current head's —
833
+ // see reviewedOwnership. A base advance leaves the
834
+ // head where it was reviewed, so there is nothing to revert. A truncated
835
+ // listing leaves ownership unknown (undefined): every delta file is then
836
+ // significant.
837
+ if (req.trigger !== "base_advance")
838
+ prFiles = await reviewedOwnership(deps.adapter, req.pr, lastReviewed, delta, prFiles);
839
+ }
840
+ catch (err) {
841
+ // A read failure is not evidence of anything. A question the reconcile
842
+ // push backstop asks again — a push, or a benign CI result at a new head,
843
+ // whose push question is the same — upholds and is asked once the read
844
+ // recovers. An adverse CI result is asked whatever its delta holds, so it
845
+ // goes on with the delta UNREAD and the session is told so. A base advance
846
+ // is NOT asked unread: almost every one touches no PR-owned file, and
847
+ // waking a session on an advance nobody could classify is the delta-blind
848
+ // staleness check the recheck exists to avoid. It upholds, with this
849
+ // anomaly row as its record — nothing asks it again.
850
+ const askUnread = ciIsAdverse(req.ci?.conclusion);
851
+ log(`recheck: #${req.pr} delta read failed`, err);
852
+ store.logEvent("recheck_delta_failed", req.pr, {
853
+ trigger: req.trigger,
854
+ error: String(err),
855
+ asked_unread: askUnread,
856
+ });
857
+ // Durable anomaly row, quiet class: DB-only, reviewed at the daily
858
+ // heartbeat.
859
+ store.logAnomaly("recheck_delta_failed", req.pr, null, `delta read failed (${req.trigger}): ${String(err)} — ${askUnread
860
+ ? "asked with the delta unread"
861
+ : req.trigger === "base_advance"
862
+ ? "upheld unread; nothing asks this base advance again"
863
+ : "the reconcile push backstop asks again"}`);
864
+ if (!askUnread)
865
+ return done({ action: "noop", reason: "no-delta-source", class: "empty" });
866
+ delta = unreadDelta();
867
+ prFiles = undefined;
868
+ }
869
+ // Conflict resolutions are never trivial: ask git which files in this
870
+ // delta's merges were hand-resolved.
871
+ const resolvedFiles = await verifyMerges(store, deps.resolvedFilesInMerges, range.from, range.to, req.pr);
872
+ const plan = planRecheck({
873
+ trigger: req.trigger,
874
+ standing: standingVerdict(mark),
875
+ last_reviewed_sha: lastReviewed,
876
+ head_sha: head,
877
+ delta,
878
+ ...(prFiles ? { prFiles: [...prFiles] } : {}),
879
+ resolvedFiles,
880
+ ...(req.ci ? { ci: req.ci } : {}),
881
+ });
882
+ if (store.getPr(req.pr)?.head_sha.toLowerCase() !== head.toLowerCase())
883
+ return done({ action: "noop", reason: "stale-head", class: "empty" });
884
+ // Carry-forward: a withheld approval pinned to an old sha would be stranded
885
+ // — the retry no-ops on "head-moved", and the recheck can never approve.
886
+ // When the delta itself upholds (empty/trivial), the never-posted approval
887
+ // moves with the head, so green CI at the NEW head releases it. A
888
+ // significant delta carries only after the session's explicit uphold
889
+ // (watcher below).
890
+ if (plan.action === "noop") {
891
+ if ((plan.reason === "empty-delta" || plan.reason === "trivial-delta") &&
892
+ head !== lastReviewed) {
893
+ carryWithheldMark(store, req.pr, head, `recheck-noop:${plan.reason}`, log);
894
+ // A trivial/empty answer is terminal for this head: claim it, so the
895
+ // reconcile backstop (below in reconcile.ts) never re-classifies the
896
+ // same question every sweep. No wake goes out, so no receipt token can
897
+ // ever exist for it: `nudge: false` keeps the undelivered sweep from
898
+ // reading its absence as a lost nudge and releasing it.
899
+ store.claimRecheck(req.pr, recheckFingerprint(req, head), {
900
+ head_sha: head,
901
+ trigger: req.trigger,
902
+ reason: `noop:${plan.reason}`,
903
+ nudge: false,
904
+ });
905
+ }
906
+ return done(plan);
907
+ }
908
+ // The runaway guard: one nudge per (trigger, head, evidence). Without it a
909
+ // reconcile replay or a redelivered webhook re-asks the same question and
910
+ // pays for it every time.
911
+ const fingerprint = recheckFingerprint(req, head);
912
+ if (store.hasRecheck(req.pr, fingerprint))
913
+ return done({
914
+ action: "noop",
915
+ reason: "already-rechecked",
916
+ class: plan.class,
917
+ classification: plan.classification,
918
+ });
919
+ const order = {
920
+ pr: req.pr,
921
+ title: row.title,
922
+ trigger: req.trigger,
923
+ standing_verdict: "APPROVE",
924
+ last_reviewed_sha: lastReviewed,
925
+ head_sha: head,
926
+ worktree: (deps.worktreeFor ?? prWorktreeRel)(req.pr),
927
+ // A base advance's delta is the base branch's range, not anything since
928
+ // the reviewed head: the evidence names the range that was compared.
929
+ evidence: req.trigger === "base_advance" && req.base
930
+ ? `the base branch \`${req.base.branch}\` advanced ${range.from.slice(0, 12)}→${range.to.slice(0, 12)}; ${plan.evidence}`
931
+ : plan.evidence,
932
+ files: plan.classification.files,
933
+ resolved_files: resolvedFiles,
934
+ decision_file: recheckDecisionFile(req.pr, head),
935
+ issued_at: now().toISOString(),
936
+ };
937
+ const render = deps.renderPrompt ?? defaultRecheckPrompt;
938
+ // The repo facts for the files the session must review
939
+ // (the significant ones; every delta file when none is) — "" unless
940
+ // lessons are delivered. Bounded and total: never a reason to hold or lose
941
+ // the nudge.
942
+ let facts = "";
943
+ if (deps.repoFacts) {
944
+ const touched = plan.classification.significant.length
945
+ ? plan.classification.significant.map((f) => f.filename)
946
+ : plan.classification.files.map((f) => f.filename);
947
+ try {
948
+ facts = await deps.repoFacts({
949
+ pr: req.pr,
950
+ title: row.title,
951
+ paths: touched,
952
+ head_sha: head,
953
+ purpose: "recheck",
954
+ });
955
+ }
956
+ catch (err) {
957
+ log(`recheck: #${req.pr} repo facts unavailable (ignored)`, err);
958
+ facts = "";
959
+ }
960
+ if (typeof facts !== "string")
961
+ facts = "";
962
+ }
963
+ // Mid-round nudges are held and flushed at round end — the PR's one main
964
+ // pi is never given two instructions at once.
965
+ let delivered;
966
+ let claimed = false;
967
+ try {
968
+ const payload = stampPrompt(withRepoFacts(render(order), facts), recheckPromptMarker(req.pr, fingerprint));
969
+ if (store.getPr(req.pr)?.head_sha.toLowerCase() !== head.toLowerCase())
970
+ return done({ action: "noop", reason: "stale-head", class: "empty" });
971
+ if (standingVerdict(store.reviewedHead(req.pr)) !== "APPROVE")
972
+ return done({ action: "noop", reason: "no-standing-approval", class: "empty" });
973
+ // A pause that landed while the reads ran: a push question is asked again
974
+ // by the reconcile backstop once it lifts.
975
+ if (req.trigger === "push" && store.getPr(req.pr)?.paused)
976
+ return done({ action: "noop", reason: "paused", class: "empty" });
977
+ claimed = store.db.transaction(() => {
978
+ if (!store.claimRecheck(req.pr, fingerprint, {
979
+ head_sha: head,
980
+ trigger: req.trigger,
981
+ reason: plan.evidence,
982
+ ...(req.trigger !== "push"
983
+ ? { delivery: { payload, reason: recheckWakeReason(req.trigger) } }
984
+ : {}),
985
+ }))
986
+ return false;
987
+ // A same-head question (an adverse CI conclusion, a base advance) is new
988
+ // evidence at a head already reviewed. Recorded on the mark the posting
989
+ // guard reads, so the change request it may lead to is not refused as a
990
+ // duplicate of the review that stands (guard 2).
991
+ if (head === lastReviewed) {
992
+ store.noteNewEvidence(req.pr, now().toISOString());
993
+ store.logEvent("recheck_evidence_noted", req.pr, {
994
+ trigger: req.trigger,
995
+ head_sha: head,
996
+ evidence: plan.evidence,
997
+ });
998
+ }
999
+ return true;
1000
+ })();
1001
+ if (!claimed)
1002
+ return done({
1003
+ action: "noop",
1004
+ reason: "already-rechecked",
1005
+ class: plan.class,
1006
+ classification: plan.classification,
1007
+ });
1008
+ delivered = await wakeOrHold({ store, inject: deps.inject, log }, req.pr,
1009
+ // The receipt token distinguishes confirmed delivery from owed work.
1010
+ // Pushes can be regenerated; CI/base questions retain these exact bytes.
1011
+ payload, recheckWakeReason(req.trigger),
1012
+ // A push question the reconcile backstop asks again; a CI or base-advance
1013
+ // question uses its durable payload, so its claim stays and the wake is held
1014
+ // for the flush paths rather than released on a failed injection — and
1015
+ // held, not delivered, while the PR is paused.
1016
+ { holdOnFailure: req.trigger !== "push", deferWhilePaused: req.trigger !== "push" });
1017
+ }
1018
+ catch (err) {
1019
+ // Pushes have a backstop. Other triggers retain the claim and exact
1020
+ // payload so a failed injection cannot consume their only question.
1021
+ if (claimed && req.trigger === "push")
1022
+ store.releaseRecheck(req.pr, fingerprint);
1023
+ store.logEvent("recheck_delivery_failed", req.pr, {
1024
+ trigger: req.trigger,
1025
+ head_sha: head,
1026
+ error: String(err),
1027
+ });
1028
+ log(`recheck: #${req.pr} delivery failed — ${req.trigger === "push" ? "claim released for retry" : "durable question retained when claimed"}`, err);
1029
+ return done({
1030
+ action: "noop",
1031
+ reason: "delivery-failed",
1032
+ class: plan.class,
1033
+ classification: plan.classification,
1034
+ });
1035
+ }
1036
+ store.logEvent("recheck_nudged", req.pr, {
1037
+ trigger: req.trigger,
1038
+ head_sha: head,
1039
+ last_reviewed_sha: lastReviewed,
1040
+ delta_class: plan.class,
1041
+ significant: plan.classification.significant.map((f) => f.filename),
1042
+ evidence: plan.evidence,
1043
+ delivered,
1044
+ });
1045
+ log(`recheck: #${req.pr} nudged (${req.trigger}, ${plan.classification.significant.length} file(s)) — ${delivered}`);
1046
+ return { ...plan, delivered, order };
1047
+ }
1048
+ function triggerEnabled(cfg, trigger) {
1049
+ switch (trigger) {
1050
+ case "push":
1051
+ return cfg.recheck.on_push;
1052
+ case "ci":
1053
+ return cfg.recheck.on_ci;
1054
+ case "base_advance":
1055
+ return cfg.recheck.on_base_advance;
1056
+ case "author_reply":
1057
+ return true; // always reaches the ping-pong no-op
1058
+ }
1059
+ }
1060
+ function emptyPlanInput(req) {
1061
+ return {
1062
+ trigger: req.trigger,
1063
+ standing: null,
1064
+ last_reviewed_sha: "",
1065
+ head_sha: req.head_sha ?? "",
1066
+ delta: [],
1067
+ };
1068
+ }
1069
+ /**
1070
+ * A base branch moved: fan the advance out to the PRs that target it and hold
1071
+ * a standing approval. Every other PR is skipped without an API call.
1072
+ */
1073
+ export async function runBaseAdvance(deps, advance) {
1074
+ const out = new Map();
1075
+ const log = deps.log ?? (() => { });
1076
+ if (!deps.cfg.recheck.enabled || !deps.cfg.recheck.on_base_advance)
1077
+ return out;
1078
+ // Deploy-window drain: skip the whole fan-out before it spends per-PR API
1079
+ // calls (runRecheck would refuse each one anyway).
1080
+ if (deps.store.drainState().draining)
1081
+ return out;
1082
+ for (const row of deps.store.allPrs()) {
1083
+ if (row.state === "closed")
1084
+ continue;
1085
+ if (standingVerdict(deps.store.reviewedHead(row.number)) !== "APPROVE")
1086
+ continue;
1087
+ if (deps.adapter.getPr) {
1088
+ try {
1089
+ // Called on the adapter, never through a detached reference: the real
1090
+ // GitHubAdapter's methods are class methods and need their `this`.
1091
+ const ref = await deps.adapter.getPr(row.number);
1092
+ // GitHub already says closed: there is nothing left to audit, and the
1093
+ // reconcile closed-sync settles the row.
1094
+ if (ref.state === "closed" || ref.merged)
1095
+ continue;
1096
+ if (ref.base_ref && ref.base_ref !== advance.branch)
1097
+ continue;
1098
+ }
1099
+ catch (err) {
1100
+ log(`recheck: #${row.number} base ref lookup failed`, err);
1101
+ continue;
1102
+ }
1103
+ }
1104
+ out.set(row.number, await runRecheck(deps, { pr: row.number, trigger: "base_advance", base: advance }));
1105
+ }
1106
+ return out;
1107
+ }
1108
+ export function validateRecheckDecision(raw) {
1109
+ if (typeof raw !== "object" || raw === null)
1110
+ return { ok: false, error: "not an object" };
1111
+ const v = raw;
1112
+ if (typeof v.pr === "string" && /^\d+$/.test(v.pr))
1113
+ v.pr = Number(v.pr);
1114
+ if (typeof v.pr !== "number" || !Number.isInteger(v.pr))
1115
+ return { ok: false, error: "pr must be an integer" };
1116
+ if (typeof v.head_sha !== "string" || v.head_sha.length === 0)
1117
+ return { ok: false, error: "head_sha must be a non-empty string" };
1118
+ const raw_outcome = typeof v.outcome === "string" ? v.outcome.trim().toLowerCase() : "";
1119
+ // NOT a rejection: an unrecognised outcome (including "approve" and
1120
+ // "needs_human", which a session may reach for out of round habit) becomes
1121
+ // the default — uphold. The recheck path has no other verdict to give.
1122
+ const coerced = raw_outcome === "uphold" || raw_outcome === "request_changes"
1123
+ ? undefined
1124
+ : String(v.outcome ?? "");
1125
+ const outcome = raw_outcome === "request_changes" ? "request_changes" : "uphold";
1126
+ return {
1127
+ ok: true,
1128
+ decision: {
1129
+ pr: v.pr,
1130
+ head_sha: v.head_sha,
1131
+ outcome,
1132
+ ...(typeof v.reason === "string" ? { reason: v.reason.trim().slice(0, 800) } : {}),
1133
+ },
1134
+ ...(coerced !== undefined ? { coerced } : {}),
1135
+ };
1136
+ }
1137
+ /**
1138
+ * Consumes `verdict/pr-<n>-recheck-<head12>.json`. It touches GitHub not at
1139
+ * all: on `uphold` there is nothing to say, and on `request_changes` the
1140
+ * session has already posted the review through the gated scripts — the daemon
1141
+ * only moves the lifecycle state.
1142
+ *
1143
+ * Neither outcome moves the reviewed-head mark to a new head. The mark
1144
+ * records what was really REVIEWED and posted; an upheld recheck posted
1145
+ * nothing, and stamping it would make the no-new-evidence guard swallow a
1146
+ * genuine review re-request at that head. A change request the session posted
1147
+ * was stamped by the posting route itself; the watcher only writes its
1148
+ * decided verdict onto that stamp (`settleChangeRequest`). The recheck's own
1149
+ * runaway guard is the `rechecks` claim table.
1150
+ */
1151
+ export class RecheckWatcher {
1152
+ deps;
1153
+ watcher = null;
1154
+ inFlight = new Set();
1155
+ processedDir;
1156
+ constructor(deps) {
1157
+ this.deps = deps;
1158
+ this.processedDir = join(deps.verdictDir, "processed");
1159
+ }
1160
+ start() {
1161
+ mkdirSync(this.deps.verdictDir, { recursive: true });
1162
+ void this.sweep();
1163
+ this.watcher = watch(this.deps.verdictDir, (_event, filename) => {
1164
+ const name = filename ? String(filename) : "";
1165
+ if (RECHECK_FILE_RE.test(name))
1166
+ void this.handleFile(name);
1167
+ });
1168
+ }
1169
+ stop() {
1170
+ this.watcher?.close();
1171
+ this.watcher = null;
1172
+ }
1173
+ /** One PR's decisions in name order, different PRs side by side: a file
1174
+ * still being written is waited out for seconds and must not
1175
+ * hold another PR's decision behind it. */
1176
+ async sweep() {
1177
+ if (!existsSync(this.deps.verdictDir))
1178
+ return [];
1179
+ const byPr = new Map();
1180
+ for (const name of readdirSync(this.deps.verdictDir).sort()) {
1181
+ const match = RECHECK_FILE_RE.exec(name);
1182
+ if (!match)
1183
+ continue;
1184
+ byPr.set(match[1], [...(byPr.get(match[1]) ?? []), name]);
1185
+ }
1186
+ const done = [];
1187
+ await Promise.all([...byPr.values()].map(async (names) => {
1188
+ for (const name of names)
1189
+ if (await this.handleFile(name))
1190
+ done.push(name);
1191
+ }));
1192
+ return done.sort();
1193
+ }
1194
+ archive(name) {
1195
+ mkdirSync(this.processedDir, { recursive: true });
1196
+ const ts = (this.deps.now ?? (() => new Date()))().toISOString().replace(/[:.]/g, "-");
1197
+ try {
1198
+ renameSync(join(this.deps.verdictDir, name), join(this.processedDir, `${ts}-${name}`));
1199
+ }
1200
+ catch (err) {
1201
+ this.deps.log?.(`recheck: could not archive ${name}`, err);
1202
+ }
1203
+ }
1204
+ /**
1205
+ * Parse `state/findings/pr-<n>-recheck-<head12>.json` into `round_findings`.
1206
+ *
1207
+ * A recheck owns no round row, so its entries are attributed to the round
1208
+ * whose verdict it just moved — the PR's reviewed-head mark, falling back to
1209
+ * its highest round number. Keys are prefixed `recheck:<head12>:` so a
1210
+ * recheck's rows can never collide with that round's own.
1211
+ */
1212
+ async recordRecheckFindings(pr, headSha) {
1213
+ const dir = this.deps.findingsDir;
1214
+ if (!dir)
1215
+ return;
1216
+ try {
1217
+ const path = join(dir, recheckDecisionFile(pr, headSha));
1218
+ const store = this.deps.store;
1219
+ const roundNo = store.reviewedHead(pr)?.round_no ??
1220
+ store.roundsFor(pr).reduce((m, r) => (r.round_no > m ? r.round_no : m), 0);
1221
+ const parsed = readFindingsRecord(path, pr, roundNo);
1222
+ if (parsed.status !== "ok" || !parsed.record) {
1223
+ store.logEvent("recheck_findings_record_absent", pr, {
1224
+ head_sha: headSha,
1225
+ status: parsed.status,
1226
+ });
1227
+ this.deps.log?.(`recheck: #${pr} moved the verdict with no findings record (${parsed.status}) — decision stands`);
1228
+ return;
1229
+ }
1230
+ const entries = parsed.record.findings.map((f) => ({
1231
+ ...f,
1232
+ key: `${recheckKeyPrefix(headSha)}${f.key}`,
1233
+ }));
1234
+ store.recordRoundFindings({ pr, round_id: null, round_no: roundNo, head_sha: headSha }, entries);
1235
+ store.logEvent("recheck_findings_recorded", pr, {
1236
+ head_sha: headSha,
1237
+ round_no: roundNo,
1238
+ entries: entries.length,
1239
+ });
1240
+ }
1241
+ catch (err) {
1242
+ this.deps.log?.(`recheck: #${pr} findings record read failed`, err);
1243
+ }
1244
+ }
1245
+ /**
1246
+ * The lifecycle half of a change request the session posted. The review on
1247
+ * GitHub is the standing decision at its head: when the shell stamped it at
1248
+ * the head the mark already held, the mark keeps the verdict it had, so the
1249
+ * decided verdict is written here — whether it posted as REQUEST_CHANGES or
1250
+ * guard 1 softened it to a COMMENT (a person approved the head), the same
1251
+ * enrichment a round's verdict gets. A mark still reading APPROVE posted
1252
+ * holds no post of this decision and is left as it is. The lifecycle moves to awaiting_author
1253
+ * unless newer work owns the PR — a round running, or a person's queued
1254
+ * re-review request — which this answer must not settle or erase; that
1255
+ * work's own verdict decides. The PR's head is GitHub's and is never
1256
+ * rewound to the decision's.
1257
+ */
1258
+ settleChangeRequest(pr, headSha, file) {
1259
+ const { store } = this.deps;
1260
+ const mark = store.reviewedHead(pr);
1261
+ if (mark &&
1262
+ mark.head_sha.toLowerCase() === headSha.toLowerCase() &&
1263
+ (mark.posted_event === "REQUEST_CHANGES" || mark.posted_event === "COMMENT") &&
1264
+ standingVerdict(mark) !== "REQUEST_CHANGES")
1265
+ store.recordReviewedVerdict(pr, mark.head_sha, "REQUEST_CHANGES");
1266
+ const row = store.getPr(pr);
1267
+ if (row?.state === "reviewing" ||
1268
+ (row?.state === "queued" && row.trigger_provenance === "human")) {
1269
+ store.logEvent("recheck_lifecycle_deferred", pr, {
1270
+ file,
1271
+ head_sha: headSha,
1272
+ state: row.state,
1273
+ trigger_provenance: row.trigger_provenance ?? null,
1274
+ });
1275
+ return;
1276
+ }
1277
+ applyEvent(store, pr, { type: "verdict", verdict: "REQUEST_CHANGES" }, {}, {
1278
+ botLogin: botAccountLogin(this.deps.cfg.github),
1279
+ ...(this.deps.now ? { now: this.deps.now } : {}),
1280
+ reason: "recheck:request-changes",
1281
+ });
1282
+ }
1283
+ superseded(pr, head) {
1284
+ const { store } = this.deps;
1285
+ const mark = store.reviewedHead(pr);
1286
+ if (!mark || mark.head_sha.toLowerCase() === head.toLowerCase())
1287
+ return false;
1288
+ if (mark.head_sha.toLowerCase() === store.getPr(pr)?.head_sha.toLowerCase())
1289
+ return true;
1290
+ const claims = store
1291
+ .rechecksFor(pr)
1292
+ .filter((claim) => claim.head_sha.toLowerCase() === head.toLowerCase());
1293
+ return claims.some((claim) => mark.reviewed_at > claim.created_at);
1294
+ }
1295
+ async handleFile(name) {
1296
+ const match = RECHECK_FILE_RE.exec(name);
1297
+ if (!match || this.inFlight.has(name))
1298
+ return false;
1299
+ this.inFlight.add(name);
1300
+ try {
1301
+ const path = join(this.deps.verdictDir, name);
1302
+ if (!existsSync(path))
1303
+ return false;
1304
+ const pr = Number(match[1]);
1305
+ // fs.watch fires the moment the file appears, often before the
1306
+ // session's write completes, so a single eager read would discard valid
1307
+ // decisions as "unparseable". An unparseable file is waited out while
1308
+ // it is still moving; one that parses is judged at once.
1309
+ const read = await readSettledJson(path, resolvePartialWritePolicy(this.deps.partialWrite));
1310
+ if (read.kind === "missing")
1311
+ return false;
1312
+ const reads = read.reads;
1313
+ const parsed = read.kind === "parsed"
1314
+ ? validateRecheckDecision(read.value)
1315
+ : { ok: false, error: `unparseable: ${read.error}` };
1316
+ if (!parsed.ok || !parsed.decision) {
1317
+ // Malformed is the safe direction here — the standing verdict simply
1318
+ // stays standing. No park, no anomaly round, nothing posted. But say
1319
+ // so out loud: a silent drop would hide a partial-read race.
1320
+ this.deps.store.logEvent("recheck_decision_malformed", pr, {
1321
+ file: name,
1322
+ error: parsed.error ?? "unknown",
1323
+ });
1324
+ this.deps.log?.(`recheck: #${pr} decision ${name} malformed after ${reads} read(s) — standing verdict kept (${parsed.error ?? "unknown"})`);
1325
+ this.archive(name);
1326
+ return true;
1327
+ }
1328
+ const d = parsed.decision;
1329
+ if (d.pr !== pr || recheckHead12(d.head_sha) !== match[2].toLowerCase()) {
1330
+ this.deps.store.logEvent("recheck_decision_rejected", pr, {
1331
+ file: name,
1332
+ reason: "pr/head do not match the filename",
1333
+ });
1334
+ this.archive(name);
1335
+ return true;
1336
+ }
1337
+ if (parsed.coerced !== undefined)
1338
+ this.deps.store.logEvent("recheck_outcome_coerced", pr, {
1339
+ file: name,
1340
+ given: parsed.coerced,
1341
+ applied: "uphold",
1342
+ });
1343
+ if (this.superseded(pr, d.head_sha)) {
1344
+ this.deps.store.logEvent("recheck_decision_superseded", pr, {
1345
+ file: name,
1346
+ head_sha: d.head_sha,
1347
+ });
1348
+ this.archive(name);
1349
+ return true;
1350
+ }
1351
+ if (d.outcome === "request_changes") {
1352
+ // The reasoning behind a backed-down approval. Best-effort
1353
+ // and strictly additive: a missing record here is logged and nothing
1354
+ // else — the decision above is what moves the PR.
1355
+ await this.recordRecheckFindings(pr, d.head_sha);
1356
+ // Ingestion yields: a newer round may have settled in the meantime.
1357
+ if (this.superseded(pr, d.head_sha)) {
1358
+ this.archive(name);
1359
+ return true;
1360
+ }
1361
+ // Approval is off the table now — a lingering withheld mark would let
1362
+ // a later green CI re-queue "for the withheld approval" of a PR whose
1363
+ // standing verdict this very decision just flipped.
1364
+ this.deps.store.clearApprovalWithheld(pr);
1365
+ this.settleChangeRequest(pr, d.head_sha, name);
1366
+ }
1367
+ if (d.outcome === "uphold") {
1368
+ // Session-examined significant delta, verdict upheld: the withheld
1369
+ // approval (if any) follows the head.
1370
+ carryWithheldMark(this.deps.store, pr, d.head_sha, "recheck-uphold", (m) => this.deps.log?.(m));
1371
+ // Ledger freshness after the uphold (optional wiring).
1372
+ if (this.deps.checkLedger && this.deps.store.getPr(pr)?.state !== "closed") {
1373
+ try {
1374
+ await this.deps.checkLedger(pr, d.head_sha);
1375
+ }
1376
+ catch (err) {
1377
+ this.deps.log?.(`recheck: ledger check failed on #${pr}`, err);
1378
+ }
1379
+ }
1380
+ }
1381
+ this.deps.store.logEvent(d.outcome === "request_changes" ? "recheck_request_changes" : "recheck_upheld", pr, {
1382
+ file: name,
1383
+ head_sha: d.head_sha,
1384
+ reason: d.reason ?? null,
1385
+ });
1386
+ this.deps.log?.(`recheck: #${pr} ${d.outcome} at ${d.head_sha.slice(0, 7)}`);
1387
+ this.archive(name);
1388
+ return true;
1389
+ }
1390
+ catch (err) {
1391
+ this.deps.log?.(`recheck: ${name} failed`, err);
1392
+ return false;
1393
+ }
1394
+ finally {
1395
+ this.inFlight.delete(name);
1396
+ }
1397
+ }
1398
+ }
1399
+ //# sourceMappingURL=recheck.js.map