@quickchatai/quickreview 0.0.0-stage → 1.1.0

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