@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,1727 @@
1
+ // Deterministic dispatch. No LLM in this path: FIFO by trigger time, at most
2
+ // `daemon.max_concurrent_rounds` rounds in flight, triggers consumed at
3
+ // dispatch, lenses/gates derived from the files API by the heuristics.
4
+ //
5
+ // Before paying for a re-review round, a SYSTEM-provenance trigger (push / CI /
6
+ // staleness — `pr_state.trigger_provenance`, stamped by
7
+ // statemachine.applyEvent) is run through the recheck delta classifier; an
8
+ // empty/trivial delta since the reviewed head consumes the trigger and upholds
9
+ // the standing verdict instead of dispatching. A human ask (fresh label, fresh
10
+ // team re-review request, author reply) is never short-circuited — but like
11
+ // every trigger it runs no round at a head already reviewed with no author
12
+ // reply since (guard 2's daemon half): it settles on the standing verdict.
13
+ //
14
+ // The round order (types.ts `RoundOrder`) is the daemon↔session contract; the
15
+ // plan and the order are built by the review planner (packages/pi-plugin/src/planner.ts), and the
16
+ // prompt renderer (packages/pi-plugin/src/templates.ts) is injected so this module has no content
17
+ // dependency.
18
+ import { botAccountLogin } from "@quickchatai/pi-quickreview/src/identity.js";
19
+ import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
20
+ import { join, resolve } from "node:path";
21
+ import { githubCloneDir, sandboxRoot } from "./config.js";
22
+ import { fingerprintRoundIntegrity, INTEGRITY_BUDGET_MS, roundIntegrityOpts, } from "./execboundary.js";
23
+ import { withinBudget } from "@quickchatai/pi-quickreview/src/budget.js";
24
+ import { DJANGO_ECOSYSTEM } from "@quickchatai/pi-quickreview/src/ecosystem.js";
25
+ import { STYLE_PASS, LINT_CONFORMANCE_PASS } from "@quickchatai/pi-quickreview/src/heuristics.js";
26
+ import { lensEnabled } from "@quickchatai/pi-quickreview/src/lenstuning.js";
27
+ import { VALIDATE_PASS } from "@quickchatai/pi-quickreview/src/include.js";
28
+ import { hasNewEvidence } from "@quickchatai/pi-quickreview/src/guards.js";
29
+ import { addTierOf, buildPassPlan, buildRoundOrder, decideFollowUp, lensesOf, optionalOrderOf, renderDeltaPatch, reviewedHeadOf, roundDeltaOf, sweepOrderOf, } from "@quickchatai/pi-quickreview/src/planner.js";
30
+ import { assembleRoundMemory, describeGaps, pinnedHeadState, MEMORY_LESS, roundContextQuery, ROUND_CONTEXT_DEFAULTS, } from "./memory/context.js";
31
+ import { carryWithheldMark, reviewedOwnership, classifyDelta, ownershipSet, recheckFingerprint, verifyMerges, } from "./recheck.js";
32
+ import { postingReceiptPath, readPostingReceipt } from "./posting-receipt.js";
33
+ import { buildRoundDelivery, deliversAnything, deliveryCounts, onlyWritten, } from "./memory/delivery.js";
34
+ import { findStyleGuide } from "@quickchatai/pi-quickreview/src/styleguide.js";
35
+ import { findCiLinters } from "@quickchatai/pi-quickreview/src/lintconfig.js";
36
+ import { writeLessonFiles, writePassTasks } from "@quickchatai/pi-quickreview/src/passtasks.js";
37
+ import { CONTEXT_PACK_FILE, readWorktreeFileForPack, renderContextPack, } from "@quickchatai/pi-quickreview/src/contextpack.js";
38
+ import { renderGateNotes } from "@quickchatai/pi-quickreview/src/templates.js";
39
+ import { firstPartyDirectiveFiles } from "@quickchatai/pi-quickreview/src/severity.js";
40
+ import { applyEvent, restoreStandingLifecycle, setPrGates } from "./statemachine.js";
41
+ import { snapshotMark, standingReview } from "@quickchatai/pi-quickreview/src/standing-review.js";
42
+ import { roundPromptMarker } from "@quickchatai/pi-quickreview/src/types.js";
43
+ import { preparePriorFindings } from "@quickchatai/pi-quickreview/src/priorfindings.js";
44
+ import { prWorktreeRel } from "./worktrees.js";
45
+ export function defaultWorktree(pr) {
46
+ return prWorktreeRel(pr);
47
+ }
48
+ /** PR size at the reviewed head, summed over the files API response. */
49
+ export function diffstatOf(files) {
50
+ return {
51
+ additions: files.reduce((n, f) => n + (f.additions ?? 0), 0),
52
+ deletions: files.reduce((n, f) => n + (f.deletions ?? 0), 0),
53
+ changed_files: files.length,
54
+ };
55
+ }
56
+ /** Round type: no prior round → "first"; a staleness recheck → "staleness";
57
+ * anything else → "re-review". `priorRounds` counts live verdicted rounds
58
+ * only: backfilled rounds and cancelled or verdict-less attempts are not
59
+ * counted, so such an attempt leaves the next round "first". */
60
+ export function roundTypeFor(row, priorRounds) {
61
+ if (priorRounds === 0)
62
+ return "first";
63
+ if (row.stale_recheck)
64
+ return "staleness";
65
+ return "re-review";
66
+ }
67
+ /** Queued PRs in FIFO order by trigger time (nulls last, then PR number).
68
+ * Overseer-paused PRs never dispatch. */
69
+ export function queuedInOrder(store) {
70
+ return store
71
+ .allPrs()
72
+ .filter((p) => p.state === "queued" && !p.paused)
73
+ .sort((a, b) => {
74
+ const at = a.trigger_ts ?? "";
75
+ const bt = b.trigger_ts ?? "";
76
+ if (at === bt)
77
+ return a.number - b.number;
78
+ if (!at)
79
+ return 1;
80
+ if (!bt)
81
+ return -1;
82
+ return at < bt ? -1 : 1;
83
+ });
84
+ }
85
+ export function reviewsInFlight(store) {
86
+ const running = store.runningRounds().length;
87
+ if (store.drainState().draining)
88
+ return { running, queued: 0 };
89
+ const queued = queuedInOrder(store).filter((row) => !store.openReviewFailure(row.number) &&
90
+ // The pump skips a row while ANY blocking command stands (dispatchUnlocked's
91
+ // `blockingSessionCommand`); a failed one among them does not drain itself.
92
+ !store.blockingSessionCommandsForPr(row.number).some((c) => c.status === "failed")).length;
93
+ return { running, queued };
94
+ }
95
+ export function capacity(store, cfg) {
96
+ return Math.max(0, cfg.daemon.max_concurrent_rounds - store.runningRounds().length);
97
+ }
98
+ /** A consume target that is already gone (label removed in an earlier round,
99
+ * no pending team request) comes back as a 404 — that is consumption already
100
+ * done, not a failure. */
101
+ function alreadyConsumed(err) {
102
+ return /→ 404\b|"status":\s*"404"/.test(String(err));
103
+ }
104
+ /**
105
+ * The round's ecosystem pre-check (behind the ecosystem seam), run once
106
+ * per round after the worktree is prepared and BEFORE the session is handed
107
+ * the order.
108
+ *
109
+ * The behavior is the ecosystem's (`packages/pi-plugin/src/ecosystem.ts`): Django detects
110
+ * migration-number collisions at the pinned base sha and posts the remedy as a
111
+ * gated **COMMENT** — never a blocker, never a verdict — while `none` is a
112
+ * first-class configured-off (`{ status: "off" }`): nothing runs, nothing is
113
+ * read, nothing is logged. What stays HERE is the plumbing the seam must not
114
+ * own: the store, the gate-backed adapter, and the mapping back onto the round
115
+ * order's `migration_collisions` / `migration_precheck_degraded` fields. The
116
+ * seam is total — every failure resolves to an outcome — so a round is never
117
+ * lost to the pre-check.
118
+ *
119
+ * A DEGRADED check (Django only: the check exists but could not run while the
120
+ * PR touches migrations) must never be quieter than no check at all ("more
121
+ * reliable, never quieter"): the caller renders an explicit
122
+ * "the pre-check did not run — check collisions yourself" note into the round
123
+ * prompt, which is what conditionally un-muzzles pass-data-safety's withdrawn
124
+ * finding class for exactly that round (see packages/pi-plugin/src/include.ts,
125
+ * MIGRATION_NUMBERING_ADDENDUM) — and raises the overseer anomaly. "off"
126
+ * takes none of those paths: configured-off is not degradation.
127
+ *
128
+ * A WITHHELD comment (the ledger holds a collision-comment row it cannot
129
+ * read, so a duplicate cannot be ruled out) is the other way a
130
+ * checked round gets quieter, and it gets the same operator surface: an
131
+ * overseer anomaly, raised ONCE per PR (`StateStore.anomalyRaised`) because
132
+ * the torn row is torn on every round until someone repairs it, and one wake
133
+ * per round would bury the anomalies that need reading. The event itself is
134
+ * logged on every withheld round — only the alert is deduplicated.
135
+ */
136
+ async function ecosystemPreCheck(deps, eco, args) {
137
+ const outcome = await eco.preCheck({
138
+ logEvent: (kind, pr, payload) => deps.store.logEvent(kind, pr, payload),
139
+ isDryRun: deps.cfg.dry_run,
140
+ ...(deps.adapter.listReviewsFull
141
+ ? {
142
+ findCollisionComment: async (pr, marker, head) => (await deps.adapter.listReviewsFull(pr)).some((review) => review.user === deps.cfg.github.bot_login &&
143
+ review.commit_id === head &&
144
+ review.body.split(/\r?\n/).includes(marker)),
145
+ }
146
+ : {}),
147
+ migrationCollisionCommented: (pr, fingerprint) => deps.store.migrationCollisionCommented(pr, fingerprint, { live: !deps.cfg.dry_run }),
148
+ createReview: async (pr, payload, options) => {
149
+ // A throw after transport begins is ambiguous. Persist that BEFORE the
150
+ // gated call, then narrow it only from the gate's returned fact. The
151
+ // pre-fanout auto-retry class excludes ambiguous/executed rounds.
152
+ deps.store.recordRoundPreflightPosting(args.round_id, "ambiguous");
153
+ const result = await deps.adapter.createReview(pr, payload, options);
154
+ deps.store.recordRoundPreflightPosting(args.round_id, result.executed ? "executed" : result.dryRunFile ? "dryrun" : "refused");
155
+ return result;
156
+ },
157
+ ...(deps.listBaseMigrations ? { listBaseMigrations: deps.listBaseMigrations } : {}),
158
+ ...(deps.log ? { log: deps.log } : {}),
159
+ }, args);
160
+ return {
161
+ collisions: outcome.status === "checked" ? outcome.collisions : [],
162
+ degraded: outcome.status === "degraded",
163
+ commentWithheld: outcome.status === "checked" && outcome.comment_withheld === true,
164
+ commentUndelivered: outcome.status === "checked" && outcome.comment_undelivered === true,
165
+ };
166
+ }
167
+ /**
168
+ * First-party directive detection + the deterministic escalation-mention pick,
169
+ * run per round after the worktree is prepared. Pure path predicate
170
+ * over the files API (`isFirstPartyDirectivePath`), plus one read-only git log
171
+ * at the round's MERGE BASE — pre-PR history, so the PR's own commits never
172
+ * elect the PR author as their own approver. The pick is logged
173
+ * (`escalation_mention_selected`) with its source; every failure degrades to
174
+ * the config fallback, never to a lost round.
175
+ */
176
+ async function directiveEscalationFor(deps, args) {
177
+ const log = deps.log ?? (() => { });
178
+ // The vocabulary is the compiled-in default extended by [review.directives]
179
+ // — extension only, never a narrowing.
180
+ const directiveFiles = firstPartyDirectiveFiles(args.files, deps.cfg.review.directives);
181
+ if (directiveFiles.length === 0)
182
+ return undefined;
183
+ // An unset [review] escalation_mention_default falls back to the owner's
184
+ // GitHub login — the config default itself stays "".
185
+ const fallback = deps.cfg.review.escalation_mention_default || deps.cfg.owner.github_login;
186
+ const sha = args.merge_base || args.base_sha;
187
+ let author = null;
188
+ if (deps.directiveAuthor && sha) {
189
+ try {
190
+ author = await deps.directiveAuthor(sha, directiveFiles);
191
+ }
192
+ catch (err) {
193
+ log(`dispatch: directive-author lookup failed on #${args.pr}`, err);
194
+ }
195
+ }
196
+ const mention = author?.login ?? fallback;
197
+ deps.store.logEvent("escalation_mention_selected", args.pr, {
198
+ files: directiveFiles,
199
+ mention,
200
+ source: author?.login ? "git-log" : "config-default",
201
+ author_name: author?.name ?? null,
202
+ sha: sha || null,
203
+ });
204
+ return { files: directiveFiles, mention };
205
+ }
206
+ /**
207
+ * Seed one round's prompt with memory: the guaranteed pinned head
208
+ * plus EXACTLY ONE budgeted context block, queried from the PR's title and
209
+ * diff shape.
210
+ *
211
+ * This is the single context call per round — one call site, one
212
+ * `assembleRoundMemory`, which itself calls `contextBlock` once. It runs after
213
+ * the worktree is prepared and the order is built, so the context is seeded
214
+ * from the same point the passes read.
215
+ *
216
+ * It cannot fail a round. `assembleRoundMemory` is total (deadline + swallowed
217
+ * throws); the extra try/catch here is belt-and-braces for a client that
218
+ * breaks the contract, and the outcome is identical either way: log the gap,
219
+ * tell the memory health monitor (`seed.onSeeded`), dispatch memory-less.
220
+ *
221
+ * There is no per-round memory anomaly: the health monitor
222
+ * (src/memory/health.ts) owns telling a person, on state transitions rather
223
+ * than once per round. The `round_memory_seeded` event is the per-round
224
+ * record, and it is what the monitor is fed from.
225
+ */
226
+ async function seedRoundMemory(deps, order, files,
227
+ /** Told once the round's context search was issued (the lesson finds
228
+ * under `delivery = "on"` wait for it). Optional. */
229
+ onContextSent) {
230
+ const seed = deps.memory;
231
+ if (!seed)
232
+ return MEMORY_LESS;
233
+ const log = deps.log ?? (() => { });
234
+ const query = roundContextQuery({ title: order.title, files });
235
+ // The session's own recall searches for the same thing.
236
+ try {
237
+ seed.publishQuery?.(order.pr, query);
238
+ }
239
+ catch (err) {
240
+ log(`dispatch: #${order.pr} recall hint not published (ignored)`, err);
241
+ }
242
+ const timeoutMs = seed.timeoutMs ?? ROUND_CONTEXT_DEFAULTS.timeoutMs;
243
+ // The seed's search waits — inside its own budget, never past
244
+ // it — until the case writer's last batch is estimated done embedding, so
245
+ // it is not queued behind those embeddings on the one-thread embedder.
246
+ const activity = seed.activity;
247
+ const quiet = activity && typeof activity.whenEmbedderQuiet === "function"
248
+ ? (maxWaitMs) => activity.whenEmbedderQuiet(maxWaitMs)
249
+ : undefined;
250
+ let mem;
251
+ try {
252
+ mem = await assembleRoundMemory(seed.assembler, query, {
253
+ ...(seed.maxTokens !== undefined ? { maxTokens: seed.maxTokens } : {}),
254
+ timeoutMs,
255
+ log,
256
+ ...(onContextSent ? { onContextSent } : {}),
257
+ ...(quiet ? { quiet } : {}),
258
+ ...(seed.searchReserveMs !== undefined ? { searchReserveMs: seed.searchReserveMs } : {}),
259
+ });
260
+ }
261
+ catch (err) {
262
+ log(`dispatch: #${order.pr} memory seeding threw — round proceeds memory-less`, err);
263
+ mem = {
264
+ ...MEMORY_LESS,
265
+ asked: true,
266
+ query,
267
+ max_tokens: seed.maxTokens ?? ROUND_CONTEXT_DEFAULTS.maxTokens,
268
+ degraded: true,
269
+ gaps: [{ part: "head", reason: `error: ${String(err)}` }],
270
+ };
271
+ }
272
+ const headState = pinnedHeadState(mem);
273
+ // The baseline for the one transition that is silent by construction: a
274
+ // readable store whose pinned area went from populated to empty. Read
275
+ // BEFORE this round's row is written, and only when it could matter.
276
+ const previousHead = headState === "empty" ? deps.store.lastReadPinnedHead() : null;
277
+ deps.store.logEvent("round_memory_seeded", order.pr, {
278
+ round_id: order.round_id,
279
+ round_no: order.round_no,
280
+ query: mem.query,
281
+ max_tokens: mem.max_tokens,
282
+ head_state: headState,
283
+ head_chars: mem.head.length,
284
+ block_chars: mem.block.length,
285
+ clamped: mem.clamped,
286
+ degraded: mem.degraded,
287
+ gaps: mem.gaps,
288
+ ms: mem.ms,
289
+ // How long the search waited for the case writer's embedding tail,
290
+ // inside `ms` (absent when no tracker is wired).
291
+ ...(mem.quiet_wait_ms !== undefined ? { quiet_wait_ms: mem.quiet_wait_ms } : {}),
292
+ });
293
+ if (previousHead && previousHead.head_chars > 0) {
294
+ // Empty is not degraded: a genuinely empty pinned area is a healthy
295
+ // answer — right for a fresh install, wrong for a container recreated
296
+ // without its data volume. One anomaly on the transition itself carries
297
+ // that signal; the next round's baseline is this empty row, so it cannot
298
+ // repeat. An owner who unpinned their last standing instruction sees it
299
+ // once too, and knows why.
300
+ deps.store.logAnomaly("pinned_head_emptied", order.pr, order.round_no, `the pinned area read as EMPTY with the store readable; the last seeded round (#${previousHead.pr ?? "?"} at ${previousHead.ts}) carried ${previousHead.head_chars} chars of standing instructions`, [
301
+ `ANOMALY (memory) — PR #${order.pr} round ${order.round_no}`,
302
+ `The memory store answered, and the pinned area is EMPTY. At the last seeded round (#${previousHead.pr ?? "?"}, ${previousHead.ts}) it held ${previousHead.head_chars} chars of standing instructions.`,
303
+ "If nobody unpinned them on purpose, the store lost its data — a container recreated without its state volume looks exactly like this. " +
304
+ "Check the openviking data directory on the state volume and the TUI's memory tab. The round was dispatched normally; this fires once, on the transition.",
305
+ ].join("\n"));
306
+ }
307
+ try {
308
+ seed.onSeeded?.(mem);
309
+ }
310
+ catch (err) {
311
+ log(`dispatch: #${order.pr} memory health observer threw (ignored)`, err);
312
+ }
313
+ if (mem.degraded) {
314
+ log(`dispatch: #${order.pr} round ${order.round_no} seeded memory-less (${describeGaps(mem)})`);
315
+ }
316
+ return mem;
317
+ }
318
+ /**
319
+ * Open the round's memory window (`RoundMemorySeed.activity`):
320
+ * from just before the seed until the round's lesson selection has taken
321
+ * over, the case writer starts no batch, so no case embedding is queued in
322
+ * front of the round's own reads on the server's one-thread embedder. Returns
323
+ * its idempotent `end`; total — a broken tracker is a no-op, never a throw.
324
+ */
325
+ function openMemoryWindow(deps, pr, log) {
326
+ const activity = deps.memory?.activity;
327
+ if (!activity)
328
+ return () => { };
329
+ try {
330
+ const end = activity.begin(`round #${pr}`);
331
+ return () => {
332
+ try {
333
+ end();
334
+ }
335
+ catch (err) {
336
+ log(`dispatch: #${pr} memory window not closed (ignored)`, err);
337
+ }
338
+ };
339
+ }
340
+ catch (err) {
341
+ log(`dispatch: #${pr} memory window not opened (ignored)`, err);
342
+ return () => { };
343
+ }
344
+ }
345
+ /** `[memory] delivery = "on"` with a hook that can deliver. Total: a
346
+ * throwing hook is "not delivering" — the shadow path. */
347
+ function lessonsDelivering(deps, log) {
348
+ const hook = deps.lessons;
349
+ if (!hook || typeof hook.select !== "function" || typeof hook.delivering !== "function")
350
+ return false;
351
+ try {
352
+ return hook.delivering() === true;
353
+ }
354
+ catch (err) {
355
+ log("dispatch: lesson delivery switch unreadable — delivering nothing (ignored)", err);
356
+ return false;
357
+ }
358
+ }
359
+ /** The round's selection under `on`, awaited beside the seed. Total: the
360
+ * hook bounds it by its prefetch budget; a throw or a rejection is `null`
361
+ * and the round renders as if nothing were selected. */
362
+ async function selectRoundLessons(deps, order, files, after) {
363
+ try {
364
+ return (await deps.lessons?.select?.(order, files, { after })) ?? null;
365
+ }
366
+ catch (err) {
367
+ (deps.log ?? (() => { }))(`dispatch: #${order.pr} lesson selection failed — no lessons this round (ignored)`, err);
368
+ return null;
369
+ }
370
+ }
371
+ /**
372
+ * Hand the round's delivered lessons to its prompt: write the
373
+ * lessons files (the validator's, and each lens the session may add) and
374
+ * return the seed carrying the delivery — or the seed untouched when nothing
375
+ * was selected, so the prompt keeps the standard bleed rule verbatim. Total: a
376
+ * lessons file that cannot be written is simply not named in the prompt.
377
+ */
378
+ function deliverRoundLessons(deps, input) {
379
+ const log = deps.log ?? (() => { });
380
+ let d = input.delivery;
381
+ if (!d || !deliversAnything(d))
382
+ return input.seeded;
383
+ try {
384
+ d = writeLessonFiles(input.dir, d);
385
+ }
386
+ catch (err) {
387
+ log(`dispatch: #${d.pr} lessons files unavailable — the round names none`, err);
388
+ }
389
+ // A lessons file that was not written delivers nothing: neither the index
390
+ // nor the bleed rule may claim it did.
391
+ d = onlyWritten(d);
392
+ return deliversAnything(d) ? { ...input.seeded, lessons: d } : input.seeded;
393
+ }
394
+ /** The `lesson_delivered` record — written once the prompt carrying the
395
+ * delivery was handed to the session. */
396
+ function logLessonDelivery(store, roundId, d) {
397
+ const counts = deliveryCounts(d);
398
+ store.logEvent("lesson_delivered", d.pr, {
399
+ round_id: roundId,
400
+ round_no: d.round_no,
401
+ lessons: counts.lessons,
402
+ cases: counts.cases,
403
+ rows: d.rows.filter((r) => r.lessons.length > 0).length,
404
+ validator: d.validator?.lessons.length ?? 0,
405
+ validator_file: d.validator?.file !== undefined,
406
+ addable_files: d.addable.filter((a) => a.file !== undefined).map((a) => a.lens),
407
+ });
408
+ }
409
+ /**
410
+ * Classify the delta (last reviewed head → current head) for a
411
+ * SYSTEM-provenance re-trigger, using the one recheck classifier
412
+ * (`recheck.classifyDelta`) — never a copy. Every uncertainty resolves to
413
+ * "run the full round": an unknown head, a missing compare seam, or a failed
414
+ * read must never quietly swallow a review.
415
+ */
416
+ async function classifySystemRetrigger(deps, row, mark) {
417
+ const log = deps.log ?? (() => { });
418
+ // Unknown head is always "new" — the same reading guards.hasNewEvidence takes.
419
+ if (!row.head_sha)
420
+ return { skip: false };
421
+ if (row.head_sha === mark.head_sha) {
422
+ // Same head. An author reply pending on the mark is a person's ask (the
423
+ // rule guard 2 reads) → full round; anything else would be a full round
424
+ // whose output posting-time guard 2 throws away.
425
+ if (hasNewEvidence(mark, row.head_sha))
426
+ return { skip: false };
427
+ return { skip: true, reason: "unchanged-head", class: "empty", files: [] };
428
+ }
429
+ if (!deps.adapter.compareFiles)
430
+ return { skip: false };
431
+ try {
432
+ const delta = await deps.adapter.compareFiles(mark.head_sha, row.head_sha);
433
+ // The union with the reviewed head's ownership: a push reverting a
434
+ // reviewed file is the PR's own change, not a base merge.
435
+ // A truncated listing leaves ownership unknown (undefined).
436
+ const prFiles = await reviewedOwnership(deps.adapter, row.number, mark.head_sha, delta, ownershipSet(await deps.adapter.listPrFiles(row.number)));
437
+ // The recheck's own merge check: a degraded one keeps the delta-only
438
+ // classification but is never silently quieter.
439
+ const resolvedFiles = new Set(await verifyMerges(deps.store, deps.resolvedFilesInMerges, mark.head_sha, row.head_sha, row.number, "dispatch"));
440
+ const cls = classifyDelta(delta, {
441
+ ...(prFiles ? { prFiles } : {}),
442
+ resolvedFiles,
443
+ });
444
+ if (cls.class === "significant")
445
+ return { skip: false };
446
+ return {
447
+ skip: true,
448
+ reason: cls.class === "empty" ? "empty-delta" : "trivial-delta",
449
+ class: cls.class,
450
+ files: cls.files.map((f) => `${f.filename} (${f.reason})`),
451
+ };
452
+ }
453
+ catch (err) {
454
+ log(`dispatch: #${row.number} delta classification failed — full round`, err);
455
+ deps.store.logEvent("dispatch_classifier_failed", row.number, { error: String(err) });
456
+ return { skip: false };
457
+ }
458
+ }
459
+ /** Consume both triggers at dispatch: the label and the team review request.
460
+ * Both go through the adapter (and therefore the dry-run gate); failures are
461
+ * logged, never fatal — the reconcile poll re-derives the truth. Returns the
462
+ * triggers really consumed: one the gate recorded instead of executing (dry
463
+ * run) or a guard refused is still on GitHub, and is logged as such. */
464
+ async function consumeTriggers(deps, pr,
465
+ /** The no-new-evidence skip consumes the triggers without starting
466
+ * a round, so it must not paint the PR "AI review in progress". */
467
+ opts = {}) {
468
+ const consumed = [];
469
+ const log = deps.log ?? (() => { });
470
+ /** One trigger: consumed, already gone (consumed too), not executed, or
471
+ * failed — logged. */
472
+ const consume = async (which, what, op) => {
473
+ try {
474
+ const result = await op();
475
+ if (result?.executed === false) {
476
+ deps.store.logEvent("trigger_consume_not_executed", pr, {
477
+ which,
478
+ ...(result.dryRunFile ? { dry_run: true } : {}),
479
+ ...(result.blocked ? { blocked: result.blocked } : {}),
480
+ });
481
+ return;
482
+ }
483
+ consumed.push(which);
484
+ }
485
+ catch (err) {
486
+ if (alreadyConsumed(err)) {
487
+ consumed.push(which);
488
+ deps.store.logEvent("trigger_already_consumed", pr, { which });
489
+ }
490
+ else {
491
+ log(`dispatch: ${what} failed on #${pr}`, err);
492
+ deps.store.logEvent("trigger_consume_failed", pr, { which, error: String(err) });
493
+ }
494
+ }
495
+ };
496
+ await consume("label", "label removal", () => deps.adapter.removeLabel(pr, deps.cfg.github.trigger_label));
497
+ await consume("team_request", "team-request DELETE", () => deps.adapter.deleteTeamReviewRequest(pr));
498
+ if (opts.markInProgress !== false) {
499
+ try {
500
+ await deps.adapter.addLabel(pr, deps.cfg.github.in_progress_label);
501
+ }
502
+ catch (err) {
503
+ log(`dispatch: in-progress label failed on #${pr}`, err);
504
+ }
505
+ }
506
+ return consumed;
507
+ }
508
+ /**
509
+ * A withheld approval's retry round that ended without deciding anything —
510
+ * cancelled by a restart's orphan sweep, a self-requeue or an operator, or
511
+ * held FAILED_LOUD until the operator's `u` — still owes the approval:
512
+ * dispatch spent the mark at that round's handoff, and the round that would
513
+ * have re-decided it never did. The mark is re-recorded here from the spend's
514
+ * own record (`approval_retry_dispatched`), so the round that replaces it
515
+ * runs instead of being skipped as no new evidence. A round that decided (a
516
+ * verdict, a guard-2 no-op) or is still running owes nothing; neither does a
517
+ * spend at another head or one a later round has overtaken.
518
+ *
519
+ * Only a round that PROVABLY posted nothing is restored: a restored mark
520
+ * takes the replacement round past guard 2's daemon half, and the approval
521
+ * route has no guard 2 of its own, so a round whose APPROVE went out would
522
+ * post a second one. So the round has no posting receipt but a guard-2
523
+ * refusal (a `prepared` or `deferred` one is unknown, never "not posted"), its
524
+ * row records no posting, and the reviewed-head mark has not been stamped
525
+ * since it started.
526
+ */
527
+ function restoreInterruptedApprovalRetry(store, row, postingReceiptDir) {
528
+ const spent = store.db
529
+ .prepare("SELECT payload FROM events WHERE pr = ? AND kind = 'approval_retry_dispatched' ORDER BY id DESC LIMIT 1")
530
+ .get(row.number);
531
+ if (!spent)
532
+ return undefined;
533
+ let record;
534
+ try {
535
+ record = JSON.parse(spent.payload);
536
+ }
537
+ catch {
538
+ return undefined;
539
+ }
540
+ if (record.head_sha !== row.head_sha || typeof record.round_id !== "number")
541
+ return undefined;
542
+ const round = store.roundsFor(row.number).find((r) => r.id === record.round_id);
543
+ if (!round || round.status === "running" || round.status === "done")
544
+ return undefined;
545
+ if (round.status === "cancelled" && round.cancel_cause === "no_new_evidence")
546
+ return undefined;
547
+ if (store.hasLaterRound(row.number, round.round_no))
548
+ return undefined;
549
+ // A receipt file that cannot be read is unknown too.
550
+ if (existsSync(postingReceiptPath(postingReceiptDir, row.number, round.round_no))) {
551
+ const receipt = readPostingReceipt(postingReceiptDir, row.number, round.round_no);
552
+ if (receipt?.status !== "skipped")
553
+ return undefined;
554
+ }
555
+ if (round.posted_event || round.gh_review_id !== null)
556
+ return undefined;
557
+ const mark = store.reviewedHead(row.number);
558
+ if (mark && mark.reviewed_at >= round.started_at)
559
+ return undefined;
560
+ store.recordApprovalWithheld(row.number, {
561
+ head_sha: row.head_sha,
562
+ ci_state: typeof record.ci_state === "string" ? record.ci_state : null,
563
+ reason: typeof record.reason === "string" ? record.reason : "red-ci",
564
+ });
565
+ store.logEvent("approval_retry_restored", row.number, {
566
+ head_sha: row.head_sha,
567
+ round_id: round.id,
568
+ round_no: round.round_no,
569
+ round_status: round.status,
570
+ cancel_cause: round.cancel_cause ?? null,
571
+ });
572
+ return store.approvalWithheld(row.number);
573
+ }
574
+ /**
575
+ * Did a webhook move this PR while dispatch awaited GitHub? The skip paths
576
+ * decide from the row they read, then await trigger consumption (two GitHub
577
+ * round-trips) before settling the PR back onto its standing verdict. A push
578
+ * or a fresh review request applied in that window updates the QUEUED row —
579
+ * nothing records a successor for a row that is not `reviewing` — and
580
+ * settling it would erase that newer review debt: the stale standing verdict
581
+ * replayed, the new trigger cleared. So a moved row is left queued for the
582
+ * next pump, which decides again from what it now says. A row that left the
583
+ * queue (a close, the operator) is not settled either: it is no longer this
584
+ * dispatch's to decide. Returns the reason, or null when the row still says
585
+ * what the decision read.
586
+ */
587
+ function movedDuringConsume(store, row) {
588
+ const now = store.getPr(row.number);
589
+ if (!now)
590
+ return null;
591
+ if (now.state !== "queued")
592
+ return "left_queue";
593
+ if (now.head_sha !== row.head_sha)
594
+ return "head_moved";
595
+ // A person's ask on a queued row keeps its trigger time and upgrades only
596
+ // the provenance — a re-request the system short-circuit must not settle.
597
+ if (now.trigger_ts !== row.trigger_ts || now.trigger_provenance !== row.trigger_provenance)
598
+ return "retriggered";
599
+ return null;
600
+ }
601
+ function logSupersededSettlement(deps, row, path, reason) {
602
+ const now = deps.store.getPr(row.number);
603
+ deps.store.logEvent("dispatch_settlement_superseded", row.number, {
604
+ path,
605
+ reason,
606
+ decided_head_sha: row.head_sha,
607
+ decided_trigger_ts: row.trigger_ts,
608
+ head_sha: now?.head_sha ?? null,
609
+ trigger_ts: now?.trigger_ts ?? null,
610
+ });
611
+ deps.log?.(`dispatch: #${row.number} ${path} not settled — the PR moved while its triggers were consumed (${reason}); left queued`);
612
+ }
613
+ export { ALL_PASSES } from "@quickchatai/pi-quickreview/src/passes.js";
614
+ // The review planner moved to packages/pi-plugin/src/planner.ts (the review core); re-exported so
615
+ // the app's importers keep their one module.
616
+ export { addTierOf, buildPassPlan, buildRoundOrder, decideFollowUp, followUpSweepOwed, LEAN_ADDABLE_PASSES, leanFollowUpPlan, lensesOf, optionalOrderOf, planFollowUp, renderDeltaPatch, reviewedHeadOf, roundDeltaOf, slowestFirst, sweepOrderOf, validatorTier, } from "@quickchatai/pi-quickreview/src/planner.js";
617
+ /** What the round row records of a plan — the same projection on the
618
+ * normal path and on the patch-write fallback, so the stored round and the
619
+ * order always describe one plan. A follow-up round's optional rows ride
620
+ * the addable record, so the executed-pass telemetry reads a run one as
621
+ * offered work rather than an unknown addition. */
622
+ function recordedPlan(cfg, plan, optionalPlan = []) {
623
+ const plannedPasses = plan.pass_plan;
624
+ const optional = optionalPlan.map((p) => ({
625
+ lens: p.lens,
626
+ tier: p.tier,
627
+ optional: true,
628
+ raisable_to: p.raisable_to,
629
+ }));
630
+ return {
631
+ plannedPasses,
632
+ lenses: lensesOf(plannedPasses),
633
+ addablePasses: [
634
+ ...plan.addable_lenses.map((lens) => ({ lens, tier: addTierOf(cfg, lens) })),
635
+ ...optional,
636
+ ],
637
+ // Null when the profile switched the validator off: the round had no
638
+ // validator, and recording the configured tier would say it did.
639
+ validateTier: plannedPasses.find((p) => p.lens === VALIDATE_PASS)?.tier ?? null,
640
+ };
641
+ }
642
+ // A reconcile, webhook and periodic tick may all ask for a pump at once. The
643
+ // store is the daemon-instance identity; joiners wait for the active pump and
644
+ // return no duplicate orders. This closes the race where two pumps both saw a
645
+ // queued PR/capacity slot and created two rounds for it.
646
+ const activePumps = new WeakMap();
647
+ /** One serialized dispatch pump: drain the queue up to the concurrency cap. */
648
+ export async function dispatchOnce(deps) {
649
+ const active = activePumps.get(deps.store);
650
+ if (active) {
651
+ await active;
652
+ return [];
653
+ }
654
+ const pump = dispatchUnlocked(deps);
655
+ activePumps.set(deps.store, pump);
656
+ try {
657
+ return await pump;
658
+ }
659
+ finally {
660
+ if (activePumps.get(deps.store) === pump)
661
+ activePumps.delete(deps.store);
662
+ }
663
+ }
664
+ async function dispatchUnlocked(deps) {
665
+ const { store, cfg } = deps;
666
+ const eco = deps.ecosystem ?? DJANGO_ECOSYSTEM;
667
+ const now = deps.now ?? (() => new Date());
668
+ const render = deps.renderPrompt ?? ((o, _m) => JSON.stringify(o, null, 2));
669
+ const worktreeFor = deps.worktreeFor ?? defaultWorktree;
670
+ const ledgerCarry = deps.ledgerCarry ?? (() => ({ settled: "" }));
671
+ const log = deps.log ?? (() => { });
672
+ // Deploy-window drain: the pump starts NOTHING while draining.
673
+ // In-flight rounds keep running to their natural end; queued rows simply
674
+ // wait and dispatch when the drain lifts. The refusal stays quiet at the
675
+ // 15s tick — the drain itself is loud (status.json, qcr status, the board
676
+ // banner, the drain_on event).
677
+ if (store.drainState().draining)
678
+ return [];
679
+ const slots = capacity(store, cfg);
680
+ if (slots <= 0)
681
+ return [];
682
+ const dispatched = [];
683
+ /** Why this queued PR may not dispatch now, or null. */
684
+ const heldBack = (row) => {
685
+ // One PR = one worktree = at most one round in flight. A queued row with a
686
+ // round still running (requeue raced the verdict) waits for the next pump —
687
+ // two rounds sharing wt/pr-<n> fight over the git index.
688
+ if (store.hasRunningRound(row.number))
689
+ return "skipped — a round is already running";
690
+ const failure = store.openReviewFailure(row.number);
691
+ if (failure) {
692
+ const action = failure.next_action === "retry_wait" && failure.next_retry_at
693
+ ? `automatic retry at ${failure.next_retry_at}`
694
+ : "operator u required";
695
+ return `held FAILED_LOUD (${failure.incident_id}); ${action}`;
696
+ }
697
+ const active = store.blockingSessionCommand(`pr-${row.number}`);
698
+ if (active)
699
+ return `skipped — session command ${active.id} is ${active.status}`;
700
+ // The operator holds the session (src/takeover.ts): the round waits for the release.
701
+ // A debug lease (src/debuground.ts) is armed for exactly this round: it goes out,
702
+ // and the worker host hands it to the debug pi in place of a worker.
703
+ const lease = store.hostLease(row.number);
704
+ if (lease && lease.kind !== "debug")
705
+ return `skipped — under a ${lease.kind} lease (${lease.holder})`;
706
+ if (store.hasHeldWakes(row.number))
707
+ return "skipped — held conversational wakes must drain first";
708
+ return null;
709
+ };
710
+ const eligible = queuedInOrder(store).filter((row) => {
711
+ const why = heldBack(row);
712
+ if (why)
713
+ log(`dispatch: #${row.number} ${why}`);
714
+ return why === null;
715
+ });
716
+ // A slot is spent by a round that went running — handed off, or retained
717
+ // behind its durable command — never by a PR this pump skipped, settled or
718
+ // failed to prepare: one PR failing every tick must not starve the queue
719
+ // behind it.
720
+ let started = 0;
721
+ for (const queued of eligible) {
722
+ if (started >= slots)
723
+ break;
724
+ // Eligibility holds when the work starts, not only when the pump listed
725
+ // the queue: every PR before this one awaited GitHub, and a drain, a
726
+ // close, a pause or a fresh hold may have landed meanwhile. The row is
727
+ // read again, so the round is decided from what it says now.
728
+ if (store.drainState().draining)
729
+ break;
730
+ const row = store.getPr(queued.number);
731
+ if (!row || row.state !== "queued" || row.paused) {
732
+ log(`dispatch: #${queued.number} skipped — no longer queued (${row?.state ?? "gone"})`);
733
+ continue;
734
+ }
735
+ const why = heldBack(row);
736
+ if (why) {
737
+ log(`dispatch: #${row.number} ${why}`);
738
+ continue;
739
+ }
740
+ let provisionalRoundId;
741
+ // The round's memory window (opened before the seed, below).
742
+ let endMemoryWindow = () => { };
743
+ // Spends the withheld-approval mark this round inherits (set below).
744
+ let spendWithholding = () => { };
745
+ // Whether this dispatch painted the PR "in progress" (trigger consumption
746
+ // for a round, below): a failure before any round owns the review takes
747
+ // the label off again.
748
+ let markedInProgress = false;
749
+ try {
750
+ // Backfilled (imported) rounds are excluded: a historical review must
751
+ // never flip a still-open PR's next live round to "re-review", whose
752
+ // template references a settled digest that never existed.
753
+ const priorRounds = store.priorRoundCount(row.number);
754
+ const roundType = roundTypeFor(row, priorRounds);
755
+ // Guard 2, daemon half: a tick with nothing new to look at is a
756
+ // no-op. The triggers are still consumed (leaving them would re-queue the
757
+ // PR on the next reconcile, forever) and the PR returns to the state its
758
+ // last verdict put it in — that verdict still stands.
759
+ //
760
+ // The mark is read through the one standing-review interpretation
761
+ // (standing-review.ts): the shell stamps `posted_event` and the watcher
762
+ // may never enrich `verdict`, so `verdict` alone can read null for a
763
+ // standing REQUEST_CHANGES. Guard 2 keys on head + author reply and
764
+ // nothing else, so THAT is the test here — provenance (a label, a team
765
+ // re-request, a reconcile re-derivation) is a scheduling signal and
766
+ // never widens the posting boundary. If the standing verdict cannot be
767
+ // read off the mark (a COMMENT-only row), no round runs either — guard
768
+ // 2 would still refuse it — but nothing is guessed: the PR lands in
769
+ // `tracked` with a precise anomaly instead of an invented verdict.
770
+ const mark = store.reviewedHead(row.number);
771
+ const standingInfo = standingReview(mark);
772
+ const standing = standingInfo.kind === "decided" ? standingInfo.verdict : null;
773
+ // A withholding standing at THIS head is new evidence of its
774
+ // own. The last round decided "no blockers, approval withheld for CI",
775
+ // and something re-queued the PR — CI going green (cigate.ts) or a
776
+ // person. Skipping here would leave that PR waiting for a verdict that is
777
+ // never re-attempted, which is the stuck-PR failure the gate must not
778
+ // create.
779
+ const withheld = store.approvalWithheld(row.number) ??
780
+ restoreInterruptedApprovalRetry(store, row, deps.postingReceiptDir ?? join(sandboxRoot(), "state", "posting-receipts"));
781
+ const withheldHere = withheld?.head_sha === row.head_sha;
782
+ // The round about to run re-decides the verdict from scratch, so the
783
+ // withholding it inherits is spent — once the round has a durable owner
784
+ // (the session handoff), never before: a failure before the handoff
785
+ // leaves the mark standing, so the next pump still owes the approval
786
+ // retry and the reconcile backstop still sees it. Only the mark read
787
+ // here is spent; a new one is recorded if this round's approval is
788
+ // withheld again (guards.ts guard 4, or the verdict's
789
+ // `approval_withheld`).
790
+ let withholdingSpent = false;
791
+ spendWithholding = (roundId) => {
792
+ if (!withheld || withholdingSpent)
793
+ return;
794
+ store.db.transaction(() => {
795
+ const current = store.approvalWithheld(row.number);
796
+ if (current &&
797
+ current.head_sha === withheld.head_sha &&
798
+ current.created_at === withheld.created_at)
799
+ store.clearApprovalWithheld(row.number);
800
+ // The spend's own record: what `restoreInterruptedApprovalRetry`
801
+ // re-records should this round end without deciding.
802
+ if (withheldHere)
803
+ store.logEvent("approval_retry_dispatched", row.number, {
804
+ head_sha: row.head_sha,
805
+ round_id: roundId,
806
+ ci_state: withheld.ci_state,
807
+ reason: withheld.reason,
808
+ });
809
+ })();
810
+ withholdingSpent = true;
811
+ };
812
+ if (cfg.guards.skip_unchanged_head && !withheldHere && !hasNewEvidence(mark, row.head_sha)) {
813
+ // Captured BEFORE the transition below clears them: current PR state
814
+ // is not historical evidence (a later failure overwrites the
815
+ // provenance).
816
+ const decision = {
817
+ stage: "dispatch",
818
+ head_sha: row.head_sha,
819
+ trigger_ts: row.trigger_ts,
820
+ trigger_provenance: row.trigger_provenance,
821
+ standing_verdict: standing,
822
+ standing_source: standingInfo.kind === "decided" ? standingInfo.source : null,
823
+ // Duplicate of standing_verdict, for readers of the older payload shape.
824
+ last_verdict: standing,
825
+ reviewed_at: mark.reviewed_at,
826
+ mark: snapshotMark(mark),
827
+ };
828
+ await consumeTriggers(deps, row.number, { markInProgress: false });
829
+ // An author reply landing during the consumption is new evidence the
830
+ // skip did not see: the next pump runs the round it now owes.
831
+ const movedSkip = movedDuringConsume(store, row) ??
832
+ (hasNewEvidence(store.reviewedHead(row.number), row.head_sha) ? "new_evidence" : null);
833
+ if (movedSkip) {
834
+ logSupersededSettlement(deps, row, "no-new-evidence skip", movedSkip);
835
+ continue;
836
+ }
837
+ store.logEvent("round_skipped_no_new_evidence", row.number, decision);
838
+ // The head this skip settles is reviewed already: a successor owed at
839
+ // it is answered, and is not promoted again on every reconcile pass.
840
+ store.settleSuccessor(row.number, row.head_sha);
841
+ if (standingInfo.kind === "unknown") {
842
+ store.logAnomaly("standing_review_unknown", row.number, null, `#${row.number} was re-triggered at ${row.head_sha.slice(0, 7)}, which is already reviewed with no author reply since, but the reviewed-head mark carries no verdict (posted_event=${standingInfo.posted_event ?? "null"}). No round was run — posting guard 2 would refuse its review — and no verdict was guessed; the PR is tracked until the author pushes or replies.`);
843
+ }
844
+ const restored = restoreStandingLifecycle(store, row.number, standing, {
845
+ botLogin: botAccountLogin(cfg.github),
846
+ now,
847
+ reason: standing ? "no-new-evidence" : "no-new-evidence:standing-unknown",
848
+ });
849
+ log(`dispatch: #${row.number} skipped — no new evidence at ${row.head_sha.slice(0, 7)}; standing ${standing ?? "review (verdict unknown)"} stands, PR ${restored}`);
850
+ continue;
851
+ }
852
+ // A SYSTEM-provenance re-trigger (push / CI / staleness at an existing
853
+ // trigger state — never a fresh human ask, which applyEvent stamps
854
+ // "human") consults the recheck delta classifier before paying for a
855
+ // full round. Empty/trivial delta since the reviewed head ⇒ consume the
856
+ // trigger and leave state as an uphold would; significant ⇒ the full
857
+ // round below runs. First reviews never short-circuit, and a withheld
858
+ // approval at this head is its own new evidence: that round must run so
859
+ // the approval can finally post.
860
+ if (roundType !== "first" &&
861
+ (standing === "APPROVE" || standing === "REQUEST_CHANGES") &&
862
+ mark?.head_sha &&
863
+ !withheldHere &&
864
+ row.trigger_provenance === "system") {
865
+ const sc = await classifySystemRetrigger(deps, row, mark);
866
+ if (sc.skip) {
867
+ await consumeTriggers(deps, row.number, { markInProgress: false });
868
+ const movedShort = movedDuringConsume(store, row);
869
+ if (movedShort) {
870
+ logSupersededSettlement(deps, row, "system short-circuit", movedShort);
871
+ continue;
872
+ }
873
+ if (row.head_sha !== mark.head_sha) {
874
+ // Uphold semantics, daemon-side: a never-posted (withheld) approval
875
+ // follows the head on a trivial/empty delta.
876
+ carryWithheldMark(store, row.number, row.head_sha, `dispatch-short-circuit:${sc.reason}`, (m) => log(m));
877
+ if (standing === "APPROVE") {
878
+ // This head's push question is answered (trivially). Claim the
879
+ // same fingerprint the reconcile push-backstop checks so it is
880
+ // not re-asked every sweep; INSERT OR IGNORE means a claim a
881
+ // real recheck already holds is left untouched. No wake is sent,
882
+ // so no receipt can exist: never the undelivered sweep's to
883
+ // release.
884
+ store.claimRecheck(row.number, recheckFingerprint({ trigger: "push" }, row.head_sha), {
885
+ head_sha: row.head_sha,
886
+ trigger: "push",
887
+ reason: `dispatch-short-circuit:${sc.reason}`,
888
+ nudge: false,
889
+ });
890
+ }
891
+ }
892
+ // The trivial delta answers this head: a successor owed at it is
893
+ // settled, so reconcile does not promote it again on every pass.
894
+ store.settleSuccessor(row.number, row.head_sha);
895
+ log(`dispatch: #${row.number} short-circuited (${sc.reason}) — system re-trigger, standing ${standing}`);
896
+ store.logEvent("dispatch_short_circuit", row.number, {
897
+ reason: sc.reason,
898
+ delta_class: sc.class,
899
+ head_sha: row.head_sha,
900
+ last_reviewed_sha: mark.head_sha,
901
+ standing_verdict: standing,
902
+ files: sc.files,
903
+ });
904
+ applyEvent(store, row.number, { type: "verdict", verdict: standing }, {}, {
905
+ botLogin: botAccountLogin(cfg.github),
906
+ now,
907
+ reason: `dispatch-short-circuit:${sc.reason}`,
908
+ });
909
+ continue;
910
+ }
911
+ }
912
+ markedInProgress = true;
913
+ const consumed = await consumeTriggers(deps, row.number);
914
+ // The branch this PR targets (base freshness) and its body
915
+ // (pass-intent's artifact + substance bar) come off one getPr read. A
916
+ // failed read degrades to "no base facts, no intent lens", never a
917
+ // failed round.
918
+ let baseRef = "";
919
+ let prBody;
920
+ try {
921
+ const ref = await deps.adapter.getPr(row.number);
922
+ baseRef = ref.base_ref;
923
+ prBody = ref.body;
924
+ }
925
+ catch (err) {
926
+ log(`dispatch: base ref lookup failed on #${row.number}`, err);
927
+ }
928
+ // Prepare the tree BEFORE building the order, so the prompt cites the sha
929
+ // the passes will actually read. `row.head_sha` is a webhook/reconcile
930
+ // snapshot; a push landing between it and the fetch would otherwise have
931
+ // passes cite one sha while reading another tree, with anchors computed
932
+ // against a diff the review is not about.
933
+ //
934
+ // And BEFORE the changed-files read: every file-derived fact of the round
935
+ // — the migration gate, the pass plan and sweep focus, the collision
936
+ // pre-check, the diffstat — must describe the head the tree holds (read
937
+ // first, a migration a racing push added would get no gate and no
938
+ // collision check). The files API is by PR number, so read after the
939
+ // fetch it lists at least the prepared head.
940
+ const tree = deps.prepareWorktree
941
+ ? await deps.prepareWorktree(row.number, baseRef, row.head_sha)
942
+ : undefined;
943
+ // The head this round reviews — the prepared tree's, else the row's —
944
+ // decided once, so the round row, the prior findings, the pre-check,
945
+ // the recovery identity and the order all name the same one.
946
+ const preparedHead = reviewedHeadOf(tree, row);
947
+ const files = await deps.adapter.listPrFiles(row.number);
948
+ const gate = eco.migrationGate(files);
949
+ // The repo's written style guide, if any: the style lens runs only on
950
+ // one (`[review] style_lens`). Total — an unreadable tree is no guide.
951
+ // Read off the prepared tree BEFORE the plan, so the plan is built once
952
+ // and the round row records exactly the order's plan.
953
+ const styleGuide = lensEnabled(cfg.lenses, STYLE_PASS, cfg.review.style_lens)
954
+ ? findStyleGuide(resolve(sandboxRoot(), worktreeFor(row.number)))
955
+ : [];
956
+ // The linters a CI job of the repo runs: lint conformance runs only on
957
+ // them (`[review] lint_conformance`). Total — an unreadable tree is none.
958
+ const ciLinters = lensEnabled(cfg.lenses, LINT_CONFORMANCE_PASS, cfg.review.lint_conformance)
959
+ ? findCiLinters(resolve(sandboxRoot(), worktreeFor(row.number)))
960
+ : [];
961
+ // The plan is pure and deterministic in `files` + the PR body + the
962
+ // style guide + the linters: built ONCE here and handed to
963
+ // buildRoundOrder, so the round row and the order never disagree.
964
+ const fullPlan = buildPassPlan(cfg, files, { title: row.title, body: prBody }, eco, deps.heuristics, styleGuide, ciLinters);
965
+ // Follow-up rounds (docs/follow-up-rounds.md): every round after the
966
+ // first reviews the PR-owned change since the last reviewed head — or,
967
+ // on any doubt, runs a full re-review. `files` stays the WHOLE
968
+ // PR for every per-PR fact (the migration gate, the pre-check, the
969
+ // directive escalation, the diffstat, dependency sources, memory and
970
+ // lessons); only the plan and the coverage check read the delta.
971
+ let followUp = null;
972
+ let fullRound = null;
973
+ if (roundType === "re-review") {
974
+ const decided = await decideFollowUp({
975
+ cfg: deps.cfg,
976
+ history: deps.store,
977
+ ...(deps.prOwnedDelta ? { prOwnedDelta: deps.prOwnedDelta } : {}),
978
+ ...(deps.heuristics ? { heuristics: deps.heuristics } : {}),
979
+ }, {
980
+ pr: row.number,
981
+ markHead: mark?.head_sha,
982
+ tree,
983
+ files,
984
+ fullPlan,
985
+ standing,
986
+ // Read before this dispatch spends it: a withheld approval is
987
+ // one GitHub never saw, so the sweep stays owed behind it.
988
+ approvalWithheld: withheld !== undefined,
989
+ ecosystem: eco,
990
+ styleGuide,
991
+ ciLinters,
992
+ });
993
+ if (decided.kind === "delta")
994
+ followUp = decided.decision;
995
+ else
996
+ fullRound = {
997
+ reason: decided.reason,
998
+ ...(decided.detail ? { detail: decided.detail } : {}),
999
+ };
1000
+ }
1001
+ let passPolicy = followUp ? followUp.plan : fullPlan;
1002
+ let { plannedPasses, lenses, addablePasses, validateTier } = recordedPlan(cfg, passPolicy, followUp?.optionalPlan);
1003
+ // The PR must still be queued when its round row is written — read in
1004
+ // the same turn as `startRound`. Preparation awaited GitHub and git, and
1005
+ // a verdict settling an older round (a late, orphan-swept one), a close
1006
+ // or the operator may have moved the row meanwhile; then this round is
1007
+ // nobody's work and no row is written. The in-progress label the
1008
+ // trigger consumption put on comes off again (best effort).
1009
+ const live = store.getPr(row.number);
1010
+ if (live?.state !== "queued") {
1011
+ log(`dispatch: #${row.number} left the queue (${live?.state ?? "gone"}) while its round was prepared — no round started`);
1012
+ store.logEvent("dispatch_abandoned", row.number, {
1013
+ state: live?.state ?? null,
1014
+ head_sha: preparedHead,
1015
+ });
1016
+ try {
1017
+ await deps.adapter.removeLabel(row.number, cfg.github.in_progress_label);
1018
+ }
1019
+ catch (err) {
1020
+ log(`dispatch: #${row.number} in-progress label not removed`, err);
1021
+ }
1022
+ continue;
1023
+ }
1024
+ // Diffstat comes free off the files list dispatch already fetched — no
1025
+ // extra GitHub call in the hot path. trigger_ts is copied off pr_state
1026
+ // here because the `dispatched` transition clears it moments later.
1027
+ // validate_tier is recorded so a later audit can tell which tier judged
1028
+ // which finding.
1029
+ const diffstat = diffstatOf(files);
1030
+ const roundId = store.startRound(row.number, roundType, lenses, cfg.daemon.round_deadline_min, {
1031
+ ...diffstat,
1032
+ trigger_ts: row.trigger_ts,
1033
+ validate_tier: validateTier,
1034
+ pass_plan: plannedPasses,
1035
+ addable_passes: addablePasses,
1036
+ head_sha: preparedHead,
1037
+ ...(tree?.merge_base ? { merge_base: tree.merge_base } : {}),
1038
+ ...(roundType === "re-review"
1039
+ ? followUp
1040
+ ? {
1041
+ scope: "delta",
1042
+ delta_base_sha: followUp.fromSha,
1043
+ delta_lines: followUp.delta.lines,
1044
+ delta_files: followUp.delta.files.length,
1045
+ sweep_owed: followUp.sweepPlan !== undefined,
1046
+ ...(followUp.sweepPlan
1047
+ ? { sweep_plan: followUp.sweepPlan }
1048
+ : { sweep_status: "not_owed" }),
1049
+ }
1050
+ : { scope: "full" }
1051
+ : {}),
1052
+ });
1053
+ provisionalRoundId = roundId;
1054
+ const roundNo = store.db.prepare("SELECT round_no FROM rounds WHERE id = ?").get(roundId).round_no;
1055
+ // The delta as one patch file, beside the round's other state
1056
+ // files. A write that fails falls the round back to a full re-review —
1057
+ // the follow-up prompt names the file, and a missing one is never a
1058
+ // quieter round.
1059
+ let roundDelta;
1060
+ if (followUp) {
1061
+ const patchFile = resolve(sandboxRoot(), "state", `pr-${row.number}-round-${roundNo}-delta.patch`);
1062
+ try {
1063
+ mkdirSync(resolve(sandboxRoot(), "state"), { recursive: true });
1064
+ writeFileSync(`${patchFile}.tmp`, renderDeltaPatch(followUp.delta.files));
1065
+ renameSync(`${patchFile}.tmp`, patchFile);
1066
+ roundDelta = roundDeltaOf(followUp, patchFile);
1067
+ }
1068
+ catch (err) {
1069
+ log(`dispatch: #${row.number} delta patch not written — full round`, err);
1070
+ fullRound = {
1071
+ reason: "patch-write-failed",
1072
+ detail: { error: err instanceof Error ? err.message : String(err) },
1073
+ };
1074
+ followUp = null;
1075
+ passPolicy = fullPlan;
1076
+ ({ plannedPasses, lenses, addablePasses, validateTier } = recordedPlan(cfg, passPolicy));
1077
+ store.setRoundPlan(roundId, lenses, {
1078
+ validate_tier: validateTier,
1079
+ pass_plan: plannedPasses,
1080
+ addable_passes: addablePasses,
1081
+ });
1082
+ store.setRoundMeta(roundId, {
1083
+ scope: "full",
1084
+ delta_base_sha: null,
1085
+ delta_lines: null,
1086
+ delta_files: null,
1087
+ sweep_owed: null,
1088
+ sweep_plan: null,
1089
+ sweep_status: null,
1090
+ });
1091
+ }
1092
+ }
1093
+ if (fullRound) {
1094
+ store.logEvent("followup_full_round", row.number, {
1095
+ round_id: roundId,
1096
+ round_no: roundNo,
1097
+ reason: fullRound.reason,
1098
+ head_sha: preparedHead,
1099
+ last_reviewed_sha: mark?.head_sha ?? null,
1100
+ ...(fullRound.detail ?? {}),
1101
+ });
1102
+ log(`dispatch: #${row.number} round ${roundNo} runs in full (${fullRound.reason})`);
1103
+ }
1104
+ if (passPolicy.behaviour_unread)
1105
+ store.logEvent("behaviour_delta_unread", row.number, {
1106
+ round_id: roundId,
1107
+ round_no: roundNo,
1108
+ files: passPolicy.behaviour_unread,
1109
+ });
1110
+ // The prior findings this follow-up round verifies — listed in
1111
+ // prior_finding_checks and written to the round's sidecar. Total: a list
1112
+ // that cannot be assembled renders an explicit "verify from your ledger"
1113
+ // note, never an empty one.
1114
+ const priorFindings = followUp
1115
+ ? preparePriorFindings({
1116
+ store,
1117
+ findingsDir: deps.findingsDir ?? resolve(sandboxRoot(), "state", "findings"),
1118
+ stateDir: resolve(sandboxRoot(), "state"),
1119
+ log,
1120
+ }, row.number, roundNo, preparedHead)
1121
+ : null;
1122
+ const priorFindingsNote = priorFindings?.note ?? "";
1123
+ if (tree?.pull_ref_sha) {
1124
+ log(`dispatch: #${row.number} GitHub's refs/pull/${row.number}/head lags (${tree.pull_ref_sha}); reviewing the API head ${tree.head_sha}`);
1125
+ store.logEvent("pull_ref_lagged", row.number, {
1126
+ round_id: roundId,
1127
+ pull_ref: tree.pull_ref_sha,
1128
+ head: tree.head_sha,
1129
+ });
1130
+ }
1131
+ if (tree && row.head_sha && tree.head_sha !== row.head_sha) {
1132
+ log(`dispatch: #${row.number} head drift ${row.head_sha} → ${tree.head_sha}`);
1133
+ store.logEvent("head_drift", row.number, {
1134
+ round_id: roundId,
1135
+ expected: row.head_sha,
1136
+ actual: tree.head_sha,
1137
+ source: "worktree_prepare",
1138
+ });
1139
+ }
1140
+ // The integrity fingerprint — git's config/hook files and this system's
1141
+ // own policy surface under the sandbox root — taken HERE: after the
1142
+ // worktree is materialised (so the tree is the one the round will read)
1143
+ // and before the session hears about the round (so nothing it does is
1144
+ // inside the baseline). `verdicts.measureUsage` re-checks it when the
1145
+ // round ends. Best-effort in both directions: a fingerprint that cannot
1146
+ // be taken leaves the round unchecked — it never fails a round. It hashes
1147
+ // the whole policy surface (dist/, scripts/, …) asynchronously, a loop
1148
+ // turn after every file, and is BOUNDED, because this await sits inside
1149
+ // the one serialized dispatch pump: a read that never returns leaves
1150
+ // this round unchecked (no baseline), never every later PR undispatched.
1151
+ try {
1152
+ const budgetMs = Math.max(0, deps.integrityBudgetMs ?? INTEGRITY_BUDGET_MS);
1153
+ const baseline = await withinBudget((deps.integrityFingerprint ?? fingerprintRoundIntegrity)(roundIntegrityOpts(githubCloneDir(cfg.github), sandboxRoot(), worktreeFor(row.number))), budgetMs);
1154
+ if (baseline.kind === "done") {
1155
+ store.recordRoundGitConfig(roundId, baseline.value);
1156
+ }
1157
+ else {
1158
+ store.logEvent("round_integrity_unchecked", row.number, {
1159
+ round_id: roundId,
1160
+ stage: "baseline",
1161
+ reason: "budget",
1162
+ budget_ms: budgetMs,
1163
+ });
1164
+ log(`dispatch: #${row.number} integrity baseline not taken within ${budgetMs} ms — the round goes unchecked`);
1165
+ }
1166
+ }
1167
+ catch (err) {
1168
+ log(`dispatch: #${row.number} git-config fingerprint unavailable`, err);
1169
+ }
1170
+ // The ecosystem pre-check, before the session hears about the round.
1171
+ // Django delivers collisions as its
1172
+ // own COMMENT, so whatever the review concludes, the renumber never
1173
+ // rides on the verdict; a `none` ecosystem runs nothing at all.
1174
+ const precheck = await ecosystemPreCheck(deps, eco, {
1175
+ round_id: roundId,
1176
+ pr: row.number,
1177
+ files,
1178
+ head_sha: preparedHead,
1179
+ base_ref: tree?.base_ref ?? baseRef,
1180
+ base_sha: tree?.base_sha ?? "",
1181
+ });
1182
+ const migrationCollisions = precheck.collisions;
1183
+ // A degraded pre-check is an anomaly the overseer should see (same
1184
+ // channel verdicts.ts uses — an overseer inject, never a new path). The
1185
+ // round itself still runs: the prompt carries the "check collisions
1186
+ // yourself" note, so the class is covered either way. A configured-off
1187
+ // ecosystem ("none") can never reach this block — off is not degraded,
1188
+ // and the seam's outcome union keeps the two apart structurally.
1189
+ if (precheck.degraded) {
1190
+ // Durable anomaly record + the overseer wake (WAKE class) — one
1191
+ // logAnomaly call, the store's sink writes the inject.
1192
+ store.logAnomaly("migration_precheck_degraded", row.number, roundNo, "migration pre-check could not run (no base facts, or the base read failed); the round prompt un-muzzles the lens", [
1193
+ `ANOMALY (migration-precheck) — PR #${row.number} round ${roundNo}`,
1194
+ "The migration-number pre-check could not run this round (no base facts, or the base read failed) — " +
1195
+ "see the migration_precheck_skipped/failed event on this PR.",
1196
+ "The round prompt tells the session to check base-branch migration-number collisions itself, so nothing is lost — " +
1197
+ "but a recurring degraded pre-check means the base clone or worktree preparation is broken and needs a look.",
1198
+ ].join("\n"));
1199
+ }
1200
+ // A withheld collision COMMENT is an anomaly too: the round
1201
+ // still hears the collisions through the gate note, but every later
1202
+ // collision comment on this PR stays withheld until the torn ledger row
1203
+ // is repaired — a human has to know that, once.
1204
+ if (precheck.commentWithheld &&
1205
+ !store.anomalyRaised("migration_collision_comment_withheld", row.number)) {
1206
+ store.logAnomaly("migration_collision_comment_withheld", row.number, roundNo, "migration collision COMMENT withheld: the event log holds a migration_collision_comment row for this PR that cannot be read, " +
1207
+ "so a duplicate cannot be ruled out; the collisions still ride the round prompt", [
1208
+ `ANOMALY (migration-precheck) — PR #${row.number} round ${roundNo}`,
1209
+ "The migration-number pre-check found collisions but WITHHELD the GitHub comment: the event log holds a " +
1210
+ "migration_collision_comment row for this PR whose payload cannot be read, so posting could duplicate an earlier comment.",
1211
+ "The session still hears the collisions through the round prompt's gate note, so nothing is lost this round — " +
1212
+ "but EVERY collision comment on this PR stays withheld until that row is repaired or deleted " +
1213
+ `(events table, kind = 'migration_collision_comment', pr = ${row.number}; see the migration_collision_comment_withheld events).`,
1214
+ ].join("\n"));
1215
+ }
1216
+ // First-party directive edits: detect them and pick the ONE
1217
+ // teammate the escalation comment @-mentions, deterministically, before
1218
+ // the session hears about the round.
1219
+ const firstPartyDirectives = await directiveEscalationFor(deps, {
1220
+ pr: row.number,
1221
+ files,
1222
+ merge_base: tree?.merge_base ?? "",
1223
+ base_sha: tree?.base_sha ?? "",
1224
+ });
1225
+ const rawRecoveryInstruction = store.roundRecoveryInstruction(row.number, roundNo);
1226
+ const recoveryInstruction = store.roundRecoveryInstructionForDispatch(row.number, roundNo, preparedHead);
1227
+ if (rawRecoveryInstruction && !recoveryInstruction) {
1228
+ store.logEvent("round_recovery_instruction_not_applied", row.number, {
1229
+ target_round_no: roundNo,
1230
+ stored_head_sha: rawRecoveryInstruction.target_head_sha,
1231
+ prepared_head_sha: preparedHead,
1232
+ source_round_no: rawRecoveryInstruction.source_round_no,
1233
+ source_command_id: rawRecoveryInstruction.source_command_id,
1234
+ reason: "exact_successor_identity_mismatch",
1235
+ });
1236
+ }
1237
+ const recovery = recoveryInstruction
1238
+ ? {
1239
+ stage: recoveryInstruction.stage,
1240
+ source_round_no: recoveryInstruction.source_round_no,
1241
+ source_command_id: recoveryInstruction.source_command_id,
1242
+ target_head_sha: recoveryInstruction.target_head_sha,
1243
+ immediate_attempt: recoveryInstruction.immediate_attempt,
1244
+ retry_epoch: recoveryInstruction.retry_epoch,
1245
+ ...(recoveryInstruction.source_incident_id
1246
+ ? { source_incident_id: recoveryInstruction.source_incident_id }
1247
+ : {}),
1248
+ }
1249
+ : undefined;
1250
+ // Third-party sources for the passes (`[review] dependency_sources`).
1251
+ // Total by contract — a dead registry yields an empty note, never a
1252
+ // failed or delayed-past-budget round.
1253
+ let dependencyNote = "";
1254
+ if (cfg.review.dependency_sources && deps.dependencySources) {
1255
+ try {
1256
+ dependencyNote = await deps.dependencySources(resolve(sandboxRoot(), worktreeFor(row.number)), files);
1257
+ }
1258
+ catch (err) {
1259
+ log(`dispatch: #${row.number} dependency sources unavailable`, err);
1260
+ }
1261
+ }
1262
+ const built = buildRoundOrder({
1263
+ tree,
1264
+ plan: passPolicy,
1265
+ ...(followUp && roundDelta
1266
+ ? {
1267
+ followUp: {
1268
+ delta: roundDelta,
1269
+ deltaFiles: followUp.deltaFiles,
1270
+ priorFindingsNote,
1271
+ ...(priorFindings?.list ? { priorFindings: priorFindings.list } : {}),
1272
+ ...(followUp.optionalPlan ? { optionalPlan: followUp.optionalPlan } : {}),
1273
+ ...(followUp.sweepPlan ? { sweepPlan: followUp.sweepPlan } : {}),
1274
+ ...(followUp.sweepUnswept ? { sweepUnswept: followUp.sweepUnswept } : {}),
1275
+ ...(followUp.sweepBehaviourUnread
1276
+ ? { sweepBehaviourUnread: followUp.sweepBehaviourUnread }
1277
+ : {}),
1278
+ },
1279
+ }
1280
+ : {}),
1281
+ ecosystem: eco,
1282
+ ...(styleGuide.length ? { styleGuide } : {}),
1283
+ ...(ciLinters.length ? { ciLinters } : {}),
1284
+ ...(dependencyNote ? { dependencyNote } : {}),
1285
+ ...(recovery ? { recovery } : {}),
1286
+ migrationCollisions,
1287
+ ...(precheck.commentUndelivered ? { migrationCollisionsUndelivered: true } : {}),
1288
+ migrationPrecheckDegraded: precheck.degraded,
1289
+ ...(firstPartyDirectives !== undefined ? { firstPartyDirectives } : {}),
1290
+ cfg,
1291
+ row,
1292
+ files,
1293
+ roundId,
1294
+ roundNo,
1295
+ reviewNo: priorRounds + 1,
1296
+ roundType,
1297
+ worktree: worktreeFor(row.number),
1298
+ ...(() => {
1299
+ // One read of the ledger mirror per order; an explicit
1300
+ // settledDigest supplier still wins for the digest.
1301
+ const fromLedger = ledgerCarry(row.number);
1302
+ return {
1303
+ settledDigest: deps.settledDigest ? deps.settledDigest(row.number) : fromLedger.settled,
1304
+ ...(fromLedger.carry ? { ledgerCarry: fromLedger.carry } : {}),
1305
+ };
1306
+ })(),
1307
+ issuedAt: now().toISOString(),
1308
+ ...(prBody !== undefined ? { prBody } : {}),
1309
+ ...(deps.heuristics !== undefined ? { heuristics: deps.heuristics } : {}),
1310
+ // The host's lens tools, so the prompt and the task files name the ones its
1311
+ // sessions have (rule 9: `lens` / `lens_wave` on workers).
1312
+ ...(deps.hooks.lensRuntime ? { lensRuntime: deps.hooks.lensRuntime } : {}),
1313
+ });
1314
+ store.stampPostingKey(roundId, built.posting_key);
1315
+ // The round row already holds the order's plan: the style guide and the
1316
+ // linters were read before the plan was built, and the plan was built
1317
+ // once.
1318
+ // The ONE memory call this round WAITS for, placed after the
1319
+ // worktree and the order so the context is seeded from the same sha the
1320
+ // passes read. It is bounded and total: the worst case is this round's
1321
+ // prompt arriving memory-less a couple of seconds later.
1322
+ //
1323
+ // Under `[memory] delivery = "on"` the round's LESSON
1324
+ // SELECTION is awaited beside it — the prompt, the task files and the
1325
+ // lessons files are rendered from it. Its budget (≤ 2 s) runs from
1326
+ // here, inside the seed's deadline, so the round waits for the slower
1327
+ // of the two, never for their sum — but no lesson find is SENT before
1328
+ // the round's own context search was: the assembler says when it
1329
+ // issued the search, and a seed that never does (no assembler, a
1330
+ // failure before the search, an assembler that does not say) opens
1331
+ // the way when it settles. So the seed's search never queues behind a
1332
+ // lesson find on the server's one-thread embedder. Under
1333
+ // `shadow`/`off` the selection starts only after the handoff,
1334
+ // unawaited (below).
1335
+ const delivering = lessonsDelivering(deps, log);
1336
+ // No case batch starts while this round reads memory — the
1337
+ // window opens here, before the seed, and closes once the selection
1338
+ // has taken over: under `on` when the awaited selection is in; under
1339
+ // `shadow` once `start` has opened the selector's own window, after the
1340
+ // handoff (a batch started during the handoff would sit in front of the
1341
+ // selection's finds). The `finally` below is the belt.
1342
+ endMemoryWindow = openMemoryWindow(deps, row.number, log);
1343
+ let seeded;
1344
+ let selection = null;
1345
+ if (delivering) {
1346
+ let searchSent;
1347
+ const seedSearchSent = new Promise((resolveSent) => (searchSent = resolveSent));
1348
+ const seeding = seedRoundMemory(deps, built, files, () => searchSent());
1349
+ void seeding.then(() => searchSent(), () => searchSent());
1350
+ [seeded, selection] = await Promise.all([
1351
+ seeding,
1352
+ selectRoundLessons(deps, built, files, seedSearchSent),
1353
+ ]);
1354
+ endMemoryWindow();
1355
+ }
1356
+ else {
1357
+ seeded = await seedRoundMemory(deps, built, files);
1358
+ }
1359
+ const delivery = selection
1360
+ ? buildRoundDelivery({
1361
+ selections: selection.selections,
1362
+ texts: selection.texts,
1363
+ pr: row.number,
1364
+ round_no: roundNo,
1365
+ head_sha: built.head_sha,
1366
+ repo: cfg.github.repo,
1367
+ rowsInTaskFiles: cfg.review.task_files,
1368
+ })
1369
+ : null;
1370
+ // `[review] task_files` (packages/pi-plugin/src/passtasks.ts): each discovery row's
1371
+ // complete lens task, written beside the round's pass outputs so the
1372
+ // session launches lenses with a pointer instead of generating every
1373
+ // task string. Total: a write that fails leaves the order without task
1374
+ // files, and the session writes the strings itself. Under `on` each
1375
+ // row's delivered lessons ride its task file.
1376
+ // `[review] context_pack = "pack-first"`: the round's CONTEXT
1377
+ // PACK, written before the task files that point at it, and only where
1378
+ // children share a cache key (`[engine] lens_cache_group`) — an unshared
1379
+ // pack costs every lens more, not less. Total: a pack that cannot be
1380
+ // written leaves the round exactly as without one; a stale pack from an
1381
+ // earlier attempt of this round number never survives into it.
1382
+ const passesDir = resolve(sandboxRoot(), "state", `pr-${row.number}-round-${roundNo}-passes`);
1383
+ const packPath = join(passesDir, CONTEXT_PACK_FILE);
1384
+ const removeStalePack = () => {
1385
+ try {
1386
+ rmSync(packPath, { force: true });
1387
+ }
1388
+ catch (err) {
1389
+ log(`dispatch: #${row.number} stale context pack not removed`, err);
1390
+ }
1391
+ };
1392
+ let packed = built;
1393
+ if (cfg.review.task_files &&
1394
+ cfg.review.context_pack === "pack-first" &&
1395
+ cfg.engine.lens_cache_group > 0) {
1396
+ try {
1397
+ const pack = renderContextPack({
1398
+ pr: row.number,
1399
+ headSha: built.head_sha,
1400
+ mergeBase: built.merge_base,
1401
+ files,
1402
+ ...(followUp ? { deltaPatch: renderDeltaPatch(followUp.delta.files) } : {}),
1403
+ readFile: (p) => readWorktreeFileForPack(resolve(sandboxRoot(), built.worktree), p),
1404
+ maxTokens: cfg.review.context_pack_max_tokens,
1405
+ });
1406
+ mkdirSync(passesDir, { recursive: true });
1407
+ writeFileSync(`${packPath}.tmp`, pack.text);
1408
+ renameSync(`${packPath}.tmp`, packPath);
1409
+ packed = { ...built, context_pack_file: packPath };
1410
+ store.logEvent("context_pack_written", row.number, {
1411
+ round_id: roundId,
1412
+ chars: pack.chars,
1413
+ diff_files: pack.diffFiles.length,
1414
+ diff_omitted: pack.diffOmitted.length,
1415
+ full_files: pack.fullFiles.length,
1416
+ });
1417
+ }
1418
+ catch (err) {
1419
+ log(`dispatch: #${row.number} context pack not written`, err);
1420
+ // A child finds the pack beside its task file, not through the
1421
+ // order — an earlier attempt's pack left here would be read as
1422
+ // this round's.
1423
+ removeStalePack();
1424
+ }
1425
+ }
1426
+ else {
1427
+ removeStalePack();
1428
+ }
1429
+ let order = packed;
1430
+ let rowsDelivered = false;
1431
+ if (cfg.review.task_files) {
1432
+ try {
1433
+ const dir = join(passesDir, "tasks");
1434
+ const tasks = writePassTasks(dir, packed, renderGateNotes(packed, eco), delivery
1435
+ ? {
1436
+ rows: new Map(delivery.rows.map((r) => [r.row, r.lessons])),
1437
+ repo: delivery.repo,
1438
+ }
1439
+ : undefined);
1440
+ order = {
1441
+ ...packed,
1442
+ pass_task_files: tasks.map((t) => ({ row: t.row, agent: t.agent, path: t.path })),
1443
+ };
1444
+ rowsDelivered = true;
1445
+ }
1446
+ catch (err) {
1447
+ log(`dispatch: #${row.number} lens task files unavailable`, err);
1448
+ }
1449
+ // The owed pre-approval sweep's task files — its own
1450
+ // directory and row index (`row` indexes `sweep_plan`), the whole PR's
1451
+ // full plan rendered exactly as a full round's. No lesson rows ride
1452
+ // them: lesson selection covers `pass_plan` rows only. Total, as above.
1453
+ // With no sweep owed, an earlier attempt's sweep dir is removed, so
1454
+ // nothing stale can be launched from it.
1455
+ const sweepDir = join(passesDir, "tasks-sweep");
1456
+ if (!built.sweep_plan || built.sweep_plan.length === 0) {
1457
+ try {
1458
+ rmSync(sweepDir, { recursive: true, force: true });
1459
+ }
1460
+ catch (err) {
1461
+ log(`dispatch: #${row.number} stale sweep task dir not removed`, err);
1462
+ }
1463
+ }
1464
+ // The optional rows' task files — their own directory and
1465
+ // row index (`row` indexes `optional_plan`), rendered from the
1466
+ // follow-up order itself, so they carry the change since the last
1467
+ // review exactly as the core rows do. Total, as above.
1468
+ const optionalDir = join(passesDir, "tasks-optional");
1469
+ if (!built.optional_plan || built.optional_plan.length === 0) {
1470
+ try {
1471
+ rmSync(optionalDir, { recursive: true, force: true });
1472
+ }
1473
+ catch (err) {
1474
+ log(`dispatch: #${row.number} stale optional task dir not removed`, err);
1475
+ }
1476
+ }
1477
+ else {
1478
+ try {
1479
+ const optionalOrder = optionalOrderOf(packed, built.optional_plan);
1480
+ const optionalTasks = writePassTasks(optionalDir, optionalOrder, renderGateNotes(optionalOrder, eco));
1481
+ order = {
1482
+ ...order,
1483
+ optional_task_files: optionalTasks.map((t) => ({
1484
+ row: t.row,
1485
+ agent: t.agent,
1486
+ path: t.path,
1487
+ })),
1488
+ };
1489
+ }
1490
+ catch (err) {
1491
+ log(`dispatch: #${row.number} optional task files unavailable`, err);
1492
+ }
1493
+ }
1494
+ if (built.sweep_plan && built.sweep_plan.length > 0) {
1495
+ try {
1496
+ const sweepOrder = sweepOrderOf(packed, built.sweep_plan);
1497
+ const sweepTasks = writePassTasks(sweepDir, sweepOrder, renderGateNotes(sweepOrder, eco));
1498
+ order = {
1499
+ ...order,
1500
+ sweep_task_files: sweepTasks.map((t) => ({
1501
+ row: t.row,
1502
+ agent: t.agent,
1503
+ path: t.path,
1504
+ })),
1505
+ };
1506
+ }
1507
+ catch (err) {
1508
+ log(`dispatch: #${row.number} sweep task files unavailable`, err);
1509
+ }
1510
+ }
1511
+ }
1512
+ const memory = deliverRoundLessons(deps, {
1513
+ seeded,
1514
+ // A row's lessons travel only in its task file: none written, none
1515
+ // delivered — the index must not say otherwise.
1516
+ delivery: delivery && !rowsDelivered ? { ...delivery, rows: [] } : delivery,
1517
+ dir: join(passesDir, "lessons"),
1518
+ });
1519
+ // A PR closed while its round was prepared gets no session and no
1520
+ // prompt: the provisional round is discarded like any failed
1521
+ // preparation (the close hook already archived the session).
1522
+ if (store.getPr(row.number)?.state === "closed")
1523
+ throw new Error(`#${row.number} closed while its round was prepared — not handed off`);
1524
+ // The round is provisional until the worktree/session/prompt handoff
1525
+ // completes; publishing reviewing/running state any earlier would leave
1526
+ // an orphan round whenever worktree preparation fails.
1527
+ const prompt = render(order, memory);
1528
+ // Rendering and optional preparation may have awaited or invoked callers
1529
+ // that changed the operator hold. Re-admit at the actual handoff seam.
1530
+ const admitted = store.getPr(row.number);
1531
+ if (!admitted || admitted.state === "closed" || admitted.paused)
1532
+ throw new Error(`#${row.number} closed or paused during preparation — not handed off`);
1533
+ await deps.hooks.dispatchRound(order, prompt);
1534
+ // Record the receipt token this round's prompt carries, so the
1535
+ // recovery paths can tell "delivered" from "never arrived" instead of
1536
+ // inferring it. Stamped only AFTER dispatchRound resolved: the marker
1537
+ // asserts that a prompt bearing it was written, and a dispatch that
1538
+ // threw may not have written it (one that persisted its durable
1539
+ // command first is retained by the catch below, unstamped).
1540
+ store.stampPromptMarker(roundId, roundPromptMarker(row.number, roundNo));
1541
+ spendWithholding(roundId);
1542
+ // The head this round reviews is the next follow-up round's
1543
+ // baseline — pin it against gc so a force-push cannot take it away.
1544
+ // Best-effort: an unpinned head is re-fetched, or the round runs in full.
1545
+ if (deps.pinReviewedHead) {
1546
+ try {
1547
+ const pinned = await deps.pinReviewedHead(row.number, order.head_sha);
1548
+ if (!pinned)
1549
+ log(`dispatch: #${row.number} reviewed head ${order.head_sha.slice(0, 7)} not pinned`);
1550
+ }
1551
+ catch (err) {
1552
+ log(`dispatch: #${row.number} reviewed head not pinned`, err);
1553
+ }
1554
+ }
1555
+ if (memory.lessons)
1556
+ logLessonDelivery(store, roundId, memory.lessons);
1557
+ // The round's lesson selection — the shadow ledger's
1558
+ // input. Started only now, with the round handed off, and never
1559
+ // awaited: under `shadow` it changes nothing in this round's prompt,
1560
+ // task files or agents, so nothing is gained by starting it sooner —
1561
+ // and starting it before the seed would queue its finds ahead of the
1562
+ // round's own memory search on the server's one-thread embedder. Under
1563
+ // `on` it already ran, awaited beside the seed (above).
1564
+ if (deps.lessons && !delivering) {
1565
+ try {
1566
+ deps.lessons.start(order, files);
1567
+ }
1568
+ catch (err) {
1569
+ log(`dispatch: #${row.number} lesson selection not started (ignored)`, err);
1570
+ }
1571
+ }
1572
+ // `start` opened the selector's own window synchronously (or there
1573
+ // is no selection) — the round's own window closes now.
1574
+ endMemoryWindow();
1575
+ setPrGates(store, row.number, gate);
1576
+ applyEvent(store, row.number, { type: "dispatched" }, {}, { botLogin: botAccountLogin(cfg.github), now });
1577
+ store.logEvent("round_dispatched", row.number, {
1578
+ round_id: roundId,
1579
+ round_no: roundNo,
1580
+ round_type: roundType,
1581
+ // What a later round reviewed (absent on a first round).
1582
+ ...(roundType === "re-review"
1583
+ ? {
1584
+ scope: order.scope === "delta" ? "delta" : "full",
1585
+ ...(order.delta
1586
+ ? {
1587
+ delta_lines: order.delta.lines,
1588
+ delta_files: order.delta.files.length,
1589
+ sweep_owed: (order.sweep_plan?.length ?? 0) > 0,
1590
+ }
1591
+ : {}),
1592
+ }
1593
+ : {}),
1594
+ lenses: order.lenses,
1595
+ validate_tier: validateTier,
1596
+ migration_gate: order.migration_gate,
1597
+ migration_collisions: migrationCollisions.length,
1598
+ migration_precheck_degraded: precheck.degraded,
1599
+ first_party_directive_files: firstPartyDirectives?.files.length ?? 0,
1600
+ head_sha: order.head_sha,
1601
+ base_ref: order.base_ref,
1602
+ base_sha: order.base_sha,
1603
+ triggers_consumed: consumed,
1604
+ // `row` is the pre-dispatch snapshot; `dispatched` has just
1605
+ // cleared these on the live row, and a late guard-2 settlement needs
1606
+ // to explain which trigger paid for the round.
1607
+ trigger_ts: row.trigger_ts,
1608
+ trigger_provenance: row.trigger_provenance,
1609
+ recovery: order.recovery ?? null,
1610
+ });
1611
+ // Slack: the PR's thread root on the first round we see, a
1612
+ // 🔄 line in-thread on later ones. Fire-and-forget: a slow or
1613
+ // blackholed Slack (bounded retries, capped Retry-After, one shared
1614
+ // post queue) must never stall the pump behind it — the announcer's
1615
+ // per-PR lane is claimed synchronously on the call, so thread order
1616
+ // still holds. The announcer swallows its own failures; the catches
1617
+ // here are belt-and-braces, and the round is recorded above already.
1618
+ if (deps.announce) {
1619
+ const pr = row.number;
1620
+ try {
1621
+ void deps.announce
1622
+ .roundStarted({
1623
+ pr: row.number,
1624
+ title: row.title,
1625
+ author: row.author,
1626
+ head_sha: order.head_sha,
1627
+ round_no: roundNo,
1628
+ round_type: roundType,
1629
+ lenses: order.lenses,
1630
+ additions: diffstat.additions,
1631
+ deletions: diffstat.deletions,
1632
+ changed_files: diffstat.changed_files,
1633
+ })
1634
+ .catch((err) => log(`dispatch: slack announcement failed on #${pr}`, err));
1635
+ }
1636
+ catch (err) {
1637
+ log(`dispatch: slack announcement failed on #${pr}`, err);
1638
+ }
1639
+ }
1640
+ provisionalRoundId = undefined;
1641
+ dispatched.push(order);
1642
+ started++;
1643
+ }
1644
+ catch (err) {
1645
+ // One bad PR must never stall the pump. Before the session handoff, the
1646
+ // provisional round is disposable. AFTER dispatchRound persisted a
1647
+ // durable command, deleting only the round would orphan a live command
1648
+ // that blocks every successor and that the supervisor cannot correlate.
1649
+ // Keep that round authoritative instead; best-effort publish reviewing
1650
+ // state and let the bounded command supervisor finish/recover it.
1651
+ let retainedCommand = false;
1652
+ if (provisionalRoundId !== undefined) {
1653
+ const provisional = store.runningRounds().find((round) => round.id === provisionalRoundId);
1654
+ const command = provisional
1655
+ ? store.sessionCommandForRound(row.number, provisional.round_no)
1656
+ : undefined;
1657
+ if (command) {
1658
+ retainedCommand = true;
1659
+ started++;
1660
+ // The retained round owns the review now: the withholding it
1661
+ // inherited is spent, exactly as on a clean handoff.
1662
+ try {
1663
+ spendWithholding(provisionalRoundId);
1664
+ }
1665
+ catch (spendError) {
1666
+ log(`dispatch: #${row.number} withheld-approval mark not spent`, spendError);
1667
+ }
1668
+ try {
1669
+ applyEvent(store, row.number, { type: "dispatched" }, {}, { botLogin: botAccountLogin(cfg.github), now });
1670
+ }
1671
+ catch (publishError) {
1672
+ log(`dispatch: #${row.number} retained command but could not publish reviewing state`, publishError);
1673
+ }
1674
+ try {
1675
+ store.logAnomaly("dispatch_commit_failed", row.number, provisional.round_no, `session command ${command.id} was persisted but dispatch bookkeeping failed: ${String(err)}; bounded supervision retains ownership`);
1676
+ }
1677
+ catch (anomalyError) {
1678
+ log(`dispatch: #${row.number} retained-command anomaly write failed`, anomalyError);
1679
+ }
1680
+ }
1681
+ else {
1682
+ store.discardRunningRound(provisionalRoundId);
1683
+ // Under `on` the round's lesson selection ran — and was
1684
+ // recorded — before the handoff; a discarded round never ran, so
1685
+ // its ledger rows go too (and a selection still in flight records
1686
+ // nothing), or the shadow report would count the attempt.
1687
+ if (deps.lessons?.discard) {
1688
+ try {
1689
+ deps.lessons.discard(provisionalRoundId);
1690
+ }
1691
+ catch (discardError) {
1692
+ log(`dispatch: #${row.number} lesson record of the discarded round not dropped (ignored)`, discardError);
1693
+ }
1694
+ }
1695
+ }
1696
+ }
1697
+ log(`dispatch: #${row.number} failed${retainedCommand ? " after durable handoff — round retained" : ""}`, err);
1698
+ // No round owns the review: the PR stays queued for the next pump,
1699
+ // which paints it again when its round really starts.
1700
+ if (!retainedCommand && markedInProgress) {
1701
+ try {
1702
+ await deps.adapter.removeLabel(row.number, cfg.github.in_progress_label);
1703
+ }
1704
+ catch (labelError) {
1705
+ log(`dispatch: #${row.number} in-progress label not removed`, labelError);
1706
+ }
1707
+ }
1708
+ try {
1709
+ store.logEvent("dispatch_failed", row.number, {
1710
+ round_id: provisionalRoundId ?? null,
1711
+ retained_command: retainedCommand,
1712
+ error: String(err),
1713
+ });
1714
+ }
1715
+ catch (eventError) {
1716
+ log(`dispatch: #${row.number} failure event write failed`, eventError);
1717
+ }
1718
+ }
1719
+ finally {
1720
+ // A round that failed after its seed never leaves the case
1721
+ // writer held (idempotent: a closed window stays closed).
1722
+ endMemoryWindow();
1723
+ }
1724
+ }
1725
+ return dispatched;
1726
+ }
1727
+ //# sourceMappingURL=dispatch.js.map