@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,999 @@
1
+ // Finished-round evidence transport.
2
+ //
3
+ // The companion cannot reach the VM — rule 8 is structural and this module
4
+ // does not widen it. So the TUI, which already holds the ssh access, copies
5
+ // every finished round's artefacts down to a local store, and the companion
6
+ // reads plain files out of a read-only mount. Nothing new is granted to it: no
7
+ // ssh, no credential, no network reach.
8
+ //
9
+ // Why on completion rather than on demand: on-demand transport loses exactly
10
+ // the episodes nobody was watching. A round that has finished is a round whose
11
+ // evidence is already complete; waiting for someone to ask for it is waiting
12
+ // for the session to be recycled first.
13
+ //
14
+ // Shape follows remote.ts: command construction is pure so tests can assert the
15
+ // exact argv, execution goes through the one injectable SshRunner, and every
16
+ // failure degrades (logged, retried on the next poll) rather than throwing into
17
+ // the paint loop.
18
+ //
19
+ // Retention: evidence is kept indefinitely by deliberate choice, not by
20
+ // omission. Do not add a TTL or a cleanup sweep.
21
+ //
22
+ // Load: the sweep's
23
+ // per-PR progress is kept on disk (`SweepProgressFile`), so a TUI re-exec —
24
+ // every local `dist/` rebuild — resumes instead of re-running `qcr why` for
25
+ // every PR; and the `why` execs are paced (`evidence_why_interval_s`) and held
26
+ // entirely while the board's last status shows the daemon draining or its
27
+ // heartbeat stale, so the sweep can never be the thing that saturates the
28
+ // container.
29
+ //
30
+ // The closed-PR archiver (docs/archiving.md §4, §14): a round
31
+ // copied after its PR's files were archived (14 days closed — a TUI that was
32
+ // off that long) finds them in `archive/pr-<n>/` instead. The transcript is
33
+ // read from its archived gzip (`zcat | head -c`, after `zcat -t` proves the
34
+ // gzip whole, so a corrupt one fails the copy rather than landing short); a
35
+ // role whose file sits only inside a bundle is recorded as `archived`
36
+ // with its pointer — the bundle and the path in it — never as `missing`.
37
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
38
+ import { join, resolve } from "node:path";
39
+ import { TUI_DEFAULTS, type TuiConfig } from "../config.js";
40
+ import type { StatusSnapshot } from "../state.js";
41
+ import {
42
+ parseManifestText,
43
+ settledIndex,
44
+ type Manifest,
45
+ type SettledRecord,
46
+ } from "../archive/manifest.js";
47
+ import { dockerShArgv, whyArgv, type RemoteResult, type SshRunner } from "./remote.js";
48
+ import { errText, writeJsonFile } from "./jsonfile.js";
49
+ import { remoteSandboxRoot } from "./layout.js";
50
+
51
+ /** Roles a round bundle can carry, in copy order. `transcript` carries the pass
52
+ * output too: lens passes run as subagent children inside the PR's one
53
+ * session, so their output is the session JSONL (root rule 9 — passes are
54
+ * leaves). */
55
+ export const EVIDENCE_ROLES = ["transcript", "ledger", "prompt", "verdict"] as const;
56
+ export type EvidenceRole = (typeof EVIDENCE_ROLES)[number];
57
+
58
+ /** Local file name each role lands under. */
59
+ const LOCAL_NAME: Record<EvidenceRole, string> = {
60
+ transcript: "session.jsonl",
61
+ ledger: "ledger.md",
62
+ prompt: "prompt.md",
63
+ verdict: "verdict.json",
64
+ };
65
+
66
+ /**
67
+ * Per-file byte cap on the remote side (`head -c`).
68
+ *
69
+ * A knob would be knob sprawl: this is not a tuning dial but a blast-radius
70
+ * bound. A transcript is ~0.6 MB per session; 32 MiB is two orders of magnitude
71
+ * of headroom and still small enough that one runaway JSONL cannot fill
72
+ * the operator's disk or stall an ssh for minutes. A truncated file is recorded as
73
+ * truncated in the manifest — never silently short.
74
+ */
75
+ export const MAX_FILE_BYTES = 32 * 1024 * 1024;
76
+
77
+ /** Rounds copied per PR per sweep. Keeps a first-ever sweep over a PR with 20
78
+ * rounds from monopolising the poll; the rest land on the following ticks. */
79
+ export const ROUNDS_PER_SWEEP = 3;
80
+
81
+ /**
82
+ * Roles whose remote file is the PR's CURRENT one, not the round's: the ledger
83
+ * and session-prompt live at `pr-<n>.md` and the transcript is "the newest
84
+ * JSONL in the session dir". Copying them into `round-<k>/` is honest only for
85
+ * a round that finished under this copier's watch — for a backlog round the
86
+ * copy would stamp today's files into an old round's dir and silently corrupt
87
+ * the adjudication evidence. Only `verdict` is genuinely round-scoped.
88
+ */
89
+ export const PR_CURRENT_ROLES: readonly EvidenceRole[] = ["transcript", "ledger", "prompt"];
90
+
91
+ /** Recorded verbatim in every manifest so a reader of the store never has to
92
+ * know the copier's version to interpret what it holds. */
93
+ export const ROLES_NOTE =
94
+ "transcript, ledger and prompt are the PR's CURRENT files at copy time (scope: pr-current), not artefacts of this round; only verdict.json is round-scoped. Rounds that finished before this copier first saw the PR carry those roles under skipped_stale instead of a misleading copy.";
95
+
96
+ export interface EvidenceFile {
97
+ role: EvidenceRole;
98
+ /** Absolute path INSIDE the container, as the VM reported it. */
99
+ remotePath: string;
100
+ }
101
+
102
+ /** One round's record, written last so its presence means "fully copied". */
103
+ export interface RoundManifest {
104
+ pr: number;
105
+ round_no: number;
106
+ copied_at: string;
107
+ round: unknown;
108
+ /** `ROLES_NOTE`: which roles are round-scoped and which are pr-current. */
109
+ roles_note: string;
110
+ files: Array<{
111
+ role: EvidenceRole;
112
+ remote_path: string;
113
+ chars: number;
114
+ truncated: boolean;
115
+ /** "round" = this file belongs to this round; "pr-current" = this is the
116
+ * PR's file AS OF COPY TIME (honest only because the round finished under
117
+ * this copier's watch). */
118
+ scope: "round" | "pr-current";
119
+ /** Where the bytes were read: the live file, or the archived gzip
120
+ * `remote_path` names, decompressed. Absent from an older copier's
121
+ * manifest, which only ever read live files. */
122
+ from?: "live" | "archive";
123
+ }>;
124
+ /** Not in the live tree, and found in the PR's archive: where
125
+ * the bytes are — `pointer` the archive file (a bundle, or a gzip) relative
126
+ * to the sandbox root, `path` the live path it was archived from (the
127
+ * record's name inside a bundle). Never counted `missing`. Absent from an
128
+ * older copier's manifest. */
129
+ archived?: Array<{ role: EvidenceRole; pointer: string; path: string }>;
130
+ /** Absent at copy time: not live, and not in the PR's archive either. */
131
+ missing: EvidenceRole[];
132
+ /** PR-current roles deliberately NOT copied: the round finished before this
133
+ * copier first saw the PR, so the current ledger/prompt/transcript would be
134
+ * stamped into an old round's dir as if they were its own. */
135
+ skipped_stale: EvidenceRole[];
136
+ }
137
+
138
+ export const MANIFEST_NAME = "manifest.json";
139
+
140
+ /** The `round-<n>` directory names under a PR's evidence dir that hold a
141
+ * manifest, i.e. rounds that copied completely. The dir must exist. */
142
+ export function completedRoundDirs(prDir: string): string[] {
143
+ return readdirSync(prDir).filter(
144
+ (name) => /^round-\d+$/.test(name) && existsSync(join(prDir, name, MANIFEST_NAME)),
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Locate a round's artefacts on the VM in ONE call, printing `role<space>path`
150
+ * for each that exists.
151
+ *
152
+ * Resolution is done remotely on purpose: the transcript is "the newest JSONL in
153
+ * the session dir" and the verdict may be either still in `verdict/` or already
154
+ * moved to `verdict/processed/<ts>-…` by the watcher. Guessing either one
155
+ * client-side would produce a copier that works until the daemon moves a file.
156
+ *
157
+ * The archive fallback: with no live transcript, the newest archived
158
+ * one — `archive/pr-<n>/transcripts/*.gz`, whose mtime the archiver keeps at
159
+ * the original's; never a `session-at-close-*` legacy copy. And when the PR
160
+ * has an archive manifest it is listed (`archive-manifest <path>`), so a role
161
+ * found nowhere live can be looked up in it instead of read as missing.
162
+ *
163
+ * Ends `exit 0`: a trailing `[ -f … ] && echo` that finds nothing would
164
+ * otherwise exit non-zero and make an entirely successful listing look failed.
165
+ */
166
+ export function manifestArgv(
167
+ cfg: TuiConfig,
168
+ sessionDir: string,
169
+ pr: number,
170
+ round: number,
171
+ ): string[] {
172
+ const root = remoteSandboxRoot(cfg);
173
+ const script = [
174
+ `r="${root}"`,
175
+ `t=$(ls -1t "$r/${sessionDir}/pr-${pr}"/*.jsonl 2>/dev/null | head -1)`,
176
+ `[ -z "$t" ] && t=$(ls -1t "$r/archive/pr-${pr}/transcripts"/*.gz 2>/dev/null | grep -v '/session-at-close-' | head -1)`,
177
+ `[ -n "$t" ] && echo "transcript $t"`,
178
+ `[ -f "$r/${ledgerSource(pr)}" ] && echo "ledger $r/${ledgerSource(pr)}"`,
179
+ `[ -f "$r/${promptSource(pr)}" ] && echo "prompt $r/${promptSource(pr)}"`,
180
+ `v=$(ls -1t "$r/verdict/pr-${pr}-round-${round}.json" "$r"/verdict/processed/*pr-${pr}-round-${round}.json 2>/dev/null | head -1)`,
181
+ `[ -n "$v" ] && echo "verdict $v"`,
182
+ `[ -f "$r/archive/pr-${pr}/manifest.json" ] && echo "${ARCHIVE_MANIFEST_LINE} $r/archive/pr-${pr}/manifest.json"`,
183
+ `exit 0`,
184
+ ].join("\n");
185
+ return dockerShArgv(cfg, script);
186
+ }
187
+
188
+ /** The listing line naming the PR's archive manifest. */
189
+ /** A PR's ledger and session prompt, relative to the sandbox root: the live
190
+ * listing and the archive lookup both name them through these. */
191
+ const ledgerSource = (pr: number): string => `state/ledgers/pr-${pr}.md`;
192
+ const promptSource = (pr: number): string => `state/session-prompts/pr-${pr}.md`;
193
+
194
+ export const ARCHIVE_MANIFEST_LINE = "archive-manifest";
195
+
196
+ /** A VM-reported path is interpolated into the next command's shell script,
197
+ * so it must be plainly shell-safe: no quote, space or metacharacter. */
198
+ export const isShellSafePath = (path: string): boolean => /^[\w./@:+-]+$/.test(path);
199
+
200
+ /** The PR's archive manifest, when the listing named one — with the same
201
+ * trust-boundary checks as an artefact path, and it must be the manifest of
202
+ * THIS PR's archive. */
203
+ export function parseArchiveManifestLine(stdout: string, root: string, pr: number): string | null {
204
+ const want = `${root}/archive/pr-${pr}/manifest.json`;
205
+ for (const line of stdout.split("\n")) {
206
+ const sp = line.indexOf(" ");
207
+ if (sp <= 0 || line.slice(0, sp).trim() !== ARCHIVE_MANIFEST_LINE) continue;
208
+ const path = line.slice(sp + 1).trim();
209
+ if (path === want && isShellSafePath(path)) return path;
210
+ }
211
+ return null;
212
+ }
213
+
214
+ /** Is this artefact path an archived gzip (read with `zcat`)? */
215
+ const isArchivedGzip = (remotePath: string, root: string): boolean =>
216
+ remotePath.startsWith(`${root}/archive/`) && remotePath.endsWith(".gz");
217
+
218
+ /**
219
+ * Parse the listing, refusing any path that is not plainly inside the sandbox
220
+ * root and plainly shell-safe. The VM answers here become the next command's
221
+ * argument, so this is the trust boundary — a path is data, never a fragment of
222
+ * script.
223
+ */
224
+ export function parseManifest(stdout: string, root: string): EvidenceFile[] {
225
+ const out: EvidenceFile[] = [];
226
+ const seen = new Set<string>();
227
+ for (const line of stdout.split("\n")) {
228
+ const sp = line.indexOf(" ");
229
+ if (sp <= 0) continue;
230
+ const role = line.slice(0, sp).trim() as EvidenceRole;
231
+ const remotePath = line.slice(sp + 1).trim();
232
+ if (!(EVIDENCE_ROLES as readonly string[]).includes(role) || seen.has(role)) continue;
233
+ if (!remotePath.startsWith(`${root}/`) || !isShellSafePath(remotePath)) continue;
234
+ seen.add(role);
235
+ out.push({ role, remotePath });
236
+ }
237
+ return out;
238
+ }
239
+
240
+ /** `head -c <cap>` one artefact. Every artefact is text (JSONL / Markdown /
241
+ * JSON), so the string the runner returns is the file — no base64 hop, and no
242
+ * dependency on tar being present in the image.
243
+ *
244
+ * An archived transcript (a `.gz` under `archive/`) is decompressed
245
+ * on the VM: `zcat -t` first proves the whole gzip decompresses — `zcat | head`
246
+ * alone exits with head's status, so a corrupt gzip would land as a short
247
+ * file that looks complete — then `zcat | head -c <cap>`. */
248
+ export function fetchArgv(cfg: TuiConfig, remotePath: string, maxBytes = MAX_FILE_BYTES): string[] {
249
+ if (isArchivedGzip(remotePath, remoteSandboxRoot(cfg)))
250
+ return dockerShArgv(
251
+ cfg,
252
+ `zcat -t '${remotePath}' && zcat '${remotePath}' | head -c ${maxBytes}`,
253
+ );
254
+ return dockerShArgv(cfg, `head -c ${maxBytes} '${remotePath}'`);
255
+ }
256
+
257
+ /** An archive manifest (`archive/pr-<n>/manifest.json`) as fetched, or null
258
+ * when it is not one. A manifest is plain, append-only JSON; one
259
+ * that does not parse is never read as "nothing archived". */
260
+ export function parseArchiveManifest(text: string): Manifest | null {
261
+ try {
262
+ return parseManifestText(text, "archive manifest");
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Where a role's file sits in the PR's archive, by its manifest:
270
+ * the latest record of the path the live listing would have found — the
271
+ * ledger, the session prompt, this round's processed verdict, or the newest
272
+ * PR transcript — or null when the archive holds none. `pointer` is the
273
+ * archive file relative to the sandbox root; `path` the live path it was
274
+ * archived from (its record's name inside a bundle).
275
+ */
276
+ export function archivedRole(
277
+ manifest: Manifest,
278
+ role: EvidenceRole,
279
+ pr: number,
280
+ round: number,
281
+ sessionDir: string,
282
+ ): { pointer: string; path: string } | null {
283
+ const verdictRe = new RegExp(`^verdict/processed/(?:[^/]*-)?pr-${pr}-round-${round}\\.json$`);
284
+ const match = (r: SettledRecord): boolean => {
285
+ switch (role) {
286
+ case "ledger":
287
+ return r.source === ledgerSource(pr);
288
+ case "prompt":
289
+ return r.source === promptSource(pr);
290
+ case "verdict":
291
+ return verdictRe.test(r.source);
292
+ case "transcript":
293
+ return (
294
+ r.kind === "transcript" &&
295
+ !r.legacy &&
296
+ r.source.startsWith(`${sessionDir}/pr-${pr}/`) &&
297
+ r.source.endsWith(".jsonl")
298
+ );
299
+ }
300
+ };
301
+ let best: SettledRecord | null = null;
302
+ for (const r of settledIndex(manifest).values())
303
+ if (match(r) && (!best || r.mtime_ms > best.mtime_ms)) best = r;
304
+ return best ? { pointer: `archive/pr-${pr}/${best.archive}`, path: best.source } : null;
305
+ }
306
+
307
+ /** The round rows `qcr why --json` reports; only the fields the copier reads. */
308
+ export interface WhyRound {
309
+ round_no: number;
310
+ status: string;
311
+ verdict: string | null;
312
+ finished_at: string | null;
313
+ [k: string]: unknown;
314
+ }
315
+
316
+ /**
317
+ * A round is copyable once it is no longer running. `done` is the common case;
318
+ * `expired`, `overdue` and `cancelled` are copied too — a round that died is
319
+ * precisely the episode worth having a local record of, and refusing them would
320
+ * leave exactly the gap this copier exists to close.
321
+ */
322
+ export function finishedRounds(rounds: WhyRound[]): WhyRound[] {
323
+ return rounds
324
+ .filter((r) => r.status !== "running" && Number.isInteger(r.round_no))
325
+ .sort((a, b) => a.round_no - b.round_no);
326
+ }
327
+
328
+ /** The finished rounds out of one `qcr why --json` answer, or `undefined`
329
+ * when the answer does not carry a round list at all. `[]` is reserved for a
330
+ * payload that SAID there are no rounds; a payload that said nothing about
331
+ * rounds is not that. */
332
+ export function readWhyRounds(stdout: string): WhyRound[] | undefined {
333
+ let parsed: unknown;
334
+ try {
335
+ parsed = JSON.parse(stdout);
336
+ } catch {
337
+ return undefined;
338
+ }
339
+ if (typeof parsed !== "object" || parsed === null) return undefined;
340
+ const rounds = (parsed as { rounds?: unknown }).rounds;
341
+ return Array.isArray(rounds) ? finishedRounds(rounds as WhyRound[]) : undefined;
342
+ }
343
+
344
+ /** Cheap "has anything changed for this PR" key, read off the poll snapshot the
345
+ * TUI already fetches. Unchanged ⇒ no `why` call at all, so the steady state
346
+ * costs the copier zero extra ssh. */
347
+ export function prSignature(pr: StatusSnapshot["prs"][number]): string {
348
+ return `${pr.rounds ?? 0}/${pr.last_verdict ?? ""}/${pr.state}/${pr.head_sha}`;
349
+ }
350
+
351
+ // ---- pacing and holds ----------------------------------------
352
+
353
+ /** `[tui] evidence_why_interval_s` — the shipped default and its bounds. A
354
+ * value outside the bounds is clamped, a non-number is the default: the knob
355
+ * tunes the pace, it can never switch the pacing off. */
356
+ export const EVIDENCE_WHY_INTERVAL_DEFAULT_S = TUI_DEFAULTS.evidence_why_interval_s;
357
+ export const EVIDENCE_WHY_INTERVAL_MIN_S = 5;
358
+ export const EVIDENCE_WHY_INTERVAL_MAX_S = 600;
359
+
360
+ /** The minimum gap, in ms, between the END of one sweep `why` exec and the
361
+ * start of the next. */
362
+ export function evidenceWhyIntervalMs(cfg: Pick<TuiConfig, "evidence_why_interval_s">): number {
363
+ const raw: unknown = cfg.evidence_why_interval_s;
364
+ const s = typeof raw === "number" && Number.isFinite(raw) ? raw : EVIDENCE_WHY_INTERVAL_DEFAULT_S;
365
+ return Math.min(EVIDENCE_WHY_INTERVAL_MAX_S, Math.max(EVIDENCE_WHY_INTERVAL_MIN_S, s)) * 1000;
366
+ }
367
+
368
+ /** Why a sweep did nothing at all this poll. */
369
+ export type SweepHold = "draining" | "heartbeat-stale";
370
+
371
+ /**
372
+ * The board's last status says the daemon should not be asked for anything
373
+ * now: it is draining (a deploy window — the container is busy finishing
374
+ * rounds), or its heartbeat is older than `[tui] stale_after_s` (the same
375
+ * bound the board's DATA STALE banner uses — the daemon writes `heartbeat`
376
+ * and `written_at` together). A heartbeat that cannot be read is not a fresh
377
+ * one (honest-unknown). A heartbeat AHEAD of this clock is clock skew between
378
+ * the PC and the VM, not staleness.
379
+ */
380
+ export function sweepHold(
381
+ snapshot: StatusSnapshot,
382
+ nowMs: number,
383
+ staleAfterS: number,
384
+ ): SweepHold | undefined {
385
+ if (snapshot.draining === true) return "draining";
386
+ const beat = typeof snapshot.heartbeat === "string" ? Date.parse(snapshot.heartbeat) : NaN;
387
+ if (!Number.isFinite(beat) || nowMs - beat > staleAfterS * 1000) return "heartbeat-stale";
388
+ return undefined;
389
+ }
390
+
391
+ // ---- the sweep's progress record -----------------------------
392
+
393
+ /** File name of the record, beside the TUI's other state files
394
+ * (`<sandbox root>/state/`). */
395
+ export const SWEEP_PROGRESS_FILE = "tui-evidence-sweep.json";
396
+ export const SWEEP_PROGRESS_VERSION = 1;
397
+
398
+ /** One PR's entry: the snapshot signature it was last cleanly reconciled at,
399
+ * and whether the store held anything for it once that reconcile was done.
400
+ * `stored: false` is the ordinary case of a PR whose rounds were all still
401
+ * RUNNING (or that had none): the reconcile was clean and copied nothing, so
402
+ * no `pr-<n>` dir exists — and a missing dir proves nothing lost. Only an
403
+ * entry that says the store held the PR's evidence can be caught out by that
404
+ * evidence having gone. */
405
+ export interface SweepEntry {
406
+ sig: string;
407
+ stored: boolean;
408
+ }
409
+
410
+ /** On disk: what `EvidenceCopier.reconciled` holds, and which store it is
411
+ * about. An entry here means "this PR, at this snapshot signature, was
412
+ * cleanly reconciled into THIS store" — a claim about the store, so the store
413
+ * is named in the record and a record about another store is not believed. */
414
+ export interface SweepProgressRecord {
415
+ version: typeof SWEEP_PROGRESS_VERSION;
416
+ store_dir: string;
417
+ updated_at: string;
418
+ /** PR number (decimal string key) → its entry. */
419
+ reconciled: Record<string, SweepEntry>;
420
+ }
421
+
422
+ /** Four answers, never a throw. `absent` (first run) and `read` are the only
423
+ * ones that are not news; `unreadable` and `other-store` are each treated as
424
+ * an EMPTY record with a notice — never as "all reconciled". */
425
+ export type SweepProgressRead =
426
+ | { kind: "absent" }
427
+ | { kind: "read"; reconciled: Map<number, SweepEntry> }
428
+ | { kind: "unreadable"; reason: string }
429
+ | { kind: "other-store"; storeDir: string };
430
+
431
+ /** The seam the copier keeps its progress through (a fake in tests). */
432
+ export interface SweepProgressStore {
433
+ read(storeDir: string): SweepProgressRead;
434
+ /** Atomic replace. May throw; the copier owns the notice. */
435
+ write(storeDir: string, reconciled: ReadonlyMap<number, SweepEntry>, at: Date): void;
436
+ }
437
+
438
+ /** Parse a record's text against the store it must describe. Strict: one
439
+ * malformed entry makes the whole record unreadable, because a half-believed
440
+ * record is a record nobody can reason about. */
441
+ export function parseSweepProgress(text: string, storeDir: string): SweepProgressRead {
442
+ let parsed: unknown;
443
+ try {
444
+ parsed = JSON.parse(text);
445
+ } catch (err) {
446
+ return { kind: "unreadable", reason: `not JSON (${msg(err)})` };
447
+ }
448
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
449
+ return { kind: "unreadable", reason: "not an object" };
450
+ const rec = parsed as Partial<Record<keyof SweepProgressRecord, unknown>>;
451
+ if (rec.version !== SWEEP_PROGRESS_VERSION)
452
+ return { kind: "unreadable", reason: `unknown version ${JSON.stringify(rec.version)}` };
453
+ if (typeof rec.store_dir !== "string" || rec.store_dir === "")
454
+ return { kind: "unreadable", reason: "no store_dir" };
455
+ const entries = rec.reconciled;
456
+ if (typeof entries !== "object" || entries === null || Array.isArray(entries))
457
+ return { kind: "unreadable", reason: "no reconciled map" };
458
+ const reconciled = new Map<number, SweepEntry>();
459
+ for (const [key, raw] of Object.entries(entries)) {
460
+ const entry = raw as Partial<Record<keyof SweepEntry, unknown>> | null;
461
+ if (
462
+ !/^[1-9]\d*$/.test(key) ||
463
+ typeof entry !== "object" ||
464
+ entry === null ||
465
+ Array.isArray(entry) ||
466
+ typeof entry.sig !== "string" ||
467
+ entry.sig === "" ||
468
+ typeof entry.stored !== "boolean"
469
+ )
470
+ return { kind: "unreadable", reason: `malformed entry ${JSON.stringify(key)}` };
471
+ reconciled.set(Number(key), { sig: entry.sig, stored: entry.stored });
472
+ }
473
+ if (resolve(rec.store_dir) !== resolve(storeDir))
474
+ return { kind: "other-store", storeDir: rec.store_dir };
475
+ return { kind: "read", reconciled };
476
+ }
477
+
478
+ /** The record as a file: read whole, written temp + rename. */
479
+ export class SweepProgressFile implements SweepProgressStore {
480
+ constructor(readonly path: string) {}
481
+
482
+ read(storeDir: string): SweepProgressRead {
483
+ let text: string;
484
+ try {
485
+ text = readFileSync(this.path, "utf8");
486
+ } catch (err) {
487
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") return { kind: "absent" };
488
+ return { kind: "unreadable", reason: msg(err) };
489
+ }
490
+ return parseSweepProgress(text, storeDir);
491
+ }
492
+
493
+ write(storeDir: string, reconciled: ReadonlyMap<number, SweepEntry>, at: Date): void {
494
+ const record: SweepProgressRecord = {
495
+ version: SWEEP_PROGRESS_VERSION,
496
+ store_dir: storeDir,
497
+ updated_at: at.toISOString(),
498
+ reconciled: Object.fromEntries(
499
+ [...reconciled]
500
+ .sort(([a], [b]) => a - b)
501
+ .map(([pr, { sig, stored }]) => [String(pr), { sig, stored }]),
502
+ ),
503
+ };
504
+ writeJsonFile(this.path, record); // atomic: a re-exec never reads a torn record
505
+ }
506
+ }
507
+
508
+ /** The local store: one dir per PR, one dir per round, manifest written last. */
509
+ export class EvidenceStore {
510
+ constructor(readonly dir: string) {}
511
+
512
+ ensure(): void {
513
+ mkdirSync(this.dir, { recursive: true });
514
+ }
515
+
516
+ roundDir(pr: number, round: number): string {
517
+ return join(this.dir, `pr-${pr}`, `round-${round}`);
518
+ }
519
+
520
+ /**
521
+ * Idempotence, and the whole of it: the manifest is written last, atomically,
522
+ * so its presence means every artefact for that round already landed. A
523
+ * crashed half-copy leaves no manifest and is simply redone — files are
524
+ * overwritten in place, never appended, so a retry cannot duplicate anything.
525
+ */
526
+ has(pr: number, round: number): boolean {
527
+ return existsSync(join(this.roundDir(pr, round), MANIFEST_NAME));
528
+ }
529
+
530
+ /** Whether the store holds anything at all for a PR (one stat, no walk). */
531
+ hasPr(pr: number): boolean {
532
+ return existsSync(join(this.dir, `pr-${pr}`));
533
+ }
534
+
535
+ /** Round numbers already copied for a PR (used only for reporting). */
536
+ copiedRounds(pr: number): number[] {
537
+ const dir = join(this.dir, `pr-${pr}`);
538
+ if (!existsSync(dir)) return [];
539
+ return completedRoundDirs(dir)
540
+ .map((d) => Number(d.slice("round-".length)))
541
+ .sort((a, b) => a - b);
542
+ }
543
+
544
+ writeArtifact(pr: number, round: number, role: EvidenceRole, body: string): void {
545
+ const dir = this.roundDir(pr, round);
546
+ mkdirSync(dir, { recursive: true });
547
+ writeFileSync(join(dir, LOCAL_NAME[role]), body);
548
+ }
549
+
550
+ writeManifest(m: RoundManifest): void {
551
+ const dir = this.roundDir(m.pr, m.round_no);
552
+ mkdirSync(dir, { recursive: true });
553
+ writeJsonFile(join(dir, MANIFEST_NAME), m); // atomic: a reader never sees a half-written marker
554
+ }
555
+ }
556
+
557
+ export interface CopierDeps {
558
+ /** Relative session dir inside the sandbox root (`[pi] session_dir`). */
559
+ sessionDir: string;
560
+ /** How long any single remote read may take before it is killed. */
561
+ timeoutMs: number;
562
+ onNotice?: (text: string) => void;
563
+ now?: () => Date;
564
+ /** Where the sweep keeps its per-PR progress across a re-exec or
565
+ * restart. Absent = in memory only (for the unit harness); the TUI always passes the file. */
566
+ progress?: SweepProgressStore;
567
+ /** The minimum gap between the END of one `why` exec and the
568
+ * start of the next — and between the copier's construction and its first
569
+ * `why`, so the pace holds across a re-exec — and with it at most one `why`
570
+ * per sweep. Absent or 0 = unpaced (the unit harness); the TUI always
571
+ * passes `evidenceWhyIntervalMs(cfg)`, which cannot be 0. */
572
+ whyIntervalMs?: number;
573
+ }
574
+
575
+ export interface SweepReport {
576
+ /** `pr-<n> round <k>` for each round that landed this sweep. */
577
+ copied: string[];
578
+ /** Human note for a PR whose copy could not complete (retried next poll). */
579
+ failures: string[];
580
+ /** Set when the sweep did nothing because of the board's last status. */
581
+ held?: SweepHold;
582
+ /** Set when a PR needed a `why` and the pace said not yet. */
583
+ paced?: boolean;
584
+ /** Notes about the progress record itself (unreadable, another store's,
585
+ * not saved) — announced once, never per poll. */
586
+ progress?: string[];
587
+ }
588
+
589
+ /**
590
+ * The copier itself. Driven from the TUI's existing poll — there is deliberately
591
+ * no second scheduler and no timer of its own: `sweep()` is called by the poll
592
+ * hook, never awaited by it, and a sweep already in flight makes the next call a
593
+ * no-op, so a wedged ssh can stall this and nothing else.
594
+ *
595
+ * Order: PRs needing a `why` are visited in a rotation by number
596
+ * starting at `nextFrom`. A failed PR moves the rotation past itself, so under
597
+ * pacing — one `why` per sweep — it is retried only after every other pending
598
+ * PR has had its turn (retried, never starving the rest). A PR that
599
+ * made progress but still has a backlog (more than `ROUNDS_PER_SWEEP` rounds)
600
+ * keeps the rotation on itself, so its backlog drains on consecutive turns.
601
+ */
602
+ export class EvidenceCopier {
603
+ private busy = false;
604
+ /** Last snapshot signature per PR that was fully reconciled, and whether
605
+ * the store held anything for it then. Persisted through `deps.progress`
606
+ * and loaded on the first sweep. */
607
+ private readonly reconciled = new Map<number, SweepEntry>();
608
+ /** The record has been loaded (or there is none to load). */
609
+ private loaded = false;
610
+ /** `reconciled` changed since the record was last written. */
611
+ private dirty = false;
612
+ /** A failed record write was announced; quiet until one succeeds. */
613
+ private writeFailAnnounced = false;
614
+ /** Rotation start: the next `why` goes to the first pending PR whose number
615
+ * is ≥ this, wrapping around. */
616
+ private nextFrom = 0;
617
+ /** When the last `why` exec RETURNED (ms since epoch), for the pace. A
618
+ * paced copier starts it at CONSTRUCTION: every TUI re-exec builds a new
619
+ * copier, and one that could fire at once would let a run of local builds
620
+ * — each a new generation — send `why`s faster than the interval. */
621
+ private lastWhyEndMs: number | undefined;
622
+ /** When THIS copier first saw each PR (ms since epoch). A round that
623
+ * finished before that moment is a BACKFILL: its pr-current roles (ledger,
624
+ * prompt, transcript) are skipped rather than stamped in as if today's
625
+ * files were that round's — only verdict.json is copied. In-memory on
626
+ * purpose: after a TUI restart everything already copied has a manifest,
627
+ * and anything not yet copied is honestly "first seen now". */
628
+ private readonly firstSeen = new Map<number, number>();
629
+ /** The last sweep was held. */
630
+ private held = false;
631
+ /** When the first sweep after a hold ran (ms since epoch; 0 = never held).
632
+ * The copier was not looking while it was held, so a round that finished
633
+ * before this moment did not finish under its watch either — a backfill,
634
+ * exactly as one that finished before `firstSeen`. It matters most after a
635
+ * drain: new rounds start the moment it lifts, so by the time the held
636
+ * rounds are copied the PR's current ledger, prompt and transcript may
637
+ * already be the next round's. */
638
+ private watchResumedMs = 0;
639
+ /** Rounds whose failure has already been announced — so a VM that stays
640
+ * unreachable degrades to silence rather than a notice every 3 seconds. */
641
+ private readonly announced = new Set<string>();
642
+
643
+ constructor(
644
+ private readonly cfg: TuiConfig,
645
+ readonly store: EvidenceStore,
646
+ private readonly ssh: SshRunner,
647
+ private readonly deps: CopierDeps,
648
+ ) {
649
+ if ((deps.whyIntervalMs ?? 0) > 0) this.lastWhyEndMs = this.nowMs();
650
+ }
651
+
652
+ /** True while a sweep is in flight (the poll hook's re-entry guard). */
653
+ get running(): boolean {
654
+ return this.busy;
655
+ }
656
+
657
+ /**
658
+ * One pass over the poll snapshot. Never throws: a failure is a note in the
659
+ * report and a retry on the next poll, because a TUI that cannot copy
660
+ * evidence is a TUI that must still paint.
661
+ */
662
+ async sweep(snapshot: StatusSnapshot | undefined): Promise<SweepReport> {
663
+ const report: SweepReport = { copied: [], failures: [] };
664
+ if (!snapshot || this.busy) return report;
665
+ this.busy = true;
666
+ try {
667
+ this.store.ensure();
668
+ this.loadProgress(report);
669
+ await this.visit(snapshot, report);
670
+ } catch (err) {
671
+ report.failures.push(`evidence sweep failed: ${msg(err)}`);
672
+ } finally {
673
+ this.saveProgress(report);
674
+ this.busy = false;
675
+ }
676
+ for (const note of report.progress ?? []) this.deps.onNotice?.(note);
677
+ for (const note of report.copied) this.deps.onNotice?.(note);
678
+ for (const note of report.failures) this.deps.onNotice?.(note);
679
+ return report;
680
+ }
681
+
682
+ /** The sweep's one pass over the snapshot (the caller owns busy, the
683
+ * record and the notices). */
684
+ private async visit(snapshot: StatusSnapshot, report: SweepReport): Promise<void> {
685
+ const nowMs = this.nowMs();
686
+ for (const pr of snapshot.prs)
687
+ if (!this.firstSeen.has(pr.number)) this.firstSeen.set(pr.number, nowMs);
688
+ // The board's last status says leave the daemon alone — no
689
+ // `why`, no listing, no fetch. The hold is visible on the board itself
690
+ // (the drain badge, DATA STALE), so it is not a notice.
691
+ const hold = sweepHold(snapshot, nowMs, this.cfg.stale_after_s);
692
+ if (hold !== undefined) {
693
+ report.held = hold;
694
+ this.held = true;
695
+ return;
696
+ }
697
+ // Back from a hold: the watch restarts now (see `watchResumedMs`).
698
+ if (this.held) {
699
+ this.held = false;
700
+ this.watchResumedMs = nowMs;
701
+ }
702
+ const byNumber = [...snapshot.prs].sort((a, b) => a.number - b.number);
703
+ // Zero excludes expired rounds, so every new signature needs one why read.
704
+ const pending = byNumber.filter(
705
+ (pr) => this.reconciled.get(pr.number)?.sig !== prSignature(pr),
706
+ );
707
+ const start = pending.findIndex((pr) => pr.number >= this.nextFrom);
708
+ const rotation = start <= 0 ? pending : [...pending.slice(start), ...pending.slice(0, start)];
709
+ for (const pr of rotation) {
710
+ if (!this.whyDue()) {
711
+ report.paced = true;
712
+ return;
713
+ }
714
+ const sig = prSignature(pr);
715
+ const before = report.copied.length;
716
+ const ok = await this.reconcilePr(pr.number, report);
717
+ // Only a clean reconcile retires the signature; anything else is
718
+ // retried on a later poll, which is what "survives a wedged ssh"
719
+ // means here.
720
+ if (ok) this.retire(pr.number, sig);
721
+ const progressed = report.copied.length > before;
722
+ // A backlog still draining keeps the rotation on this PR; a finished
723
+ // PR or a FAILED one moves it on — a failure must not make the same
724
+ // PR the first pick every turn.
725
+ this.nextFrom = !ok && progressed ? pr.number : pr.number + 1;
726
+ // Successful work is bounded to one PR per sweep: the poll runs every
727
+ // few seconds and a backlog drains across ticks without ever holding
728
+ // the loop. A FAILURE does not consume the sweep — a failed PR keeps
729
+ // its stale signature, so stopping on it would make one permanently
730
+ // failing PR the first pick every tick and starve all the others
731
+ // forever. Move on to the next PR instead (when the pace allows; it
732
+ // otherwise waits for the next turn of the rotation).
733
+ if (ok || progressed) return;
734
+ }
735
+ }
736
+
737
+ private nowMs(): number {
738
+ return (this.deps.now?.() ?? new Date()).getTime();
739
+ }
740
+
741
+ /** May a `why` run now? Unpaced (the unit harness) always; paced, only once
742
+ * the full interval has passed since the previous one RETURNED — measured
743
+ * from the end, so a `why` slowed by a starved container is followed by the
744
+ * same idle gap as a fast one, never by another `why` at once — or, before
745
+ * the first, since this copier was built. */
746
+ private whyDue(): boolean {
747
+ const interval = this.deps.whyIntervalMs ?? 0;
748
+ if (interval <= 0 || this.lastWhyEndMs === undefined) return true;
749
+ return this.nowMs() - this.lastWhyEndMs >= interval;
750
+ }
751
+
752
+ /** Was this PR last reconciled at a signature that counted rounds? */
753
+ /** Record a clean reconcile, with whether the store now holds anything
754
+ * for the PR (one stat, only when the signature moved). */
755
+ private retire(pr: number, sig: string): void {
756
+ if (this.reconciled.get(pr)?.sig === sig) return;
757
+ this.reconciled.set(pr, { sig, stored: this.store.hasPr(pr) });
758
+ this.dirty = true;
759
+ }
760
+
761
+ private note(report: SweepReport, text: string): void {
762
+ (report.progress ??= []).push(text);
763
+ }
764
+
765
+ /**
766
+ * First sweep only: believe the record where it can be believed. An entry
767
+ * that says the store held the PR's evidence is kept only while the store
768
+ * still holds that PR — a record cannot vouch for evidence that is no
769
+ * longer on disk. An entry that says the store held nothing (its rounds
770
+ * were all still running, or it had none) is believed without a dir: there
771
+ * was never one to lose.
772
+ */
773
+ private loadProgress(report: SweepReport): void {
774
+ if (this.loaded) return;
775
+ this.loaded = true;
776
+ const progress = this.deps.progress;
777
+ if (!progress) return;
778
+ const read = progress.read(this.store.dir);
779
+ switch (read.kind) {
780
+ case "absent":
781
+ return;
782
+ case "unreadable":
783
+ this.dirty = true; // replace it with a readable one at the next save
784
+ this.note(
785
+ report,
786
+ `evidence: sweep progress record unreadable (${read.reason}) — re-checking every PR`,
787
+ );
788
+ return;
789
+ case "other-store":
790
+ this.dirty = true;
791
+ this.note(
792
+ report,
793
+ `evidence: sweep progress record is for another store (${read.storeDir}) — re-checking every PR`,
794
+ );
795
+ return;
796
+ case "read": {
797
+ let gone = 0;
798
+ for (const [pr, entry] of read.reconciled) {
799
+ if (entry.stored && !this.store.hasPr(pr)) {
800
+ gone += 1;
801
+ continue;
802
+ }
803
+ this.reconciled.set(pr, entry);
804
+ }
805
+ if (gone > 0) {
806
+ this.dirty = true;
807
+ this.note(
808
+ report,
809
+ `evidence: ${gone} PR${gone === 1 ? "" : "s"} in the sweep record ${gone === 1 ? "has" : "have"} no evidence on disk — re-checking ${gone === 1 ? "it" : "them"}`,
810
+ );
811
+ }
812
+ return;
813
+ }
814
+ }
815
+ }
816
+
817
+ /** Write the record when it changed. A failed write costs a notice (once)
818
+ * and a retry at the next sweep (a temp file and a rename, local) — never
819
+ * the sweep itself. */
820
+ private saveProgress(report: SweepReport): void {
821
+ const progress = this.deps.progress;
822
+ if (!progress || !this.dirty) return;
823
+ try {
824
+ progress.write(this.store.dir, this.reconciled, this.deps.now?.() ?? new Date());
825
+ this.dirty = false;
826
+ this.writeFailAnnounced = false;
827
+ } catch (err) {
828
+ if (!this.writeFailAnnounced) {
829
+ this.writeFailAnnounced = true;
830
+ this.note(
831
+ report,
832
+ `evidence: sweep progress not saved (${msg(err)}) — a restart re-checks every PR`,
833
+ );
834
+ }
835
+ }
836
+ }
837
+
838
+ private async reconcilePr(pr: number, report: SweepReport): Promise<boolean> {
839
+ let res: RemoteResult;
840
+ try {
841
+ res = await this.ssh(this.cfg.ssh_host, whyArgv(this.cfg, pr), {
842
+ timeoutMs: this.deps.timeoutMs,
843
+ });
844
+ } finally {
845
+ this.lastWhyEndMs = this.nowMs();
846
+ }
847
+ if (!res.ok)
848
+ return this.fail(
849
+ report,
850
+ `pr-${pr}`,
851
+ `evidence: #${pr} — could not read round list (ssh${remoteCause(res)})`,
852
+ );
853
+ // The round list is what `qcr why --json` PROVES; anything else that came
854
+ // back — a wrapper's own JSON, an older build's shape, a torn write — is an
855
+ // unreadable list, not an empty one. Reading it as empty would retire this
856
+ // PR's signature with nothing copied, and nothing would ever retry it.
857
+ const rounds = readWhyRounds(res.stdout);
858
+ if (rounds === undefined)
859
+ return this.fail(report, `pr-${pr}`, `evidence: #${pr} — unreadable round list`);
860
+ this.announced.delete(`pr-${pr}`);
861
+ const pending = rounds.filter((r) => !this.store.has(pr, r.round_no));
862
+ if (pending.length === 0) return true;
863
+ let complete = true;
864
+ for (const round of pending.slice(0, ROUNDS_PER_SWEEP)) {
865
+ const done = await this.copyRound(pr, round, report);
866
+ if (!done) {
867
+ complete = false;
868
+ break; // the VM is unhappy; stop hammering it this tick
869
+ }
870
+ }
871
+ return complete && pending.length <= ROUNDS_PER_SWEEP;
872
+ }
873
+
874
+ private async copyRound(pr: number, round: WhyRound, report: SweepReport): Promise<boolean> {
875
+ const key = `pr-${pr} round ${round.round_no}`;
876
+ const listed = await this.ssh(
877
+ this.cfg.ssh_host,
878
+ manifestArgv(this.cfg, this.deps.sessionDir, pr, round.round_no),
879
+ {
880
+ timeoutMs: this.deps.timeoutMs,
881
+ },
882
+ );
883
+ if (!listed.ok)
884
+ return this.fail(
885
+ report,
886
+ key,
887
+ `evidence: ${key} — could not list artefacts (ssh${remoteCause(listed)})`,
888
+ );
889
+ const files = parseManifest(listed.stdout, remoteSandboxRoot(this.cfg));
890
+ // A round that finished before this copier first saw the PR — or before
891
+ // it came back from a hold — is a backfill: the pr-current
892
+ // roles on the VM are the PR's files as of TODAY, not that round's, so
893
+ // copying them would fabricate round evidence. Verdict only. An
894
+ // unparseable/absent finished_at is treated as backfill — the honest
895
+ // direction, since freshness cannot be shown.
896
+ const watchedFrom = Math.max(this.firstSeen.get(pr) ?? 0, this.watchResumedMs);
897
+ const finishedMs = round.finished_at === null ? NaN : Date.parse(round.finished_at);
898
+ const backfill = !Number.isFinite(finishedMs) || finishedMs < watchedFrom;
899
+ const skipped: EvidenceRole[] = backfill
900
+ ? PR_CURRENT_ROLES.filter((r) => files.some((f) => f.role === r))
901
+ : [];
902
+ const toCopy = backfill ? files.filter((f) => !PR_CURRENT_ROLES.includes(f.role)) : files;
903
+ // A role found nowhere live may be in the PR's archive. Its
904
+ // manifest says where; a role it holds is `archived` (a pr-current one
905
+ // in a backfill is skipped as stale, exactly as a live one is), never
906
+ // `missing`. A manifest that cannot be fetched or read fails the copy —
907
+ // a manifest written now would say `missing` forever.
908
+ const archived: NonNullable<RoundManifest["archived"]> = [];
909
+ const absent = EVIDENCE_ROLES.filter((r) => !files.some((f) => f.role === r));
910
+ const manifestPath = parseArchiveManifestLine(listed.stdout, remoteSandboxRoot(this.cfg), pr);
911
+ if (absent.length > 0 && manifestPath) {
912
+ const got = await this.ssh(this.cfg.ssh_host, fetchArgv(this.cfg, manifestPath), {
913
+ timeoutMs: this.deps.timeoutMs,
914
+ });
915
+ if (!got.ok)
916
+ return this.fail(
917
+ report,
918
+ key,
919
+ `evidence: ${key} — archive manifest fetch failed${remoteCause(got)}`,
920
+ );
921
+ const manifest = parseArchiveManifest(got.stdout);
922
+ if (!manifest)
923
+ return this.fail(report, key, `evidence: ${key} — unreadable archive manifest`);
924
+ for (const role of absent) {
925
+ const at = archivedRole(manifest, role, pr, round.round_no, this.deps.sessionDir);
926
+ if (!at) continue;
927
+ if (backfill && PR_CURRENT_ROLES.includes(role)) skipped.push(role);
928
+ else archived.push({ role, ...at });
929
+ }
930
+ }
931
+ const written: RoundManifest["files"] = [];
932
+ for (const f of toCopy) {
933
+ const got = await this.ssh(this.cfg.ssh_host, fetchArgv(this.cfg, f.remotePath), {
934
+ timeoutMs: this.deps.timeoutMs,
935
+ });
936
+ // An ssh that failed is a transport fault, not an absent artefact: leaving
937
+ // the manifest unwritten is what makes the round come back next poll.
938
+ if (!got.ok)
939
+ return this.fail(
940
+ report,
941
+ key,
942
+ `evidence: ${key} — ${f.role} fetch failed${remoteCause(got)}`,
943
+ );
944
+ this.store.writeArtifact(pr, round.round_no, f.role, got.stdout);
945
+ written.push({
946
+ role: f.role,
947
+ remote_path: f.remotePath,
948
+ chars: got.stdout.length,
949
+ // The cap is BYTES (`head -c`) and stdout arrives decoded, so compare
950
+ // in bytes — multibyte text would otherwise read as short of the cap.
951
+ truncated: Buffer.byteLength(got.stdout, "utf8") >= MAX_FILE_BYTES,
952
+ scope: PR_CURRENT_ROLES.includes(f.role) ? "pr-current" : "round",
953
+ from: isArchivedGzip(f.remotePath, remoteSandboxRoot(this.cfg)) ? "archive" : "live",
954
+ });
955
+ }
956
+ this.store.writeManifest({
957
+ pr,
958
+ round_no: round.round_no,
959
+ copied_at: (this.deps.now?.() ?? new Date()).toISOString(),
960
+ round,
961
+ roles_note: ROLES_NOTE,
962
+ files: written,
963
+ archived,
964
+ missing: EVIDENCE_ROLES.filter(
965
+ (r) =>
966
+ !written.some((w) => w.role === r) &&
967
+ !skipped.includes(r) &&
968
+ !archived.some((a) => a.role === r),
969
+ ),
970
+ skipped_stale: skipped,
971
+ });
972
+ this.announced.delete(key);
973
+ report.copied.push(
974
+ `evidence: ${key} copied (${written.length} file${written.length === 1 ? "" : "s"}` +
975
+ `${archived.length > 0 ? `, ${archived.length} archived` : ""})`,
976
+ );
977
+ return true;
978
+ }
979
+
980
+ /** Announce a failure once, then stay quiet until that PR's round list
981
+ * (`pr-<n>`) or that round (`pr-<n> round <k>`) next reads cleanly. */
982
+ private fail(report: SweepReport, key: string, text: string): false {
983
+ if (!this.announced.has(key)) {
984
+ this.announced.add(key);
985
+ report.failures.push(text);
986
+ }
987
+ return false;
988
+ }
989
+ }
990
+
991
+ /** A failed remote call's cause for a notice: its exit status and the last
992
+ * line of its stderr, bounded. */
993
+ function remoteCause(res: RemoteResult): string {
994
+ const last = res.stderr.trim().split("\n").pop()?.trim() ?? "";
995
+ const status = res.code === null ? "no exit status" : `exit ${res.code}`;
996
+ return `: ${status}${last ? ` — ${last.length > 160 ? `${last.slice(0, 160)}…` : last}` : ""}`;
997
+ }
998
+
999
+ const msg = errText;