@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,1253 @@
1
+ // The optional escalation feature (the never-hand-to-a-human rule).
2
+ //
3
+ // OFF BY DEFAULT. With `[escalation] enabled = false` — what every shipped
4
+ // config carries — nothing here runs: no rule is evaluated, no note is
5
+ // rendered into any prompt, no mutation is built. `tests/escalation.test.ts`
6
+ // pins that as a property, not a hope.
7
+ //
8
+ // ---------------------------------------------------------------------------
9
+ // THE TWO INVARIANTS, AND HOW THEY ARE STRUCTURAL RATHER THAN PROMPTED
10
+ // ---------------------------------------------------------------------------
11
+ //
12
+ // 1. THE REVIEWER STILL DECIDES. Escalation is additive notification beside a
13
+ // finished verdict; it never defers, never parks, never hands the review
14
+ // back (manifesto belief 2; there is no park).
15
+ //
16
+ // Enforced by SHAPE, not by discipline: nothing in this module's public
17
+ // surface returns, produces or contains a `Verdict`. `deliverEscalations`
18
+ // resolves to `EscalationOutcome[]` — a delivery record — so there is no
19
+ // channel through which a verdict could be withheld, altered or postponed.
20
+ // The one caller (`src/verdicts.ts`) invokes it AFTER the verdict has been
21
+ // applied and the PR transitioned, wrapped so a throw cannot reach the
22
+ // verdict path. `tests/escalation.test.ts` re-reads this source and fails
23
+ // if the word `Verdict` ever appears in an exported signature here, and
24
+ // `tests/verdicts.test.ts` pins the verdict landed with escalation on
25
+ // against the same round with it off.
26
+ //
27
+ // 2. ESCALATE A QUESTION, NEVER A REVIEW. The human is never asked to "review
28
+ // this PR". They are pointed at one thing — a file and line range, a
29
+ // migration, a named decision — the smallest thing that actually needs
30
+ // their eyes. Handing someone a whole review is what this system exists to
31
+ // stop; an escalation that does it has failed even when it fired on exactly
32
+ // the right PR.
33
+ //
34
+ // Enforced five ways — four braces and one belt. (a) `EscalationScope` is a
35
+ // discriminated union with no "the whole PR" member. (b)
36
+ // `validateEscalationRequest` refuses a scope whose path or decision is
37
+ // blank — an escalation with nothing to look at is not an escalation.
38
+ // (c) EVERY path in a scope must be a file THIS PR ACTUALLY TOUCHES —
39
+ // including a `decision` scope's optional one — compared exactly against
40
+ // the diff, so `path: "the entire diff"` or `path: "**"` cannot spell a
41
+ // whole-PR escalation. (d) the comment body is RENDERED from the scope
42
+ // by `renderEscalationComment`, and `decision` is capped to a NAME, not a
43
+ // paragraph. (e) the belt: all three free-text slots — `question`,
44
+ // `decision`, and a session's `why` — go through `asksForWholeReview`.
45
+ //
46
+ // (a)–(d) are the BRACES and they are exact: a whole-PR *scope* is
47
+ // unspellable. (e) is a heuristic over natural language, it will never be
48
+ // complete, and it is deliberately tuned to under-block rather than over-
49
+ // block — a false positive is a SILENT DROP, and the braces are what the
50
+ // guarantee rests on.
51
+ //
52
+ // This is the discipline the codebase already has twice — `[check this]`
53
+ // inline comments and the `### For a human to double-check` review section
54
+ // both hand a person a small, minutes-verifiable job beside a finished verdict
55
+ // (see `packages/pi-plugin/src/severity.ts`). Escalation is that mechanism WITH A NAMED
56
+ // RECIPIENT, not a new one, and the rendered text deliberately reads like
57
+ // them.
58
+ //
59
+ // ---------------------------------------------------------------------------
60
+ // TRIGGERS
61
+ // ---------------------------------------------------------------------------
62
+ //
63
+ // * Operator-defined DETERMINISTIC rules always fire. Paths touched, a PR
64
+ // label, a severity floor, a named check failing — `evaluateEscalationRules`
65
+ // is a pure function over facts the daemon already has, inspectable and
66
+ // testable with no LLM anywhere near it.
67
+ // * The PR's session MAY ALSO escalate on its own judgement, when the operator
68
+ // turned that on. It is bounded three ways and must stay so: it can only
69
+ // reach a login in `[escalation] targets` (filtered here, daemon-side, not
70
+ // asked of the model), it can never replace a verdict (invariant 1), and it
71
+ // rides the same per-PR cap, the same dedup ledger and the same mutation
72
+ // gate as everything else.
73
+ //
74
+ // ---------------------------------------------------------------------------
75
+ // DELIVERY
76
+ // ---------------------------------------------------------------------------
77
+ //
78
+ // One path, daemon-owned, for both trigger kinds and both shapes — which is
79
+ // what makes the target bound and the scope check structural instead of
80
+ // prompt-level.
81
+ //
82
+ // * `comment` (the default): a gated `create_review` with `event: "COMMENT"`
83
+ // and `reviewPurpose: "escalation"` — the same mechanically-authored gated
84
+ // comment the migration pre-check posts. No new mutation
85
+ // kind, no new posting route, and (like the pre-check) it never claims the
86
+ // head, because it carries no verdict.
87
+ // * `review_request`: the comment, PLUS the `request_reviewer` gate kind, so
88
+ // the PR enters that person's review-requested queue. GitHub's own UI frames
89
+ // that as "review this pull request" — precisely the framing invariant 2
90
+ // rules out — so the comment is NOT optional on this shape: it is what
91
+ // carries the narrow ask. `deliverEscalations` posts the comment first and
92
+ // requests only after it succeeded.
93
+ //
94
+ // Both ride the ONE MutationGate. `request_reviewer` is an ORDINARY kind:
95
+ // suppressed in dry-run like everything else, never `forceLive`, never in
96
+ // `APP_SELF_CONFIG_KINDS`.
97
+ import { createHash } from "node:crypto";
98
+ import { GitHubRequestError } from "./github.js";
99
+ import type { EscalationRecoveryRow, StateStore } from "./state.js";
100
+ import type { EscalationConfig, EscalationRule, EscalationSeverityFloor } from "./config.js";
101
+ import { codePath } from "@quickchatai/pi-quickreview/src/codespan.js";
102
+ import { globBody } from "@quickchatai/pi-quickreview/src/glob.js";
103
+ import { severityRank } from "@quickchatai/pi-quickreview/src/severityrank.js";
104
+ import type { GateResult } from "@quickchatai/pi-quickreview/src/gate.js";
105
+
106
+ // ---- what to look at ------------------------------------------------------
107
+
108
+ /**
109
+ * The thing a human is being pointed at. A discriminated union with no
110
+ * "whole PR" member — that omission is invariant 2's first line of defence:
111
+ * a whole-PR escalation cannot be spelled, so it cannot be built.
112
+ */
113
+ export type EscalationScope =
114
+ | { kind: "code"; path: string; start_line?: number; end_line?: number }
115
+ | { kind: "migration"; path: string; migration?: string }
116
+ | { kind: "decision"; decision: string; path?: string };
117
+
118
+ /**
119
+ * Stable identity of a scope, for the dedup ledger.
120
+ *
121
+ * Line numbers are deliberately not in the key. The unit a
122
+ * human experiences is "you were pointed at this file" — keying on the line
123
+ * range would make an escalation at lines 12–30 and one at 14–32 two distinct
124
+ * asks producing near-identical notes, which is the nag the ledger exists to
125
+ * prevent. Two genuinely different places in one file cost one escalation
126
+ * between them; `max_per_pr` bounds that anyway.
127
+ */
128
+ export function scopeKey(scope: EscalationScope): string {
129
+ switch (scope.kind) {
130
+ case "code":
131
+ return `code:${scope.path}`;
132
+ case "migration":
133
+ return `migration:${scope.path}`;
134
+ case "decision":
135
+ // The decision text IS the identity here, and it is capped and
136
+ // whitespace-collapsed by normalizeScope, so it cannot drift by spacing.
137
+ return `decision:${scope.decision}:${scope.path ?? ""}`;
138
+ }
139
+ }
140
+
141
+ /** The scope as one operator-facing phrase — the "Look at:" line. Paths are
142
+ * the PR author's file names, so each renders through `codePath`: a name
143
+ * holding backticks or newlines stays inside its own code span instead of
144
+ * writing prose (or an @-mention) into a bot-authored comment. */
145
+ export function renderScope(scope: EscalationScope): string {
146
+ switch (scope.kind) {
147
+ case "code": {
148
+ const lines =
149
+ scope.start_line && scope.end_line && scope.end_line !== scope.start_line
150
+ ? `, lines ${scope.start_line}–${scope.end_line}`
151
+ : scope.start_line
152
+ ? `, line ${scope.start_line}`
153
+ : "";
154
+ return `${codePath(scope.path)}${lines}`;
155
+ }
156
+ case "migration":
157
+ return scope.migration
158
+ ? `migration ${codePath(scope.migration)} (${codePath(scope.path)})`
159
+ : `the migration in ${codePath(scope.path)}`;
160
+ case "decision":
161
+ return scope.path
162
+ ? `the decision "${scope.decision}" (${codePath(scope.path)})`
163
+ : `the decision "${scope.decision}"`;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Coerce a scope out of whatever a session wrote (or a rule assembled).
169
+ * Returns null when there is nothing concrete to look at — which is the
170
+ * refusal invariant 2 asks for, expressed as a value rather than a throw.
171
+ */
172
+ export function normalizeScope(raw: unknown): EscalationScope | null {
173
+ if (typeof raw !== "object" || raw === null) return null;
174
+ const r = raw as Record<string, unknown>;
175
+ const str = (v: unknown): string => (typeof v === "string" ? v.trim() : "");
176
+ const num = (v: unknown): number | undefined => {
177
+ const n =
178
+ typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : typeof v === "number" ? v : NaN;
179
+ return Number.isInteger(n) && n > 0 ? n : undefined;
180
+ };
181
+ const kind = str(r.kind).toLowerCase();
182
+ const path = str(r.path);
183
+ if (kind === "migration") {
184
+ if (!path) return null;
185
+ const migration = str(r.migration).slice(0, 80);
186
+ return migration ? { kind: "migration", path, migration } : { kind: "migration", path };
187
+ }
188
+ if (kind === "decision") {
189
+ // Capped and whitespace-collapsed HERE, so `decision` cannot become a
190
+ // paragraph-length free-text field carrying a whole-review ask.
191
+ // `validateEscalationRequest` additionally puts it through
192
+ // `asksForWholeReview` (session origin), exactly like `question`.
193
+ const decision = decisionName(str(r.decision));
194
+ if (!decision) return null;
195
+ return path ? { kind: "decision", decision, path } : { kind: "decision", decision };
196
+ }
197
+ // Default and by far the common case: a place in the code.
198
+ if (!path) return null;
199
+ const start = num(r.start_line);
200
+ // An end without a start is meaningless, so a lone `end_line` is dropped.
201
+ const end = start === undefined ? undefined : num(r.end_line);
202
+ return {
203
+ kind: "code",
204
+ path,
205
+ ...(start !== undefined ? { start_line: start } : {}),
206
+ ...(end !== undefined ? { end_line: Math.max(end, start!) } : {}),
207
+ };
208
+ }
209
+
210
+ // ---- the request ----------------------------------------------------------
211
+
212
+ /** Where an escalation came from. Recorded on every delivery. */
213
+ export type EscalationOrigin = "rule" | "session";
214
+
215
+ export interface EscalationRequest {
216
+ /** GitHub login, no leading "@". Always one of `[escalation] targets`. */
217
+ target: string;
218
+ /** WHAT to look at. */
219
+ scope: EscalationScope;
220
+ /** The narrow ask — one or two sentences about the scope, never about the PR. */
221
+ question: string;
222
+ /** WHY this person. From the rule's `why`, or the session's own reason. */
223
+ why: string;
224
+ origin: EscalationOrigin;
225
+ /** The rule that fired, when `origin === "rule"`. */
226
+ rule?: string;
227
+ }
228
+
229
+ /**
230
+ * Why an escalation was refused. Every one is an ANSWER, never a throw:
231
+ * escalation is notification, and a malformed one costs a log line and
232
+ * nothing else — least of all a verdict.
233
+ */
234
+ export type EscalationRefusal =
235
+ | "disabled"
236
+ | "no-target"
237
+ | "target-not-configured"
238
+ | "no-scope"
239
+ /** A `code`/`migration` scope naming a file the PR does not touch. */
240
+ | "path-not-in-diff"
241
+ | "no-question"
242
+ | "asks-for-whole-review"
243
+ | "session-judgement-off"
244
+ | "capped"
245
+ | "already-delivered";
246
+
247
+ export type EscalationValidation =
248
+ { ok: true; request: EscalationRequest } | { ok: false; refusal: EscalationRefusal };
249
+
250
+ /** Longest question we will carry. A paragraph is not a narrow ask. */
251
+ export const MAX_ESCALATION_QUESTION = 400;
252
+
253
+ /** Longest `decision` name. A decision is a NAME, not an essay — an uncapped
254
+ * one would be a route to a whole-review ask. */
255
+ export const MAX_ESCALATION_DECISION = 120;
256
+
257
+ /**
258
+ * Invariant 2's belt, and it is only a belt.
259
+ *
260
+ * The braces are structural: the union has no whole-PR member, a `code`/
261
+ * `migration` path must be a file the PR actually touches, the body is
262
+ * rendered from the scope, and `decision` is capped. What is left is two short
263
+ * free-text slots — `question` and `decision` — and this is what keeps a
264
+ * whole-review ask out of them.
265
+ *
266
+ * It must catch whole-review phrasings ("Could you cast an eye over all of
267
+ * it?", "Are you happy with everything here?") without treating `whole`,
268
+ * `entire`, `everything` as damning on their own. Those are the standard
269
+ * vocabulary of SCOPE-OF-EFFECT questions, and a scope-of-effect question is
270
+ * the canonical thing this feature exists to ask:
271
+ *
272
+ * Does this lock the whole row, or just the counter?
273
+ * Is the entire cache invalidated here, or only the tenant slice?
274
+ *
275
+ * A false positive here is a SILENT DROP — `escalation_refused`, no delivery,
276
+ * nobody told. Over-blocking is therefore the more expensive direction, not
277
+ * the safe one: it is the difference between a feature that works when
278
+ * switched on and one that quietly does not.
279
+ *
280
+ * So a breadth word only damns when it is talking about THE CHANGE:
281
+ *
282
+ * BREADTH — a word that means "the lot": whole, entire, everything,
283
+ * all of it, a second pair of eyes, top to bottom… On its own
284
+ * it means nothing; "the whole row" is as narrow as it gets.
285
+ * ARTIFACT — the change named as a whole: PR, diff, branch, the changes.
286
+ * ASK — a review-ish verb: review, sign off, cast an eye, happy with.
287
+ *
288
+ * refuse ⟺ (BREADTH ∧ (ARTIFACT ∨ ASK)) ∨ (ARTIFACT ∧ ASK)
289
+ *
290
+ * "the whole row" has BREADTH and neither of the others — narrow, allowed.
291
+ * "the whole diff" has BREADTH and ARTIFACT — refused. Pure intensifiers
292
+ * (`at all`, `overall`) are deliberately NOT breadth words: "is this index
293
+ * used at all?" is a perfectly narrow question.
294
+ *
295
+ * It will never be complete — natural language is not a closed set. Whole-PR
296
+ * phrasings that name no artifact and use no breadth word ("Is this safe to
297
+ * merge?", "LGTM?", "Any objections?") get through, and that is accepted: the
298
+ * BRACES are what make a whole-PR *scope* unspellable, and this is only the
299
+ * belt over the free-text slots. Both sides are pinned in
300
+ * `tests/escalation.test.ts` — the phrasings it must catch, and the narrow
301
+ * questions it must not touch.
302
+ */
303
+ // NB `at all` and `overall` are absent on purpose: they are pure
304
+ // intensifiers, not breadth — "is this index used at all?" is narrow.
305
+ const BREADTH_RE =
306
+ /\b(?:whole|entire|everything|in general|as a whole|top to bottom|end[- ]to[- ]end|full review|all of (?:it|this|them|these)|it all|(?:second|another|extra|fresh) (?:pair|set) of eyes|review it|look at it)\b/i;
307
+
308
+ const WHOLE_ARTIFACT_RE =
309
+ /\b(?:pull\s*requests?|prs?|mrs?|merge\s*requests?|diffs?|branch|change\s?sets?|changes|patch|submission|the code here)\b/i;
310
+
311
+ const REVIEW_ASK_RE =
312
+ /\b(?:review(?:ed|ing|s)?|look(?:ed|ing)?\s+(?:at|over|through)|read(?:ing)?\s+(?:through|over)|check(?:ing|s)?\s+(?:over|through)|cast\s+(?:an?\s+)?eye|eyes?|glance|skim|scan|go(?:ing)?\s+through|sign(?:ing|s)?\s+off|approv(?:e|al|ing)|happy\s+with|comfortable\s+with|opinion\s+on|thoughts\s+on|merged?|ship(?:s|ped|ping)?|bless)\b/i;
313
+
314
+ /**
315
+ * Does this text ask for a review of the change rather than a look at one
316
+ * thing? Applied to all THREE free-text slots — `question`, a `decision`
317
+ * name, and (for a session-origin escalation only) `why`.
318
+ */
319
+ export function asksForWholeReview(text: string): boolean {
320
+ const artifact = WHOLE_ARTIFACT_RE.test(text);
321
+ const ask = REVIEW_ASK_RE.test(text);
322
+ // A breadth word damns only when it is talking about the change — either by
323
+ // naming it, or by asking for a review of it.
324
+ if (BREADTH_RE.test(text) && (artifact || ask)) return true;
325
+ return artifact && ask;
326
+ }
327
+
328
+ /**
329
+ * Turn a candidate — a fired rule's assembly, or whatever a session wrote into
330
+ * its verdict file — into a deliverable request, or refuse it.
331
+ *
332
+ * Every gate invariant 2 and the target bound need lives here, in one
333
+ * function, so there is exactly one place to read and exactly one to test.
334
+ */
335
+ export function validateEscalationRequest(
336
+ raw: unknown,
337
+ cfg: EscalationConfig,
338
+ /**
339
+ * WHERE THIS CANDIDATE CAME FROM — an argument, never a field on `raw`.
340
+ *
341
+ * This is a trust boundary. For a session candidate `raw` IS the verdict
342
+ * file's `escalations` entry, which the model writes, so an `origin` read
343
+ * off the candidate would let a session set `"origin": "rule"`, buy the
344
+ * rule-origin belt exemption on all three free-text slots, and forge the
345
+ * audit trail besides (the ledger row and the rendered comment both name
346
+ * the rule it claimed).
347
+ *
348
+ * `verdicts.ts` builds the two groups separately, so origin is a property
349
+ * of the CALL SITE.
350
+ */
351
+ origin: EscalationOrigin,
352
+ /**
353
+ * The PR's changed files. Every path in a scope must name one of them, so
354
+ * `path: "the entire diff"` or `path: "**"` cannot spell a whole-PR
355
+ * escalation.
356
+ *
357
+ * REQUIRED IN PRACTICE. `deliverEscalations` always passes it, and when the
358
+ * file listing is unavailable it passes `[]` — so every path-bearing scope
359
+ * is refused rather than waved through. Silence beats a whole-PR ask, and
360
+ * that is the same direction every other unreadable fact takes here. The
361
+ * parameter is optional only so `decision`-scoped unit cases and callers
362
+ * with no diff in reach can still validate the rest.
363
+ */
364
+ changedPaths?: readonly string[],
365
+ ): EscalationValidation {
366
+ if (!cfg.enabled) return { ok: false, refusal: "disabled" };
367
+ if (typeof raw !== "object" || raw === null) return { ok: false, refusal: "no-scope" };
368
+ const r = raw as Record<string, unknown>;
369
+ const str = (v: unknown): string => (typeof v === "string" ? v.trim() : "");
370
+
371
+ const target = str(r.target).replace(/^@/, "");
372
+ if (!target) return { ok: false, refusal: "no-target" };
373
+ // THE BOUND. Filtered daemon-side against the operator's list — never asked
374
+ // of the model, never negotiable by the text of a verdict file.
375
+ if (!cfg.targets.includes(target)) return { ok: false, refusal: "target-not-configured" };
376
+
377
+ // NB `r.origin` is deliberately NOT read. The caller says where this came
378
+ // from; a candidate that labels itself is labelling its own trust level.
379
+ if (origin === "session" && !cfg.session_judgement)
380
+ return { ok: false, refusal: "session-judgement-off" };
381
+
382
+ const scope = normalizeScope(r.scope);
383
+ if (!scope) return { ok: false, refusal: "no-scope" };
384
+ // EVERY path in a scope must be a REAL file in this PR's diff — including a
385
+ // `decision` scope's OPTIONAL path, which would otherwise render any string
386
+ // and mint a distinct dedup key. Compared exactly against what GitHub
387
+ // returned — no globbing, no prefix match, nothing an invented string could
388
+ // satisfy.
389
+ const scopePath: string | undefined = scope.path;
390
+ if (scopePath !== undefined && changedPaths !== undefined && !changedPaths.includes(scopePath)) {
391
+ return { ok: false, refusal: "path-not-in-diff" };
392
+ }
393
+ const oneLine = (v: unknown): string =>
394
+ str(v).replace(/\s+/g, " ").slice(0, MAX_ESCALATION_QUESTION);
395
+ const question = oneLine(r.question);
396
+ if (!question) return { ok: false, refusal: "no-question" };
397
+ const why = oneLine(r.why);
398
+
399
+ // ---- the belt, and exactly whose text it guards -------------------------
400
+ //
401
+ // All THREE free-text slots — `question`, a `decision` name, and `why` —
402
+ // but only on a SESSION-origin escalation, because that is the text a MODEL
403
+ // wrote. Rule-origin text is the operator's own config plus our template:
404
+ //
405
+ // * `why` is rendered verbatim by design; an operator may word their
406
+ // reason however they like. (A session's `why` IS belted, or a narrow
407
+ // question could ride beside a "**Why you:** just review the whole
408
+ // thing and sign off".)
409
+ // * a rule's `question` and `decision` are composed by `questionForRule`
410
+ // and `scopeForRule` from the operator's own rule name, label or check
411
+ // name. Belting those means an operator who names a label
412
+ // "review the whole thing" gets their fired rule SILENTLY DROPPED —
413
+ // a false positive is a silent drop, and this feature's worst failure
414
+ // is a configured rule that quietly does nothing.
415
+ //
416
+ // Model-written text CAN reach a rule-built scope — a finding's reason and
417
+ // its path both come from the reasoning record. Neither is handled here:
418
+ // the reason is filtered where it enters (`scopeForRule`), so a trip costs
419
+ // that candidate its scope rather than dropping the operator's whole rule,
420
+ // and the path is caught by the path-in-diff check above, which sits OUTSIDE
421
+ // this origin gate on purpose.
422
+ //
423
+ // `origin` is an ARGUMENT. A candidate cannot elect itself into the exempt
424
+ // group by writing `"origin": "rule"` — see the parameter's docstring.
425
+ if (origin === "session") {
426
+ if (asksForWholeReview(question)) return { ok: false, refusal: "asks-for-whole-review" };
427
+ if (scope.kind === "decision" && asksForWholeReview(scope.decision)) {
428
+ return { ok: false, refusal: "asks-for-whole-review" };
429
+ }
430
+ if (asksForWholeReview(why)) return { ok: false, refusal: "asks-for-whole-review" };
431
+ }
432
+ // Likewise the rule NAME: only a rule-origin candidate may carry one, and
433
+ // that one was written by `evaluateEscalationRules` from the operator's own
434
+ // config. A session naming a rule would forge the ledger row and the
435
+ // "_Escalated by the configured rule `x`._" line for a rule that need not
436
+ // even exist.
437
+ const rule = origin === "rule" ? str(r.rule) : "";
438
+ return {
439
+ ok: true,
440
+ request: {
441
+ target,
442
+ scope,
443
+ question,
444
+ why: why || "named by the operator as the person to ask about this",
445
+ origin,
446
+ ...(rule ? { rule } : {}),
447
+ },
448
+ };
449
+ }
450
+
451
+ // ---- the deterministic triggers -------------------------------------------
452
+
453
+ /** Everything a rule may key on. Every field is what the daemon already knows
454
+ * at the end of a round; nothing here is a model's opinion. */
455
+ export interface EscalationFacts {
456
+ /** Repo-relative paths the PR touches. Empty ⇒ path rules cannot fire. */
457
+ changed_paths: readonly string[];
458
+ /** Labels on the PR. Empty ⇒ label rules cannot fire. */
459
+ labels: readonly string[];
460
+ /** Findings the round POSTED, with the severity each went out at. */
461
+ findings: readonly { severity: string; path?: string | null; title?: string | null }[];
462
+ /** Names of checks whose LATEST run at the head is not green. */
463
+ failing_checks: readonly string[];
464
+ }
465
+
466
+ /** Is `severity` at or above `floor`? Unknown spellings never satisfy a floor
467
+ * — a rule must not fire on a severity nobody can read. */
468
+ export function severityAtLeast(severity: string, floor: EscalationSeverityFloor): boolean {
469
+ const s = severityRank(severity);
470
+ return s !== undefined && s <= severityRank(floor)!;
471
+ }
472
+
473
+ /**
474
+ * Glob match, deliberately small: `*` matches within a path segment, `**`
475
+ * across segments, everything else is literal. No dependency, and the whole
476
+ * grammar fits in the config comment an operator reads.
477
+ */
478
+ export function pathMatches(pattern: string, path: string): boolean {
479
+ return new RegExp(`^${globBody(pattern)}$`).test(path);
480
+ }
481
+
482
+ // One predicate per condition, shared by `ruleFires` (does any fact match?)
483
+ // and `scopeForRule` (which fact matched?), so the two never disagree.
484
+ const sameName = (a: string, b: string): boolean => a.toLowerCase() === b.toLowerCase();
485
+ const pathHits = (rule: EscalationRule, path: string): boolean =>
486
+ rule.paths.some((pat) => pathMatches(pat, path));
487
+ const labelHits = (rule: EscalationRule, label: string): boolean =>
488
+ rule.labels.some((n) => sameName(n, label));
489
+ const checkHits = (rule: EscalationRule, check: string): boolean =>
490
+ rule.failing_checks.some((n) => sameName(n, check));
491
+ const findingHits = (rule: EscalationRule, f: EscalationFacts["findings"][number]): boolean =>
492
+ severityAtLeast(String(f.severity ?? ""), rule.min_severity as EscalationSeverityFloor);
493
+
494
+ /** A `decision` name: whitespace collapsed, trimmed, capped. */
495
+ function decisionName(text: string): string {
496
+ return text.replace(/\s+/g, " ").trim().slice(0, MAX_ESCALATION_DECISION);
497
+ }
498
+
499
+ /** Did this rule's conditions all hold? A rule with no condition never fires
500
+ * (config validation refuses one, so this is belt). */
501
+ export function ruleFires(rule: EscalationRule, facts: EscalationFacts): boolean {
502
+ let declared = 0;
503
+ if (rule.paths.length > 0) {
504
+ declared++;
505
+ if (!facts.changed_paths.some((p) => pathHits(rule, p))) return false;
506
+ }
507
+ if (rule.labels.length > 0) {
508
+ declared++;
509
+ if (!facts.labels.some((l) => labelHits(rule, l))) return false;
510
+ }
511
+ if (rule.failing_checks.length > 0) {
512
+ declared++;
513
+ if (!facts.failing_checks.some((c) => checkHits(rule, c))) return false;
514
+ }
515
+ if (rule.min_severity) {
516
+ declared++;
517
+ if (!facts.findings.some((f) => findingHits(rule, f))) return false;
518
+ }
519
+ return declared > 0;
520
+ }
521
+
522
+ /** The scope one matching finding points at: its anchor, else the decision
523
+ * its title names (belted), else none. */
524
+ function findingScope(hit: EscalationFacts["findings"][number]): EscalationScope | null {
525
+ if (hit.path) return { kind: "code", path: hit.path };
526
+ // The finding's reason is model-written text reaching a rule-built scope,
527
+ // so it is belted HERE, at the boundary it enters — not in
528
+ // `validateEscalationRequest`, where a trip would drop the operator's whole
529
+ // rule instead of just this candidate name. A reason that asks for a whole
530
+ // review simply yields no scope, and the caller reports
531
+ // `no-scope-derivable`.
532
+ //
533
+ // It is not the only model-written field that gets here: `hit.path` above
534
+ // comes from the same findings record. That one needs no belt because the
535
+ // unconditional path-in-diff check in `validateEscalationRequest` sits
536
+ // OUTSIDE the origin gate and catches it — deliberately, since a path is
537
+ // checkable against a fact and prose is not.
538
+ if (hit.title) {
539
+ const decision = decisionName(hit.title);
540
+ if (!asksForWholeReview(decision)) return { kind: "decision", decision };
541
+ }
542
+ return null;
543
+ }
544
+
545
+ /**
546
+ * The scope a fired rule points at. A rule names a CLASS of thing, so the
547
+ * scope is derived from the fact that actually matched — the touched file, the
548
+ * finding's anchor, the failing check — never "the PR".
549
+ */
550
+ export function scopeForRule(rule: EscalationRule, facts: EscalationFacts): EscalationScope | null {
551
+ if (rule.paths.length > 0) {
552
+ const hit = facts.changed_paths.find((p) => pathHits(rule, p));
553
+ if (hit)
554
+ return /(^|\/)migrations?\//i.test(hit)
555
+ ? { kind: "migration", path: hit }
556
+ : { kind: "code", path: hit };
557
+ }
558
+ if (rule.min_severity) {
559
+ // The first matching finding that yields a scope the PR admits (an anchor
560
+ // in the diff, or a decision); else the first one's, so its refusal is
561
+ // what gets reported.
562
+ const scopes = facts.findings.filter((f) => findingHits(rule, f)).map((f) => findingScope(f));
563
+ const usable = scopes.find(
564
+ (sc) => sc && (sc.kind !== "code" || facts.changed_paths.includes(sc.path)),
565
+ );
566
+ if (usable) return usable;
567
+ if (scopes[0]) return scopes[0];
568
+ }
569
+ if (rule.failing_checks.length > 0) {
570
+ const hit = facts.failing_checks.find((c) => checkHits(rule, c));
571
+ if (hit) return { kind: "decision", decision: `the failing check "${hit}"` };
572
+ }
573
+ if (rule.labels.length > 0) {
574
+ const hit = facts.labels.find((l) => labelHits(rule, l));
575
+ if (hit)
576
+ return {
577
+ kind: "decision",
578
+ decision: `what the "${hit}" label is asking for on this pull request`,
579
+ };
580
+ }
581
+ return null;
582
+ }
583
+
584
+ /**
585
+ * The question a fired rule asks. Narrow by construction — it names the scope
586
+ * and the rule, and asks for a judgement on that one thing.
587
+ *
588
+ * This wording and the label decision above use natural phrasing: the belt
589
+ * (`asksForWholeReview`) does not run on rule-origin text, so a rule-built
590
+ * string never needs rewording to dodge it. `tests/escalation.test.ts` pins
591
+ * that a fired rule is never dropped by its wording, the operator's own
592
+ * included.
593
+ */
594
+ export function questionForRule(rule: EscalationRule, scope: EscalationScope): string {
595
+ const full = `Your rule "${rule.name}" fires on this change. Is ${renderScope(scope)} right, in your judgement?`;
596
+ // Validation cuts a question at MAX_ESCALATION_QUESTION, which could drop the
597
+ // closing delimiter of a rendered path; a long one is left to the note's
598
+ // "Look at" line, which carries the scope whole.
599
+ if (full.length <= MAX_ESCALATION_QUESTION) return full;
600
+ return `Your rule "${rule.name}" fires on this change. Is the scope under "Look at" right, in your judgement?`;
601
+ }
602
+
603
+ /**
604
+ * Evaluate the operator's rules. Pure: same facts in, same escalations out,
605
+ * no I/O, no model. This is the function `tests/escalation.test.ts` drives to
606
+ * prove the deterministic half is inspectable without an LLM.
607
+ */
608
+ export function evaluateEscalationRules(
609
+ cfg: EscalationConfig,
610
+ facts: EscalationFacts,
611
+ /**
612
+ * Called when a rule FIRED but its candidate did not survive validation.
613
+ *
614
+ * A deterministic rule that silently produces nothing is the worst failure
615
+ * this feature has: the operator configured it, it
616
+ * matched, and nobody was told — including the operator. Session-origin
617
+ * candidates already log `escalation_refused` at delivery; this is the same
618
+ * visibility for the half that is refused before delivery is even reached.
619
+ * Optional so the function stays pure for its own tests.
620
+ */
621
+ onRefused?: (rule: EscalationRule, refusal: EscalationRefusal | "no-scope-derivable") => void,
622
+ ): EscalationRequest[] {
623
+ if (!cfg.enabled) return [];
624
+ const out: EscalationRequest[] = [];
625
+ for (const rule of cfg.rules) {
626
+ if (!ruleFires(rule, facts)) continue;
627
+ const scope = scopeForRule(rule, facts);
628
+ if (!scope) {
629
+ // Nothing concrete to point at ⇒ no escalation (invariant 2), but the
630
+ // operator's rule DID fire, so this is not silence.
631
+ onRefused?.(rule, "no-scope-derivable");
632
+ continue;
633
+ }
634
+ const candidate = {
635
+ target: rule.to,
636
+ scope,
637
+ question: questionForRule(rule, scope),
638
+ why: rule.why,
639
+ rule: rule.name,
640
+ };
641
+ // Validated against the SAME facts the scope came from — so a rule firing
642
+ // on a finding anchored outside the diff is refused here rather than
643
+ // pointing a person at a file this PR does not touch. Origin is passed,
644
+ // not embedded: this call site IS the operator's half.
645
+ const v = validateEscalationRequest(candidate, cfg, "rule", facts.changed_paths);
646
+ if (v.ok) out.push(v.request);
647
+ else onRefused?.(rule, v.refusal);
648
+ }
649
+ return out;
650
+ }
651
+
652
+ // ---- the rendered note ----------------------------------------------------
653
+
654
+ /** The marker every escalation comment opens with — one grammar, greppable,
655
+ * and never confusable with the three finding markers in `packages/pi-plugin/src/severity.ts`. */
656
+ export const ESCALATION_TAG = "[one thing to look at]";
657
+
658
+ export interface EscalationRenderContext {
659
+ /** The verdict the round already reached, on the merits. Stated in the note
660
+ * so the recipient can see the review is DONE — read, never consulted. */
661
+ verdict: string;
662
+ /** Round number, for the "already posted" sentence. */
663
+ round_no?: number;
664
+ /** True when the review-request shape is also being used, so the note can
665
+ * say what GitHub's queue entry does and does not mean. */
666
+ review_requested?: boolean;
667
+ /**
668
+ * Did the ROUND'S OWN review actually reach GitHub? From the posting
669
+ * receipt, not inferred.
670
+ *
671
+ * This matters more here than anywhere else: the escalation
672
+ * note is the one posting exempt from guard 2, so the case where the round's
673
+ * own review was refused is exactly the case where this note still goes out.
674
+ * Telling a named human "the review is posted" when it is not would send
675
+ * them looking for something that is not there. Unknown (`undefined`) is
676
+ * treated as not-confirmed: the note says DECIDED, which the daemon knows,
677
+ * and claims nothing about GitHub.
678
+ */
679
+ review_posted?: boolean;
680
+ }
681
+
682
+ /**
683
+ * The escalation body. Everything structural is composed from the scope, which
684
+ * is exact: it names one file, migration or decision, and a path in it is a
685
+ * file this PR touches.
686
+ *
687
+ * The free-text slots are `question`, a `decision` name and `why`. On a
688
+ * SESSION escalation all three are belted by `asksForWholeReview` (a heuristic
689
+ * — see its docstring). On a RULE escalation `why` is the operator's own text
690
+ * and is rendered VERBATIM by design, so this function can and will render
691
+ * whatever they wrote there, "please review this PR" included. That is their
692
+ * call to make about their own reviewers; what it cannot do is change the
693
+ * scope the note points at.
694
+ */
695
+ export function renderEscalationComment(
696
+ req: EscalationRequest,
697
+ ctx: EscalationRenderContext,
698
+ ): string {
699
+ const round = ctx.round_no ? ` (round ${ctx.round_no})` : "";
700
+ const lines = [
701
+ `${ESCALATION_TAG} @${req.target} — one specific thing, not a review of this pull request.`,
702
+ "",
703
+ `**Look at:** ${renderScope(req.scope)}`,
704
+ `**Why you:** ${req.why}`,
705
+ `**The question:** ${req.question}`,
706
+ "",
707
+ ctx.review_posted
708
+ ? `This review is already decided and posted${round} — the verdict is **${ctx.verdict}** and it does not ` +
709
+ "wait on you. This note asks only for your eyes on the item above."
710
+ : `This review is already decided${round} — the verdict is **${ctx.verdict}** and it does not wait on you. ` +
711
+ "This note asks only for your eyes on the item above. (The review itself is not confirmed on this pull " +
712
+ "request yet; if you cannot find it, that is why, and it is not something you need to chase.)",
713
+ ];
714
+ if (ctx.review_requested) {
715
+ lines.push(
716
+ "",
717
+ // Conditional on purpose: this note is posted BEFORE the request is
718
+ // made, and the request can still fail (GitHub 422s the PR author or a
719
+ // non-collaborator) — so it must not promise the queue entry.
720
+ "_A review request is also being sent to you, so this pull request may appear in your " +
721
+ "review-requested queue: that is how the notification is delivered, and GitHub words it as " +
722
+ '"review this pull request". It is not what is being asked. The item above is._',
723
+ );
724
+ }
725
+ if (req.origin === "rule" && req.rule) {
726
+ lines.push("", `_Escalated by the configured rule \`${req.rule}\`._`);
727
+ }
728
+ return lines.join("\n");
729
+ }
730
+
731
+ // ---- the prompt-side half -------------------------------------------------
732
+
733
+ // The targets a round order carries and the round-prompt block live in
734
+ // packages/pi-plugin/src/escalationprompt.ts (the review core renders the prompt); re-exported.
735
+ export {
736
+ promptTargetsFor,
737
+ renderEscalationPromptNote,
738
+ } from "@quickchatai/pi-quickreview/src/escalationprompt.js";
739
+
740
+ /** The most `escalations` entries one verdict file is read for. */
741
+ const MAX_SESSION_ESCALATIONS = 10;
742
+
743
+ /**
744
+ * Pull the `escalations` array out of a verdict file, tolerantly.
745
+ *
746
+ * Deliberately total: anything unreadable comes back as `[]` with a note.
747
+ * A malformed escalation must never fail a verdict — that would be invariant 1
748
+ * lost at the parser.
749
+ */
750
+ export function normalizeSessionEscalations(raw: unknown): { entries: unknown[]; note?: string } {
751
+ if (raw === undefined || raw === null) return { entries: [] };
752
+ if (!Array.isArray(raw)) return { entries: [], note: "escalations was not an array — ignored" };
753
+ const objects = raw.filter((e) => typeof e === "object" && e !== null);
754
+ const entries = objects.slice(0, MAX_SESSION_ESCALATIONS);
755
+ const notes: string[] = [];
756
+ if (objects.length < raw.length)
757
+ notes.push(`${raw.length - objects.length} escalation entr(ies) were not objects — ignored`);
758
+ if (entries.length < objects.length)
759
+ notes.push(
760
+ `${objects.length - entries.length} escalation entr(ies) past the first ${MAX_SESSION_ESCALATIONS} — ignored`,
761
+ );
762
+ return { entries, ...(notes.length ? { note: notes.join("; ") } : {}) };
763
+ }
764
+
765
+ // ---- delivery -------------------------------------------------------------
766
+
767
+ /** The write half of the adapter, both calls gated. Optional methods mean an
768
+ * adapter that cannot do a shape simply does not do it — never a throw. */
769
+ export interface EscalationAdapter {
770
+ createReview(
771
+ pr: number,
772
+ payload: { commit_id: string; event: "COMMENT"; body: string },
773
+ options?: { purpose?: "escalation" | "migration_precheck" },
774
+ ): Promise<GateResult>;
775
+ /** POST /repos/{o}/{r}/pulls/{n}/requested_reviewers — the `request_reviewer`
776
+ * gate kind. Absent on adapters that predate the feature. */
777
+ requestEscalationReview?(pr: number, logins: string[]): Promise<GateResult>;
778
+ /** Read-only: exact marker/body, reviewed head and bot identity must match. */
779
+ findEscalationComment?(
780
+ pr: number,
781
+ head: string,
782
+ marker: string,
783
+ body: string,
784
+ ): Promise<{ id: number } | null>;
785
+ }
786
+
787
+ /** The ledger slice: the per-PR cap and the never-twice dedup. */
788
+ export interface EscalationStore extends Pick<
789
+ StateStore,
790
+ | "escalationRecovery"
791
+ | "prepareEscalationRecovery"
792
+ | "claimEscalationComment"
793
+ | "escalationPostingCount"
794
+ | "retryEscalationComment"
795
+ | "noteEscalationRecovery"
796
+ | "settleEscalationComment"
797
+ | "finishEscalationRecovery"
798
+ | "pendingEscalationRecoveries"
799
+ > {
800
+ logEvent(kind: string, pr: number | null, payload?: unknown): void;
801
+ escalationDelivered(pr: number, key: string): boolean;
802
+ escalationCount(pr: number): number;
803
+ recordEscalation(
804
+ pr: number,
805
+ row: {
806
+ target: string;
807
+ scope_key: string;
808
+ shape: string;
809
+ origin: string;
810
+ rule: string | null;
811
+ round_no: number | null;
812
+ },
813
+ ): void;
814
+ }
815
+
816
+ export interface EscalationDeps {
817
+ cfg: EscalationConfig;
818
+ store: EscalationStore;
819
+ adapter: EscalationAdapter;
820
+ /** Snapshot of the operator flag; dry-run never leaves recovery debt. */
821
+ dryRun?: boolean;
822
+ /** Stops dispatching new recovery writes during watcher shutdown. */
823
+ isDraining?: () => boolean;
824
+ log?: (msg: string, extra?: unknown) => void;
825
+ }
826
+
827
+ export interface EscalationOutcome {
828
+ target: string;
829
+ scope_key: string;
830
+ origin: EscalationOrigin;
831
+ /** Why the candidate was refused before posting; null when a post was
832
+ * attempted — delivered, recorded as a dry-run intent, blocked by a posting
833
+ * guard, or failed (`comment_executed` says whether it reached GitHub). */
834
+ refused: EscalationRefusal | null;
835
+ /** Did the comment actually reach GitHub (false in dry-run — the gate
836
+ * recorded it instead, exactly like every other suppressed mutation). */
837
+ comment_executed: boolean;
838
+ /** Did the review request reach GitHub? Absent on the comment shape. */
839
+ review_requested?: boolean;
840
+ }
841
+
842
+ /** Stable evidence attached to the one narrow comment, never a retry nonce. */
843
+ function escalationMarker(pr: number, target: string, key: string, head: string): string {
844
+ return `<!-- qcr-escalation:${createHash("sha256")
845
+ .update(JSON.stringify([pr, target, key, head]))
846
+ .digest("hex")} -->`;
847
+ }
848
+
849
+ const activeEscalations = new WeakMap<EscalationStore, Set<number>>();
850
+ function recoveryOutcome(
851
+ row: EscalationRecoveryRow,
852
+ executed: boolean,
853
+ requested?: boolean,
854
+ ): EscalationOutcome {
855
+ return {
856
+ target: row.target,
857
+ scope_key: row.scope_key,
858
+ origin: row.origin,
859
+ refused: null,
860
+ comment_executed: executed,
861
+ ...(row.shape === "review_request" ? { review_requested: requested ?? false } : {}),
862
+ };
863
+ }
864
+
865
+ /** A 422 is the existing terminal policy; network/5xx failures still owe the queue write. */
866
+ function terminalRequestFailure(error: unknown): boolean {
867
+ const status =
868
+ typeof error === "object" && error !== null
869
+ ? (error as { status?: unknown }).status
870
+ : undefined;
871
+ return (
872
+ status === 422 ||
873
+ (status === undefined && /requested_reviewers[^\n]*(?:→|HTTP)\s*422\b/.test(String(error)))
874
+ );
875
+ }
876
+
877
+ async function continueEscalation(
878
+ deps: EscalationDeps,
879
+ row: EscalationRecoveryRow,
880
+ journal = true,
881
+ ): Promise<EscalationOutcome> {
882
+ const { store, adapter, cfg } = deps;
883
+ if (
884
+ deps.isDraining?.() ||
885
+ (row.next_attempt_at && row.next_attempt_at > new Date().toISOString())
886
+ )
887
+ return recoveryOutcome(row, row.phase === "request_pending");
888
+ const log = deps.log ?? (() => {});
889
+ const busy = activeEscalations.get(store) ?? new Set<number>();
890
+ activeEscalations.set(store, busy);
891
+ if (busy.has(row.pr)) return recoveryOutcome(row, false);
892
+ busy.add(row.pr);
893
+ const note = (error: string): void => {
894
+ if (journal) store.noteEscalationRecovery(row, error);
895
+ };
896
+ const finish = (): void => {
897
+ if (journal) store.finishEscalationRecovery(row);
898
+ };
899
+ try {
900
+ if (!cfg.targets.includes(row.target)) {
901
+ note("paused: target no longer configured");
902
+ return recoveryOutcome(row, false);
903
+ }
904
+ if (row.phase === "request_pending" && cfg.shape !== "review_request") {
905
+ note("paused: review-request shape disabled");
906
+ return recoveryOutcome(row, true);
907
+ }
908
+ let deliveredNow = false;
909
+ if (row.phase === "posting") {
910
+ let found: { id: number } | null | undefined;
911
+ let readError: string | undefined;
912
+ try {
913
+ found = await adapter.findEscalationComment?.(row.pr, row.head_sha, row.marker, row.body);
914
+ } catch (error) {
915
+ readError = `comment read unavailable: ${String(error)}`;
916
+ }
917
+ if (!found) {
918
+ note(readError ?? "comment delivery remains unknown; no matching marker confirmed");
919
+ store.logEvent("escalation_comment_unknown", row.pr, {
920
+ target: row.target,
921
+ scope_key: row.scope_key,
922
+ });
923
+ return recoveryOutcome(row, false);
924
+ }
925
+ store.settleEscalationComment(row);
926
+ deliveredNow = true;
927
+ store.logEvent("escalation_comment_recovered", row.pr, {
928
+ target: row.target,
929
+ scope_key: row.scope_key,
930
+ comment_id: found.id,
931
+ });
932
+ row = { ...row, phase: "request_pending" };
933
+ } else if (row.phase === "prepared") {
934
+ if (store.escalationCount(row.pr) >= cfg.max_per_pr) {
935
+ finish();
936
+ store.logEvent("escalation_capped", row.pr, {
937
+ target: row.target,
938
+ scope_key: row.scope_key,
939
+ max_per_pr: cfg.max_per_pr,
940
+ });
941
+ return { ...recoveryOutcome(row, false), refused: "capped" };
942
+ }
943
+ if (
944
+ journal &&
945
+ store.escalationCount(row.pr) + store.escalationPostingCount(row.pr) >= cfg.max_per_pr
946
+ ) {
947
+ note("capacity reserved by unresolved comment POST");
948
+ return { ...recoveryOutcome(row, false), refused: "capped" };
949
+ }
950
+ if (journal && !store.claimEscalationComment(row)) return recoveryOutcome(row, false);
951
+ let result: GateResult;
952
+ try {
953
+ result = await adapter.createReview(
954
+ row.pr,
955
+ { commit_id: row.head_sha, event: "COMMENT", body: row.body },
956
+ { purpose: "escalation" },
957
+ );
958
+ } catch (error) {
959
+ if (
960
+ journal &&
961
+ error instanceof GitHubRequestError &&
962
+ error.status >= 400 &&
963
+ error.status < 500 &&
964
+ error.status !== 408
965
+ ) {
966
+ if (error.status === 422 || error.status === 400 || error.status === 410) finish();
967
+ else
968
+ store.retryEscalationComment(
969
+ row,
970
+ `comment POST refused: ${String(error)}`,
971
+ new Date(Date.now() + Math.max(30_000, error.retryAfterMs ?? 0)).toISOString(),
972
+ );
973
+ } else note(`comment POST outcome unknown: ${String(error)}`);
974
+ store.logEvent("escalation_comment_failed", row.pr, {
975
+ target: row.target,
976
+ scope_key: row.scope_key,
977
+ origin: row.origin,
978
+ round_no: row.round_no,
979
+ error: String(error),
980
+ });
981
+ log(`escalation: #${row.pr} → @${row.target} comment failed: ${String(error)}`);
982
+ return recoveryOutcome(row, false);
983
+ }
984
+ if (result.blocked) {
985
+ // pr-unreadable is a failed GitHub read inside the guard, not a
986
+ // decision about the PR: the obligation stays owed and is retried.
987
+ if (journal && result.blocked === "pr-unreadable")
988
+ store.retryEscalationComment(
989
+ row,
990
+ `comment blocked: ${result.blocked}`,
991
+ new Date(Date.now() + 30_000).toISOString(),
992
+ );
993
+ else finish();
994
+ store.logEvent("escalation_blocked", row.pr, {
995
+ target: row.target,
996
+ scope_key: row.scope_key,
997
+ reason: result.blocked,
998
+ });
999
+ return recoveryOutcome(row, false);
1000
+ }
1001
+ if (!result.executed) {
1002
+ // Preserve both dry-run intents; neither spends a delivery cap slot.
1003
+ let requested = false;
1004
+ if (row.shape === "review_request" && adapter.requestEscalationReview) {
1005
+ try {
1006
+ requested = (await adapter.requestEscalationReview(row.pr, [row.target])).executed;
1007
+ } catch (error) {
1008
+ log(`escalation: dry-run reviewer intent failed`, error);
1009
+ }
1010
+ }
1011
+ finish();
1012
+ store.logEvent("escalation_not_executed", row.pr, {
1013
+ target: row.target,
1014
+ scope_key: row.scope_key,
1015
+ shape: row.shape,
1016
+ origin: row.origin,
1017
+ round_no: row.round_no,
1018
+ dry_run_file: result.dryRunFile ?? null,
1019
+ });
1020
+ return recoveryOutcome(row, false, requested);
1021
+ }
1022
+ if (journal) store.settleEscalationComment(row);
1023
+ else store.recordEscalation(row.pr, row);
1024
+ deliveredNow = true;
1025
+ row = { ...row, phase: "request_pending" };
1026
+ }
1027
+ let requested = false;
1028
+ if (row.shape === "review_request") {
1029
+ if (deps.isDraining?.()) {
1030
+ note("reviewer request pending while stopping");
1031
+ return recoveryOutcome(row, true);
1032
+ }
1033
+ if (cfg.shape !== "review_request") {
1034
+ note("paused: review-request shape disabled");
1035
+ return recoveryOutcome(row, true);
1036
+ }
1037
+ if (!adapter.requestEscalationReview) note("reviewer request adapter unavailable");
1038
+ else
1039
+ try {
1040
+ const result = await adapter.requestEscalationReview(row.pr, [row.target]);
1041
+ requested = result.executed;
1042
+ if (requested) finish();
1043
+ else note(`reviewer request not executed: ${result.blocked ?? "dry run"}`);
1044
+ } catch (error) {
1045
+ if (terminalRequestFailure(error)) finish();
1046
+ else note(`reviewer request pending: ${String(error)}`);
1047
+ store.logEvent("escalation_request_failed", row.pr, {
1048
+ target: row.target,
1049
+ scope_key: row.scope_key,
1050
+ error: String(error),
1051
+ });
1052
+ log(`escalation: #${row.pr} → @${row.target} review request failed: ${String(error)}`);
1053
+ }
1054
+ }
1055
+ if (deliveredNow) {
1056
+ store.logEvent("escalation_delivered", row.pr, {
1057
+ target: row.target,
1058
+ scope_key: row.scope_key,
1059
+ shape: row.shape,
1060
+ origin: row.origin,
1061
+ rule: row.rule,
1062
+ round_no: row.round_no,
1063
+ comment_executed: true,
1064
+ review_requested: requested,
1065
+ });
1066
+ log(
1067
+ `escalation: #${row.pr} → @${row.target} (${row.scope_key}, ${row.shape}, ${row.origin})`,
1068
+ );
1069
+ } else if (requested)
1070
+ store.logEvent("escalation_request_recovered", row.pr, {
1071
+ target: row.target,
1072
+ scope_key: row.scope_key,
1073
+ });
1074
+ return recoveryOutcome(row, true, requested);
1075
+ } catch (error) {
1076
+ // A successful POST followed by a local receipt failure remains in
1077
+ // posting. Its marker is reconciled on a later sweep, never re-posted.
1078
+ let confirmed = false;
1079
+ try {
1080
+ confirmed = store.escalationDelivered(row.pr, `${row.target}|${row.scope_key}`);
1081
+ note(`local delivery checkpoint unavailable: ${String(error)}`);
1082
+ store.logEvent("escalation_recovery_pending", row.pr, {
1083
+ target: row.target,
1084
+ scope_key: row.scope_key,
1085
+ error: String(error),
1086
+ });
1087
+ } catch {
1088
+ /* the previously durable journal still carries the obligation */
1089
+ }
1090
+ log(`escalation: local recovery checkpoint failed on #${row.pr}`, error);
1091
+ return recoveryOutcome(row, confirmed);
1092
+ } finally {
1093
+ busy.delete(row.pr);
1094
+ }
1095
+ }
1096
+
1097
+ /** Resume durable obligations even when no later round proposes the candidate. */
1098
+ export async function resumeEscalations(deps: EscalationDeps): Promise<EscalationOutcome[]> {
1099
+ if (!deps.cfg.enabled || deps.dryRun || deps.isDraining?.()) return [];
1100
+ const outcomes: EscalationOutcome[] = [];
1101
+ for (const row of deps.store.pendingEscalationRecoveries()) {
1102
+ if (deps.isDraining?.()) break;
1103
+ try {
1104
+ outcomes.push(await continueEscalation(deps, row));
1105
+ } catch (error) {
1106
+ deps.log?.(`escalation: recovery failed on #${row.pr}`, error);
1107
+ }
1108
+ }
1109
+ return outcomes;
1110
+ }
1111
+
1112
+ /**
1113
+ * Deliver escalations for one round.
1114
+ *
1115
+ * NOTE THE RETURN TYPE. `EscalationOutcome[]` carries no verdict and no
1116
+ * "should the review wait" signal, because there is nothing this function may
1117
+ * say about the verdict — that is invariant 1 expressed as a type. The caller
1118
+ * has already applied the verdict before this runs.
1119
+ */
1120
+ export async function deliverEscalations(
1121
+ deps: EscalationDeps,
1122
+ args: {
1123
+ pr: number;
1124
+ /** The verdict the round decided. INPUT ONLY — quoted into the note so the
1125
+ * recipient knows the review is finished. Never read to decide anything. */
1126
+ verdict: string;
1127
+ /** The head the round reviewed. The comment is anchored to it, so posting
1128
+ * guard 5 refuses a stale escalation exactly as it refuses a stale review
1129
+ * — and, because nothing is recorded on a refusal, a later round retries. */
1130
+ head_sha: string;
1131
+ round_no?: number;
1132
+ /** The PR's changed files, for the path-in-diff check. Pass `[]` when the
1133
+ * listing could not be read: every path-bearing scope is then refused,
1134
+ * which is the safe direction. */
1135
+ changed_paths?: readonly string[];
1136
+ /** Did the round's own review reach GitHub (the posting receipt)? Rendered
1137
+ * into the note; see `EscalationRenderContext.review_posted`. */
1138
+ review_posted?: boolean;
1139
+ /**
1140
+ * The OPERATOR's half: candidates `evaluateEscalationRules` built from
1141
+ * configured rules. Trusted as operator text.
1142
+ */
1143
+ ruleRequests?: readonly EscalationRequest[];
1144
+ /**
1145
+ * The MODEL's half: raw `escalations` entries out of the verdict file.
1146
+ * Never trusted, and — the point of keeping the two apart — never able to
1147
+ * relabel itself into the group above.
1148
+ */
1149
+ sessionCandidates?: readonly unknown[];
1150
+ },
1151
+ ): Promise<EscalationOutcome[]> {
1152
+ const log = deps.log ?? (() => {});
1153
+ const { cfg, store } = deps;
1154
+ const outcomes: EscalationOutcome[] = [];
1155
+ if (!cfg.enabled || !args.head_sha) return outcomes;
1156
+
1157
+ // The two groups stay separate all the way to validation, and each carries
1158
+ // its origin as a property of WHERE IT CAME FROM rather than of what it
1159
+ // says about itself. Rules first: they are the deterministic half, and on a
1160
+ // capped PR the operator's own rules should win the budget.
1161
+ const queued: Array<{ raw: unknown; origin: EscalationOrigin }> = [
1162
+ ...(args.ruleRequests ?? []).map((raw) => ({ raw: raw, origin: "rule" as const })),
1163
+ ...(args.sessionCandidates ?? []).map((raw) => ({ raw, origin: "session" as const })),
1164
+ ];
1165
+
1166
+ for (const { raw, origin } of queued) {
1167
+ const v = validateEscalationRequest(raw, cfg, origin, args.changed_paths ?? []);
1168
+ if (!v.ok) {
1169
+ store.logEvent("escalation_refused", args.pr, {
1170
+ reason: v.refusal,
1171
+ origin,
1172
+ round_no: args.round_no ?? null,
1173
+ candidate: raw,
1174
+ });
1175
+ log(`escalation: #${args.pr} refused (${origin}) — ${v.refusal}`);
1176
+ outcomes.push({
1177
+ target: "",
1178
+ scope_key: "",
1179
+ origin,
1180
+ refused: v.refusal,
1181
+ comment_executed: false,
1182
+ });
1183
+ continue;
1184
+ }
1185
+ const req = v.request;
1186
+ const key = scopeKey(req.scope);
1187
+ const pending = store.escalationRecovery(args.pr, req.target, key);
1188
+ if (pending) {
1189
+ // A replay in dry-run must leave the preexisting live obligation untouched.
1190
+ outcomes.push(
1191
+ deps.dryRun ? recoveryOutcome(pending, false) : await continueEscalation(deps, pending),
1192
+ );
1193
+ continue;
1194
+ }
1195
+ if (store.escalationDelivered(args.pr, `${req.target}|${key}`)) {
1196
+ outcomes.push({
1197
+ target: req.target,
1198
+ scope_key: key,
1199
+ origin: req.origin,
1200
+ refused: "already-delivered",
1201
+ comment_executed: false,
1202
+ });
1203
+ continue;
1204
+ }
1205
+ if (store.escalationCount(args.pr) >= cfg.max_per_pr) {
1206
+ store.logEvent("escalation_capped", args.pr, {
1207
+ target: req.target,
1208
+ scope_key: key,
1209
+ max_per_pr: cfg.max_per_pr,
1210
+ });
1211
+ outcomes.push({
1212
+ target: req.target,
1213
+ scope_key: key,
1214
+ origin: req.origin,
1215
+ refused: "capped",
1216
+ comment_executed: false,
1217
+ });
1218
+ continue;
1219
+ }
1220
+
1221
+ const wantsRequest = cfg.shape === "review_request";
1222
+ const body = renderEscalationComment(req, {
1223
+ verdict: args.verdict,
1224
+ ...(args.round_no !== undefined ? { round_no: args.round_no } : {}),
1225
+ review_requested: wantsRequest,
1226
+ ...(args.review_posted !== undefined ? { review_posted: args.review_posted } : {}),
1227
+ });
1228
+ const marker = escalationMarker(args.pr, req.target, key, args.head_sha);
1229
+ const input = {
1230
+ pr: args.pr,
1231
+ target: req.target,
1232
+ scope_key: key,
1233
+ head_sha: args.head_sha,
1234
+ body: `${body}\n\n${marker}`,
1235
+ marker,
1236
+ shape: cfg.shape,
1237
+ origin: req.origin,
1238
+ rule: req.rule ?? null,
1239
+ round_no: args.round_no ?? null,
1240
+ };
1241
+ const row = deps.dryRun
1242
+ ? {
1243
+ ...input,
1244
+ phase: "prepared" as const,
1245
+ last_error: null,
1246
+ next_attempt_at: null,
1247
+ updated_at: new Date().toISOString(),
1248
+ }
1249
+ : store.prepareEscalationRecovery(input);
1250
+ outcomes.push(await continueEscalation(deps, row, !deps.dryRun));
1251
+ }
1252
+ return outcomes;
1253
+ }