@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,1361 @@
1
+ import { prActivity } from "./pr-activity.js";
2
+ // Outcome capture: what became of every finding the reviewer posted.
3
+ //
4
+ // Why. `round_findings` records why each candidate was raised or killed; this
5
+ // module records what happened after a finding reached the PR — did the
6
+ // author fix it, argue it away, or leave it standing? — and links each posted
7
+ // finding to the GitHub comment that carried it, so a human reply joins back
8
+ // to the lens that raised the finding. The learner (finding cases, lessons)
9
+ // needs exactly that join. This module builds it, daemon-side and deterministic — no model in the
10
+ // loop, no GitHub write, nothing a round or a bench prompt can see.
11
+ //
12
+ // Three tables, owned here (on the state database, like the ping-pong
13
+ // ledger's):
14
+ //
15
+ // - `finding_comments` — one row per ROOT inline comment the bot posted, with
16
+ // the facts needed to link it (path, original line, review id, the marker,
17
+ // the title line) and the link itself: `(round_no, finding_key)` and how it
18
+ // was made (`via`). Linking, most trusted first:
19
+ // 1. `marker` — the hidden `<!-- qcr:f=<round>:<key> -->` the live render
20
+ // route stamps into every comment (packages/pi-plugin/src/findingkey.ts). Exact.
21
+ // 2. `thread_id` — the record's own `thread_id` names the comment.
22
+ // 3. `path_line` — the one posted finding at that path and line in the
23
+ // comment's round (the round its review id names, else the registry's
24
+ // attribution); ties broken by the title line (`path_line_title`), then
25
+ // the tag (`path_line_tag`). A comment none of these settles stays
26
+ // UNMATCHED (null) — never a guess.
27
+ // Facts arrive from the reconcile comment sweep's existing read (the bot's
28
+ // own inline comments), from the human replies
29
+ // already in `pr_comments` (a reply names its thread's root, path and line —
30
+ // GitHub's CURRENT line, which drifts as the branch moves, so it is the
31
+ // weakest fact), and from the record's `thread_id`. A fallback link is kept
32
+ // while its facts stand; when the bot's own comment supplies different ones
33
+ // (the line it was posted on, its review id), the link is re-opened and
34
+ // resolved again. No new GitHub read, and no gate: this only ever reads.
35
+ // - `finding_outcomes` — per posted finding, a FATE from the closed set
36
+ // below, with the rule that decided it (`basis`), the evidence and when.
37
+ // - `review_outcomes` — the WHOLE-REVIEW signals (a review a human set aside,
38
+ // a merge over a standing block), recorded per review and never smeared over
39
+ // the review's findings: nothing says which finding made a human set a
40
+ // review aside.
41
+ //
42
+ // The fates, and what decides them. Every piece of evidence is timestamped;
43
+ // the LATEST decisive one wins, so a finding upheld after a reply and fixed by
44
+ // a later push reads `fixed`.
45
+ // fixed — the session's thread decision said `fixed`; a human
46
+ // reply in the thread cites a commit made after the
47
+ // review (checked in git: a sha-shaped token that is
48
+ // no such commit — a UUID segment, a colour, the
49
+ // commit that introduced the behaviour — is no fix);
50
+ // the thread was resolved after a push that changed
51
+ // the anchored lines; or the finding is gone at the
52
+ // next round and the code at its anchor changed.
53
+ // refuted_conceded — the session's decision said `refuted`; a later round
54
+ // recorded it `conceded` with the code at its anchor
55
+ // unchanged and no sha cited; or the session resolved
56
+ // the thread with no push since the review and no
57
+ // unobserved head. The last two are INFERRED from the session's own act, so a
58
+ // human reply that accepts the point (`concedesPoint`:
59
+ // "fair catch", "fixed", "updated the description")
60
+ // turns them into `unknown`, basis `conflict:…`.
61
+ // upheld_after_reply — a human replied and the bot held: the decision said
62
+ // `held` or kept the thread open, or the next round
63
+ // posted the finding again. A thread kept open right
64
+ // after a reply citing a commit is no hold: a fix git
65
+ // verified stands, one it cannot check is `unknown`.
66
+ // open_at_close — none of the above, and the PR is closed.
67
+ // unknown — none of the above, and the PR is open.
68
+ // A `conceded` record is not read as a refutation on its own: most of them
69
+ // are fixes ("Fixed in <sha>"). It counts as `fixed` when a
70
+ // sha was cited or the anchored code changed, as `refuted_conceded` only when
71
+ // the code provably did not, and as no evidence when that cannot be told — a
72
+ // cited sha git cannot check included.
73
+ //
74
+ // A resolve is the session's: the event exists only when its thread decision
75
+ // said `resolve`. GitHub answering "already resolved" (a human clicked first)
76
+ // changes who clicked, not what the session judged, so it reads as the
77
+ // session's resolve; only the human-only refusals (`[check this]`,
78
+ // `[deferred]`) are no resolve at all. A decision that names its own
79
+ // `outcome` overrides the resolve it made.
80
+ //
81
+ // A later round's entry is this finding only when that is provable: it names
82
+ // this finding's thread or `settled:` it, or it names this finding's round in
83
+ // `raised_in_round` and either carries this finding's key or — same path,
84
+ // shared lens, within `CONTINUATION_LINES` — is nearer to this finding than
85
+ // to any other entry of that round. Only such a STRONG match carries
86
+ // evidence. A WEAK one (the next round, same path and lens, nearby, any
87
+ // round named or none) may be a different finding, so it only keeps this one
88
+ // from reading "gone", erring to `unknown`.
89
+ //
90
+ // A finding the next round posted again is CARRIED: the earlier row inherits
91
+ // the later one's evidence, so one finding raised in three rounds reads one
92
+ // fate three times rather than three different ones.
93
+ //
94
+ // Bounded. The reconcile pass runs `sweep()`: open PRs and PRs closed in the
95
+ // last day, at most `OUTCOME_SWEEP_MAX_PRS`, and a PR whose inputs have not
96
+ // moved since its last capture is skipped. Git reads (`AnchorGit`) are local,
97
+ // read-only and memoised; a commit the clone does not hold is UNKNOWN, never a
98
+ // change and never no-change. Every failure is the sweep's own — reconcile's
99
+ // health (which lifts a deploy drain) never waits on it.
100
+ import { carriedToRef, isPostedRow, predecessorOf, sameSite, } from "@quickchatai/pi-quickreview/src/finding-chain.js";
101
+ import { parseFindingMarker } from "@quickchatai/pi-quickreview/src/findingkey.js";
102
+ import { storedLensIds } from "@quickchatai/pi-quickreview/src/lensids.js";
103
+ import { bareTag } from "@quickchatai/pi-quickreview/src/severity.js";
104
+ import { yieldToLoop } from "@quickchatai/pi-quickreview/src/yieldloop.js";
105
+ export const FINDING_FATES = [
106
+ "fixed",
107
+ "refuted_conceded",
108
+ "upheld_after_reply",
109
+ "open_at_close",
110
+ "unknown",
111
+ ];
112
+ /** Bump when a rule changes, so every PR is re-captured once. */
113
+ export const OUTCOME_RULES_VERSION = 5;
114
+ /** PRs captured per reconcile pass. */
115
+ export const OUTCOME_SWEEP_MAX_PRS = 25;
116
+ /** How long after a close a PR is still captured — the close is evidence. */
117
+ export const OUTCOME_CLOSED_WINDOW_MS = 24 * 60 * 60 * 1000;
118
+ /**
119
+ * Does the bounded pass (`OutcomeCapture.sweep`) still visit this PR? Open
120
+ * PRs, and PRs closed within `OUTCOME_CLOSED_WINDOW_MS`; a PR with no
121
+ * `pr_state` row never. The ONE spelling of the window: the case writer
122
+ * (src/cases.ts) catches up the PRs outside it itself.
123
+ */
124
+ export function inCaptureWindow(p, nowMs) {
125
+ if (!p)
126
+ return false;
127
+ return p.state !== "closed" || nowMs - (ms(p.updated_at) ?? 0) <= OUTCOME_CLOSED_WINDOW_MS;
128
+ }
129
+ /** A PR whose inputs have not moved is still re-captured this often: a git
130
+ * answer that was unknown (a commit not yet fetched) may since be known. */
131
+ export const OUTCOME_RECAPTURE_MS = 6 * 60 * 60 * 1000;
132
+ /** Wall time one pass may spend before it stops STARTING PRs: the pass runs
133
+ * inside reconcile, ahead of the dispatch pump, so the first capture after a
134
+ * deploy (every open PR, cold git) must not hold the pump for long. The PRs
135
+ * left over come first next pass. */
136
+ export const OUTCOME_SWEEP_BUDGET_MS = 10_000;
137
+ const SCHEMA = `
138
+ CREATE TABLE IF NOT EXISTS finding_comments (
139
+ comment_id INTEGER PRIMARY KEY,
140
+ pr INTEGER NOT NULL,
141
+ review_id INTEGER,
142
+ path TEXT,
143
+ line INTEGER,
144
+ head TEXT,
145
+ marker_round INTEGER,
146
+ marker_key TEXT,
147
+ created_at TEXT,
148
+ -- 'github' (the bot's own comment, read) | 'reply' (a human reply named it)
149
+ -- | 'record' (a findings record's thread_id named it)
150
+ source TEXT NOT NULL,
151
+ round_no INTEGER,
152
+ finding_key TEXT,
153
+ via TEXT,
154
+ linked_at TEXT,
155
+ seen_at TEXT NOT NULL
156
+ );
157
+ CREATE INDEX IF NOT EXISTS finding_comments_finding
158
+ ON finding_comments (pr, round_no, finding_key);
159
+ CREATE TABLE IF NOT EXISTS finding_outcomes (
160
+ pr INTEGER NOT NULL,
161
+ round_no INTEGER NOT NULL,
162
+ finding_key TEXT NOT NULL,
163
+ comment_id INTEGER,
164
+ fate TEXT NOT NULL,
165
+ basis TEXT,
166
+ evidence TEXT,
167
+ decided_at TEXT,
168
+ human_replies INTEGER NOT NULL DEFAULT 0,
169
+ carried_to TEXT,
170
+ fate_changed_at TEXT NOT NULL,
171
+ updated_at TEXT NOT NULL,
172
+ PRIMARY KEY (pr, round_no, finding_key)
173
+ );
174
+ CREATE TABLE IF NOT EXISTS review_outcomes (
175
+ signal_id INTEGER PRIMARY KEY,
176
+ pr INTEGER NOT NULL,
177
+ kind TEXT NOT NULL,
178
+ round_no INTEGER,
179
+ gh_review_id INTEGER,
180
+ at TEXT NOT NULL,
181
+ recorded_at TEXT NOT NULL
182
+ );
183
+ CREATE TABLE IF NOT EXISTS outcome_capture_activity (
184
+ pr INTEGER PRIMARY KEY,
185
+ captured_at TEXT NOT NULL,
186
+ comments_max INTEGER NOT NULL,
187
+ signals_max INTEGER NOT NULL
188
+ );
189
+ CREATE TABLE IF NOT EXISTS outcome_capture_marks (
190
+ pr INTEGER PRIMARY KEY,
191
+ signature TEXT NOT NULL,
192
+ captured_at TEXT NOT NULL
193
+ );
194
+ `;
195
+ /**
196
+ * A retried posting can stamp one finding's marker on two comments: the
197
+ * earliest keeps the link (`via = marker`), every later one stays unlinked
198
+ * with the marker. Per linked comment, its twins' ids, ascending — the other
199
+ * threads that are the same finding's. One rule for the outcome capture and
200
+ * the finding cases.
201
+ */
202
+ export function markerTwins(rows) {
203
+ const linked = new Map();
204
+ for (const c of rows)
205
+ if (c.round_no !== null && c.finding_key !== null)
206
+ linked.set(fkey(c.round_no, c.finding_key), c);
207
+ const out = new Map();
208
+ for (const c of [...rows].sort((a, b) => a.comment_id - b.comment_id)) {
209
+ if (c.finding_key !== null || c.marker_round === null || c.marker_key === null)
210
+ continue;
211
+ const root = linked.get(fkey(c.marker_round, c.marker_key));
212
+ if (root?.via !== "marker")
213
+ continue;
214
+ out.set(root.comment_id, [...(out.get(root.comment_id) ?? []), c.comment_id]);
215
+ }
216
+ return out;
217
+ }
218
+ /** A `finding_outcomes.carried_to` address: `<round>:<key>`. */
219
+ export { carriedToRef } from "@quickchatai/pi-quickreview/src/finding-chain.js";
220
+ /** Which rows reached the PR, and which earlier entry a later one continues:
221
+ * the review core reads them too (packages/pi-plugin/src/priorfindings.ts). */
222
+ export { CONTINUATION_LINES, isPostedRow, predecessorOf, } from "@quickchatai/pi-quickreview/src/finding-chain.js";
223
+ /** The finding a `carried_to` address names — split at the FIRST ':', since
224
+ * the key itself may hold ':' — or null when the round part is not an
225
+ * integer. */
226
+ export function parseCarriedTo(ref) {
227
+ const cut = ref.indexOf(":");
228
+ const round = Number(ref.slice(0, cut));
229
+ if (cut <= 0 || !Number.isInteger(round))
230
+ return null;
231
+ return { round_no: round, finding_key: ref.slice(cut + 1) };
232
+ }
233
+ // ---- small pure helpers ---------------------------------------------------
234
+ /** How many sha-shaped tokens of one reply are checked in git. */
235
+ const MAX_CITED_SHAS = 5;
236
+ /** How many later heads a cited sha is checked against. */
237
+ const MAX_LATER_HEADS = 8;
238
+ /** The sha-shaped tokens a human reply carries (7–40 hex characters with at
239
+ * least one digit and one letter, standing alone — "Fixed in 3f2a9c1", a
240
+ * commit URL; never `#`-prefixed like a colour, never a dash-joined run like
241
+ * a UUID's segments), distinct, in order, at most `MAX_CITED_SHAS`. A token
242
+ * is only a CANDIDATE: whether it is a fix is git's question
243
+ * (`AnchorGit.introducedBetween`). */
244
+ export function citedShas(text) {
245
+ if (typeof text !== "string")
246
+ return [];
247
+ const out = [];
248
+ for (const m of text.matchAll(/(?<![0-9a-z#-])[0-9a-f]{7,40}(?![0-9a-z-])/gi)) {
249
+ const t = m[0].toLowerCase();
250
+ if (/[0-9]/.test(t) && /[a-f]/.test(t) && !out.includes(t))
251
+ out.push(t);
252
+ if (out.length >= MAX_CITED_SHAS)
253
+ break;
254
+ }
255
+ return out;
256
+ }
257
+ /** The first candidate `citedShas` finds, or null. */
258
+ export function citedSha(text) {
259
+ return citedShas(text)[0] ?? null;
260
+ }
261
+ /** The tags and title a comment opens with: `[blocker] title`,
262
+ * `**[check this]** title`, `[ask your human] [question] title`. */
263
+ export function commentHead(body) {
264
+ const first = (typeof body === "string" ? body : "").split("\n").find((l) => l.trim() !== "") ?? "";
265
+ let rest = first.trim();
266
+ const tags = [];
267
+ for (;;) {
268
+ rest = rest.replace(/^[*_`~>\s]+/, "");
269
+ const m = /^\[([^\]]{1,40})\][*_`~\s]*/.exec(rest);
270
+ if (!m)
271
+ break;
272
+ tags.push(m[1].trim().toLowerCase());
273
+ rest = rest.slice(m[0].length);
274
+ }
275
+ return { tags, title: rest.replace(/[*_~\s]+$/, "").trim() };
276
+ }
277
+ function normTitle(t) {
278
+ return (t ?? "")
279
+ .toLowerCase()
280
+ .replace(/[`*_~]/g, "")
281
+ .replace(/\s+/g, " ")
282
+ .replace(/[.!?:;\s]+$/, "")
283
+ .trim();
284
+ }
285
+ function ms(iso) {
286
+ if (!iso)
287
+ return null;
288
+ const t = Date.parse(iso);
289
+ return Number.isFinite(t) ? t : null;
290
+ }
291
+ const fkey = (round, key) => `${round}\u0000${key}`;
292
+ const FATE_RANK = {
293
+ fixed: 3,
294
+ refuted_conceded: 2,
295
+ upheld_after_reply: 1,
296
+ unknown: 0,
297
+ };
298
+ /**
299
+ * Does a human reply ACCEPT the finding — agree it was right, or say it was
300
+ * fixed or the description corrected? A deliberately small lexicon, read
301
+ * only to hold back a refutation the record would otherwise infer from the
302
+ * session's own act (it resolved the thread, or a later round conceded) with
303
+ * the anchored code unchanged. Many such "refutations" are agreements: "fair
304
+ * catch", "You are right, … I have corrected it", "Updated: the description
305
+ * now says …", a fix made away from the anchor. It over-matches on purpose
306
+ * — a hit only turns a refutation into `unknown`, never into a fix.
307
+ */
308
+ const CONCESSION_RE = /\b(?:you(?:'|\u2019)?re (?:right|correct)|you are (?:right|correct)|(?:good|fair|great|nice) (?:catch|point|spot)|fixed|addressed|corrected|updated|amended|reworded|clarified|agreed|accepted|acknowledged|my (?:omission|mistake|bad)|will fix|made (?:it )?part of this pr)\b|^\s*done\b/im;
309
+ export function concedesPoint(text) {
310
+ return typeof text === "string" && CONCESSION_RE.test(text);
311
+ }
312
+ /** The latest evidence; at one instant, fixed > refuted > upheld > unknown. */
313
+ export function decideFate(evidence) {
314
+ let best = null;
315
+ for (const e of evidence)
316
+ if (!best || e.at > best.at || (e.at === best.at && FATE_RANK[e.fate] > FATE_RANK[best.fate]))
317
+ best = e;
318
+ return best;
319
+ }
320
+ /** When a human reply was made: GitHub's time, else when it was captured. */
321
+ function replyMs(r) {
322
+ return ms(r.created_at) ?? ms(r.captured_at) ?? 0;
323
+ }
324
+ /** A resolve that counts: the bot's own, or one GitHub answered "already
325
+ * resolved" (a human clicked first; the session still decided `resolve`). The
326
+ * human-only refusals are no resolve at all. */
327
+ function resolveCounts(skipped) {
328
+ return skipped === null || skipped === "already-resolved";
329
+ }
330
+ function intOf(v) {
331
+ if (typeof v === "number" && Number.isInteger(v))
332
+ return v;
333
+ if (typeof v === "string" && /^\d+$/.test(v))
334
+ return Number(v);
335
+ return null;
336
+ }
337
+ export class OutcomeCapture {
338
+ deps;
339
+ store;
340
+ now;
341
+ constructor(deps) {
342
+ this.deps = deps;
343
+ this.store = deps.store;
344
+ this.now = deps.now ?? (() => new Date());
345
+ this.store.db.exec(SCHEMA);
346
+ }
347
+ ts() {
348
+ return this.now().toISOString();
349
+ }
350
+ // ---- facts in ---------------------------------------------------------------
351
+ /**
352
+ * Keep the link facts of the bot's own ROOT inline comments from a read the
353
+ * caller already made (the reconcile comment sweep's `listReviewCommentsFull`).
354
+ * Replies, other authors' comments and the body beyond its first line are
355
+ * not kept. Returns how many rows were new or gained facts.
356
+ *
357
+ * Facts that change re-open the row's link: a fallback link made from a
358
+ * human reply's drifted line, or before the review id was known, was a
359
+ * resolution of the OLD facts, so it goes back to unmatched and the next
360
+ * `linkComments` resolves it from the new ones. Only a marker link whose
361
+ * marker is unchanged stands.
362
+ */
363
+ observeBotComments(pr, comments) {
364
+ const keep = "(finding_comments.via = 'marker'" +
365
+ " AND finding_comments.marker_round IS excluded.marker_round" +
366
+ " AND finding_comments.marker_key IS excluded.marker_key)";
367
+ const stmt = this.store.db.prepare(`INSERT INTO finding_comments
368
+ (comment_id, pr, review_id, path, line, head, marker_round, marker_key, created_at,
369
+ source, seen_at)
370
+ VALUES (@comment_id, @pr, @review_id, @path, @line, @head, @marker_round, @marker_key,
371
+ @created_at, 'github', @seen_at)
372
+ ON CONFLICT(comment_id) DO UPDATE SET
373
+ review_id = excluded.review_id, path = excluded.path, line = excluded.line,
374
+ head = excluded.head, marker_round = excluded.marker_round,
375
+ marker_key = excluded.marker_key, created_at = excluded.created_at, source = 'github',
376
+ seen_at = excluded.seen_at,
377
+ round_no = CASE WHEN ${keep} THEN finding_comments.round_no ELSE NULL END,
378
+ finding_key = CASE WHEN ${keep} THEN finding_comments.finding_key ELSE NULL END,
379
+ via = CASE WHEN ${keep} THEN finding_comments.via ELSE NULL END,
380
+ linked_at = CASE WHEN ${keep} THEN finding_comments.linked_at ELSE NULL END
381
+ WHERE finding_comments.source != 'github'
382
+ OR finding_comments.review_id IS NOT excluded.review_id
383
+ OR finding_comments.path IS NOT excluded.path
384
+ OR finding_comments.line IS NOT excluded.line
385
+ OR finding_comments.head IS NOT excluded.head
386
+ OR finding_comments.marker_round IS NOT excluded.marker_round
387
+ OR finding_comments.marker_key IS NOT excluded.marker_key`);
388
+ let changed = 0;
389
+ const seen = this.ts();
390
+ this.store.db.transaction(() => {
391
+ for (const c of comments) {
392
+ if (c.user !== this.deps.botLogin || c.in_reply_to_id !== null)
393
+ continue;
394
+ const marker = parseFindingMarker(c.body);
395
+ const head = commentHead(c.body);
396
+ const info = stmt.run({
397
+ comment_id: c.id,
398
+ pr,
399
+ review_id: c.pull_request_review_id ?? null,
400
+ path: c.path || null,
401
+ // The line it was POSTED on — `line` drifts as the branch moves.
402
+ line: c.original_line ?? c.line ?? null,
403
+ head: [head.tags.map((t) => `[${t}]`).join(" "), head.title]
404
+ .filter(Boolean)
405
+ .join(" ")
406
+ .slice(0, 300),
407
+ marker_round: marker?.round_no ?? null,
408
+ marker_key: marker?.key ?? null,
409
+ created_at: c.created_at ?? null,
410
+ seen_at: seen,
411
+ });
412
+ changed += info.changes;
413
+ }
414
+ })();
415
+ return changed;
416
+ }
417
+ /** Link facts the state database already holds: the root a human reply
418
+ * names (when the registry says the bot posted it — its path, and its line
419
+ * as GitHub reported it then, which may have drifted from the posted one)
420
+ * and a record's own `thread_id`. Never overwrites a row: the bot's own
421
+ * comment, when read, replaces these facts (and re-opens their link). */
422
+ ingestLocalFacts(pr, posted) {
423
+ const insert = this.store.db.prepare(`INSERT OR IGNORE INTO finding_comments (comment_id, pr, path, line, created_at, source, seen_at)
424
+ VALUES (?, ?, ?, ?, ?, ?, ?)`);
425
+ // The registry knows when the bot posted the comment. Without it a
426
+ // reply- or record-sourced row has no posting time, and a fate rule
427
+ // falls back to the round's record time — a later round's, on a link
428
+ // the registry contradicts.
429
+ const backfill = this.store.db.prepare("UPDATE finding_comments SET created_at = ? WHERE comment_id = ? AND created_at IS NULL");
430
+ const seen = this.ts();
431
+ const add = (commentId, path, line, source) => {
432
+ const postedAt = this.store.botCommentPostedAt(commentId, "review_comment");
433
+ insert.run(commentId, pr, path, line, postedAt, source, seen);
434
+ if (postedAt !== null)
435
+ backfill.run(postedAt, commentId);
436
+ };
437
+ this.store.db.transaction(() => {
438
+ for (const c of this.store.prComments(pr)) {
439
+ if (c.source !== "review_comment" || c.in_reply_to_id === null)
440
+ continue;
441
+ if (!this.store.isBotComment(c.in_reply_to_id, "review_comment"))
442
+ continue;
443
+ add(c.in_reply_to_id, c.path, c.line, "reply");
444
+ }
445
+ for (const f of posted) {
446
+ if (f.thread_id === null || !this.store.isBotComment(f.thread_id, "review_comment"))
447
+ continue;
448
+ add(f.thread_id, f.path, f.line, "record");
449
+ }
450
+ })();
451
+ }
452
+ // ---- linking ------------------------------------------------------------------
453
+ /** The rounds of a PR that have a parsed findings record. */
454
+ recordedRounds(pr, rows) {
455
+ const out = new Map();
456
+ for (const r of rows) {
457
+ const at = ms(r.recorded_at) ?? 0;
458
+ const prev = out.get(r.round_no);
459
+ out.set(r.round_no, {
460
+ at: prev ? Math.min(prev.at, at) : at,
461
+ head: prev?.head || r.head_sha || null,
462
+ });
463
+ }
464
+ // A record with no entries leaves no rows — its event is the evidence
465
+ // the round was recorded at all.
466
+ for (const e of this.store.prEvents(pr, ["findings_recorded"])) {
467
+ const round = intOf(e.payload.round_no);
468
+ if (round === null || out.has(round))
469
+ continue;
470
+ out.set(round, { at: ms(e.ts) ?? 0, head: null });
471
+ }
472
+ // A head the rows do not carry: the round's posted review's commit.
473
+ for (const r of this.store.roundsFor(pr)) {
474
+ const rec = out.get(r.round_no);
475
+ if (rec && !rec.head && r.gh_commit_id)
476
+ rec.head = r.gh_commit_id;
477
+ }
478
+ return out;
479
+ }
480
+ /**
481
+ * Link every bot comment of a PR to its finding (see the module header for
482
+ * the order). A marker link is final. Every other comment is resolved
483
+ * AGAIN on every capture, from its facts alone, so the result never
484
+ * depends on the order comments are visited or on what an earlier pass
485
+ * linked.
486
+ *
487
+ * * The round: the one the comment's review id names, else the one the
488
+ * registry (`bot_comments.round_no`) attributes it to. When either is
489
+ * known, ONLY that round's findings are candidates — a comment whose
490
+ * round holds no posted finding at its path and line is unmatched,
491
+ * never linked to another round's finding. Only a comment
492
+ * neither names falls back to the first round recorded after it was
493
+ * posted.
494
+ * * A finding two comments could claim by fallback is NEITHER's, and one
495
+ * a marker claims is the marker's.
496
+ *
497
+ * Returns the counts after.
498
+ */
499
+ linkComments(pr) {
500
+ const rows = this.store.roundFindings(pr);
501
+ const posted = rows.filter((r) => r.disposition === "posted");
502
+ this.ingestLocalFacts(pr, posted);
503
+ const comments = this.store.db
504
+ .prepare("SELECT * FROM finding_comments WHERE pr = ? ORDER BY comment_id")
505
+ .all(pr);
506
+ const byKey = new Map(rows.map((r) => [fkey(r.round_no, r.finding_key), r]));
507
+ const recorded = this.recordedRounds(pr, rows);
508
+ const reviewRound = new Map();
509
+ for (const r of this.store.roundsFor(pr))
510
+ if (r.gh_review_id !== null)
511
+ reviewRound.set(r.gh_review_id, r.round_no);
512
+ const update = this.store.db.prepare("UPDATE finding_comments SET round_no = ?, finding_key = ?, via = ?, linked_at = ? WHERE comment_id = ?");
513
+ const unlink = this.store.db.prepare("UPDATE finding_comments SET round_no = NULL, finding_key = NULL, via = NULL, linked_at = NULL WHERE comment_id = ?");
514
+ const at = this.ts();
515
+ const resolve = (c) => {
516
+ if (c.marker_round !== null && c.marker_key !== null) {
517
+ const f = byKey.get(fkey(c.marker_round, c.marker_key));
518
+ if (f)
519
+ return { f, via: "marker" };
520
+ // Its round's record is not parsed yet: wait for it rather than guess.
521
+ if (!recorded.has(c.marker_round))
522
+ return null;
523
+ // Parsed, and the key is not in it (the record was rewritten after
524
+ // rendering): the fallback below may still place it.
525
+ }
526
+ const byThread = posted.filter((f) => f.thread_id === c.comment_id);
527
+ if (byThread.length === 1)
528
+ return { f: byThread[0], via: "thread_id" };
529
+ if (c.path === null || c.line === null)
530
+ return null;
531
+ const here = posted.filter((f) => f.path === c.path && f.line === c.line);
532
+ if (here.length === 0)
533
+ return null;
534
+ const exactRound = c.review_id !== null ? reviewRound.get(c.review_id) : undefined;
535
+ const named = exactRound ?? this.store.botCommentRound(c.comment_id, "review_comment") ?? undefined;
536
+ let pool;
537
+ if (named !== undefined)
538
+ pool = here.filter((f) => f.round_no === named);
539
+ else {
540
+ // The comment's round is the first whose record was parsed after the
541
+ // comment was posted (a record is written after posting).
542
+ const rounds = [...new Set(here.map((f) => f.round_no))].sort((a, b) => a - b);
543
+ const created = ms(c.created_at);
544
+ const round = created === null
545
+ ? rounds.length === 1
546
+ ? rounds[0]
547
+ : undefined
548
+ : rounds.find((r) => (recorded.get(r)?.at ?? -Infinity) >= created);
549
+ pool = round === undefined ? [] : here.filter((f) => f.round_no === round);
550
+ }
551
+ if (pool.length === 1)
552
+ return { f: pool[0], via: "path_line" };
553
+ if (pool.length === 0 || c.head === null)
554
+ return null;
555
+ const head = commentHead(c.head);
556
+ const byTitle = pool.filter((f) => f.title && normTitle(f.title) === normTitle(head.title));
557
+ if (byTitle.length === 1)
558
+ return { f: byTitle[0], via: "path_line_title" };
559
+ const tag = head.tags.find((t) => t !== "ask your human" && t !== "deferred") ?? head.tags[0];
560
+ const byTag = pool.filter((f) => f.posted_as && bareTag(f.posted_as) === tag);
561
+ if (byTag.length === 1)
562
+ return { f: byTag[0], via: "path_line_tag" };
563
+ return null;
564
+ };
565
+ // Pass 1: every comment's own answer, from its facts alone.
566
+ const hits = new Map();
567
+ const claims = new Map();
568
+ for (const c of comments) {
569
+ const hit = c.via === "marker" && c.round_no !== null && c.finding_key !== null
570
+ ? (() => {
571
+ const f = byKey.get(fkey(c.round_no, c.finding_key));
572
+ return f ? { f, via: "marker" } : resolve(c);
573
+ })()
574
+ : resolve(c);
575
+ hits.set(c.comment_id, hit);
576
+ if (!hit)
577
+ continue;
578
+ const k = fkey(hit.f.round_no, hit.f.finding_key);
579
+ const claim = claims.get(k) ?? { marker: [], fallback: [] };
580
+ (hit.via === "marker" ? claim.marker : claim.fallback).push(c.comment_id);
581
+ claims.set(k, claim);
582
+ }
583
+ // Pass 2: a finding goes to its marker (the earliest comment, when a
584
+ // retried posting stamped one marker twice), else to its one fallback
585
+ // claimant; contested by fallbacks alone, it goes to nobody.
586
+ this.store.db.transaction(() => {
587
+ for (const c of comments) {
588
+ const hit = hits.get(c.comment_id) ?? null;
589
+ let keep = null;
590
+ if (hit) {
591
+ const claim = claims.get(fkey(hit.f.round_no, hit.f.finding_key));
592
+ if (hit.via === "marker")
593
+ keep = Math.min(...claim.marker) === c.comment_id ? hit : null;
594
+ else
595
+ keep = claim.marker.length === 0 && claim.fallback.length === 1 ? hit : null;
596
+ }
597
+ if (!keep) {
598
+ if (c.finding_key !== null)
599
+ unlink.run(c.comment_id);
600
+ continue;
601
+ }
602
+ if (c.finding_key === keep.f.finding_key &&
603
+ c.round_no === keep.f.round_no &&
604
+ c.via === keep.via)
605
+ continue;
606
+ update.run(keep.f.round_no, keep.f.finding_key, keep.via, at, c.comment_id);
607
+ }
608
+ })();
609
+ const counts = this.store.db
610
+ .prepare("SELECT COUNT(finding_key) AS linked, COUNT(*) - COUNT(finding_key) AS unmatched FROM finding_comments WHERE pr = ?")
611
+ .get(pr);
612
+ return counts;
613
+ }
614
+ // ---- fates ----------------------------------------------------------------
615
+ loadFacts(pr) {
616
+ const rows = this.store.roundFindings(pr);
617
+ const posted = rows.filter((r) => r.disposition === "posted");
618
+ const byRound = new Map();
619
+ for (const r of rows)
620
+ byRound.set(r.round_no, [...(byRound.get(r.round_no) ?? []), r]);
621
+ const comments = this.commentsFor(pr);
622
+ const links = new Map();
623
+ for (const c of comments)
624
+ if (c.round_no !== null && c.finding_key !== null)
625
+ links.set(fkey(c.round_no, c.finding_key), c);
626
+ const twins = markerTwins(comments);
627
+ const replies = new Map();
628
+ for (const c of this.store.prComments(pr)) {
629
+ if (c.source !== "review_comment" || c.in_reply_to_id === null)
630
+ continue;
631
+ if (c.author === this.deps.botLogin)
632
+ continue;
633
+ replies.set(c.in_reply_to_id, [...(replies.get(c.in_reply_to_id) ?? []), c]);
634
+ }
635
+ const events = this.store.prEvents(pr, [
636
+ "pingpong_decision_applied",
637
+ "pingpong_thread_resolved",
638
+ "head_push",
639
+ "pr_closed",
640
+ ]);
641
+ const facts = {
642
+ rows,
643
+ priorReports: this.store.priorFindingChecks(pr),
644
+ posted,
645
+ byRound,
646
+ recorded: this.recordedRounds(pr, rows),
647
+ rounds: this.store.roundsFor(pr),
648
+ links,
649
+ twins,
650
+ replies,
651
+ decisions: [],
652
+ resolves: [],
653
+ pushes: [],
654
+ currentHead: null,
655
+ closedAt: null,
656
+ };
657
+ for (const e of events) {
658
+ const at = ms(e.ts) ?? 0;
659
+ const thread = intOf(e.payload.thread_id);
660
+ const comment = intOf(e.payload.comment_id);
661
+ if (e.kind === "pingpong_decision_applied" && thread !== null && comment !== null)
662
+ facts.decisions.push({
663
+ at,
664
+ thread_id: thread,
665
+ comment_id: comment,
666
+ resolution: typeof e.payload.resolution === "string" ? e.payload.resolution : "",
667
+ outcome: typeof e.payload.outcome === "string" ? e.payload.outcome : null,
668
+ });
669
+ else if (e.kind === "pingpong_thread_resolved" && thread !== null && comment !== null)
670
+ facts.resolves.push({
671
+ at,
672
+ thread_id: thread,
673
+ comment_id: comment,
674
+ // A value that is neither null nor a known string is no resolve.
675
+ skipped: e.payload.skipped === undefined || e.payload.skipped === null
676
+ ? null
677
+ : typeof e.payload.skipped === "string"
678
+ ? e.payload.skipped
679
+ : "unreadable",
680
+ });
681
+ else if (e.kind === "head_push" && typeof e.payload.head_sha === "string")
682
+ facts.pushes.push({ at, head_sha: e.payload.head_sha });
683
+ else if (e.kind === "pr_closed")
684
+ facts.closedAt = at;
685
+ }
686
+ // Closed is the PR's state NOW: a PR reopened since its close is open.
687
+ const row = this.store.getPr(pr);
688
+ facts.currentHead = row?.head_sha || null;
689
+ if (row && row.state !== "closed")
690
+ facts.closedAt = null;
691
+ else if (row && facts.closedAt === null)
692
+ facts.closedAt = ms(row.updated_at) ?? 0;
693
+ return facts;
694
+ }
695
+ /** Later-round entries that are this finding again (see the module
696
+ * header). Strong: the entry names its thread or its `settled:` thread, or
697
+ * names its round in `raised_in_round` with the same path and is THIS
698
+ * finding's by key, else by being the one nearest same-lens entry of that
699
+ * round. Weak — the NEXT round only — the same path and lens near the same
700
+ * line, whoever it belongs to. */
701
+ continuations(f, facts, threads) {
702
+ const ownRound = (facts.byRound.get(f.round_no) ?? []).filter(isPostedRow);
703
+ const strong = [];
704
+ for (const e of facts.rows) {
705
+ if (e.round_no <= f.round_no)
706
+ continue;
707
+ const word = (e.word ?? "").trim().toLowerCase();
708
+ if (threads.some((t) => e.thread_id === t || word === `settled:${t}`)) {
709
+ strong.push(e);
710
+ continue;
711
+ }
712
+ if (e.raised_in_round !== f.round_no || !f.path || e.path !== f.path)
713
+ continue;
714
+ if (predecessorOf(ownRound, e) === f.finding_key)
715
+ strong.push(e);
716
+ }
717
+ const nextRound = [...facts.recorded.keys()]
718
+ .filter((r) => r > f.round_no)
719
+ .sort((a, b) => a - b)[0];
720
+ if (nextRound === undefined)
721
+ return { strong, next: null };
722
+ const weak = (facts.byRound.get(nextRound) ?? []).filter((e) => sameSite(f, e));
723
+ return { strong, next: { round: nextRound, weak } };
724
+ }
725
+ async touched(f, to) {
726
+ if (!this.deps.git || !f.path || !f.head_sha || !to)
727
+ return null;
728
+ try {
729
+ return await this.deps.git.anchorTouched(f.path, f.line, f.head_sha, to);
730
+ }
731
+ catch {
732
+ return null;
733
+ }
734
+ }
735
+ /** The heads a fix of `f` can live in: the PR's head now, the heads of the
736
+ * rounds after `f`'s, and every pushed head, newest first — never `f`'s own. */
737
+ laterHeads(f, facts) {
738
+ const out = [];
739
+ const add = (h) => {
740
+ if (h && h !== f.head_sha && !out.includes(h))
741
+ out.push(h);
742
+ };
743
+ add(facts.currentHead);
744
+ for (const r of [...facts.recorded.keys()].sort((a, b) => b - a))
745
+ if (r > f.round_no)
746
+ add(facts.recorded.get(r)?.head);
747
+ for (const p of [...facts.pushes].reverse())
748
+ add(p.head_sha);
749
+ return out.slice(0, MAX_LATER_HEADS);
750
+ }
751
+ /** Does a human reply cite a commit made after `f`'s head (see `CitedFix`)?
752
+ * Every reply is read, each on its own. */
753
+ async citedFix(f, facts, replies) {
754
+ const heads = this.laterHeads(f, facts);
755
+ const byReply = new Map();
756
+ let latest = null;
757
+ let unverified = false;
758
+ for (const r of replies) {
759
+ const own = await this.replyCitesFix(f, heads, r);
760
+ byReply.set(r.comment_id, own.state);
761
+ if (own.state === "verified") {
762
+ if (latest === null || replyMs(r) >= replyMs(latest.reply))
763
+ latest = own;
764
+ }
765
+ else if (own.state === "unverified")
766
+ unverified = true;
767
+ }
768
+ const overall = latest ?? (unverified ? { state: "unverified" } : { state: "none" });
769
+ return { overall, byReply };
770
+ }
771
+ /** One reply's citation: the first of its shas git confirms is a commit
772
+ * after `f`'s head, else unverified when git could not answer for one. */
773
+ async replyCitesFix(f, heads, r) {
774
+ let unverified = false;
775
+ for (const sha of citedShas(r.body)) {
776
+ if (!this.deps.git || !f.head_sha || heads.length === 0) {
777
+ unverified = true;
778
+ continue;
779
+ }
780
+ for (const to of heads) {
781
+ let answer;
782
+ try {
783
+ answer = await this.deps.git.introducedBetween(sha, f.head_sha, to);
784
+ }
785
+ catch {
786
+ answer = null;
787
+ }
788
+ if (answer === true)
789
+ return { state: "verified", sha, reply: r };
790
+ if (answer === null)
791
+ unverified = true;
792
+ }
793
+ }
794
+ return unverified ? { state: "unverified" } : { state: "none" };
795
+ }
796
+ async evidenceFor(f, facts, memo) {
797
+ const k = fkey(f.round_no, f.finding_key);
798
+ let p = memo.get(k);
799
+ if (!p) {
800
+ p = this.computeEvidence(f, facts, memo);
801
+ memo.set(k, p);
802
+ }
803
+ return p;
804
+ }
805
+ async computeEvidence(f, facts, memo) {
806
+ const evidence = [];
807
+ const link = facts.links.get(fkey(f.round_no, f.finding_key)) ?? null;
808
+ const thread = link?.comment_id ?? null;
809
+ /** Every thread of this finding: its linked comment's, and its twins'. */
810
+ const threads = thread === null ? [] : [thread, ...(facts.twins.get(thread) ?? [])];
811
+ const postedAt = ms(link?.created_at) ?? facts.recorded.get(f.round_no)?.at ?? ms(f.recorded_at) ?? 0;
812
+ // Backfilled replies and marker twins interleave by when they were written.
813
+ const replies = threads
814
+ .flatMap((t) => facts.replies.get(t) ?? [])
815
+ .sort((a, b) => replyMs(a) - replyMs(b) || a.comment_id - b.comment_id);
816
+ /** A human reply in the thread, no later than `at`, that accepts the
817
+ * point (`concedesPoint`) — the latest one, or null. */
818
+ const humanConceded = (at) => [...replies].reverse().find((r) => replyMs(r) <= at && concedesPoint(r.body)) ?? null;
819
+ // 1. A human reply that cites a commit made after the review — "Fixed in
820
+ // 3f2a9c1", checked in git. One git cannot check is no evidence, and no
821
+ // "nothing was cited" either.
822
+ const cites = await this.citedFix(f, facts, replies);
823
+ const cited = cites.overall;
824
+ const fixCited = cited.state === "verified";
825
+ if (cited.state === "verified")
826
+ evidence.push({
827
+ fate: "fixed",
828
+ basis: "sha_cited",
829
+ at: replyMs(cited.reply),
830
+ detail: `reply ${cited.reply.comment_id} cites ${cited.sha}, a commit after the review`,
831
+ });
832
+ // 2. The session's thread decisions.
833
+ const decided = new Set();
834
+ if (thread !== null) {
835
+ for (const d of facts.decisions.filter((x) => threads.includes(x.thread_id))) {
836
+ const fate = d.outcome === "fixed"
837
+ ? "fixed"
838
+ : d.outcome === "refuted"
839
+ ? "refuted_conceded"
840
+ : d.outcome === "held"
841
+ ? "upheld_after_reply"
842
+ : null;
843
+ if (fate) {
844
+ decided.add(d.comment_id);
845
+ evidence.push({
846
+ fate,
847
+ basis: "pingpong_outcome",
848
+ at: d.at,
849
+ detail: `thread decision on reply ${d.comment_id}: ${d.outcome}`,
850
+ });
851
+ }
852
+ else if (d.resolution === "keep_open") {
853
+ // Keeping a thread open right after "Fixed in <sha>" is the
854
+ // session not having re-verified the new head yet — not the bot
855
+ // holding against a human. When the human reply it answered (the
856
+ // decision's own comment id) cites a commit: a fix git verified
857
+ // stands (nothing is added — `sha_cited` is the evidence); one git
858
+ // cannot check yet leaves the finding undecided. Another reply's
859
+ // citation decides nothing here.
860
+ const answered = replies.find((r) => r.comment_id === d.comment_id) ?? null;
861
+ const citesSha = answered !== null && citedShas(answered.body).length > 0;
862
+ const answeredCite = answered
863
+ ? (cites.byReply.get(answered.comment_id) ?? "none")
864
+ : "none";
865
+ if (citesSha && answeredCite === "verified")
866
+ continue;
867
+ evidence.push(citesSha && answeredCite === "unverified"
868
+ ? {
869
+ fate: "unknown",
870
+ basis: "conflict:kept_open_after_unverified_sha",
871
+ at: d.at,
872
+ detail: `kept open after reply ${answered.comment_id} cited a commit git could not check`,
873
+ }
874
+ : {
875
+ fate: "upheld_after_reply",
876
+ basis: "kept_open",
877
+ at: d.at,
878
+ detail: `thread decision on reply ${d.comment_id}: keep_open`,
879
+ });
880
+ }
881
+ }
882
+ // 3. The session's resolves, where its decision did not say why. A
883
+ // thread a human had already resolved is still the session's resolve
884
+ // (it decided `resolve`), at the latest at the event's time.
885
+ for (const r of facts.resolves.filter((x) => threads.includes(x.thread_id) && resolveCounts(x.skipped))) {
886
+ if (decided.has(r.comment_id))
887
+ continue;
888
+ const who = r.skipped === "already-resolved" ? " (a human had resolved it first)" : "";
889
+ if (fixCited) {
890
+ evidence.push({
891
+ fate: "fixed",
892
+ basis: "resolved_after_cited_fix",
893
+ at: r.at,
894
+ detail: `resolved after a reply cited a commit${who}`,
895
+ });
896
+ continue;
897
+ }
898
+ const since = facts.pushes.filter((p) => p.at > postedAt && p.at <= r.at);
899
+ const head = since[since.length - 1]?.head_sha ?? null;
900
+ if (head === null) {
901
+ if (cited.state === "unverified")
902
+ continue;
903
+ // Push events carry observation times. A head that differs now may
904
+ // have landed before this resolve and only been captured afterwards.
905
+ const observedHead = facts.pushes.filter((p) => p.at <= postedAt).at(-1)?.head_sha ?? f.head_sha;
906
+ if (!facts.currentHead ||
907
+ !observedHead ||
908
+ facts.currentHead.toLowerCase() !== observedHead.toLowerCase()) {
909
+ evidence.push({
910
+ fate: "unknown",
911
+ basis: "conflict:resolved_unobserved_head",
912
+ at: r.at,
913
+ detail: `resolved without a push observed before it${who}, but the current head does not confirm the reviewed head`,
914
+ });
915
+ continue;
916
+ }
917
+ const accepted = humanConceded(r.at);
918
+ evidence.push(accepted
919
+ ? {
920
+ fate: "unknown",
921
+ basis: "conflict:resolved_no_change",
922
+ at: r.at,
923
+ detail: `resolved with no push since the review${who}, but reply ${accepted.comment_id} accepts the point`,
924
+ }
925
+ : {
926
+ fate: "refuted_conceded",
927
+ basis: "resolved_no_change",
928
+ at: r.at,
929
+ detail: `resolved with no push since the review${who}`,
930
+ });
931
+ continue;
932
+ }
933
+ if ((await this.touched(f, head)) === true)
934
+ evidence.push({
935
+ fate: "fixed",
936
+ basis: "resolved_after_push",
937
+ at: r.at,
938
+ detail: `resolved after ${head.slice(0, 12)} changed the anchored lines${who}`,
939
+ });
940
+ }
941
+ }
942
+ // 4. Later rounds. Only a strong match is this finding's evidence.
943
+ const { strong, next } = this.continuations(f, facts, threads);
944
+ const conceded = strong.filter((e) => e.disposition === "conceded");
945
+ for (const e of conceded) {
946
+ const head = facts.recorded.get(e.round_no)?.head ?? e.head_sha ?? null;
947
+ const changed = await this.touched(f, head);
948
+ const at = facts.recorded.get(e.round_no)?.at ?? ms(e.recorded_at) ?? 0;
949
+ if (fixCited || changed === true)
950
+ evidence.push({
951
+ fate: "fixed",
952
+ basis: "conceded_after_fix",
953
+ at,
954
+ detail: `round ${e.round_no} recorded it conceded after ${fixCited ? "a cited commit" : "the anchored lines changed"}`,
955
+ });
956
+ else if (changed === false && cited.state === "none") {
957
+ const accepted = humanConceded(at);
958
+ evidence.push(accepted
959
+ ? {
960
+ fate: "unknown",
961
+ basis: "conflict:conceded_unchanged",
962
+ at,
963
+ detail: `round ${e.round_no} recorded it conceded with the anchored lines unchanged, but reply ${accepted.comment_id} accepts the point (a fix may sit away from the anchor)`,
964
+ }
965
+ : {
966
+ fate: "refuted_conceded",
967
+ basis: "conceded_unchanged",
968
+ at,
969
+ detail: `round ${e.round_no} recorded it conceded with the anchored lines unchanged`,
970
+ });
971
+ }
972
+ }
973
+ // Posted again, provably the same finding: CARRIED — this row inherits the
974
+ // later one's evidence. Only a strong match carries: a weak one (same
975
+ // path and lens, nearby) may be a different finding, so it only keeps
976
+ // this one from reading "gone".
977
+ const reposted = strong.filter((e) => e.disposition === "posted").sort((a, b) => a.round_no - b.round_no)[0] ??
978
+ null;
979
+ let carriedTo = null;
980
+ if (reposted) {
981
+ carriedTo = reposted;
982
+ const againAt = facts.recorded.get(reposted.round_no)?.at ?? ms(reposted.recorded_at) ?? 0;
983
+ if (replies.some((r) => replyMs(r) < againAt))
984
+ evidence.push({
985
+ fate: "upheld_after_reply",
986
+ basis: "reposted_after_reply",
987
+ at: againAt,
988
+ detail: `round ${reposted.round_no} posted it again after a human reply`,
989
+ });
990
+ }
991
+ else if (next &&
992
+ conceded.length === 0 &&
993
+ next.weak.length === 0 &&
994
+ !strong.some((e) => e.round_no === next.round) &&
995
+ !facts.priorReports.some((report) => report.round_no === f.round_no &&
996
+ report.finding_key === f.finding_key &&
997
+ report.checked_round === next.round &&
998
+ (report.status === "open" || report.status === "unverifiable"))) {
999
+ // Gone at the next round. Fixed only if the code at its anchor changed.
1000
+ const head = facts.recorded.get(next.round)?.head ?? null;
1001
+ if ((await this.touched(f, head)) === true)
1002
+ evidence.push({
1003
+ fate: "fixed",
1004
+ basis: "gone_after_change",
1005
+ at: facts.recorded.get(next.round)?.at ?? 0,
1006
+ detail: `gone at round ${next.round}, whose head changed the anchored lines`,
1007
+ });
1008
+ }
1009
+ if (carriedTo) {
1010
+ const successor = carriedTo;
1011
+ const later = await this.evidenceFor(successor, facts, memo);
1012
+ evidence.push(...later.evidence.map((e) => ({
1013
+ ...e,
1014
+ basis: e.basis.startsWith("carried:") ? e.basis : `carried:${e.basis}`,
1015
+ detail: `round ${successor.round_no} ${successor.finding_key}: ${e.detail}`,
1016
+ })));
1017
+ }
1018
+ return {
1019
+ evidence,
1020
+ comment: thread,
1021
+ replies: replies.length,
1022
+ carriedTo: carriedTo ? carriedToRef(carriedTo.round_no, carriedTo.finding_key) : null,
1023
+ };
1024
+ }
1025
+ /** Record the whole-review signals of a PR, one row per signal. */
1026
+ recordReviewOutcomes(pr, rounds) {
1027
+ const upsert = this.store.db.prepare(`INSERT INTO review_outcomes (signal_id, pr, kind, round_no, gh_review_id, at, recorded_at)
1028
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1029
+ ON CONFLICT(signal_id) DO UPDATE SET round_no = excluded.round_no,
1030
+ gh_review_id = excluded.gh_review_id`);
1031
+ const at = this.ts();
1032
+ for (const s of this.store.feedbackSignalsFor(pr)) {
1033
+ if (s.kind === "review_set_aside" && s.review_id !== null) {
1034
+ const round = rounds.find((r) => r.gh_review_id === s.review_id) ?? null;
1035
+ upsert.run(s.id, pr, "set_aside", round?.round_no ?? null, s.review_id, s.created_at, at);
1036
+ }
1037
+ else if (s.kind === "merged_over_block") {
1038
+ // The review that stood: the latest decided round before the merge,
1039
+ // and only if it decided REQUEST_CHANGES — otherwise unknown.
1040
+ const signalAt = ms(s.created_at) ?? Infinity;
1041
+ const decided = rounds
1042
+ .filter((r) => (r.verdict === "APPROVE" || r.verdict === "REQUEST_CHANGES") &&
1043
+ (ms(r.finished_at ?? r.started_at) ?? Infinity) <= signalAt)
1044
+ .sort((a, b) => a.round_no - b.round_no);
1045
+ const last = decided[decided.length - 1];
1046
+ const stood = last && last.verdict === "REQUEST_CHANGES" ? last : null;
1047
+ upsert.run(s.id, pr, "merged_over_block", stood?.round_no ?? null, stood?.gh_review_id ?? null, s.created_at, at);
1048
+ }
1049
+ }
1050
+ }
1051
+ /** Link, then decide and record the fate of every posted finding of a PR. */
1052
+ async capturePr(pr) {
1053
+ const link = this.linkComments(pr);
1054
+ const facts = this.loadFacts(pr);
1055
+ this.recordReviewOutcomes(pr, facts.rounds);
1056
+ // The fingerprint of exactly the inputs read above, taken BEFORE the first
1057
+ // await: a reply, push, decision or record that lands while the git reads
1058
+ // below are in flight was not read, so it must move the fingerprint away
1059
+ // from this mark and bring the PR back next pass.
1060
+ const signature = this.signature(pr);
1061
+ const activity = prActivity(this.store, pr);
1062
+ const memo = new Map();
1063
+ const decided = [];
1064
+ for (const f of facts.posted)
1065
+ decided.push({ f, chain: await this.evidenceFor(f, facts, memo) });
1066
+ const fates = Object.fromEntries(FINDING_FATES.map((x) => [x, 0]));
1067
+ const at = this.ts();
1068
+ const existing = new Map(this.store.db
1069
+ .prepare("SELECT round_no, finding_key, fate FROM finding_outcomes WHERE pr = ?")
1070
+ .all(pr).map((r) => [fkey(r.round_no, r.finding_key), r.fate]));
1071
+ const upsert = this.store.db.prepare(`INSERT INTO finding_outcomes
1072
+ (pr, round_no, finding_key, comment_id, fate, basis, evidence, decided_at, human_replies,
1073
+ carried_to, fate_changed_at, updated_at)
1074
+ VALUES (@pr, @round_no, @finding_key, @comment_id, @fate, @basis, @evidence, @decided_at,
1075
+ @human_replies, @carried_to, @at, @at)
1076
+ ON CONFLICT(pr, round_no, finding_key) DO UPDATE SET
1077
+ comment_id = excluded.comment_id, fate = excluded.fate, basis = excluded.basis,
1078
+ evidence = excluded.evidence, decided_at = excluded.decided_at,
1079
+ human_replies = excluded.human_replies, carried_to = excluded.carried_to,
1080
+ fate_changed_at = CASE WHEN finding_outcomes.fate = excluded.fate
1081
+ THEN finding_outcomes.fate_changed_at ELSE excluded.fate_changed_at END,
1082
+ updated_at = excluded.updated_at`);
1083
+ let changed = 0;
1084
+ this.store.db.transaction(() => {
1085
+ for (const { f, chain } of decided) {
1086
+ const best = decideFate(chain.evidence);
1087
+ // A latest piece of evidence that is a CONFLICT reads `unknown`, PR
1088
+ // open or closed — its basis says why — and is decided by nobody.
1089
+ const fate = best
1090
+ ? best.fate
1091
+ : facts.closedAt !== null
1092
+ ? "open_at_close"
1093
+ : "unknown";
1094
+ const decidedAt = best && best.fate !== "unknown"
1095
+ ? new Date(best.at).toISOString()
1096
+ : !best && facts.closedAt !== null
1097
+ ? new Date(facts.closedAt).toISOString()
1098
+ : null;
1099
+ fates[fate]++;
1100
+ if (existing.get(fkey(f.round_no, f.finding_key)) !== fate)
1101
+ changed++;
1102
+ upsert.run({
1103
+ pr,
1104
+ round_no: f.round_no,
1105
+ finding_key: f.finding_key,
1106
+ comment_id: chain.comment,
1107
+ fate,
1108
+ basis: best ? best.basis : fate === "open_at_close" ? "closed" : null,
1109
+ evidence: best ? best.detail.slice(0, 500) : null,
1110
+ decided_at: decidedAt,
1111
+ human_replies: chain.replies,
1112
+ carried_to: chain.carriedTo,
1113
+ at,
1114
+ });
1115
+ }
1116
+ // A row whose finding is no longer posted (a re-recorded round) goes.
1117
+ const live = new Set(decided.map(({ f }) => fkey(f.round_no, f.finding_key)));
1118
+ const drop = this.store.db.prepare("DELETE FROM finding_outcomes WHERE pr = ? AND round_no = ? AND finding_key = ?");
1119
+ for (const k of existing.keys())
1120
+ if (!live.has(k)) {
1121
+ const [round, key] = k.split("\u0000");
1122
+ drop.run(pr, Number(round), key);
1123
+ }
1124
+ })();
1125
+ this.markCaptured(pr, signature, activity);
1126
+ return {
1127
+ posted: facts.posted.length,
1128
+ linked: link.linked,
1129
+ unmatched: link.unmatched,
1130
+ changed,
1131
+ fates,
1132
+ };
1133
+ }
1134
+ // ---- the bounded pass -----------------------------------------------------
1135
+ /** A fingerprint of everything a capture reads, so an unmoved PR is skipped. */
1136
+ signature(pr) {
1137
+ const get = (sql) => this.store.db.prepare(sql).get(pr);
1138
+ const row = this.store.getPr(pr);
1139
+ return JSON.stringify([
1140
+ OUTCOME_RULES_VERSION,
1141
+ this.deps.git ? 1 : 0,
1142
+ get("SELECT COUNT(*) c, MAX(id) m, MAX(recorded_at) r FROM round_findings WHERE pr = ?"),
1143
+ get("SELECT COUNT(*) c, MAX(id) m FROM pr_comments WHERE pr = ?"),
1144
+ get("SELECT MAX(id) m FROM events WHERE pr = ? AND kind IN " +
1145
+ "('pingpong_decision_applied','pingpong_thread_resolved','head_push','pr_closed','findings_recorded')"),
1146
+ get("SELECT COUNT(*) c, MAX(id) m FROM feedback_signals WHERE pr = ?"),
1147
+ this.store.priorFindingChecks(pr),
1148
+ get(
1149
+ // `seen_at` moves when a comment's facts change (its anchor or head
1150
+ // corrected), which can re-link it with every count unchanged.
1151
+ "SELECT COUNT(*) c, COUNT(finding_key) l, COUNT(marker_key) k, COUNT(review_id) v, MAX(seen_at) s FROM finding_comments WHERE pr = ?"),
1152
+ get("SELECT COUNT(*) c FROM bot_comments WHERE pr = ?"),
1153
+ // a posting receipt landing later (gh_review_id) can settle a link
1154
+ get("SELECT COUNT(*) c, COUNT(gh_review_id) v, COUNT(gh_commit_id) h, MAX(finished_at) f FROM rounds WHERE pr = ?"),
1155
+ row?.state ?? null,
1156
+ row?.head_sha ?? null,
1157
+ ]);
1158
+ }
1159
+ markCaptured(pr, signature, activity) {
1160
+ const at = this.ts();
1161
+ this.store.db.transaction(() => {
1162
+ this.store.db
1163
+ .prepare(`INSERT INTO outcome_capture_marks (pr, signature, captured_at) VALUES (?, ?, ?)
1164
+ ON CONFLICT(pr) DO UPDATE SET signature=excluded.signature, captured_at=excluded.captured_at`)
1165
+ .run(pr, signature, at);
1166
+ this.store.db
1167
+ .prepare(`INSERT INTO outcome_capture_activity (pr, captured_at, comments_max, signals_max) VALUES (?, ?, ?, ?)
1168
+ ON CONFLICT(pr) DO UPDATE SET captured_at=excluded.captured_at, comments_max=excluded.comments_max, signals_max=excluded.signals_max`)
1169
+ .run(pr, at, activity.comments, activity.signals);
1170
+ })();
1171
+ }
1172
+ /**
1173
+ * One bounded pass: open PRs and PRs closed within the window, the ones
1174
+ * captured longest ago first, at most `maxPrs` and no new PR once
1175
+ * `budgetMs` of wall time is spent, skipping a PR whose inputs have not
1176
+ * moved (unless its capture is older than the recapture period). Never
1177
+ * throws; a PR that fails is named in `errors` and retried next pass.
1178
+ */
1179
+ async sweep(opts = {}) {
1180
+ const report = {
1181
+ considered: 0,
1182
+ captured: 0,
1183
+ skipped: 0,
1184
+ fates_changed: 0,
1185
+ errors: [],
1186
+ };
1187
+ const nowMs = this.now().getTime();
1188
+ const max = opts.maxPrs ?? OUTCOME_SWEEP_MAX_PRS;
1189
+ const started = performance.now();
1190
+ const budget = opts.budgetMs ?? OUTCOME_SWEEP_BUDGET_MS;
1191
+ try {
1192
+ const marks = new Map(this.store.db
1193
+ .prepare("SELECT pr, signature, captured_at FROM outcome_capture_marks")
1194
+ .all().map((m) => [m.pr, m]));
1195
+ const candidates = this.store
1196
+ .allPrs()
1197
+ .filter((p) => inCaptureWindow(p, nowMs))
1198
+ .sort((a, b) => {
1199
+ const ma = marks.get(a.number)?.captured_at ?? "";
1200
+ const mb = marks.get(b.number)?.captured_at ?? "";
1201
+ return ma < mb ? -1 : ma > mb ? 1 : a.number - b.number;
1202
+ });
1203
+ // Attempts, not successes: a capture that throws still spent its slot
1204
+ // and its time, so failures cannot run the pass past either limit.
1205
+ let attempted = 0;
1206
+ for (const p of candidates) {
1207
+ if (attempted >= max)
1208
+ break;
1209
+ if (attempted > 0 && performance.now() - started > budget)
1210
+ break;
1211
+ // A real event-loop turn per PR: a capture whose git answers are all
1212
+ // memoized (or with no git at all) never awaits real I/O, so a run of
1213
+ // them would otherwise block the loop as one long synchronous stretch.
1214
+ await yieldToLoop();
1215
+ report.considered++;
1216
+ try {
1217
+ const mark = marks.get(p.number);
1218
+ if (!opts.force &&
1219
+ mark &&
1220
+ nowMs - (ms(mark.captured_at) ?? 0) < OUTCOME_RECAPTURE_MS &&
1221
+ mark.signature === this.signature(p.number)) {
1222
+ report.skipped++;
1223
+ continue;
1224
+ }
1225
+ attempted++;
1226
+ const r = await this.capturePr(p.number);
1227
+ report.captured++;
1228
+ report.fates_changed += r.changed;
1229
+ }
1230
+ catch (err) {
1231
+ report.errors.push(`outcomes #${p.number}: ${String(err)}`);
1232
+ }
1233
+ }
1234
+ }
1235
+ catch (err) {
1236
+ report.errors.push(`outcomes: ${String(err)}`);
1237
+ }
1238
+ if (report.fates_changed > 0 || report.errors.length > 0)
1239
+ this.store.logEvent("outcomes_captured", null, report);
1240
+ return report;
1241
+ }
1242
+ // ---- reading back ---------------------------------------------------------
1243
+ outcomesFor(pr) {
1244
+ return this.store.db
1245
+ .prepare("SELECT * FROM finding_outcomes WHERE pr = ? ORDER BY round_no, finding_key")
1246
+ .all(pr);
1247
+ }
1248
+ commentsFor(pr) {
1249
+ return this.store.db
1250
+ .prepare("SELECT * FROM finding_comments WHERE pr = ? ORDER BY comment_id")
1251
+ .all(pr);
1252
+ }
1253
+ reviewOutcomesFor(pr) {
1254
+ return this.store.db
1255
+ .prepare("SELECT * FROM review_outcomes WHERE pr = ? ORDER BY signal_id")
1256
+ .all(pr);
1257
+ }
1258
+ /**
1259
+ * The per-lens fate table over findings posted since `sinceIso`. A finding
1260
+ * several lenses raised counts once under each of them; one raised outside
1261
+ * any known lens counts under `(no lens)`. Whole-review signals are counted
1262
+ * per review, never per lens.
1263
+ */
1264
+ summary(sinceIso) {
1265
+ const rows = this.store.db
1266
+ .prepare(`SELECT f.lens_ids, f.lens, o.fate, o.human_replies,
1267
+ EXISTS (SELECT 1 FROM finding_comments c WHERE c.pr = f.pr AND c.round_no = f.round_no
1268
+ AND c.finding_key = f.finding_key) AS linked
1269
+ FROM round_findings f
1270
+ LEFT JOIN finding_outcomes o
1271
+ ON o.pr = f.pr AND o.round_no = f.round_no AND o.finding_key = f.finding_key
1272
+ WHERE f.disposition = 'posted' AND f.recorded_at >= ?`)
1273
+ .all(sinceIso);
1274
+ const empty = () => ({
1275
+ posted: 0,
1276
+ linked: 0,
1277
+ uncaptured: 0,
1278
+ ...Object.fromEntries(FINDING_FATES.map((x) => [x, 0])),
1279
+ });
1280
+ const byLens = new Map();
1281
+ const total = empty();
1282
+ let replied = 0;
1283
+ let repliedKnown = 0;
1284
+ for (const r of rows) {
1285
+ const ids = storedLensIds(r.lens_ids) ?? [];
1286
+ const count = (c) => {
1287
+ c.posted++;
1288
+ if (r.linked)
1289
+ c.linked++;
1290
+ if (r.fate)
1291
+ c[r.fate]++;
1292
+ else
1293
+ c.uncaptured++;
1294
+ };
1295
+ for (const id of ids.length ? ids : ["(no lens)"]) {
1296
+ const c = byLens.get(id) ?? empty();
1297
+ count(c);
1298
+ byLens.set(id, c);
1299
+ }
1300
+ count(total);
1301
+ if ((r.human_replies ?? 0) > 0) {
1302
+ replied++;
1303
+ if (r.fate && r.fate !== "unknown" && r.fate !== "open_at_close")
1304
+ repliedKnown++;
1305
+ }
1306
+ }
1307
+ const reviews = this.store.db
1308
+ .prepare("SELECT kind, COUNT(*) AS n FROM review_outcomes WHERE at >= ? GROUP BY kind ORDER BY kind")
1309
+ .all(sinceIso);
1310
+ return {
1311
+ since: sinceIso,
1312
+ total,
1313
+ lenses: [...byLens.entries()]
1314
+ .sort(([a, x], [b, y]) => y.posted - x.posted || a.localeCompare(b))
1315
+ .map(([lens, counts]) => ({ lens, ...counts })),
1316
+ replied_threads: replied,
1317
+ replied_known: repliedKnown,
1318
+ reviews: Object.fromEntries(reviews.map((r) => [r.kind, r.n])),
1319
+ };
1320
+ }
1321
+ }
1322
+ /** The summary as the operator reads it (`qcr outcomes`). */
1323
+ export function renderOutcomeSummary(s) {
1324
+ const pct = (n, d) => (d === 0 ? "—" : `${Math.round((100 * n) / d)}%`);
1325
+ const cols = [
1326
+ "posted",
1327
+ "linked",
1328
+ "fixed",
1329
+ "refuted",
1330
+ "upheld",
1331
+ "open@close",
1332
+ "unknown",
1333
+ "uncaptured",
1334
+ ];
1335
+ const line = (name, c) => [
1336
+ name.padEnd(24),
1337
+ ...[
1338
+ c.posted,
1339
+ c.linked,
1340
+ c.fixed,
1341
+ c.refuted_conceded,
1342
+ c.upheld_after_reply,
1343
+ c.open_at_close,
1344
+ c.unknown,
1345
+ c.uncaptured,
1346
+ ].map((n, i) => String(n).padStart(cols[i].length + 1)),
1347
+ ].join("");
1348
+ const out = [
1349
+ `Posted findings since ${s.since} — fate per lens (a finding several lenses raised counts under each).`,
1350
+ "",
1351
+ ["lens".padEnd(24), ...cols.map((c) => ` ${c}`)].join(""),
1352
+ ...s.lenses.map((l) => line(l.lens, l)),
1353
+ line("all findings", s.total),
1354
+ "",
1355
+ `linked to their comment: ${s.total.linked}/${s.total.posted} (${pct(s.total.linked, s.total.posted)})`,
1356
+ `human-replied threads with a known fate: ${s.replied_known}/${s.replied_threads} (${pct(s.replied_known, s.replied_threads)})`,
1357
+ `whole-review signals (per review, never per lens): set aside ${s.reviews.set_aside ?? 0}, merged over a block ${s.reviews.merged_over_block ?? 0}`,
1358
+ ];
1359
+ return out.join("\n");
1360
+ }
1361
+ //# sourceMappingURL=outcomes.js.map